# Template Rules - Purchase Trade

Statut: `draft`
Version: `v0.3`
Derniere mise a jour: `2026-04-02`

## 1) Scope

- Domaine: `templates Relatorio .fodt`
- Modules concernes:
  - `purchase_trade`
  - `sale`
  - `account_invoice`

## 2) Objectif

- Eviter les erreurs de parsing Relatorio/Genshi lors de la generation des documents.
- Standardiser la maniere d'alimenter les templates metier a partir du code Python.
- Centraliser les proprietes `report_*` dans une documentation reutilisable.

## 2.1) Index de reference

- Catalogue des proprietes templates:
  - `modules/purchase_trade/docs/template-properties.md`

## 3) Regles pratiques

### TR-001 - Toujours partir du template standard voisin

- Avant de modifier un template metier (`invoice_ict.fodt`, `sale_ict.fodt`, etc.), comparer avec le template standard du module source:
  - `modules/account_invoice/invoice.fodt`
  - `modules/sale/sale.fodt`
- Reprendre en priorite la syntaxe Relatorio deja validee dans ces templates.

### TR-002 - Eviter les expressions Genshi trop complexes dans le `.fodt`

- Preferer des proprietes Python simples exposees par le modele.
- Le template doit consommer au maximum des champs ou proprietes du type:
  - `record.report_address`
  - `record.report_price`
  - `record.report_payment_date`
- Si un template a besoin de donnees issues d'un autre modele lie, creer un petit pont Python.

### TR-003 - Regles de syntaxe XML/Relatorio dans les placeholders

- Dans un `text:placeholder`, utiliser:
  - `&quot;...&quot;` pour les guillemets doubles
  - `&apos;...&apos;` pour les apostrophes
- Eviter les formes avec antislashs:
  - interdit: `\'\'`
  - interdit: `\'value\'`
- Exemples corrects:
  - `&lt;replace text:p=&quot;set_lang(invoice.party.lang)&quot;&gt;`
  - `&lt;if test=&quot;invoice.report_payment_description&quot;&gt;`
  - `&lt;tax.description or &apos;&apos;&gt;`

### TR-004 - Pour une facture issue d'une vente, preferer un pont `account.invoice -> sale`

- Si le template facture doit reutiliser la logique de la pro forma vente, ne pas dupliquer les calculs directement dans le `.fodt`.
- Ajouter plutot dans `purchase_trade` une extension `account.invoice` avec des proprietes `report_*` qui relaient vers `invoice.sales[0]`.
- Exemple de proprietes utiles:
  - `report_address`
  - `report_contract_number`
  - `report_shipment`
  - `report_product_description`
  - `report_crop_name`
  - `report_attributes_name`
  - `report_price`
  - `report_payment_date`
  - `report_nb_bale`
  - `report_gross`
  - `report_net`
  - `report_lbs`

### TR-005 - Reutiliser les proprietes existantes du module `purchase_trade.sale`

- Avant d'ajouter une nouvelle logique pour un template vente ou facture issue d'une vente, verifier si une propriete existe deja sur `sale.sale`.
- Proprietes deja utiles:
  - `report_terms`
  - `report_gross`
  - `report_net`
  - `report_qt`
  - `report_nb_bale`
  - `report_deal`
  - `report_packing`
  - `report_price`
  - `report_delivery`
  - `report_payment_date`
  - `report_shipment`

### TR-006 - Penser au cache des reports facture avant d'accuser le `.fodt`

- Les actions de report `account.invoice` peuvent partager le meme moteur de rendu.
- Dans `modules/account_invoice/invoice.py`, le champ `invoice_report_cache` peut reutiliser un document deja genere.
- Symptome typique:
  - plusieurs actions differentes (`Provisional Invoice`, `Final Invoice`, `Prepayment`, etc.) semblent ouvrir le meme template ou le meme rendu
- Reflexe a avoir:
  - verifier si le probleme vient du cache avant de modifier le `.fodt`
  - pour un report alternatif, ne pas reutiliser le cache du report standard `account_invoice/invoice.fodt`
  - si besoin, bypasser la lecture/ecriture du cache pour les templates alternatifs

### TR-007 - Pour une facture trade, privilegier le lot physique comme chemin de navigation

