# 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: - `"..."` pour les guillemets doubles - `'...'` pour les apostrophes - Eviter les formes avec antislashs: - interdit: `\'\'` - interdit: `\'value\'` - Exemples corrects: - `<replace text:p="set_lang(invoice.party.lang)">` - `<if test="invoice.report_payment_description">` - `<tax.description or ''>` ### 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 - pour les clients multi-templates, preferer une configuration metier qui stocke le nom du template par action (`Invoice`, `CN/DN`, `Prepayment`) plutot qu'une modification manuelle de `ir_action_report.report` ### TR-012 - Centraliser les templates client dans `Document Templates` - Pour les templates client-specifiques, ne pas modifier `ir_action_report.report` en base a la main selon l'environnement ou le client. - Preferer la configuration singleton `purchase_trade.configuration`, exposee dans `Documents > Configuration > Document Templates`. - Sections actuellement attendues: - `Sale` - `Invoice` - `Purchase` - `Shipment` - Regle: - si le champ de template correspondant est vide, le report doit echouer explicitement avec `No template found` - ne pas masquer dynamiquement l'action d'impression si ce n'est pas necessaire ### TR-013 - `sale_melya.fodt` et `invoice_melya.fodt` doivent afficher nom + description produit - Dans les templates client Melya, le bloc produit doit prevoir: - une ligne pour le nom produit - une ligne pour la description produit - Ne pas dereferencer directement `line.product.name` / `line.product.description` dans les `.fodt`. - Preferer: - `sale.report_product_name` - `sale.report_product_description` - `invoice.report_product_name` - `invoice.report_product_description` ### TR-014 - `invoice_melya.fodt` doit afficher `Invoice` et `Reference` sur les bons champs - Pour `modules/account_invoice/invoice_melya.fodt`: - `Invoice` doit afficher `invoice.number` - `Reference` doit afficher `invoice.report_contract_number` - Ne pas reutiliser `invoice.reference` pour ce label dans ce template client sans demande explicite ### TR-015 - Le template `stock/insurance.fodt` doit lire sur `stock.shipment.in` - Le template `modules/stock/insurance.fodt` est pilote par le report `stock.shipment.in.insurance`. - Toutes les croix rouges / placeholders metier doivent etre remplacees par des proprietes `report_*` exposees sur `stock.shipment.in`. - Pour ce template, ne pas compter sur une variable Genshi locale `shipment` dans tout le document; preferer `records[0]....` dans le `.fodt`. - Source de verite du montant assure: - le `fee.fee` du shipment dont le produit contient `Insurance` - montant via `fee.get_amount()` ### TR-016 - Hypotheses actuelles pour le certificat d'assurance shipment - Tant qu'une source metier plus precise n'est pas fournie: - numero du certificat: `shipment.bl_number`, sinon `shipment.number` - `insured for account of`: client de la premiere ligne metier retrouvee via lot physique, sinon `shipment.supplier` - `surveyor`: `shipment.controller`, sinon fournisseur du fee `Insurance` - lieu/date d'emission: ville de la societe + date du jour - Si une source differente est decidee plus tard, corriger la propriete Python plutot que complexifier `insurance.fodt` ### 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 `"` / `'`. 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`