9.0 KiB
9.0 KiB
Business rules
Memo racine pour les regles metier transverses ou multi-modules.
Pour les regles propres a un module, preferer la documentation locale quand elle existe, par exemple:
modules/purchase_trade/docs/business-rules.mdmodules/purchase_trade/docs/padding-invoice-accounting.md
Session 2026-04-28 - Devises, TVA, padding et lots
account.move.line / saisie manuelle en seconde devise
rateest un vrai champ stocke et editable, plus seulement un champ de visualisation.- Le sens du taux suit l'usage Tryton: taux devise seconde / devise societe.
Donc, avec une devise societe a 1, le montant societe se calcule par
amount_second_currency / rate. - Une ligne est consideree comme manuelle si elle n'a ni
origin, nimove_origin, nimove.origin. - En manuel, si l'utilisateur saisit d'abord
amount_second_currency, le systeme reprend le dernier taux disponible pour la devise seconde a la date de la ligne, puis calculedebitoucredit. - Si l'utilisateur veut forcer le taux, il efface d'abord
debit/credit, saisitrate, et le montant societe est recalcule depuis le montant en devise seconde. - Si le montant societe et le montant devise seconde sont deja presents, le
taux implicite est
abs(amount_second_currency) / abs(base_amount). - Les lignes generees automatiquement depuis facture doivent aussi renseigner
ratequandamount_second_currencyexiste. - Un fallback central sur
account.move.line.createcalcule le taux manquant depuis les montants pour eviter les chemins oublies. - Comme
ratedevient stocke, une mise a jour du module est necessaire; les anciennes lignes doivent etre regenerees ou backfillees si on veut afficher le taux historique.
Causes racines des bugs devises
- Deux
on_change_amount_second_currencyexistaient; le second ecrasait la logique de conversion. - La branche negative testait deux fois
> 0, donc un montant devise seconde negatif ne generait pas correctement le credit. - Le taux initial etait calcule a l'envers par rapport au standard Tryton attendu.
- Les records transitoires Tryton peuvent ne pas exposer un champ non assigne;
les chemins generiques doivent utiliser
getattr(record, field, None).
account.invoice / taux et devise
on_change_with_ratedepend explicitement decurrency,company,invoice_date,selection_rate,rate_dateetlines.- Ne pas declarer
lines.amountdans@fields.depends: ce champ relationnel n'existe pas cote definition de vue et provoque unKeyError. - Les move lines de facture, lignes produit, lignes stock/drop et lignes TVA
doivent renseigner
ratedes qu'elles portentamount_second_currency.
TVA
- Les taxes sur
invoice.lines[].taxesalimentent les taxes calculees de la facture via les lignes taxables. - Les taxes globales de facture (
invoice.taxes) peuvent etre automatiques ou manuelles. - Les taxes manuelles sont preservees par
update_taxes; les taxes automatiques sont synchronisees, supprimees ou recreees selon les lignes. - Une ligne facture genere des
account.tax.linede typebase. - Une ligne de taxe facture genere une move line comptable avec
account.tax.linede typetax; si la taxe est manuelle, une lignebasecomplementaire peut etre ajoutee. - La seconde devise est portee par
account.move.line(second_currency/amount_second_currency), pas paraccount.tax.linequi suit la devise comptable de la move line.
Padding
- Les chemins padding doivent verifier que
lotexiste avant de liresale_invoice_line_provousale_invoice_line. - Le bug observe n'etait pas la logique padding elle-meme mais un acces direct a un lot absent sur certaines lignes de facture.
Payment term
- Le calcul des echeances doit fonctionner meme si la premiere ligne facture
n'a pas d'
origin. term_linesdoit etre calcule hors de la branche qui depend du modele d'origine, avec une ligne metier optionnelle.
Periodes comptables
Period.findutilise un cache par societe/date/etat.- Une periode creee ou ouverte peut continuer a etre vue comme absente par un worker tant que le cache ou le serveur n'a pas ete rafraichi.
lot.qt / validation sale et purchase
- Cote
sale.line, la creation de la ligne cree deja le lot virtuel et sa quantite. - Le passage initial de
quantity_theoricaldeNonea la quantite de la ligne ne doit pas etre traite comme un delta physique; sinonlot.qtest double. - Le correctif sale ignore le delta quand l'ancienne valeur theorique est
None; les modifications suivantes restent bien appliquees en delta. - Cote
purchase.line, la logique etait deja protegee par une baseline sur la quantite courante du lot virtuel quand l'ancienne valeur estNone.
Valuation / PNL achat
- Une purchase line peut ne pas produire de valorisation si elle est finie,
sans lot, filtree par
valuation_type, sanslot_pricepour les modespriced/efp, sans fees relies a des lots, ou sans derivatives. - En mode
basis, sans composant, le fallback metier continue de passer par la ligne summary quand elle existe.
Session 2026-04-29 - Purchase trade: rate fee et PnL lots ouverts
fee.fee / mode % rate
- Le calcul du montant d'un fee en mode
rateest aligne entre purchase et sale. - Il ne depend pas du
payment_term. - Il ne depend pas de la date du jour ni de
fee_date. - La periode de financement est directement
fin_int_deltasur la ligneEstimated dateavectrigger = bldate. - La formule reste en interet simple ACT/360:
amount = rate_factor * unit_price * quantity, avecrate_factor = (price / 100) * fin_int_delta / 360. - Si aucune Estimated Date
bldaten'est renseignee, le fee% ratene calcule pas de montant.
Valuation / PnL avec lots ouverts matches
- Tant que les lots physiques ne sont pas crees, une purchase et une sale
peuvent etre matchees uniquement via
lot.qt:lot_ppurchase ouvert ->lot_ssale ouvert. - Dans ce cas, les lignes PnL purchase-side (
Pur. price,Pur. fee) doivent aussi renseignersaleetsale_linepour apparaitre dans l'onglet PnL de la sale matchee. - Quand le lot physique existe,
lot.sale_linereste le chemin prioritaire. - Si un lot purchase ouvert est matche a plusieurs sales differentes, ne pas attacher arbitrairement une seule sale aux lignes purchase-side.
Mark as finishedne veut pas dire "ne plus calculer le PnL": il ignore seulement le reliquat ouvert/virtuel; les lots physiques, fees physiques et derivatives continuent d'etre valorises.
Session 2026-04-30 - PnL fees ouverts et % rate
PnL fees sur lots ouverts
- Un lot ouvert / virtuel avec quantite courante a zero ne doit plus generer de lignes de fees PnL.
- Cette regle suit la meme intention que
Mark as finished: le reliquat ouvert/virtuel ignore ne doit pas porter de PnL, y compris pour les fees. - Les lots physiques restent valorisables; le filtre vise seulement les lots non physiques vides.
- Le cas important est celui des fees
rate,ppackoulumpsum, dont le montant peut rester non nul meme quand la quantite affichee du lot est zero.
Fee % rate
fin_int_deltaest la periode absolue de calcul du financement.- Le montant ne depend pas de
Date.today(), defee_date, ni d'un intervalle entre la date du jour etBL date + delta. - Purchase et sale appliquent la meme formule ACT/360:
amount = unit_price * quantity * (price / 100) * fin_int_delta / 360. - La ligne
Estimated dateavectrigger = bldatereste le support metier pour porterfin_int_delta; la date estimee elle-meme ne sert pas au calcul du montant% rate.
Session 2026-05-01 - Solde ouvert apres lots physiques
purchase.line / sale.line et lot.qt
- Une modification de
quantity_theoricalne doit plus ajuster lelot.qtlibre uniquement par delta. - Le solde ouvert est recalcule en tenant compte des lots physiques deja crees:
quantity_theorical - somme(lots physiques). - Le
lot.qtlibre non matche / non shippe est ensuite aligne sur ce solde moins leslot.qtdeja matches ou shippes. - Si ce calcul donne un solde negatif, la modification est bloquee avec
Please unlink or unmatch lot. - Cas de reference: une ligne achat passee de
10000a20000avec deja10000physiques doit rester a10000ouverts et10000physiques.
fee.fee / fee.lots
- Le lien entre un fee et son lot virtuel est conserve comme fallback.
- Des qu'un fee possede au moins un lot physique dans
fee.lots, les lots physiques deviennent la base effective defee.quantity. - Une hausse purement ouverte de
quantity_theoricaln'impacte donc pas un fee deja porte par des lots physiques. - En
ppack,fee.quantitysuit la somme deslot_qtphysiques; dans les autres modes quantitatifs, elle suit la somme des quantites courantes des lots physiques. - A la suppression du dernier physique, le fee retombe sur son lot virtuel.
- Le PnL fee suit la meme logique: full open tant que seul le virtuel existe, puis uniquement les lots physiques effectifs.