16 KiB
16 KiB
Business Rules - Purchase Trade
Statut: draft
Version: v0.4
Derniere mise a jour: 2026-04-02
Owner metier: a completer
Owner technique: a completer
1) Scope
- Domaine:
purchase_trade - Hors scope:
- Modules impactes:
purchase_tradelot
2) Glossaire
Purchase Line: ligne d'achat.quantity_theorical: quantite theorique contractuelle de la ligne.Virtual Lot: lot unique de typevirtualrattache a unepurchase.line.lot.qt: table des quantites ouvertes, matchées ou shippées par lot.lot.qt ouvert: enregistrementlot.qtaveclot_p = virtual lot,lot_s = Noneet sans shipment.
3) Regles metier
BR-PT-001 - Ajustement de la quantite theorique apres creation du contrat
- Intent: conserver la coherence entre la quantite theorique de la ligne d'achat, le lot virtuel associe et les quantites ouvertes stockees dans
lot.qt. - Description:
- Quand
purchase.line.quantity_theoricalest modifiee apres creation du contrat, le systeme doit recalculer le delta entre l'ancienne et la nouvelle valeur. - La regle s'applique au lot unique de type
virtualrattache a lapurchase.line.
- Quand
- Conditions d'entree:
- Une
purchase.lineexiste deja. - Son champ
quantity_theoricalest modifie viawrite. - Un lot
virtualest rattache a la ligne.
- Une
- Resultat attendu:
- Si
delta > 0:- augmenter la quantite courante du lot
virtualviaset_current_quantitypour conserver l'historiquelot.qt.hist - augmenter le
lot.qtouvert existant - si aucun
lot.qtouvert n'existe, en creer un nouveau avec le delta
- augmenter la quantite courante du lot
- Si
delta < 0:- diminuer le
lot.qtouvert uniquement si la quantite ouverte disponible est suffisante - diminuer la quantite courante du lot
virtualdu meme delta - si aucun
lot.qtouvert n'existe ou si sa quantite est insuffisante, bloquer avec l'erreurPlease unlink or unmatch lot
- diminuer le
- Si
- Definition du
lot.qtouvert:lot_p = virtual lotlot_s = Nonelot_shipment_in = Nonelot_shipment_internal = Nonelot_shipment_out = None
- Exceptions:
- si aucun lot
virtualn'est trouve sur la ligne, la regle ne fait rien
- si aucun lot
- Priorite:
bloquante
- Source:
Decision metier documentee dans les commentaires de purchase_trade.purchase.Line.write
BR-PT-002 - Le lot physique est le pont metier entre purchase, sale et shipment
- Intent: disposer d'un chemin unique et stable pour retrouver les informations logistiques et de facturation reliees a un contrat d'achat ou de vente.
- Description:
- Le lot physique (
lot_type = physic) porte simultanement le lien vers:- la
purchase.linevialot.line - la
sale.linevialot.sale_line - le shipment via
lot.lot_shipment_in/lot.lot_shipment_internal/lot.lot_shipment_out
- la
- Pour toute logique qui doit naviguer entre achat, vente, shipment et facture, il faut privilegier ce lot physique comme source de verite.
- Le lot physique (
- Resultat attendu:
- depuis une facture d'achat:
- remonter a la
purchase.line - puis au lot physique de la ligne
- puis au shipment et aux donnees logistiques associees
- remonter a la
- depuis une facture de vente:
- remonter a la
sale.line - puis au lot physique matchant qui porte aussi la
purchase.line - puis au shipment et aux donnees logistiques associees
- remonter a la
- depuis une facture d'achat:
- Cas d'usage typiques:
- recuperer
bl_date,bl_number,controller,from_location,to_location - retrouver une facture provisoire liee au lot
- retrouver des fees rattaches au shipment
- recuperer
- Priorite:
structurante
BR-PT-003 - Le freight amount des templates facture vient du fee de shipment
- Intent: afficher dans les documents facture la vraie valeur de fret maritime rattachee au shipment du lot physique.
- Description:
- Le
FREIGHT VALUEd'une facture ne doit pas etre pris sur la facture elle-meme. - Il doit etre calcule a partir du
fee.feerattache au shipment (shipment_in) du lot physique relie a la facture.
- Le
- Regle de navigation:
- retrouver le lot physique pertinent depuis la facture
- retrouver son shipment
- chercher le
fee.feeavec:shipment_in = shipment.idproduct.name = 'Maritime freight'
- utiliser
fee.get_amount()comme montant de fret
- Portee:
- s'applique aussi bien aux factures d'achat qu'aux factures de vente
- cote vente, la remontee doit passer par le lot physique qui fait le lien entre
purchase.lineetsale.line
- Priorite:
importante
BR-PT-004 - La valuation doit couvrir les flux purchase et sale, y compris les sales non matchees
- Intent: obtenir un PnL coherent cote achat et cote vente, meme lorsqu'une sale n'est pas encore matchee a une purchase.
- Description:
- Le flux historique de valuation part de
purchase.linepuis remonte vers les ventes via les lots/lots matchants. - Le systeme doit egalement savoir valoriser directement une
sale.linenon matchee ("sale-first"). - Une sale non matchee doit creer des lignes dans
valuation.valuationetvaluation.valuation.lineafin d'apparaitre dans l'onglet PnL de la sale.
- Le flux historique de valuation part de
- Resultat attendu:
- pour une
sale.linenon matchee, generer au minimum les types:sale pricedsale feederivativesi la ligne porte des derives
- si la sale est matchee via un lot physique, les lignes purchase portees par
ce lot physique doivent aussi renseigner
saleetsale_line - une sale matchee doit donc voir:
- ses lignes
sale * - les lignes purchase portees par le lot physique partage
- ses lignes
- pour une
- Priorite:
structurante
BR-PT-005 - Les references de valuation doivent decrire la nature du lot de la ligne
- Intent: eviter les ambiguïtes dans les ecrans PnL entre lots
openet lotsphysic. - Description:
- La reference affichee dans la valuation doit decrire la ligne elle-meme, pas son vis-a-vis.
- Les references autorisees pour les lignes de prix sont:
Purchase/OpenPurchase/PhysicSale/OpenSale/Physic
- Resultat attendu:
- un lot
virtualcote purchase ne doit jamais sortir avec la referencePurchase/Physic - un lot
virtualcote sale ne doit jamais sortir avec la referenceSale/Physic - un lot physique matche peut produire:
- une ligne purchase en
Purchase/Physic - une ligne sale en
Sale/Physic
- une ligne purchase en
- un open sale matche a un open purchase peut produire des quantites egales
tout en gardant des references differentes (
Purchase/OpenvsSale/Open)
- un lot
- Priorite:
importante
BR-PT-006 - Une sale basis sans prix detaille doit quand meme apparaitre en valuation
- Intent: ne pas perdre les lignes de PnL lorsque le detail de pricing n'est pas encore renseigne.
- Description:
- Une
sale.linede typebasispeut exister avec un lotvirtual, sansprice_summaryet sanslot_price_sale. - Dans ce cas, la valuation doit quand meme creer une ligne
sale priced.
- Une
- Resultat attendu:
- si
price_summaryest vide:- creer une ligne
sale priced - avec
price = 0 - avec
amount = 0 - avec un
statede typeunfixed
- creer une ligne
- si
lot_price_saleest vide sur un lot sale, utilisersale_line.unit_pricecomme fallback quand il existe
- si
- Priorite:
importante
BR-PT-007 - Le MTM de valuation ne s'applique pas aux fees
- Intent: distinguer les lignes de prix marquables au marche des lignes de frais qui ne doivent pas etre mark-to-market.
- Description:
- Le systeme peut renseigner
mtm_price,mtmetstrategyuniquement pour:pur. pricedsale pricedderivative
- Les fees (
pur. fee,sale fee,shipment fee,line fee) ne doivent jamais porter de valorisation MTM.
- Le systeme peut renseigner
- Resultat attendu:
- les lignes de fee doivent conserver:
mtm_price = NULLmtm = NULLstrategy = NULL
mtm_pricedoit representer le prix brut de valorisation sans appliquer le ratio de composantmtmreste le montant calcule selon la logique de strategie
- les lignes de fee doivent conserver:
- Priorite:
structurante
BR-PT-008 - Le premium fait partie du prix contractuel en priced et en basis
- Intent: garantir que le montant total valorise et facture reflete toujours le premium/discount saisi sur la ligne.
- Description:
- Le
premiumd'unepurchase.lineousale.linedoit impacter le prix total quelle que soit laprice_type. - Cette regle vaut pour:
- les calculs de
amount - la valuation / PnL
- les calculs de
- Le
- Resultat attendu:
- le
unit_pricereste le prix de base, hors premium - en
priced, le montant economique =unit_price + premium - en
basis, le premium s'ajoute aussi au prix total economique - en valuation
basis, le premium s'applique a chaque composant valorise (ex: meme premium repete sur chaque bloc ICE)
- le
- Exemple metier:
8.30 USC/LB 500 TONS ON ICE MCH'268.30 USC/LB 500 TONS ON ICE MAY 26- le premium
8.30 USC/LBs'applique a chaque composant
- Priorite:
structurante
BR-PT-009 - En linked currency, le premium est exprime dans la devise/unite liee
- Intent: respecter la facon dont les traders saisissent les prix sur certains
produits (ex: coton en
USC/LB). - Description:
- Quand
enable_linked_currencyest coche, lepremiumest saisi dans la devise / unite liee, pas dans la devise / unite native de la ligne. - Le systeme doit convertir ce premium vers le repere de la ligne pour les calculs internes de montant et de valuation.
- Quand
- Resultat attendu:
premiumest interprete dans le reperelinked_currency/linked_unit- le
unit_pricene doit pas absorber ce premium - les
amountet valuations doivent refleter ce premium converti - si
linked currencyest cochee,linked_price,linked_currencyetlinked_unitsont obligatoires
- Priorite:
structurante
BR-PT-010 - En basis + linked currency, le linked price suit le basis brut
- Intent: rendre lisible la decomposition entre prix basis de marche et premium.
- Description:
- Quand une ligne est en
basisetlinked currency, le bloclinked_pricedoit etre recalcule automatiquement. - Ce
linked_pricedoit representer le prix basis brut, hors premium. - Le
unit_pricede la ligne doit rester ce prix brut converti. - Le premium converti n'est ajoute qu'au niveau du
amount.
- Quand une ligne est en
- Resultat attendu:
- modification du basis -> mise a jour automatique du
linked_price linked_price= base market / basisunit_price=linked_priceconvertiamount= quantite * (unit_price+ premium converti)
- modification du basis -> mise a jour automatique du
- Priorite:
importante
BR-PT-011 - Une sale line non matchee avec lot virtuel doit generer une valuation sale-first des la validation
- Intent: ne pas attendre un matching purchase pour afficher le PnL d'une sale ouverte.
- Description:
- Lors de la validation d'une
sale.line, le systeme peut creer un lotvirtual. - Si aucun
lot.qtne relie ce lot a unepurchase.line, il faut tout de meme generer la valuation cote sale.
- Lors de la validation d'une
BR-PT-012 - Le wizard Create contracts peut creer un seul achat matche a plusieurs open sales
- Intent: permettre la creation d'un contrat achat unique a partir de plusieurs
lot.qtde vente selectionnes. - Description:
- En mode
matched, le wizardCreate contractspeut recevoir plusieurslot.qtselectionnes. - Il doit creer un seul contrat, avec une ligne par lot source selectionne.
- Chaque ligne doit conserver son lot d'origine pour le matching.
- En mode
- Resultat attendu:
- le wizard agrege les quantites de la selection
- il refuse une quantite saisie differente du total selectionne
- il conserve
created_by_code = Truesur les lignes creees pour ne pas declencher les creations automatiques parasites lors des validations
- Priorite:
importante
BR-PT-013 - Le texte par defaut de pricing_rule est configure globalement
- Intent: centraliser un texte metier recurrent reutilise a la creation des lignes achat et vente.
- Description:
- Le module expose un singleton
purchase_trade.configurationavec un champ textepricing_rule. - Toute nouvelle
purchase.lineetsale.linedoit prendre ce texte comme valeur par defaut depricing_rule.
- Le module expose un singleton
- Resultat attendu:
- la configuration est accessible depuis le menu
Prices - la valeur sert de defaut a la creation des lignes
- les lignes existantes ne sont pas modifiees retroactivement
- la configuration est accessible depuis le menu
- Priorite:
importante
- Resultat attendu:
- apres creation du lot virtuel, si aucun matching purchase n'existe:
- appeler
Valuation.generate_from_sale_line(line) - creer au moins la ligne
sale pricedfallback si la ligne porte un prix economique via le premium
- appeler
- apres creation du lot virtuel, si aucun matching purchase n'existe:
- Priorite:
importante
BR-PT-012 - Fallback valuation basis sans summary: utiliser le prix economique de la ligne
- Intent: eviter qu'une valuation
basisouverte sorte a zero alors que la ligne a bien une valeur economique via le premium. - Description:
- Une ligne
basispeut ne pas avoir encore deprice_summary. - Dans ce cas, la valuation fallback ne doit pas prendre
unit_priceseul si celui-ci est brut et hors premium.
- Une ligne
- Resultat attendu:
- le fallback valuation
basisdoit utiliser:unit_price + premium converti
- cette regle vaut au minimum pour:
sale.linenon matcheepurchase.linesans summary
- le fallback valuation
- Priorite:
importante
BR-PT-013 - Create Contracts multi-lots doit conserver un matching par lot source
- Intent: permettre la creation d'un seul contrat mirror a partir de plusieurs open quantities sans perdre le lien lot-a-lot.
- Description:
- Le wizard
Create contractspeut etre lance avec plusieurslot.qtselectionnes. - En creation
matched, le systeme doit creer un seul contrat avec une ligne par lot source selectionne, et chaque ligne doit etre matchee avec son lot d'origine.
- Le wizard
- Resultat attendu:
- la quantite totale du wizard = somme des open quantities selectionnees
- le contrat cree porte plusieurs lignes si plusieurs lots source sont selectionnes
- chaque ligne creee reutilise le
shipment_originet le lot source qui lui correspondent created_by_codedoit rester positionne sur les lignes creees par wizard pour eviter la recreation automatique de lots virtuels dans lesvalidatedepurchase.line,sale.lineetlot.lot
- Priorite:
importante
4) Exemples concrets
Exemple E1 - Augmentation simple
- Donnees:
ancienne quantity_theorical = 100nouvelle quantity_theorical = 120lot.qt ouvert = 40
- Attendu:
- lot
virtualaugmente de20 lot.qt ouvertpasse de40a60
- lot
Exemple E2 - Augmentation sans lot.qt ouvert
- Donnees:
ancienne quantity_theorical = 100nouvelle quantity_theorical = 110- aucun
lot.qtouvert
- Attendu:
- lot
virtualaugmente de10 - creation d'un
lot.qtouvert a10
- lot
Exemple E3 - Diminution possible
- Donnees:
ancienne quantity_theorical = 100nouvelle quantity_theorical = 90lot.qt ouvert = 25
- Attendu:
- lot
virtualdiminue de10 lot.qt ouvertpasse de25a15
- lot
Exemple E4 - Diminution impossible
- Donnees:
ancienne quantity_theorical = 100nouvelle quantity_theorical = 80lot.qt ouvert = 5
- Attendu:
- blocage avec
Please unlink or unmatch lot
- blocage avec
5) Impact code attendu
- Fichiers Python concernes:
modules/purchase_trade/purchase.pymodules/purchase_trade/lot.pymodules/purchase_trade/valuation.pymodules/purchase_trade/sale.py
6) Strategie de tests
Pour cette regle, couvrir au minimum:
- augmentation avec
lot.qtouvert existant - augmentation sans
lot.qtouvert - diminution possible
- diminution impossible avec erreur
- valuation purchase/sale sur lot physique matche
- valuation sale-first sur sale non matchee avec lot virtual
- valuation sale
basissansprice_summary - absence de MTM sur les fees
- premium en
priced - premium en
basis - premium en
linked currency - synchro
basis->linked_price->unit_price