<?php
namespace App\Service;
use DateTime;
use Exception;
use App\Entity\User;
use App\Entity\Order;
use App\Util\ExcelUtil;
use App\Entity\OrderType;
use App\Util\GenericUtil;
use App\Entity\BasketItem;
use App\Entity\InfoClient;
use App\Entity\PaymentType;
use App\Entity\OrderAddress;
use App\Entity\OrderProduct;
use App\Entity\SurfaceReboisement;
use App\Repository\UserRepository;
use App\Entity\OrderProductSurface;
use App\Repository\OrderRepository;
use App\Repository\ProductRepository;
use App\Repository\OrderTypeRepository;
use Doctrine\ORM\EntityManagerInterface;
use App\Repository\PaymentTypeRepository;
use Twig\Environment as Twig_Environment;
use App\Repository\SurfaceReboisementRepository;
use Doctrine\Common\Collections\ArrayCollection;
use App\Repository\ViewSurfaceQteArbresRepository;
use App\Repository\ViewOrderProductSansArbresRepository;
use Nucleos\DompdfBundle\Wrapper\DompdfWrapperInterface;
use App\Repository\ViewTotalZoneNbrArbresACreerRepository;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class OrderService
{
const TYPE_DOCUMENT = [
'FACTURE' => 1,
'BON_DE_LIVRAISON' => 2
];
private $session;
private $tokenStorage;
private $basketService;
private $entityManager;
private $productRepository;
private $orderRepository;
private $stripeService;
private $paymentTypeRepository;
private $stockService;
private $surfaceReboisementRepository;
private $configService;
private $mailService;
private $viewTotalZoneNbrArbresACreerRepository;
private $viewOrderProductSansArbresRepository;
private $viewSurfaceQteArbresRepository;
private $orderTypeRepository;
private $userRepository;
private $twig;
private $wrapper;
private $fileHandler;
private $parameterBag;
private $spreadsheetService;
public function __construct(
SessionInterface $session,
TokenStorageInterface $tokenStorage,
BasketService $basketService,
EntityManagerInterface $entityManager,
ProductRepository $productRepository,
OrderRepository $orderRepository,
PaymentTypeRepository $paymentTypeRepository,
StripeService $stripeService,
StockService $stockService,
SurfaceReboisementRepository $surfaceReboisementRepository,
ConfigService $configService,
MailService $mailService,
ViewTotalZoneNbrArbresACreerRepository $viewTotalZoneNbrArbresACreerRepository,
ViewOrderProductSansArbresRepository $viewOrderProductSansArbresRepository,
ViewSurfaceQteArbresRepository $viewSurfaceQteArbresRepository,
OrderTypeRepository $orderTypeRepository,
UserRepository $userRepository,
Twig_Environment $twig,
DompdfWrapperInterface $wrapper,
FileHandler $fileHandler,
ParameterBagInterface $parameterBag,
SpreadsheetService $spreadsheetService
)
{
$this->session = $session;
$this->tokenStorage = $tokenStorage;
$this->basketService = $basketService;
$this->entityManager = $entityManager;
$this->productRepository = $productRepository;
$this->orderRepository = $orderRepository;
$this->stripeService = $stripeService;
$this->paymentTypeRepository = $paymentTypeRepository;
$this->stockService = $stockService;
$this->surfaceReboisementRepository = $surfaceReboisementRepository;
$this->configService = $configService;
$this->mailService = $mailService;
$this->viewTotalZoneNbrArbresACreerRepository = $viewTotalZoneNbrArbresACreerRepository;
$this->viewOrderProductSansArbresRepository = $viewOrderProductSansArbresRepository;
$this->viewSurfaceQteArbresRepository = $viewSurfaceQteArbresRepository;
$this->orderTypeRepository = $orderTypeRepository;
$this->userRepository = $userRepository;
$this->twig = $twig;
$this->wrapper = $wrapper;
$this->fileHandler = $fileHandler;
$this->parameterBag = $parameterBag;
$this->spreadsheetService = $spreadsheetService;
}
public function getParrain(){
$parrainToken = $this->session->get('parrain');
if ($parrainToken === null) {
return null;
}
$parrain = $this->userRepository->findOneBy(['username' => $parrainToken]);
if ($parrain === null) {
$parrain = $this->userRepository->findUserByToken($parrainToken);
}
if ($parrain === null) {
$this->session->remove('parrain');
return null;
}
if (!in_array('ROLE_EGERIE', $parrain->getRoles(), true)) {
$this->session->remove('parrain');
return null;
}
return $parrain;
}
public function saveOrder(Order $order, InfoClient $infoClient): ?Order{
try{
$this->entityManager->beginTransaction();
$basket = $this->basketService->getRefreshedBasket();
if(count($basket) == 0) throw new Exception("Le panier est vide");
$order->setStatus(Order::CREATED);
$parrain = $this->getParrain();
$orderUser = $order->getUser();
if($parrain && $orderUser){
if($parrain->getId() != $orderUser->getId()){
$order->setParrain($parrain);
}
}
$this->entityManager->persist($infoClient);
$amount = 0;
$qteTotal = 0;
$remiseWithCouponCode = $order->getRemiseWithCouponCode() ? $order->getRemiseWithCouponCode() : 0;
$totalTva = 0;
foreach($basket as $basketItem){
$this->basketService->checkBasketItem($basketItem);
$orderProduct = new OrderProduct();
$orderProduct->setProduct($basketItem->getProduct());
$orderProduct->setPrice($basketItem->getProduct()->getCost());
$orderProduct->setQty($basketItem->getQuantity());
$orderProductTVA = ProductService::calculateTvaFromTTC($orderProduct->getPrice() * $orderProduct->getQty(), $orderProduct->getProduct()->getTva());
$totalTva += $orderProductTVA;
$orderProduct->setTva($orderProductTVA);
$orderProduct->setOrderParent($order);
$this->entityManager->persist($orderProduct);
$order->addOrderProduct($orderProduct);
$this->checkPreOrder($orderProduct);
$amount += $basketItem->getProduct()->getCost() * $basketItem->getQuantity();
$qteTotal += $orderProduct->getQty();
}
$this->attribuerSurfaces($order->getOrderProducts(), false, $qteTotal);
$order->setMontantSansFraisLivraison($amount);
$order->setTva($totalTva);
$order->setFraisLivraison($this->configService->calculerFraisDeLivraison($order->getMontantSansFraisLivraison()));
$order->setAmount(($order->getMontantSansFraisLivraison() + $order->getFraisLivraison() - $remiseWithCouponCode ));
$paymentIntent = $this->stripeService->paymentIntent($order->getAmount());
$order->setChargeId($paymentIntent->id);
$this->entityManager->persist($order);
$this->entityManager->flush();
$this->basketService->removeBasket();
$this->entityManager->commit();
try{
// $this->notifAdminZonesACreer();
} catch(Exception $ex){}
return $order;
}
catch(\Exception $ex){
if($this->entityManager->getConnection()->isTransactionActive()) {
$this->entityManager->rollback();
}
throw $ex;
}
finally {
$this->entityManager->clear();
}
}
public function saveOrderApiRavintsara(Order $order, InfoClient $infoClient, $datas): ?Order{
try{
$this->entityManager->beginTransaction();
if($datas['quantity'] == 0) throw new Exception("Le panier est vide.");
$this->entityManager->persist($infoClient);
$order->setInfoClient($infoClient);
$order->setAmount(0);
$order->setOrderDate(new DateTime());
$order->setStatus(Order::CREATED);
$paymentType = $this->paymentTypeRepository->find(PaymentType::ONE_TIME_PURCHASE);
$order->setPaymentType($paymentType);
$orderType = $this->orderTypeRepository->find(OrderType::FROM_RAVINTSARA);
$order->setOrderType($orderType);
$this->entityManager->persist($order);
$product = $this->productRepository->find($datas['product_id']);
$orderProduct = new OrderProduct();
$orderProduct->setProduct($product);
$orderProduct->setPrice($product->getCost());
$orderProduct->setQty($datas['quantity']);
$order->addOrderProduct($orderProduct);
$this->checkPreOrder($orderProduct);
$this->entityManager->persist($orderProduct);
$amount = $product->getCost() * $datas['quantity'];
$order->setMontantSansFraisLivraison($amount);
$order->setTva($this->configService->findTva());
$order->setFraisLivraison($this->configService->calculerFraisDeLivraison($order->getMontantSansFraisLivraison()));
$order->setAmount($order->getMontantSansFraisLivraison() + $order->getFraisLivraison());
$this->attribuerSurfaces($order->getOrderProducts(), false, $orderProduct->getQty());
$paymentIntent = $this->stripeService->paymentIntent($order->getAmount());
$order->setChargeId($paymentIntent->id);
$this->entityManager->flush();
$this->entityManager->commit();
return $order;
}
catch(\Exception $ex){
if($this->entityManager->getConnection()->isTransactionActive()) {
$this->entityManager->rollback();
}
throw $ex;
}
finally {
$this->entityManager->clear();
}
}
public function attribuerSurfaces(ArrayCollection $orderProducts, $checkBase=true, $qteTotal=-1){
$surfaces = $this->surfaceReboisementRepository->findSurfacesDispo($qteTotal);
$indexOp = 0;
$qteRestanteOp = $orderProducts->get(0)->getQty();
if($checkBase) $qteRestanteOp = $orderProducts->get(0)->getOrderProductSansArbres()->getNbrACreer();
for($i=0; $i<count($surfaces) && $indexOp < count($orderProducts); $i++){
$surface = $surfaces[$i];
$qteRestanteSurface = $surface->getSurfaceQteArbres()->getNbrArbresRestants();
while($qteRestanteSurface > 0 && $indexOp < count($orderProducts)){
$op = $orderProducts->get($indexOp);
$qteAEnlever = ($qteRestanteSurface >= $qteRestanteOp) ? $qteRestanteOp : $qteRestanteSurface;
$orderProductSurface = new OrderProductSurface();
$orderProductSurface->setQte($qteAEnlever);
$orderProductSurface->setSurface($surface);
$op->addSurface($orderProductSurface);
$this->entityManager->persist($orderProductSurface);
$qteRestanteSurface -= $qteAEnlever;
$qteRestanteOp -= $qteAEnlever;
if($qteRestanteOp == 0){
$indexOp++;
if($indexOp < count($orderProducts)) {
$qteRestanteOp = $orderProducts->get($indexOp)->getQty();
if($checkBase) $qteRestanteOp = $orderProducts->get($indexOp)->getOrderProductSansArbres()->getNbrACreer();
}
}
}
}
}
public function getOpSansArbres(){
$qteTotal = 0;
$opSansArbres = new ArrayCollection();
$result = $this->viewOrderProductSansArbresRepository->findAvecNbrACreer();
for($i=0; $i<count($result); $i++){
$opSansArbres->add($result[$i]->getOrderProduct());
$qteTotal += $result[$i]->getNbrACreer();
}
return [$opSansArbres, $qteTotal];
}
public function attribuerSurfacesOpSansArbres(SurfaceReboisement $surface){
try{
$this->entityManager->beginTransaction();
if($surface){
$sqa = $this->viewSurfaceQteArbresRepository->find($surface->getId());
$surface->setSurfaceQteArbres($sqa);
}
$result = $this->getOpSansArbres();
$this->attribuerSurfaces($result[0], true, $result[1]);
$this->entityManager->flush();
$this->basketService->removeBasket();
$this->entityManager->commit();
}
catch(\Exception $ex){
if($this->entityManager->getConnection()->isTransactionActive()) {
$this->entityManager->rollback();
}
throw $ex;
}
finally {
$this->entityManager->clear();
}
}
public function notifAdminZonesACreer(){
// $nbrACreer = $this->viewTotalZoneNbrArbresACreerRepository->findUnique()->getNbrACreer();
// if($nbrACreer > 0){
// $this->mailService->notifAdminZonesACreer($nbrACreer);
// }
}
public function payOrder(Order $order){
$paymentIntent = $this->stripeService->getPaymentIntent($order->getChargeId());
if($paymentIntent->status != "succeeded") throw new Exception("Erreur rencontrée lors du paiement");
$invoicePath = $this->getInvoicePath($order);
$order->setStatus(Order::VALIDATED);
$order->setInvoicePath($invoicePath);
$this->entityManager->persist($order);
$this->entityManager->flush();
try{
$this->mailService->sendFacture($order, $invoicePath);
} catch(Exception $ex){}
}
public function checkPreOrder(OrderProduct $orderProduct){
$orderProduct->getProduct()->checkQty($orderProduct->getQty());
$qtePreCmd = $orderProduct->getQty() - $orderProduct->getProduct()->getProduitQteStock()->getQteStock();
if($qtePreCmd <= 0) $qtePreCmd = 0;
else $orderProduct->getOrderParent()->setPrecommande(true);
$orderProduct->setQtePreCmd($qtePreCmd);
$qteSortieStock = $orderProduct->getQty() - $qtePreCmd;
if($qteSortieStock > 0){
$this->stockService->faireSortie($orderProduct->getProduct(), $qteSortieStock, $orderProduct->getOrderParent()->getOrderDate());
}
}
public function saveOrderApi(Order $order, InfoClient $infoClient, string $stripeToken, $datas): ?Order{
try{
$this->entityManager->beginTransaction();
if($datas['quantity'] == 0) throw new Exception("Le panier est vide.");
$this->entityManager->persist($infoClient);
$this->entityManager->persist($order);
$product = $this->productRepository->find($datas['product_id']);
$amount = $product->getCost() * $datas['quantity'];
$orderProduct = new OrderProduct();
$orderProduct->setProduct($product);
$orderProduct->setPrice($product->getCost());
$orderProduct->setQty($datas['quantity']);
$orderProduct->setOrderParent($order);
$this->entityManager->persist($orderProduct);
$payment_type_id = $datas['payment_type_id'] ?? PaymentType::ONE_TIME_PURCHASE;
$paymentType = $this->paymentTypeRepository->find($payment_type_id);
if($paymentType == null) throw new \Exception("Le type de paiement n°".$payment_type_id." n'existe pas dans la base de donnée.");
if($payment_type_id == PaymentType::ONE_TIME_PURCHASE){
$this->checkPreOrder($orderProduct);
} else{
$orderProduct->setQtePreCmd(0);
}
$order->addOrderProduct($orderProduct);
$order->setTva($this->configService->findTva());
$order->setAmount($amount);
$order->setPaymentType($paymentType);
if($payment_type_id == PaymentType::SUBSCRIPTION){
$amountSubscription = $amount;
$paymentMethodId = $stripeToken; // Here stripeToken is a payment method id
$user = [
"mail"=> $infoClient->getMail(),
"lastname"=> $infoClient->getLastname()
];
$data = $this->stripeService->subscribe($paymentMethodId, $amountSubscription, $user, $datas);
$order->setSubscriptionId($data['id']);
}
else if($payment_type_id == PaymentType::ONE_TIME_PURCHASE){
$chargeId = $this->stripeService
->createCharge(
$stripeToken,
$order->getAmount(), [
'description' => 'Paiement unique de la commande'
]);
$order->setChargeId($chargeId);
}
$this->entityManager->flush();
$this->entityManager->commit();
return $order;
}
catch(\Exception $ex){
if($this->entityManager->getConnection()->isTransactionActive()) {
$this->entityManager->rollback();
}
throw $ex;
}
finally {
$this->entityManager->clear();
}
}
public function changeStatus(int $orderId, int $status)
{
$order = $this->orderRepository->find($orderId);
if(!$order) {
throw new Exception("La commande n°".$orderId." n'existe pas");
}
$order->setStatus($status);
$this->entityManager->flush();
}
/**
* @param array $context (Variables et Informations, ...)
* @param integer $typeDocument
*/
public function generateDocument(array $context, int $typeDocument)
{
$logoBase64 = $typeDocument === self::TYPE_DOCUMENT['FACTURE']
? $this->fileHandler->convertImageToBase64('assets/tanymena/logo_tanymena/png/logo_principale_clair.png')
: $this->fileHandler->convertImageToBase64('assets/tanymena/logo_tanymena/png/logo_principale_clair.png');
return $this->twig->render('pdf/document.html.twig', [
'context' => $context,
'logoBase64' => $logoBase64,
'typeDocument' => $typeDocument
]);
}
public function getInvoicePath(Order $order)
{
$context = [
'order' => $order,
'title' => 'Facture'
];
$facturePdf = $this->generateDocument($context, self::TYPE_DOCUMENT['FACTURE']);
$binary = $this->wrapper->getPdf($facturePdf, ['isRemoteEnabled' => true, 'isHtml5ParserEnabled'=>true, 'defaultFont'=> 'Arial']);
$directory = "factures";
$pj_filepath = $this->fileHandler->saveBinary($binary, "Facture Tany Mena-Commande n°".$order->getId()." du ".date('Y-m-d-H-i-s').'.pdf', $directory);
return $pj_filepath ;
}
public function getDeliveryOrderPath(Order $order)
{
$context = [
'order' => $order,
'title' => 'Bon de livraison'
];
$facturePdf = $this->generateDocument($context, self::TYPE_DOCUMENT['BON_DE_LIVRAISON']);
$binary = $this->wrapper->getPdf($facturePdf, ['isRemoteEnabled' => true, 'isHtml5ParserEnabled'=>true, 'defaultFont'=> 'Arial']);
$directory = "factures";
$pj_filepath = $this->fileHandler->saveBinary($binary, "Bon de livraison Aromaforest-Commande n°".$order->getId()." du ".date('Y-m-d-H-i-s').'.pdf', $directory);
return $pj_filepath ;
}
public function makeDeliveryOrder($order_id)
{
$order = $this->orderRepository->find($order_id);
$deliveryOrderPath = $this->getDeliveryOrderPath($order);
$this->mailService->sendDeliveryOrder($order, $deliveryOrderPath);
$order->setDeliveryOrderPath($deliveryOrderPath);
$this->entityManager->flush();
}
public function exportOrdersToCsv($orders){
$headers = [
"N° commande", "Identifiant client", "Mode d'expédition", "Code point relais", "Type point relais", "Lot acheminement",
"Distribution sort", "Version Plan de tri", "Date d’expédition prévue (départ entrepôt)", "Civilité du client livré", "Nom du client livré",
"Prénom du client livré", "Raison Sociale du client livré", "Contact du client de livraison", "Adresse 1 livraison", "Adresse 2 livraison",
"Adresse 3 livraison", "Adresse 4 livraison", "Code postal livraison", "Ville livraison", "Code pays livraison", "Pays livraison", "Tél fixe livraison",
"Port livraison", "Fax livraison", "Email livraison", "Nom et Prénom du client facturé", "Adresse 1 facturation", "Adresse 2 facturation",
"Adresse 3 facturation", "Adresse 4 facturation", "Code postal facturation", "Ville facturation", "Code pays facturation", "Pays facturation",
"Contact facturation", "Tél facturation", "Port facturation", "Fax facturation", "Email facturation", "Référence article", "Quantité commandée",
"Numéro commande B2B ", "Référence fournisseur", "Prise de rendez-vous obligatoire", "Commentaires livraison", "Prix de vente de l'article"
];
$fields = [
"id", "user.id", "ModeExpedition", null, null, null,
null, null, null, null, "infoClient.lastname",
"infoClient.firstname", null, null, "infoClient.deliveryAddress3", "infoClient.deliveryAddress2", // deliveryAddress3 => Adresse 1 livraison (correct)
"infoClient.deliveryAddress", null, "infoClient.postalCode", "infoClient.city", "infoClient.deliveryCountryCode", "infoClient.deliveryCountry", null,
"infoClient.phone", null, "infoClient.mail", "infoClient.fullname", "infoClient.billingAddress3", "infoClient.billingAddress2", // billingAddress3 => Adresse 1 facturation (correct)
"infoClient.billingAddress", null, "infoClient.billingPostalCode", "infoClient.billingCity", "infoClient.deliveryCountryCode", "infoClient.billingCountry",
null, null, "infoClient.phone", null, "infoClient.mail", "productsInOrder", "orderProductsQuantity",
null, null, null, null, "itemsAmount"
];
$options = [
'convert_string_to_numeric' => ['fields' => ['itemsAmount']],
'repeat_row' => ['field' => 'productsInOrder']
];
return $this->spreadsheetService->export($orders, $fields, $headers, $options);
}
}