<?php
namespace App\Controller\Front;
use App\Entity\Address;
use App\Entity\Category;
use App\Entity\Comment;
use App\Entity\Delivery;
use App\Entity\Document;
use App\Entity\DocumentDeclinationProduit;
use App\Entity\DocumentProduit;
use App\Entity\Produit;
use App\Entity\ProduitDeclinationValue;
use App\Entity\Promotion;
use App\Entity\Stock;
use App\Entity\User;
use App\Mail\Mail;
use App\Repository\DeclinationRepository;
use App\Repository\DeliveryRepository;
use App\Repository\DocumentRepository;
use App\Repository\ProduitRepository;
use App\Repository\PromotionRepository;
use App\Repository\ResourceRepository;
use App\Repository\SupplierRepository;
use App\Repository\TvaRepository;
use App\Repository\UserRepository;
use App\Service\RightService;
use Doctrine\DBAL\Exception;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Messenger\Stamp\DelayStamp;
use Symfony\Component\Routing\Annotation\Route;
use App\Service\ActivityService;
use App\Repository\ProduitDeclinationValueRepository;
class CartController extends AbstractController
{
/** @var EntityManagerInterface */
private $em;
private $tvaRepository;
private $deliveryRepository;
private $produitRepository;
private $produitDeclinationValueRepository;
private $documentRepository;
private $resourceRepository;
private $activityService;
private $declinationRepository;
private $promotionRepository;
public function __construct(EntityManagerInterface $manager,
TvaRepository $tvaRepository,
ProduitRepository $produitRepository,
ProduitDeclinationValueRepository $produitDeclinationValueRepository,
DeliveryRepository $deliveryRepository,
DocumentRepository $documentRepository,
ResourceRepository $resourceRepository,
ActivityService $activityService,
DeclinationRepository $declinationRepository,
PromotionRepository $promotionRepository
)
{
$this->em = $manager;
$this->tvaRepository = $tvaRepository;
$this->deliveryRepository = $deliveryRepository;
$this->produitRepository = $produitRepository;
$this->produitDeclinationValueRepository = $produitDeclinationValueRepository;
$this->documentRepository = $documentRepository;
$this->resourceRepository = $resourceRepository;
$this->activityService = $activityService;
$this->declinationRepository = $declinationRepository;
$this->promotionRepository = $promotionRepository;
}
/**
* @Route("/cart", name="cart_new",options={"expose"=true}, methods={"GET"})
*/
public function index(): Response
{
/*$sql = 'select * from category limit 6';
$stmt = $this->em->getConnection()->prepare($sql);
$categories = $stmt->executeQuery()->fetchAllAssociative();*/
//$categories = $this->em->getRepository(Category::class)->findAll();
$delivery = $this->em->getRepository(Delivery::class)->findOneBy(array('isSelected' => 1));
return $this->render('front/cart/index.html.twig', [
//'categories' => $categories,
'delivery' => $delivery,
]);
}
/**
* @Route("/api/promo-code", name="promo_code", options={"expose"=true}, methods={"POST"})
*/
public function promoAPI(Request $request): Response {
//$code = $request->query->get('code');
$code = $request->request->get('code');
$total = $request->request->get('total');
if(!$code){
$response = [
'res' => 'ERROR',
'message' => 'Veuillez saisir un code promo.',
];
return new jsonResponse($response);
}
$promotion = $this->em->getRepository(Promotion::class)->findOneBy(array('code' => $code));
if(!$promotion){
$response = [
'res' => 'ERROR',
'message' => 'Code promo invalide.',
];
return new jsonResponse($response);
}
$date = new \DateTime('now');
if ($promotion->getStartAt() > $date || $promotion->getEndAt() < $date) {
$response = [
'res' => 'ERROR',
'message' => 'Code promo expiré.',
];
return new jsonResponse($response);
}
// Vérification de quantité si déja saisie
if (($promotion->getTotalQuantity() > 0 && $promotion->getQuntityUser() > 0) && $promotion->getQuntityUser() >= $promotion->getTotalQuantity()) {
$response = [
'res' => 'ERROR',
'message' => "Code promo atteint nombre maximum d'utilisations.",
];
return new jsonResponse($response);
}
// Si total de panier inférieur au remise on l'applique pas
if ($promotion->getDiscountType() == 'amount' && $promotion->getDiscountValue() > $total) {
$response = [
'res' => 'ERROR',
'message' => 'Il vous manque ' . number_format(($promotion->getDiscountValue() - $total), 3, '.', ' ') . ' TND pour appliquer ce code promo',
];
return new jsonResponse($response);
}
$response = [
'res' => 'OK',
'data' => $promotion,
'message' => 'Code promo appliqué avec succès.',
];
return new jsonResponse($response);
}
/**
* @Route("/api/verify-client", name="verify_client", options={"expose"=true}, methods={"POST"})
*/
public function verifyClientAPI(Request $request): Response {
//$code = $request->query->get('code');
// Récupérer les donnés coté navigateur
$client = json_decode($request->request->get('client'), true);
if (!$client['telephone']) {
$response = [
'res' => 'ERROR',
'message' => 'Saisir votre numéro de téléphone.',
];
return new jsonResponse($response);
}
/*if (!$client['gouvernorat']) {
$response = [
'res' => 'ERROR',
'message' => 'Sélectionner votre gouvernorat.',
];
return new jsonResponse($response);
}
if (!$client['adresse']) {
$response = [
'res' => 'ERROR',
'message' => 'Saisir votre adresse de livraison.',
];
return new jsonResponse($response);
}*/
// Si le client connecté de type admin alors bloquer la commande
if ($this->getUser() && $this->getUser()->getType() != 'client') {
$response = [
'res' => 'ERROR',
'message' => 'Vous ne pouvez passer une commande web à travers un compte admin.',
];
return new jsonResponse($response);
}
// Vérifier l'existance de client dans la base
$user = $this->em->getRepository(User::class)->findOneBy(array('phone' => str_replace(' ', '', $client['telephone'])));
if (!$user) { // le client n'existe pas dans la base de données.
$response = [
'res' => 'OK',
'data' => false,
'message' => 'Nouveau client.',
];
return new jsonResponse($response);
}
$response = [
'res' => 'OK',
'data' => true,
'message' => 'Client déjà existant.',
];
return new jsonResponse($response);
}
/**
* @Route("/checkout-new", name="checkout_new",options={"expose"=true}, methods={"GET"})
*/
public function checkout(): Response
{
$delivery = $this->em->getRepository(Delivery::class)->findOneBy(array('isSelected' => 1));
// Read the JSON file of regions
$path = $this->getParameter('kernel.project_dir') . '/public' . '/front/assets/json/tunisia.json';
$json = file_get_contents($path);
// Decode the JSON file
$regions = json_decode($json, true);
//dd($regions);
return $this->render('front/cart/checkout.html.twig', [
'delivery' => $delivery,
'regions' => $regions
]);
}
/**
* @Route("/api/verify-stock/{idDec}/{qty}", name="verify_stock", options={"expose"=true}, methods={"GET"})
*/
public function verifyStockAPI(Request $request, $idDec, $qty): Response
{
// Récupérer les données coté navigateur
$produitDecllination = $this->produitDeclinationValueRepository->find($idDec);
// Calcul de stock
$quantity = 0;
foreach ($produitDecllination->getStocks() as $stock)
$quantity = $quantity + ($stock->getQtAvailable() - $stock->getQtReserved());
$response = [
'res' => 'OK',
'stock' => $quantity,
'message' => 'Stock récupéré.',
];
return new jsonResponse($response);
}
/**
* @Route("/api/commande-new", name="new_commande", methods={"POST"}, options={"expose"=true})
*/
public function newCommande(Request $request, MessageBusInterface $bus): Response
{
//$rights = $this->rightService->getAllRights($this->getUser());
// Récupérer les donnés coté client
$client = json_decode($request->request->get('client'), true);
$cart = json_decode($request->request->get('cart'), true);
$remise = json_decode($request->request->get('remise'), true);
$delivery = json_decode($request->request->get('delivery'), true);
// Vérifier l'existence de client dans la base
$userByPhone = $this->em->getRepository(User::class)->findOneBy(array('phone' => str_replace(' ', '', $client['telephone'])));
// Vérifier téléphone et username si nouveau client
if (!$this->getUser()) {
if ($userByPhone) {
$response = [
'res' => 'ERROR',
'message' => "Numéro de téléphone déja utilisé.",
];
}
}
// Contrôle saisie des informations client si nouveau client
if (!$this->getUser() && !$userByPhone) {
/* if (!$client['nom']) {
$response = [
'res' => 'ERROR',
'message' => 'Saisir votre nom.',
];
return new jsonResponse($response);
}*/
/*if (!$client['prenom']) {
$response = [
'res' => 'ERROR',
'message' => 'Saisir votre prénom.',
];
return new jsonResponse($response);
}*/
if (!$client['telephone']) {
$response = [
'res' => 'ERROR',
'message' => 'Saisir votre numéro de téléphone.',
];
return new jsonResponse($response);
}
/* if (!$client['gouvernorat']) {
$response = [
'res' => 'ERROR',
'message' => 'Sélectionner votre gouvernorat.',
];
return new jsonResponse($response);
}
if (!$client['adresse']) {
$response = [
'res' => 'ERROR',
'message' => 'Saisir votre adresse de livraison.',
];
return new jsonResponse($response);
}*/
if ($client['email'])
if ($client['email'] && !filter_var($client['email'], FILTER_VALIDATE_EMAIL)) {
$response = [
'res' => 'ERROR',
'message' => 'Adresse mail non valide.',
];
return new jsonResponse($response);
} else {
$userByEmail = $this->em->getRepository(User::class)->findOneBy(array('email' => $client['email']));
if ($userByEmail) {
$response = [
'res' => 'ERROR',
'message' => "E-mail déja utilisé.",
];
return new jsonResponse($response);
}
}
}
// Vérifcation de code promo si on a
$date = new \DateTime('now');
$promotionServer = null;
if ($remise['id']) {
$promotionServer = $this->promotionRepository->find($remise['id']);
if ($promotionServer) {
if (!$promotionServer->isValid()) {
$response = [
'res' => 'ERROR',
'message' => 'Code promo expiré.',
];
return new jsonResponse($response);
}
// Vérifier la remise si sa quantité est définie
if (($promotionServer->getQuntityUser() > 0 && $promotionServer->getTotalQuantity() > 0) && $promotionServer->getQuntityUser() >= $promotionServer->getTotalQuantity()) {
$response = [
'res' => 'ERROR',
'message' => "Code promo atteint le nombre maximum d'utilisations.",
];
return new jsonResponse($response);
}
}
}
// Vérifier si les produits commandés existe en stock et recalculer le total et le total ht
$total = 0;
$totalHt = 0;
$cartServer = [];
// Vérification des declinaisons et stock et calcul de total panier
foreach ($cart as $item) {
$produitDecllination = $this->produitDeclinationValueRepository->find($item['id']);
if (!$produitDecllination) {
$response = [
'res' => 'ERROR',
'message' => 'Le produit ' . $item['name'] . ' est en rupture de stock.',
];
return new jsonResponse($response);
}
if($produitDecllination){
// Vérifier si on a de stock restant pour ce
$stock = $produitDecllination->getStocks()[0];
if(!$stock){
$response = [
'res' => 'ERROR',
'message' => 'Le produit '.$item['name'].' est en rupture de stock.',
];
return new jsonResponse($response);
}
// Si le stock disponible est égale à 0
if ($stock->getQtAvailable() - $stock->getQtReserved() <= 0) {
$response = [
'res' => 'ERROR',
'message' => 'Le produit ' . $item['name'] . ' est en rupture de stock.',
];
return new jsonResponse($response);
}
// Si le stock diponible est > 0 mais n'est pas suffisant
if(($item['qty'] > ($stock->getQtAvailable() - $stock->getQtReserved())) && (($stock->getQtAvailable()-$stock->getQtReserved()) > 0)){
$response = [
'res' => 'ERROR',
'message' => 'Il ne reste que '.($stock->getQtAvailable()-$stock->getQtReserved()).' unités pour le produit '.$item['name'].'.',
];
return new jsonResponse($response);
};
// Le produit existe et le stock est suffisant
if($produitDecllination){
array_push($cartServer, $produitDecllination);
// Calcul de prix si le produit est en promotion
$promotion = $produitDecllination->getProduit()->getPromotion();
$decPrice = $produitDecllination->getProduit()->getPriceTtc();
$decPriceHt = $produitDecllination->getProduit()->getPriceHt();
if ($promotion) {
if ($promotion->isValid()) {
$decPrice = ($promotion->getDiscountType() == 'percent') ? $decPrice - ((($decPrice / 100) * $promotion->getDiscountValue()))
: $decPrice - $promotion->getDiscountValue();
$decPriceHt = ($promotion->getDiscountType() == 'percent') ? $decPriceHt - ((($decPriceHt / 100) * $promotion->getDiscountValue()))
: $decPriceHt - $promotion->getDiscountValue();
}
}
$total += $item['qty'] * floatval($decPrice);
$totalHt += $item['qty'] * floatval($decPriceHt);
}
}
}
// Calcule de montant de remise
$montantRemise = 0;
if($promotionServer){
// Si total de panier inférieur à la remise on ne l'applique pas
if ($promotionServer->getDiscountType() == 'amount' && $promotionServer->getDiscountValue() > $total){
$response = [
'res' => 'ERROR',
'message' => 'Il vous manque '.number_format(($promotionServer->getDiscountValue() - $total), 3, '.', ' ').' pour appliquer ce code promo',
];
return new jsonResponse($response);
}
// Calcul de montant de remise
switch ($promotionServer->getDiscountType()){
case 'amount':
$montantRemise = $promotionServer->getDiscountValue();
break;
case 'percent':
$montantRemise = $total*$promotionServer->getDiscountValue()/100;
break;
}
}
// Suspend auto commit : début de la transaction
$this->em->getConnection()->beginTransaction();
try {
$user = $this->getUser() ?? $userByPhone ?? null;
if (!$user) { // Création de nouveau utilisateur
$user = new User();
$user->setType('client');
$user->setUsermane(($client['nom']?$client['nom']:"Foulen") . ' ' . ($client['prenom']?$client['prenom']:"ben foulen"));
$user->setFirstName($client['nom']?$client['nom']:"Foulen");
$user->setLastName($client['prenom']?$client['prenom']:"ben foulen");
$user->setCivility($client['civilite']);
$user->setPhone(str_replace(' ', '', $client['telephone']));
if ($client['email']) $user->setEmail($client['email']);
$user->setAdress($client['adresse']);
$user->setCreatedAt($date);
if($client['adresse2'])$user->setSecondAdress($client['adresse2']);
$user->setCountry('Tunisia');
$user->setRegion($client['gouvernorat']);
if($client['ville'])$user->setCity($client['ville']);
if($client['codePostale'])$user->setZip($client['codePostale']);
// Enregistrer le nouveau utilisateur
$this->em->persist($user);
}
/*if( ! $user->getMultiAddress()->count() ){
$adress = new Address();
$adress->setAddress($client['adresse']);
$adress->setCity($client['ville']);
$adress->setZip($client['codePostale']);
$adress->setRegion($client['gouvernorat']);
$adress->setCountry('Tunisia');
$this->em->persist($adress);
$user->addMultiAddress($adress);
}*/
if(empty($user->getAdress()))$user->setAdress($client['adresse']);
if(empty($user->getRegion()))$user->setRegion($client['gouvernorat']);
if(empty($user->getZip()))$user->setZip($client['codePostale']);
if(empty($user->getCountry())) $user->setCountry('Tunisia');
// Enregistrer la commande
$document = new Document();
//$date = new \DateTime('now');
$type = 'commande';
$document->setType($type);
$document->setCategory('client');
$document->setCreatedAt(new \DateTime('now'));
$document->setEndAt($date->add(new \DateInterval('P30D')));
$document->setAdress($client['adresse'].' '.$client['codePostale'].' '.$client['ville'].' '.$client['gouvernorat']);
$document->setPaymentMethod('especes');
$document->setStatus('En attente');
$document->setConditionDocument('Aucun');
$document->setInternalNbr('BDC-' . date('dmY') . '-' . rand(10000, 99999));
$document->setClient($user);
$document->setUser($user);
//if ($request->isMethod('POST')) {
//dd($request);
$message = 'Le client ' . $user->getFirstName() . ' ajouter un bon commande ' . $document->getInternalNbr();
$this->activityService->addActivity('info', $message, $document, $user, 'document');
//if ($request->get('form_document')) {
//$data = $request->get('form_document');
//if ($remie['discount'] == "") $data['discount'] = 0;
//$dateDelivery = ($data['deliveryAt'] && $data['deliveryAt'] != '' && $data['deliveryAt'] != null)?
// new \DateTime($data['deliveryAt']): null;
// Livraison
$deliveryServer = $this->deliveryRepository->find($delivery['id']);
$document
->setObject('Commande site web n°: ' . $document->getInternalNbr())
->setTotalAmountHt($totalHt)
// Code promo appliqué
->setPromotion($promotionServer ? $promotionServer : null)
//->setParcelTrackingNbr($data['parcelTrackingNbr'])
//->setPaymentMethod($data['paymentMethod'])
//->setPaymentDeadline($data['paymentDeadline'])
->setDiscount($promotionServer ? $promotionServer->getDiscountValue() : null)
->setDiscountType($promotionServer ? $promotionServer->getDiscountType() : null)
->setAdvancePayment(0);
//->setAdvancePaymentType($data['advancePaymentType'])
//->setPackagesNbr($data['packagesNbr'])
//->setUrlTracking($data['urlTracking'])
//->setDeliveryAt($dateDelivery)
//->setNote($client['commentaire'] ?$client['commentaire'] :null);
// Incrémenter l'usage de code promo
if ($promotionServer && $promotionServer->getQuntityUser() > 0 && $promotionServer->getTotalQuantity() > 0) {
if ($promotionServer->getQuntityUser() >= $promotionServer->getTotalQuantity()) {
$response = [
'res' => 'ERROR',
'message' => "Code promo atteint le nombre de maximum d'utilisations.",
];
return new jsonResponse($response);
}
$promotionServer->setQuntityUser($promotionServer->getQuntityUser() + 1);
$this->em->persist($promotionServer);
}
// Ressource
$resource = $this->resourceRepository->find(6); // Site Web
$document->setResource($resource);
// Livraison
$max = isset($_ENV['FREE_DELIVERY_AMOUNT']) ? floatval($_ENV['FREE_DELIVERY_AMOUNT']) : 0;
if ($deliveryServer) {
$document->setDelivery($deliveryServer)
->setDeliveryPrice($deliveryServer->getPrice());
// Si le seuil de livraison gratuite est atteint
if ($max > 0 && $total - $montantRemise >= $max) {
$document->setDeliveryDiscount(100)
->setDeliveryDiscountType('percent')
//->setDeliveryTotal($deliveryServer->getTotalPrice())
->setDeliveryTotal($deliveryServer->getPrice())
->setDeliveryPrice(0);
} else {
//$document->setDeliveryTotal($deliveryServer->getTotalPrice())
$document->setDeliveryTotal($deliveryServer->getPrice())
->setDeliveryPrice($deliveryServer->getPrice());
}
};
// Livraison gratuite
// $montantLivraisonGratuite = 200;
$deliveryPrice = 0;
if (!$deliveryServer || ($deliveryServer && $max > 0 && $total - $montantRemise >= $max)){
$document->setIsFreeDelivery(true);
}
else{
$document->setIsFreeDelivery(false);
$deliveryPrice = $deliveryServer->getPrice();
}
// total ttc
// On ajoute le prix de livraison au total ttc
$document->setTotalAmountTtc($total - $montantRemise + $deliveryPrice);
// On applique la tva 19%
//$tvaDelivery = $this->tvaRepository->find(2);
//$document->setDeliveryTva(null);
//Ajout de commentaire
if ($client['commentaire']) {
//foreach ($data['comment'] as $item) {
$comment = new Comment();
$comment->setCreateAt(new \DateTime('now'));
$comment->setDescription($client['commentaire'])
->setUser($user);
$this->em->persist($comment);
$document->addComment($comment);
//$document->addDocumentComment($comment);
//}
}
$this->em->persist($document);
// Enregistrement des produits commandés
if ($cartServer) {
foreach ($cartServer as $key => $item) {
$entity = new DocumentDeclinationProduit();
$entity->setType('produitDeclination');
if ($item->getID()) {
//$produitDecllination = $this->produitDeclinationValueRepository->find($item['id']);
$entity->setProduitDeclinationValue($item);
// Ajouter la quantité réservé dans le stoack
$stock = $item->getStocks()[0];
if ($stock) {
$message = 'Ajouter au quantité réserver ' . ($stock->getQtReserved() + $cart[$key]['qty']) . 'au declinaison' . $stock->getDeclinationProduit()->getReference();
$this->activityService->addActivity('info', $message, $stock, $user, 'stock');
$stock->setQtReserved($stock->getQtReserved() + $cart[$key]['qty']);
}
// Set unité
$entity->setUnite($produitDecllination->getProduit()->getUnit());
}
// Calcul de prix
$productPrice = $item->getProduit()->getPriceTtc();
$discountPrice = 0;
// Vérifier si le produit est en promotion et la promotion est valide
$promotion = $item->getProduit()->getPromotion();
if ($promotion && $promotion->isValid()) {
$productPrice = ($promotion->getDiscountType() == 'percent') ? $productPrice - ((($productPrice / 100) * $promotion->getDiscountValue()))
: $productPrice - $promotion->getDiscountValue();
$discountPrice = ($promotion->getDiscountType() == 'percent') ? (($productPrice / 100) * $promotion->getDiscountValue())
: $promotion->getDiscountValue();
}
$entity->setReference($item->getReference())
->setDiscount(($promotion && $promotion->isValid()) ? $promotion->getDiscountValue() : 0)
->setDiscountType($promotion && $promotion->isValid() ? $promotion->getDiscountType() : 'amount')
->setPriceHt($item->getProduit()->getPriceTtc()) // Prix original sans remise
->setQuantity($cart[$key]['qty'])
->setCreatedAt(new \DateTime('now'))
->setTotalAmount(floatval($productPrice) * $cart[$key]['qty']); // Total avec prix avec remise
if ($item->getDescription()) $entity->setDescription($item->getDescription());
// Affecter la tva de chaque produit (default 0%)
$tva = ($item->getProduit()->getTva()) ? $item->getProduit()->getTva() : $this->tvaRepository->findOneBy(["id" => 1]);
// Appliquer le Tva de chaque produit
$entity->setTva($tva);
//$entity->setDocument($document);
// Fix le problème détails commande dans email
$document->addDocumentDeclinationProduit($entity);
$this->em->persist($entity);
}
}
//}
$this->em->flush();
// Envoi mail
if (isset($_ENV['MAILER_DSN']) && isset($_ENV['MAILER_MAIL'])) {
if ($document->getClient()->getEmail() && filter_var($document->getClient()->getEmail(), FILTER_VALIDATE_EMAIL)) {
$bus->dispatch(new Mail('front/mail/order.html.twig', $document), [new DelayStamp(10000)]);
}
};
//}
// Try and commit the transaction
$this->em->getConnection()->commit();
} catch(Exception $e) {
// Rollback the failed transaction
$this->em->getConnection()->rollBack();
throw $e;
}
$response = [
'res' => 'OK',
'data' => $document->getInternalNbr(),
'message' => 'Votre commande a été enregistré avec succès.',
];
return new jsonResponse($response);
}
// Fonction pour afficher la page de Commande crée avec succès
/**
* @Route("/recap", name="recap", options={"expose"=true}, methods={"GET"})
*/
public function orderRecap(Request $request,DocumentRepository $documentRepository): Response
{
// Récupérer référence de la commande
//$idCommande = $request->query->get('idCommande');
$id = $request->query->get('id');
//$document = $this->em->getRepository(Document::class)->findOneBy(array('internalNbr' => $idCommande));
// dd(sha1($document->getInternalNbr()));
$document = $documentRepository->findBySha1($id);
// Récupérer les catégories
//$categories = $this->em->getRepository(Category::class)->findAll();
$max = isset($_ENV['FREE_DELIVERY_AMOUNT']) ? floatval($_ENV['FREE_DELIVERY_AMOUNT']) : 0;
return $this->render('front/cart/recapCommande.html.twig', [
'document' => $document,
'max' => $max
]);
}
}