<?php declare(strict_types=1);
namespace App\Order\Controller;
use App\Customer\Service\BillingAddressService;
use App\Customer\Service\CustomerService;
use App\Customer\Service\DeliveryAddressService;
use App\Delivery\Service\DeliveryScheduleService;
use App\Framework\Controller\APIController;
use App\Framework\Serializer\APISerializerGroup;
use App\Order\Entity\Cart;
use App\Order\Service\CartService;
use App\Product\Service\ProductService;
use App\Stock\Service\StockService;
use DateTime;
use Doctrine\ORM\EntityNotFoundException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
#[Route(path: '/api/carts')]
class CartController extends APIController
{
private CartService $cartService;
private CustomerService $customerService;
private DeliveryAddressService $shippingAddressService;
private DeliveryScheduleService $deliveryScheduleService;
private BillingAddressService $billingAddressService;
private ProductService $productService;
private StockService $stockService;
public function __construct(
CartService $cartService,
CustomerService $customerService,
DeliveryAddressService $shippingAddressService,
DeliveryScheduleService $deliveryScheduleService,
BillingAddressService $billingAddressService,
ProductService $productService,
StockService $stockService
) {
$this->cartService = $cartService;
$this->customerService = $customerService;
$this->shippingAddressService = $shippingAddressService;
$this->deliveryScheduleService = $deliveryScheduleService;
$this->billingAddressService = $billingAddressService;
$this->productService = $productService;
$this->stockService = $stockService;
}
/**
* @return mixed[]
*/
#[Route(path: '/{id}', requirements: ['id' => '\d+'], methods: ['GET'])]
#[APISerializerGroup('cart:default', 'product:list', 'billing_address:default', 'streetaddress:default')]
public function getCart(int $id): array
{
$cart = $this->cartService->getCartById($id);
return $this->createResponse($cart);
}
/**
* @return mixed[]
*/
#[Route(path: '', methods: ['POST'])]
#[APISerializerGroup('cart:default', 'product:list')]
public function createCartAction(): array
{
$cart = $this->cartService->createCart();
return $this->createResponse($cart);
}
/**
* @return mixed[]
*/
#[Route(path: '/{id}', requirements: ['id' => '\d+'], methods: ['PATCH'])]
#[APISerializerGroup('cart:default', 'product:list', 'streetaddress:default', 'billing_address:default')]
public function updateCart(int $id): array
{
$cart = $this->cartService->getCartById($id);
$customerId = $this->getRequestBodyData('customer', false, null);
$customer = $customerId ? $this->customerService->getById($customerId) : null;
$addressId = $this->getRequestBodyData('shippingAddress', false, null);
$address = $addressId ? $this->shippingAddressService->getAddressById($addressId) : null;
$billingAddressId = $this->getRequestBodyData('billingAddress', false, null);
$billingAddress = $billingAddressId ? $this->billingAddressService->getAddressById($billingAddressId) : null;
$schedule = $this->getRequestBodyData('schedule', false, null);
if ($schedule && is_numeric($schedule)) {
$schedule = $this->deliveryScheduleService->getScheduleById($schedule);
} elseif ($schedule) {
$schedule['start'] = new DateTime($schedule['start']);
$schedule['end'] = new DateTime($schedule['end']);
$schedule['date'] = new DateTime($schedule['date']);
}
$orderType = $this->getRequestBodyData('orderType', false, null);
if ($customer !== null) {
$this->cartService->updateCartCustomer($cart, $customer);
}
if ($address !== null) {
$this->cartService->updateCartShippingAddress($cart, $address);
}
if ($billingAddress !== null) {
$this->cartService->updateCartBillingAddress($cart, $billingAddress);
}
if ($schedule) {
if (is_array($schedule)) {
$this->cartService->updateCartDeliveryCustomSchedule($cart, $schedule);
} else {
$this->cartService->updateCartDeliverySchedule($cart, $schedule);
}
}
if ($orderType) {
$this->cartService->updateCartOrderType($cart, $orderType);
}
return $this->createResponse($cart);
}
/**
* @throws EntityNotFoundException
*
* @return mixed[]
*/
#[Route(path: '/{id}', requirements: ['id' => '\d+'], methods: ['PUT'])]
#[APISerializerGroup('cart:default', 'product:list')]
public function addAction(Cart $cart): array
{
$productId = (int) $this->getRequestBodyData('product', true);
$quantity = (int) $this->getRequestBodyData('quantity', true);
$product = $this->productService->getProductById($productId);
// Add the product to the cart
if ($quantity > 0) {
$this->cartService->add($cart, $product, $quantity);
} else {
$this->cartService->remove($cart, $product, -$quantity);
}
return $this->createResponse($cart);
}
/**
* @throws LogicException
* @throws NotNormalizableValueException
*
* @return array<string, mixed>
*/
private function createResponse(Cart $cart): array
{
$result = [];
$result['id'] = $cart->getId();
$result['billingAddress'] = $cart->getBillingAddress();
$result['customer'] = $cart->getCustomer();
$result['shippingAddress'] = $cart->getShippingAddress();
$result['deliveryDate'] = $cart->getDeliveryDate();
$result['deliveryStartTime'] = $cart->getDeliveryStartTime();
$result['deliveryEndTime'] = $cart->getDeliveryEndTime();
$result['items'] = $cart->getItems();
$productsSubTotal = 0.0;
$shippingSubTotal = 0.0;
// Get stock for the products
$result['stock'] = [];
foreach ($cart->getItems() as $item) {
$product = $item->getProduct();
if ($product->getType() === 'shipping') {
$shippingSubTotal += $product->getPrice() * $item->getQuantity();
} else {
$productsSubTotal += $product->getPrice() * $item->getQuantity();
}
$publicSale = $item->getProduct()->getPublicSale();
$stock = $this->stockService->getStockByProduct($product);
if ($stock != null) {
$result['stock'][] = [
'product' => $stock->getProduct()->getId(),
'stock' => $publicSale ? $stock->getCurrentStock() : 0,
];
}
}
// Fetch discounts if any
$discounts = $this->cartService->getDiscountsForOrder($cart);
$discountTotal = 0;
foreach ($discounts as $discount) {
$discountTotal += $discount['amount'];
}
$errors = $this->cartService->getValidationErrors($cart);
// Now calculate the actual total cost
$total = $productsSubTotal + $shippingSubTotal - $discountTotal;
$result['subtotal'] = $productsSubTotal;
$result['shippingCost'] = $shippingSubTotal;
$result['total'] = $total;
$result['discounts'] = $discounts;
$result['errors'] = $errors;
return $result;
}
}