<?php
namespace App\Entity;
use App\Entity\Enum\TransactionStatus;
use App\Entity\Traits\Identifiable;
use App\Entity\Traits\Timestampable;
use App\Repository\OrderRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: OrderRepository::class)]
#[ORM\Table(name: '`order`')]
class Order
{
use Identifiable;
use Timestampable;
#[ORM\Column(type: 'float')]
private $amount = 0;
#[ORM\Column(type: 'string', length: 10, nullable: true)]
private $type;
#[ORM\OneToOne(inversedBy: 'order', targetEntity: Registration::class, cascade: ['persist', 'remove'])]
private $registration;
#[ORM\OneToMany(mappedBy: 'order', targetEntity: Cart::class, cascade: ['persist', 'remove'])]
private $carts;
#[ORM\OneToMany(mappedBy: 'order', targetEntity: Transaction::class, cascade: ['persist', 'remove'])]
private $transactions;
public function __construct()
{
$this->carts = new ArrayCollection();
$this->transactions = new ArrayCollection();
$this->addCart(new Cart());
}
public function getAmount(): ?float
{
return $this->amount;
}
public function getDiscountAmount(?bool $aids = null): float
{
$total = 0;
foreach ($this->getCart()?->getDiscounts($aids) ?? [] as $cartItemDiscount) {
if ($discount = $cartItemDiscount->getDiscount()) {
$total += ($discount->getRemainingToPay() * -1);
} else {
$total += $cartItemDiscount->getPrice();
}
}
return $total * -1;
}
public function getRemainingAmount($includeDiscounts = true): float
{
$amount = $this->getAmount();
$statuses = [TransactionStatus::Waiting, TransactionStatus::Successful];
foreach ($this->getTransactions(statuses: $statuses) as $transaction) {
if ($transaction->getStatus() === TransactionStatus::Successful) {
$amount -= $transaction->getAmountPayed();
} elseif ($transaction instanceof TransactionCardMulti
&& $transaction->getStatus() === TransactionStatus::Waiting) {
$amount -= $transaction->getAmount();
}
}
if ($includeDiscounts) {
$amount -= $this->getDiscountAmount(true);
}
return $amount;
}
public function getPayedAmount(): float
{
$amount = 0;
foreach ($this->getTransactions() as $transaction) {
if ($transaction->getStatus() === TransactionStatus::Successful) {
$amount += $transaction->getAmountPayed();
} elseif ($transaction instanceof TransactionCardMulti &&
$transaction->getStatus() === TransactionStatus::Waiting &&
$transaction->getCycle() > 1) {
$amount += $transaction->getAmount();
}
}
return $amount;
}
public function setAmount(float $amount): self
{
$this->amount = $amount;
return $this;
}
public function getType(): ?string
{
return $this->type;
}
public function setType(?string $type): self
{
$this->type = $type;
return $this;
}
public function getRegistration(): ?Registration
{
return $this->registration;
}
public function setRegistration(?Registration $registration): self
{
$this->registration = $registration;
return $this;
}
/**
* @return Collection<int, Cart>
*/
public function getCarts(): Collection
{
return $this->carts;
}
public function addCart(Cart $cart): self
{
if (!$this->carts->contains($cart)) {
$this->carts[] = $cart;
$cart->setOrder($this);
}
return $this;
}
public function removeCart(Cart $cart): self
{
if ($this->carts->removeElement($cart)) {
// set the owning side to null (unless already changed)
if ($cart->getOrder() === $this) {
$cart->setOrder(null);
}
}
return $this;
}
public function getCart(): ?Cart
{
$cart = $this->carts->first();
return $cart === false ? null : $cart;
}
/**
* @param string|null $type
* @param TransactionStatus|array<int, TransactionStatus>|null $statuses
* @return Collection<int, Transaction>
*/
public function getTransactions(?string $type = null, array|TransactionStatus $statuses = null): Collection
{
$transactions = $this->transactions;
if ($type) {
$transactions = $transactions->filter(fn(Transaction $t) => ($t instanceof $type));
}
if ($statuses) {
if (!is_array($statuses)) {
$statuses = [$statuses];
}
$transactions = $transactions->filter(fn(Transaction $t) => (in_array($t->getStatus(), $statuses, true)));
}
return $transactions;
}
public function addTransaction(Transaction $transaction): self
{
if (!$this->transactions->contains($transaction)) {
$this->transactions[] = $transaction;
$transaction->setOrder($this);
}
return $this;
}
public function removeTransaction(Transaction $transaction): self
{
if ($this->transactions->removeElement($transaction)) {
// set the owning side to null (unless already changed)
if ($transaction->getOrder() === $this) {
$transaction->setOrder(null);
}
}
return $this;
}
public function getExpectedTransactionType(): ?string
{
foreach ($this->getTransactions() as $transaction) {
if ($transaction->getStatus() === TransactionStatus::Waiting) {
return get_class($transaction);
}
}
return null;
}
public function hasSuccessfulTransaction(?array $types = []): bool
{
$transactions = $this->getTransactions()
->filter(fn(Transaction $t) => $t->getStatus() === TransactionStatus::Successful);
if (count($types) > 0) {
$transactions = $transactions->filter(fn(Transaction $t) => in_array(get_class($t), $types, true));
}
return $transactions->count() > 0;
}
}