From 527e47d822cdc26f4222e601e33b244552b92fd7 Mon Sep 17 00:00:00 2001 From: tristan Date: Thu, 25 Jun 2026 14:09:33 +0200 Subject: [PATCH] =?UTF-8?q?fix(logistique)=20:=20bon=20de=20pes=C3=A9e=20?= =?UTF-8?q?=E2=80=94=20cartouche=20tiers=20+=20filtrage=20des=20listes=20c?= =?UTF-8?q?ontrepartie=20par=20site=20(ERP-208)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PDF : cartouche bordé en haut à droite avec le type (Client/Fournisseur/Autre) et le nom du tiers (getCounterpartyName + getCounterpartyTypeLabel). - Écran ticket : listes Client/Fournisseur filtrées sur le site courant (param siteId[]) et rechargées au changement de site ; reset du tiers sélectionné s'il sort du périmètre du nouveau site. --- .../2026-06-25-erp-208-fix-ticket-pesee.md | 353 ++++++++++++++++++ ...6-06-25-erp-208-fix-ticket-pesee-design.md | 124 ++++++ .../useWeighingTicketReferentials.spec.ts | 44 +++ .../useWeighingTicketReferentials.ts | 23 +- .../__tests__/weighingTicketEdit.spec.ts | 2 + .../pages/__tests__/weighingTicketNew.spec.ts | 11 +- .../pages/weighing-tickets/[id]/edit.vue | 25 +- .../logistique/pages/weighing-tickets/new.vue | 25 +- .../Domain/Entity/WeighingTicket.php | 29 ++ .../weighing_ticket_print.html.twig | 36 +- .../WeighingTicketCounterpartyNameTest.php | 59 +++ 11 files changed, 712 insertions(+), 19 deletions(-) create mode 100644 docs/superpowers/plans/2026-06-25-erp-208-fix-ticket-pesee.md create mode 100644 docs/superpowers/specs/2026-06-25-erp-208-fix-ticket-pesee-design.md create mode 100644 frontend/modules/logistique/composables/__tests__/useWeighingTicketReferentials.spec.ts create mode 100644 tests/Module/Logistique/Domain/WeighingTicketCounterpartyNameTest.php diff --git a/docs/superpowers/plans/2026-06-25-erp-208-fix-ticket-pesee.md b/docs/superpowers/plans/2026-06-25-erp-208-fix-ticket-pesee.md new file mode 100644 index 0000000..a612126 --- /dev/null +++ b/docs/superpowers/plans/2026-06-25-erp-208-fix-ticket-pesee.md @@ -0,0 +1,353 @@ +# ERP-208 — Fix ticket de pesée — Plan d'implémentation + +> **For agentic workers:** REQUIRED SUB-SKILL: superpowers:subagent-driven-development ou superpowers:executing-plans. Étapes en cases à cocher (`- [ ]`). + +**Goal:** Ajouter le nom du tiers dans un cartouche bordé en haut à droite du bon de pesée PDF, et filtrer les listes Client/Fournisseur du formulaire de ticket sur le site courant (avec recharge au changement de site). + +**Architecture:** Le filtre back `?siteId[]=` existe déjà sur `/clients` et `/suppliers` (joint adresses→sites) → point 2 = front uniquement. Point 1 = une méthode entité `getCounterpartyName()` + refonte du header du template Twig en table 2 colonnes (Dompdf = CSS 2.1). + +**Tech Stack:** PHP 8.4 / Symfony / API Platform / Doctrine / Twig + Dompdf ; Nuxt 4 / Vue 3 / Vitest. + +## Global Constraints + +- `declare(strict_types=1);` en tête de tout fichier PHP. +- Commentaires en **français**, code (noms) en anglais. +- Front : `useApi()` uniquement, composants `Malio*`, 4 espaces, TS strict. +- Dompdf : **CSS 2.1 uniquement** (pas de flex/grid) → mise en page par tableaux. +- **Aucun commit sans demande explicite de Tristan** (les étapes « commit » sont différées en fin de chantier, sur demande). +- Vérif finale : `make test` + `make nuxt-test` + `make php-cs-fixer-allow-risky`. Pas d'E2E. + +--- + +### Task 1 : `WeighingTicket::getCounterpartyName()` (back) + +**Files:** +- Modify: `src/Module/Logistique/Domain/Entity/WeighingTicket.php` (ajout méthode près de `getOtherLabel`, ~ligne 449) +- Test: `tests/Module/Logistique/Domain/WeighingTicketCounterpartyNameTest.php` (create) + +**Interfaces:** +- Produces: `WeighingTicket::getCounterpartyName(): ?string` — companyName du client/fournisseur ou otherLabel selon `counterpartyType`, null sinon. Consommé par le template Twig (Task 2). + +- [ ] **Step 1 : test qui échoue** + +```php +setCompanyName('Ferme du Pré'); + $ticket = (new WeighingTicket())->setCounterpartyType('CLIENT')->setClient($client); + + self::assertSame('Ferme du Pré', $ticket->getCounterpartyName()); + } + + public function testReturnsSupplierCompanyNameForSupplierCounterparty(): void + { + $supplier = (new Supplier())->setCompanyName('Coop Sud'); + $ticket = (new WeighingTicket())->setCounterpartyType('FOURNISSEUR')->setSupplier($supplier); + + self::assertSame('Coop Sud', $ticket->getCounterpartyName()); + } + + public function testReturnsOtherLabelForOtherCounterparty(): void + { + $ticket = (new WeighingTicket())->setCounterpartyType('AUTRE')->setOtherLabel('Particulier'); + + self::assertSame('Particulier', $ticket->getCounterpartyName()); + } + + public function testReturnsNullWhenNoCounterparty(): void + { + self::assertNull((new WeighingTicket())->getCounterpartyName()); + } +} +``` + +- [ ] **Step 2 : lancer le test → échec** + +`make test` filtré : `docker exec php-starseed-fpm php bin/phpunit tests/Module/Logistique/Domain/WeighingTicketCounterpartyNameTest.php` +Attendu : FAIL (`getCounterpartyName` n'existe pas). Vérifier au passage que `Client`/`Supplier` ont bien un constructeur sans argument et `setCompanyName` (sinon adapter l'instanciation du test au pattern existant des entités). + +- [ ] **Step 3 : implémentation minimale** + +Dans `WeighingTicket.php`, après `getOtherLabel()`/`setOtherLabel()` : + +```php + /** + * Nom du tiers à afficher (bon de pesée PDF, ERP-208) : raison sociale du + * client/fournisseur ou libellé libre selon le type de contrepartie (RG-5.03). + * Null si aucune contrepartie cohérente (brouillon). + */ + public function getCounterpartyName(): ?string + { + return match ($this->counterpartyType) { + 'CLIENT' => $this->client?->getCompanyName(), + 'FOURNISSEUR' => $this->supplier?->getCompanyName(), + 'AUTRE' => $this->otherLabel, + default => null, + }; + } +``` + +- [ ] **Step 4 : lancer le test → succès** + +`docker exec php-starseed-fpm php bin/phpunit tests/Module/Logistique/Domain/WeighingTicketCounterpartyNameTest.php` → PASS. + +--- + +### Task 2 : Cartouche tiers dans le template PDF + +**Files:** +- Modify: `templates/logistique/weighing_ticket_print.html.twig` + +**Interfaces:** +- Consumes: `ticket.counterpartyName` (Task 1). + +- [ ] **Step 1 : ajouter le style du cartouche + header 2 colonnes** + +Dans le ` - {% if logoSrc %} - - {% endif %} - -
SA LIOT Châtellerault
-
Email : lpc.contacts@lpc-liot.fr
-
RCS Châtellerault B 339 505 612
+ + + + {# Cartouche tiers (ERP-208) : nom du client / fournisseur / « autre ». #} + + +
+ {% if logoSrc %} + + {% endif %} +
SA LIOT Châtellerault
+
Email : lpc.contacts@lpc-liot.fr
+
RCS Châtellerault B 339 505 612
+
+ {% if ticket.counterpartyName %} +
+
{{ ticket.counterpartyTypeLabel }} :
+ {{ ticket.counterpartyName }} +
+ {% endif %} +
Ticket de pesée
diff --git a/tests/Module/Logistique/Domain/WeighingTicketCounterpartyNameTest.php b/tests/Module/Logistique/Domain/WeighingTicketCounterpartyNameTest.php new file mode 100644 index 0000000..5b1fa22 --- /dev/null +++ b/tests/Module/Logistique/Domain/WeighingTicketCounterpartyNameTest.php @@ -0,0 +1,59 @@ +setCompanyName('Ferme du Pré'); + $ticket = new WeighingTicket()->setCounterpartyType('CLIENT')->setClient($client); + + self::assertSame('Ferme du Pré', $ticket->getCounterpartyName()); + } + + public function testReturnsSupplierCompanyNameForSupplierCounterparty(): void + { + $supplier = new Supplier()->setCompanyName('Coop Sud'); + $ticket = new WeighingTicket()->setCounterpartyType('FOURNISSEUR')->setSupplier($supplier); + + self::assertSame('Coop Sud', $ticket->getCounterpartyName()); + } + + public function testReturnsOtherLabelForOtherCounterparty(): void + { + $ticket = new WeighingTicket()->setCounterpartyType('AUTRE')->setOtherLabel('Particulier'); + + self::assertSame('Particulier', $ticket->getCounterpartyName()); + } + + public function testReturnsNullWhenNoCounterparty(): void + { + self::assertNull(new WeighingTicket()->getCounterpartyName()); + } + + public function testTypeLabelIsFrenchPerCounterpartyType(): void + { + self::assertSame('Client', new WeighingTicket()->setCounterpartyType('CLIENT')->getCounterpartyTypeLabel()); + self::assertSame('Fournisseur', new WeighingTicket()->setCounterpartyType('FOURNISSEUR')->getCounterpartyTypeLabel()); + self::assertSame('Autre', new WeighingTicket()->setCounterpartyType('AUTRE')->getCounterpartyTypeLabel()); + } + + public function testTypeLabelIsNullWhenNoCounterparty(): void + { + self::assertNull(new WeighingTicket()->getCounterpartyTypeLabel()); + } +}