<?php
namespace App\Entity\PointsCatalog;
use App\Annotation\SiteAware;
use App\Entity\AbstractBase;
use App\Entity\Garages\Garage;
use App\Entity\Interfaces\SiteInterface;
use App\Entity\MiniAbstractBase;
use App\Entity\Traits\GarageTrait;
use App\Entity\Traits\SiteTrait;
use App\Entity\User;
use App\Enum\CatalogOrderStatusEnum;
use App\Exception\CatalogOrderCannotBeCancelled;
use App\Exception\CatalogOrderLineCannotBeCancelled;
use App\Exception\PointsLimitException;
use App\Repository\PointsCatalog\CatalogOrderRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Money\Currency;
use Money\Money;
/**
* @ORM\Table(name="vulco_points_catalog_order", indexes={@ORM\Index(name="catalog_order_site_idx", columns={"site"})})
*
* @ORM\Entity(repositoryClass=CatalogOrderRepository::class)
*
* @Gedmo\SoftDeleteable(fieldName="removedAt", timeAware=false)
*
* @SiteAware(siteFieldName="site")
*/
class CatalogOrder extends AbstractBase implements SiteInterface
{
use GarageTrait;
use SiteTrait;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Garages\Garage", fetch="EAGER")
*
* @ORM\JoinColumn(name="garage_id", referencedColumnName="id")
*/
private Garage $garage;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\PointsCatalog\CatalogGiftProvider", fetch="EAGER")
*
* @ORM\JoinColumn(name="provider_id", referencedColumnName="id")
*/
private CatalogGiftProvider $provider;
/**
* @ORM\OneToMany(targetEntity="App\Entity\PointsCatalog\CatalogOrderGift", mappedBy="catalogOrder", cascade={"persist"})
*/
private Collection $gifts;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\User", fetch="EAGER")
*
* @ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private User $user;
/**
* @ORM\Column(type="integer")
*/
private int $state;
/**
* @ORM\Column(type="datetime", nullable=true)
*/
private ?\DateTimeInterface $processedAt = null;
/**
* @ORM\Column(type="datetime", nullable=true)
*/
private ?\DateTimeInterface $cancelledAt = null;
public function __construct(Garage $garage, CatalogGiftProvider $provider, User $user)
{
$this->garage = $garage;
$this->provider = $provider;
$this->user = $user;
$this->state = CatalogOrderStatusEnum::PENDING;
$this->gifts = new ArrayCollection();
}
public function description(): string
{
$result = '';
if (count($this->gifts()) > 0) {
$result = $this->gifts()->first()->name();
}
return $result;
}
public function getDescription(): string
{
return $this->description();
}
public function user(): User
{
return $this->user;
}
public function getUser(): User
{
return $this->user();
}
public function setUser(User $user): self
{
$this->user = $user;
return $this;
}
public function garage(): Garage
{
return $this->garage;
}
public function provider(): CatalogGiftProvider
{
return $this->provider;
}
public function getProvider(): CatalogGiftProvider
{
return $this->provider();
}
public function setProvider(CatalogGiftProvider $provider): self
{
$this->provider = $provider;
return $this;
}
public function state(): int
{
/** @var CatalogOrderGift $orderLines */
foreach ($this->gifts() as $orderLines) {
if ($orderLines->isPending()) {
return CatalogOrderStatusEnum::PENDING;
}
}
if ($this->areAllLinesCancelled()) {
return CatalogOrderStatusEnum::CANCELLED;
}
return CatalogOrderStatusEnum::PROCESSED;
}
public function areAllLinesCancelled(): bool
{
/** @var CatalogOrderGift $orderLines */
foreach ($this->gifts() as $orderLines) {
if (!$orderLines->isCancelled()) {
return false;
}
}
return true;
}
public function isPending(): bool
{
return CatalogOrderStatusEnum::PENDING === $this->state();
}
public function isProcessed(): bool
{
return CatalogOrderStatusEnum::PROCESSED === $this->state();
}
public function isCancelled(): bool
{
return CatalogOrderStatusEnum::CANCELLED === $this->state();
}
public function gifts(): Collection
{
return $this->gifts;
}
public function getGifts(): Collection
{
return $this->gifts();
}
public function setGifts(Collection $gifts): self
{
$this->gifts = $gifts;
return $this;
}
public function addGift(CatalogGift $gift, int $units, CatalogSize $size = null): self
{
$this->gifts->add(CatalogOrderGift::makeCatalogGiftOrderLine($this, $gift, $units, $size));
return $this;
}
public function addDeliveryOrderLine(): self
{
$this->gifts->add(CatalogOrderGift::makeDeliveryOrderLine($this, $this->provider));
return $this;
}
public function removeGift(CatalogOrderGift $gift): self
{
$this->gifts->removeElement($gift);
return $this;
}
public function process(): array
{
$processedOrderLinesIds = [];
/** @var CatalogOrderGift $orderLines */
foreach ($this->unprocessedOrderLines() as $orderLine) {
$processedOrderLinesIds[] = $orderLine->getId();
$orderLine->process();
}
$this->processedAt = new \DateTimeImmutable();
$this->state = CatalogOrderStatusEnum::PROCESSED;
return $processedOrderLinesIds;
}
public function processedAt(): ?\DateTimeInterface
{
return $this->processedAt;
}
public function getProcessedAt(): ?\DateTimeInterface
{
return $this->processedAt();
}
public function setProcessedAt(?\DateTimeInterface $processedAt): self
{
$this->processedAt = $processedAt;
return $this;
}
/**
* @throws PointsLimitException
* @throws CatalogOrderCannotBeCancelled
*/
public function cancel(string $reasonToCancelDescription): array
{
if (!$this->canBeCancelled()) {
throw new CatalogOrderCannotBeCancelled();
}
$returnPointMovements = [];
/** @var CatalogOrderGift $orderLine */
foreach ($this->gifts as $orderLine) {
try {
$reasonToCancelDescription = str_replace(['%description%', '%units%'], [$orderLine->name(), $orderLine->units()], $reasonToCancelDescription);
$returnPointMovements[] = $orderLine->cancel($reasonToCancelDescription);
} catch (CatalogOrderLineCannotBeCancelled $e) {
continue;
}
}
$this->state = CatalogOrderStatusEnum::CANCELLED;
$this->cancelledAt = new \DateTimeImmutable();
return $returnPointMovements;
}
public function canBeCancelled(): bool
{
return CatalogOrderStatusEnum::PENDING === $this->state();
}
public function cancelledAt(): ?\DateTimeInterface
{
return $this->cancelledAt;
}
public function getCancelledAt(): ?\DateTimeInterface
{
return $this->cancelledAt();
}
public function setCancelledAt(?\DateTimeInterface $cancelledAt): self
{
$this->cancelledAt = $cancelledAt;
return $this;
}
public function isDeliveryOrder(): bool
{
return 1 !== count($this->gifts()) ? false : $this->gifts()->first()->isDeliveryOrderLine();
}
public function isOrderLinesProcessingAllowed(): bool
{
return CatalogOrderStatusEnum::PENDING === $this->state() && count($this->gifts) > 1 && !$this->hasAtLeastOneDeliveryLine();
}
private function hasAtLeastOneDeliveryLine(): bool
{
/** @var CatalogOrderGift $orderLines */
foreach ($this->gifts() as $orderLine) {
if ($orderLine->isDeliveryOrderLine()) {
return true;
}
}
return false;
}
public function totalUnits(): int
{
$units = 0;
/** @var CatalogOrderGift $gift */
foreach ($this->gifts() as $gift) {
$units += $gift->units();
}
return $units;
}
public function totalPoints(): int
{
$totalPoints = 0;
/** @var CatalogOrderGift $catalogOrderGift */
foreach ($this->gifts as $catalogOrderGift) {
$totalPoints += $catalogOrderGift->totalPoints();
}
return $totalPoints;
}
public function totalPointsWithoutCancelledItems(): int
{
$totalPoints = 0;
/** @var CatalogOrderGift $catalogOrderGift */
foreach ($this->gifts as $catalogOrderGift) {
if (!$catalogOrderGift->isCancelled()) {
$totalPoints += $catalogOrderGift->totalPoints();
}
}
return $totalPoints;
}
public function totalPrice(): Money
{
$totalPrice = new Money(0, new Currency(MiniAbstractBase::DEFAULT_EURO_CURRENCY));
/** @var CatalogOrderGift $catalogOrderGift */
foreach ($this->gifts as $catalogOrderGift) {
$totalPrice = $totalPrice->add($catalogOrderGift->totalPrice());
}
return $totalPrice;
}
public function lineCount(): int
{
return count($this->gifts());
}
public function owner(): User
{
return $this->getGarage()->getOwner();
}
private function unprocessedOrderLines(): array
{
return array_filter($this->gifts()->toArray(), static function (CatalogOrderGift $oderLine) {
return $oderLine->isPending();
});
}
public function __toString(): string
{
return $this->id.' · '.$this->getCreatedAtAsString().' · '.$this->user;
}
}