<?php
namespace Whater\Domain\Product\Model;
use BornFree\TacticianDomainEvent\Recorder\ContainsRecordedEvents;
use BornFree\TacticianDomainEvent\Recorder\EventRecorderCapabilities;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Ramsey\Uuid\Uuid;
use Whater\Domain\Common\Exception\InvalidUUIDException;
use Whater\Domain\Product\Event\CartOrderStatusWasUpdated;
use Whater\Domain\Product\Exception\InvalidCartOrderChangeUserException;
use Whater\Domain\Product\Exception\InvalidCartOrderTypeException;
use Whater\Domain\Product\Exception\InvalidOrderStatusException;
use Whater\Domain\User\Model\OrganizationLicense;
use Whater\Domain\User\Model\User;
/**
* Class CartOrder
*
* @package Whater\Domain\Product\Model
*/
class CartOrder implements ContainsRecordedEvents
{
use EventRecorderCapabilities;
public const ORDER_STATUS_PENDING = "PENDING";
public const ORDER_STATUS_PAID = "PAID";
public const ORDER_STATUS_INVALID_PAID = "INVALID_PAID";
public const ORDER_STATUS_RETURNED = "RETURNED";
public const ORDER_STATUS_CANCELED = "CANCELED";
public const ORDER_STATUS_PARTIAL_RETURNED = "PARTIAL_RETURNED";
public const ORDER_STATUS_ABANDONED = "ABANDONED";
public const ORDER_TYPE_LICENSE = 'ORDER_TYPE_LICENSE';
public const ORDER_TYPE_WHATERCOINS = 'ORDER_TYPE_WHATERCOINS';
public const ORDER_TYPE_WHATER_REFILLS = 'ORDER_TYPE_WHATER_REFILLS';
public const ORDER_TYPE_CATALOG_PRODUCT = 'ORDER_TYPE_CATALOG_PRODUCT';
public const WHATER_REFILL_STATUS_PENDING = 'WHATER_REFILL_STATUS_PENDING';
public const WHATER_REFILL_STATUS_SENT = 'WHATER_REFILL_STATUS_SENT';
public const WHATER_REFILL_STATUS_SENT_CONFIRMED = 'WHATER_REFILL_STATUS_SENT_CONFIRMED';
public const WHATER_REFILL_STATUS_NACK = 'WHATER_REFILL_STATUS_NACK';
public const WHATER_REFILL_STATUS_ACK = 'WHATER_REFILL_STATUS_ACK';
/**
* @var string
*/
private $uuid;
/**
* @var string
*/
protected $cartOrderNumber;
/**
* @var string
*/
private $cartOrderType;
/**
* @var string
*/
private $status;
/**
* @var string
*/
private $whaterRefillStatus;
/**
* @var int
*/
private $numberOfWhaterRefillPurchased;
/**
* @var int
*/
private $numberOfWhaterRefillPendingUse;
/**
* @var array
*/
private $datesOfWhaterRefillUses;
/**
* @var string
*/
private $invoiceNumber;
/**
* @var float
*/
private $totalWhatercoins;
/**
* @var float
*/
private $totalEuros;
/**
* @var float
*/
private $totalShippingCost;
/**
* @var string
*/
private $shippingCountry;
/**
* @var string
*/
private $shippingAddress;
/**
* @var string
*/
private $shippingPostalCode;
/**
* @var string
*/
private $notes;
/**
* @var User
*/
private $user;
/**
* @var OrganizationLicense
*/
private $organizationLicense;
/**
* @var Collection
*/
private $cartOrderItems;
/**
* @var \DateTime
*/
private $invoiceDate;
/**
* @var \DateTime
*/
private $purchaseDate;
/**
* @var \DateTime
*/
private $createdAt;
/**
* @var null|\DateTime
*/
private $updatedAt;
/**
* @var array
*/
private $stripePaymentIntent;
/**
* @var array
*/
private $stripeSubscription;
/**
* @param string $CartOrder
*/
public function __construct(
string $cartOrderId = null,
string $cartOrderType,
string $cartOrderNumber,
User $user = null,
string $notes = null,
OrganizationLicense $organizationLicense = null
) {
try {
$this->uuid = Uuid::fromString($cartOrderId ?: Uuid::uuid4())->toString();
} catch (\InvalidArgumentException $e) {
throw new InvalidUUIDException();
}
switch ($cartOrderType) {
case CartOrder::ORDER_TYPE_LICENSE:
$this->cartOrderType = $cartOrderType;
$this->organizationLicense = $organizationLicense;
if ($organizationLicense == null) {
throw new InvalidCartOrderTypeException();
}
break;
case CartOrder::ORDER_TYPE_CATALOG_PRODUCT:
case CartOrder::ORDER_TYPE_WHATER_REFILLS:
case CartOrder::ORDER_TYPE_WHATERCOINS:
$this->cartOrderType = $cartOrderType;
break;
default:
throw new InvalidCartOrderTypeException();
}
$this->notes = $notes;
$this->user = $user;
$this->cartOrderNumber = $cartOrderNumber;
$this->status = CartOrder::ORDER_STATUS_PENDING;
$this->cartOrderItems = new ArrayCollection();
$this->totalEuros = 0;
$this->totalWhatercoins = 0;
$this->totalShippingCost = 0;
$this->numberOfWhaterRefillPurchased = 0;
$this->numberOfWhaterRefillPendingUse = 0;
$this->createdAt = new \DateTime();
$this->updatedAt = new \DateTime();
}
public function updateStatus(
string $status
) {
if ($this->status() == CartOrder::ORDER_STATUS_PENDING) {
switch ($status) {
case CartOrder::ORDER_STATUS_PAID:
$this->status = $status;
$this->invoiceDate = new \DateTime();
$this->record(new CartOrderStatusWasUpdated($this));
break;
case CartOrder::ORDER_STATUS_INVALID_PAID:
$this->status = $status;
$this->record(new CartOrderStatusWasUpdated($this));
break;
case CartOrder::ORDER_STATUS_ABANDONED:
$this->status = $status;
$this->record(new CartOrderStatusWasUpdated($this));
break;
case CartOrder::ORDER_STATUS_PENDING:
break;
default:
throw new InvalidOrderStatusException();
}
} else if ($this->status() == CartOrder::ORDER_STATUS_INVALID_PAID) {
switch ($status) {
case CartOrder::ORDER_STATUS_PAID:
$this->status = $status;
$this->invoiceDate = new \DateTime();
$this->record(new CartOrderStatusWasUpdated($this));
break;
case CartOrder::ORDER_STATUS_INVALID_PAID:
$this->status = $status;
$this->record(new CartOrderStatusWasUpdated($this));
break;
case CartOrder::ORDER_STATUS_ABANDONED:
$this->status = $status;
$this->record(new CartOrderStatusWasUpdated($this));
break;
case CartOrder::ORDER_STATUS_PENDING:
break;
default:
throw new InvalidOrderStatusException();
}
} else if ($this->status() == CartOrder::ORDER_STATUS_PAID) {
switch ($status) {
case CartOrder::ORDER_STATUS_PARTIAL_RETURNED:
case CartOrder::ORDER_STATUS_RETURNED:
case CartOrder::ORDER_STATUS_CANCELED:
$this->status = $status;
$this->record(new CartOrderStatusWasUpdated($this));
break;
default:
throw new InvalidOrderStatusException();
}
} else if ($this->status() == CartOrder::ORDER_STATUS_PARTIAL_RETURNED) {
switch ($status) {
case CartOrder::ORDER_STATUS_PARTIAL_RETURNED:
case CartOrder::ORDER_STATUS_RETURNED:
$this->status = $status;
$this->record(new CartOrderStatusWasUpdated($this));
break;
default:
throw new InvalidOrderStatusException();
}
}
$this->updatedAt = new \DateTime();
return $this;
}
public function updateShippingData(
float $shippingCost = 0,
string $shippingCountry = null,
string $shippingAddress = null,
string $shippingPostalCode = null
) {
$this->totalShippingCost = $shippingCost;
$this->shippingCountry = $shippingCountry;
$this->shippingAddress = $shippingAddress;
$this->shippingPostalCode = $shippingPostalCode;
}
public function updateWhaterRefillsData(
int $numberOfWhaterRefillPendingUse,
array $datesOfWhaterRefillUses
) {
$this->numberOfWhaterRefillPendingUse = $numberOfWhaterRefillPendingUse;
$this->datesOfWhaterRefillUses = $datesOfWhaterRefillUses;
$this->updatedAt = new \DateTime();
}
public function updateTotal(
float $totalEuros = null,
float $totalWhatercoins = null
) {
$acceptWhatercoins = true;
$cartOrderItemsWhatercoins = 0;
if ($totalEuros == null) {
$t = 0;
foreach ($this->cartOrderItems() as $cartOrderItem) {
if ($cartOrderItem->product()->productType() == Product::PRODUCT_TYPE_WATER_SUPPLY) {
$cartOrderItemsWhatercoins += $cartOrderItem->pricePerUnit() * $cartOrderItem->units();
$acceptWhatercoins = true;
} else {
$t += $cartOrderItem->pricePerUnit() * $cartOrderItem->units();
if (!$cartOrderItem->product()->acceptWhatercoins()) {
$acceptWhatercoins = false;
} else {
if ($cartOrderItem->product()->productType() == Product::PRODUCT_TYPE_WATER_SUPPLY) {
$cartOrderItemsWhatercoins += $cartOrderItem->pricePerUnit() * $cartOrderItem->units();
} else if (array_key_exists('whatercoins_for_discount', $cartOrderItem->meta())) {
$cartOrderItemsWhatercoins += $cartOrderItem->meta()['whatercoins_for_discount'] * $cartOrderItem->units();
}
}
}
}
$this->totalEuros = $t;
} else {
$this->totalEuros = $totalEuros;
}
if ($acceptWhatercoins) {
if ($totalWhatercoins == null) {
$this->totalWhatercoins = $cartOrderItemsWhatercoins;
} else {
$this->totalWhatercoins = $totalWhatercoins;
}
} else {
$this->totalWhatercoins = 0;
}
$this->updatedAt = new \DateTime();
return $this;
}
public function updateStripeInfo(
array $stripePaymentIntent,
array $stripeSubscription = null
) {
$this->stripePaymentIntent = $stripePaymentIntent;
$this->stripeSubscription = $stripeSubscription;
}
public function updatePurchaseDate(
\DateTime $purchaseDate
) {
if ($this->purchaseDate == null) {
$this->purchaseDate = $purchaseDate;
}
}
public function updateWhaterRefillStatus(
string $whaterRefillStatus
) {
if ($this->cartOrderType() == CartOrder::ORDER_TYPE_WHATER_REFILLS) {
switch ($whaterRefillStatus) {
case CartOrder::WHATER_REFILL_STATUS_PENDING:
case CartOrder::WHATER_REFILL_STATUS_SENT:
case CartOrder::WHATER_REFILL_STATUS_SENT_CONFIRMED:
case CartOrder::WHATER_REFILL_STATUS_ACK:
case CartOrder::WHATER_REFILL_STATUS_NACK:
$this->whaterRefillStatus = $whaterRefillStatus;
}
}
}
public function setUser(User $user)
{
if ($this->user == null) {
$this->user = $user;
} else {
throw new InvalidCartOrderChangeUserException();
}
$this->updatedAt = new \DateTime();
}
/**
* @return string
*/
public function id(): string
{
return $this->uuid;
}
/**
* @return string
*/
public function cartOrderNumber(): string
{
return $this->cartOrderNumber;
}
/**
* @return string
*/
public function cartOrderType(): string
{
return $this->cartOrderType;
}
public function cartItem(int $index)
{
if (count($this->cartOrderItems()) >= $index) {
return $this->cartOrderItems()->get($index);
}
return null;
}
public function cartItemValoration(int $index): ?ProductValoration
{
return $this->cartItem($index)->productValoration();
}
/**
* @return string
*/
public function status(): string
{
return $this->status;
}
/**
* @return string
*/
public function invoiceNumber(): ?string
{
return $this->invoiceNumber;
}
/**
* @return User
*/
public function user(): ?User
{
return $this->user;
}
/**
* @return OrganizationLicense
*/
public function organizationLicense(): ?OrganizationLicense
{
return $this->organizationLicense;
}
/**
* @return array
*/
public function stripePaymentIntent(): ?array
{
return $this->stripePaymentIntent;
}
/**
* @return array
*/
public function stripeSubscription(): ?array
{
return $this->stripeSubscription;
}
/**
* @return float
*/
public function totalEuros(): float
{
return $this->totalEuros;
}
/**
* @return float
*/
public function totalShippingCost(): float
{
return $this->totalShippingCost;
}
/**
* @return string
*/
public function shippingCountry(): ?string
{
return $this->shippingCountry;
}
/**
* @return string
*/
public function shippingAddress(): string
{
return $this->shippingAddress;
}
/**
* @return string
*/
public function shippingPostalCode(): string
{
return $this->shippingPostalCode;
}
public function shippingItems(): array
{
$shippingItems = [];
foreach ($this->cartOrderItems() as $cartOrderItem) {
$product = $cartOrderItem->product();
$whaterCompany = $product->whaterOrganization();
if (!array_key_exists($whaterCompany->slug(), $shippingItems)) {
$shippingItems[$whaterCompany->slug()] = [];
}
$shippingItem = [
'isShippable' => $product->isShippable(),
'requiresShippingCosts' => $product->requiresShippingCosts(),
'productWeight' => $product->productWeight(),
'productLength' => $product->productLength(),
'productWidth' => $product->productWidth(),
'productHeight' => $product->productHeight(),
'itemDescription' => $cartOrderItem->itemDescription(),
'pricePerUnit' => $cartOrderItem->pricePerUnit(),
'units' => $cartOrderItem->units(),
'marketplaceCountry' => $whaterCompany->marketplaceCountry(),
'marketplacePostalCode' => $whaterCompany->marketplacePostalCode(),
'marketplaceAddress' => $whaterCompany->marketplaceAddress()
];
array_push($shippingItems[$whaterCompany->slug()], $shippingItem);
}
return $shippingItems;
}
/**
* @return float
*/
public function totalWhatercoins(): float
{
return $this->totalWhatercoins;
}
public function taxesEuros(): float
{
$taxes = 0;
foreach ($this->cartOrderItems() as $cartOrderItem) {
$taxesPercent = $cartOrderItem->taxesPercent();
$taxes += round(
($cartOrderItem->pricePerUnit() * $cartOrderItem->units()) * $taxesPercent / (100 + $taxesPercent),
2
);
}
if ($this->totalWhatercoins() > 0) {
// whatercoins do not include taxes!
$taxes = 0;
}
return $taxes;
}
public function subTotalEuros(): float
{
return $this->totalEuros() - $this->taxesEuros();
}
/**
* @return string|null
*/
public function notes(): ?string
{
return $this->notes;
}
/**
* @return Collection
*/
public function cartOrderItems(): Collection
{
return $this->cartOrderItems;
}
/**
* @return string
*/
public function whaterRefillStatus(): ?string
{
return $this->whaterRefillStatus;
}
/**
* @return int|null
*/
public function numberOfWhaterRefillPurchased(): ?int
{
return $this->numberOfWhaterRefillPurchased;
}
/**
* @return int|null
*/
public function numberOfWhaterRefillPendingUse(): ?int
{
return $this->numberOfWhaterRefillPendingUse;
}
/**
* @return array|null
*/
public function datesOfWhaterRefillUses(): ?array
{
return $this->datesOfWhaterRefillUses;
}
/**
* @return \DateTime|null
*/
public function invoiceDate(): ?\DateTime
{
return $this->invoiceDate;
}
/**
* @return \DateTime|null
*/
public function purchaseDate(): ?\DateTime
{
return $this->purchaseDate;
}
/**
* @return \DateTime
*/
public function createdAt(): \DateTime
{
return $this->createdAt;
}
/**
* @return \DateTime|null
*/
public function updatedAt(): ?\DateTime
{
return $this->updatedAt;
}
}