- Pour remonter d'une facture vers des donnees logistiques ou metier, ne pas dupliquer de chemins differents selon achat/vente.
- Regle pratique:
  - partir de la ligne metier (`purchase.line` ou `sale.line`)
  - retrouver le lot physique associe
  - utiliser ce lot comme pont vers le shipment et les autres objets lies
- Ce chemin doit etre privilegie pour exposer des proprietes `report_*` comme:
  - `report_bl_date`
  - `report_loading_port`
  - `report_discharge_port`
  - `report_controller_name`
  - `report_si_number`
  - `report_proforma_invoice_number`
  - `report_proforma_invoice_date`

### TR-008 - Le freight amount d'un template facture vient du fee de shipment

- Ne pas lire le fret directement sur `account.invoice`.
- Pour les templates `invoice_ict*`, le `FREIGHT VALUE` doit etre expose par une propriete Python du type `invoice.report_freight_amount`.
- La logique attendue est:
  - retrouver le lot physique pertinent
  - retrouver son shipment
  - chercher le `fee.fee` du shipment avec `product.name = 'Maritime freight'`
  - utiliser `fee.get_amount()`
- Si le fee a sa propre devise, preferer aussi exposer le symbole de devise depuis le fee plutot que depuis la facture.

### TR-009 - Ne pas dereferencer directement `del_period.description` dans les templates

- Eviter les expressions du type:
  - `sale.lines[0].del_period.description`
  - `purchase.lines[0].del_period.description`
- Meme avec un `if ... else`, ces acces sont fragiles dans un `.fodt` et
  rendent le debug plus difficile.
- Preferer une propriete Python stable:
  - `sale.report_delivery_period_description`
  - `purchase.report_delivery_period_description`
  - `invoice.report_delivery_period_description`

### TR-010 - En template, un contrat `basis` affiche le premium comme prix

- Pour les templates commerciaux/facture (`sale_ict`, `invoice_ict`, etc.),
  le prix affiche d'une ligne `basis` ne doit pas etre le prix economique total
  (`unit_price`, `linked_price` ou prix basis brut).
- La valeur a afficher est uniquement le `premium`:
  - en devise/unite liee si `linked currency` est active
  - sinon dans la devise/unite native de la ligne
- Le texte de curve / pricing (`ON ICE ...`) reste affiche a cote, mais la
  valeur numerique et sa version en lettres doivent representer le premium.

### TR-011 - Pour `NB BALES` sur une facture, sommer les `lot_qt` des lignes facture

- Pour `invoice_ict.fodt` et `invoice_ict_final.fodt`, la source de verite du
  nombre de bales n'est pas le poids (`report_net`, `report_gross`) mais
  `line.lot.lot_qt`.
- La regle attendue est:
  - lire les lignes de facture
  - recuperer leur `lot`
  - sommer `lot.lot_qt`
  - sur une note finale, tenir compte du signe de la ligne de facture pour que
    les lignes positives et negatives se compensent
- Ne pas recalculer le nombre de bales a partir du poids:
  - les poids peuvent varier (humidite, poids net/gross)
  - le nombre de bales peut rester stable

## 4) Workflow recommande pour corriger un template en erreur

1. Identifier le placeholder exact qui provoque l'erreur Relatorio.
2. Comparer sa syntaxe avec le template standard equivalent.
3. Remplacer les guillemets/quotes non valides par `&quot;` / `&apos;`.
4. Si l'expression devient trop longue, la deplacer dans une propriete Python `report_*`.
5. Ne modifier que les placeholders necessaires.
6. Regenerer le document pour verifier la prochaine erreur eventuelle.
7. Si plusieurs actions affichent le meme rendu, verifier ensuite le cache `invoice_report_cache`.

## 5) Cas documentes dans ce repo

### Invoice ICT

- Fichier: `modules/account_invoice/invoice_ict.fodt`
- Strategie retenue:
  - aligner la syntaxe sur `modules/account_invoice/invoice.fodt`
  - reutiliser au maximum les proprietes metier deja exposees
  - exposer dans `modules/purchase_trade/invoice.py` des proprietes de pont `account.invoice -> sale/purchase`
  - pour les donnees shipment et freight, passer par le lot physique comme pont achat/vente

### Sale ICT

- Fichier: `modules/sale/sale_ict.fodt`
- Usage:
  - reference principale pour les champs metier proches d'une pro forma / facture commerciale
  - source de verite pratique pour les placeholders `report_*` issus de `purchase_trade.sale`
