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

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