src/Service/CaisseService.php line 71

Open in your IDE?
  1. <?php
  2. namespace App\Service;
  3. use App\Entity\Client;
  4. use App\Entity\CommandeCaisse;
  5. use App\Entity\Devis;
  6. use App\Entity\DevisLine;
  7. use App\Entity\Entreprise;
  8. use App\Entity\LigneCommandeCaisse;
  9. use App\Entity\Produit;
  10. use App\Entity\Salarie;
  11. use App\Entity\TableRestaurant;
  12. use App\Entity\User;
  13. use App\Repository\CommandeCaisseRepository;
  14. use Doctrine\ORM\EntityManagerInterface;
  15. use Symfony\Component\HttpFoundation\RequestStack;
  16. /**
  17.  * Cœur de la caisse : commande en cours du poste, calcul des totaux (source
  18.  * unique pour l'écran, le ticket et l'enregistrement), mise en attente,
  19.  * tables de restaurant, bon de cuisine et encaissement.
  20.  *
  21.  * La commande en cours est en base (CommandeCaisse) ; la session ne retient
  22.  * que son id. Un poste peut donc reprendre une commande commencée sur un autre
  23.  * (tables, commandes en attente), et rien n'est perdu à la déconnexion.
  24.  */
  25. class CaisseService
  26. {
  27.     public const SESSION_COMMANDE = 'caisse_commande_id';
  28.     /** Commande d'origine à rouvrir après l'encaissement d'une addition partagée. */
  29.     public const SESSION_RETOUR = 'caisse_commande_retour_id';
  30.     public const MODES_PAIEMENT = [
  31.         'espece' => 'Espèces',
  32.         'carte' => 'Carte bancaire',
  33.         'cheque' => 'Chèque',
  34.         'nonPaye' => 'Non payé',
  35.     ];
  36.     private $em;
  37.     private $entrepriseResolver;
  38.     private $requestStack;
  39.     private $numerotation;
  40.     private $stockService;
  41.     private $commandeRepository;
  42.     public function __construct(
  43.         EntityManagerInterface $em,
  44.         EntrepriseResolver $entrepriseResolver,
  45.         RequestStack $requestStack,
  46.         NumerotationService $numerotation,
  47.         StockService $stockService,
  48.         CommandeCaisseRepository $commandeRepository
  49.     ) {
  50.         $this->em = $em;
  51.         $this->entrepriseResolver = $entrepriseResolver;
  52.         $this->requestStack = $requestStack;
  53.         $this->numerotation = $numerotation;
  54.         $this->stockService = $stockService;
  55.         $this->commandeRepository = $commandeRepository;
  56.     }
  57.     // ------------------------------------------------------------------
  58.     // Commande en cours du poste
  59.     // ------------------------------------------------------------------
  60.     public function commandeCourante(bool $creer = false): ?CommandeCaisse
  61.     {
  62.         $session = $this->requestStack->getSession();
  63.         $id = $session->get(self::SESSION_COMMANDE);
  64.         $commande = $id ? $this->commandeRepository->find($id) : null;
  65.         $owner = $this->entrepriseResolver->getOwnerUser();
  66.         if ($commande && (!$commande->isModifiable() || !$owner || $commande->getUser()->getId() !== $owner->getId())) {
  67.             $commande = null;
  68.         }
  69.         if (!$commande) {
  70.             $session->remove(self::SESSION_COMMANDE);
  71.             if ($creer) {
  72.                 $commande = $this->nouvelleCommande();
  73.             }
  74.         }
  75.         return $commande;
  76.     }
  77.     public function nouvelleCommande(?TableRestaurant $table = null): CommandeCaisse
  78.     {
  79.         $entreprise = $this->entrepriseResolver->getCurrentEntreprise();
  80.         $commande = new CommandeCaisse();
  81.         $commande->setUser($this->entrepriseResolver->getOwnerUser());
  82.         $commande->setEntreprise($entreprise);
  83.         $commande->setCreePar($this->entrepriseResolver->getCurrentPrincipal());
  84.         $commande->setTable($table);
  85.         if ($entreprise && $entreprise->isCaisseModeRestaurant()) {
  86.             $commande->setTypeCommande(CommandeCaisse::TYPE_SUR_PLACE);
  87.         }
  88.         $this->em->persist($commande);
  89.         $this->em->flush();
  90.         $this->definirCourante($commande);
  91.         return $commande;
  92.     }
  93.     /**
  94.      * Fait de cette commande celle du poste (reprise d'une commande en attente
  95.      * ou d'une table).
  96.      */
  97.     public function definirCourante(CommandeCaisse $commande): void
  98.     {
  99.         if ($commande->getStatut() === CommandeCaisse::STATUT_EN_ATTENTE) {
  100.             $commande->setStatut(CommandeCaisse::STATUT_OUVERTE);
  101.             $this->em->flush();
  102.         }
  103.         $this->requestStack->getSession()->set(self::SESSION_COMMANDE, $commande->getId());
  104.     }
  105.     public function libererPoste(): void
  106.     {
  107.         $this->requestStack->getSession()->remove(self::SESSION_COMMANDE);
  108.     }
  109.     // ------------------------------------------------------------------
  110.     // Articles
  111.     // ------------------------------------------------------------------
  112.     public function ajouterProduit(CommandeCaisse $commande, Produit $produit, float $quantite = 1.0, ?string $note = null): LigneCommandeCaisse
  113.     {
  114.         $note = $note !== null && trim($note) !== '' ? trim($note) : null;
  115.         foreach ($commande->getLignes() as $ligne) {
  116.             if ($ligne->getProduit() && $ligne->getProduit()->getId() === $produit->getId() && $ligne->getNote() === $note) {
  117.                 $ligne->setQuantite($ligne->getQuantite() + $quantite);
  118.                 $this->em->flush();
  119.                 return $ligne;
  120.             }
  121.         }
  122.         $ligne = new LigneCommandeCaisse();
  123.         $ligne->setProduit($produit);
  124.         $ligne->setQuantite($quantite);
  125.         $ligne->setNote($note);
  126.         $this->appliquerPrixProduit($ligne, $produit, $commande->getEntreprise());
  127.         $commande->addLigne($ligne);
  128.         $this->em->persist($ligne);
  129.         $this->em->flush();
  130.         return $ligne;
  131.     }
  132.     public function modifierQuantite(LigneCommandeCaisse $ligne, float $quantite): void
  133.     {
  134.         if ($quantite <= 0) {
  135.             $this->supprimerLigne($ligne);
  136.             return;
  137.         }
  138.         $ligne->setQuantite($quantite);
  139.         if ($ligne->getQuantiteEnvoyeeCuisine() > $quantite) {
  140.             $ligne->setQuantiteEnvoyeeCuisine($quantite);
  141.         }
  142.         $this->em->flush();
  143.     }
  144.     public function supprimerLigne(LigneCommandeCaisse $ligne): void
  145.     {
  146.         $ligne->getCommande()->removeLigne($ligne);
  147.         $this->em->remove($ligne);
  148.         $this->em->flush();
  149.     }
  150.     public function vider(CommandeCaisse $commande): void
  151.     {
  152.         foreach ($commande->getLignes()->toArray() as $ligne) {
  153.             $commande->removeLigne($ligne);
  154.             $this->em->remove($ligne);
  155.         }
  156.         $this->em->flush();
  157.     }
  158.     /**
  159.      * Recopie le prix, la TVA et la promo actuels des produits : l'écran et
  160.      * l'encaissement utilisent toujours le prix à jour.
  161.      */
  162.     public function synchroniserPrix(CommandeCaisse $commande): void
  163.     {
  164.         $modifie = false;
  165.         foreach ($commande->getLignes() as $ligne) {
  166.             if ($ligne->getProduit()) {
  167.                 $avant = [$ligne->getPrixUnitaireHt(), $ligne->getTauxTva(), $ligne->getRemiseProduit(), $ligne->getLibelle()];
  168.                 $this->appliquerPrixProduit($ligne, $ligne->getProduit(), $commande->getEntreprise());
  169.                 $modifie = $modifie || $avant !== [$ligne->getPrixUnitaireHt(), $ligne->getTauxTva(), $ligne->getRemiseProduit(), $ligne->getLibelle()];
  170.             }
  171.         }
  172.         if ($modifie) {
  173.             $this->em->flush();
  174.         }
  175.     }
  176.     private function appliquerPrixProduit(LigneCommandeCaisse $ligne, Produit $produit, ?Entreprise $entreprise): void
  177.     {
  178.         $tvaDefaut = $entreprise && $entreprise->getDefaultTvaRate() !== null ? (float) $entreprise->getDefaultTvaRate() : 19.0;
  179.         $ligne->setLibelle($produit->getDescription() ?: ($produit->getReference() ?: 'Article'));
  180.         $ligne->setPrixUnitaireHt((float) ($produit->getPrixVente() ?: $produit->getPuht() ?: 0));
  181.         $ligne->setTauxTva((float) ($produit->getTvaRate() ?: $tvaDefaut));
  182.         $ligne->setRemiseProduit((float) ($produit->getDiscountRate() ?: 0));
  183.     }
  184.     // ------------------------------------------------------------------
  185.     // Calcul
  186.     // ------------------------------------------------------------------
  187.     /**
  188.      * Totaux de la commande, avec les mêmes arrondis (3 décimales) que les
  189.      * bons de livraison : ce qui est affiché est exactement ce qui est encaissé.
  190.      */
  191.     public function calculer(CommandeCaisse $commande, float $remiseGlobale = 0.0): array
  192.     {
  193.         $lignes = [];
  194.         $totalHT = 0.0;
  195.         $totalTva = 0.0;
  196.         $totalRemise = 0.0;
  197.         $detailsTva = [];
  198.         foreach ($commande->getLignes() as $ligne) {
  199.             $pu = round($ligne->getPrixUnitaireHt(), 3);
  200.             $taux = $ligne->getTauxRemise();
  201.             $puRemise = $taux > 0 ? round($pu * (1 - $taux / 100), 3) : $pu;
  202.             $q = $ligne->getQuantite();
  203.             $ht = round($puRemise * $q, 3);
  204.             $tauxTva = $ligne->getTauxTva();
  205.             $tva = round($ht * $tauxTva / 100, 3);
  206.             $totalRemise += round(($pu - $puRemise) * $q, 3);
  207.             $totalHT += $ht;
  208.             $totalTva += $tva;
  209.             $cle = (string) $tauxTva;
  210.             if (!isset($detailsTva[$cle])) {
  211.                 $detailsTva[$cle] = ['taux' => $tauxTva, 'base' => 0.0, 'montant' => 0.0];
  212.             }
  213.             $detailsTva[$cle]['base'] += $ht;
  214.             $detailsTva[$cle]['montant'] += $tva;
  215.             $lignes[] = [
  216.                 'ligne' => $ligne,
  217.                 'prixUnitaire' => $pu,
  218.                 'tauxRemise' => $taux,
  219.                 'prixApresRemise' => $puRemise,
  220.                 'totalHT' => $ht,
  221.                 'tauxTva' => $tauxTva,
  222.                 'montantTva' => $tva,
  223.                 'totalTTC' => round($ht + $tva, 3),
  224.             ];
  225.         }
  226.         $totalHT = round($totalHT, 3);
  227.         $totalTva = round($totalTva, 3);
  228.         $totalTTC = round($totalHT + $totalTva, 3);
  229.         $remiseGlobale = round(max(0.0, min($remiseGlobale, $totalTTC)), 3);
  230.         $entreprise = $commande->getEntreprise();
  231.         $timbre = ($entreprise && $entreprise->isDroitDeTimbre() && $lignes) ? 1.0 : 0.0;
  232.         return [
  233.             'lignes' => $lignes,
  234.             'totalHT' => $totalHT,
  235.             'totalTva' => $totalTva,
  236.             'detailsTva' => $detailsTva,
  237.             'totalRemise' => round($totalRemise, 3),
  238.             'totalTTC' => $totalTTC,
  239.             'remiseGlobale' => $remiseGlobale,
  240.             'timbre' => $timbre,
  241.             'totalAPayer' => round($totalTTC - $remiseGlobale + $timbre, 3),
  242.         ];
  243.     }
  244.     // ------------------------------------------------------------------
  245.     // Attente / annulation
  246.     // ------------------------------------------------------------------
  247.     public function mettreEnAttente(CommandeCaisse $commande, ?string $libelle = null): void
  248.     {
  249.         if ($libelle !== null) {
  250.             $commande->setLibelle($libelle);
  251.         }
  252.         if (!$commande->getTable()) {
  253.             $commande->setStatut(CommandeCaisse::STATUT_EN_ATTENTE);
  254.         }
  255.         $this->em->flush();
  256.         $this->libererPoste();
  257.     }
  258.     public function annuler(CommandeCaisse $commande): void
  259.     {
  260.         $commande->setStatut(CommandeCaisse::STATUT_ANNULEE);
  261.         $commande->setTable(null);
  262.         $this->em->flush();
  263.         $this->libererSiCourante($commande);
  264.     }
  265.     private function libererSiCourante(CommandeCaisse $commande): void
  266.     {
  267.         $session = $this->requestStack->getSession();
  268.         if ((int) $session->get(self::SESSION_COMMANDE) === $commande->getId()) {
  269.             $session->remove(self::SESSION_COMMANDE);
  270.         }
  271.     }
  272.     // ------------------------------------------------------------------
  273.     // Restaurant
  274.     // ------------------------------------------------------------------
  275.     /**
  276.      * Ouvre la table (nouvelle commande) ou reprend sa commande en cours.
  277.      */
  278.     public function ouvrirTable(TableRestaurant $table, ?int $couverts = null): CommandeCaisse
  279.     {
  280.         $commande = $this->commandeRepository->findOuverteParTable($table);
  281.         if (!$commande) {
  282.             $commande = $this->nouvelleCommande($table);
  283.             $commande->setNbCouverts($couverts);
  284.             $this->em->flush();
  285.         } else {
  286.             if ($couverts) {
  287.                 $commande->setNbCouverts($couverts);
  288.             }
  289.             $this->definirCourante($commande);
  290.         }
  291.         return $commande;
  292.     }
  293.     /**
  294.      * Déplace la commande vers une autre table ; si cette table est déjà
  295.      * occupée, les deux commandes sont fusionnées. Renvoie la commande résultante.
  296.      */
  297.     public function transfererVersTable(CommandeCaisse $commande, TableRestaurant $cible): CommandeCaisse
  298.     {
  299.         $existante = $this->commandeRepository->findOuverteParTable($cible);
  300.         if (!$existante || $existante->getId() === $commande->getId()) {
  301.             $commande->setTable($cible);
  302.             $commande->setTypeCommande(CommandeCaisse::TYPE_SUR_PLACE);
  303.             if ($commande->getStatut() === CommandeCaisse::STATUT_EN_ATTENTE) {
  304.                 $commande->setStatut(CommandeCaisse::STATUT_OUVERTE);
  305.             }
  306.             $this->em->flush();
  307.             $this->definirCourante($commande);
  308.             return $commande;
  309.         }
  310.         foreach ($commande->getLignes()->toArray() as $ligne) {
  311.             $commande->removeLigne($ligne);
  312.             $existante->addLigne($ligne);
  313.         }
  314.         if ($commande->getNbCouverts()) {
  315.             $existante->setNbCouverts((int) $existante->getNbCouverts() + $commande->getNbCouverts());
  316.         }
  317.         if ($commande->getNote()) {
  318.             $existante->setNote(trim($existante->getNote() . "\n" . $commande->getNote()));
  319.         }
  320.         $this->libererSiCourante($commande);
  321.         $this->em->remove($commande);
  322.         $this->em->flush();
  323.         $this->definirCourante($existante);
  324.         return $existante;
  325.     }
  326.     /**
  327.      * Articles pas encore transmis à la cuisine ; les marque comme envoyés.
  328.      *
  329.      * @return array<array{libelle:string, quantite:float, note:?string}>
  330.      */
  331.     public function envoyerEnCuisine(CommandeCaisse $commande): array
  332.     {
  333.         $bon = [];
  334.         foreach ($commande->getLignes() as $ligne) {
  335.             $aEnvoyer = $ligne->getQuantiteAEnvoyer();
  336.             if ($aEnvoyer <= 0) {
  337.                 continue;
  338.             }
  339.             $bon[] = ['libelle' => $ligne->getLibelle(), 'quantite' => $aEnvoyer, 'note' => $ligne->getNote()];
  340.             $ligne->setQuantiteEnvoyeeCuisine($ligne->getQuantite());
  341.         }
  342.         $this->em->flush();
  343.         return $bon;
  344.     }
  345.     /**
  346.      * Addition partagée : sort les quantités choisies dans une nouvelle
  347.      * commande, à encaisser tout de suite. La commande d'origine garde le reste.
  348.      *
  349.      * @param array<int, float> $quantitesParLigne id de ligne => quantité à payer maintenant
  350.      */
  351.     public function separer(CommandeCaisse $commande, array $quantitesParLigne): ?CommandeCaisse
  352.     {
  353.         $partie = new CommandeCaisse();
  354.         $partie->setUser($commande->getUser());
  355.         $partie->setEntreprise($commande->getEntreprise());
  356.         $partie->setCreePar($this->entrepriseResolver->getCurrentPrincipal());
  357.         $partie->setTypeCommande($commande->getTypeCommande());
  358.         $partie->setClient($commande->getClient());
  359.         $partie->setLibelle(sprintf('%s (partie)', $commande->getNomAffiche()));
  360.         foreach ($commande->getLignes()->toArray() as $ligne) {
  361.             $q = min((float) ($quantitesParLigne[$ligne->getId()] ?? 0), $ligne->getQuantite());
  362.             if ($q <= 0) {
  363.                 continue;
  364.             }
  365.             $copie = new LigneCommandeCaisse();
  366.             $copie->setProduit($ligne->getProduit());
  367.             $copie->setLibelle($ligne->getLibelle());
  368.             $copie->setPrixUnitaireHt($ligne->getPrixUnitaireHt());
  369.             $copie->setTauxTva($ligne->getTauxTva());
  370.             $copie->setRemiseProduit($ligne->getRemiseProduit());
  371.             $copie->setRemiseManuelle($ligne->getRemiseManuelle());
  372.             $copie->setNote($ligne->getNote());
  373.             $copie->setQuantite($q);
  374.             $copie->setQuantiteEnvoyeeCuisine(min($q, $ligne->getQuantiteEnvoyeeCuisine()));
  375.             $partie->addLigne($copie);
  376.             $reste = $ligne->getQuantite() - $q;
  377.             if ($reste <= 0) {
  378.                 $commande->removeLigne($ligne);
  379.                 $this->em->remove($ligne);
  380.             } else {
  381.                 $ligne->setQuantite($reste);
  382.                 $ligne->setQuantiteEnvoyeeCuisine(max(0.0, $ligne->getQuantiteEnvoyeeCuisine() - $copie->getQuantiteEnvoyeeCuisine()));
  383.             }
  384.         }
  385.         if ($partie->isVide()) {
  386.             return null;
  387.         }
  388.         $this->em->persist($partie);
  389.         $this->em->flush();
  390.         $this->requestStack->getSession()->set(self::SESSION_RETOUR, $commande->getId());
  391.         $this->definirCourante($partie);
  392.         return $partie;
  393.     }
  394.     // ------------------------------------------------------------------
  395.     // Encaissement
  396.     // ------------------------------------------------------------------
  397.     /**
  398.      * Encaisse la commande : crée le bon de livraison (numéro unique), sort le
  399.      * stock et clôt la commande. Tout ou rien (transaction).
  400.      *
  401.      * @return array{devis: Devis, calcul: array, stock: array, client: Client}
  402.      */
  403.     public function encaisser(CommandeCaisse $commande, string $modePaiement, float $montantRecu = 0.0, float $remiseGlobale = 0.0, ?Client $client = null): array
  404.     {
  405.         if (!$commande->isModifiable()) {
  406.             throw new \RuntimeException('Cette commande est déjà clôturée.');
  407.         }
  408.         if ($commande->isVide()) {
  409.             throw new \RuntimeException('Le panier est vide.');
  410.         }
  411.         if (!isset(self::MODES_PAIEMENT[$modePaiement])) {
  412.             throw new \RuntimeException('Mode de paiement inconnu.');
  413.         }
  414.         $owner = $commande->getUser();
  415.         $principal = $this->entrepriseResolver->getCurrentPrincipal();
  416.         $entreprise = $commande->getEntreprise() ?: $this->entrepriseResolver->getCurrentEntreprise();
  417.         $this->synchroniserPrix($commande);
  418.         $calcul = $this->calculer($commande, $remiseGlobale);
  419.         $client = $client ?: $commande->getClient() ?: $this->clientComptant($owner);
  420.         $maintenant = new \DateTime();
  421.         $resultat = $this->em->wrapInTransaction(function () use ($commande, $modePaiement, $montantRecu, $calcul, $client, $owner, $principal, $entreprise, $maintenant) {
  422.             $devis = new Devis();
  423.             $devis->setType('livraison');
  424.             $devis->setUser($owner);
  425.             $devis->setClient($client);
  426.             $devis->setEntreprise($entreprise);
  427.             if ($principal instanceof Salarie) {
  428.                 $devis->setVendeurSalarie($principal);
  429.             } elseif ($principal instanceof User) {
  430.                 $devis->setVendeurUser($principal);
  431.             }
  432.             $devis->setReference($this->numerotation->suivant('devis', 'reference', 'L-' . $maintenant->format('Y') . '-', $owner->getId()));
  433.             $devis->setDescription($this->descriptionVente($commande, $maintenant));
  434.             $devis->setDateCreation($maintenant);
  435.             $devis->setModePaiement($modePaiement);
  436.             $devis->setStatut($modePaiement === 'nonPaye' ? 'non payé' : 'payé');
  437.             foreach ($calcul['lignes'] as $l) {
  438.                 /** @var LigneCommandeCaisse $ligne */
  439.                 $ligne = $l['ligne'];
  440.                 $devisLine = new DevisLine();
  441.                 $devisLine->setProduit($ligne->getProduit());
  442.                 $devisLine->setDescription($ligne->getNote() ? $ligne->getLibelle() . ' (' . $ligne->getNote() . ')' : $ligne->getLibelle());
  443.                 $devisLine->setReference($ligne->getProduit() ? $ligne->getProduit()->getReference() : null);
  444.                 $devisLine->setNb($ligne->getQuantite());
  445.                 $devisLine->setPuht($l['prixUnitaire']);
  446.                 $devisLine->setTvaRate($l['tauxTva']);
  447.                 if ($l['tauxRemise'] > 0) {
  448.                     $devisLine->setDiscountRate($l['tauxRemise']);
  449.                 }
  450.                 $devisLine->setPrixht($l['totalHT']);
  451.                 $devisLine->setTtcAmount($l['totalTTC']);
  452.                 $devis->addDevisLine($devisLine);
  453.                 $this->em->persist($devisLine);
  454.             }
  455.             $devis->setTotalht($calcul['totalHT']);
  456.             $devis->setTotaltva($calcul['totalTva']);
  457.             $devis->setTotalremise($calcul['totalRemise']);
  458.             $devis->setGlobalDiscount($calcul['remiseGlobale'] > 0 ? $calcul['remiseGlobale'] : null);
  459.             if ($calcul['timbre'] > 0) {
  460.                 $devis->setTimbre($calcul['timbre']);
  461.             }
  462.             $devis->setTotalttc($calcul['totalAPayer']);
  463.             if ($modePaiement === 'espece') {
  464.                 $recu = round(max($montantRecu, 0), 3);
  465.                 $devis->setMontantRecu($recu);
  466.                 $devis->setMonnaieRendre(round(max(0, $recu - $calcul['totalAPayer']), 3));
  467.             }
  468.             $this->em->persist($devis);
  469.             $stock = $this->stockService->sortirVente($devis);
  470.             $commande->setDevis($devis);
  471.             $commande->setClient($client);
  472.             $commande->setStatut(CommandeCaisse::STATUT_PAYEE);
  473.             $commande->setTable(null);
  474.             $this->em->flush();
  475.             return ['devis' => $devis, 'stock' => $stock];
  476.         });
  477.         $this->libererSiCourante($commande);
  478.         $this->reprendreCommandeOrigine();
  479.         return ['devis' => $resultat['devis'], 'calcul' => $calcul, 'stock' => $resultat['stock'], 'client' => $client];
  480.     }
  481.     /**
  482.      * Après le paiement d'une partie d'addition, le poste revient sur la
  483.      * commande d'origine (s'il reste quelque chose à payer).
  484.      */
  485.     private function reprendreCommandeOrigine(): void
  486.     {
  487.         $session = $this->requestStack->getSession();
  488.         $retourId = $session->get(self::SESSION_RETOUR);
  489.         if (!$retourId) {
  490.             return;
  491.         }
  492.         $session->remove(self::SESSION_RETOUR);
  493.         $origine = $this->commandeRepository->find($retourId);
  494.         if ($origine && $origine->isModifiable()) {
  495.             if ($origine->isVide() && !$origine->getTable()) {
  496.                 $origine->setStatut(CommandeCaisse::STATUT_ANNULEE);
  497.                 $this->em->flush();
  498.                 return;
  499.             }
  500.             $this->definirCourante($origine);
  501.         }
  502.     }
  503.     private function descriptionVente(CommandeCaisse $commande, \DateTimeInterface $date): string
  504.     {
  505.         $description = 'Vente caisse du ' . $date->format('d/m/Y');
  506.         if ($commande->getTable()) {
  507.             $description .= ' - ' . $commande->getTable()->getLibelle();
  508.         } elseif ($commande->getTypeLibelle()) {
  509.             $description .= ' - ' . $commande->getTypeLibelle();
  510.         }
  511.         return $description;
  512.     }
  513.     public function clientComptant(User $owner): Client
  514.     {
  515.         $client = $this->em->getRepository(Client::class)->findOneBy(['user' => $owner, 'nom' => 'Client Comptant']);
  516.         if (!$client) {
  517.             $client = new Client();
  518.             $client->setNom('Client Comptant');
  519.             $client->setUser($owner);
  520.             $this->em->persist($client);
  521.             $this->em->flush();
  522.         }
  523.         return $client;
  524.     }
  525.     /**
  526.      * Données du ticket client imprimé après l'encaissement (format attendu
  527.      * par printTicket() dans la caisse).
  528.      */
  529.     public function donneesTicket(array $encaissement, ?Entreprise $entreprise): array
  530.     {
  531.         /** @var Devis $devis */
  532.         $devis = $encaissement['devis'];
  533.         $calcul = $encaissement['calcul'];
  534.         $client = $encaissement['client'];
  535.         $items = [];
  536.         foreach ($calcul['lignes'] as $l) {
  537.             $ligne = $l['ligne'];
  538.             $items[] = [
  539.                 'description' => $ligne->getLibelle() . ($ligne->getNote() ? ' (' . $ligne->getNote() . ')' : ''),
  540.                 'quantite' => $ligne->getQuantite(),
  541.                 'prixUnitaire' => $l['prixApresRemise'],
  542.                 'total' => $l['totalHT'],
  543.                 'tauxRemise' => $l['tauxRemise'],
  544.                 'tauxTva' => $l['tauxTva'],
  545.             ];
  546.         }
  547.         return [
  548.             'reference' => $devis->getReference(),
  549.             'date' => $devis->getDateCreation()->format('d/m/Y H:i'),
  550.             'items' => $items,
  551.             'totalHT' => $calcul['totalHT'],
  552.             'tva' => $calcul['totalTva'],
  553.             'totalTTC' => $calcul['totalAPayer'],
  554.             'montantRecu' => (float) $devis->getMontantRecu(),
  555.             'monnaieRendre' => (float) $devis->getMonnaieRendre(),
  556.             'modePaiement' => self::MODES_PAIEMENT[$devis->getModePaiement()] ?? $devis->getModePaiement(),
  557.             'nomEntreprise' => $entreprise ? $entreprise->getNomentreprise() : 'VOTRE ENTREPRISE',
  558.             'nomClient' => trim($client->getNom() . ' ' . $client->getPrenom()),
  559.             'detailsTva' => $calcul['detailsTva'],
  560.             'timbre' => $calcul['timbre'],
  561.             'totalRemise' => $calcul['totalRemise'],
  562.             'globalDiscount' => $calcul['remiseGlobale'] > 0 ? $calcul['remiseGlobale'] : null,
  563.         ];
  564.     }
  565. }