<?php
namespace App\Controller;
use App\Entity\Produit;
use App\Entity\Commande;
use App\Entity\ProduitVariant;
use App\Service\PanierService;
use App\Entity\CommandeDetails;
use App\Repository\ProduitRepository;
use Doctrine\ORM\EntityManagerInterface;
use App\Repository\ProduitVariantRepository;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Translation\LocaleSwitcher;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class PanierController extends AbstractController
{
public $panierService;
public function __construct(PanierService $panierService)
{
$this->panierService = $panierService;
}
/**
* True only when the product's shop exists and is active.
*/
private function shopIsActive($product): bool
{
try {
$shop = method_exists($product, 'getMagasin') ? $product->getMagasin() : null;
return $shop && method_exists($shop, 'isActive') && $shop->isActive() === true;
} catch (\Throwable $e) {
return false;
}
}
/**
* Look up the delivery fee record for a shop + region via raw SQL.
* Returns ['fee' => float, 'can_deliver' => bool] or null if no record.
*/
private function lookupDeliveryFee(EntityManagerInterface $em, int $shopId, int $regionId): ?array
{
try {
$conn = $em->getConnection();
$row = null;
if (method_exists($conn, 'fetchAssociative')) {
$row = $conn->fetchAssociative(
'SELECT fee, can_deliver FROM magasin_delivery_fee WHERE magasin_id = :m AND region_id = :r',
['m' => $shopId, 'r' => $regionId]
);
} else {
$stmt = $conn->executeQuery(
'SELECT fee, can_deliver FROM magasin_delivery_fee WHERE magasin_id = ? AND region_id = ?',
[$shopId, $regionId]
);
$row = method_exists($stmt, 'fetchAssociative') ? $stmt->fetchAssociative() : $stmt->fetch();
}
if (!$row) return null;
return [
'fee' => isset($row['fee']) ? (float) $row['fee'] : 0.0,
'can_deliver' => isset($row['can_deliver']) ? ((int) $row['can_deliver'] === 1) : false,
];
} catch (\Throwable $e) {
error_log('[Julico lookupDeliveryFee] ' . $e->getMessage());
return null;
}
}
private function unitPriceOf($product): float
{
try {
$sp = method_exists($product, 'getSalePrice') ? $product->getSalePrice() : null;
if ($sp !== null && (float) $sp > 0) return (float) $sp;
$px = method_exists($product, 'getPrix') ? $product->getPrix() : null;
if ($px !== null) return (float) $px;
} catch (\Throwable $e) {}
return 0.0;
}
/**
* Price for a cart line: the VARIANT's own price when a variant is selected,
* otherwise the product's (sale) price.
*/
private function unitPriceOfLine($product, $variant): float
{
try {
if ($variant !== null && method_exists($variant, 'getPrix')) {
$vp = $variant->getPrix();
if ($vp !== null && (float) $vp > 0) {
return (float) $vp;
}
}
} catch (\Throwable $e) {}
return $this->unitPriceOf($product);
}
#[Route('/panier', name: 'app_panier')]
public function index(SessionInterface $session, ProduitRepository $productrepository,
ProduitVariantRepository $productVariantRepository
,TranslatorInterface $translator,LocaleSwitcher $localeSwitcher,Request $request
,EntityManagerInterface $entityManager): Response
{
if ($request->query->has('_locale')) {
$locale = $request->getSession()->get('_locale');
if($locale != null){
$localeSwitcher->setLocale($locale);
$translator->setLocale($locale);
}
}
$panier = $session->get('panier',[]);
// ── Build cart rows (skip products that were deleted) ──
$panierWithData = [];
foreach($panier as $pan){
$product = $productrepository->find($pan["id"]);
if (!$product) continue;
// ── Skip products whose shop isn't active (not sellable) ──
if (!$this->shopIsActive($product)) { continue; }
$productVariant = null;
if ($pan["idVariante"] != 'notdefined'){
$productVariant = $productVariantRepository->find($pan["idVariante"]);
}
$panierWithData[] = [
'product' => $product,
'productVariant' => $productVariant,
'quantity' => (int) $pan["qtt"],
];
}
// ── Resolve which address to calculate delivery against ──
$user = $this->getUser();
$addresses = [];
$selectedAddress = null;
$requestedAddrId = $request->query->get('addr');
if ($user && method_exists($user, 'getAddresses')) {
foreach ($user->getAddresses() as $a) { $addresses[] = $a; }
if ($requestedAddrId) {
foreach ($addresses as $a) {
if ((string) $a->getId() === (string) $requestedAddrId) { $selectedAddress = $a; break; }
}
}
if (!$selectedAddress) {
foreach ($addresses as $a) {
if (method_exists($a, 'isDefaultAddresse') && $a->isDefaultAddresse()) { $selectedAddress = $a; break; }
}
}
if (!$selectedAddress && count($addresses) > 0) {
$selectedAddress = $addresses[0];
}
}
$selectedRegion = null;
if ($selectedAddress && method_exists($selectedAddress, 'getRegion')) {
try { $selectedRegion = $selectedAddress->getRegion(); } catch (\Throwable $e) {}
}
$selectedRegionId = ($selectedRegion && method_exists($selectedRegion, 'getId')) ? (int) $selectedRegion->getId() : null;
$selectedRegionName = ($selectedRegion && method_exists($selectedRegion, 'getName')) ? $selectedRegion->getName() : null;
$deliveryResolvable = ($selectedRegionId !== null);
// ── Group items by shop ──
$groups = [];
$subtotal = 0.0;
foreach ($panierWithData as $row) {
$product = $row['product'];
$variant = $row['productVariant'];
$qty = $row['quantity'];
$unitPrice = $this->unitPriceOfLine($product, $variant); // ← variant price if present
$lineTotal = $unitPrice * $qty;
$subtotal += $lineTotal;
$shop = method_exists($product, 'getMagasin') ? $product->getMagasin() : null;
$shopId = ($shop && method_exists($shop, 'getId')) ? (int) $shop->getId() : 0;
$shopName = ($shop && method_exists($shop, 'getNom')) ? $shop->getNom() : 'Shop';
if (!isset($groups[$shopId])) {
$groups[$shopId] = [
'shopId' => $shopId,
'shopName' => $shopName,
'items' => [],
'subtotal' => 0.0,
'canDeliver' => null,
'deliveryFee' => null,
'feeKnown' => false,
];
}
$groups[$shopId]['items'][] = [
'product' => $product,
'productVariant' => $variant,
'quantity' => $qty,
'unitPrice' => $unitPrice,
'lineTotal' => $lineTotal,
];
$groups[$shopId]['subtotal'] += $lineTotal;
}
// ── Per-shop delivery calculation ──
$totalDelivery = 0.0;
$hasUndeliverable = false;
foreach ($groups as $sid => &$g) {
if (!$deliveryResolvable) {
$g['canDeliver'] = null; $g['deliveryFee'] = null; $g['feeKnown'] = false;
continue;
}
$feeData = ($sid > 0) ? $this->lookupDeliveryFee($entityManager, $sid, $selectedRegionId) : null;
if ($feeData === null) {
$g['canDeliver'] = false; $g['deliveryFee'] = null; $g['feeKnown'] = false;
$hasUndeliverable = true;
} else {
$g['canDeliver'] = $feeData['can_deliver'];
$g['deliveryFee'] = $feeData['fee'];
$g['feeKnown'] = true;
if ($feeData['can_deliver']) {
$totalDelivery += $feeData['fee'];
} else {
$hasUndeliverable = true;
}
}
}
unset($g);
$grandTotal = $subtotal + $totalDelivery;
$canCheckout = ($user !== null)
&& ($selectedAddress !== null)
&& $deliveryResolvable
&& !$hasUndeliverable
&& count($groups) > 0;
return $this->render('panier/index.html.twig', [
'shopGroups' => array_values($groups),
'addresses' => $addresses,
'selectedAddress' => $selectedAddress,
'selectedAddressId' => $selectedAddress ? $selectedAddress->getId() : null,
'selectedRegionName' => $selectedRegionName,
'subtotal' => $subtotal,
'totalDelivery' => $totalDelivery,
'grandTotal' => $grandTotal,
'hasUndeliverable' => $hasUndeliverable,
'deliveryResolvable' => $deliveryResolvable,
'canCheckout' => $canCheckout,
'itemCount' => count($panierWithData),
'isLoggedIn' => $user !== null,
]);
}
#[Route('/panier/add/{id}/{idVariante}/{option}', name: 'panier_add')]
public function add($id, $idVariante, $option, SessionInterface $session, Request $request,
EntityManagerInterface $entityManager, TranslatorInterface $translator,
LocaleSwitcher $localeSwitcher): Response
{
if (empty($idVariante)) { $idVariante = 'notdefined'; }
if (!isset($idVariante)) { $idVariante = 'notdefined'; }
if ($request->query->has('_locale')) {
$locale = $request->getSession()->get('_locale');
if($locale != null){
$localeSwitcher->setLocale($locale);
$translator->setLocale($locale);
}
}
// ── Check if AJAX request ──
$isAjax = $request->isXmlHttpRequest()
|| $request->headers->get('X-Requested-With') === 'XMLHttpRequest'
|| $request->headers->get('Accept') === 'application/json';
$srv_msg = [];
$panier = $session->get('panier', []);
$qtt = $request->request->get('qttprdid') ?? 1;
$size = $request->request->get('chosenSize');
$color = $request->request->get('chosenColor');
if ($id != null){
$produit = $entityManager->getRepository(Produit::class)->find($id);
// ── Block ordering from a shop that isn't active yet ──
if ($produit !== null && !$this->shopIsActive($produit)) {
$msg = "This shop isn't active yet — its products can't be ordered.";
$session->set('srv_msg', $msg);
if ($isAjax) {
return new JsonResponse(['success' => false, 'message' => $msg], 403);
}
return $this->redirectToRoute("show_produit", ['id' => $id]);
}
$stock = $produit->getQtt();
if($stock !== null && $stock < $qtt){
$srv_msg = ["data" => "Available Qty $stock"];
$session->set('srv_msg', $srv_msg["data"]);
if ($isAjax) {
return new JsonResponse(['success' => false, 'message' => "Only $stock available"], 400);
}
return $this->redirectToRoute("show_produit", ['id' => $id]);
}
if ($idVariante == 'notdefined'){
if($size != null || $color != null){
$variant = $entityManager->getRepository(ProduitVariant::class)
->findProductByColorAndSizeAndProductId($id, $color, $size);
if ($variant != null){
$idVariante = $variant->getId();
$stock = $variant->getQtt();
} else {
$stock = 0;
}
}
} else {
$variant = $entityManager->getRepository(ProduitVariant::class)->find($idVariante);
if ($variant != null){
$stock = $variant->getQtt();
} else {
$stock = 0;
}
}
if($stock !== null && $stock < $qtt){
$srv_msg = ["data" => "Available Qty $stock"];
$session->set('srv_msg', $srv_msg["data"]);
if ($isAjax) {
return new JsonResponse(['success' => false, 'message' => "Only $stock available"], 400);
}
return $this->redirectToRoute("show_produit", ['id' => $id]);
}
}
if(count($panier) == 0){
$session->set('duration', 240);
$session->set('start_time', date("Y-m-d H:i:s"));
$end_time = date('Y-m-d H:i:s',
strtotime('+' . $session->get('duration') . 'minutes',
strtotime($session->get('start_time'))));
$session->set('end_time', $end_time);
}
try {
$panier = $session->get('panier', []);
if ($this->panierService->produit_existe_dans_panier($panier, $id, $idVariante)) {
$this->panierService->modifierQuantite($session, $id, $idVariante, $qtt);
} else {
$this->panierService->ajouterAuPanier($session, $id, $idVariante, $qtt);
}
// ── Return JSON for AJAX calls ──
if ($isAjax) {
$newPanier = $session->get('panier', []);
$count = array_sum(array_column($newPanier, 'qtt'));
return new JsonResponse(['success' => true, 'cartCount' => $count]);
}
if ($option == "done"){
return $this->redirectToRoute("app_panier");
}
} catch(\Exception $e) {
if ($isAjax) {
return new JsonResponse(['success' => false, 'message' => 'Error'], 500);
}
return $this->redirectToRoute("app_home");
}
return $this->redirectToRoute("app_home");
}
// ══════════════════════════════════════════════════════
// REMOVE ITEM FROM CART
// ══════════════════════════════════════════════════════
#[Route('/panier/remove/{id}/{idVariante}', name: 'panier_remove', methods: ['POST', 'GET'])]
public function remove($id, $idVariante, SessionInterface $session, Request $request): Response
{
if (empty($idVariante) || !isset($idVariante)) {
$idVariante = 'notdefined';
}
$isAjax = $request->isXmlHttpRequest()
|| $request->headers->get('X-Requested-With') === 'XMLHttpRequest'
|| $request->headers->get('Accept') === 'application/json';
$panier = $session->get('panier', []);
$newPanier = [];
$removed = false;
foreach ($panier as $item) {
$itemId = isset($item['id']) ? (string)$item['id'] : '';
$itemVar = isset($item['idVariante']) ? (string)$item['idVariante'] : 'notdefined';
if ($itemId === (string)$id && $itemVar === (string)$idVariante) {
$removed = true;
continue;
}
$newPanier[] = $item;
}
$session->set('panier', array_values($newPanier));
if ($isAjax) {
$count = array_sum(array_column($newPanier, 'qtt'));
return new JsonResponse([
'success' => true,
'removed' => $removed,
'cartCount' => $count,
'isEmpty' => count($newPanier) === 0,
]);
}
return $this->redirectToRoute("app_panier");
}
#[Route('/panier/minus/{id}/{idVariante}/{option}', name: 'panier_minus')]
public function minus($id, $idVariante, $option, SessionInterface $session, Request $request): Response
{
if (empty($idVariante)) { $idVariante = 'notdefined'; }
if (!isset($idVariante)) { $idVariante = 'notdefined'; }
$isAjax = $request->isXmlHttpRequest()
|| $request->headers->get('X-Requested-With') === 'XMLHttpRequest';
try {
$this->panierService->supprimerDuPanier($session, $id, $idVariante);
} catch(\Exception $e) {}
if ($isAjax) {
$newPanier = $session->get('panier', []);
$count = array_sum(array_column($newPanier, 'qtt'));
return new JsonResponse(['success' => true, 'cartCount' => $count]);
}
return $this->redirectToRoute("app_panier");
}
}