Files
Starseed/templates/logistique/weighing_ticket_print.html.twig
T
tristan 086be7b4f0
Auto Tag Develop / tag (push) Successful in 14s
fix(logistique) : bon de pesée — cartouche tiers + filtrage des listes contrepartie par site (ERP-208) (#155)
## 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: #155
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-06-25 13:02:31 +00:00

113 lines
5.1 KiB
Twig

{#
Ticket de pesée (M5 Logistique) — gabarit imprimable hydraté côté serveur puis
converti en PDF par WeighingTicketPdfRenderer (Dompdf). Cf. spec-back M5 § 2.12
/ § 4.6 (RG-5.08). Reproduit fidèlement le modèle fourni (ticket_pesee.pdf).
En-tête FIXE (logo + identité société) : le ticket ne change pas en fonction du
site (décision Tristan, ERP-192). Le logo est injecté en data-URI par le renderer
(logoSrc) ; l'identité société est en dur ci-dessous.
Contraintes Dompdf : CSS2.1 (pas de flexbox/grid), mise en page par tableaux.
Police DejaVu Sans (UTF-8 — accents FR et « ° » rendus correctement).
#}
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<style>
@page { margin: 18mm 16mm; }
* { font-family: "DejaVu Sans", sans-serif; }
body { color: #000; font-size: 10px; margin: 0; }
.logo { margin-bottom: 16px; }
.logo img { height: 100px; }
.company-name { font-weight: bold; font-size: 12px; }
.company-line { font-size: 12px; }
/* En-tête 2 colonnes (Dompdf = CSS 2.1, pas de flex/grid) : identité
société à gauche, cartouche du tiers à droite (ERP-208). Largeurs
fixes par cellule + cartouche en bloc (pas d'inline-block/min-width,
mal supportés par Dompdf) : le cartouche occupe la colonne de droite
et un nom long passe à la ligne au lieu de déborder. */
.header { width: 100%; border-collapse: collapse; }
.header td { vertical-align: top; }
.header .h-left { width: 62%; }
.header .h-right { width: 38%; }
.party-box { border: 1px solid #000; padding: 8px 12px; }
.party-label { font-weight: bold; font-size: 14px; margin-bottom: 4px; }
.party-name { font-size: 11px; word-wrap: break-word; }
.title { font-size: 22px; font-weight: bold; margin: 22px 0 18px; }
/* Lignes des deux pesées : tableau sans bordure, colonnes alignées. */
.weighings { border-collapse: collapse; font-size: 12px; }
.weighings td { vertical-align: top; white-space: nowrap; }
.weighings .c-label { width: 130px; }
.weighings .c-weight { width: 95px; }
.weighings .c-num { width: 175px; }
.weighings .c-dsd { width: auto; }
.net { font-size: 18px; font-weight: bold; margin-top: 26px; }
</style>
</head>
<body>
{# Libellé FR du type de contrepartie (couche de rendu, pas le Domain — ERP-208). #}
{% set counterpartyLabels = { 'CLIENT': 'Client', 'FOURNISSEUR': 'Fournisseur', 'AUTRE': 'Autre' } %}
<table class="header">
<tr>
<td class="h-left">
{% if logoSrc %}
<div class="logo"><img src="{{ logoSrc }}" alt="LPC LIOT"></div>
{% endif %}
<div class="company-name">SA LIOT Châtellerault</div>
<div class="company-line">Email : lpc.contacts@lpc-liot.fr</div>
<div class="company-line">RCS Châtellerault B 339 505 612</div>
</td>
{# 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é. #}
<td class="h-right">
{% if ticket.counterpartyType %}
<div class="party-box">
<div class="party-label">{{ counterpartyLabels[ticket.counterpartyType] ?? ticket.counterpartyType }} :</div>
{% if ticket.counterpartyName %}
<div class="party-name">{{ ticket.counterpartyName }}</div>
{% endif %}
</div>
{% endif %}
</td>
</tr>
</table>
<div class="title">Ticket de pesée</div>
{#
DSD de la pesée : valeur du pont en AUTO, valeur saisie par l'opérateur en
MANUAL (ERP-193). Un seul champ `dsd` dans les deux cas.
#}
{% set emptyRef = ticket.emptyDsd %}
{% set fullRef = ticket.fullDsd %}
<table class="weighings">
<tr>
<td class="c-label">Poids à vide</td>
<td class="c-weight">{{ ticket.emptyWeight is not null ? ticket.emptyWeight ~ ' kg' : '' }}</td>
<td class="c-num">N° pesée à vide</td>
<td class="c-dsd">{% if emptyRef is not null %}DSD : {{ emptyRef }}{% endif %}{% if ticket.emptyDate %} {{ ticket.emptyDate|date('d/m/Y H:i:s') }}{% endif %}</td>
</tr>
<tr>
<td class="c-label">Poids à plein</td>
<td class="c-weight">{{ ticket.fullWeight is not null ? ticket.fullWeight ~ ' kg' : '' }}</td>
<td class="c-num">N° pesée à plein</td>
<td class="c-dsd">{% if fullRef is not null %}DSD : {{ fullRef }}{% endif %}{% if ticket.fullDate %} {{ ticket.fullDate|date('d/m/Y H:i:s') }}{% endif %}</td>
</tr>
</table>
<div class="net">Poids : {{ ticket.netWeight is not null ? ticket.netWeight ~ ' kg' : '—' }}</div>
</body>
</html>