src/Domain/Product/Model/CartOrder.php line 25

Open in your IDE?
  1. <?php
  2. namespace Whater\Domain\Product\Model;
  3. use BornFree\TacticianDomainEvent\Recorder\ContainsRecordedEvents;
  4. use BornFree\TacticianDomainEvent\Recorder\EventRecorderCapabilities;
  5. use Doctrine\Common\Collections\ArrayCollection;
  6. use Doctrine\Common\Collections\Collection;
  7. use Locale;
  8. use Ramsey\Uuid\Uuid;
  9. use Whater\Domain\Common\Exception\InvalidUUIDException;
  10. use Whater\Domain\Product\Event\CartOrderStatusWasUpdated;
  11. use Whater\Domain\Product\Exception\CartOrderShippingItemsUpdateException;
  12. use Whater\Domain\Product\Exception\InvalidCartOrderChangeUserException;
  13. use Whater\Domain\Product\Exception\InvalidCartOrderTypeException;
  14. use Whater\Domain\Product\Exception\InvalidOrderStatusException;
  15. use Whater\Domain\User\Model\OrganizationLicense;
  16. use Whater\Domain\User\Model\User;
  17. /**
  18. * Class CartOrder
  19. *
  20. * @package Whater\Domain\Product\Model
  21. */
  22. class CartOrder implements ContainsRecordedEvents
  23. {
  24. use EventRecorderCapabilities;
  25. public const ORDER_STATUS_PENDING = "PENDING";
  26. public const ORDER_STATUS_PAID = "PAID";
  27. public const ORDER_STATUS_INVALID_PAID = "INVALID_PAID";
  28. public const ORDER_STATUS_RETURNED = "RETURNED";
  29. public const ORDER_STATUS_CANCELED = "CANCELED";
  30. public const ORDER_STATUS_PARTIAL_RETURNED = "PARTIAL_RETURNED";
  31. public const ORDER_STATUS_ABANDONED = "ABANDONED";
  32. public const ORDER_TYPE_LICENSE = 'ORDER_TYPE_LICENSE';
  33. public const ORDER_TYPE_WHATERCOINS = 'ORDER_TYPE_WHATERCOINS';
  34. public const ORDER_TYPE_WHATER_REFILLS = 'ORDER_TYPE_WHATER_REFILLS';
  35. public const ORDER_TYPE_CATALOG_PRODUCT = 'ORDER_TYPE_CATALOG_PRODUCT';
  36. public const WHATER_REFILL_STATUS_PENDING = 'WHATER_REFILL_STATUS_PENDING';
  37. public const WHATER_REFILL_STATUS_SENT = 'WHATER_REFILL_STATUS_SENT';
  38. public const WHATER_REFILL_STATUS_SENT_CONFIRMED = 'WHATER_REFILL_STATUS_SENT_CONFIRMED';
  39. public const WHATER_REFILL_STATUS_NACK = 'WHATER_REFILL_STATUS_NACK';
  40. public const WHATER_REFILL_STATUS_ACK = 'WHATER_REFILL_STATUS_ACK';
  41. /**
  42. * @var string
  43. */
  44. private $uuid;
  45. /**
  46. * @var string
  47. */
  48. protected $cartOrderNumber;
  49. /**
  50. * @var string
  51. */
  52. private $cartOrderType;
  53. /**
  54. * @var string
  55. */
  56. private $status;
  57. /**
  58. * @var string
  59. */
  60. private $whaterRefillStatus;
  61. /**
  62. * @var int
  63. */
  64. private $numberOfWhaterRefillPurchased;
  65. /**
  66. * @var int
  67. */
  68. private $numberOfWhaterRefillPendingUse;
  69. /**
  70. * @var array
  71. */
  72. private $datesOfWhaterRefillUses;
  73. /**
  74. * @var string
  75. */
  76. private $invoiceNumber;
  77. /**
  78. * @var float
  79. */
  80. private $totalWhatercoins;
  81. /**
  82. * @var float
  83. */
  84. private $totalEuros;
  85. /**
  86. * @var float
  87. */
  88. private $totalShippingCost;
  89. /**
  90. * @var string
  91. */
  92. private $shippingCountry;
  93. /**
  94. * @var string
  95. */
  96. private $shippingAddress;
  97. /**
  98. * @var string
  99. */
  100. private $shippingTown;
  101. /**
  102. * @var string
  103. */
  104. private $shippingPostalCode;
  105. /**
  106. * @var string
  107. */
  108. private $shippingMethodName;
  109. /**
  110. * @var array
  111. */
  112. private $shippingItems;
  113. /**
  114. * @var string
  115. */
  116. private $notes;
  117. /**
  118. * @var User
  119. */
  120. private $user;
  121. /**
  122. * @var OrganizationLicense
  123. */
  124. private $organizationLicense;
  125. /**
  126. * @var Collection
  127. */
  128. private $cartOrderItems;
  129. /**
  130. * @var \DateTime
  131. */
  132. private $invoiceDate;
  133. /**
  134. * @var \DateTime
  135. */
  136. private $purchaseDate;
  137. /**
  138. * @var \DateTime
  139. */
  140. private $createdAt;
  141. /**
  142. * @var null|\DateTime
  143. */
  144. private $updatedAt;
  145. /**
  146. * @var array
  147. */
  148. private $stripePaymentIntent;
  149. /**
  150. * @var array
  151. */
  152. private $stripeSubscription;
  153. /**
  154. * @param string $CartOrder
  155. */
  156. public function __construct(
  157. string $cartOrderId = null,
  158. string $cartOrderType,
  159. string $cartOrderNumber,
  160. User $user = null,
  161. string $notes = null,
  162. OrganizationLicense $organizationLicense = null
  163. ) {
  164. try {
  165. $this->uuid = Uuid::fromString($cartOrderId ?: Uuid::uuid4())->toString();
  166. } catch (\InvalidArgumentException $e) {
  167. throw new InvalidUUIDException();
  168. }
  169. switch ($cartOrderType) {
  170. case CartOrder::ORDER_TYPE_LICENSE:
  171. $this->cartOrderType = $cartOrderType;
  172. $this->organizationLicense = $organizationLicense;
  173. if ($organizationLicense == null) {
  174. throw new InvalidCartOrderTypeException();
  175. }
  176. break;
  177. case CartOrder::ORDER_TYPE_CATALOG_PRODUCT:
  178. case CartOrder::ORDER_TYPE_WHATER_REFILLS:
  179. case CartOrder::ORDER_TYPE_WHATERCOINS:
  180. $this->cartOrderType = $cartOrderType;
  181. break;
  182. default:
  183. throw new InvalidCartOrderTypeException();
  184. }
  185. $this->notes = $notes;
  186. $this->user = $user;
  187. $this->cartOrderNumber = $cartOrderNumber;
  188. $this->shippingItems = [];
  189. if ($this->user()->town() != null) {
  190. $this->shippingCountry = $this->user()->town()->country()->isoCode();
  191. $this->shippingTown = $this->user()->town()->name();
  192. }
  193. $this->status = CartOrder::ORDER_STATUS_PENDING;
  194. $this->cartOrderItems = new ArrayCollection();
  195. $this->totalEuros = 0;
  196. $this->totalWhatercoins = 0;
  197. $this->totalShippingCost = 0;
  198. $this->numberOfWhaterRefillPurchased = 0;
  199. $this->numberOfWhaterRefillPendingUse = 0;
  200. $this->createdAt = new \DateTime();
  201. $this->updatedAt = new \DateTime();
  202. }
  203. public function updateStatus(
  204. string $status
  205. ) {
  206. if ($this->status() == CartOrder::ORDER_STATUS_PENDING) {
  207. switch ($status) {
  208. case CartOrder::ORDER_STATUS_PAID:
  209. $this->status = $status;
  210. $this->invoiceDate = new \DateTime();
  211. $this->record(new CartOrderStatusWasUpdated($this));
  212. break;
  213. case CartOrder::ORDER_STATUS_INVALID_PAID:
  214. $this->status = $status;
  215. $this->record(new CartOrderStatusWasUpdated($this));
  216. break;
  217. case CartOrder::ORDER_STATUS_ABANDONED:
  218. $this->status = $status;
  219. $this->record(new CartOrderStatusWasUpdated($this));
  220. break;
  221. case CartOrder::ORDER_STATUS_PENDING:
  222. break;
  223. default:
  224. throw new InvalidOrderStatusException();
  225. }
  226. } else if ($this->status() == CartOrder::ORDER_STATUS_INVALID_PAID) {
  227. switch ($status) {
  228. case CartOrder::ORDER_STATUS_PAID:
  229. $this->status = $status;
  230. $this->invoiceDate = new \DateTime();
  231. $this->record(new CartOrderStatusWasUpdated($this));
  232. break;
  233. case CartOrder::ORDER_STATUS_INVALID_PAID:
  234. $this->status = $status;
  235. $this->record(new CartOrderStatusWasUpdated($this));
  236. break;
  237. case CartOrder::ORDER_STATUS_ABANDONED:
  238. $this->status = $status;
  239. $this->record(new CartOrderStatusWasUpdated($this));
  240. break;
  241. case CartOrder::ORDER_STATUS_PENDING:
  242. break;
  243. default:
  244. throw new InvalidOrderStatusException();
  245. }
  246. } else if ($this->status() == CartOrder::ORDER_STATUS_PAID) {
  247. switch ($status) {
  248. case CartOrder::ORDER_STATUS_PARTIAL_RETURNED:
  249. case CartOrder::ORDER_STATUS_RETURNED:
  250. case CartOrder::ORDER_STATUS_CANCELED:
  251. $this->status = $status;
  252. $this->record(new CartOrderStatusWasUpdated($this));
  253. break;
  254. default:
  255. throw new InvalidOrderStatusException();
  256. }
  257. } else if ($this->status() == CartOrder::ORDER_STATUS_PARTIAL_RETURNED) {
  258. switch ($status) {
  259. case CartOrder::ORDER_STATUS_PARTIAL_RETURNED:
  260. case CartOrder::ORDER_STATUS_RETURNED:
  261. $this->status = $status;
  262. $this->record(new CartOrderStatusWasUpdated($this));
  263. break;
  264. default:
  265. throw new InvalidOrderStatusException();
  266. }
  267. }
  268. $this->updatedAt = new \DateTime();
  269. return $this;
  270. }
  271. public function updateShippingData(
  272. float $shippingCost = 0,
  273. string $shippingCountry = null,
  274. string $shippingAddress = null,
  275. string $shippingTown = null,
  276. string $shippingPostalCode = null,
  277. string $shippingMethodName = null
  278. ) {
  279. $this->totalShippingCost = $shippingCost;
  280. $this->shippingCountry = $shippingCountry;
  281. $this->shippingAddress = $shippingAddress;
  282. $this->shippingTown = $shippingTown;
  283. $this->shippingPostalCode = $shippingPostalCode;
  284. $this->shippingMethodName = $shippingMethodName;
  285. }
  286. public function updateWhaterRefillsData(
  287. int $numberOfWhaterRefillPendingUse,
  288. array $datesOfWhaterRefillUses
  289. ) {
  290. $this->numberOfWhaterRefillPendingUse = $numberOfWhaterRefillPendingUse;
  291. $this->datesOfWhaterRefillUses = $datesOfWhaterRefillUses;
  292. $this->updatedAt = new \DateTime();
  293. }
  294. public function updateTotal(
  295. float $totalEuros = null,
  296. float $totalWhatercoins = null
  297. ) {
  298. $acceptWhatercoins = true;
  299. $cartOrderItemsWhatercoins = 0;
  300. if ($totalEuros == null) {
  301. $t = 0;
  302. foreach ($this->cartOrderItems() as $cartOrderItem) {
  303. if ($cartOrderItem->product()->productType() == Product::PRODUCT_TYPE_WATER_SUPPLY) {
  304. $cartOrderItemsWhatercoins += $cartOrderItem->pricePerUnit() * $cartOrderItem->units();
  305. $acceptWhatercoins = true;
  306. } else {
  307. $t += $cartOrderItem->pricePerUnit() * $cartOrderItem->units();
  308. if (!$cartOrderItem->product()->acceptWhatercoins()) {
  309. $acceptWhatercoins = false;
  310. } else {
  311. if ($cartOrderItem->product()->productType() == Product::PRODUCT_TYPE_WATER_SUPPLY) {
  312. $cartOrderItemsWhatercoins += $cartOrderItem->pricePerUnit() * $cartOrderItem->units();
  313. } else if (array_key_exists('whatercoins_for_discount', $cartOrderItem->meta())) {
  314. $cartOrderItemsWhatercoins += $cartOrderItem->meta()['whatercoins_for_discount'] * $cartOrderItem->units();
  315. }
  316. }
  317. }
  318. }
  319. $this->totalEuros = $t;
  320. } else {
  321. $this->totalEuros = $totalEuros;
  322. }
  323. if ($acceptWhatercoins) {
  324. if ($totalWhatercoins == null) {
  325. $this->totalWhatercoins = $cartOrderItemsWhatercoins;
  326. } else {
  327. $this->totalWhatercoins = $totalWhatercoins;
  328. }
  329. } else {
  330. $this->totalWhatercoins = 0;
  331. }
  332. $this->updatedAt = new \DateTime();
  333. return $this;
  334. }
  335. public function updateStripeInfo(
  336. array $stripePaymentIntent,
  337. array $stripeSubscription = null
  338. ) {
  339. $this->stripePaymentIntent = $stripePaymentIntent;
  340. $this->stripeSubscription = $stripeSubscription;
  341. }
  342. public function updatePurchaseDate(
  343. \DateTime $purchaseDate
  344. ) {
  345. if ($this->purchaseDate == null) {
  346. $this->purchaseDate = $purchaseDate;
  347. }
  348. }
  349. public function updateWhaterRefillStatus(
  350. string $whaterRefillStatus
  351. ) {
  352. if ($this->cartOrderType() == CartOrder::ORDER_TYPE_WHATER_REFILLS) {
  353. switch ($whaterRefillStatus) {
  354. case CartOrder::WHATER_REFILL_STATUS_PENDING:
  355. case CartOrder::WHATER_REFILL_STATUS_SENT:
  356. case CartOrder::WHATER_REFILL_STATUS_SENT_CONFIRMED:
  357. case CartOrder::WHATER_REFILL_STATUS_ACK:
  358. case CartOrder::WHATER_REFILL_STATUS_NACK:
  359. $this->whaterRefillStatus = $whaterRefillStatus;
  360. }
  361. }
  362. }
  363. public function setUser(User $user)
  364. {
  365. if ($this->user == null) {
  366. $this->user = $user;
  367. } else {
  368. throw new InvalidCartOrderChangeUserException();
  369. }
  370. $this->updatedAt = new \DateTime();
  371. }
  372. /**
  373. * @return string
  374. */
  375. public function id(): string
  376. {
  377. return $this->uuid;
  378. }
  379. /**
  380. * @return string
  381. */
  382. public function cartOrderNumber(): string
  383. {
  384. return $this->cartOrderNumber;
  385. }
  386. /**
  387. * @return string
  388. */
  389. public function cartOrderType(): string
  390. {
  391. return $this->cartOrderType;
  392. }
  393. public function cartItem(int $index)
  394. {
  395. if (count($this->cartOrderItems()) >= $index) {
  396. return $this->cartOrderItems()->get($index);
  397. }
  398. return null;
  399. }
  400. public function cartItemValoration(int $index): ?ProductValoration
  401. {
  402. return $this->cartItem($index)->productValoration();
  403. }
  404. /**
  405. * @return string
  406. */
  407. public function status(): string
  408. {
  409. return $this->status;
  410. }
  411. /**
  412. * @return string
  413. */
  414. public function invoiceNumber(): ?string
  415. {
  416. return $this->invoiceNumber;
  417. }
  418. /**
  419. * @return User
  420. */
  421. public function user(): ?User
  422. {
  423. return $this->user;
  424. }
  425. /**
  426. * @return OrganizationLicense
  427. */
  428. public function organizationLicense(): ?OrganizationLicense
  429. {
  430. return $this->organizationLicense;
  431. }
  432. /**
  433. * @return array
  434. */
  435. public function stripePaymentIntent(): ?array
  436. {
  437. return $this->stripePaymentIntent;
  438. }
  439. /**
  440. * @return array
  441. */
  442. public function stripeSubscription(): ?array
  443. {
  444. return $this->stripeSubscription;
  445. }
  446. /**
  447. * @return float
  448. */
  449. public function totalEuros(): float
  450. {
  451. return $this->totalEuros;
  452. }
  453. /**
  454. * @return float
  455. */
  456. public function totalShippingCost(): float
  457. {
  458. return $this->totalShippingCost;
  459. }
  460. /**
  461. * @return string
  462. */
  463. public function completeShippingAddress(): ?string
  464. {
  465. $completeShippingAddress = [];
  466. if ($this->shippingAddress() != null) {
  467. $completeShippingAddress[] = $this->shippingAddress();
  468. }
  469. if ($this->shippingPostalCode() != null) {
  470. $completeShippingAddress[] = $this->shippingPostalCode();
  471. }
  472. if ($this->shippingCountry() != null) {
  473. $completeShippingAddress[] = Locale::getDisplayRegion('-' . $this->shippingCountry(), 'es');
  474. }
  475. return implode(', ', $completeShippingAddress);
  476. }
  477. /**
  478. * @return string
  479. */
  480. public function shippingCountry(): ?string
  481. {
  482. return $this->shippingCountry;
  483. }
  484. /**
  485. * @return string
  486. */
  487. public function shippingAddress(): ?string
  488. {
  489. return $this->shippingAddress;
  490. }
  491. /**
  492. * @return string
  493. */
  494. public function shippingPostalCode(): ?string
  495. {
  496. return $this->shippingPostalCode;
  497. }
  498. /**
  499. * @return string
  500. */
  501. public function shippingTown(): ?string
  502. {
  503. return $this->shippingTown;
  504. }
  505. /**
  506. * @return string
  507. */
  508. public function shippingMethodName(): ?string
  509. {
  510. return $this->shippingMethodName;
  511. }
  512. public function shippingItems(): array
  513. {
  514. return $this->shippingItems;
  515. }
  516. public function updateShippingItems(array $shippingItems = null): array
  517. {
  518. if ($this->status() != CartOrder::ORDER_STATUS_PENDING) {
  519. throw new CartOrderShippingItemsUpdateException();
  520. }
  521. if ($shippingItems != null) {
  522. // Actualizamos shippingItems
  523. $this->shippingItems = $shippingItems;
  524. } else {
  525. // Calculamos shippingItems
  526. $shippingItems = [];
  527. foreach ($this->cartOrderItems() as $cartOrderItem) {
  528. $product = $cartOrderItem->product();
  529. $whaterOrganization = $product->whaterOrganization();
  530. if ($whaterOrganization != null) {
  531. if (!array_key_exists($whaterOrganization->slug(), $shippingItems)) {
  532. $shippingItems[$whaterOrganization->slug()] = [];
  533. }
  534. $shippingItem = [
  535. 'isShippable' => $product->isShippable(),
  536. 'requiresShippingCosts' => $product->requiresShippingCosts(),
  537. 'productWeight' => $product->productWeight(),
  538. 'productLength' => $product->productLength(),
  539. 'productWidth' => $product->productWidth(),
  540. 'productHeight' => $product->productHeight(),
  541. 'itemDescription' => $cartOrderItem->itemDescription(),
  542. 'pricePerUnit' => $cartOrderItem->pricePerUnit(),
  543. 'units' => $cartOrderItem->units(),
  544. 'marketplaceCountry' => $whaterOrganization->marketplaceCountry(),
  545. 'marketplacePostalCode' => $whaterOrganization->marketplacePostalCode(),
  546. 'marketplaceAddress' => $whaterOrganization->marketplaceAddress()
  547. ];
  548. array_push($shippingItems[$whaterOrganization->slug()], $shippingItem);
  549. }
  550. }
  551. $this->shippingItems = $shippingItems;
  552. }
  553. return $shippingItems;
  554. }
  555. /**
  556. * @return float
  557. */
  558. public function totalWhatercoins(): float
  559. {
  560. return $this->totalWhatercoins;
  561. }
  562. public function taxesEuros(): float
  563. {
  564. $taxes = 0;
  565. foreach ($this->cartOrderItems() as $cartOrderItem) {
  566. $taxesPercent = $cartOrderItem->taxesPercent();
  567. $taxes += round(
  568. ($cartOrderItem->pricePerUnit() * $cartOrderItem->units()) * $taxesPercent / (100 + $taxesPercent),
  569. 2
  570. );
  571. }
  572. if ($this->totalWhatercoins() > 0) {
  573. // whatercoins do not include taxes!
  574. $taxes = 0;
  575. }
  576. return $taxes;
  577. }
  578. public function subTotalEuros(): float
  579. {
  580. return $this->totalEuros() - $this->taxesEuros();
  581. }
  582. /**
  583. * @return string|null
  584. */
  585. public function notes(): ?string
  586. {
  587. return $this->notes;
  588. }
  589. /**
  590. * @return Collection
  591. */
  592. public function cartOrderItems(): Collection
  593. {
  594. return $this->cartOrderItems;
  595. }
  596. /**
  597. * @return string
  598. */
  599. public function whaterRefillStatus(): ?string
  600. {
  601. return $this->whaterRefillStatus;
  602. }
  603. /**
  604. * @return int|null
  605. */
  606. public function numberOfWhaterRefillPurchased(): ?int
  607. {
  608. return $this->numberOfWhaterRefillPurchased;
  609. }
  610. /**
  611. * @return int|null
  612. */
  613. public function numberOfWhaterRefillPendingUse(): ?int
  614. {
  615. return $this->numberOfWhaterRefillPendingUse;
  616. }
  617. /**
  618. * @return array|null
  619. */
  620. public function datesOfWhaterRefillUses(): ?array
  621. {
  622. return $this->datesOfWhaterRefillUses;
  623. }
  624. /**
  625. * @return \DateTime|null
  626. */
  627. public function invoiceDate(): ?\DateTime
  628. {
  629. return $this->invoiceDate;
  630. }
  631. /**
  632. * @return \DateTime|null
  633. */
  634. public function purchaseDate(): ?\DateTime
  635. {
  636. return $this->purchaseDate;
  637. }
  638. /**
  639. * @return \DateTime
  640. */
  641. public function createdAt(): \DateTime
  642. {
  643. return $this->createdAt;
  644. }
  645. /**
  646. * @return \DateTime|null
  647. */
  648. public function updatedAt(): ?\DateTime
  649. {
  650. return $this->updatedAt;
  651. }
  652. }