Files
tradon/modules/purchase_trade/docs/template-rules.md

14 KiB

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_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
    • Payment
    • Purchase
    • Shipment
  • Dans la section Shipment, les templates metier attendus sont:
    • Shipping
    • Insurance
    • Packing 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.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:
    • sommer les montants des incoming_moves du shipment
    • montant d'un move = move.quantity * move.unit_price
    • si move.unit_price est 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_amount doit afficher ce 110%, avec fallback fee Insurance si 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, sinon shipment.number
    • insured for account of: client de la premiere ligne metier retrouvee via lot physique, sinon shipment.supplier
    • surveyor: shipment.surveyor, sinon 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-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_reference
    • report_payment_order_from_account_nb
    • report_payment_order_to_bank_name
    • report_payment_order_to_bank_city
    • report_payment_order_amount
    • report_payment_order_currency_code
    • report_payment_order_amount_text
    • report_payment_order_value_date
    • report_payment_order_company_address
    • report_payment_order_beneficiary_account_nb
    • report_payment_order_beneficiary_bank_name
    • report_payment_order_beneficiary_bank_city
    • report_payment_order_swift_code
    • report_payment_order_other_instructions
    • report_payment_order_reference
    • report_payment_order_current_user
    • report_payment_order_current_user_email
  • Eviter les marqueurs conditionnels heredites de l'ancien moteur (++...): privilegier des placeholders simples avec fallback or ''.

TR-018 - Un template configure n'apparait dans le form que si une action report existe

  • Ajouter un champ dans Document Templates ne suffit pas a rendre un template imprimable depuis la fiche.
  • Pour afficher l'entree dans account.invoice, il faut aussi:
    • un ir.action.report sur model = account.invoice
    • un ir.action.keyword form_print lie a cette action
  • Appliquer cette regle pour Payment Order comme pour Invoice, Prepayment et CN/DN.

TR-019 - Un placeholder Relatorio doit etre dans une balise text:placeholder

  • Dans un .fodt, une expression du type &lt;records[0].report_* ...&gt; 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 &lt;...&gt; directement dans un text:span, text:p, text:h, etc.
  • Exemple:
    • incorrect: PAYMENT ORDER &lt;records[0].report_payment_order_document_reference or &apos;&apos;&gt;
    • correct: PAYMENT ORDER <text:placeholder text:placeholder-type="text">&lt;records[0].report_payment_order_document_reference or &apos;&apos;&gt;</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.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