# AGENTS.md - Module `purchase_trade` Ce guide complete le `AGENTS.md` racine. Pour ce module, les regles locales ci-dessous priment. ## 1) Perimetre metier Le module `purchase_trade` etend les flux achat/vente Tryton avec une logique de negoce physique: - contrats d'achat (`purchase.purchase`, `purchase.line`) - contrats de vente (`sale.sale`, `sale.line`) - lots physiques et virtuels - matching achat/vente - shipments et execution logistique - frais (`fee.fee`) - templates de documents metier et facture ## 2) Fichiers pivots - Contrats achat: - `modules/purchase_trade/purchase.py` - Contrats vente: - `modules/purchase_trade/sale.py` - Lots / matching / invoicing: - `modules/purchase_trade/lot.py` - Shipments / lien facture-lot: - `modules/purchase_trade/stock.py` - Fees: - `modules/purchase_trade/fee.py` - Bridge facture / templates: - `modules/purchase_trade/invoice.py` - Vues: - `modules/purchase_trade/view/*.xml` - Actions module: - `modules/purchase_trade/*.xml` - Manifest: - `modules/purchase_trade/tryton.cfg` ## 3) Documentation locale a lire en priorite - Regles metier: - `modules/purchase_trade/docs/business-rules.md` - Documentation business publiee dans le wiki: - `modules/purchase_trade/docs/business/*.md` - Sources de verite de la documentation business generee: - `modules/purchase_trade/docs_source/business/*.md` - Regles templates: - `modules/purchase_trade/docs/template-rules.md` - Catalogue des proprietes templates: - `modules/purchase_trade/docs/template-properties.md` - Padding facture provisoire vente / validation comptable: - `modules/purchase_trade/docs/padding-invoice-accounting.md` ## 4) Invariants metier a preserver - Un lot `virtual` est la reference d'ouverture de quantite pour une `purchase.line`. - Une `sale.line` doit aussi avoir au minimum un lot `virtual`; une valuation cote sale ne doit donc pas disparaitre juste parce que le lot est `open`. - Le lot physique est le pont principal entre: - `purchase.line` - `sale.line` - shipment - facture - Pour remonter d'une facture vers shipment / BL / controller / fret: - privilegier le lot physique - ne pas multiplier des chemins d'acces concurrents - Pour les champs de colis (`NB BALES`) dans les templates facture: - la source de verite est `line.lot.lot_qt` - sur une facture, sommer les `lot_qt` des lignes de facture - tenir compte du signe de la ligne de facture pour les notes finales - ne pas proratiser depuis le poids (`net` / `gross`) - Le `FREIGHT VALUE` d'un template facture vient du `fee.fee` du shipment dont le produit est `Maritime freight`. - Pour `stock/insurance.fodt`, le `Amount insured` doit venir en priorite de `110%` du total des `incoming_moves` (fallback fee `Insurance` si aucun montant incoming calculable). - Pour le surveyor du certificat d'assurance shipment, la priorite est: `shipment.surveyor` -> `shipment.controller` -> fournisseur du fee `Insurance`. - Pour `payment_order.fodt`, utiliser des proprietes `invoice.report_payment_order_*` plutot que des tokens legacy `<...>`. - Ajouter un champ de template dans `Document Templates` ne rend pas le report visible dans la fiche: il faut aussi l'action `ir.action.report` + `ir.action.keyword` (`form_print`) cote `account.invoice`. - Le wizard `Create contracts` en mode `matched` peut maintenant partir de plusieurs `lot.qt`, mais doit conserver un matching par lot source et laisser `created_by_code = True` sur les lignes creees pour ne pas declencher les creations automatiques de lots dans les validations. - En valuation / PnL: - la valeur stockee dans `type` est la cle technique (`pur. priced`, `sale priced`, `pur. fee`, etc.), pas le label affiche dans l'UI - les references doivent rester coherentes avec le type de lot: `Purchase/Open`, `Purchase/Physic`, `Sale/Open`, `Sale/Physic` - pour une sale matchee, les lignes de valuation purchase generees sur un lot physique doivent aussi renseigner `sale` et `sale_line` afin de remonter dans l'onglet PnL de la sale - une sale non matchee doit etre valorisable "sale-first" et alimenter `valuation.valuation` / `valuation.valuation.line` - si une `sale.line` `basis` n'a ni `price_summary` ni `lot_price_sale`, creer quand meme une ligne `sale priced` avec `price = 0` et `amount = 0` plutot que de ne rien generer - le MTM ne doit etre renseigne que pour `pur. priced`, `sale priced` et `derivative`; jamais pour les fees - `mtm_price` doit afficher le prix brut de valorisation (sans ratio), alors que `mtm` reste le montant calcule selon la logique de strategie - En pricing: - le `unit_price` doit rester un prix de base, hors `premium` - le `premium` doit impacter le prix total economique et donc le `amount`, aussi bien en `priced` qu'en `basis` - dans `pricing.pricing` en saisie manuelle, l'utilisateur renseigne seulement `quantity` et `settl_price` - `fixed_qt`, `fixed_qt_price`, `unfixed_qt`, `unfixed_qt_price` et `eod_price` sont des valeurs derivees et ne doivent pas etre saisies a la main - en manuel, `fixed_qt` = cumul des `quantity` du groupe trie par `pricing_date` - en manuel, `fixed_qt_price` = moyenne ponderee cumulee des `settl_price` - en manuel, `unfixed_qt_price` = `settl_price` de la ligne - pour les documents commerciaux / facture, une ligne `basis` affiche le `premium` comme prix visible, pas le prix economique total - si `linked currency` est active, le `premium` est saisi dans la devise / unite liee (ex: `USC/LB`) puis converti vers le repere de la ligne pour le calcul du `amount` - en `basis + linked currency`, le `linked_price` doit representer le prix basis brut (hors premium) dans la devise liee; le `unit_price` reste ce prix brut converti, et le `premium` converti est ajoute seulement dans l'`amount` - si `linked currency` est cochee, `linked_price`, `linked_currency` et `linked_unit` sont requis - dans les forms, presenter le bloc prix dans l'ordre: `price_type` -> linked fields -> `premium` -> `unit_price` -> `amount` - en valuation `basis`, le premium s'applique a chaque composant, pas uniquement a une ligne de resume - pour une ligne `basis` sans `price_summary`, la valuation fallback doit utiliser `unit_price + premium` (et pas `unit_price` seul) - a la validation d'une `sale.line`, si un lot virtuel est cree et qu'aucun matching purchase n'existe, il faut lancer `generate_from_sale_line()` pour alimenter le PnL sale-first - En padding de facture provisoire vente: - le padding est saisi globalement dans `lot.invoice` - il est uniquement applique aux factures provisoires cote vente - il est reparti par lot et stocke dans `lot.sale_invoice_padding` - la ligne facture affiche `Inc. padding` depuis le lot - le padding augmente `account.invoice.line.quantity`, donc le move principal de facture inclut deja le montant padding - les comptes de padding viennent de `account.configuration`: `Default Sale Padding` et `Default Accrual Padding` - la section `Padding` doit rester avant la section `Invoice` dans `Financial / Configuration` - la provisoire cree un `additional_move` debit `Default Sale Padding` / credit `Default Accrual Padding` - la finale cree l'inverse en reprenant le montant depuis `lot.sale_invoice_line_prov`, donc avec le prix, la devise, la date et le taux de la provisoire - le wizard final doit calculer son delta depuis la quantite provisoire hors padding: `sale_invoice_line_prov.quantity - sale_invoice_padding` - En modification de quantite contractuelle: - `quantity_theorical` est la source contractuelle; `quantity` peut refleter l'execute physique. - le `lot.qt` libre doit etre resynchronise sur le solde ouvert reel: `quantity_theorical - lots physiques - lot.qt deja matches/shippes`. - ne jamais ajuster le `lot.qt` libre uniquement par delta si des lots physiques existent deja. - les fees suivent les lots effectifs: physiques s'il en existe dans `fee.lots`, sinon virtuel. - conserver le lien `fee.lots` vers le virtuel comme fallback; ne pas le supprimer lors de la creation d'un physique. - le PnL fee applique la meme regle: full open tant qu'il n'y a que le virtuel, puis uniquement les physiques. - detail durable: `modules/purchase_trade/docs/business-rules.md` BR-PT-020 / BR-PT-021. - En execution controller / SLA: - les objectifs de repartition controller utilisent `party.execution.area` (`country.region`). - les couts SLA utilisent `party.execution.place` et peuvent matcher par `country`, par `location`, ou par couple `country + location`. - la creation du fee controller depuis shipment part de `shipment.to_location`; le pays de matching est `shipment.to_location.country`. - priorite cout SLA: couple pays+location, puis location seule, puis pays seul. ## 5) Conventions de modification ### Documentation business - Ne pas modifier directement une page generee sous `modules/purchase_trade/docs/business/` si elle contient le commentaire `Generated from docs_source/business`. - Toute regle business nouvelle ou modifiee doit etre editee dans `modules/purchase_trade/docs_source/business/`. - Apres modification des sources business, regenerer le wiki avec: `python modules/purchase_trade/docs/tools/render_business_docs.py` - Avant de rendre une modification documentaire, verifier que le rendu publie est synchronise avec: `python modules/purchase_trade/docs/tools/render_business_docs.py --check` - Les pages francaises et anglaises miroir doivent rester synchronisees. - Le rendu publie doit rester lisible dans MkDocs meme sans extensions optionnelles: eviter les marqueurs bruts `!!!` et `:material-...:` dans les fichiers publies. 1. Modifier la logique metier dans le fichier pivot le plus proche. 2. Si un template `.fodt` devient complexe, deplacer la logique dans une propriete Python `report_*`. 3. Pour une facture trade, preferer enrichir `modules/purchase_trade/invoice.py` plutot que surcharger lourdement le `.fodt`. 4. Si une regle metier durable change, mettre a jour `docs/business-rules.md`. 5. Si une convention de template change, mettre a jour `docs/template-rules.md`. 6. Pour les vues XML Tryton de ce module, utiliser `editable="1"` sur les `` editables; ne pas utiliser `editable="bottom"`. 7. Ne pas ajouter `keyword_open` / `open_keyword` sur les vues ``: ces attributs ne sont pas supportes par Tryton. 8. Si une regle de texte par defaut durable est demandee sur achat/vente, preferer un singleton de configuration expose dans un menu fonctionnel existant plutot qu'un menu technique `purchase_trade`. ## 6) Pieges connus - Plusieurs actions de report `account.invoice` peuvent sembler rendre le meme document a cause du cache `invoice_report_cache`. - Les reports alternatifs (`Final Invoice`, `Prepayment`, etc.) ne doivent pas reutiliser le cache du report standard sans verification. - Pour les donnees achat/vente partagees, ne pas supposer qu'une facture de vente doit lire directement sur la `sale.line`: souvent, la verite metier passe par le lot physique et/ou la `account.invoice.line`. - Les templates `invoice_ict*` peuvent partager les memes proprietes Python; si une regle doit valoir pour provisional et final, la mettre dans `modules/purchase_trade/invoice.py` plutot que dupliquer dans les `.fodt`. - Dans les ecrans PnL, le label `Sale price` correspond au type stocke `sale priced`; idem pour `Pur. price` / `pur. priced`. - Une ligne `basis` sans resume de pricing peut sinon disparaitre de la valuation si aucun fallback explicite a `0` n'est prevu. - Le calcul du prix peut diverger entre `unit_price`, `linked_price`, `lot_price` et valuation si le premium n'est pas traite explicitement dans chaque maillon. - Sur `account.invoice`, le workflow `Validate` doit maintenant aligner fournisseur et client pour: - creation du `account.move` - attribution du `number` - `Post` ne doit pas reintroduire une difference de session/fresh login cote client - Pour le padding provisoire vente, ne pas changer la quantite physique du lot: l'ecart doit rester porte par `lot.sale_invoice_padding` et visible sur la facture via `Inc. padding`. ## 7) Definition of done (module `purchase_trade`) - Le flux achat/vente/lot cible reste coherent. - Les impacts templates/facture ont ete verifies conceptuellement. - Les docs locales ont ete mises a jour si une nouvelle regle durable a emerge. - Le patch reste minimal et local au domaine demande.