This commit is contained in:
2026-05-14 11:20:33 +02:00
parent 355831c76d
commit 2b3c823743
41 changed files with 2101 additions and 554 deletions

View File

@@ -69,6 +69,10 @@ Guide rapide pour les agents qui codent dans ce repository.
- `notes/business_rules.md` - `notes/business_rules.md`
- Regles metier locales `purchase_trade`: - Regles metier locales `purchase_trade`:
- `modules/purchase_trade/docs/business-rules.md` - `modules/purchase_trade/docs/business-rules.md`
- `modules/purchase_trade/docs_source/business/` pour les sources de verite
des pages business publiees dans le wiki
- `modules/purchase_trade/docs/business/` pour les pages generees lues par le
wiki
- Decisions templates / reports: - Decisions templates / reports:
- `notes/template_business_rules.md` - `notes/template_business_rules.md`
- Documentation comptable et reporting: - Documentation comptable et reporting:
@@ -80,6 +84,7 @@ Guide rapide pour les agents qui codent dans ce repository.
- Regles sensibles `purchase_trade` a relire avant de toucher lots, quantites - Regles sensibles `purchase_trade` a relire avant de toucher lots, quantites
ou fees: ou fees:
- `modules/purchase_trade/AGENTS.md` - `modules/purchase_trade/AGENTS.md`
- `modules/purchase_trade/docs_source/business/lots-and-quantities.md`
- `modules/purchase_trade/docs/business-rules.md` BR-PT-020 / BR-PT-021 - `modules/purchase_trade/docs/business-rules.md` BR-PT-020 / BR-PT-021
(`quantity_theorical`, `lot.qt`, lots physiques, fees et PnL fee). (`quantity_theorical`, `lot.qt`, lots physiques, fees et PnL fee).

View File

