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 %} + {# Libellé FR du type de contrepartie (couche de rendu, pas le Domain — ERP-208). #} + {% set counterpartyLabels = { 'CLIENT': 'Client', 'FOURNISSEUR': 'Fournisseur', 'AUTRE': 'Autre' } %} -
SA LIOT Châtellerault
-
Email : lpc.contacts@lpc-liot.fr
-
RCS Châtellerault B 339 505 612
+ + + + {# Cartouche tiers (ERP-208) : type (libellé) + nom du client / fournisseur / + « autre ». Conditionné sur le TYPE : un brouillon sans type n'affiche rien ; + un type sans nom (cas limite) affiche au moins le libellé. #} + + +
+ {% if logoSrc %} + + {% endif %} +
SA LIOT Châtellerault
+
Email : lpc.contacts@lpc-liot.fr
+
RCS Châtellerault B 339 505 612
+
+ {% if ticket.counterpartyType %} +
+
{{ counterpartyLabels[ticket.counterpartyType] ?? ticket.counterpartyType }} :
+ {% if ticket.counterpartyName %} +
{{ ticket.counterpartyName }}
+ {% endif %} +
+ {% 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..92cbccf --- /dev/null +++ b/tests/Module/Logistique/Domain/WeighingTicketCounterpartyNameTest.php @@ -0,0 +1,47 @@ +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()); + } +}