fix(logistique) : bon de pesée — cartouche tiers + filtrage des listes contrepartie par site (ERP-208) #155
Reference in New Issue
Block a user
Delete Branch "fix/erp-208-ticket-pesee"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
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).É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.
?siteId[]=de /clients et /suppliers (aucun changement back sur le filtre).Tests
WeighingTicketCounterpartyNameTest(nom + libellé) ; test PDF existant inchangé.À 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).
Code review — ERP-208 (cartouche tiers + filtrage par site)
Le périmètre back (cartouche
getCounterpartyName/getCounterpartyTypeLabel, template Twig, tests) est propre et bien couvert. Les points à traiter se concentrent côté front, autour de la purge du tiers sélectionné.🔴 Correction
1. Perte silencieuse du tiers à l'édition d'un ticket existant —
edit.vue(reloadReferentials/onMounted)La purge vide
form.clientIri/supplierIridès que la valeur n'est plus dans la liste rechargée. Or sur l'écran d'édition cette liste peut légitimement ne pas contenir le tiers déjà enregistré :/clients?siteId[]=→ purgé.load()est tolérant (Promise.allSettled→ liste vide si 403 selon le rôle). Liste vide ⇒ tout IRI paraît « absent » ⇒ purge. Un rôle qui édite des tickets sans read sur/clientsperd la contrepartie.onMounted,reloadReferentials(...)est lancé sansawaitpuisawait fetchTicket → form.hydrate(qui poseclientIri). Siload()résout aprèshydrate, la purge nulle la valeur hydratée.Conséquence : à la sauvegarde, la vraie contrepartie est écrasée par
null(ou 422 RG-5.03). Intermittent. Le design (§ « switch de site avec tiers sélectionné ») n'a prévu le reset-si-absent que pour le switch pendant la saisie, pas pour le chargement d'un ticket persisté.2. L'édition filtre par
currentSitealors que le site du ticket est immuable —edit.vue(watch(currentSite.id))Basculer le site courant en cours d'édition rejoue la purge et vide une contrepartie valide d'un ticket resté sur son site (RG-5.09,
site_idimmuable). Sur l'écran d'édition le filtre devrait être piloté par le site du ticket, pas parcurrentSite.3. Scoping front contourné quand
currentSiteest null —new.vue+useWeighingTicketReferentials.tscurrentSiten'est hydraté que parSiteSelector(syncFromAuth), rendu uniquement sisitesactif etuser.sitesnon vide. Sinonload(null)n'envoie passiteId[]→ les selects montrent tous les tiers, tous sites confondus (cloisonnement ERP-208 perdu côté front).4. Chargements concurrents last-write-wins —
useWeighingTicketReferentials.loadload()assigneclients.value/suppliers.valuesans séquencement ni annulation. Sur des switches rapides, une réponse de l'ancien site peut arriver en dernier → listes filtrées sur le mauvais site, puis purge contre une liste périmée.🟡 Qualité / altitude
5. Littéraux
'CLIENT'/'FOURNISSEUR'/'AUTRE'répétés en 4 endroits —WeighingTicket.php(Assert\Choice,switchdevalidateCounterpartyConsistency, 2 nouveauxmatch). La classe utilise déjàpublic const string STATUS_*. Une faute dans un bras dematch(FOURNISEUR) compile et imprime un nom vide sans test rouge → extraire des constantes / un enumCounterpartyType.6.
{% if ticket.counterpartyName %}masque aussi le libellé de type —weighing_ticket_print.html.twig: type défini mais nom vide (brouillon imprimable, ou client legacy àcompanyNamenull) ⇒ tout le cartouche disparaît. Garder surcounterpartyTypeLabelet conditionner le nom à l'intérieur.7. Libellés FR dans la couche Domain —
WeighingTicket.php(getCounterpartyTypeLabel) : chaînes UI codées en dur dans l'entité, et tests domaine couplés au copywriting. Leur place est le template de rendu.8. Robustesse Dompdf —
weighing_ticket_print.html.twig:inline-block+min-widthest mal supporté (CSS 2.1) et le<td>gauche n'a pas de largeur ; uncompanyNamelong peut déborder le cartouche ou comprimer le bloc société. À valider sur un vrai rendu Dompdf.9.
reloadReferentials+watch+onMounteddupliqués à l'identique entrenew.vueetedit.vue: la règle de purge en double dérivera dès qu'une page gagnera un champ que l'autre n'a pas → candidat à remonter dansuseWeighingTicketReferentials.Prioritaire : traiter #1 + #2 ensemble — sur l'édition, ne pas purger une contrepartie déjà persistée :
await reloadReferentialsaprèshydrate, ne purger que sur un vrai changement de site (pas au chargement initial), et toujours conserver l'option du tiers actuellement sélectionné (l'injecter si absente) plutôt que la retirer. #5→#9 sont non bloquants.