@@ -41,6 +41,10 @@ de negoce physique:
- Regles metier: - Regles metier:
- `modules/purchase_trade/docs/business-rules.md` - `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: - Regles templates:
- `modules/purchase_trade/docs/template-rules.md` - `modules/purchase_trade/docs/template-rules.md`
- Catalogue des proprietes templates: - Catalogue des proprietes templates:
@@ -179,6 +183,23 @@ de negoce physique:
## 5) Conventions de modification ## 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. 1. Modifier la logique metier dans le fichier pivot le plus proche.
2. Si un template `.fodt` devient complexe, deplacer la logique dans une 2. Si un template `.fodt` devient complexe, deplacer la logique dans une
propriete Python `report_*`. propriete Python `report_*`.

View File

@@ -1,48 +1,91 @@
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
# Index thematique des regles business # Index thematique des regles business
Statut: `migration partielle` Statut: `migration partielle`
## Comment chercher une regle ## Comment chercher une regle
- Contrats, dates, lieux, banques: [contracts.md](contracts.md) <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Lots virtuels, lots physiques, `lot.qt`, weighing: [FR](lots-and-quantities.md) / [EN](lots-and-quantities.en.md) <li style="margin:0.38rem 0;">Contrats, dates, lieux, banques: <a href="contracts.md">contracts.md</a>
- Matching, Create Contracts, back-to-back: [matching.md](matching.md) </li>
- Shipments, controllers, SLA, weight reports: [shipments-execution.md](shipments-execution.md) <li style="margin:0.38rem 0;">Lots virtuels, lots physiques, <code>lot.qt</code>, weighing: <a href="lots-and-quantities.md">FR</a> / <a href="lots-and-quantities.en.md">EN</a>
- Pricing manuel, basis, premium, linked currency: [pricing.md](pricing.md) </li>
- Fees, freight, lots effectifs, `% rate`: [fees.md](fees.md) <li style="margin:0.38rem 0;">Matching, Create Contracts, back-to-back: <a href="matching.md">matching.md</a>
- Valuation, PnL, MTM, derivatives: [valuation-pnl-mtm.md](valuation-pnl-mtm.md) </li>
- Factures provisoires/finales, padding: [invoicing.md](invoicing.md) <li style="margin:0.38rem 0;">Shipments, controllers, SLA, weight reports: <a href="shipments-execution.md">shipments-execution.md</a>
- Impacts `account.move`, validate/post: [accounting-bridge.md](accounting-bridge.md) </li>
- Comptes bancaires, payment terms, payment orders: [payments-banking.md](payments-banking.md) <li style="margin:0.38rem 0;">Pricing manuel, basis, premium, linked currency: <a href="pricing.md">pricing.md</a>
- Relatorio, `.fodt`, proprietes `report_*`: [reports-templates.md](reports-templates.md) </li>
- Risque, credit, forex: [risk-credit-forex.md](risk-credit-forex.md) <li style="margin:0.38rem 0;">Fees, freight, lots effectifs, <code>% rate</code>: <a href="fees.md">fees.md</a>
- Rapport Lots Management: [lots-management.md](lots-management.md) </li>
- Diagnostics SQL des invariants: [sql/README.md](sql/README.md) <li style="margin:0.38rem 0;">Valuation, PnL, MTM, derivatives: <a href="valuation-pnl-mtm.md">valuation-pnl-mtm.md</a>
</li>
<li style="margin:0.38rem 0;">Factures provisoires/finales, padding: <a href="invoicing.md">invoicing.md</a>
</li>
<li style="margin:0.38rem 0;">Impacts <code>account.move</code>, validate/post: <a href="accounting-bridge.md">accounting-bridge.md</a>
</li>
<li style="margin:0.38rem 0;">Comptes bancaires, payment terms, payment orders: <a href="payments-banking.md">payments-banking.md</a>
</li>
<li style="margin:0.38rem 0;">Relatorio, <code>.fodt</code>, proprietes <code>report_*</code>: <a href="reports-templates.md">reports-templates.md</a>
</li>
<li style="margin:0.38rem 0;">Risque, credit, forex: <a href="risk-credit-forex.md">risk-credit-forex.md</a>
</li>
<li style="margin:0.38rem 0;">Rapport Lots Management: <a href="lots-management.md">lots-management.md</a>
</li>
<li style="margin:0.38rem 0;">Diagnostics SQL des invariants: <a href="sql/README.md">sql/README.md</a>
</li>
</ul>
## Regles migrees dans cette premiere passe ## Regles migrees dans cette premiere passe
- `BR-PT-CON-001`: texte par defaut de pricing rule. <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- `BR-PT-CON-002`: delivery period coherent. <li style="margin:0.38rem 0;"><code>BR-PT-CON-001</code>: texte par defaut de pricing rule.
- `BR-PT-CON-003`: lieux stock propages dans Create Contracts. </li>
- `BR-PT-LOT-001`: cycle de vie des lots et des quantites. <li style="margin:0.38rem 0;"><code>BR-PT-CON-002</code>: delivery period coherent.
- `BR-PT-LOT-002`: quantity contractuelle, execute physique et ligne finie. </li>
- `BR-PT-LOT-003`: garde-fous Python et diagnostics SQL des invariants de <li style="margin:0.38rem 0;"><code>BR-PT-CON-003</code>: lieux stock propages dans Create Contracts.
quantite. </li>
- `BR-PT-MAT-001`: Create Contracts multi-lots. <li style="margin:0.38rem 0;"><code>BR-PT-LOT-001</code>: cycle de vie des lots et des quantites.
- `BR-PT-SHP-001`: affectation controller. </li>
- `BR-PT-SHP-002`: couts SLA controller. <li style="margin:0.38rem 0;"><code>BR-PT-LOT-002</code>: quantity contractuelle, execute physique et ligne finie.
- `BR-PT-SHP-003`: weight reports distants. </li>
- `BR-PT-PRI-001`: premium dans priced et basis. <li style="margin:0.38rem 0;"><code>BR-PT-LOT-003</code>: garde-fous Python et diagnostics SQL des invariants de quantite.
- `BR-PT-PRI-002`: linked currency. </li>
- `BR-PT-PRI-003`: pricing manuel. <li style="margin:0.38rem 0;"><code>BR-PT-MAT-001</code>: Create Contracts multi-lots.
- `BR-PT-FEE-001`: maritime freight depuis fee shipment. </li>
- `BR-PT-FEE-002`: lots effectifs des fees. <li style="margin:0.38rem 0;"><code>BR-PT-SHP-001</code>: affectation controller.
- `BR-PT-FEE-003`: `% rate` via delta de financement. </li>
- `BR-PT-VAL-001`: valuation achat/vente et sale-first. <li style="margin:0.38rem 0;"><code>BR-PT-SHP-002</code>: couts SLA controller.
- `BR-PT-VAL-002`: references de valuation. </li>
- `BR-PT-VAL-003`: MTM hors fees. <li style="margin:0.38rem 0;"><code>BR-PT-SHP-003</code>: weight reports distants.
- `BR-PT-INV-001`: padding facture provisoire vente. </li>
- `BR-PT-ACC-001`: Validate facture client attribue le numero. <li style="margin:0.38rem 0;"><code>BR-PT-PRI-001</code>: premium dans priced et basis.
- `BR-PT-PAY-001`: comptes bancaires tiers vs compagnie. </li>
- `BR-PT-RPT-001`: templates trade via proprietes Python. <li style="margin:0.38rem 0;"><code>BR-PT-PRI-002</code>: linked currency.
- `BR-PT-LOTMGT-001`: filtres Lots Management. </li>
<li style="margin:0.38rem 0;"><code>BR-PT-PRI-003</code>: pricing manuel.
</li>
<li style="margin:0.38rem 0;"><code>BR-PT-FEE-001</code>: maritime freight depuis fee shipment.
</li>
<li style="margin:0.38rem 0;"><code>BR-PT-FEE-002</code>: lots effectifs des fees.
</li>
<li style="margin:0.38rem 0;"><code>BR-PT-FEE-003</code>: <code>% rate</code> via delta de financement.
</li>
<li style="margin:0.38rem 0;"><code>BR-PT-VAL-001</code>: valuation achat/vente et sale-first.
</li>
<li style="margin:0.38rem 0;"><code>BR-PT-VAL-002</code>: references de valuation.
</li>
<li style="margin:0.38rem 0;"><code>BR-PT-VAL-003</code>: MTM hors fees.
</li>
<li style="margin:0.38rem 0;"><code>BR-PT-INV-001</code>: padding facture provisoire vente.
</li>
<li style="margin:0.38rem 0;"><code>BR-PT-ACC-001</code>: Validate facture client attribue le numero.
</li>
<li style="margin:0.38rem 0;"><code>BR-PT-PAY-001</code>: comptes bancaires tiers vs compagnie.
</li>
<li style="margin:0.38rem 0;"><code>BR-PT-RPT-001</code>: templates trade via proprietes Python.
</li>
<li style="margin:0.38rem 0;"><code>BR-PT-LOTMGT-001</code>: filtres Lots Management.
</li>
</ul>

View File

@@ -1,3 +1,5 @@
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
# Guide de lecture des règles business # Guide de lecture des règles business
Statut: `migration partielle` Statut: `migration partielle`
@@ -6,32 +8,44 @@ Dernière mise à jour: `2026-05-13`
Ce dossier devient la source de lecture thématique publiée dans le wiki pour les Ce dossier devient la source de lecture thématique publiée dans le wiki pour les
règles business du module `purchase_trade`. règles business du module `purchase_trade`.
Certaines pages peuvent être générées depuis une source de vérité plus sobre, Toutes les pages business publiées dans `modules/purchase_trade/docs/business/`
rangée hors du dossier wiki dans `modules/purchase_trade/docs_source/`. Dans ce sont générées depuis une source de vérité plus sobre, rangée hors du dossier
cas, la page publiée dans `modules/purchase_trade/docs/` porte un commentaire wiki dans `modules/purchase_trade/docs_source/business/`.
`Generated from ...` en tête de fichier et ne doit pas être modifiée
directement. Les pages publiées portent un commentaire `Generated from ...` en tête de
fichier et ne doivent pas être modifiées directement. Aucun contenu business ne
doit être ajouté ou modifié sans passer par cette source puis par le script de
génération.
Chaque page doit rester lisible par deux publics: Chaque page doit rester lisible par deux publics:
- les consultants, qui ont besoin d'une règle fonctionnelle stable sans détail <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
de code inutile; <li style="margin:0.38rem 0;">les consultants, qui ont besoin d&#x27;une règle fonctionnelle stable sans détail de code inutile;
- les développeurs, qui ont besoin des champs, modèles, fichiers et tests </li>
concernés pour appliquer la règle sans l'interpréter. <li style="margin:0.38rem 0;">les développeurs, qui ont besoin des champs, modèles, fichiers et tests concernés pour appliquer la règle sans l&#x27;interpréter.
</li>
</ul>
## Convention de langues ## Convention de langues
Chaque page thématique durable doit exister en deux versions maintenues Chaque page thématique durable doit exister en deux versions maintenues
ensemble: ensemble:
- une page française, rédigée en français correct avec accents, typographie et <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
formulations naturelles pour le wiki consultant; <li style="margin:0.38rem 0;">une page française, rédigée en français correct avec accents, typographie et formulations naturelles pour le wiki consultant;
- une page anglaise miroir, portant le même contenu fonctionnel et technique. </li>
<li style="margin:0.38rem 0;">une page anglaise miroir, portant le même contenu fonctionnel et technique.
</li>
</ul>
Convention de nommage: Convention de nommage:
- page française principale: `theme.md`; <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- page anglaise miroir: `theme.en.md`. <li style="margin:0.38rem 0;">page française principale: <code>theme.md</code>;
</li>
<li style="margin:0.38rem 0;">page anglaise miroir: <code>theme.en.md</code>.
</li>
</ul>
Toute modification d'une règle business, d'un statut, d'un champ technique ou Toute modification d'une règle business, d'un statut, d'un champ technique ou
d'un point de vigilance doit être reportée dans les deux pages au même moment. d'un point de vigilance doit être reportée dans les deux pages au même moment.
@@ -39,50 +53,61 @@ Les deux pages doivent indiquer leur page miroir en en-tête.
## Convention source / wiki ## Convention source / wiki
Pour les pages qui ont besoin d'une présentation riche dans MkDocs: Pour toute page business:
- éditer la source de vérité dans `modules/purchase_trade/docs_source/`; <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- régénérer la version wiki avec: <li style="margin:0.38rem 0;">éditer la source de vérité dans <code>modules/purchase_trade/docs_source/business/</code>;
</li>
<li style="margin:0.38rem 0;">régénérer la version wiki avec:
</li>
</ul>
```bash <pre style="background:#263238; color:#eef7ff; padding:1rem; border-radius:0.35rem; overflow:auto;"><code>python modules/purchase_trade/docs/tools/render_business_docs.py</code></pre>
python modules/purchase_trade/docs/tools/render_business_docs.py
```
Le rendu wiki privilégie du HTML simple et portable plutôt que des extensions Le rendu wiki privilégie du HTML simple et portable plutôt que des extensions
MkDocs optionnelles. Cela évite d'exposer dans le wiki des marqueurs non rendus MkDocs optionnelles. Cela évite d'exposer dans le wiki des marqueurs non rendus
comme `!!!` ou `:material-...:`. comme `!!!` ou `:material-...:`.
Les fichiers générés dans `modules/purchase_trade/docs/business/` sont des
artefacts de publication: ils peuvent être relus, mais toute correction doit
être reportée dans `docs_source/business/` avant régénération.
Avant de livrer une modification documentaire, vérifier que les pages publiées
sont à jour avec:
<pre style="background:#263238; color:#eef7ff; padding:1rem; border-radius:0.35rem; overflow:auto;"><code>python modules/purchase_trade/docs/tools/render_business_docs.py --check</code></pre>
## Convention de rédaction ## Convention de rédaction
Pour chaque règle durable, utiliser autant que possible ce format: Pour chaque règle durable, utiliser autant que possible ce format:
```md <pre style="background:#263238; color:#eef7ff; padding:1rem; border-radius:0.35rem; overflow:auto;"><code>### BR-PT-THEME-001 - Titre court
### BR-PT-THEME-001 - Titre court
Statut: active Statut: active
Source: business-rules.md / note de session / décision projet Source: business-rules.md / note de session / décision projet
#### Règle consultant #### Règle consultant
Texte fonctionnel, sans nom de champ si ce n'est pas nécessaire. Texte fonctionnel, sans nom de champ si ce n&#x27;est pas nécessaire.
#### Notes développeur #### Notes développeur
- Modèles/champs: - Modèles/champs:
- Fichiers: - Fichiers:
- Tests: - Tests:
- Points de vigilance: - Points de vigilance:</code></pre>
```
## Convention de validation ## Convention de validation
Quand une règle business devient structurante pour l'intégrité des données, elle Quand une règle business devient structurante pour l'intégrité des données, elle
doit être accompagnée autant que possible de deux garde-fous: doit être accompagnée autant que possible de deux garde-fous:
- un check applicatif bloquant dans le code Python, appelé à la fin des flux qui <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
modifient les données concernées; <li style="margin:0.38rem 0;">un check applicatif bloquant dans le code Python, appelé à la fin des flux qui modifient les données concernées;
- un diagnostic SQL en lecture seule pour auditer les bases existantes ou les </li>
bases de test. <li style="margin:0.38rem 0;">un diagnostic SQL en lecture seule pour auditer les bases existantes ou les bases de test.
</li>
</ul>
Les diagnostics SQL du module sont rangés dans `business/sql/`. Ils ne Les diagnostics SQL du module sont rangés dans `business/sql/`. Ils ne
remplacent pas les règles applicatives: ils servent à retrouver et qualifier les remplacent pas les règles applicatives: ils servent à retrouver et qualifier les
@@ -94,10 +119,19 @@ Les anciennes pages ne sont pas supprimées à cette étape. Elles restent des
sources de vérification jusqu'à ce que chaque décision soit promue dans une sources de vérification jusqu'à ce que chaque décision soit promue dans une
page thématique: page thématique:
- `modules/purchase_trade/docs/business-rules.md` <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- `modules/purchase_trade/docs/fees.md` <li style="margin:0.38rem 0;"><code>modules/purchase_trade/docs/business-rules.md</code>
- `modules/purchase_trade/docs/padding-invoice-accounting.md` </li>
- `modules/purchase_trade/docs/template-rules.md` <li style="margin:0.38rem 0;"><code>modules/purchase_trade/docs/fees.md</code>
- `modules/purchase_trade/docs/template-properties.md` </li>
- `notes/business_rules.md` <li style="margin:0.38rem 0;"><code>modules/purchase_trade/docs/padding-invoice-accounting.md</code>
- `notes/template_business_rules.md` </li>
<li style="margin:0.38rem 0;"><code>modules/purchase_trade/docs/template-rules.md</code>
</li>
<li style="margin:0.38rem 0;"><code>modules/purchase_trade/docs/template-properties.md</code>
</li>
<li style="margin:0.38rem 0;"><code>notes/business_rules.md</code>
</li>
<li style="margin:0.38rem 0;"><code>notes/template_business_rules.md</code>
</li>
</ul>

View File

@@ -1,3 +1,5 @@
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
# Pont comptable # Pont comptable
Statut: `migration partielle` Statut: `migration partielle`
@@ -13,13 +15,16 @@ validation, comme une facture fournisseur.
### Notes developpeur ### Notes developpeur
- Cible: `account.invoice` avec `type = out`. <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Workflow `Validate`: creer `account.move` et attribuer `number`. <li style="margin:0.38rem 0;">Cible: <code>account.invoice</code> avec <code>type = out</code>.
- Workflow `Post`: ne doit pas reintroduire une session fraiche specifique au </li>
flux client. <li style="margin:0.38rem 0;">Workflow <code>Validate</code>: creer <code>account.move</code> et attribuer <code>number</code>.
</li>
<li style="margin:0.38rem 0;">Workflow <code>Post</code>: ne doit pas reintroduire une session fraiche specifique au flux client.
</li>
</ul>
## Notes de migration ## Notes de migration
Les notes comptables detaillees restent dans `notes/accounting/` tant qu'elles Les notes comptables detaillees restent dans `notes/accounting/` tant qu'elles
n'ont pas ete promues ici. n'ont pas ete promues ici.

View File

@@ -1,3 +1,5 @@
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
# Contrats achat / vente # Contrats achat / vente
Statut: `migration partielle` Statut: `migration partielle`
@@ -13,9 +15,14 @@ repris automatiquement sur les nouvelles lignes achat et vente.
### Notes developpeur ### Notes developpeur
- Configuration: `purchase_trade.configuration.pricing_rule`. <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Cibles: `purchase.line.pricing_rule`, `sale.line.pricing_rule`. <li style="margin:0.38rem 0;">Configuration: <code>purchase_trade.configuration.pricing_rule</code>.
- Les lignes existantes ne sont pas modifiees retroactivement. </li>
<li style="margin:0.38rem 0;">Cibles: <code>purchase.line.pricing_rule</code>, <code>sale.line.pricing_rule</code>.
</li>
<li style="margin:0.38rem 0;">Les lignes existantes ne sont pas modifiees retroactivement.
</li>
</ul>
## BR-PT-CON-002 - Delivery period coherent ## BR-PT-CON-002 - Delivery period coherent
@@ -28,9 +35,12 @@ borne renseignee reste acceptee.
### Notes developpeur ### Notes developpeur
- Champs: `purchase.line.from_del`, `purchase.line.to_del`, <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
`sale.line.from_del`, `sale.line.to_del`. <li style="margin:0.38rem 0;">Champs: <code>purchase.line.from_del</code>, <code>purchase.line.to_del</code>, <code>sale.line.from_del</code>, <code>sale.line.to_del</code>.
- Validation attendue: bloquer si `from_del > to_del`. </li>
<li style="margin:0.38rem 0;">Validation attendue: bloquer si <code>from_del &gt; to_del</code>.
</li>
</ul>
## BR-PT-CON-003 - Propagation des lieux stock dans Create Contracts ## BR-PT-CON-003 - Propagation des lieux stock dans Create Contracts
@@ -43,8 +53,13 @@ logistiques doivent etre proposes selon le flux source pour eviter la ressaisie.
### Notes developpeur ### Notes developpeur
- Champs: `from_location`, `to_location`. <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Flux fournisseur vers client: recopier le couple source. <li style="margin:0.38rem 0;">Champs: <code>from_location</code>, <code>to_location</code>.
- Achat vers stock puis vente: `sale.from_location = purchase.to_location`. </li>
- Vente depuis stock puis achat: `purchase.to_location = sale.from_location`. <li style="margin:0.38rem 0;">Flux fournisseur vers client: recopier le couple source.
</li>
<li style="margin:0.38rem 0;">Achat vers stock puis vente: <code>sale.from_location = purchase.to_location</code>.
</li>
<li style="margin:0.38rem 0;">Vente depuis stock puis achat: <code>purchase.to_location = sale.from_location</code>.
</li>
</ul>

View File

@@ -1,3 +1,5 @@
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
# Fees # Fees
Statut: `migration partielle` Statut: `migration partielle`
@@ -15,10 +17,16 @@ shipment, pas d'un champ direct de la facture.
### Notes developpeur ### Notes developpeur
- Retrouver le lot physique depuis la facture. <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Retrouver son `shipment_in`. <li style="margin:0.38rem 0;">Retrouver le lot physique depuis la facture.
- Chercher le `fee.fee` avec `product.name = 'Maritime freight'`. </li>
- Utiliser `fee.get_amount()`. <li style="margin:0.38rem 0;">Retrouver son <code>shipment_in</code>.
</li>
<li style="margin:0.38rem 0;">Chercher le <code>fee.fee</code> avec <code>product.name = &#x27;Maritime freight&#x27;</code>.
</li>
<li style="margin:0.38rem 0;">Utiliser <code>fee.get_amount()</code>.
</li>
</ul>
## BR-PT-FEE-002 - Les fees lies aux lots privilegient les physiques ## BR-PT-FEE-002 - Les fees lies aux lots privilegient les physiques
@@ -31,12 +39,18 @@ physique est lie, les lots physiques deviennent la base de calcul du fee.
### Notes developpeur ### Notes developpeur
- Ne pas supprimer le lien virtuel: il reste le fallback. <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Quantite `ppack`: somme de `lot.lot_qt` des physiques. <li style="margin:0.38rem 0;">Ne pas supprimer le lien virtuel: il reste le fallback.
- Modes quantitatifs: quantites courantes converties des physiques. </li>
- La meme selection s'applique au PnL fee. <li style="margin:0.38rem 0;">Quantite <code>ppack</code>: somme de <code>lot.lot_qt</code> des physiques.
- Points de synchronisation: creation fee, lien `fee.lots`, changement de </li>
`quantity_theorical`, weighing, suppression de physique. <li style="margin:0.38rem 0;">Modes quantitatifs: quantites courantes converties des physiques.
</li>
<li style="margin:0.38rem 0;">La meme selection s&#x27;applique au PnL fee.
</li>
<li style="margin:0.38rem 0;">Points de synchronisation: creation fee, lien <code>fee.lots</code>, changement de <code>quantity_theorical</code>, weighing, suppression de physique.
</li>
</ul>
## BR-PT-FEE-003 - Fees `% rate` via delta de financement ## BR-PT-FEE-003 - Fees `% rate` via delta de financement
@@ -49,7 +63,11 @@ de la ligne d'estimation `BL date`, pas avec la date du jour.
### Notes developpeur ### Notes developpeur
- Formule: `amount = unit_price * quantity * (price / 100) * fin_int_delta / 360`. <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Source du delta: ligne `Estimated date` avec `trigger = bldate`. <li style="margin:0.38rem 0;">Formule: <code>amount = unit_price * quantity * (price / 100) * fin_int_delta / 360</code>.
- Si aucune ligne `bldate` n'existe, ne pas calculer de montant `% rate`. </li>
<li style="margin:0.38rem 0;">Source du delta: ligne <code>Estimated date</code> avec <code>trigger = bldate</code>.
</li>
<li style="margin:0.38rem 0;">Si aucune ligne <code>bldate</code> n&#x27;existe, ne pas calculer de montant <code>% rate</code>.
</li>
</ul>

View File

@@ -1,22 +1,42 @@
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
# Glossaire purchase_trade # Glossaire purchase_trade
Statut: `migration partielle` Statut: `migration partielle`
- `Purchase Line`: ligne d'achat. <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- `Sale Line`: ligne de vente. <li style="margin:0.38rem 0;"><code>Purchase Line</code>: ligne d&#x27;achat.
- `quantity_theorical`: quantite contractuelle theorique d'une ligne. </li>
- `Virtual Lot`: lot de type `virtual`, representant un reliquat ouvert. <li style="margin:0.38rem 0;"><code>Sale Line</code>: ligne de vente.
- `Physical Lot`: lot de type `physic`, representant une quantite executee. </li>
- `lot.qt`: ligne de quantite ouverte, matchee ou rattachee a un shipment. <li style="margin:0.38rem 0;"><code>quantity_theorical</code>: quantite contractuelle theorique d&#x27;une ligne.
- `lot.qt ouvert`: `lot.qt` libre, sans lot oppose et sans shipment. </li>
- `Shipment In`: shipment entrant utilise aussi pour les flux dropship dans ce module. <li style="margin:0.38rem 0;"><code>Virtual Lot</code>: lot de type <code>virtual</code>, representant un reliquat ouvert.
- `Dropship`: flux fournisseur vers client, sans passage par stock interne. </li>
- `Inbound`: flux entrant classique qui ne correspond pas au dropship. <li style="margin:0.38rem 0;"><code>Physical Lot</code>: lot de type <code>physic</code>, representant une quantite executee.
- `Basis`: mode de prix construit a partir d'un prix de marche et d'un premium. </li>
- `Premium`: prime ou discount commercial ajoute au prix economique. <li style="margin:0.38rem 0;"><code>lot.qt</code>: ligne de quantite ouverte, matchee ou rattachee a un shipment.
- `Linked currency`: saisie d'un prix dans une devise/unite liee, par exemple `USC/LB`. </li>
- `Valuation`: lignes de PnL generees pour prix, fees, derivatives et MTM. <li style="margin:0.38rem 0;"><code>lot.qt ouvert</code>: <code>lot.qt</code> libre, sans lot oppose et sans shipment.
- `MTM`: mark-to-market applique aux lignes valorisables au marche. </li>
- `Fee`: frais commercial ou logistique rattache a une ligne, un lot ou un shipment. <li style="margin:0.38rem 0;"><code>Shipment In</code>: shipment entrant utilise aussi pour les flux dropship dans ce module.
- `Report property`: propriete Python exposee pour simplifier un template Relatorio. </li>
<li style="margin:0.38rem 0;"><code>Dropship</code>: flux fournisseur vers client, sans passage par stock interne.
</li>
<li style="margin:0.38rem 0;"><code>Inbound</code>: flux entrant classique qui ne correspond pas au dropship.
</li>
<li style="margin:0.38rem 0;"><code>Basis</code>: mode de prix construit a partir d&#x27;un prix de marche et d&#x27;un premium.
</li>
<li style="margin:0.38rem 0;"><code>Premium</code>: prime ou discount commercial ajoute au prix economique.
</li>
<li style="margin:0.38rem 0;"><code>Linked currency</code>: saisie d&#x27;un prix dans une devise/unite liee, par exemple <code>USC/LB</code>.
</li>
<li style="margin:0.38rem 0;"><code>Valuation</code>: lignes de PnL generees pour prix, fees, derivatives et MTM.
</li>
<li style="margin:0.38rem 0;"><code>MTM</code>: mark-to-market applique aux lignes valorisables au marche.
</li>
<li style="margin:0.38rem 0;"><code>Fee</code>: frais commercial ou logistique rattache a une ligne, un lot ou un shipment.
</li>
<li style="margin:0.38rem 0;"><code>Report property</code>: propriete Python exposee pour simplifier un template Relatorio.
</li>
</ul>

View File

@@ -1,3 +1,5 @@
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
# Invariants structurants # Invariants structurants
Statut: `migration partielle` Statut: `migration partielle`
@@ -15,11 +17,14 @@ contrats et l'execution logistique.
### Notes developpeur ### Notes developpeur
- Source historique: `BR-PT-002`. <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Voir aussi: [lots-and-quantities.md](lots-and-quantities.md), <li style="margin:0.38rem 0;">Source historique: <code>BR-PT-002</code>.
[matching.md](matching.md), [reports-templates.md](reports-templates.md). </li>
- Champs frequents: `lot.line`, `lot.sale_line`, `lot_shipment_in`, <li style="margin:0.38rem 0;">Voir aussi: <a href="lots-and-quantities.md">lots-and-quantities.md</a>, <a href="matching.md">matching.md</a>, <a href="reports-templates.md">reports-templates.md</a>.
`lot_shipment_internal`, `lot_shipment_out`. </li>
<li style="margin:0.38rem 0;">Champs frequents: <code>lot.line</code>, <code>lot.sale_line</code>, <code>lot_shipment_in</code>, <code>lot_shipment_internal</code>, <code>lot_shipment_out</code>.
</li>
</ul>
## INV-PT-002 - Le reliquat ouvert ne doit pas doubler les lots physiques ## INV-PT-002 - Le reliquat ouvert ne doit pas doubler les lots physiques
@@ -31,19 +36,22 @@ executer.
### Notes developpeur ### Notes developpeur
- Source historique: `BR-PT-020`. <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Le calcul doit tenir compte de la quantite contractuelle, des lots physiques <li style="margin:0.38rem 0;">Source historique: <code>BR-PT-020</code>.
existants et des `lot.qt` deja matches ou shippes. </li>
- Regle de conservation: <li style="margin:0.38rem 0;">Le calcul doit tenir compte de la quantite contractuelle, des lots physiques existants et des <code>lot.qt</code> deja matches ou shippes.
`sum(lots physiques) + lot virtuel = quantity_theorical`. </li>
- Regle du forecast ouvert: <li style="margin:0.38rem 0;">Regle de conservation: <code>sum(lots physiques) + lot virtuel = quantity_theorical</code>.
`sum(lot.qt non zero) = max(lot virtuel, 0)`. </li>
- Les lignes `lot.qt` a zero sont ignorees par les checks: elles peuvent servir <li style="margin:0.38rem 0;">Regle du forecast ouvert: <code>sum(lot.qt non zero) = max(lot virtuel, 0)</code>.
de memoire d'une prevision consommee. </li>
- Le check applicatif est centralise dans <li style="margin:0.38rem 0;">Les lignes <code>lot.qt</code> a zero sont ignorees par les checks: elles peuvent servir de memoire d&#x27;une prevision consommee.
`lot.lot.assert_lines_quantity_consistency()`. </li>
- Le diagnostic SQL correspondant est <li style="margin:0.38rem 0;">Le check applicatif est centralise dans <code>lot.lot.assert_lines_quantity_consistency()</code>.
[sql/quantity_consistency_checks.sql](sql/quantity_consistency_checks.sql). </li>
<li style="margin:0.38rem 0;">Le diagnostic SQL correspondant est <a href="sql/quantity_consistency_checks.sql">sql/quantity_consistency_checks.sql</a>.
</li>
</ul>
## INV-PT-003 - Les fees utilisent leurs lots effectifs ## INV-PT-003 - Les fees utilisent leurs lots effectifs
@@ -55,10 +63,14 @@ effective de calcul.
### Notes developpeur ### Notes developpeur
- Source historique: `BR-PT-021`. <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Ne pas supprimer le lien virtuel: il reste le fallback si les physiques sont <li style="margin:0.38rem 0;">Source historique: <code>BR-PT-021</code>.
retires. </li>
- Voir [fees.md](fees.md). <li style="margin:0.38rem 0;">Ne pas supprimer le lien virtuel: il reste le fallback si les physiques sont retires.
</li>
<li style="margin:0.38rem 0;">Voir <a href="fees.md">fees.md</a>.
</li>
</ul>
## INV-PT-004 - Les templates doivent rester simples ## INV-PT-004 - Les templates doivent rester simples
@@ -69,7 +81,11 @@ chemin technique pour les retrouver est complexe.
### Notes developpeur ### Notes developpeur
- Preferer des proprietes Python `report_*` aux expressions Genshi complexes. <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Ne pas supposer qu'une variable locale comme `shipment` existe partout dans <li style="margin:0.38rem 0;">Preferer des proprietes Python <code>report_*</code> aux expressions Genshi complexes.
un `.fodt`. </li>
- Voir [reports-templates.md](reports-templates.md). <li style="margin:0.38rem 0;">Ne pas supposer qu&#x27;une variable locale comme <code>shipment</code> existe partout dans un <code>.fodt</code>.
</li>
<li style="margin:0.38rem 0;">Voir <a href="reports-templates.md">reports-templates.md</a>.
</li>
</ul>

View File

@@ -1,3 +1,5 @@
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
# Facturation trade # Facturation trade
Statut: `migration partielle` Statut: `migration partielle`
@@ -13,12 +15,15 @@ constituer une provision, sans modifier la quantite physique du lot.
### Notes developpeur ### Notes developpeur
- Le padding global du wizard `lot.invoice` est reparti entre les lots <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
selectionnes. <li style="margin:0.38rem 0;">Le padding global du wizard <code>lot.invoice</code> est reparti entre les lots selectionnes.
- La ligne facture expose `Inc. padding`. </li>
- Le lot conserve sa part dans `sale_invoice_padding`. <li style="margin:0.38rem 0;">La ligne facture expose <code>Inc. padding</code>.
- La facture finale retire le padding de la quantite provisoire avant de </li>
calculer le delta. <li style="margin:0.38rem 0;">Le lot conserve sa part dans <code>sale_invoice_padding</code>.
- Les ecritures d'extourne doivent relire la provisoire depuis </li>
`lot.sale_invoice_line_prov`. <li style="margin:0.38rem 0;">La facture finale retire le padding de la quantite provisoire avant de calculer le delta.
</li>
<li style="margin:0.38rem 0;">Les ecritures d&#x27;extourne doivent relire la provisoire depuis <code>lot.sale_invoice_line_prov</code>.
</li>
</ul>

View File

@@ -296,174 +296,379 @@ them with Python guards and SQL diagnostics.
### Key Fields ### Key Fields
- Purchase line: `purchase.line` <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Sale line: `sale.line` <li style="margin:0.38rem 0;">Purchase line: <code>purchase.line</code>
- Lot: `lot.lot` </li>
- Forecast: `lot.qt` <li style="margin:0.38rem 0;">Sale line: <code>sale.line</code>
- History: `lot.qt.hist` </li>
- Purchase business quantity: `purchase.line.quantity_theorical` <li style="margin:0.38rem 0;">Lot: <code>lot.lot</code>
- Sale business quantity: `sale.line.quantity_theorical` </li>
- Technical counter: `quantity` <li style="margin:0.38rem 0;">Forecast: <code>lot.qt</code>
- Finished line: `purchase.line.finished`, `sale.line.finished` </li>
- Virtual / physical lot: `lot.lot.lot_type = virtual / physic` <li style="margin:0.38rem 0;">History: <code>lot.qt.hist</code>
- Purchase link: `lot.lot.line` </li>
- Sale link: `lot.lot.sale_line` <li style="margin:0.38rem 0;">Purchase business quantity: <code>purchase.line.quantity_theorical</code>
- Purchase forecast: `lot.qt.lot_p` </li>
- Sale forecast: `lot.qt.lot_s` <li style="margin:0.38rem 0;">Sale business quantity: <code>sale.line.quantity_theorical</code>
- Forecast quantity: `lot.qt.lot_quantity` </li>
- Weight basis: `purchase.purchase.wb`, `sale.sale.wb` <li style="margin:0.38rem 0;">Technical counter: <code>quantity</code>
- Weight basis state: `purchase.weight.basis.qt_type` </li>
- Packing: `lot.lot.lot_qt`, `lot.lot.lot_unit` <li style="margin:0.38rem 0;">Finished line: <code>purchase.line.finished</code>, <code>sale.line.finished</code>
- Tolerances: `tol_min`, `tol_max`, `tol_min_qt`, `tol_max_qt`, </li>
`tol_min_v`, `tol_max_v` <li style="margin:0.38rem 0;">Virtual / physical lot: <code>lot.lot.lot_type = virtual / physic</code>
</li>
<li style="margin:0.38rem 0;">Purchase link: <code>lot.lot.line</code>
</li>
<li style="margin:0.38rem 0;">Sale link: <code>lot.lot.sale_line</code>
</li>
<li style="margin:0.38rem 0;">Purchase forecast: <code>lot.qt.lot_p</code>
</li>
<li style="margin:0.38rem 0;">Sale forecast: <code>lot.qt.lot_s</code>
</li>
<li style="margin:0.38rem 0;">Forecast quantity: <code>lot.qt.lot_quantity</code>
</li>
<li style="margin:0.38rem 0;">Weight basis: <code>purchase.purchase.wb</code>, <code>sale.sale.wb</code>
</li>
<li style="margin:0.38rem 0;">Weight basis state: <code>purchase.weight.basis.qt_type</code>
</li>
<li style="margin:0.38rem 0;">Packing: <code>lot.lot.lot_qt</code>, <code>lot.lot.lot_unit</code>
</li>
<li style="margin:0.38rem 0;">Tolerances: <code>tol_min</code>, <code>tol_max</code>, <code>tol_min_qt</code>, <code>tol_max_qt</code>, <code>tol_min_v</code>, <code>tol_max_v</code>
</li>
</ul>
### Line / Virtual Lot Creation ### Line / Virtual Lot Creation
- Purchase: `purchase.py`, `Line.validate` <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Sale: `sale.py`, `SaleLine.validate` <li style="margin:0.38rem 0;">Purchase: <code>purchase.py</code>, <code>Line.validate</code>
- If `quantity_theorical` is entered and `quantity` is empty or zero: </li>
- `quantity` is initialized from `quantity_theorical`; <li style="margin:0.38rem 0;">Sale: <code>sale.py</code>, <code>SaleLine.validate</code>
- only if no physical lot exists. </li>
- If the line is eligible: <li style="margin:0.38rem 0;">If <code>quantity_theorical</code> is entered and <code>quantity</code> is empty or zero:
- not `created_by_code`; <ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
- no lot yet; <li style="margin:0.38rem 0;"><code>quantity</code> is initialized from <code>quantity_theorical</code>;
- non-service product; </li>
- `quantity_theorical != 0`; <li style="margin:0.38rem 0;">only if no physical lot exists.
- create one `virtual` lot. </li>
- The virtual lot receives a first `lot.qt.hist` entry. </ul>
- `Lot.validate` creates the open `lot.qt` through `createVirtualPart`. </li>
<li style="margin:0.38rem 0;">If the line is eligible:
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
<li style="margin:0.38rem 0;">not <code>created_by_code</code>;
</li>
<li style="margin:0.38rem 0;">no lot yet;
</li>
<li style="margin:0.38rem 0;">non-service product;
</li>
<li style="margin:0.38rem 0;"><code>quantity_theorical != 0</code>;
</li>
<li style="margin:0.38rem 0;">create one <code>virtual</code> lot.
</li>
</ul>
</li>
<li style="margin:0.38rem 0;">The virtual lot receives a first <code>lot.qt.hist</code> entry.
</li>
<li style="margin:0.38rem 0;"><code>Lot.validate</code> creates the open <code>lot.qt</code> through <code>createVirtualPart</code>.
</li>
</ul>
### Updating `quantity_theorical` ### Updating `quantity_theorical`
- Purchase: `purchase.py`, `Line.write` <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Sale: `sale.py`, `SaleLine.write` <li style="margin:0.38rem 0;">Purchase: <code>purchase.py</code>, <code>Line.write</code>
- Virtual lot target: </li>
<li style="margin:0.38rem 0;">Sale: <code>sale.py</code>, <code>SaleLine.write</code>
</li>
<li style="margin:0.38rem 0;">Virtual lot target:
</li>
</ul>
<pre style="background:#263238; color:#eef7ff; padding:1rem; border-radius:0.35rem; overflow:auto;"><code>target_quantity = quantity_theorical - sum(converted physical lots)</code></pre> <pre style="background:#263238; color:#eef7ff; padding:1rem; border-radius:0.35rem; overflow:auto;"><code>target_quantity = quantity_theorical - sum(converted physical lots)</code></pre>
- If `target_quantity < 0`: <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- block with `Please unlink or unmatch lot`. <li style="margin:0.38rem 0;">If <code>target_quantity &lt; 0</code>:
- Free `lot.qt` target: <ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
<li style="margin:0.38rem 0;">block with <code>Please unlink or unmatch lot</code>.
</li>
</ul>
</li>
<li style="margin:0.38rem 0;">Free <code>lot.qt</code> target:
</li>
</ul>
<pre style="background:#263238; color:#eef7ff; padding:1rem; border-radius:0.35rem; overflow:auto;"><code>free_quantity = target_quantity - sum(already matched or shipped lot.qt)</code></pre> <pre style="background:#263238; color:#eef7ff; padding:1rem; border-radius:0.35rem; overflow:auto;"><code>free_quantity = target_quantity - sum(already matched or shipped lot.qt)</code></pre>
- If `free_quantity < 0`: <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- block with `Please unlink or unmatch lot`. <li style="margin:0.38rem 0;">If <code>free_quantity &lt; 0</code>:
- If a free `lot.qt` exists: <ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
- replace its quantity. <li style="margin:0.38rem 0;">block with <code>Please unlink or unmatch lot</code>.
- If no free `lot.qt` exists and `free_quantity > 0`: </li>
- create a new `lot.qt`. </ul>
- Line fees are resynchronized. </li>
<li style="margin:0.38rem 0;">If a free <code>lot.qt</code> exists:
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
<li style="margin:0.38rem 0;">replace its quantity.
</li>
</ul>
</li>
<li style="margin:0.38rem 0;">If no free <code>lot.qt</code> exists and <code>free_quantity &gt; 0</code>:
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
<li style="margin:0.38rem 0;">create a new <code>lot.qt</code>.
</li>
</ul>
</li>
<li style="margin:0.38rem 0;">Line fees are resynchronized.
</li>
</ul>
### Adding Physical Lots ### Adding Physical Lots
- Wizard: `lot.add` <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Methods: <li style="margin:0.38rem 0;">Wizard: <code>lot.add</code>
- `LotQt.add_physical_lots` </li>
- `LotQt.add_physical_lot` <li style="margin:0.38rem 0;">Methods:
- Mandatory source: one `lot.qt` line. <ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
- Direct add from a physical lot is refused. <li style="margin:0.38rem 0;"><code>LotQt.add_physical_lots</code>
- Physical add on sale side through this wizard is refused: use </li>
`Apply matching`. <li style="margin:0.38rem 0;"><code>LotQt.add_physical_lot</code>
- The physical lot inherits: </li>
- purchase line; </ul>
- matched sale, if any; </li>
- shipment; <li style="margin:0.38rem 0;">Mandatory source: one <code>lot.qt</code> line.
- product; </li>
- unit; <li style="margin:0.38rem 0;">Direct add from a physical lot is refused.
- quantities; </li>
- premium; <li style="margin:0.38rem 0;">Physical add on sale side through this wizard is refused: use <code>Apply matching</code>.
- chunk key. </li>
- After creation: <li style="margin:0.38rem 0;">The physical lot inherits:
- source `lot.qt` is reduced; <ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
- `lot.qt` cannot become negative; <li style="margin:0.38rem 0;">purchase line;
- virtual lot is recalculated; </li>
- `quantity` is recalculated; <li style="margin:0.38rem 0;">matched sale, if any;
- moves and fees are updated when needed. </li>
<li style="margin:0.38rem 0;">shipment;
</li>
<li style="margin:0.38rem 0;">product;
</li>
<li style="margin:0.38rem 0;">unit;
</li>
<li style="margin:0.38rem 0;">quantities;
</li>
<li style="margin:0.38rem 0;">premium;
</li>
<li style="margin:0.38rem 0;">chunk key.
</li>
</ul>
</li>
<li style="margin:0.38rem 0;">After creation:
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
<li style="margin:0.38rem 0;">source <code>lot.qt</code> is reduced;
</li>
<li style="margin:0.38rem 0;"><code>lot.qt</code> cannot become negative;
</li>
<li style="margin:0.38rem 0;">virtual lot is recalculated;
</li>
<li style="margin:0.38rem 0;"><code>quantity</code> is recalculated;
</li>
<li style="margin:0.38rem 0;">moves and fees are updated when needed.
</li>
</ul>
</li>
</ul>
### Removing Physical Lots ### Removing Physical Lots
- Wizard: `lot.remove` <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Open lot: removal forbidden. <li style="margin:0.38rem 0;">Wizard: <code>lot.remove</code>
- Lot with `stock.move`: </li>
- move must be `draft`. <li style="margin:0.38rem 0;">Open lot: removal forbidden.
- Matched or shipped lot: </li>
- confirmable warning. <li style="margin:0.38rem 0;">Lot with <code>stock.move</code>:
- Effects: <ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
- draft move deletion; <li style="margin:0.38rem 0;">move must be <code>draft</code>.
- restore quantity into `lot.qt`; </li>
- restore context through shipment, `getVlot_p()`, `getVlot_s()`; </ul>
- recalculate virtual lot, `quantity`, fees. </li>
<li style="margin:0.38rem 0;">Matched or shipped lot:
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
<li style="margin:0.38rem 0;">confirmable warning.
</li>
</ul>
</li>
<li style="margin:0.38rem 0;">Effects:
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
<li style="margin:0.38rem 0;">draft move deletion;
</li>
<li style="margin:0.38rem 0;">restore quantity into <code>lot.qt</code>;
</li>
<li style="margin:0.38rem 0;">restore context through shipment, <code>getVlot_p()</code>, <code>getVlot_s()</code>;
</li>
<li style="margin:0.38rem 0;">recalculate virtual lot, <code>quantity</code>, fees.
</li>
</ul>
</li>
</ul>
### Weighing / Quantity States ### Weighing / Quantity States
- Wizard: `lot.weighing` <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- UI action: `Do weighing` <li style="margin:0.38rem 0;">Wizard: <code>lot.weighing</code>
- Writes or updates `lot.qt.hist`. </li>
- May update `lot_state`. <li style="margin:0.38rem 0;">UI action: <code>Do weighing</code>
- Synchronizes: </li>
- lot; <li style="margin:0.38rem 0;">Writes or updates <code>lot.qt.hist</code>.
- open quantities; </li>
- fees. <li style="margin:0.38rem 0;">May update <code>lot_state</code>.
- `lot.qt.hist` views are consultative. </li>
<li style="margin:0.38rem 0;">Synchronizes:
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
<li style="margin:0.38rem 0;">lot;
</li>
<li style="margin:0.38rem 0;">open quantities;
</li>
<li style="margin:0.38rem 0;">fees.
</li>
</ul>
</li>
<li style="margin:0.38rem 0;"><code>lot.qt.hist</code> views are consultative.
</li>
</ul>
### Quantity Counter `quantity` ### Quantity Counter `quantity`
- Method: `Lot._recalc_line_quantity` <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Without physical lots: <li style="margin:0.38rem 0;">Method: <code>Lot._recalc_line_quantity</code>
- `quantity` follows the virtual lot. </li>
- With physical lots: <li style="margin:0.38rem 0;">Without physical lots:
- `quantity` sums physical lots only. <ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
- `quantity` is readonly on trade lines. <li style="margin:0.38rem 0;"><code>quantity</code> follows the virtual lot.
</li>
</ul>
</li>
<li style="margin:0.38rem 0;">With physical lots:
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
<li style="margin:0.38rem 0;"><code>quantity</code> sums physical lots only.
</li>
</ul>
</li>
<li style="margin:0.38rem 0;"><code>quantity</code> is readonly on trade lines.
</li>
</ul>
### Line Amount ### Line Amount
- Purchase: `purchase.line.on_change_with_amount()` <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Sale: `sale.line.on_change_with_amount()` <li style="margin:0.38rem 0;">Purchase: <code>purchase.line.on_change_with_amount()</code>
- Helpers: </li>
- `_get_amount_quantity()` <li style="margin:0.38rem 0;">Sale: <code>sale.line.on_change_with_amount()</code>
- `_get_weight_basis_quantity()` </li>
- Priorities: <li style="margin:0.38rem 0;">Helpers:
- `finished = False`: `quantity_theorical` <ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
- `finished = True` + usable Weight basis: physical sum in that state <li style="margin:0.38rem 0;"><code>_get_amount_quantity()</code>
- `finished = True` without usable Weight basis: `quantity` </li>
- legacy fallback: `quantity` if `quantity_theorical` is empty <li style="margin:0.38rem 0;"><code>_get_weight_basis_quantity()</code>
</li>
</ul>
</li>
<li style="margin:0.38rem 0;">Priorities:
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
<li style="margin:0.38rem 0;"><code>finished = False</code>: <code>quantity_theorical</code>
</li>
<li style="margin:0.38rem 0;"><code>finished = True</code> + usable Weight basis: physical sum in that state
</li>
<li style="margin:0.38rem 0;"><code>finished = True</code> without usable Weight basis: <code>quantity</code>
</li>
<li style="margin:0.38rem 0;">legacy fallback: <code>quantity</code> if <code>quantity_theorical</code> is empty
</li>
</ul>
</li>
</ul>
### Python Guards ### Python Guards
- Central check: <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- `lot.lot.assert_lines_quantity_consistency()` <li style="margin:0.38rem 0;">Central check:
- Non-zero orphan `lot.qt` block: <ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
- `lot.qt.validate` <li style="margin:0.38rem 0;"><code>lot.lot.assert_lines_quantity_consistency()</code>
- Called after: </li>
- `quantity_theorical` update; </ul>
- physical lot creation / deletion; </li>
- matching / unmatching; <li style="margin:0.38rem 0;">Non-zero orphan <code>lot.qt</code> block:
- shipping / unshipping; <ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
- weighing. <li style="margin:0.38rem 0;"><code>lot.qt.validate</code>
</li>
</ul>
</li>
<li style="margin:0.38rem 0;">Called after:
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
<li style="margin:0.38rem 0;"><code>quantity_theorical</code> update;
</li>
<li style="margin:0.38rem 0;">physical lot creation / deletion;
</li>
<li style="margin:0.38rem 0;">matching / unmatching;
</li>
<li style="margin:0.38rem 0;">shipping / unshipping;
</li>
<li style="margin:0.38rem 0;">weighing.
</li>
</ul>
</li>
</ul>
### SQL Diagnostic ### SQL Diagnostic
- Script: <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- [sql/quantity_consistency_checks.sql](sql/quantity_consistency_checks.sql) <li style="margin:0.38rem 0;">Script:
- Use: <ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
- test database audit; <li style="margin:0.38rem 0;"><a href="sql/quantity_consistency_checks.sql">sql/quantity_consistency_checks.sql</a>
- historical data audit; </li>
- qualification before repair. </ul>
- The script completely ignores `lot.qt = 0`. </li>
<li style="margin:0.38rem 0;">Use:
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
<li style="margin:0.38rem 0;">test database audit;
</li>
<li style="margin:0.38rem 0;">historical data audit;
</li>
<li style="margin:0.38rem 0;">qualification before repair.
</li>
</ul>
</li>
<li style="margin:0.38rem 0;">The script completely ignores <code>lot.qt = 0</code>.
</li>
</ul>
## Nearby Tests ## Nearby Tests
- `modules/purchase_trade/tests/test_module.py` <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Existing coverage: <li style="margin:0.38rem 0;"><code>modules/purchase_trade/tests/test_module.py</code>
- readonly `quantity`; </li>
- initialization from `quantity_theorical`; <li style="margin:0.38rem 0;">Existing coverage:
- protection when physical lots exist; <ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
- amount on theoretical / physical / Weight basis; <li style="margin:0.38rem 0;">readonly <code>quantity</code>;
- virtual lot resynchronization; </li>
- blocking when open quantity is not enough. <li style="margin:0.38rem 0;">initialization from <code>quantity_theorical</code>;
- Tests to add: </li>
- readonly `lot_hist`; <li style="margin:0.38rem 0;">protection when physical lots exist;
- `Do weighing` creates or updates a state; </li>
- virtual lot without direct `lot_qt` / `lot_unit` entry; <li style="margin:0.38rem 0;">amount on theoretical / physical / Weight basis;
- SQL checks replayed on inconsistent datasets. </li>
<li style="margin:0.38rem 0;">virtual lot resynchronization;
</li>
<li style="margin:0.38rem 0;">blocking when open quantity is not enough.
</li>
</ul>
</li>
<li style="margin:0.38rem 0;">Tests to add:
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
<li style="margin:0.38rem 0;">readonly <code>lot_hist</code>;
</li>
<li style="margin:0.38rem 0;"><code>Do weighing</code> creates or updates a state;
</li>
<li style="margin:0.38rem 0;">virtual lot without direct <code>lot_qt</code> / <code>lot_unit</code> entry;
</li>
<li style="margin:0.38rem 0;">SQL checks replayed on inconsistent datasets.
</li>
</ul>
</li>
</ul>

View File

@@ -295,173 +295,379 @@ puis les sécuriser par des checks Python et des diagnostics SQL.
### Champs clés ### Champs clés
- Ligne achat : `purchase.line` <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Ligne vente : `sale.line` <li style="margin:0.38rem 0;">Ligne achat : <code>purchase.line</code>
- Lot : `lot.lot` </li>
- Forecast : `lot.qt` <li style="margin:0.38rem 0;">Ligne vente : <code>sale.line</code>
- Historique : `lot.qt.hist` </li>
- Quantité métier achat : `purchase.line.quantity_theorical` <li style="margin:0.38rem 0;">Lot : <code>lot.lot</code>
- Quantité métier vente : `sale.line.quantity_theorical` </li>
- Compteur technique : `quantity` <li style="margin:0.38rem 0;">Forecast : <code>lot.qt</code>
- Ligne finie : `purchase.line.finished`, `sale.line.finished` </li>
- Lot virtuel / physique : `lot.lot.lot_type = virtual / physic` <li style="margin:0.38rem 0;">Historique : <code>lot.qt.hist</code>
- Lien achat : `lot.lot.line` </li>
- Lien vente : `lot.lot.sale_line` <li style="margin:0.38rem 0;">Quantité métier achat : <code>purchase.line.quantity_theorical</code>
- Forecast achat : `lot.qt.lot_p` </li>
- Forecast vente : `lot.qt.lot_s` <li style="margin:0.38rem 0;">Quantité métier vente : <code>sale.line.quantity_theorical</code>
- Quantité forecast : `lot.qt.lot_quantity` </li>
- Weight basis : `purchase.purchase.wb`, `sale.sale.wb` <li style="margin:0.38rem 0;">Compteur technique : <code>quantity</code>
- État Weight basis : `purchase.weight.basis.qt_type` </li>
- Packing : `lot.lot.lot_qt`, `lot.lot.lot_unit` <li style="margin:0.38rem 0;">Ligne finie : <code>purchase.line.finished</code>, <code>sale.line.finished</code>
- Tolerances : `tol_min`, `tol_max`, `tol_min_qt`, `tol_max_qt`, </li>
`tol_min_v`, `tol_max_v` <li style="margin:0.38rem 0;">Lot virtuel / physique : <code>lot.lot.lot_type = virtual / physic</code>
</li>
<li style="margin:0.38rem 0;">Lien achat : <code>lot.lot.line</code>
</li>
<li style="margin:0.38rem 0;">Lien vente : <code>lot.lot.sale_line</code>
</li>
<li style="margin:0.38rem 0;">Forecast achat : <code>lot.qt.lot_p</code>
</li>
<li style="margin:0.38rem 0;">Forecast vente : <code>lot.qt.lot_s</code>
</li>
<li style="margin:0.38rem 0;">Quantité forecast : <code>lot.qt.lot_quantity</code>
</li>
<li style="margin:0.38rem 0;">Weight basis : <code>purchase.purchase.wb</code>, <code>sale.sale.wb</code>
</li>
<li style="margin:0.38rem 0;">État Weight basis : <code>purchase.weight.basis.qt_type</code>
</li>
<li style="margin:0.38rem 0;">Packing : <code>lot.lot.lot_qt</code>, <code>lot.lot.lot_unit</code>
</li>
<li style="margin:0.38rem 0;">Tolerances : <code>tol_min</code>, <code>tol_max</code>, <code>tol_min_qt</code>, <code>tol_max_qt</code>, <code>tol_min_v</code>, <code>tol_max_v</code>
</li>
</ul>
### Création ligne / lot virtuel ### Création ligne / lot virtuel
- Achat : `purchase.py`, `Line.validate` <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Vente : `sale.py`, `SaleLine.validate` <li style="margin:0.38rem 0;">Achat : <code>purchase.py</code>, <code>Line.validate</code>
- Si `quantity_theorical` est saisi et que `quantity` est vide ou zéro : </li>
- `quantity` est initialisée depuis `quantity_theorical` ; <li style="margin:0.38rem 0;">Vente : <code>sale.py</code>, <code>SaleLine.validate</code>
- seulement si aucun lot physique n'existe. </li>
- Si la ligne est éligible : <li style="margin:0.38rem 0;">Si <code>quantity_theorical</code> est saisi et que <code>quantity</code> est vide ou zéro :
- pas `created_by_code` ; <ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
- pas encore de lot ; <li style="margin:0.38rem 0;"><code>quantity</code> est initialisée depuis <code>quantity_theorical</code> ;
- produit non service ; </li>
- `quantity_theorical != 0` ; <li style="margin:0.38rem 0;">seulement si aucun lot physique n&#x27;existe.
- création d'un lot `virtual`. </li>
- Le lot virtuel reçoit une première entrée `lot.qt.hist`. </ul>
- `Lot.validate` crée le `lot.qt` ouvert via `createVirtualPart`. </li>
<li style="margin:0.38rem 0;">Si la ligne est éligible :
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
<li style="margin:0.38rem 0;">pas <code>created_by_code</code> ;
</li>
<li style="margin:0.38rem 0;">pas encore de lot ;
</li>
<li style="margin:0.38rem 0;">produit non service ;
</li>
<li style="margin:0.38rem 0;"><code>quantity_theorical != 0</code> ;
</li>
<li style="margin:0.38rem 0;">création d&#x27;un lot <code>virtual</code>.
</li>
</ul>
</li>
<li style="margin:0.38rem 0;">Le lot virtuel reçoit une première entrée <code>lot.qt.hist</code>.
</li>
<li style="margin:0.38rem 0;"><code>Lot.validate</code> crée le <code>lot.qt</code> ouvert via <code>createVirtualPart</code>.
</li>
</ul>
### Modification de `quantity_theorical` ### Modification de `quantity_theorical`
- Achat : `purchase.py`, `Line.write` <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Vente : `sale.py`, `SaleLine.write` <li style="margin:0.38rem 0;">Achat : <code>purchase.py</code>, <code>Line.write</code>
- Cible lot virtuel : </li>
<li style="margin:0.38rem 0;">Vente : <code>sale.py</code>, <code>SaleLine.write</code>
</li>
<li style="margin:0.38rem 0;">Cible lot virtuel :
</li>
</ul>
<pre style="background:#263238; color:#eef7ff; padding:1rem; border-radius:0.35rem; overflow:auto;"><code>target_quantity = quantity_theorical - somme(lots physiques convertis)</code></pre> <pre style="background:#263238; color:#eef7ff; padding:1rem; border-radius:0.35rem; overflow:auto;"><code>target_quantity = quantity_theorical - somme(lots physiques convertis)</code></pre>
- Si `target_quantity < 0` : <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- blocage : `Please unlink or unmatch lot`. <li style="margin:0.38rem 0;">Si <code>target_quantity &lt; 0</code> :
- Cible `lot.qt` libre : <ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
<li style="margin:0.38rem 0;">blocage : <code>Please unlink or unmatch lot</code>.
</li>
</ul>
</li>
<li style="margin:0.38rem 0;">Cible <code>lot.qt</code> libre :
</li>
</ul>
<pre style="background:#263238; color:#eef7ff; padding:1rem; border-radius:0.35rem; overflow:auto;"><code>free_quantity = target_quantity - somme(lot.qt déjà matchés ou shippés)</code></pre> <pre style="background:#263238; color:#eef7ff; padding:1rem; border-radius:0.35rem; overflow:auto;"><code>free_quantity = target_quantity - somme(lot.qt déjà matchés ou shippés)</code></pre>
- Si `free_quantity < 0` : <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- blocage : `Please unlink or unmatch lot`. <li style="margin:0.38rem 0;">Si <code>free_quantity &lt; 0</code> :
- Si un `lot.qt` libre existe : <ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
- sa quantité est remplacée. <li style="margin:0.38rem 0;">blocage : <code>Please unlink or unmatch lot</code>.
- Si aucun `lot.qt` libre n'existe et `free_quantity > 0` : </li>
- création d'un nouveau `lot.qt`. </ul>
- Les fees de ligne sont resynchronisés. </li>
<li style="margin:0.38rem 0;">Si un <code>lot.qt</code> libre existe :
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
<li style="margin:0.38rem 0;">sa quantité est remplacée.
</li>
</ul>
</li>
<li style="margin:0.38rem 0;">Si aucun <code>lot.qt</code> libre n&#x27;existe et <code>free_quantity &gt; 0</code> :
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
<li style="margin:0.38rem 0;">création d&#x27;un nouveau <code>lot.qt</code>.
</li>
</ul>
</li>
<li style="margin:0.38rem 0;">Les fees de ligne sont resynchronisés.
</li>
</ul>
### Ajout de lots physiques ### Ajout de lots physiques
- Wizard : `lot.add` <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Méthodes : <li style="margin:0.38rem 0;">Wizard : <code>lot.add</code>
- `LotQt.add_physical_lots` </li>
- `LotQt.add_physical_lot` <li style="margin:0.38rem 0;">Méthodes :
- Source obligatoire : une ligne `lot.qt`. <ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
- Ajout direct depuis un lot physique refusé. <li style="margin:0.38rem 0;"><code>LotQt.add_physical_lots</code>
- Ajout physique côté vente par ce wizard refusé : utiliser `Apply matching`. </li>
- Le lot physique reprend : <li style="margin:0.38rem 0;"><code>LotQt.add_physical_lot</code>
- ligne achat ; </li>
- vente matchée si présente ; </ul>
- shipment ; </li>
- produit ; <li style="margin:0.38rem 0;">Source obligatoire : une ligne <code>lot.qt</code>.
- unité ; </li>
- quantités ; <li style="margin:0.38rem 0;">Ajout direct depuis un lot physique refusé.
- premium ; </li>
- chunk key. <li style="margin:0.38rem 0;">Ajout physique côté vente par ce wizard refusé : utiliser <code>Apply matching</code>.
- Après création : </li>
- réduction de la ligne `lot.qt` source ; <li style="margin:0.38rem 0;">Le lot physique reprend :
- pas de quantité `lot.qt` négative ; <ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
- recalcul du lot virtuel ; <li style="margin:0.38rem 0;">ligne achat ;
- recalcul de `quantity` ; </li>
- mise à jour moves et fees si nécessaire. <li style="margin:0.38rem 0;">vente matchée si présente ;
</li>
<li style="margin:0.38rem 0;">shipment ;
</li>
<li style="margin:0.38rem 0;">produit ;
</li>
<li style="margin:0.38rem 0;">unité ;
</li>
<li style="margin:0.38rem 0;">quantités ;
</li>
<li style="margin:0.38rem 0;">premium ;
</li>
<li style="margin:0.38rem 0;">chunk key.
</li>
</ul>
</li>
<li style="margin:0.38rem 0;">Après création :
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
<li style="margin:0.38rem 0;">réduction de la ligne <code>lot.qt</code> source ;
</li>
<li style="margin:0.38rem 0;">pas de quantité <code>lot.qt</code> négative ;
</li>
<li style="margin:0.38rem 0;">recalcul du lot virtuel ;
</li>
<li style="margin:0.38rem 0;">recalcul de <code>quantity</code> ;
</li>
<li style="margin:0.38rem 0;">mise à jour moves et fees si nécessaire.
</li>
</ul>
</li>
</ul>
### Retrait de lots physiques ### Retrait de lots physiques
- Wizard : `lot.remove` <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Lot ouvert : retrait interdit. <li style="margin:0.38rem 0;">Wizard : <code>lot.remove</code>
- Lot avec `stock.move` : </li>
- move obligatoire en `draft`. <li style="margin:0.38rem 0;">Lot ouvert : retrait interdit.
- Lot matché ou shippé : </li>
- warning confirmable. <li style="margin:0.38rem 0;">Lot avec <code>stock.move</code> :
- Effets : <ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
- suppression du move draft ; <li style="margin:0.38rem 0;">move obligatoire en <code>draft</code>.
- restauration de la quantité dans `lot.qt` ; </li>
- contexte restauré via shipment, `getVlot_p()`, `getVlot_s()` ; </ul>
- recalcul lot virtuel, `quantity`, fees. </li>
<li style="margin:0.38rem 0;">Lot matché ou shippé :
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
<li style="margin:0.38rem 0;">warning confirmable.
</li>
</ul>
</li>
<li style="margin:0.38rem 0;">Effets :
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
<li style="margin:0.38rem 0;">suppression du move draft ;
</li>
<li style="margin:0.38rem 0;">restauration de la quantité dans <code>lot.qt</code> ;
</li>
<li style="margin:0.38rem 0;">contexte restauré via shipment, <code>getVlot_p()</code>, <code>getVlot_s()</code> ;
</li>
<li style="margin:0.38rem 0;">recalcul lot virtuel, <code>quantity</code>, fees.
</li>
</ul>
</li>
</ul>
### Weighing / états de quantité ### Weighing / états de quantité
- Wizard : `lot.weighing` <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Action UI : `Do weighing` <li style="margin:0.38rem 0;">Wizard : <code>lot.weighing</code>
- Écrit ou met à jour `lot.qt.hist`. </li>
- Peut mettre à jour `lot_state`. <li style="margin:0.38rem 0;">Action UI : <code>Do weighing</code>
- Synchronise : </li>
- lot ; <li style="margin:0.38rem 0;">Écrit ou met à jour <code>lot.qt.hist</code>.
- quantités ouvertes ; </li>
- fees. <li style="margin:0.38rem 0;">Peut mettre à jour <code>lot_state</code>.
- Les vues `lot.qt.hist` sont consultatives. </li>
<li style="margin:0.38rem 0;">Synchronise :
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
<li style="margin:0.38rem 0;">lot ;
</li>
<li style="margin:0.38rem 0;">quantités ouvertes ;
</li>
<li style="margin:0.38rem 0;">fees.
</li>
</ul>
</li>
<li style="margin:0.38rem 0;">Les vues <code>lot.qt.hist</code> sont consultatives.
</li>
</ul>
### Quantité compteur `quantity` ### Quantité compteur `quantity`
- Méthode : `Lot._recalc_line_quantity` <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Sans physique : <li style="margin:0.38rem 0;">Méthode : <code>Lot._recalc_line_quantity</code>
- `quantity` suit le lot virtuel. </li>
- Avec physiques : <li style="margin:0.38rem 0;">Sans physique :
- `quantity` somme uniquement les lots physiques. <ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
- `quantity` est readonly côté ligne trade. <li style="margin:0.38rem 0;"><code>quantity</code> suit le lot virtuel.
</li>
</ul>
</li>
<li style="margin:0.38rem 0;">Avec physiques :
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
<li style="margin:0.38rem 0;"><code>quantity</code> somme uniquement les lots physiques.
</li>
</ul>
</li>
<li style="margin:0.38rem 0;"><code>quantity</code> est readonly côté ligne trade.
</li>
</ul>
### Montant de ligne ### Montant de ligne
- Achat : `purchase.line.on_change_with_amount()` <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Vente : `sale.line.on_change_with_amount()` <li style="margin:0.38rem 0;">Achat : <code>purchase.line.on_change_with_amount()</code>
- Helper : </li>
- `_get_amount_quantity()` <li style="margin:0.38rem 0;">Vente : <code>sale.line.on_change_with_amount()</code>
- `_get_weight_basis_quantity()` </li>
- Priorités : <li style="margin:0.38rem 0;">Helper :
- `finished = False` : `quantity_theorical` <ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
- `finished = True` + Weight basis disponible : somme physique dans cet état <li style="margin:0.38rem 0;"><code>_get_amount_quantity()</code>
- `finished = True` sans Weight basis exploitable : `quantity` </li>
- fallback legacy : `quantity` si `quantity_theorical` vide <li style="margin:0.38rem 0;"><code>_get_weight_basis_quantity()</code>
</li>
</ul>
</li>
<li style="margin:0.38rem 0;">Priorités :
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
<li style="margin:0.38rem 0;"><code>finished = False</code> : <code>quantity_theorical</code>
</li>
<li style="margin:0.38rem 0;"><code>finished = True</code> + Weight basis disponible : somme physique dans cet état
</li>
<li style="margin:0.38rem 0;"><code>finished = True</code> sans Weight basis exploitable : <code>quantity</code>
</li>
<li style="margin:0.38rem 0;">fallback legacy : <code>quantity</code> si <code>quantity_theorical</code> vide
</li>
</ul>
</li>
</ul>
### Garde-fous Python ### Garde-fous Python
- Check central : <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- `lot.lot.assert_lines_quantity_consistency()` <li style="margin:0.38rem 0;">Check central :
- Blocage `lot.qt` orphelin non zéro : <ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
- `lot.qt.validate` <li style="margin:0.38rem 0;"><code>lot.lot.assert_lines_quantity_consistency()</code>
- Appels après : </li>
- modification `quantity_theorical` ; </ul>
- création / suppression de lots physiques ; </li>
- matching / unmatching ; <li style="margin:0.38rem 0;">Blocage <code>lot.qt</code> orphelin non zéro :
- shipping / unshipping ; <ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
- weighing. <li style="margin:0.38rem 0;"><code>lot.qt.validate</code>
</li>
</ul>
</li>
<li style="margin:0.38rem 0;">Appels après :
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
<li style="margin:0.38rem 0;">modification <code>quantity_theorical</code> ;
</li>
<li style="margin:0.38rem 0;">création / suppression de lots physiques ;
</li>
<li style="margin:0.38rem 0;">matching / unmatching ;
</li>
<li style="margin:0.38rem 0;">shipping / unshipping ;
</li>
<li style="margin:0.38rem 0;">weighing.
</li>
</ul>
</li>
</ul>
### Diagnostic SQL ### Diagnostic SQL
- Script : <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- [sql/quantity_consistency_checks.sql](sql/quantity_consistency_checks.sql) <li style="margin:0.38rem 0;">Script :
- Usage : <ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
- audit des bases de test ; <li style="margin:0.38rem 0;"><a href="sql/quantity_consistency_checks.sql">sql/quantity_consistency_checks.sql</a>
- audit des données historiques ; </li>
- qualification avant correction. </ul>
- Le script ignore totalement les `lot.qt = 0`. </li>
<li style="margin:0.38rem 0;">Usage :
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
<li style="margin:0.38rem 0;">audit des bases de test ;
</li>
<li style="margin:0.38rem 0;">audit des données historiques ;
</li>
<li style="margin:0.38rem 0;">qualification avant correction.
</li>
</ul>
</li>
<li style="margin:0.38rem 0;">Le script ignore totalement les <code>lot.qt = 0</code>.
</li>
</ul>
## Tests proches ## Tests proches
- `modules/purchase_trade/tests/test_module.py` <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Couverture existante : <li style="margin:0.38rem 0;"><code>modules/purchase_trade/tests/test_module.py</code>
- `quantity` readonly ; </li>
- initialisation depuis `quantity_theorical` ; <li style="margin:0.38rem 0;">Couverture existante :
- protection si lots physiques ; <ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
- amount sur théorique / physique / Weight basis ; <li style="margin:0.38rem 0;"><code>quantity</code> readonly ;
- resynchronisation des lots virtuels ; </li>
- blocages quand l'open ne suffit plus. <li style="margin:0.38rem 0;">initialisation depuis <code>quantity_theorical</code> ;
- Tests à ajouter : </li>
- `lot_hist` readonly ; <li style="margin:0.38rem 0;">protection si lots physiques ;
- `Do weighing` crée ou met à jour un état ; </li>
- lot virtuel sans saisie directe `lot_qt` / `lot_unit` ; <li style="margin:0.38rem 0;">amount sur théorique / physique / Weight basis ;
- contrôles SQL rejoués sur jeux de données incohérents. </li>
<li style="margin:0.38rem 0;">resynchronisation des lots virtuels ;
</li>
<li style="margin:0.38rem 0;">blocages quand l&#x27;open ne suffit plus.
</li>
</ul>
</li>
<li style="margin:0.38rem 0;">Tests à ajouter :
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
<li style="margin:0.38rem 0;"><code>lot_hist</code> readonly ;
</li>
<li style="margin:0.38rem 0;"><code>Do weighing</code> crée ou met à jour un état ;
</li>
<li style="margin:0.38rem 0;">lot virtuel sans saisie directe <code>lot_qt</code> / <code>lot_unit</code> ;
</li>
<li style="margin:0.38rem 0;">contrôles SQL rejoués sur jeux de données incohérents.
</li>
</ul>
</li>
</ul>

View File

@@ -1,3 +1,5 @@
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
# Lots Management # Lots Management
Statut: `migration partielle` Statut: `migration partielle`
@@ -13,15 +15,21 @@ commercial, le sens achat/vente et l'avancement logistique.
### Notes developpeur ### Notes developpeur
- Filtres: `Matching status`, `Side`, `Shipping status`, `Dimension`, <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
`Strategy`. <li style="margin:0.38rem 0;">Filtres: <code>Matching status</code>, <code>Side</code>, <code>Shipping status</code>, <code>Dimension</code>, <code>Strategy</code>.
- Dates `As of` / `To`: `purchase.purchase_date` et `sale.sale_date`. </li>
- `Unshipped`: aucun `shipment_in`. <li style="margin:0.38rem 0;">Dates <code>As of</code> / <code>To</code>: <code>purchase.purchase_date</code> et <code>sale.sale_date</code>.
- `Scheduled`: shipment `draft`. </li>
- `Shipped`: shipment `started`. <li style="margin:0.38rem 0;"><code>Unshipped</code>: aucun <code>shipment_in</code>.
- `Received`: shipment `received` ou `done`. </li>
- `Shipment Type = Dropship` si `from_location.type = supplier` et <li style="margin:0.38rem 0;"><code>Scheduled</code>: shipment <code>draft</code>.
`to_location.type = customer`, sinon `Inbound`. </li>
- `Mark as finished` masque seulement les reliquats ouverts / virtuels, pas <li style="margin:0.38rem 0;"><code>Shipped</code>: shipment <code>started</code>.
les lots physiques. </li>
<li style="margin:0.38rem 0;"><code>Received</code>: shipment <code>received</code> ou <code>done</code>.
</li>
<li style="margin:0.38rem 0;"><code>Shipment Type = Dropship</code> si <code>from_location.type = supplier</code> et <code>to_location.type = customer</code>, sinon <code>Inbound</code>.
</li>
<li style="margin:0.38rem 0;"><code>Mark as finished</code> masque seulement les reliquats ouverts / virtuels, pas les lots physiques.
</li>
</ul>

View File

@@ -1,3 +1,5 @@
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
# Matching achat / vente # Matching achat / vente
Statut: `migration partielle` Statut: `migration partielle`
@@ -14,14 +16,16 @@ lot source.
### Notes developpeur ### Notes developpeur
- La quantite du wizard doit correspondre a la somme des quantites ouvertes <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
selectionnees. <li style="margin:0.38rem 0;">La quantite du wizard doit correspondre a la somme des quantites ouvertes selectionnees.
- Creer une ligne par `lot.qt` source. </li>
- Conserver `created_by_code = True` pour eviter les creations automatiques <li style="margin:0.38rem 0;">Creer une ligne par <code>lot.qt</code> source.
parasites lors des validations. </li>
<li style="margin:0.38rem 0;">Conserver <code>created_by_code = True</code> pour eviter les creations automatiques parasites lors des validations.
</li>
</ul>
## Notes de migration ## Notes de migration
Les regles sur `Apply matching` presentes dans les notes de session du Les regles sur `Apply matching` presentes dans les notes de session du
`2026-05-09` doivent encore etre promues ici. `2026-05-09` doivent encore etre promues ici.

View File

@@ -1,3 +1,5 @@
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
# Paiements et banques # Paiements et banques
Statut: `migration partielle` Statut: `migration partielle`
@@ -13,9 +15,13 @@ bancaire utilise par la compagnie courante pour encaisser ou payer.
### Notes developpeur ### Notes developpeur
- Contrats: `sale.sale`, `purchase.purchase`. <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- `bank_account`: compte de la party du contrat. <li style="margin:0.38rem 0;">Contrats: <code>sale.sale</code>, <code>purchase.purchase</code>.
- `our_bank_account`: compte de la compagnie courante, selectionnable parmi </li>
les comptes disponibles. <li style="margin:0.38rem 0;"><code>bank_account</code>: compte de la party du contrat.
- La devise du contrat est prioritaire pour proposer un compte par defaut. </li>
<li style="margin:0.38rem 0;"><code>our_bank_account</code>: compte de la compagnie courante, selectionnable parmi les comptes disponibles.
</li>
<li style="margin:0.38rem 0;">La devise du contrat est prioritaire pour proposer un compte par defaut.
</li>
</ul>

View File

@@ -1,3 +1,5 @@
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
# Pricing, basis, premium # Pricing, basis, premium
Statut: `migration partielle` Statut: `migration partielle`
@@ -13,9 +15,14 @@ la ligne soit en prix fixe ou en basis.
### Notes developpeur ### Notes developpeur
- `unit_price` reste le prix de base hors premium. <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Montant economique: `unit_price + premium converti si necessaire`. <li style="margin:0.38rem 0;"><code>unit_price</code> reste le prix de base hors premium.
- En basis, le premium s'applique aussi aux blocs valorises. </li>
<li style="margin:0.38rem 0;">Montant economique: <code>unit_price + premium converti si necessaire</code>.
</li>
<li style="margin:0.38rem 0;">En basis, le premium s&#x27;applique aussi aux blocs valorises.
</li>
</ul>
## BR-PT-PRI-002 - Linked currency ## BR-PT-PRI-002 - Linked currency
@@ -28,11 +35,14 @@ dans ce meme repere puis converti pour les calculs internes.
### Notes developpeur ### Notes developpeur
- Champs obligatoires si active: `linked_price`, `linked_currency`, <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
`linked_unit`. <li style="margin:0.38rem 0;">Champs obligatoires si active: <code>linked_price</code>, <code>linked_currency</code>, <code>linked_unit</code>.
- En `basis + linked currency`, `linked_price` represente le basis brut hors </li>
premium. <li style="margin:0.38rem 0;">En <code>basis + linked currency</code>, <code>linked_price</code> represente le basis brut hors premium.
- `amount` ajoute le premium converti. </li>
<li style="margin:0.38rem 0;"><code>amount</code> ajoute le premium converti.
</li>
</ul>
## BR-PT-PRI-003 - Pricing manuel ## BR-PT-PRI-003 - Pricing manuel
@@ -45,9 +55,13 @@ le prix de marche. Les cumuls et prix moyens sont calcules automatiquement.
### Notes developpeur ### Notes developpeur
- Champs saisis: `quantity`, `settl_price`. <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Champs derives: `fixed_qt`, `fixed_qt_price`, `unfixed_qt`, <li style="margin:0.38rem 0;">Champs saisis: <code>quantity</code>, <code>settl_price</code>.
`unfixed_qt_price`, `eod_price`, `last`. </li>
- Groupe metier: `line + component` ou `sale_line + component`. <li style="margin:0.38rem 0;">Champs derives: <code>fixed_qt</code>, <code>fixed_qt_price</code>, <code>unfixed_qt</code>, <code>unfixed_qt_price</code>, <code>eod_price</code>, <code>last</code>.
- Le composant choisi doit appartenir a la ligne courante. </li>
<li style="margin:0.38rem 0;">Groupe metier: <code>line + component</code> ou <code>sale_line + component</code>.
</li>
<li style="margin:0.38rem 0;">Le composant choisi doit appartenir a la ligne courante.
</li>
</ul>

View File

@@ -1,12 +1,19 @@
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
# Reports et templates # Reports et templates
Statut: `migration partielle` Statut: `migration partielle`
Voir aussi: Voir aussi:
- `../template-rules.md` <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- `../template-properties.md` <li style="margin:0.38rem 0;"><code>../template-rules.md</code>
- `../../../../notes/template_business_rules.md` </li>
<li style="margin:0.38rem 0;"><code>../template-properties.md</code>
</li>
<li style="margin:0.38rem 0;"><code>../../../../notes/template_business_rules.md</code>
</li>
</ul>
## BR-PT-RPT-001 - Templates trade via proprietes Python ## BR-PT-RPT-001 - Templates trade via proprietes Python
@@ -19,21 +26,30 @@ d'expressions fragiles dans le fichier bureautique.
### Notes developpeur ### Notes developpeur
- Preferer des proprietes Python simples, souvent prefixees `report_*`. <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Dans les placeholders XML, utiliser `&quot;` et `&apos;` plutot que des <li style="margin:0.38rem 0;">Preferer des proprietes Python simples, souvent prefixees <code>report_*</code>.
antislashs. </li>
- Pour les factures liees a vente/achat/shipment, privilegier le lot physique <li style="margin:0.38rem 0;">Dans les placeholders XML, utiliser <code>&amp;quot;</code> et <code>&amp;apos;</code> plutot que des antislashs.
comme pont. </li>
- Verifier le cache `invoice_report_cache` avant de conclure qu'une action <li style="margin:0.38rem 0;">Pour les factures liees a vente/achat/shipment, privilegier le lot physique comme pont.
report pointe vers le mauvais `.fodt`. </li>
- Pour les templates shipment, preferer `records[0]...` ou des proprietes sur <li style="margin:0.38rem 0;">Verifier le cache <code>invoice_report_cache</code> avant de conclure qu&#x27;une action report pointe vers le mauvais <code>.fodt</code>.
`stock.shipment.in` plutot qu'une variable locale supposee. </li>
<li style="margin:0.38rem 0;">Pour les templates shipment, preferer <code>records[0]...</code> ou des proprietes sur <code>stock.shipment.in</code> plutot qu&#x27;une variable locale supposee.
</li>
</ul>
## Decisions deja documentees a migrer ensuite ## Decisions deja documentees a migrer ensuite
- `insurance.fodt`: compagnie courante, amount insured a 110%, surveyor. <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- `packing_list.fodt`: date du jour, unites depuis `purchase.line`. <li style="margin:0.38rem 0;"><code>insurance.fodt</code>: compagnie courante, amount insured a 110%, surveyor.
- `bill.fodt`: maturity date reelle et montant en lettres depuis le total. </li>
- `invoice_ict.fodt` / `invoice_ict_final.fodt`: poids, shipments et lots. <li style="margin:0.38rem 0;"><code>packing_list.fodt</code>: date du jour, unites depuis <code>purchase.line</code>.
- `sale_ict.fodt`: priorite lots et unite reelle. </li>
<li style="margin:0.38rem 0;"><code>bill.fodt</code>: maturity date reelle et montant en lettres depuis le total.
</li>
<li style="margin:0.38rem 0;"><code>invoice_ict.fodt</code> / <code>invoice_ict_final.fodt</code>: poids, shipments et lots.
</li>
<li style="margin:0.38rem 0;"><code>sale_ict.fodt</code>: priorite lots et unite reelle.
</li>
</ul>

View File

@@ -1,3 +1,5 @@
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
# Risque, credit, forex # Risque, credit, forex
Statut: `placeholder` Statut: `placeholder`
@@ -9,7 +11,11 @@ Aucune regle canonique `purchase_trade` n'a ete promue ici dans cette premiere
passe. Les notes comptables et forex existantes doivent etre relues avant toute passe. Les notes comptables et forex existantes doivent etre relues avant toute
migration: migration:
- `notes/accounting/README.md` <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- `notes/accounting/business_rules.md` <li style="margin:0.38rem 0;"><code>notes/accounting/README.md</code>
- `notes/accounting/reporting.md` </li>
<li style="margin:0.38rem 0;"><code>notes/accounting/business_rules.md</code>
</li>
<li style="margin:0.38rem 0;"><code>notes/accounting/reporting.md</code>
</li>
</ul>

View File

@@ -1,3 +1,5 @@
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
# Journal de migration et sessions # Journal de migration et sessions
Statut: `non canonique` Statut: `non canonique`
@@ -7,25 +9,28 @@ canonique seulement quand elle est reprise dans une page thematique.
## Sources historiques ## Sources historiques
- `modules/purchase_trade/docs/business-rules.md` <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- `modules/purchase_trade/docs/business-rules-architecture-proposal.md` <li style="margin:0.38rem 0;"><code>modules/purchase_trade/docs/business-rules.md</code>
- `notes/business_rules.md` </li>
- `notes/template_business_rules.md` <li style="margin:0.38rem 0;"><code>modules/purchase_trade/docs/business-rules-architecture-proposal.md</code>
</li>
<li style="margin:0.38rem 0;"><code>notes/business_rules.md</code>
</li>
<li style="margin:0.38rem 0;"><code>notes/template_business_rules.md</code>
</li>
</ul>
## Notes deja partiellement promues ## Notes deja partiellement promues
- Session `2026-04-30`: PnL fees ouverts et `% rate`, promue dans <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
[fees.md](fees.md) et [valuation-pnl-mtm.md](valuation-pnl-mtm.md). <li style="margin:0.38rem 0;">Session <code>2026-04-30</code>: PnL fees ouverts et <code>% rate</code>, promue dans <a href="fees.md">fees.md</a> et <a href="valuation-pnl-mtm.md">valuation-pnl-mtm.md</a>.
- Session `2026-05-01`: solde ouvert apres lots physiques et lots effectifs </li>
des fees, promue dans [lots-and-quantities.md](lots-and-quantities.md) et <li style="margin:0.38rem 0;">Session <code>2026-05-01</code>: solde ouvert apres lots physiques et lots effectifs des fees, promue dans <a href="lots-and-quantities.md">lots-and-quantities.md</a> et <a href="fees.md">fees.md</a>.
[fees.md](fees.md). </li>
- Session `2026-05-06`: Remove physical lot, promue dans <li style="margin:0.38rem 0;">Session <code>2026-05-06</code>: Remove physical lot, promue dans <a href="lots-and-quantities.md">lots-and-quantities.md</a>.
[lots-and-quantities.md](lots-and-quantities.md). </li>
- Session `2026-05-09`: Lots Management, promue partiellement dans <li style="margin:0.38rem 0;">Session <code>2026-05-09</code>: Lots Management, promue partiellement dans <a href="lots-management.md">lots-management.md</a>.
[lots-management.md](lots-management.md). </li>
- Session `2026-05-13`: cadrage `quantity_theorical` / `quantity`, amount de <li style="margin:0.38rem 0;">Session <code>2026-05-13</code>: cadrage <code>quantity_theorical</code> / <code>quantity</code>, amount de ligne, Weight basis, invariants de quantite, checks Python bloquants et diagnostic SQL. Promue dans <a href="lots-and-quantities.md">lots-and-quantities.md</a>, <a href="lots-and-quantities.en.md">lots-and-quantities.en.md</a>, <a href="invariants.md">invariants.md</a> et <a href="sql/README.md">sql/README.md</a>.
ligne, Weight basis, invariants de quantite, checks Python bloquants et </li>
diagnostic SQL. Promue dans </ul>
[lots-and-quantities.md](lots-and-quantities.md),
[lots-and-quantities.en.md](lots-and-quantities.en.md),
[invariants.md](invariants.md) et [sql/README.md](sql/README.md).

View File

@@ -1,3 +1,5 @@
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
# Shipments et execution # Shipments et execution
Statut: `migration partielle` Statut: `migration partielle`
@@ -13,10 +15,16 @@ retard par rapport a son objectif.
### Notes developpeur ### Notes developpeur
- Configuration: onglet `Execution` de `party.party`. <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- La zone du shipment vient de `shipment.to_location.country`. <li style="margin:0.38rem 0;">Configuration: onglet <code>Execution</code> de <code>party.party</code>.
- Une region parente couvre ses sous-regions. </li>
- `% achieved` compte seulement les shipments deja affectes a un controller. <li style="margin:0.38rem 0;">La zone du shipment vient de <code>shipment.to_location.country</code>.
</li>
<li style="margin:0.38rem 0;">Une region parente couvre ses sous-regions.
</li>
<li style="margin:0.38rem 0;"><code>% achieved</code> compte seulement les shipments deja affectes a un controller.
</li>
</ul>
## BR-PT-SHP-002 - Couts SLA controller par pays et/ou lieu ## BR-PT-SHP-002 - Couts SLA controller par pays et/ou lieu
@@ -29,8 +37,12 @@ lieu. Le couple est le cas le plus specifique.
### Notes developpeur ### Notes developpeur
- Matching: `country + location`, puis `location`, puis `country`. <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Le pays vient de `shipment.to_location.country`. <li style="margin:0.38rem 0;">Matching: <code>country + location</code>, puis <code>location</code>, puis <code>country</code>.
</li>
<li style="margin:0.38rem 0;">Le pays vient de <code>shipment.to_location.country</code>.
</li>
</ul>
## BR-PT-SHP-003 - Weight reports distants par lot ## BR-PT-SHP-003 - Weight reports distants par lot
@@ -43,7 +55,11 @@ sur le shipment.
### Notes developpeur ### Notes developpeur
- Exporter seulement les lots physiques des `incoming_moves`. <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Exiger au minimum `controller` et `returned_id` sur le shipment. <li style="margin:0.38rem 0;">Exporter seulement les lots physiques des <code>incoming_moves</code>.
- Conserver les cles distantes et la date d'envoi sur le `weight.report`. </li>
<li style="margin:0.38rem 0;">Exiger au minimum <code>controller</code> et <code>returned_id</code> sur le shipment.
</li>
<li style="margin:0.38rem 0;">Conserver les cles distantes et la date d&#x27;envoi sur le <code>weight.report</code>.
</li>
</ul>

View File

@@ -1,3 +1,5 @@
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
# SQL diagnostics for purchase_trade business rules # SQL diagnostics for purchase_trade business rules
These scripts are read-only diagnostics for a PostgreSQL test database. These scripts are read-only diagnostics for a PostgreSQL test database.
@@ -23,24 +25,34 @@ reported as anomalous.
Run it on a restored test database: Run it on a restored test database:
```sql <pre style="background:#263238; color:#eef7ff; padding:1rem; border-radius:0.35rem; overflow:auto;"><code>\i modules/purchase_trade/docs/business/sql/quantity_consistency_checks.sql</code></pre>
\i modules/purchase_trade/docs/business/sql/quantity_consistency_checks.sql
```
The script returns rows only when it finds a potential issue. The script returns rows only when it finds a potential issue.
Main columns: Main columns:
- `check_name`: invariant or diagnostic that failed. <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- `contract_model`: `purchase.purchase` or `sale.sale`. <li style="margin:0.38rem 0;"><code>check_name</code>: invariant or diagnostic that failed.
- `contract_id`: database id of the contract. </li>
- `contract_number`: purchase or sale contract number. <li style="margin:0.38rem 0;"><code>contract_model</code>: <code>purchase.purchase</code> or <code>sale.sale</code>.
- `line_id`: `purchase.line` or `sale.line` id depending on the check. </li>
- `virtual_lot_id`: virtual lot involved in the inconsistency. <li style="margin:0.38rem 0;"><code>contract_id</code>: database id of the contract.
- `observed_value`: value found in the database. </li>
- `expected_value`: value required by the business rule. <li style="margin:0.38rem 0;"><code>contract_number</code>: purchase or sale contract number.
- `diff`: observed minus expected. </li>
- `detail`: human-readable explanation. <li style="margin:0.38rem 0;"><code>line_id</code>: <code>purchase.line</code> or <code>sale.line</code> id depending on the check.
</li>
<li style="margin:0.38rem 0;"><code>virtual_lot_id</code>: virtual lot involved in the inconsistency.
</li>
<li style="margin:0.38rem 0;"><code>observed_value</code>: value found in the database.
</li>
<li style="margin:0.38rem 0;"><code>expected_value</code>: value required by the business rule.
</li>
<li style="margin:0.38rem 0;"><code>diff</code>: observed minus expected.
</li>
<li style="margin:0.38rem 0;"><code>detail</code>: human-readable explanation.
</li>
</ul>
Cross-category UoM rows are reported as manual-review diagnostics because the Cross-category UoM rows are reported as manual-review diagnostics because the
Python code may pass explicit conversion factors that cannot be inferred safely Python code may pass explicit conversion factors that cannot be inferred safely

View File

@@ -1,3 +1,5 @@
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
# Valuation, PnL, MTM # Valuation, PnL, MTM
Statut: `migration partielle` Statut: `migration partielle`
@@ -13,12 +15,14 @@ n'est pas encore matchee a un achat.
### Notes developpeur ### Notes developpeur
- Une `sale.line` non matchee doit generer au minimum `sale priced`, `sale fee` <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
et `derivative` si applicable. <li style="margin:0.38rem 0;">Une <code>sale.line</code> non matchee doit generer au minimum <code>sale priced</code>, <code>sale fee</code> et <code>derivative</code> si applicable.
- Une sale basis sans detail de prix doit quand meme produire une ligne a zero </li>
ou au prix economique fallback selon la regle applicable. <li style="margin:0.38rem 0;">Une sale basis sans detail de prix doit quand meme produire une ligne a zero ou au prix economique fallback selon la regle applicable.
- Ne pas attacher arbitrairement une sale unique si plusieurs sales sont </li>
matchees au meme ouvert. <li style="margin:0.38rem 0;">Ne pas attacher arbitrairement une sale unique si plusieurs sales sont matchees au meme ouvert.
</li>
</ul>
## BR-PT-VAL-002 - References de valuation ## BR-PT-VAL-002 - References de valuation
@@ -31,9 +35,12 @@ vente, ouverte ou physique.
### Notes developpeur ### Notes developpeur
- References autorisees: `Purchase/Open`, `Purchase/Physic`, `Sale/Open`, <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
`Sale/Physic`. <li style="margin:0.38rem 0;">References autorisees: <code>Purchase/Open</code>, <code>Purchase/Physic</code>, <code>Sale/Open</code>, <code>Sale/Physic</code>.
- Un lot virtuel ne doit pas sortir avec une reference physique. </li>
<li style="margin:0.38rem 0;">Un lot virtuel ne doit pas sortir avec une reference physique.
</li>
</ul>
## BR-PT-VAL-003 - MTM hors fees ## BR-PT-VAL-003 - MTM hors fees
@@ -45,7 +52,11 @@ Le mark-to-market s'applique aux prix et aux derives, pas aux frais.
### Notes developpeur ### Notes developpeur
- MTM autorise pour `pur. priced`, `sale priced`, `derivative`. <ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
- Fees hors MTM: `pur. fee`, `sale fee`, `shipment fee`, `line fee`. <li style="margin:0.38rem 0;">MTM autorise pour <code>pur. priced</code>, <code>sale priced</code>, <code>derivative</code>.
- Pour les fees: `mtm_price`, `mtm`, `strategy` doivent rester vides. </li>
<li style="margin:0.38rem 0;">Fees hors MTM: <code>pur. fee</code>, <code>sale fee</code>, <code>shipment fee</code>, <code>line fee</code>.
</li>
<li style="margin:0.38rem 0;">Pour les fees: <code>mtm_price</code>, <code>mtm</code>, <code>strategy</code> doivent rester vides.
</li>
</ul>

View File

@@ -10,18 +10,13 @@ from __future__ import annotations
import argparse import argparse
import html import html
import re import re
import sys
from pathlib import Path from pathlib import Path
ROOT = Path(__file__).resolve().parents[1] ROOT = Path(__file__).resolve().parents[1]
BUSINESS = ROOT / "business" BUSINESS = ROOT / "business"
SOURCE = ROOT.parent / "docs_source" / "business" SOURCE = ROOT.parent / "docs_source" / "business"
TARGETS = (
"lots-and-quantities.md",
"lots-and-quantities.en.md",
)
MATERIAL_ICON = re.compile(r":material-[a-z0-9-]+:\s*") MATERIAL_ICON = re.compile(r":material-[a-z0-9-]+:\s*")
@@ -190,6 +185,56 @@ def render_callout(lines: list[str]) -> str:
) )
def is_bullet_line(line: str) -> bool:
return bool(re.match(r"^\s*-\s+", line))
def bullet_level(line: str) -> int:
return len(re.match(r"^\s*", line).group(0)) // 2
def render_list(lines: list[str]) -> str:
root: list[dict[str, object]] = []
stack: list[tuple[int, list[dict[str, object]]]] = [(-1, root)]
last_item: dict[str, object] | None = None
for line in lines:
if is_bullet_line(line):
level = bullet_level(line)
content = re.sub(r"^\s*-\s+", "", line).strip()
while stack[-1][0] >= level:
stack.pop()
item: dict[str, object] = {
"content": content,
"children": [],
}
stack[-1][1].append(item)
stack.append((level, item["children"]))
last_item = item
elif line.strip() and last_item is not None:
last_item["content"] = str(last_item["content"]) + " " + line.strip()
def render_items(items: list[dict[str, object]], level: int = 0) -> str:
margin = "1.1rem" if level == 0 else "1.35rem"
bullet = "disc" if level == 0 else "circle"
parts = [
f'<ul style="margin:0.65rem 0 1rem {margin}; padding-left:1rem; list-style-type:{bullet};">'
]
for item in items:
children = item["children"]
parts.append(
'<li style="margin:0.38rem 0;">'
f"{render_inline(str(item['content']))}"
)
if children:
parts.append(render_items(children, level + 1))
parts.append("</li>")
parts.append("</ul>")
return "\n".join(parts)
return render_items(root)
def render_source(text: str) -> str: def render_source(text: str) -> str:
lines = text.splitlines() lines = text.splitlines()
out = [ out = [
@@ -244,20 +289,47 @@ def render_source(text: str) -> str:
out.extend(table) out.extend(table)
continue continue
if is_bullet_line(line):
list_lines = []
while i < len(lines) and (
is_bullet_line(lines[i])
or (
lines[i].startswith(" ")
and lines[i].strip()
and not lines[i].lstrip().startswith("|")
)
):
list_lines.append(lines[i])
i += 1
out.append(render_list(list_lines))
continue
out.append(line) out.append(line)
i += 1 i += 1
return "\n".join(out).strip() + "\n" return "\n".join(out).strip() + "\n"
def render_all(normalize: bool = False) -> None: def source_files() -> list[Path]:
return sorted(path for path in SOURCE.rglob("*.md") if path.is_file())
def render_all(normalize: bool = False, check: bool = False) -> bool:
SOURCE.mkdir(parents=True, exist_ok=True) SOURCE.mkdir(parents=True, exist_ok=True)
for name in TARGETS: ok = True
source = SOURCE / name for source in source_files():
target = BUSINESS / name target = BUSINESS / source.relative_to(SOURCE)
target.parent.mkdir(parents=True, exist_ok=True)
if normalize: if normalize:
source.write_text(normalize_source(source.read_text(encoding="utf-8")), encoding="utf-8") source.write_text(normalize_source(source.read_text(encoding="utf-8")), encoding="utf-8")
target.write_text(render_source(source.read_text(encoding="utf-8")), encoding="utf-8") rendered = render_source(source.read_text(encoding="utf-8"))
if check:
if not target.exists() or target.read_text(encoding="utf-8") != rendered:
print(f"Outdated generated doc: {target.relative_to(ROOT.parent)}")
ok = False
else:
target.write_text(rendered, encoding="utf-8")
return ok
def main() -> None: def main() -> None:
@@ -267,8 +339,16 @@ def main() -> None:
action="store_true", action="store_true",
help="Clean existing source files from wiki-only syntax before rendering.", help="Clean existing source files from wiki-only syntax before rendering.",
) )
parser.add_argument(
"--check",
action="store_true",
help="Fail if generated wiki files are not up to date.",
)
args = parser.parse_args() args = parser.parse_args()
render_all(normalize=args.normalize_source) if args.normalize_source and args.check:
parser.error("--normalize-source cannot be used with --check")
if not render_all(normalize=args.normalize_source, check=args.check):
sys.exit(1)
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -0,0 +1,48 @@
# Index thematique des regles business
Statut: `migration partielle`
## Comment chercher une regle
- Contrats, dates, lieux, banques: [contracts.md](contracts.md)
- Lots virtuels, lots physiques, `lot.qt`, weighing: [FR](lots-and-quantities.md) / [EN](lots-and-quantities.en.md)
- Matching, Create Contracts, back-to-back: [matching.md](matching.md)
- Shipments, controllers, SLA, weight reports: [shipments-execution.md](shipments-execution.md)
- Pricing manuel, basis, premium, linked currency: [pricing.md](pricing.md)
- Fees, freight, lots effectifs, `% rate`: [fees.md](fees.md)
- Valuation, PnL, MTM, derivatives: [valuation-pnl-mtm.md](valuation-pnl-mtm.md)
- Factures provisoires/finales, padding: [invoicing.md](invoicing.md)
- Impacts `account.move`, validate/post: [accounting-bridge.md](accounting-bridge.md)
- Comptes bancaires, payment terms, payment orders: [payments-banking.md](payments-banking.md)
- Relatorio, `.fodt`, proprietes `report_*`: [reports-templates.md](reports-templates.md)
- Risque, credit, forex: [risk-credit-forex.md](risk-credit-forex.md)
- Rapport Lots Management: [lots-management.md](lots-management.md)
- Diagnostics SQL des invariants: [sql/README.md](sql/README.md)
## Regles migrees dans cette premiere passe
- `BR-PT-CON-001`: texte par defaut de pricing rule.
- `BR-PT-CON-002`: delivery period coherent.
- `BR-PT-CON-003`: lieux stock propages dans Create Contracts.
- `BR-PT-LOT-001`: cycle de vie des lots et des quantites.
- `BR-PT-LOT-002`: quantity contractuelle, execute physique et ligne finie.
- `BR-PT-LOT-003`: garde-fous Python et diagnostics SQL des invariants de
quantite.
- `BR-PT-MAT-001`: Create Contracts multi-lots.
- `BR-PT-SHP-001`: affectation controller.
- `BR-PT-SHP-002`: couts SLA controller.
- `BR-PT-SHP-003`: weight reports distants.
- `BR-PT-PRI-001`: premium dans priced et basis.
- `BR-PT-PRI-002`: linked currency.
- `BR-PT-PRI-003`: pricing manuel.
- `BR-PT-FEE-001`: maritime freight depuis fee shipment.
- `BR-PT-FEE-002`: lots effectifs des fees.
- `BR-PT-FEE-003`: `% rate` via delta de financement.
- `BR-PT-VAL-001`: valuation achat/vente et sale-first.
- `BR-PT-VAL-002`: references de valuation.
- `BR-PT-VAL-003`: MTM hors fees.
- `BR-PT-INV-001`: padding facture provisoire vente.
- `BR-PT-ACC-001`: Validate facture client attribue le numero.
- `BR-PT-PAY-001`: comptes bancaires tiers vs compagnie.
- `BR-PT-RPT-001`: templates trade via proprietes Python.
- `BR-PT-LOTMGT-001`: filtres Lots Management.

View File

@@ -0,0 +1,117 @@
# Guide de lecture des règles business
Statut: `migration partielle`
Dernière mise à jour: `2026-05-13`
Ce dossier devient la source de lecture thématique publiée dans le wiki pour les
règles business du module `purchase_trade`.
Toutes les pages business publiées dans `modules/purchase_trade/docs/business/`
sont générées depuis une source de vérité plus sobre, rangée hors du dossier
wiki dans `modules/purchase_trade/docs_source/business/`.
Les pages publiées portent un commentaire `Generated from ...` en tête de
fichier et ne doivent pas être modifiées directement. Aucun contenu business ne
doit être ajouté ou modifié sans passer par cette source puis par le script de
génération.
Chaque page doit rester lisible par deux publics:
- les consultants, qui ont besoin d'une règle fonctionnelle stable sans détail
de code inutile;
- les développeurs, qui ont besoin des champs, modèles, fichiers et tests
concernés pour appliquer la règle sans l'interpréter.
## Convention de langues
Chaque page thématique durable doit exister en deux versions maintenues
ensemble:
- une page française, rédigée en français correct avec accents, typographie et
formulations naturelles pour le wiki consultant;
- une page anglaise miroir, portant le même contenu fonctionnel et technique.
Convention de nommage:
- page française principale: `theme.md`;
- page anglaise miroir: `theme.en.md`.
Toute modification d'une règle business, d'un statut, d'un champ technique ou
d'un point de vigilance doit être reportée dans les deux pages au même moment.
Les deux pages doivent indiquer leur page miroir en en-tête.
## Convention source / wiki
Pour toute page business:
- éditer la source de vérité dans `modules/purchase_trade/docs_source/business/`;
- régénérer la version wiki avec:
```bash
python modules/purchase_trade/docs/tools/render_business_docs.py
```
Le rendu wiki privilégie du HTML simple et portable plutôt que des extensions
MkDocs optionnelles. Cela évite d'exposer dans le wiki des marqueurs non rendus
comme `!!!` ou `:material-...:`.
Les fichiers générés dans `modules/purchase_trade/docs/business/` sont des
artefacts de publication: ils peuvent être relus, mais toute correction doit
être reportée dans `docs_source/business/` avant régénération.
Avant de livrer une modification documentaire, vérifier que les pages publiées
sont à jour avec:
```bash
python modules/purchase_trade/docs/tools/render_business_docs.py --check
```
## Convention de rédaction
Pour chaque règle durable, utiliser autant que possible ce format:
```md
### BR-PT-THEME-001 - Titre court
Statut: active
Source: business-rules.md / note de session / décision projet
#### Règle consultant
Texte fonctionnel, sans nom de champ si ce n'est pas nécessaire.
#### Notes développeur
- Modèles/champs:
- Fichiers:
- Tests:
- Points de vigilance:
```
## Convention de validation
Quand une règle business devient structurante pour l'intégrité des données, elle
doit être accompagnée autant que possible de deux garde-fous:
- un check applicatif bloquant dans le code Python, appelé à la fin des flux qui
modifient les données concernées;
- un diagnostic SQL en lecture seule pour auditer les bases existantes ou les
bases de test.
Les diagnostics SQL du module sont rangés dans `business/sql/`. Ils ne
remplacent pas les règles applicatives: ils servent à retrouver et qualifier les
écarts déjà présents dans une base.
## Sources pendant la migration
Les anciennes pages ne sont pas supprimées à cette étape. Elles restent des
sources de vérification jusqu'à ce que chaque décision soit promue dans une
page thématique:
- `modules/purchase_trade/docs/business-rules.md`
- `modules/purchase_trade/docs/fees.md`
- `modules/purchase_trade/docs/padding-invoice-accounting.md`
- `modules/purchase_trade/docs/template-rules.md`
- `modules/purchase_trade/docs/template-properties.md`
- `notes/business_rules.md`
- `notes/template_business_rules.md`

View File

@@ -0,0 +1,25 @@
# Pont comptable
Statut: `migration partielle`
## BR-PT-ACC-001 - Validate facture client attribue aussi le numero
Source: `BR-PT-017`
### Regle consultant
Une facture client doit recevoir son mouvement comptable et son numero des la
validation, comme une facture fournisseur.
### Notes developpeur
- Cible: `account.invoice` avec `type = out`.
- Workflow `Validate`: creer `account.move` et attribuer `number`.
- Workflow `Post`: ne doit pas reintroduire une session fraiche specifique au
flux client.
## Notes de migration
Les notes comptables detaillees restent dans `notes/accounting/` tant qu'elles
n'ont pas ete promues ici.

View File

@@ -0,0 +1,50 @@
# Contrats achat / vente
Statut: `migration partielle`
## BR-PT-CON-001 - Texte par defaut de pricing rule
Source: `BR-PT-013`
### Regle consultant
Le texte de regle de pricing recurrent doit etre configure une seule fois et
repris automatiquement sur les nouvelles lignes achat et vente.
### Notes developpeur
- Configuration: `purchase_trade.configuration.pricing_rule`.
- Cibles: `purchase.line.pricing_rule`, `sale.line.pricing_rule`.
- Les lignes existantes ne sont pas modifiees retroactivement.
## BR-PT-CON-002 - Delivery period coherent
Source: `BR-PT-014` historique, doublon de numerotation a corriger
### Regle consultant
Une periode de livraison ne peut pas commencer apres sa date de fin. Une seule
borne renseignee reste acceptee.
### Notes developpeur
- Champs: `purchase.line.from_del`, `purchase.line.to_del`,
`sale.line.from_del`, `sale.line.to_del`.
- Validation attendue: bloquer si `from_del > to_del`.
## BR-PT-CON-003 - Propagation des lieux stock dans Create Contracts
Source: `BR-PT-024`
### Regle consultant
Quand un contrat miroir est cree depuis une quantite ouverte, les lieux
logistiques doivent etre proposes selon le flux source pour eviter la ressaisie.
### Notes developpeur
- Champs: `from_location`, `to_location`.
- Flux fournisseur vers client: recopier le couple source.
- Achat vers stock puis vente: `sale.from_location = purchase.to_location`.
- Vente depuis stock puis achat: `purchase.to_location = sale.from_location`.

View File

@@ -0,0 +1,55 @@
# Fees
Statut: `migration partielle`
Voir aussi la page technique historique: `../fees.md`.
## BR-PT-FEE-001 - Freight value depuis fee shipment
Source: `BR-PT-003`
### Regle consultant
La valeur de fret affichee sur les documents facture vient du fee maritime du
shipment, pas d'un champ direct de la facture.
### Notes developpeur
- Retrouver le lot physique depuis la facture.
- Retrouver son `shipment_in`.
- Chercher le `fee.fee` avec `product.name = 'Maritime freight'`.
- Utiliser `fee.get_amount()`.
## BR-PT-FEE-002 - Les fees lies aux lots privilegient les physiques
Source: `BR-PT-021`
### Regle consultant
Un fee suit le lot virtuel tant qu'aucun lot physique n'est lie. Des qu'un lot
physique est lie, les lots physiques deviennent la base de calcul du fee.
### Notes developpeur
- Ne pas supprimer le lien virtuel: il reste le fallback.
- Quantite `ppack`: somme de `lot.lot_qt` des physiques.
- Modes quantitatifs: quantites courantes converties des physiques.
- La meme selection s'applique au PnL fee.
- Points de synchronisation: creation fee, lien `fee.lots`, changement de
`quantity_theorical`, weighing, suppression de physique.
## BR-PT-FEE-003 - Fees `% rate` via delta de financement
Source: `BR-PT-016` historique et notes `2026-04-30`
### Regle consultant
Les frais financiers en pourcentage se calculent avec le delta de financement
de la ligne d'estimation `BL date`, pas avec la date du jour.
### Notes developpeur
- Formule: `amount = unit_price * quantity * (price / 100) * fin_int_delta / 360`.
- Source du delta: ligne `Estimated date` avec `trigger = bldate`.
- Si aucune ligne `bldate` n'existe, ne pas calculer de montant `% rate`.

View File

@@ -0,0 +1,22 @@
# Glossaire purchase_trade
Statut: `migration partielle`
- `Purchase Line`: ligne d'achat.
- `Sale Line`: ligne de vente.
- `quantity_theorical`: quantite contractuelle theorique d'une ligne.
- `Virtual Lot`: lot de type `virtual`, representant un reliquat ouvert.
- `Physical Lot`: lot de type `physic`, representant une quantite executee.
- `lot.qt`: ligne de quantite ouverte, matchee ou rattachee a un shipment.
- `lot.qt ouvert`: `lot.qt` libre, sans lot oppose et sans shipment.
- `Shipment In`: shipment entrant utilise aussi pour les flux dropship dans ce module.
- `Dropship`: flux fournisseur vers client, sans passage par stock interne.
- `Inbound`: flux entrant classique qui ne correspond pas au dropship.
- `Basis`: mode de prix construit a partir d'un prix de marche et d'un premium.
- `Premium`: prime ou discount commercial ajoute au prix economique.
- `Linked currency`: saisie d'un prix dans une devise/unite liee, par exemple `USC/LB`.
- `Valuation`: lignes de PnL generees pour prix, fees, derivatives et MTM.
- `MTM`: mark-to-market applique aux lignes valorisables au marche.
- `Fee`: frais commercial ou logistique rattache a une ligne, un lot ou un shipment.
- `Report property`: propriete Python exposee pour simplifier un template Relatorio.

View File

@@ -0,0 +1,75 @@
# Invariants structurants
Statut: `migration partielle`
Ces invariants doivent etre relus avant de modifier des flux `purchase_trade`
touchant lots, quantites, fees, PnL, factures ou templates.
## INV-PT-001 - Le lot physique est le pont metier stable
### Regle consultant
Quand une information doit relier achat, vente, shipment et facture, le chemin
fonctionnel de reference passe par le lot physique. Il porte le lien entre les
contrats et l'execution logistique.
### Notes developpeur
- Source historique: `BR-PT-002`.
- Voir aussi: [lots-and-quantities.md](lots-and-quantities.md),
[matching.md](matching.md), [reports-templates.md](reports-templates.md).
- Champs frequents: `lot.line`, `lot.sale_line`, `lot_shipment_in`,
`lot_shipment_internal`, `lot_shipment_out`.
## INV-PT-002 - Le reliquat ouvert ne doit pas doubler les lots physiques
### Regle consultant
Une quantite deja executee physiquement ne doit pas rester disponible comme
quantite ouverte. Le reliquat ouvert represente seulement ce qui reste a
executer.
### Notes developpeur
- Source historique: `BR-PT-020`.
- Le calcul doit tenir compte de la quantite contractuelle, des lots physiques
existants et des `lot.qt` deja matches ou shippes.
- Regle de conservation:
`sum(lots physiques) + lot virtuel = quantity_theorical`.
- Regle du forecast ouvert:
`sum(lot.qt non zero) = max(lot virtuel, 0)`.
- Les lignes `lot.qt` a zero sont ignorees par les checks: elles peuvent servir
de memoire d'une prevision consommee.
- Le check applicatif est centralise dans
`lot.lot.assert_lines_quantity_consistency()`.
- Le diagnostic SQL correspondant est
[sql/quantity_consistency_checks.sql](sql/quantity_consistency_checks.sql).
## INV-PT-003 - Les fees utilisent leurs lots effectifs
### Regle consultant
Un fee ouvert suit le lot virtuel tant qu'il n'y a pas de lot physique. Des
qu'un ou plusieurs lots physiques sont lies au fee, ils deviennent la base
effective de calcul.
### Notes developpeur
- Source historique: `BR-PT-021`.
- Ne pas supprimer le lien virtuel: il reste le fallback si les physiques sont
retires.
- Voir [fees.md](fees.md).
## INV-PT-004 - Les templates doivent rester simples
### Regle consultant
Les documents doivent afficher des informations metier stables, meme si le
chemin technique pour les retrouver est complexe.
### Notes developpeur
- Preferer des proprietes Python `report_*` aux expressions Genshi complexes.
- Ne pas supposer qu'une variable locale comme `shipment` existe partout dans
un `.fodt`.
- Voir [reports-templates.md](reports-templates.md).

View File

@@ -0,0 +1,24 @@
# Facturation trade
Statut: `migration partielle`
## BR-PT-INV-001 - Padding facture provisoire vente
Source: `BR-PT-019` et `../padding-invoice-accounting.md`
### Regle consultant
Le padding d'une facture provisoire vente augmente la quantite facturee pour
constituer une provision, sans modifier la quantite physique du lot.
### Notes developpeur
- Le padding global du wizard `lot.invoice` est reparti entre les lots
selectionnes.
- La ligne facture expose `Inc. padding`.
- Le lot conserve sa part dans `sale_invoice_padding`.
- La facture finale retire le padding de la quantite provisoire avant de
calculer le delta.
- Les ecritures d'extourne doivent relire la provisoire depuis
`lot.sale_invoice_line_prov`.

View File

@@ -0,0 +1,27 @@
# Lots Management
Statut: `migration partielle`
## BR-PT-LOTMGT-001 - Separations matching, side et shipping status
Source: `BR-PT-023` et notes `2026-05-09`
### Regle consultant
Le rapport Lots Management doit permettre de lire separement le matching
commercial, le sens achat/vente et l'avancement logistique.
### Notes developpeur
- Filtres: `Matching status`, `Side`, `Shipping status`, `Dimension`,
`Strategy`.
- Dates `As of` / `To`: `purchase.purchase_date` et `sale.sale_date`.
- `Unshipped`: aucun `shipment_in`.
- `Scheduled`: shipment `draft`.
- `Shipped`: shipment `started`.
- `Received`: shipment `received` ou `done`.
- `Shipment Type = Dropship` si `from_location.type = supplier` et
`to_location.type = customer`, sinon `Inbound`.
- `Mark as finished` masque seulement les reliquats ouverts / virtuels, pas
les lots physiques.

View File

@@ -0,0 +1,27 @@
# Matching achat / vente
Statut: `migration partielle`
## BR-PT-MAT-001 - Create Contracts multi-lots conserve le matching source
Source: `BR-PT-012` et doublon historique `BR-PT-013`
### Regle consultant
Le wizard `Create contracts` peut creer un seul contrat miroir depuis plusieurs
quantites ouvertes selectionnees. Chaque ligne creee doit rester reliee a son
lot source.
### Notes developpeur
- La quantite du wizard doit correspondre a la somme des quantites ouvertes
selectionnees.
- Creer une ligne par `lot.qt` source.
- Conserver `created_by_code = True` pour eviter les creations automatiques
parasites lors des validations.
## Notes de migration
Les regles sur `Apply matching` presentes dans les notes de session du
`2026-05-09` doivent encore etre promues ici.

View File

@@ -0,0 +1,21 @@
# Paiements et banques
Statut: `migration partielle`
## BR-PT-PAY-001 - Distinguer banque tiers et banque compagnie
Source: `BR-PT-018`
### Regle consultant
Un contrat distingue le compte bancaire du client ou fournisseur du compte
bancaire utilise par la compagnie courante pour encaisser ou payer.
### Notes developpeur
- Contrats: `sale.sale`, `purchase.purchase`.
- `bank_account`: compte de la party du contrat.
- `our_bank_account`: compte de la compagnie courante, selectionnable parmi
les comptes disponibles.
- La devise du contrat est prioritaire pour proposer un compte par defaut.

View File

@@ -0,0 +1,53 @@
# Pricing, basis, premium
Statut: `migration partielle`
## BR-PT-PRI-001 - Le premium fait partie du prix economique
Source: `BR-PT-008`
### Regle consultant
Le premium ou discount saisi sur une ligne fait partie du prix economique, que
la ligne soit en prix fixe ou en basis.
### Notes developpeur
- `unit_price` reste le prix de base hors premium.
- Montant economique: `unit_price + premium converti si necessaire`.
- En basis, le premium s'applique aussi aux blocs valorises.
## BR-PT-PRI-002 - Linked currency
Source: `BR-PT-009` et `BR-PT-010`
### Regle consultant
Quand le prix est saisi dans une devise ou unite liee, le premium est exprime
dans ce meme repere puis converti pour les calculs internes.
### Notes developpeur
- Champs obligatoires si active: `linked_price`, `linked_currency`,
`linked_unit`.
- En `basis + linked currency`, `linked_price` represente le basis brut hors
premium.
- `amount` ajoute le premium converti.
## BR-PT-PRI-003 - Pricing manuel
Source: `BR-PT-016` et doublon historique `BR-PT-015`
### Regle consultant
En pricing manuel, l'utilisateur saisit uniquement la quantite fixee du jour et
le prix de marche. Les cumuls et prix moyens sont calcules automatiquement.
### Notes developpeur
- Champs saisis: `quantity`, `settl_price`.
- Champs derives: `fixed_qt`, `fixed_qt_price`, `unfixed_qt`,
`unfixed_qt_price`, `eod_price`, `last`.
- Groupe metier: `line + component` ou `sale_line + component`.
- Le composant choisi doit appartenir a la ligne courante.

View File

@@ -0,0 +1,39 @@
# Reports et templates
Statut: `migration partielle`
Voir aussi:
- `../template-rules.md`
- `../template-properties.md`
- `../../../../notes/template_business_rules.md`
## BR-PT-RPT-001 - Templates trade via proprietes Python
Source: `template-rules.md`, `template-properties.md`, notes AGENTS
### Regle consultant
Un document trade doit afficher des informations metier fiables, sans dependre
d'expressions fragiles dans le fichier bureautique.
### Notes developpeur
- Preferer des proprietes Python simples, souvent prefixees `report_*`.
- Dans les placeholders XML, utiliser `&quot;` et `&apos;` plutot que des
antislashs.
- Pour les factures liees a vente/achat/shipment, privilegier le lot physique
comme pont.
- Verifier le cache `invoice_report_cache` avant de conclure qu'une action
report pointe vers le mauvais `.fodt`.
- Pour les templates shipment, preferer `records[0]...` ou des proprietes sur
`stock.shipment.in` plutot qu'une variable locale supposee.
## Decisions deja documentees a migrer ensuite
- `insurance.fodt`: compagnie courante, amount insured a 110%, surveyor.
- `packing_list.fodt`: date du jour, unites depuis `purchase.line`.
- `bill.fodt`: maturity date reelle et montant en lettres depuis le total.
- `invoice_ict.fodt` / `invoice_ict_final.fodt`: poids, shipments et lots.
- `sale_ict.fodt`: priorite lots et unite reelle.

View File

@@ -0,0 +1,15 @@
# Risque, credit, forex
Statut: `placeholder`
Cette page est reservee aux regles business sur le risque, le credit et les
ecarts de change.
Aucune regle canonique `purchase_trade` n'a ete promue ici dans cette premiere
passe. Les notes comptables et forex existantes doivent etre relues avant toute
migration:
- `notes/accounting/README.md`
- `notes/accounting/business_rules.md`
- `notes/accounting/reporting.md`

View File

@@ -0,0 +1,31 @@
# Journal de migration et sessions
Statut: `non canonique`
Cette page sert de routeur vers les notes historiques. Une note devient
canonique seulement quand elle est reprise dans une page thematique.
## Sources historiques
- `modules/purchase_trade/docs/business-rules.md`
- `modules/purchase_trade/docs/business-rules-architecture-proposal.md`
- `notes/business_rules.md`
- `notes/template_business_rules.md`
## Notes deja partiellement promues
- Session `2026-04-30`: PnL fees ouverts et `% rate`, promue dans
[fees.md](fees.md) et [valuation-pnl-mtm.md](valuation-pnl-mtm.md).
- Session `2026-05-01`: solde ouvert apres lots physiques et lots effectifs
des fees, promue dans [lots-and-quantities.md](lots-and-quantities.md) et
[fees.md](fees.md).
- Session `2026-05-06`: Remove physical lot, promue dans
[lots-and-quantities.md](lots-and-quantities.md).
- Session `2026-05-09`: Lots Management, promue partiellement dans
[lots-management.md](lots-management.md).
- Session `2026-05-13`: cadrage `quantity_theorical` / `quantity`, amount de
ligne, Weight basis, invariants de quantite, checks Python bloquants et
diagnostic SQL. Promue dans
[lots-and-quantities.md](lots-and-quantities.md),
[lots-and-quantities.en.md](lots-and-quantities.en.md),
[invariants.md](invariants.md) et [sql/README.md](sql/README.md).

View File

@@ -0,0 +1,49 @@
# Shipments et execution
Statut: `migration partielle`
## BR-PT-SHP-001 - Affectation controller par ecart a l'objectif
Source: `BR-PT-014`
### Regle consultant
Le controller propose automatiquement est celui dont la zone a le plus grand
retard par rapport a son objectif.
### Notes developpeur
- Configuration: onglet `Execution` de `party.party`.
- La zone du shipment vient de `shipment.to_location.country`.
- Une region parente couvre ses sous-regions.
- `% achieved` compte seulement les shipments deja affectes a un controller.
## BR-PT-SHP-002 - Couts SLA controller par pays et/ou lieu
Source: `BR-PT-014-bis`
### Regle consultant
Un cout controller peut etre defini pour un pays, un lieu, ou le couple pays +
lieu. Le couple est le cas le plus specifique.
### Notes developpeur
- Matching: `country + location`, puis `location`, puis `country`.
- Le pays vient de `shipment.to_location.country`.
## BR-PT-SHP-003 - Weight reports distants par lot
Source: `BR-PT-015`
### Regle consultant
L'export distant par lot part du weight report global choisi par l'utilisateur
sur le shipment.
### Notes developpeur
- Exporter seulement les lots physiques des `incoming_moves`.
- Exiger au minimum `controller` et `returned_id` sur le shipment.
- Conserver les cles distantes et la date d'envoi sur le `weight.report`.

View File

@@ -0,0 +1,47 @@
# SQL diagnostics for purchase_trade business rules
These scripts are read-only diagnostics for a PostgreSQL test database.
They exist to support the same business rules enforced by Python guards. The
expected workflow is:
1. write the consultant/developer rule in the thematic documentation;
2. enforce the invariant in the application code when feasible;
3. provide a read-only SQL diagnostic to audit existing data.
## quantity_consistency_checks.sql
Checks the two core lot quantity invariants documented in
`lots-and-quantities.md` and `lots-and-quantities.en.md`.
Zero `lot_qt` rows are ignored completely. They are treated as legitimate
memory of an open quantity consumed by a physical lot. This is required because
`lot_qt` represents usable open forecast and stops at zero, while the virtual
lot may become negative to compensate the difference between theoretical and
executed quantity. A non-zero `lot_qt` row without both `lot_p` and `lot_s` is
reported as anomalous.
Run it on a restored test database:
```sql
\i modules/purchase_trade/docs/business/sql/quantity_consistency_checks.sql
```
The script returns rows only when it finds a potential issue.
Main columns:
- `check_name`: invariant or diagnostic that failed.
- `contract_model`: `purchase.purchase` or `sale.sale`.
- `contract_id`: database id of the contract.
- `contract_number`: purchase or sale contract number.
- `line_id`: `purchase.line` or `sale.line` id depending on the check.
- `virtual_lot_id`: virtual lot involved in the inconsistency.
- `observed_value`: value found in the database.
- `expected_value`: value required by the business rule.
- `diff`: observed minus expected.
- `detail`: human-readable explanation.
Cross-category UoM rows are reported as manual-review diagnostics because the
Python code may pass explicit conversion factors that cannot be inferred safely
from SQL alone.

View File

@@ -0,0 +1,51 @@
# Valuation, PnL, MTM
Statut: `migration partielle`
## BR-PT-VAL-001 - La valuation couvre achat, vente et sale-first
Source: `BR-PT-004`, `BR-PT-006`, `BR-PT-011`
### Regle consultant
Le PnL doit exister pour les achats et pour les ventes, meme quand une vente
n'est pas encore matchee a un achat.
### Notes developpeur
- Une `sale.line` non matchee doit generer au minimum `sale priced`, `sale fee`
et `derivative` si applicable.
- Une sale basis sans detail de prix doit quand meme produire une ligne a zero
ou au prix economique fallback selon la regle applicable.
- Ne pas attacher arbitrairement une sale unique si plusieurs sales sont
matchees au meme ouvert.
## BR-PT-VAL-002 - References de valuation
Source: `BR-PT-005`
### Regle consultant
La reference de PnL doit decrire la nature de la ligne valorisee: achat ou
vente, ouverte ou physique.
### Notes developpeur
- References autorisees: `Purchase/Open`, `Purchase/Physic`, `Sale/Open`,
`Sale/Physic`.
- Un lot virtuel ne doit pas sortir avec une reference physique.
## BR-PT-VAL-003 - MTM hors fees
Source: `BR-PT-007`
### Regle consultant
Le mark-to-market s'applique aux prix et aux derives, pas aux frais.
### Notes developpeur
- MTM autorise pour `pur. priced`, `sale priced`, `derivative`.
- Fees hors MTM: `pur. fee`, `sale fee`, `shipment fee`, `line fee`.
- Pour les fees: `mtm_price`, `mtm`, `strategy` doivent rester vides.