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