From 086be7b4f00eb3ed1d666f487a6da1bfe9faf0d3 Mon Sep 17 00:00:00 2001 From: tristan Date: Thu, 25 Jun 2026 13:02:31 +0000 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)=20(#155)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## ERP-208 — Fix ticket de pesée ### Bon de pesée (PDF) Ajout d'un **cartouche bordé en haut à droite** du bon de pesée, contenant le **type de contrepartie** (Client / Fournisseur / Autre, en gras au-dessus) et le **nom du tiers**. - `WeighingTicket::getCounterpartyName()` + `getCounterpartyTypeLabel()` (testés). - En-tête du template passé en table 2 colonnes (contrainte Dompdf CSS 2.1). ### Écran de saisie (Ajouter / Modifier) Les listes **Client / Fournisseur** sont **filtrées sur le site courant** (un tiers est rattaché à un site via les sites de ses adresses) et **rechargées au changement de site**. - Réutilise le filtre back existant `?siteId[]=` de /clients et /suppliers (aucun changement back sur le filtre). - Au switch de site : le tiers sélectionné est réinitialisé **uniquement** s'il sort du périmètre du nouveau site. - Portée limitée au ticket de pesée : les répertoires M1/M2 ne changent pas. ### Tests - Back : test unitaire `WeighingTicketCounterpartyNameTest` (nom + libellé) ; test PDF existant inchangé. - Front : specs référentiels + écrans Ajouter/Modifier (673/673). - Pas de migration, pas de RBAC, pas d'E2E. ### À vérifier en recette En **modification**, si le tiers d'un ticket n'a pas d'adresse sur le site courant, le select peut s'afficher vide (valeur conservée mais option filtrée). Reviewed-on: https://gitea.malio.fr/MALIO-DEV/Starseed/pulls/155 Co-authored-by: tristan Co-committed-by: tristan --- .../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 | 10 +- .../pages/__tests__/weighingTicketNew.spec.ts | 11 +- .../pages/weighing-tickets/[id]/edit.vue | 26 +- .../logistique/pages/weighing-tickets/new.vue | 25 +- .../Domain/Entity/WeighingTicket.php | 32 +- .../weighing_ticket_print.html.twig | 46 ++- .../WeighingTicketCounterpartyNameTest.php | 47 +++ 11 files changed, 718 insertions(+), 23 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 %} + {# 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()); + } +}