<?php
namespace App\Controller\Website\Site;
use App\Controller\Website\ThemeRenderController;
use App\Entity\Generic\Customer\Address;
use App\Entity\Website\Order\Coupon;
use App\Entity\Website\Order\Order;
use App\Entity\Website\Order\OrderItem;
use App\Entity\Website\Order\OrderStatus;
use App\Form\Website\Panel\Order\Coupon\CouponType;
use App\Form\Website\Site\ApplyCouponType;
use App\Form\Website\Site\CheckoutType;
use App\Form\Website\Site\PaymentType;
use App\Service\Payment\PaymentGatewayManager;
use App\Service\Payment\ZarinpalGateway;
use App\Service\Website\Order\OrderCalculatorService;
use App\Service\Website\Setting\SettingService;
use App\Service\WebsiteManager;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class CartController extends ThemeRenderController
{
private SettingService $settingService;
public function __construct(WebsiteManager $shopManager, EntityManagerInterface $entityManager, SettingService $settingService)
{
parent::__construct($shopManager, $entityManager);
$this->settingService = $settingService;
}
#[Route('/cart', name: 'app_shop_site_cart')]
public function showCart(Request $request, EntityManagerInterface $em, OrderCalculatorService $orderCalculatorService): Response
{
$customer = $this->getUser();
$cartStatus = $em->getRepository(OrderStatus::class)->findOneBy(['code' => 'cart']);
$order = $em->getRepository(Order::class)->findOneBy([
'customer' => $customer,
'active' => 1,
'deletedAt' => null,
'status' => $cartStatus,
]);
if ($order) {
// $subtotal = array_reduce($order->getOrderItems()->toArray(), fn($carry, $item) => $carry + $item->getPrice() * $item->getQuantity(), 0);
foreach ($order->getOrderItems() as $orderItem) {
$orderedTotal = $orderItem->getQuantity();
$product = $orderItem->getProduct();
if ($product->isVariablePriced()) {
$productPrice = $orderItem->getProductPrice(); // فرض: هر OrderItem به ProductPrice وصله
if ($productPrice && $productPrice->getInStock() !== null) {
if ($orderedTotal > $productPrice->getInStock()) {
$orderItem->setQuantity($productPrice->getInStock());
}
}
} else {
if ($product->getStock() !== null && $orderedTotal > $product->getStock()) {
$orderItem->setQuantity($product->getStock());
}
}
}
$em->flush();
foreach ($order->getOrderItems() as $orderItem) {
if ($orderItem->getQuantity() == 0) {
$order->removeOrderItem($orderItem);
}
}
$em->flush();
$orderCalculatorService->calculate($order);
$subtotal = $order->getTotalPrice();
} else {
$subtotal = 0;
}
return $this->renderUserTemplate('cart.html.twig', [
'order' => $order,
'total' => $subtotal ?: null,
]);
}
#[Route('/cart/checkout', name: 'cart_checkout')]
public function checkout(Request $request, EntityManagerInterface $em, OrderCalculatorService $orderCalculatorService): Response
{
$customer = $this->getUser();
$cartStatus = $em->getRepository(OrderStatus::class)->findOneBy(['code' => 'cart']);
$order = $em->getRepository(Order::class)->findOneBy([
'customer' => $customer,
'status' => $cartStatus,
'active' => 1,
'deletedAt' => null,
]);
if (!$order || $order->getOrderItems()->isEmpty()) {
$this->addFlash('warning', 'سبد خرید شما خالی است.');
return $this->redirectToRoute('app_shop_site_cart');
}
$form = $this->createForm(CheckoutType::class, null, [
'website' => $this->shopManager->getShop(),
'customer' => $customer,
]);
$orderCalculatorService->calculate($order);
$subtotal = $order->getTotalPrice();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
if ($form->get('use_new_address')->getData()) {
$newData = $form->get('new_address')->getData();
if (
$newData['city'] == null ||
$newData['state'] == null ||
$newData['address'] == null ||
$newData['postalCode'] == null
) {
$this->addFlash('error', 'آدرس به درستی وارد نشده');
return $this->redirectToRoute('cart_checkout');
}
$address = new Address();
$address->setCity($newData['city']);
$address->setState($newData['state']);
$address->setCustomer($customer);
$address->setAddress($newData['address']);
$address->setPostalCode($newData['postalCode']);
} else {
$address = $form->get('existing_address')->getData();
}
if ($address === null) {
$this->addFlash('error', 'آدرس به درستی وارد نشده است');
return $this->redirectToRoute('cart_checkout');
}
$em->persist($address);
$order->setAddress($address);
$shippingMethod = $form->get('shipping_method')->getData();
$order->setShippingMethod($shippingMethod);
$order->setShippingCost($shippingMethod->getCost());
$orderCalculatorService->calculate($order);
$em->persist($order);
$em->flush();
return $this->redirectToRoute('cart_payment');
}
return $this->renderUserTemplate('checkout.html.twig', [
'order' => $order,
'total' => $subtotal ?: null,
'form' => $form->createView(),
]);
}
#[Route('/cart/payment', name: 'cart_payment')]
public function payment(Request $request, EntityManagerInterface $em, OrderCalculatorService $orderCalculatorService): Response
{
$customer = $this->getUser();
$cartStatus = $em->getRepository(OrderStatus::class)->findOneBy(['code' => 'cart']);
/**
* @var Order $order
*/
$order = $em->getRepository(Order::class)->findOneBy([
'customer' => $customer,
'status' => $cartStatus,
'active' => 1,
'deletedAt' => null,
]);
if (!$order || $order->getOrderItems()->isEmpty()) {
$this->addFlash('warning', 'سبد خرید شما خالی است.');
return $this->redirectToRoute('app_shop_site_cart');
}
// TODO : more than one result
// $orderCalculatorService->validateStockAndCleanCart($order, $em);
$orderCalculatorService->calculate($order);
$couponForm = $this->createForm(ApplyCouponType::class, null, ['coupon' => $order->getCoupon()]);
$couponForm->handleRequest($request);
if ($couponForm->isSubmitted() && $couponForm->isValid()) {
$code = $couponForm->get('code')->getData();
/** @var Coupon|null $coupon */
$coupon = $em->getRepository(Coupon::class)->findOneBy([
'code' => $code,
'website' => $order->getWebsite(),
'active' => 1,
'deletedAt' => null,
]);
if (!$coupon) {
$this->addFlash('danger', 'کد تخفیف نامعتبر است.');
} elseif ($coupon->getStartDate() && $coupon->getStartDate() > new \DateTime()) {
$this->addFlash('danger', 'کد تخفیف هنوز فعال نشده است.');
} elseif ($coupon->getEndDate() && $coupon->getEndDate() < new \DateTime()) {
$this->addFlash('danger', 'کد تخفیف منقضی شده است.');
} elseif ($coupon->getUsedCount() >= $coupon->getMaxUses()) {
$this->addFlash('danger', 'سقف استفاده از این کد تخفیف پر شده است.');
} else {
$order->setCoupon($coupon);
$em->flush();
$orderCalculatorService->calculate($order);
}
return $this->redirectToRoute('cart_payment');
}
$gateWayForm = $this->createForm(PaymentType::class, null, [
'website' => $order->getWebsite(),
'setting_service' => $this->settingService,
'user' => $customer,
]);
$gateWayForm->handleRequest($request);
if ($gateWayForm->isSubmitted() && $gateWayForm->isValid()) {
$order->setPaymentGateway($gateWayForm->get('gateway')->getData());
$em->flush();
return $this->redirectToRoute('cart_start_payment', ['wallet' => $gateWayForm->get('useWallet')->getData()]);
}
$subtotal = $orderCalculatorService->getSubtotal($order);
$shippingCost = $order->getShippingCost();
$discount = $orderCalculatorService->getDiscount($order);
$total = $orderCalculatorService->getTotalInvoice($order);
return $this->renderUserTemplate('payment.html.twig', [
'order' => $order,
'couponForm' => $couponForm->createView(),
'subtotal' => $subtotal, // مجموع قیمت آیتمها
'shippingCost' => $shippingCost, // هزینه ارسال
'discount' => $discount, // مقدار تخفیف
'total' => $total, // قیمت نهایی پرداختی
'gateWayForm' => $gateWayForm->createView(),
]);
}
#[Route('/cart/payment-start', name: 'cart_start_payment')]
public function cart_start_payment(EntityManagerInterface $em, Request $request, SettingService $shopSettingService, ZarinpalGateway $zarinpalGateway)
{
$useWallet = $request->get('wallet', 0);
$customer = $this->getUser();
if ($useWallet) {
$walletAmount = $customer->getWallet();
} else {
$walletAmount = 0;
}
$shop = $this->shopManager->getShop();
$cartStatus = $em->getRepository(OrderStatus::class)->findOneBy(['code' => 'cart']);
$order = $em->getRepository(Order::class)->findOneBy([
'customer' => $customer,
'status' => $cartStatus,
'active' => 1,
'deletedAt' => null,
]);
switch ($order->getPaymentGateway()) {
case 'zarinpal':
$gateway = $zarinpalGateway;
// $gatewayConfig = $shopSettingService->getGatewayConfig($website, 'zarinpal');
$response = $gateway->pay($order, $walletAmount);
if ($response->success) {
$order->setPaymentGateway('zarinpal');
$em->flush();
return $this->redirect($response->redirectUrl);
}else{
$this->addFlash('danger', $response->message ?? 'خطا در شروع پرداخت');
return $this->redirectToRoute('cart_checkout');
}
break;
default:
return $this->redirectToRoute('cart_checkout');
break;
}
}
#[Route('/cart/remove/{id}', name: 'cart_remove_item')]
public function remove_order_item(OrderItem $orderItem, EntityManagerInterface $em)
{
$cartStatus = $em->getRepository(OrderStatus::class)->findOneBy(['code' => 'cart']);
$customer = $this->getUser();
$order = $em->getRepository(Order::class)->findOneBy([
'customer' => $customer,
'status' => $cartStatus,
]);
$order->removeOrderItem($orderItem);
foreach ($orderItem->getOrderItemAttributes() as $orderItemAttribute) {
$em->remove($orderItemAttribute);
}
if (count($order->getOrderItems()) < 1) {
$order->setActive(0);
$order->setDeletedAt(new \DateTime());
}
$em->flush();
return $this->redirectToRoute('app_shop_site_cart');
}
#[Route('/cart/remove-coupon', name: 'cart_remove_coupon')]
public function cart_remove_coupon(EntityManagerInterface $em)
{
$customer = $this->getUser();
$cartStatus = $em->getRepository(OrderStatus::class)->findOneBy(['code' => 'cart']);
$order = $em->getRepository(Order::class)->findOneBy([
'customer' => $customer,
'status' => $cartStatus,
'active' => 1,
'deletedAt' => null,
]);
$order->setCoupon(null);
$em->flush();
return $this->redirectToRoute('cart_payment');
}
}