From 09c78395f14357eca9aaf9e2d048c581eca12336 Mon Sep 17 00:00:00 2001 From: laurentbarontini Date: Mon, 11 May 2026 20:19:44 +0200 Subject: [PATCH] Bug accrual fee + invoice sale side --- .../business-rules-architecture-proposal.md | 163 ++++++++++++++++++ modules/purchase_trade/fee.py | 44 +++-- modules/purchase_trade/lot.py | 10 +- 3 files changed, 200 insertions(+), 17 deletions(-) create mode 100644 modules/purchase_trade/docs/business-rules-architecture-proposal.md diff --git a/modules/purchase_trade/docs/business-rules-architecture-proposal.md b/modules/purchase_trade/docs/business-rules-architecture-proposal.md new file mode 100644 index 0000000..31f2ebd --- /dev/null +++ b/modules/purchase_trade/docs/business-rules-architecture-proposal.md @@ -0,0 +1,163 @@ +# Proposition d'architecture des regles business CTRM + +Statut: `proposal` +Date: `2026-05-11` +Scope: documentation metier `purchase_trade` / CTRM + +## Objectif + +Cette note garde la proposition de structure documentaire pour les regles +business trading. L'objectif est de pouvoir s'appuyer sur une source de verite +exhaustive sans charger trop de contexte quand une demande concerne seulement +un theme precis. + +## Etat des lieux + +- `modules/AGENTS.md` porte les regles courtes communes aux modules. +- `modules/purchase_trade/AGENTS.md` porte les invariants critiques du module + trading et les fichiers pivots. +- `modules/purchase_trade/docs/business-rules.md` est la base metier la plus + riche, mais elle melange regles canoniques, notes de session et quelques + doublons de numerotation. +- `notes/business_rules.md` sert de journal transverse de sessions. +- `notes/accounting/` documente plutot le standard comptable Tryton, les gaps + de reporting et les extensions comptables `purchase_trade`. +- Les fichiers specialises comme `fees.md`, + `padding-invoice-accounting.md`, `template-rules.md` et + `template-properties.md` sont de bons exemples de documentation ciblee. + +## Principe recommande + +Garder `purchase_trade` comme source de verite canonique pour le metier CTRM, +mais decouper les regles par theme. + +`AGENTS.md` doit rester court et servir de routeur: + +- charger seulement les invariants toujours utiles; +- indiquer quel fichier thematique lire selon la demande; +- eviter d'embarquer toute la documentation business a chaque intervention. + +Les notes de session doivent rester historiques. Une decision devient +canonique seulement quand elle est promue dans un fichier thematique. + +## Structure cible proposee + +```text +modules/purchase_trade/docs/business/ + README.md + INDEX.md + glossary.md + invariants.md + sessions.md + + contracts.md + lots-and-quantities.md + matching.md + shipments-execution.md + pricing.md + fees.md + valuation-pnl-mtm.md + invoicing.md + accounting-bridge.md + payments-banking.md + reports-templates.md + risk-credit-forex.md + lots-management.md +``` + +## Role des fichiers + +- `README.md`: explique la convention documentaire et le statut des fichiers. +- `INDEX.md`: routeur par theme, avec les fichiers a lire selon le sujet. +- `glossary.md`: vocabulaire stable du CTRM Tradon. +- `invariants.md`: principes structurants toujours vrais. +- `sessions.md`: journal historique non canonique. +- fichiers thematiques: source canonique exhaustive pour chaque domaine. + +## Grands themes metier + +- `contracts.md`: achats, ventes, lignes, dates, lieux, banques, + counterparties, quantite contractuelle, statut finished. +- `lots-and-quantities.md`: lots virtuels/physiques, `lot.qt`, historique, + weighing, split/merge, suppression de lot physique, solde ouvert. +- `matching.md`: matching purchase/sale, open matching via `lot.qt`, wizard + `Create contracts`, back-to-back, multi-lots. +- `shipments-execution.md`: shipments, dropship/inbound, controller, SLA, + locations, surveyor, weight reports, BL. +- `pricing.md`: `priced`, `basis`, components, manual pricing, summary, + linked currency, premium, settlement price. +- `fees.md`: fees ligne/shipment, lots effectifs, `% rate`, `ppack`, + maritime freight, insurance, declencheurs PnL. +- `valuation-pnl-mtm.md`: valuation achat/vente, sale-first, references, + MTM strategies, derivatives, exclusions fees. +- `invoicing.md`: provisional/final, facture achat/vente, padding, quantites + facturees, liens lot/facture, templates invoice. +- `accounting-bridge.md`: effets sur `account_invoice`, `account.move`, + taxes, devises, additional moves, validate/post. +- `payments-banking.md`: payment terms, echeances, comptes bancaires + party/company, payment order. +- `reports-templates.md`: Relatorio, proprietes `report_*`, documents trade, + regles XML, cache report. +- `risk-credit-forex.md`: credit risk, forex, FX revaluation, exposition. +- `lots-management.md`: rapports operationnels, filtres matching/side/shipping, + dimension, strategy. + +## Format conseille pour une regle + +```md +### BR-PT-VAL-007 - Le MTM ne s'applique pas aux fees + +Theme: valuation-pnl-mtm +Status: active +Scope: purchase_trade +Code: valuation.py, fee.py +Depends on: BR-PT-FEE-002 + +Summary: Les lignes de fee ne portent jamais mtm, mtm_price ou strategy. + +Rule: +- MTM autorise seulement pour `pur. priced`, `sale priced`, `derivative`. +- Fees: `pur. fee`, `sale fee`, `shipment fee`, `line fee` restent hors MTM. + +Tests: +- valuation fee sans mtm +- valuation priced avec mtm +``` + +Les identifiants devraient encoder le theme (`LOT`, `MAT`, `PRI`, `FEE`, +`VAL`, `INV`, `ACC`, etc.) plutot qu'une numerotation globale unique. Cela +evite les collisions quand les regles sont ajoutees au fil des sessions. + +## Routeur de contexte a ajouter plus tard dans `AGENTS.md` + +```md +Si la demande concerne: +- lots, quantites, matching: lire `docs/business/lots-and-quantities.md` et + `docs/business/matching.md` +- pricing, premium, basis: lire `docs/business/pricing.md` +- fees, freight, insurance: lire `docs/business/fees.md` +- valuation, PnL, MTM: lire `docs/business/valuation-pnl-mtm.md` +- facture, padding, payment order: lire `docs/business/invoicing.md` et + `docs/business/accounting-bridge.md` +- templates `.fodt`: lire `docs/business/reports-templates.md` et + `docs/template-rules.md` +``` + +## Migration proposee + +1. Creer `docs/business/INDEX.md`, `invariants.md` et `glossary.md`. +2. Decouper `docs/business-rules.md` par theme, sans changer le fond. +3. Deplacer les notes de session dans `sessions.md`. +4. Promouvoir uniquement les decisions durables dans les fichiers thematiques. +5. Reduire `modules/purchase_trade/AGENTS.md` a un memo court + routeur. +6. Garder `notes/accounting/` comme documentation comptable Tryton et comme + vue specialisee des extensions comptables `purchase_trade`. + +## Risque actuel a traiter lors de la migration + +Le risque principal n'est pas le manque de regles, mais leur dilution entre +plusieurs fichiers. Certaines decisions existent a la fois dans `AGENTS.md`, +`notes/business_rules.md`, `business-rules.md`, `fees.md` et les notes +accounting. La migration devra designer une source canonique par theme et +laisser les autres fichiers jouer leur role d'index, de journal ou de vue +specialisee. diff --git a/modules/purchase_trade/fee.py b/modules/purchase_trade/fee.py index 8f1b3af..d0b8e34 100755 --- a/modules/purchase_trade/fee.py +++ b/modules/purchase_trade/fee.py @@ -569,10 +569,28 @@ class Fee(ModelSQL,ModelView): # def validate(cls, fees): # super(Fee, cls).validate(fees) - @classmethod - def create(cls, vlist): - vlist = [x.copy() for x in vlist] - fees = super(Fee, cls).create(vlist) + @classmethod + def _save_fee_accrual_or_warn(cls, account_move, fee, lot): + AccountMove = Pool().get('account.move') + if account_move: + AccountMove.save([account_move]) + return + + Warning = Pool().get('res.user.warning') + warning_name = Warning.format( + "Fee accrual not generated", [fee.product, lot]) + if Warning.check(warning_name): + raise UserWarning( + warning_name, + "The fee can be saved, but the accrual entry could not be " + "generated because the stock accounting configuration or " + "accounting period is missing or incomplete. Do you want to " + "continue without creating the accrual entry?") + + @classmethod + def create(cls, vlist): + vlist = [x.copy() for x in vlist] + fees = super(Fee, cls).create(vlist) qt_sh = Decimal(0) qt_line = Decimal(0) unit = None @@ -677,16 +695,16 @@ class Fee(ModelSQL,ModelView): if move: Warning = Pool().get('res.user.warning') warning_name = Warning.format("Lot ever received", []) - if Warning.check(warning_name): - raise UserWarning(warning_name, - "By clicking yes, an accrual for this fee will be created") - AccountMove = Pool().get('account.move') - account_move = move._get_account_stock_move_fee(fee) - AccountMove.save([account_move]) - else: - AccountMove = Pool().get('account.move') + if Warning.check(warning_name): + raise UserWarning(warning_name, + "By clicking yes, an accrual for this fee will be created") + account_move = move._get_account_stock_move_fee(fee) + cls._save_fee_accrual_or_warn( + account_move, fee, fl.lot) + else: account_move = fee._get_account_move_fee(fl.lot) - AccountMove.save([account_move]) + cls._save_fee_accrual_or_warn( + account_move, fee, fl.lot) cls._regenerate_fee_pnl(fees=fees) return fees diff --git a/modules/purchase_trade/lot.py b/modules/purchase_trade/lot.py index 964786a..c1f54ef 100755 --- a/modules/purchase_trade/lot.py +++ b/modules/purchase_trade/lot.py @@ -3057,10 +3057,12 @@ class LotInvoice(Wizard): # Warning = Pool().get('res.user.warning') # warning_name = Warning.format("Lot not confirmed", []) # if Warning.check(warning_name): - # raise QtWarning(warning_name, - # "Lot not confirmed, click yes to confirm and invoice") - # continue - if lot.invoice_line: + # raise QtWarning(warning_name, + # "Lot not confirmed, click yes to confirm and invoice") + # continue + if self.inv.type == 'purchase' and lot.invoice_line: + continue + if self.inv.type == 'sale' and lot.sale_invoice_line: continue lots.append(lot)