src/Order/Service/CartService.php line 53

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace App\Order\Service;
  3. use App\Core\Service\ParameterService;
  4. use App\Customer\Entity\BillingAddress;
  5. use App\Customer\Entity\Customer;
  6. use App\Customer\Entity\StreetAddress;
  7. use App\Delivery\Entity\DeliverySchedule;
  8. use App\Delivery\Service\DeliveryScheduleService;
  9. use App\Delivery\Service\DeliveryZoneService;
  10. use App\Order\Entity\Cart;
  11. use App\Order\Entity\CartItem;
  12. use App\Order\Entity\Order;
  13. use App\Order\Exception\InvalidAddressException;
  14. use App\Order\Repository\CartRepository;
  15. use App\Product\Entity\Product;
  16. use App\Stock\Service\StockService;
  17. use Doctrine\ORM\EntityNotFoundException;
  18. use Exception;
  19. class CartService
  20. {
  21.     private CartRepository $repository;
  22.     private ParameterService $parameterService;
  23.     private DeliveryZoneService $deliveryZoneService;
  24.     private StockService $stockService;
  25.     private DeliveryScheduleService $deliveryScheduleService;
  26.     public function __construct(
  27.         CartRepository $repository,
  28.         ParameterService $parameterService,
  29.         DeliveryZoneService $deliveryZoneService,
  30.         DeliveryScheduleService $deliveryScheduleService,
  31.         StockService $stockService
  32.     ) {
  33.         $this->repository $repository;
  34.         $this->parameterService $parameterService;
  35.         $this->deliveryZoneService $deliveryZoneService;
  36.         $this->deliveryScheduleService $deliveryScheduleService;
  37.         $this->stockService $stockService;
  38.     }
  39.     public function getCartById(int $id): Cart
  40.     {
  41.         $cart $this->repository->findOneBy(['id' => $id]);
  42.         if ($cart === null) {
  43.             throw EntityNotFoundException::fromClassNameAndIdentifier(Cart::class, [$id]);
  44.         }
  45.         return $cart;
  46.     }
  47.     public function getCartsByCustomer(Customer $customer): array
  48.     {
  49.         return $this->repository->findBy([
  50.             'customer' => $customer
  51.         ]);
  52.     }
  53.     public function createCart(): Cart
  54.     {
  55.         $cart = new Cart();
  56.         $this->repository->add($carttrue);
  57.         return $cart;
  58.     }
  59.     public function updateCartCustomer(Cart $cartCustomer $customer): void
  60.     {
  61.         $cart->setCustomer($customer);
  62.         $cart->setShippingAddress(null);
  63.         $this->repository->flush();
  64.     }
  65.     public function updateCartShippingAddress(Cart $cartStreetAddress $address): void
  66.     {
  67.         $customer $cart->getCustomer();
  68.         if ($customer === null) {
  69.             throw new InvalidAddressException();
  70.         }
  71.         if ($customer !== $address->getCustomer()) {
  72.             throw new InvalidAddressException();
  73.         }
  74.         $cart->setShippingAddress($address);
  75.         // reset schedule
  76.         $cart->setDeliveryDate(null);
  77.         $cart->setDeliveryStartTime(null);
  78.         $cart->setDeliveryEndTime(null);
  79.         // update the shipping cost
  80.         $this->updateShippingCost($cart);
  81.         $this->repository->flush();
  82.     }
  83.     public function updateCartBillingAddress(Cart $cartBillingAddress $address): void
  84.     {
  85.         $customer $cart->getCustomer();
  86.         if ($customer === null) {
  87.             throw new InvalidAddressException();
  88.         }
  89.         if ($customer !== $address->getCustomer()) {
  90.             throw new InvalidAddressException();
  91.         }
  92.         $cart->setBillingAddress($address);
  93.         $this->repository->flush();
  94.     }
  95.     public function updateCartDeliverySchedule(Cart $cartDeliverySchedule $deliverySchedule): void
  96.     {
  97.         // TODO: MAke sure this schedule is valid for the current address ??
  98.         $cart->setDeliverySchedule($deliverySchedule);
  99.         $cart->setDeliveryStartTime($deliverySchedule->getStart());
  100.         $cart->setDeliveryEndTime($deliverySchedule->getEnd());
  101.         // calculate the actual delivery date
  102.         $deliveryDate $this->deliveryScheduleService->getActualDateForSchedule($deliverySchedule);
  103.         $cart->setDeliveryDate($deliveryDate);
  104.         $this->repository->flush();
  105.     }
  106.     public function updateCartDeliveryCustomSchedule(Cart $cart, array $schedule): void
  107.     {
  108.         $cart->setDeliverySchedule(null);
  109.         $cart->setDeliveryDate($schedule['date']);
  110.         $cart->setDeliveryStartTime($schedule['start']);
  111.         $cart->setDeliveryEndTime($schedule['end']);
  112.         $this->repository->flush();
  113.     }
  114.     public function updateCartOrderType(Cart $cartstring $orderType): void
  115.     {
  116.         $cart->setOrderType($orderType);
  117.         $this->repository->flush();
  118.     }
  119.     /**
  120.      * Add product to cart.
  121.      */
  122.     public function add(Cart $cartProduct $productint $quantity): void
  123.     {
  124.         // add the item
  125.         $this->addItemInternal($cart$product$quantity);
  126.         // update shipping cost if required
  127.         $this->updateShippingCost($cart);
  128.         // Persist and flush related entities
  129.         $this->repository->flush();
  130.     }
  131.     /**
  132.      * Remove product from Cart.
  133.      */
  134.     public function remove(Cart $cartProduct $productint $quantity): void
  135.     {
  136.         $this->removeItemInternal($cart$product$quantity);
  137.         $this->updateShippingCost($cart);
  138.         // Persist and flush related entities
  139.         $this->repository->flush();
  140.     }
  141.     /**
  142.      * @throws Exception
  143.      *
  144.      * @return array<int, array<string, mixed>>
  145.      */
  146.     public function getDiscountsForOrder(Cart $cart): array
  147.     {
  148.         $discounts = [];
  149.         if ($cart->getOrderType() === Order::OrderTypeSupport) {
  150.             $discounts[] = [
  151.                 'name' => 'Descuento reposición',
  152.                 'amount' => $cart->getSubtotal(),
  153.             ];
  154.         }
  155.         return $discounts;
  156.     }
  157.     /**
  158.      * @return string[]
  159.      */
  160.     public function getValidationErrors(Cart $cart): array
  161.     {
  162.         $errors = [];
  163.         // support orders have no associated costs
  164.         if ($cart->getOrderType() === Order::OrderTypeSupport) {
  165.             return $errors;
  166.         }
  167.         $maxNoBilling $this->parameterService->getParameterValue('order.max_no_billing');
  168.         $minSale $this->parameterService->getParameterValue('order.min_sale');
  169.         $subtotal $cart->getSubtotal();
  170.         $discounts $this->getDiscountsForOrder($cart);
  171.         $discountsTotal 0;
  172.         foreach ($discounts as $discount) {
  173.             $discountsTotal += $discount['amount'];
  174.         }
  175.         $total $subtotal $discountsTotal;
  176.         if ($total >= $maxNoBilling && (!$cart->getBillingAddress() || !$cart->getBillingAddress()->getCuit())) {
  177.             $errors[] = 'error.order.billing_required';
  178.         }
  179.         if ($subtotal $minSale) {
  180.             $errors[] = 'error.order.total_below_minimum_sale';
  181.         }
  182.         // check that products in the cart are in stock
  183.         foreach ($cart->getItems() as $item) {
  184.             $publicSale $item->getProduct()->getPublicSale();
  185.             $stock $this->stockService->getStockByProduct($item->getProduct());
  186.             if ($stock && (!$publicSale || $item->getQuantity() > $stock->getCurrentStock())) {
  187.                 $errors[] = 'error.order.out_of_stock';
  188.                 break;
  189.             }
  190.         }
  191.         return $errors;
  192.     }
  193.     public function deleteCart(Cart $cart): void
  194.     {
  195.         $this->repository->remove($cart);
  196.         $this->repository->flush();
  197.     }
  198.     private function addItemInternal(Cart $cartProduct $productint $quantity): CartItem
  199.     {
  200.         // Find an existing cart item
  201.         $cartItem $cart->getItem($product);
  202.         // If it wasn't found, create a new one
  203.         if ($cartItem === null) {
  204.             $cartItem = new CartItem();
  205.             $cartItem->setProduct($product);
  206.             $cartItem->setQuantity($quantity);
  207.             // Add it to the cart
  208.             $cart->addItem($cartItem);
  209.         } else {
  210.             $cartItem->setQuantity($quantity $cartItem->getQuantity());
  211.         }
  212.         return $cartItem;
  213.     }
  214.     private function removeItemInternal(Cart $cartProduct $productint $quantity): void
  215.     {
  216.         if ($quantity < -1) {
  217.             return;
  218.         }
  219.         // Find an existing cart item
  220.         $cartItem $cart->getItem($product);
  221.         if ($cartItem === null) {
  222.             return;
  223.         }
  224.         $newQuantity $quantity === -$cartItem->getQuantity() - $quantity;
  225.         // Either update or remove
  226.         if ($newQuantity 0) {
  227.             // Change the quantity on the item itself
  228.             $cartItem->setQuantity($newQuantity);
  229.         } else {
  230.             $cart->removeItem($cartItem);
  231.         }
  232.     }
  233.     private function updateShippingCost(Cart $cart): void
  234.     {
  235.         if ($cart->getShippingAddress() == null) {
  236.             $cart->setShippingCartProduct(null);
  237.             return;
  238.         }
  239.         $freeShippingMinimum $this->parameterService->getParameterValue('order.free_shipping');
  240.         
  241.         $zone $this->deliveryZoneService->getZoneForAddress($cart->getShippingAddress());
  242.         if ($zone == null) {
  243.             $cart->setShippingCartProduct(null);
  244.             return;
  245.         }
  246.         $zoneShippingProduct $zone->getShippingCostProduct();
  247.         $cartShippingProduct $cart->getShippingCartProduct();
  248.         // if products subtotal is over the free shipping threshold, remove the automatic shipping cost product
  249.         if ($cart->getProductsSubtotal() > $freeShippingMinimum) {
  250.             if ($cartShippingProduct != null) {
  251.                 $this->removeItemInternal($cart$cartShippingProduct1);
  252.                 $cart->setShippingCartProduct(null);
  253.             }
  254.         } else {
  255.             // not reached the free threshold
  256.             if ($cartShippingProduct !== $zoneShippingProduct) {
  257.                 if ($cartShippingProduct != null) {
  258.                     $this->removeItemInternal($cart$cartShippingProduct1);
  259.                     $cart->setShippingCartProduct(null);
  260.                 }
  261.                 if ($zoneShippingProduct != null) {
  262.                     $this->addItemInternal($cart$zoneShippingProduct1);
  263.                     $cart->setShippingCartProduct($zoneShippingProduct);
  264.                 }
  265.             }
  266.         }
  267.     }
  268. }