Template Rules - Purchase Trade
Statut: draft
Version: v0.4
Derniere mise a jour: 2026-04-07
1) Scope
- Domaine:
templates Relatorio .fodt - Modules concernes:
purchase_tradesaleaccount_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.fodtmodules/sale/sale.fodt- Reprendre en priorite la syntaxe Relatorio deja validee dans ces templates.
- Pour creer un nouveau report
.fodt, copier d'abord un template voisin qui fonctionne deja dans le meme module, puis remplacer uniquement les blocs metier necessaires. Ne pas demarrer avec un FODT minimal construit a la main: Relatorio et LibreOffice attendent une enveloppe ODT complete (office:meta, settings, namespacesdraw/xlink, styles, master pages, header/footer). Un XML valide peut quand meme echouer au parsing Relatorio ou produire un rendu inutilisable. - Exemple: pour
account/general_ledger_detail.fodt, partir deaccount/general_ledger.fodtet adapter les colonnes plutot que reconstruire la structure ODT.
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_addressrecord.report_pricerecord.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_tradeune extensionaccount.invoiceavec des proprietesreport_*qui relaient versinvoice.sales[0]. - Exemple de proprietes utiles:
report_addressreport_contract_numberreport_shipmentreport_product_descriptionreport_crop_namereport_attributes_namereport_pricereport_payment_datereport_nb_balereport_grossreport_netreport_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_termsreport_grossreport_netreport_qtreport_nb_balereport_dealreport_packingreport_pricereport_deliveryreport_payment_datereport_shipment
TR-006 - Penser au cache des reports facture avant d'accuser le .fodt
- Les actions de report
account.invoicepeuvent partager le meme moteur de rendu. - Dans
modules/account_invoice/invoice.py, le champinvoice_report_cachepeut 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 deir_action_report.report
TR-012 - Centraliser les templates client dans Document Templates
- Pour les templates client-specifiques, ne pas modifier
ir_action_report.reporten base a la main selon l'environnement ou le client. - Preferer la configuration singleton
purchase_trade.configuration, exposee dansDocuments > Configuration > Document Templates. - Sections actuellement attendues:
SaleInvoicePaymentPurchaseShipment- Dans la section
Shipment, les templates metier attendus sont: ShippingInsurancePacking List- 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.descriptiondans les.fodt. - Preferer:
sale.report_product_namesale.report_product_descriptioninvoice.report_product_nameinvoice.report_product_description
TR-014 - invoice_melya.fodt doit afficher Invoice et Reference sur les bons champs
- Pour
modules/account_invoice/invoice_melya.fodt: Invoicedoit afficherinvoice.numberReferencedoit afficherinvoice.report_contract_number- Ne pas reutiliser
invoice.referencepour 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.fodtest pilote par le reportstock.shipment.in.insurance. - Toutes les croix rouges / placeholders metier doivent etre remplacees par
des proprietes
report_*exposees surstock.shipment.in. - Pour ce template, ne pas compter sur une variable Genshi locale
shipmentdans tout le document; prefererrecords[0]....dans le.fodt. - Source de verite du montant assure:
- sommer les montants des
incoming_movesdu shipment - montant d'un move =
move.quantity * move.unit_price - si
move.unit_priceest vide, fallback via lot:lot.line.unit_price * lot.get_current_quantity_converted() - exposer au moins:
- le montant total des incoming moves
- le montant assure a
110%de ce total
- pour le placeholder
Amount insured,report_insurance_amountdoit afficher ce110%, avec fallback feeInsurancesi aucun montant incoming n'est calculable
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, sinonshipment.number insured for account of: client de la premiere ligne metier retrouvee via lot physique, sinonshipment.suppliersurveyor:shipment.surveyor, sinonshipment.controller, sinon fournisseur du feeInsurance- 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-017 - payment_order.fodt doit utiliser des proprietes report_payment_order_*
- Pour
modules/account_invoice/payment_order.fodt, ne pas utiliser des placeholders externes legacy (tokens metier entre<...>du systeme source). - Tous les placeholders du template doivent pointer vers des proprietes Python
stables exposees sur
account.invoice: report_payment_order_document_referencereport_payment_order_from_account_nbreport_payment_order_to_bank_namereport_payment_order_to_bank_cityreport_payment_order_amountreport_payment_order_currency_codereport_payment_order_amount_textreport_payment_order_value_datereport_payment_order_company_addressreport_payment_order_beneficiary_account_nbreport_payment_order_beneficiary_bank_namereport_payment_order_beneficiary_bank_cityreport_payment_order_swift_codereport_payment_order_other_instructionsreport_payment_order_referencereport_payment_order_current_userreport_payment_order_current_user_email- Eviter les marqueurs conditionnels heredites de l'ancien moteur (
++...): privilegier des placeholders simples avec fallbackor ''.
TR-018 - Un template configure n'apparait dans le form que si une action report existe
- Ajouter un champ dans
Document Templatesne suffit pas a rendre un template imprimable depuis la fiche. - Pour afficher l'entree dans
account.invoice, il faut aussi: - un
ir.action.reportsurmodel = account.invoice - un
ir.action.keywordform_printlie a cette action - Appliquer cette regle pour
Payment Ordercomme pourInvoice,PrepaymentetCN/DN.
TR-019 - Un placeholder Relatorio doit etre dans une balise text:placeholder
- Dans un
.fodt, une expression du type<records[0].report_* ...>ecrite en texte brut peut s'afficher telle quelle a l'impression. - Regle stricte:
- encapsuler les expressions dans
<text:placeholder text:placeholder-type="text">...</text:placeholder> - ne pas laisser de token
<...>directement dans untext:span,text:p,text:h, etc. - Exemple:
- incorrect:
PAYMENT ORDER <records[0].report_payment_order_document_reference or ''> - correct:
PAYMENT ORDER <text:placeholder text:placeholder-type="text"><records[0].report_payment_order_document_reference or ''></text:placeholder>
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.lineousale.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_datereport_loading_portreport_discharge_portreport_controller_namereport_si_numberreport_proforma_invoice_numberreport_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*, leFREIGHT VALUEdoit etre expose par une propriete Python du typeinvoice.report_freight_amount. - La logique attendue est:
- retrouver le lot physique pertinent
- retrouver son shipment
- chercher le
fee.feedu shipment avecproduct.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.descriptionpurchase.lines[0].del_period.description- Meme avec un
if ... else, ces acces sont fragiles dans un.fodtet rendent le debug plus difficile. - Preferer une propriete Python stable:
sale.report_delivery_period_descriptionpurchase.report_delivery_period_descriptioninvoice.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 lignebasisne doit pas etre le prix economique total (unit_price,linked_priceou prix basis brut). - La valeur a afficher est uniquement le
premium: - en devise/unite liee si
linked currencyest 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.fodtetinvoice_ict_final.fodt, la source de verite du nombre de bales n'est pas le poids (report_net,report_gross) maisline.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
- Identifier le placeholder exact qui provoque l'erreur Relatorio.
- Comparer sa syntaxe avec le template standard equivalent.
- Remplacer les guillemets/quotes non valides par
"/'. - Si l'expression devient trop longue, la deplacer dans une propriete Python
report_*. - Ne modifier que les placeholders necessaires.
- Regenerer le document pour verifier la prochaine erreur eventuelle.
- 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.pydes proprietes de pontaccount.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 depurchase_trade.sale