Compare commits
563 Commits
dev
...
3dfbb25281
| Author | SHA1 | Date | |
|---|---|---|---|
| 3dfbb25281 | |||
| eb38977dca | |||
| ac3198ee70 | |||
| cf4bd94d03 | |||
| 07ee8b4f99 | |||
| 7fc134f7a6 | |||
| e4590e6177 | |||
| 5a644d430b | |||
|
|
808e75daa6 | ||
|
|
febe29c6b4 | ||
|
|
b312ea9bd3 | ||
| 3321830b5e | |||
| f2fe626d72 | |||
| ddd88cd840 | |||
| 47d0f025b3 | |||
| 8719a63fa5 | |||
| 27b865184c | |||
| c2a523430e | |||
| 8d386e6754 | |||
| 62eed772fc | |||
| b0bcaf293d | |||
| 9cf1bc3604 | |||
| 858b7258d9 | |||
| 3122255bc7 | |||
| 5c3464da5c | |||
| 82143ba87d | |||
| 1b292e4594 | |||
| 0f4edc16ce | |||
| 7ac1e118ae | |||
| 94fd181c11 | |||
| 4ea701d669 | |||
| b3b14039e0 | |||
| ae39ff2f7d | |||
| 2ffd99b03a | |||
| 7d5ac651ab | |||
| e8b3fe5bc4 | |||
| ea85927a17 | |||
| 99d5d2a849 | |||
| c0ac8fac68 | |||
| 2743a7bc14 | |||
| cd49703879 | |||
| cc4a48c38c | |||
| 6fedb92b1a | |||
| cad7f7235e | |||
| b08d2f4dab | |||
| 2a30b03cbe | |||
| eb244b1e3e | |||
| f2947c9f86 | |||
| f9ade37eab | |||
| 5635856711 | |||
| edc20fa546 | |||
| d4f9d02cb4 | |||
| 780f3dcbdb | |||
| d5c6d5c4b5 | |||
| 104d5c616c | |||
| f45cdb0807 | |||
| b7b54c8b7b | |||
| c9dceb9c06 | |||
| cf7aa7f5b5 | |||
| 2998d8d570 | |||
| 83b879d200 | |||
| 50b262dbd2 | |||
| fbf3842e6c | |||
| ef8d914820 | |||
| 423d9476fc | |||
| d08510f084 | |||
| ed818c2287 | |||
| 703868001b | |||
| 6af3de0ffb | |||
| 95dba725a8 | |||
| a880535bc4 | |||
| 8f676c2396 | |||
| dfbc006efb | |||
| 4f331d5c8e | |||
| 227a6c14a8 | |||
| 2b0381c2ae | |||
| 2a1512a436 | |||
| 1c20181d06 | |||
| 6c2061e779 | |||
| 1f24646227 | |||
| 2d737b2628 | |||
| 903b42f730 | |||
| f8d53a27fa | |||
| 4b5e49f264 | |||
| 3b3f9c0988 | |||
| 248e410d88 | |||
| a754e15e32 | |||
| ee183e23c5 | |||
| 8441bf98e7 | |||
| 02e5c43f3d | |||
| 6ba5361269 | |||
| f0714d5670 | |||
| b8bfcc4308 | |||
| c268c3b460 | |||
| 04b3c1d924 | |||
| d8012bd2da | |||
| dd2acc2d09 | |||
| a8102d513f | |||
| ee1cb3e7fb | |||
| d0dd8965cd | |||
| 0833baa2ab | |||
| 8eba0cd066 | |||
| 38af948374 | |||
| 36ee92cbb2 | |||
| fdf855f7db | |||
| 31f617ae1c | |||
| 07e48944b6 | |||
| 3a0edea8cd | |||
| 9b7ccf1feb | |||
| dd8e3e3056 | |||
| d8a0dd43cf | |||
| 7b427813be | |||
| d705d86e80 | |||
| 667b66487d | |||
| 9c92904b3e | |||
| 8a60a8e67f | |||
| e6de614507 | |||
| a7c061f52d | |||
| e4e2087463 | |||
| c15b13cb91 | |||
| e981f96cad | |||
| aac7bad852 | |||
| 1e4d9d6e17 | |||
| a0363fa9c7 | |||
| e19dcafcb7 | |||
| 6a5a267cfd | |||
| 5c9f437945 | |||
| ec90d19cc7 | |||
| 77b1ab333b | |||
| 1f3e32fbe8 | |||
| a435631289 | |||
| 1a5e2e4033 | |||
| 7107625664 | |||
| dbfd6a0d89 | |||
| 6ac19a1334 | |||
| 855dc3a4dd | |||
| 52459a267b | |||
| 9d13cfd587 | |||
| de8310891b | |||
| 925fdfd32b | |||
| 96ad6dd1c6 | |||
| 68bd105e59 | |||
| 90f64cf55f | |||
| f722c6fa1c | |||
| 217344d3d5 | |||
| 5cb4317742 | |||
| cf3ad4bb31 | |||
| fcc5c3866a | |||
| f59150d1a9 | |||
| 67cb5491e2 | |||
| 734c0c8a34 | |||
| fd04810f0c | |||
| c8e8e4f866 | |||
| 3a6f426033 | |||
| 5838d848c2 | |||
| 79c701b711 | |||
| c36c6a411f | |||
| 745325eb20 | |||
| dbd5e5b77f | |||
| c2f3d3995f | |||
| 02a15c93de | |||
| abbcdfd2ec | |||
| dc067fb812 | |||
| 8691ba430f | |||
| e2e0e8cece | |||
| d182b0eceb | |||
| 74d1465470 | |||
| 1abfbbbc3d | |||
| 72501ab625 | |||
| 65e9f4d7d6 | |||
| 0a72076346 | |||
| 3977e138e2 | |||
| 45acff11bc | |||
| 1b15ab4c69 | |||
| 7735306f9c | |||
| 76b34ba280 | |||
| eb82211da7 | |||
| c2495bb6ea | |||
| 9e5cd39629 | |||
| 05a90f64a5 | |||
| 4d3c5e26e2 | |||
| 6ac1904a0c | |||
| 8e82986bd5 | |||
| 6ffc159255 | |||
| 4ebfbe6ddd | |||
| 700adb87f9 | |||
| 126225ea6a | |||
| 1339f9b2f1 | |||
| b3b7803313 | |||
| 24b4e00080 | |||
| 846c2d6a93 | |||
| 801e53f953 | |||
| 06f9a62dbc | |||
| eceeca997e | |||
| f37d21c761 | |||
| 1b97899091 | |||
| afee4243cc | |||
| 743c76c131 | |||
| acf0d4b687 | |||
| d659f14e12 | |||
| b74389f9a6 | |||
| b3c33ed314 | |||
| 73006db98a | |||
| 3599570d5e | |||
| 2ae512e074 | |||
| 90932a290d | |||
| 77922997eb | |||
| 574c1a67f4 | |||
| 1de1d15064 | |||
| 25800aa8a4 | |||
| 5a377fda13 | |||
| 3ab58e63f8 | |||
| a114c14e89 | |||
| a98415fe62 | |||
| c272630a8a | |||
| 9cc0f0715b | |||
| 6860ba9e60 | |||
| e9bd6ff57f | |||
| 0efa60f3b5 | |||
| d3a118db07 | |||
| 48d0b6a3f6 | |||
| 4e1d6895f8 | |||
| 4a0461df44 | |||
| adcce718fd | |||
| 5763ca64a5 | |||
| 0094bd6974 | |||
| c096b39259 | |||
| 654a8418bf | |||
| dd4b8055a7 | |||
| 9556f870ec | |||
|
|
4c08b95b3b | ||
|
|
a6e90f6e8b | ||
| 7df570c860 | |||
| 31d0e00f89 | |||
| 9a1673c6cd | |||
| fb17c13a61 | |||
| 671251d23e | |||
| 4e7911b0ac | |||
| c233e8cb77 | |||
| de0ef9fdf4 | |||
| c95b273291 | |||
| 542246baac | |||
| 0415ced262 | |||
| b767f7648d | |||
| 405c2c2bf0 | |||
| f39675543a | |||
| 2fecf60295 | |||
| 3b9a6ca943 | |||
| fcd24af41b | |||
| b0d69207e4 | |||
| 91bf2a3de4 | |||
| e9a8355587 | |||
| d483e7cf69 | |||
| 2dd8360d62 | |||
| 779e1aa361 | |||
| 2223927d34 | |||
| a26d0516f0 | |||
| 5605b19164 | |||
| f8d7f1e9d9 | |||
| 735c5431af | |||
| 38c614af98 | |||
| fcd809a3e3 | |||
| 83f7e43a6e | |||
| a9b048d4cf | |||
| 7da99b3bf7 | |||
| 242406588c | |||
| 3c4261ccf6 | |||
| 2428978d90 | |||
| c9028f1590 | |||
| 1af7ae99a4 | |||
| 26f43c2438 | |||
| 5b5685ae12 | |||
| 28c4fb3ff8 | |||
| 181c4b3d34 | |||
| 50ac9f297c | |||
| c2d4e34516 | |||
| 5b5f1d77ff | |||
| 0fffb341fe | |||
| 8b4daeac84 | |||
| 4da56e4b8a | |||
| e7a1a83c90 | |||
| c6d7b0da2b | |||
| 12727e53cb | |||
| 436d0b9a0c | |||
| 7a39754a63 | |||
| 55f342f106 | |||
| e78977dfa5 | |||
| ddca542bfc | |||
| 1e5611f726 | |||
| aed68ba6f4 | |||
| 54309a1980 | |||
| 1d398ab4b5 | |||
| 2a5eec0e62 | |||
| 20f51de50f | |||
| a22d185fb0 | |||
| 390207a371 | |||
| 01c23c1ec2 | |||
| c7146dd8a7 | |||
| 7569d06254 | |||
| f0ed5b9bd3 | |||
| 1075f49bd1 | |||
| 1efaad8c38 | |||
| 1dc8803287 | |||
| 50636c1294 | |||
| 6dfb12c125 | |||
| 96653be311 | |||
| 488fb15498 | |||
| f56f394ec3 | |||
| 4b9621a5f0 | |||
| 900d733fcd | |||
| 1adc4248ee | |||
| 3c5c20276d | |||
| f16b334089 | |||
| f29664fd77 | |||
| f7e70e0438 | |||
| 2cf1b25dad | |||
| 27e081228f | |||
| ea3cb65d7d | |||
| 9ad0217a69 | |||
| 1f3616858a | |||
| 072960307a | |||
| 83612eeea3 | |||
| 8e16259ab2 | |||
| 3a1b9f2d90 | |||
| 22a083a853 | |||
| c3d768d5a8 | |||
| 133ca49eda | |||
| f8aded48af | |||
| c6a2d48e4c | |||
| 22861dbc91 | |||
| 8ff380e229 | |||
| 80b2fed34b | |||
| 74e6cd0bf2 | |||
| 70f4bc291c | |||
| a231f32646 | |||
| d1c0f4a738 | |||
| ce883fa950 | |||
| 149306ff4e | |||
| 23fbcfa988 | |||
| 3a31cc354a | |||
| 62941a5c3b | |||
| d2ae65af82 | |||
| b35fa829e2 | |||
| 9d436d6ea0 | |||
| 9a6bb83c19 | |||
| ba79f88b43 | |||
| 4d2f203469 | |||
| c907a87b09 | |||
| ff75f9ab33 | |||
| 3877ff0b6f | |||
| 74b6425e93 | |||
| b20c3d54a4 | |||
| 60f26ddc9d | |||
| 07ddb212e3 | |||
| 4395884754 | |||
| 160ec1544b | |||
| 18c0329a94 | |||
| cdfc117220 | |||
| a613f59a2d | |||
| 4925cc8bfd | |||
| dd2b6952bc | |||
| 30b9da1a78 | |||
| 1a041fdcbf | |||
| 5f159b06dd | |||
| 30e0aa6543 | |||
| 20a4e20e91 | |||
| 82a30f246d | |||
| e683ce7e62 | |||
| 3c788d4764 | |||
| b4d944b10c | |||
| 24826bec50 | |||
| 9677d0dae2 | |||
| 2b3c823743 | |||
| 355831c76d | |||
| e6bbf2f1c1 | |||
| 0d034816f7 | |||
| b0a0d31528 | |||
| a85ab97d46 | |||
| 14ba0361d2 | |||
| 26c955ef53 | |||
| a71ce107f0 | |||
| 343fc8527c | |||
| de78da846d | |||
| 50e25138fd | |||
| f2c7adc2b3 | |||
| c017aac9a7 | |||
| 09c78395f1 | |||
| 4ae64269f4 | |||
| 90593b70c2 | |||
| 4d5bbc5523 | |||
| a23aac4527 | |||
| e37970a43d | |||
| fe3a3ea71e | |||
| 8e890f3c43 | |||
| a684c7b9c1 | |||
| 28d3400651 | |||
| 000cf54fbc | |||
| 65fe45bae2 | |||
| d8ac25dcda | |||
| c27c915700 | |||
| d4da45344c | |||
| bfa874da43 | |||
| 199caa2632 | |||
| 0f218374a7 | |||
| e03dee7def | |||
| 83aa474073 | |||
| 75bd34b673 | |||
| 03d65c253c | |||
| adac3ca630 | |||
| 221e701507 | |||
| 5a7b5845b6 | |||
| 616cf6d73c | |||
| 589ff16401 | |||
| a7d360b584 | |||
| 56b173d225 | |||
| 27450d51f6 | |||
| ea31706ff8 | |||
| 35fe191939 | |||
| 062b42f83f | |||
| debe374f1b | |||
| b5b554bad0 | |||
| 1d6f3158c0 | |||
| ff2b897b7a | |||
| 30d4362c09 | |||
| aa9a18fb93 | |||
| 4faa7fd83c | |||
| ca20d418cc | |||
| 3314d31fcd | |||
| f6db19d1d9 | |||
| 257916bd6c | |||
| 2cdcbe7303 | |||
| 98893647f6 | |||
| 2e276e2b1f | |||
| 384c1285b1 | |||
| 308252215f | |||
| 7965b22954 | |||
| 8ae2c7ca9c | |||
| 16151381cc | |||
| 4db0395a11 | |||
| fbfa73110f | |||
| 9544418897 | |||
| e40a32e796 | |||
| 530f2f9d97 | |||
| 9879926f08 | |||
| 04e9d7dc03 | |||
| 7a123b1af6 | |||
| a2b053c565 | |||
| 6018429369 | |||
| 4d4e7ab625 | |||
| 68ce1f3a62 | |||
| d8a56637c9 | |||
| a1486175f2 | |||
| 228ee07a9c | |||
| 036a2801e5 | |||
| c2838dcfd3 | |||
| 6e26f178ce | |||
| 4458871423 | |||
| 99782b5b0e | |||
| d09d183cdc | |||
| ec45440400 | |||
| edb23b008c | |||
| fca279a038 | |||
| 512862113a | |||
| 1ed79a2363 | |||
| 05a75f000a | |||
| 77a5366622 | |||
| c203e1b300 | |||
| 22ad8547e4 | |||
| 2ea2165653 | |||
| 5e72d4f98d | |||
| b1680cc96c | |||
| cfb719a7da | |||
| 9e4a12605c | |||
| 4013f9d74c | |||
| 280cff5fdb | |||
| 897c6f6824 | |||
| 8906f00d36 | |||
| 29a719c117 | |||
| d71257720e | |||
| aee9c3277e | |||
| b68f475e22 | |||
| 90eab73430 | |||
| 472806ef06 | |||
| 0def187750 | |||
| 229b6037fb | |||
| 8a90216357 | |||
| 4bbd7a5e76 | |||
| 9c8d7f11ae | |||
| b39607d987 | |||
| 65482b4a8b | |||
| 8b9787d4c0 | |||
| a1ab7dec82 | |||
| 5ae8af84fb | |||
| c90b14fcc1 | |||
| e9ff9c76ab | |||
| 63d8266a9c | |||
| add4cdc137 | |||
| 4d94aa78ed | |||
| da01249f66 | |||
|
|
5cbb57c657 | ||
| 50a8c6328f | |||
| eaa5c8b544 | |||
| 78e9e06a8b | |||
| 9f06398b2c | |||
| 51a84f1f2e | |||
| 00330008d1 | |||
| 3480eb8a7a | |||
| 5179d98289 | |||
| 2109d7a3e4 | |||
| 1f350e6207 | |||
| 7722292482 | |||
| ec359f6b8a | |||
| 845b9cf749 | |||
| 48b941b109 | |||
| 18ece66cdb | |||
| acfc2fe88a | |||
| 888b880bd6 | |||
| 1f62ae91dd | |||
| 05e68636ad | |||
| bfb9bb3188 | |||
| b78e64f9f1 | |||
| 199b8aec12 | |||
| 1757075f2b | |||
| 172d38479d | |||
| 4902368b15 | |||
| 7b4f757cb5 | |||
| b37f132cdf | |||
| 15f791bd92 | |||
| 58cd66e543 | |||
| cc6ce82ec1 | |||
| 11526ef3ee | |||
| 6d52317804 | |||
| a99efcfc5b | |||
| 0d5cf7dffc | |||
| 613b679908 | |||
| 51ced23ab8 | |||
| 2958e1fb9e | |||
| 346a34951d | |||
| b644aea007 | |||
| 5dbaba5f32 | |||
| d133665fc7 | |||
| c2cb2a874c | |||
| 408970c339 | |||
| ea2627c9ae | |||
| ac988a714a | |||
| 97eae6e4a6 | |||
| 3976b387d7 | |||
| 9b8e8127a1 | |||
| f53a9bce27 | |||
| a7753b974f | |||
| c687828ba5 | |||
| 5054b64cd0 | |||
| 06922973b7 | |||
| 18ebf7f06c | |||
| 44c4560f24 | |||
| 7643bf21fb | |||
| 97677025d7 | |||
| 02fe5b3e5d | |||
| 6e529deca0 | |||
| efee365fc6 | |||
| 6bf245ac64 | |||
| 238869989a | |||
|
|
806e374ceb |
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
*.pyc
|
||||
~$*
|
||||
notes/accounting/excel_web_api/.env
|
||||
55
AGENTS.md
55
AGENTS.md
@@ -38,15 +38,55 @@ Guide rapide pour les agents qui codent dans ce repository.
|
||||
- Lire `wsgi.py`, `rpc.py`, `protocols/*`, `tests/test_rpc.py`, `tests/test_wsgi.py`.
|
||||
- Si bug metier:
|
||||
- Modifier uniquement `modules/<module>/` + ses tests.
|
||||
- Conventions de champs dates:
|
||||
- Dans ce projet, ne pas introduire de `fields.DateTime`.
|
||||
- Utiliser `fields.Date` pour les dates metier et les champs de suivi UI, sauf demande explicite deja existante dans le module cible.
|
||||
- Si bug template Relatorio (`.fodt`):
|
||||
- Lire d'abord le template standard voisin du meme domaine (`invoice.fodt`, `sale.fodt`, etc.).
|
||||
- Preferer des proprietes Python simples exposees par le modele plutot que des expressions Genshi complexes dans le template.
|
||||
- Dans les placeholders XML, utiliser `"` et `'` plutot que des antislashs type `\'`.
|
||||
- Si un document facture depend fortement d'une vente/achat, ajouter au besoin un petit pont Python pour exposer des `report_*` stables au template.
|
||||
- Pour les templates `stock.shipment.in`, preferer aussi des proprietes `report_*` sur le shipment plutot que des contextes ad hoc (`si_*`) quand le document devient metier ou client-specifique.
|
||||
- Si plusieurs actions de report pointent vers `report_name = 'account.invoice'`, verifier aussi le cache `invoice_report_cache` dans `modules/account_invoice/invoice.py`: un mauvais cache peut faire croire que plusieurs actions utilisent le meme `.fodt`.
|
||||
- Avant de conclure qu'un template ou une action est faux, verifier si le report alternatif doit bypasser le cache standard.
|
||||
- Pour les templates shipment, ne pas supposer qu'une variable locale comme `shipment` sera definie partout dans Genshi, surtout dans les headers/footers; preferer `records[0]....` ou des placeholders alignes sur le scope reel du report.
|
||||
- Dans `purchase_trade`, pour remonter d'une facture vers shipment, pro forma, freight ou autres donnees logistiques, privilegier le lot physique comme pont entre `purchase.line`, `sale.line` et shipment.
|
||||
- Pour `FREIGHT VALUE`, ne pas lire un champ direct sur la facture: retrouver le fee de shipment (`shipment_in`) dont le produit est `Maritime freight`, puis utiliser `fee.get_amount()`.
|
||||
- Rappels session templates (2026-04-08):
|
||||
- `insurance.fodt`: le texte "insured for account of" doit afficher la compagnie courante (shipment.company.party), pas le client.
|
||||
- `insurance.fodt`: exposer des proprietes Python `report_*` sur `stock.shipment.in` pour les montants (incoming moves) et les zones client-specifiques.
|
||||
- `insurance.fodt`: "Amount insured" suit la regle metier 110% du montant incoming (base calculee via lot -> purchase.line.unit_price * quantite courante convertie).
|
||||
- `insurance.fodt`: zone "Contact the following surveyor" alimentee par une propriete dediee, avec champ `surveyor` (party.party) cote shipment.
|
||||
- `packing_list.fodt`: date en haut a droite = date du jour; unites Net/Gross = unite de `purchase.line`.
|
||||
- `bill.fodt` (sale): la 2eme date doit etre une vraie maturity date (depuis `invoice.lines_to_pay.maturity_date`), pas `payment_term.rec_name`.
|
||||
- `bill.fodt` (sale): le montant en lettres doit provenir du montant du bill (facture/total), pas du `unit_price` de ligne.
|
||||
- Quand un template affiche les placeholders en brut, verifier que les champs sont bien des placeholders Relatorio dans le XML (pas du texte litteral).
|
||||
- Eviter les apostrophes echappees style `\'` dans placeholders; preferer `"` et `'`.
|
||||
|
||||
## 4.bis) Memos metier et templates
|
||||
|
||||
- Regles metier transverses:
|
||||
- `notes/business_rules.md`
|
||||
- Regles metier locales `purchase_trade`:
|
||||
- `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:
|
||||
- `notes/template_business_rules.md`
|
||||
- Documentation comptable et reporting:
|
||||
- `notes/accounting/README.md`
|
||||
- `notes/accounting/gap_analysis_workflow.md`
|
||||
- `notes/accounting/reporting.md`
|
||||
- `notes/accounting/business_rules.md`
|
||||
- `notes/accounting/purchase_trade_extensions.md`
|
||||
- Regles sensibles `purchase_trade` a relire avant de toucher lots, quantites
|
||||
ou fees:
|
||||
- `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
|
||||
(`quantity_theorical`, `lot.qt`, lots physiques, fees et PnL fee).
|
||||
|
||||
## 5) Workflow de modification (obligatoire)
|
||||
|
||||
@@ -105,3 +145,18 @@ Toujours fournir:
|
||||
3. Proposer le patch minimal.
|
||||
4. Implementer + tester cible.
|
||||
5. Rendre avec le contrat de sortie (section 8).
|
||||
- Rappels session 2026-04-09:
|
||||
- `invoice_ict.fodt` / `invoice_ict_final.fodt`: poids et unites depuis `lot.qt.hist` / `lot_unit_line`, priorite lots `physic`, sinon lot `virtual` unique.
|
||||
- `invoice_ict.fodt` / `invoice_ict_final.fodt`: infos shipment depuis les lots reels des lignes facture; ne rien afficher si plusieurs shipments differents.
|
||||
- `invoice_ict.fodt` / `invoice_ict_final.fodt`: `S/I` = `shipment.reference`; `NB BALES: 0` => `Unchanged` sur le final.
|
||||
- `invoice_ict.fodt` / `invoice_ict_final.fodt`: quantites uniformisees a `2` decimales; conversion `LBS` via UoM, jamais via un facteur fixe aveugle.
|
||||
- `invoice_ict.fodt` / `invoice_ict_final.fodt`: si plusieurs lignes reutilisent le meme lot, les lignes detaillees suivent la quantite facturee convertie, mais le `GROSS` global doit rester le vrai delta historique du lot.
|
||||
- `sale_ict.fodt`: meme priorite lots; les mots suivent l'unite reelle; le total convertit vers une unite commune, qui est celle du lot virtuel seulement s'il y a un seul lot virtuel sur tout le report.
|
||||
- `lot.report.r_del_period`: utiliser `sale.line.del_period` pour `lot_s` sans `lot_p`, sinon `purchase.line.del_period`.
|
||||
- `lot.do_weighing`: `lot_qt` editable et ecrasement direct de `lot.lot_qt`.
|
||||
- `account.invoice`: `Validate` cree aussi le `account.move` pour les factures client, attribue aussi le `number` a ce stade pour les factures client comme fournisseur; `Post` ne doit plus forcer une fresh session sur ce flux.
|
||||
- `pricing.pricing`: saisie manuelle autorisee meme sans composant; en manuel, l'utilisateur saisit seulement `Qt` et `Settl. price`; `fixed_qt`, `fixed_qt_price`, `unfixed_qt`, `unfixed_qt_price` et `eod_price` sont derives automatiquement.
|
||||
- `pricing.pricing`: en manuel, `fixed_qt` = cumul des `quantity`, `fixed_qt_price` = moyenne ponderee cumulee des `settl_price`, `unfixed_qt` = reste a fixer, `unfixed_qt_price` = `settl_price` de la ligne.
|
||||
- `pricing.pricing`: `eod_price` reste non editable et calcule en prix moyen pondere; `last=True` gere par groupe `line + component`, choisi sur la `pricing_date` la plus grande.
|
||||
- `purchase_trade`: `trader` filtre sur `TRADER`, `operator` sur `OPERATOR`; fallback sur `quantity` si `quantity_theorical` est vide dans les quotas/pricings.
|
||||
- `sale.line` / `purchase.line`: en mode `basis`, sans `price_component`, le `Price` et le `Fix. progress` de la ligne doivent remonter depuis la ligne `Summary` sans component.
|
||||
|
||||
1
debug.log
Normal file
1
debug.log
Normal file
@@ -0,0 +1 @@
|
||||
[0407/143111.471:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: Accès refusé. (0x5)
|
||||
5
deployment/README.md
Normal file
5
deployment/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Deployment Notes
|
||||
|
||||
- Runbook onboarding SSH: `deployment/runbooks/vps-onboarding.md`
|
||||
- VPS 46.202.173.47 credentials: `deployment/vps/46.202.173.47-credentials.md`
|
||||
- VPS 46.202.173.47 quickstart: `deployment/vps/46.202.173.47-quickstart.md`
|
||||
57
deployment/runbooks/vps-onboarding.md
Normal file
57
deployment/runbooks/vps-onboarding.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# Procedure - Ajouter un nouveau VPS (SSH)
|
||||
|
||||
Date de reference: 2026-04-07
|
||||
|
||||
## 1) Preparation locale (Windows)
|
||||
|
||||
1. Creer le dossier SSH local si absent:
|
||||
`New-Item -ItemType Directory -Force $env:USERPROFILE\.ssh`
|
||||
|
||||
2. Generer une cle dediee VPS:
|
||||
`ssh-keygen -t ed25519 -C "vps-deploy" -f $env:USERPROFILE\.ssh\vps_deploy_key`
|
||||
|
||||
3. Lire la cle publique:
|
||||
`Get-Content $env:USERPROFILE\.ssh\vps_deploy_key.pub`
|
||||
|
||||
## 2) Installer la cle sur le VPS
|
||||
|
||||
1. Se connecter au VPS avec mot de passe (premiere fois):
|
||||
`ssh <user>@<ip_vps>`
|
||||
|
||||
2. Sur le VPS, preparer le dossier SSH:
|
||||
`mkdir -p ~/.ssh`
|
||||
`chmod 700 ~/.ssh`
|
||||
|
||||
3. Ajouter la cle publique (une ligne complete):
|
||||
`echo "ssh-ed25519 ... vps-deploy" >> ~/.ssh/authorized_keys`
|
||||
`chmod 600 ~/.ssh/authorized_keys`
|
||||
|
||||
## 3) Tester la connexion par cle
|
||||
|
||||
Depuis Windows:
|
||||
`ssh -i $env:USERPROFILE\.ssh\vps_deploy_key <user>@<ip_vps>`
|
||||
|
||||
## 4) Test operationnel minimal
|
||||
|
||||
Creer un dossier distant:
|
||||
`ssh -i $env:USERPROFILE\.ssh\vps_deploy_key <user>@<ip_vps> "mkdir -p ~/test_codex_deploy && ls -ld ~/test_codex_deploy"`
|
||||
|
||||
## 5) Durcissement recommande
|
||||
|
||||
- Desactiver l'authentification par mot de passe apres validation de la cle.
|
||||
- Utiliser une cle dediee par environnement (dev/staging/prod).
|
||||
- Documenter user + IP + chemin de cle dans une fiche VPS separee.
|
||||
|
||||
## 6) Note importante - Contrainte sandbox Codex
|
||||
|
||||
- Si une commande SSH/SCP echoue avec un message proche de:
|
||||
- `Identity file ... not accessible: Permission denied`
|
||||
- Cause probable:
|
||||
- la session est en sandbox et ne peut pas lire la cle locale dans
|
||||
`C:\Users\<user>\.ssh\...`.
|
||||
- Action:
|
||||
- relancer la commande en mode `require_escalated` (hors sandbox) pour
|
||||
autoriser l'acces a la cle locale.
|
||||
- Exemple observe le 2026-04-07:
|
||||
- creation du dossier `/root/test` sur `46.202.173.47` reussie uniquement
|
||||
apres escalation.
|
||||
36
deployment/vps/46.202.173.47-credentials.md
Normal file
36
deployment/vps/46.202.173.47-credentials.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Fiche VPS - 46.202.173.47
|
||||
|
||||
Date de reference: 2026-04-07
|
||||
|
||||
## Identite serveur
|
||||
|
||||
- IP: `46.202.173.47`
|
||||
- Hostname alias conseille: `vps3`
|
||||
|
||||
## Acces
|
||||
|
||||
- Cle publique Laurent Barontini (vps-deploy):
|
||||
`ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEm8JMCYsk6I1IoYhIHXNrdyERHdh+eeDCJagOHaRAEK vps-deploy`
|
||||
|
||||
- Cle publique Sylvain Duvernay (s.duvernay@singa-associates.com):
|
||||
`ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG6Xsp/v6q6JO04ETv1880qoSPptUMxlWQvgcBz67o63 s.duvernay@singa-associates.com`
|
||||
- Fichier local: `$env:USERPROFILE\.ssh\id_ed25519`
|
||||
|
||||
- Mot de passe fourni:
|
||||
`!!OpenSquared!!`
|
||||
|
||||
- Utilisateur SSH:
|
||||
'root'
|
||||
- Port SSH:
|
||||
'22'
|
||||
|
||||
## Commande de connexion type
|
||||
|
||||
- Laurent Barontini (cle vps-deploy):
|
||||
`ssh -i $env:USERPROFILE\.ssh\vps_deploy_key <user>@46.202.173.47`
|
||||
|
||||
- Sylvain Duvernay (cle id_ed25519):
|
||||
`ssh -i $env:USERPROFILE\.ssh\id_ed25519 <user>@46.202.173.47`
|
||||
|
||||
- Avec port custom:
|
||||
`ssh -i $env:USERPROFILE\.ssh\id_ed25519 -p <port> <user>@46.202.173.47`
|
||||
39
deployment/vps/46.202.173.47-quickstart.md
Normal file
39
deployment/vps/46.202.173.47-quickstart.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Commandes Rapides - VPS 46.202.173.47
|
||||
|
||||
Date de reference: 2026-04-07
|
||||
|
||||
## Cles SSH par utilisateur
|
||||
|
||||
| Utilisateur | Fichier cle locale |
|
||||
|---|---|
|
||||
| Laurent Barontini | `$env:USERPROFILE\.ssh\vps_deploy_key` |
|
||||
| Sylvain Duvernay | `$env:USERPROFILE\.ssh\id_ed25519` |
|
||||
|
||||
> Les commandes ci-dessous utilisent `id_ed25519` (Sylvain Duvernay).
|
||||
|
||||
## 1) Test SSH
|
||||
|
||||
`ssh -i $env:USERPROFILE\.ssh\id_ed25519 <user>@46.202.173.47 "echo ok"`
|
||||
|
||||
## 2) Creer un dossier test distant
|
||||
|
||||
`ssh -i $env:USERPROFILE\.ssh\id_ed25519 <user>@46.202.173.47 "mkdir -p ~/test_codex_deploy && ls -ld ~/test_codex_deploy"`
|
||||
|
||||
## 3) Lister home distant
|
||||
|
||||
`ssh -i $env:USERPROFILE\.ssh\id_ed25519 <user>@46.202.173.47 "ls -la ~"`
|
||||
|
||||
## 4) Copier un fichier local vers le VPS
|
||||
|
||||
`scp -i $env:USERPROFILE\.ssh\id_ed25519 .\local.txt <user>@46.202.173.47:~/local.txt`
|
||||
|
||||
## 5) Recuperer un fichier du VPS
|
||||
|
||||
`scp -i $env:USERPROFILE\.ssh\id_ed25519 <user>@46.202.173.47:~/remote.txt .\remote.txt`
|
||||
|
||||
## 6) Depannage sandbox (Codex)
|
||||
|
||||
- Symptome:
|
||||
- `Identity file ... not accessible: Permission denied`
|
||||
- Correctif:
|
||||
- relancer la commande SSH/SCP en mode escalade (`require_escalated`).
|
||||
@@ -73,6 +73,7 @@ attlist.field &=
|
||||
| "text"
|
||||
| "time"
|
||||
| "timedelta"
|
||||
| "tolerance_gauge"
|
||||
| "url"
|
||||
}?
|
||||
attlist.field &= attribute fill { "0" | "1" }?
|
||||
@@ -122,6 +123,12 @@ attlist.field &= attribute symbol { text }?
|
||||
attlist.field &= [a:defaultValue = "1"] attribute grouping { "0" | "1" }?
|
||||
attlist.field &= [a:defaultValue = "square"] attribute border { "square" | "circle" | "rounded" }?
|
||||
attlist.field &= attribute loading { "lazy" | "eager" }?
|
||||
attlist.field &= attribute min { text }?
|
||||
attlist.field &= attribute max { text }?
|
||||
attlist.field &= attribute min_field { text }?
|
||||
attlist.field &= attribute max_field { text }?
|
||||
attlist.field &= attribute center { text }?
|
||||
attlist.field &= attribute digits { text }?
|
||||
image = element image { attlist.image, empty }
|
||||
attlist.image &= attribute name { text }
|
||||
attlist.image &= [a:defaultValue = "icon"] attribute type { "icon" | "url" }?
|
||||
|
||||
@@ -251,6 +251,7 @@
|
||||
<value>text</value>
|
||||
<value>time</value>
|
||||
<value>timedelta</value>
|
||||
<value>tolerance_gauge</value>
|
||||
<value>url</value>
|
||||
</choice>
|
||||
</attribute>
|
||||
@@ -577,6 +578,54 @@
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="attlist.field" combine="interleave">
|
||||
<optional>
|
||||
<attribute>
|
||||
<name ns="">min</name>
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="attlist.field" combine="interleave">
|
||||
<optional>
|
||||
<attribute>
|
||||
<name ns="">max</name>
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="attlist.field" combine="interleave">
|
||||
<optional>
|
||||
<attribute>
|
||||
<name ns="">min_field</name>
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="attlist.field" combine="interleave">
|
||||
<optional>
|
||||
<attribute>
|
||||
<name ns="">max_field</name>
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="attlist.field" combine="interleave">
|
||||
<optional>
|
||||
<attribute>
|
||||
<name ns="">center</name>
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="attlist.field" combine="interleave">
|
||||
<optional>
|
||||
<attribute>
|
||||
<name ns="">digits</name>
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="attlist.field" combine="interleave">
|
||||
<optional>
|
||||
<attribute>
|
||||
|
||||
@@ -42,6 +42,7 @@ attlist.field &=
|
||||
| "text"
|
||||
| "time"
|
||||
| "timedelta"
|
||||
| "tolerance_gauge"
|
||||
| "url"
|
||||
}?
|
||||
attlist.field &=
|
||||
@@ -75,6 +76,12 @@ attlist.field &= attribute help_field { text }?
|
||||
attlist.field &= attribute view_ids { text }?
|
||||
attlist.field &= attribute symbol { text }?
|
||||
attlist.field &= [a:defaultValue = "1"] attribute grouping { "0" | "1" }?
|
||||
attlist.field &= attribute min { text }?
|
||||
attlist.field &= attribute max { text }?
|
||||
attlist.field &= attribute min_field { text }?
|
||||
attlist.field &= attribute max_field { text }?
|
||||
attlist.field &= attribute center { text }?
|
||||
attlist.field &= attribute digits { text }?
|
||||
prefix = element prefix { attlist.affix, empty }
|
||||
suffix = element suffix { attlist.affix, empty }
|
||||
attlist.affix &= attribute string { text }?
|
||||
|
||||
@@ -139,6 +139,7 @@
|
||||
<value>text</value>
|
||||
<value>time</value>
|
||||
<value>timedelta</value>
|
||||
<value>tolerance_gauge</value>
|
||||
<value>url</value>
|
||||
</choice>
|
||||
</attribute>
|
||||
@@ -330,6 +331,54 @@
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="attlist.field" combine="interleave">
|
||||
<optional>
|
||||
<attribute>
|
||||
<name ns="">min</name>
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="attlist.field" combine="interleave">
|
||||
<optional>
|
||||
<attribute>
|
||||
<name ns="">max</name>
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="attlist.field" combine="interleave">
|
||||
<optional>
|
||||
<attribute>
|
||||
<name ns="">min_field</name>
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="attlist.field" combine="interleave">
|
||||
<optional>
|
||||
<attribute>
|
||||
<name ns="">max_field</name>
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="attlist.field" combine="interleave">
|
||||
<optional>
|
||||
<attribute>
|
||||
<name ns="">center</name>
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="attlist.field" combine="interleave">
|
||||
<optional>
|
||||
<attribute>
|
||||
<name ns="">digits</name>
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="prefix">
|
||||
<element>
|
||||
<name ns="">prefix</name>
|
||||
|
||||
10
modules/AGENTS.md
Normal file
10
modules/AGENTS.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# AGENTS.md - Regles globales des modules
|
||||
|
||||
Ce guide complete le `AGENTS.md` racine et s'applique a tous les modules sous `modules/`.
|
||||
|
||||
## Regles transversales
|
||||
|
||||
- Ne pas introduire de `fields.DateTime` dans les modules metier sauf demande explicite et code cible deja base sur ce type.
|
||||
- Pour afficher une date issue d'un champ technique comme `create_date`, preferer un champ fonctionnel en `fields.Date` plutot qu'un acces direct en vue.
|
||||
- Si une vue doit afficher une information technique non declaree explicitement sur le modele, ajouter un champ fonctionnel dedie cote Python avant de modifier le XML.
|
||||
|
||||
@@ -30,6 +30,8 @@ def register():
|
||||
account.GeneralLedgerAccountParty,
|
||||
account.GeneralLedgerLine,
|
||||
account.GeneralLedgerLineContext,
|
||||
account.GeneralLedgerDetail,
|
||||
account.GeneralLedgerDetailContext,
|
||||
account.BalanceSheetContext,
|
||||
account.BalanceSheetComparisionContext,
|
||||
account.IncomeStatementContext,
|
||||
@@ -118,6 +120,7 @@ def register():
|
||||
Pool.register(
|
||||
account.AccountTypeStatement,
|
||||
account.GeneralLedger,
|
||||
account.GeneralLedgerDetailReport,
|
||||
account.TrialBalance,
|
||||
account.AgedBalanceReport,
|
||||
move.GeneralJournal,
|
||||
|
||||
@@ -7,9 +7,10 @@ from decimal import Decimal
|
||||
from itertools import zip_longest
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from sql import Column, Literal, Null, Window
|
||||
from sql import Column, Literal, Null, Union, Window
|
||||
from sql.aggregate import Count, Max, Min, Sum
|
||||
from sql.conditionals import Case, Coalesce
|
||||
from sql.operators import Concat
|
||||
|
||||
from trytond import backend
|
||||
from trytond.i18n import gettext
|
||||
@@ -2583,6 +2584,544 @@ class GeneralLedgerLineContext(GeneralLedgerAccountContext):
|
||||
return super().get_context(fields_names=fields_names)
|
||||
|
||||
|
||||
class GeneralLedgerDetailContext(GeneralLedgerAccountContext):
|
||||
'General Ledger Detail Context'
|
||||
__name__ = 'account.general_ledger.detail.context'
|
||||
|
||||
account = fields.Many2One(
|
||||
'account.account', "Account",
|
||||
domain=[
|
||||
('company', '=', Eval('company')),
|
||||
('type', '!=', None),
|
||||
('closed', '!=', True),
|
||||
],
|
||||
depends=['company'])
|
||||
account_from = fields.Char("Account From")
|
||||
account_to = fields.Char("Account To")
|
||||
currency = fields.Many2One('currency.currency', "Transaction Currency")
|
||||
party = fields.Many2One(
|
||||
'party.party', "Party",
|
||||
context={
|
||||
'company': Eval('company', -1),
|
||||
},
|
||||
depends=['company'])
|
||||
document_number = fields.Char("Document Number")
|
||||
|
||||
@classmethod
|
||||
def default_account(cls):
|
||||
return Transaction().context.get('account')
|
||||
|
||||
@classmethod
|
||||
def default_account_from(cls):
|
||||
return Transaction().context.get('account_from')
|
||||
|
||||
@classmethod
|
||||
def default_account_to(cls):
|
||||
return Transaction().context.get('account_to')
|
||||
|
||||
@classmethod
|
||||
def default_currency(cls):
|
||||
return Transaction().context.get('currency')
|
||||
|
||||
@classmethod
|
||||
def default_party(cls):
|
||||
return Transaction().context.get('party')
|
||||
|
||||
@classmethod
|
||||
def default_document_number(cls):
|
||||
return Transaction().context.get('document_number')
|
||||
|
||||
@classmethod
|
||||
def get_context(cls, fields_names=None):
|
||||
fields_names = fields_names.copy() if fields_names is not None else []
|
||||
fields_names += [
|
||||
'account', 'account_from', 'account_to', 'currency', 'party',
|
||||
'document_number']
|
||||
return super().get_context(fields_names=fields_names)
|
||||
|
||||
|
||||
class GeneralLedgerDetail(DescriptionOriginMixin, ModelSQL, ModelView):
|
||||
'General Ledger Detail'
|
||||
__name__ = 'account.general_ledger.detail'
|
||||
|
||||
row_type = fields.Selection([
|
||||
('opening', "Opening"),
|
||||
('movement', "Movement"),
|
||||
('closing', "Closing"),
|
||||
], "Row Type", sort=False)
|
||||
row_sequence = fields.Integer("Row Sequence")
|
||||
|
||||
company = fields.Many2One('company.company', "Company")
|
||||
account = fields.Many2One('account.account', "Account")
|
||||
account_code = fields.Char("Account Code")
|
||||
account_name = fields.Char("Account Name")
|
||||
transaction_currency = fields.Many2One(
|
||||
'currency.currency', "Transaction Currency")
|
||||
base_currency = fields.Many2One('currency.currency', "Base Currency")
|
||||
|
||||
posting_date = fields.Date("Posting Date")
|
||||
journal = fields.Many2One('account.journal', "Journal")
|
||||
journal_entry_number = fields.Char("Journal Entry Number")
|
||||
document_number = fields.Function(
|
||||
fields.Char("Document Number"), 'get_document_field')
|
||||
voucher_number = fields.Char("Voucher Number")
|
||||
document_type = fields.Function(
|
||||
fields.Char("Document Type"), 'get_document_field')
|
||||
document_date = fields.Date("Document Date")
|
||||
posting_status = fields.Selection([
|
||||
('draft', "Draft"),
|
||||
('posted', "Posted"),
|
||||
], "Posting Status", sort=False)
|
||||
|
||||
party = fields.Many2One('party.party', "Party")
|
||||
move = fields.Many2One('account.move', "Move")
|
||||
move_line = fields.Many2One('account.move.line', "Move Line")
|
||||
origin = fields.Reference('Origin', selection='get_origin')
|
||||
description = fields.Char("Line Desc.")
|
||||
move_description = fields.Char("Description")
|
||||
reference = fields.Char("Reference")
|
||||
|
||||
debit_base_currency = fields.Numeric(
|
||||
"Debit Base Currency", digits=(16, 2))
|
||||
credit_base_currency = fields.Numeric(
|
||||
"Credit Base Currency", digits=(16, 2))
|
||||
balance_base_currency = fields.Numeric(
|
||||
"Balance Base Currency", digits=(16, 2))
|
||||
running_balance_base_currency = fields.Numeric(
|
||||
"Running Balance Base Currency", digits=(16, 2))
|
||||
|
||||
debit_transaction_currency = fields.Numeric(
|
||||
"Debit Transaction Currency", digits=(16, 2))
|
||||
credit_transaction_currency = fields.Numeric(
|
||||
"Credit Transaction Currency", digits=(16, 2))
|
||||
balance_transaction_currency = fields.Numeric(
|
||||
"Balance Transaction Currency", digits=(16, 2))
|
||||
running_balance_transaction_currency = fields.Numeric(
|
||||
"Running Balance Transaction Currency", digits=(16, 2))
|
||||
|
||||
payable_qty = fields.Numeric("Quantity", digits=(16, 5))
|
||||
uom = fields.Char("UoM")
|
||||
|
||||
entered_by = fields.Many2One('res.user', "Entered By")
|
||||
entered_date = fields.Date("Entered Date")
|
||||
modified_by = fields.Many2One('res.user', "Modified By")
|
||||
modified_date = fields.Date("Modified Date")
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls._order = [
|
||||
('account_code', 'ASC'),
|
||||
('transaction_currency', 'ASC'),
|
||||
('row_sequence', 'ASC'),
|
||||
('posting_date', 'ASC'),
|
||||
('journal_entry_number', 'ASC'),
|
||||
('id', 'ASC'),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def _get_period_ids(cls, name, context):
|
||||
pool = Pool()
|
||||
Period = pool.get('account.period')
|
||||
|
||||
period = None
|
||||
if name == 'opening':
|
||||
period_ids = []
|
||||
if context.get('start_period'):
|
||||
period = Period(context['start_period'])
|
||||
elif name == 'closing':
|
||||
period_ids = []
|
||||
if context.get('end_period'):
|
||||
period = Period(context['end_period'])
|
||||
else:
|
||||
periods = Period.search([
|
||||
('fiscalyear', '=', context.get('fiscalyear')),
|
||||
('type', '=', 'standard'),
|
||||
],
|
||||
order=[('start_date', 'DESC')], limit=1)
|
||||
if periods:
|
||||
period, = periods
|
||||
else:
|
||||
start_period_ids = set(cls._get_period_ids('opening', context))
|
||||
end_period_ids = set(cls._get_period_ids('closing', context))
|
||||
return list(end_period_ids.difference(start_period_ids))
|
||||
|
||||
if period:
|
||||
if name == 'opening':
|
||||
date_clause = ('end_date', '<=', period.start_date)
|
||||
else:
|
||||
date_clause = [
|
||||
('end_date', '<=', period.end_date),
|
||||
('start_date', '<', period.end_date),
|
||||
]
|
||||
periods = Period.search([
|
||||
('fiscalyear', '=', context.get('fiscalyear')),
|
||||
date_clause,
|
||||
])
|
||||
if period.start_date == period.end_date:
|
||||
periods.append(period)
|
||||
if periods:
|
||||
period_ids = [p.id for p in periods]
|
||||
if name == 'closing':
|
||||
period_ids.append(period.id)
|
||||
return period_ids
|
||||
|
||||
@classmethod
|
||||
def _get_dates(cls, name, context):
|
||||
if name == 'opening':
|
||||
to_date = context.get('from_date') or datetime.date.min
|
||||
if to_date:
|
||||
try:
|
||||
to_date -= datetime.timedelta(days=1)
|
||||
except OverflowError:
|
||||
pass
|
||||
return None, to_date
|
||||
elif name == 'closing':
|
||||
return None, context.get('to_date')
|
||||
return context.get('from_date'), context.get('to_date')
|
||||
|
||||
@classmethod
|
||||
def _query_context(cls, name, context):
|
||||
if context.get('start_period') or context.get('end_period'):
|
||||
return {
|
||||
'periods': cls._get_period_ids(name, context),
|
||||
'from_date': None,
|
||||
'to_date': None,
|
||||
}
|
||||
elif context.get('from_date') or context.get('to_date'):
|
||||
from_date, to_date = cls._get_dates(name, context)
|
||||
return {
|
||||
'periods': None,
|
||||
'from_date': from_date,
|
||||
'to_date': to_date,
|
||||
}
|
||||
else:
|
||||
if name == 'opening':
|
||||
return {
|
||||
'periods': [],
|
||||
'from_date': None,
|
||||
'to_date': None,
|
||||
}
|
||||
return {
|
||||
'periods': None,
|
||||
'from_date': None,
|
||||
'to_date': None,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def _extra_where(cls, line, move, account, tx_currency, context):
|
||||
where = (
|
||||
(move.company == context.get('company'))
|
||||
& (account.type != Null)
|
||||
& (account.closed != Literal(True)))
|
||||
if context.get('account'):
|
||||
where &= line.account == context['account']
|
||||
if context.get('account_from'):
|
||||
where &= account.code >= context['account_from']
|
||||
if context.get('account_to'):
|
||||
where &= account.code <= context['account_to']
|
||||
if context.get('currency'):
|
||||
where &= tx_currency == context['currency']
|
||||
if context.get('party'):
|
||||
where &= line.party == context['party']
|
||||
if context.get('document_number'):
|
||||
value = '%%%s%%' % context['document_number']
|
||||
where &= (
|
||||
move.number.ilike(value)
|
||||
| move.post_number.ilike(value)
|
||||
| move.ext_ref.ilike(value))
|
||||
return where
|
||||
|
||||
@classmethod
|
||||
def _tables(cls):
|
||||
pool = Pool()
|
||||
Line = pool.get('account.move.line')
|
||||
Move = pool.get('account.move')
|
||||
Account = pool.get('account.account')
|
||||
Company = pool.get('company.company')
|
||||
line = Line.__table__()
|
||||
move = Move.__table__()
|
||||
account = Account.__table__()
|
||||
company = Company.__table__()
|
||||
return Line, line, move, account, company
|
||||
|
||||
@classmethod
|
||||
def _lot_tables(cls, Line):
|
||||
if 'lot' not in Line._fields:
|
||||
return None, None
|
||||
pool = Pool()
|
||||
try:
|
||||
Lot = pool.get('lot.lot')
|
||||
Uom = pool.get('product.uom')
|
||||
except KeyError:
|
||||
return None, None
|
||||
return Lot.__table__(), Uom.__table__()
|
||||
|
||||
@classmethod
|
||||
def _account_label(cls, account):
|
||||
return Concat(
|
||||
Concat(Coalesce(account.code, ''), ' - '),
|
||||
Coalesce(account.name, ''))
|
||||
|
||||
@classmethod
|
||||
def _opening_balance_query(cls, context):
|
||||
Line, line, move, account, company = cls._tables()
|
||||
tx_currency = Coalesce(line.second_currency, company.currency)
|
||||
base_amount = Coalesce(line.debit, 0) - Coalesce(line.credit, 0)
|
||||
tx_amount = Case(
|
||||
(line.second_currency != Null,
|
||||
Coalesce(line.amount_second_currency, 0)),
|
||||
else_=base_amount)
|
||||
|
||||
with Transaction().set_context(
|
||||
context, **cls._query_context('opening', context)):
|
||||
line_query, _ = Line.query_get(line)
|
||||
where = line_query & cls._extra_where(
|
||||
line, move, account, tx_currency, context)
|
||||
|
||||
return (line.join(move, condition=line.move == move.id)
|
||||
.join(account, condition=line.account == account.id)
|
||||
.join(company, condition=move.company == company.id)
|
||||
.select(
|
||||
line.account.as_('account'),
|
||||
tx_currency.as_('transaction_currency'),
|
||||
Sum(base_amount).as_('base_amount'),
|
||||
Sum(tx_amount).as_('transaction_amount'),
|
||||
where=where,
|
||||
group_by=[line.account, tx_currency],
|
||||
having=Sum(base_amount) != 0))
|
||||
|
||||
@classmethod
|
||||
def _line_query(cls, name, context):
|
||||
Line, line, move, account, company = cls._tables()
|
||||
lot, uom = cls._lot_tables(Line)
|
||||
opening_balance = cls._opening_balance_query(context)
|
||||
account_label = cls._account_label(account)
|
||||
quantity = Literal(None)
|
||||
unit = Literal(None)
|
||||
tx_currency = Coalesce(line.second_currency, company.currency)
|
||||
from_ = (line.join(move, condition=line.move == move.id)
|
||||
.join(account, condition=line.account == account.id)
|
||||
.join(company, condition=move.company == company.id))
|
||||
from_ = from_.join(opening_balance, 'LEFT',
|
||||
condition=(
|
||||
(opening_balance.account == line.account)
|
||||
& (opening_balance.transaction_currency == tx_currency)))
|
||||
if lot is not None:
|
||||
from_ = (from_
|
||||
.join(lot, 'LEFT', condition=Column(line, 'lot') == lot.id)
|
||||
.join(uom, 'LEFT', condition=lot.lot_unit_line == uom.id))
|
||||
quantity = lot.lot_qt
|
||||
unit = uom.symbol
|
||||
base_amount = Coalesce(line.debit, 0) - Coalesce(line.credit, 0)
|
||||
second_amount = Coalesce(line.amount_second_currency, 0)
|
||||
tx_amount = Case(
|
||||
(line.second_currency != Null, second_amount),
|
||||
else_=base_amount)
|
||||
debit_tx = Case(
|
||||
(line.second_currency == Null, Coalesce(line.debit, 0)),
|
||||
(second_amount > 0, second_amount),
|
||||
else_=Literal(0))
|
||||
credit_tx = Case(
|
||||
(line.second_currency == Null, Coalesce(line.credit, 0)),
|
||||
(second_amount < 0, -second_amount),
|
||||
else_=Literal(0))
|
||||
|
||||
with Transaction().set_context(
|
||||
context, **cls._query_context(name, context)):
|
||||
line_query, _ = Line.query_get(line)
|
||||
where = line_query & cls._extra_where(
|
||||
line, move, account, tx_currency, context)
|
||||
|
||||
window = Window(
|
||||
[line.account, tx_currency],
|
||||
order_by=[move.date.asc, move.number.asc, line.id.asc])
|
||||
|
||||
return from_.select(
|
||||
(line.id * 3).as_('id'),
|
||||
line.create_uid.as_('create_uid'),
|
||||
line.create_date.as_('create_date'),
|
||||
line.write_uid.as_('write_uid'),
|
||||
line.write_date.as_('write_date'),
|
||||
Literal('movement').as_('row_type'),
|
||||
Literal(1).as_('row_sequence'),
|
||||
move.company.as_('company'),
|
||||
line.account.as_('account'),
|
||||
account_label.as_('account_code'),
|
||||
account.name.as_('account_name'),
|
||||
tx_currency.as_('transaction_currency'),
|
||||
company.currency.as_('base_currency'),
|
||||
move.date.as_('posting_date'),
|
||||
move.journal.as_('journal'),
|
||||
move.number.as_('journal_entry_number'),
|
||||
move.post_number.as_('voucher_number'),
|
||||
move.date.as_('document_date'),
|
||||
move.state.as_('posting_status'),
|
||||
line.party.as_('party'),
|
||||
line.move.as_('move'),
|
||||
line.id.as_('move_line'),
|
||||
move.origin.as_('origin'),
|
||||
line.description.as_('description'),
|
||||
move.description.as_('move_description'),
|
||||
Coalesce(move.ext_ref, move.description).as_('reference'),
|
||||
line.debit.as_('debit_base_currency'),
|
||||
line.credit.as_('credit_base_currency'),
|
||||
base_amount.as_('balance_base_currency'),
|
||||
(Coalesce(opening_balance.base_amount, 0)
|
||||
+ Sum(base_amount, window=window)).as_(
|
||||
'running_balance_base_currency'),
|
||||
debit_tx.as_('debit_transaction_currency'),
|
||||
credit_tx.as_('credit_transaction_currency'),
|
||||
tx_amount.as_('balance_transaction_currency'),
|
||||
(Coalesce(opening_balance.transaction_amount, 0)
|
||||
+ Sum(tx_amount, window=window)).as_(
|
||||
'running_balance_transaction_currency'),
|
||||
quantity.as_('payable_qty'),
|
||||
unit.as_('uom'),
|
||||
line.create_uid.as_('entered_by'),
|
||||
line.create_date.as_('entered_date'),
|
||||
line.write_uid.as_('modified_by'),
|
||||
line.write_date.as_('modified_date'),
|
||||
where=where)
|
||||
|
||||
@classmethod
|
||||
def _summary_query(cls, name, context):
|
||||
Line, line, move, account, company = cls._tables()
|
||||
account_label = cls._account_label(account)
|
||||
tx_currency = Coalesce(line.second_currency, company.currency)
|
||||
base_amount = Coalesce(line.debit, 0) - Coalesce(line.credit, 0)
|
||||
tx_amount = Case(
|
||||
(line.second_currency != Null,
|
||||
Coalesce(line.amount_second_currency, 0)),
|
||||
else_=base_amount)
|
||||
row_sequence = 0 if name == 'opening' else 2
|
||||
row_id = (
|
||||
(Min(line.id) * 3 - 1) if name == 'opening'
|
||||
else (Min(line.id) * 3 - 2))
|
||||
|
||||
with Transaction().set_context(
|
||||
context, **cls._query_context(name, context)):
|
||||
line_query, _ = Line.query_get(line)
|
||||
where = line_query & cls._extra_where(
|
||||
line, move, account, tx_currency, context)
|
||||
|
||||
return (line.join(move, condition=line.move == move.id)
|
||||
.join(account, condition=line.account == account.id)
|
||||
.join(company, condition=move.company == company.id)
|
||||
.select(
|
||||
row_id.as_('id'),
|
||||
Literal(0).as_('create_uid'),
|
||||
Max(line.create_date).as_('create_date'),
|
||||
Literal(0).as_('write_uid'),
|
||||
Max(line.write_date).as_('write_date'),
|
||||
Literal(name).as_('row_type'),
|
||||
Literal(row_sequence).as_('row_sequence'),
|
||||
move.company.as_('company'),
|
||||
line.account.as_('account'),
|
||||
account_label.as_('account_code'),
|
||||
account.name.as_('account_name'),
|
||||
tx_currency.as_('transaction_currency'),
|
||||
company.currency.as_('base_currency'),
|
||||
Literal(None).as_('posting_date'),
|
||||
Literal(None).as_('journal'),
|
||||
Literal(None).as_('journal_entry_number'),
|
||||
Literal(None).as_('voucher_number'),
|
||||
Literal(None).as_('document_date'),
|
||||
Literal(None).as_('posting_status'),
|
||||
Literal(None).as_('party'),
|
||||
Literal(None).as_('move'),
|
||||
Literal(None).as_('move_line'),
|
||||
Literal(None).as_('origin'),
|
||||
Literal(name.title()).as_('description'),
|
||||
Literal(None).as_('move_description'),
|
||||
Literal(None).as_('reference'),
|
||||
Literal(0).as_('debit_base_currency'),
|
||||
Literal(0).as_('credit_base_currency'),
|
||||
Sum(base_amount).as_('balance_base_currency'),
|
||||
Sum(base_amount).as_('running_balance_base_currency'),
|
||||
Literal(0).as_('debit_transaction_currency'),
|
||||
Literal(0).as_('credit_transaction_currency'),
|
||||
Sum(tx_amount).as_('balance_transaction_currency'),
|
||||
Sum(tx_amount).as_('running_balance_transaction_currency'),
|
||||
Literal(None).as_('payable_qty'),
|
||||
Literal(None).as_('uom'),
|
||||
Literal(None).as_('entered_by'),
|
||||
Literal(None).as_('entered_date'),
|
||||
Literal(None).as_('modified_by'),
|
||||
Literal(None).as_('modified_date'),
|
||||
where=where,
|
||||
group_by=[
|
||||
move.company, line.account, account.code, account.name,
|
||||
tx_currency, company.currency],
|
||||
having=Sum(base_amount) != 0))
|
||||
|
||||
@classmethod
|
||||
def table_query(cls):
|
||||
pool = Pool()
|
||||
DetailContext = pool.get('account.general_ledger.detail.context')
|
||||
context = DetailContext.get_context()
|
||||
return Union(
|
||||
cls._summary_query('opening', context),
|
||||
cls._line_query('movement', context),
|
||||
cls._summary_query('closing', context))
|
||||
|
||||
@classmethod
|
||||
def get_origin(cls):
|
||||
Move = Pool().get('account.move')
|
||||
return Move.get_origin()
|
||||
|
||||
def _get_related_payment(self):
|
||||
if not self.move_line:
|
||||
return None
|
||||
try:
|
||||
Payment = Pool().get('account.payment')
|
||||
except KeyError:
|
||||
return None
|
||||
payments = Payment.search([('line', '=', self.move_line.id)], limit=1)
|
||||
return payments[0] if payments else None
|
||||
|
||||
def get_document_field(self, name):
|
||||
origin = self.move.origin if self.move else None
|
||||
if origin:
|
||||
model_name = origin.__name__
|
||||
if model_name == 'account.invoice':
|
||||
if name == 'document_number':
|
||||
return origin.number or origin.reference
|
||||
side = {
|
||||
'out': 'Customer',
|
||||
'in': 'Supplier',
|
||||
}.get(origin.type, '')
|
||||
stage = ''
|
||||
reference = (origin.reference or '').lower()
|
||||
if 'provisional' in reference:
|
||||
stage = 'Provisional'
|
||||
elif 'final' in reference:
|
||||
stage = 'Final'
|
||||
return ' '.join(filter(None, ['Invoice', side, stage]))
|
||||
if model_name == 'account.payment':
|
||||
if name == 'document_number':
|
||||
return origin.number
|
||||
side = {
|
||||
'receivable': 'Customer',
|
||||
'payable': 'Supplier',
|
||||
}.get(origin.kind, '')
|
||||
return ' '.join(filter(None, ['Payment', side]))
|
||||
if name == 'document_number':
|
||||
return getattr(origin, 'number', None) or getattr(
|
||||
origin, 'reference', None)
|
||||
return getattr(origin, 'rec_name', None) or model_name
|
||||
payment = self._get_related_payment()
|
||||
if payment:
|
||||
if name == 'document_number':
|
||||
return payment.number
|
||||
side = {
|
||||
'receivable': 'Customer',
|
||||
'payable': 'Supplier',
|
||||
}.get(payment.kind, '')
|
||||
return ' '.join(filter(None, ['Payment', side]))
|
||||
return None
|
||||
|
||||
|
||||
class GeneralLedger(Report):
|
||||
__name__ = 'account.general_ledger'
|
||||
|
||||
@@ -2611,6 +3150,52 @@ class GeneralLedger(Report):
|
||||
return report_context
|
||||
|
||||
|
||||
class GeneralLedgerDetailReport(Report):
|
||||
__name__ = 'account.general_ledger.detail.report'
|
||||
|
||||
@classmethod
|
||||
def get_context(cls, records, header, data):
|
||||
pool = Pool()
|
||||
Company = pool.get('company.company')
|
||||
Fiscalyear = pool.get('account.fiscalyear')
|
||||
Period = pool.get('account.period')
|
||||
context = Transaction().context
|
||||
|
||||
report_context = super().get_context(records, header, data)
|
||||
report_context['company'] = Company(context['company'])
|
||||
report_context['fiscalyear'] = Fiscalyear(context['fiscalyear'])
|
||||
for period in ['start_period', 'end_period']:
|
||||
if context.get(period):
|
||||
report_context[period] = Period(context[period])
|
||||
else:
|
||||
report_context[period] = None
|
||||
report_context['from_date'] = context.get('from_date')
|
||||
report_context['to_date'] = context.get('to_date')
|
||||
report_context['groups'] = cls._groups(records)
|
||||
return report_context
|
||||
|
||||
@classmethod
|
||||
def _groups(cls, records):
|
||||
groups = []
|
||||
current_key = None
|
||||
current = None
|
||||
for line in records:
|
||||
currency = getattr(line, 'transaction_currency', None)
|
||||
key = (
|
||||
getattr(line, 'account_code', None),
|
||||
getattr(currency, 'id', None))
|
||||
if key != current_key:
|
||||
current = {
|
||||
'account': line.account_code or line.account_name or '',
|
||||
'currency': currency,
|
||||
'lines': [],
|
||||
}
|
||||
groups.append(current)
|
||||
current_key = key
|
||||
current['lines'].append(line)
|
||||
return groups
|
||||
|
||||
|
||||
class TrialBalance(Report):
|
||||
__name__ = 'account.trial_balance'
|
||||
|
||||
|
||||
@@ -806,6 +806,70 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="rule_group" ref="rule_group_general_ledger_line_companies"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="general_ledger_detail_view_list">
|
||||
<field name="model">account.general_ledger.detail</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">general_ledger_detail_list</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_general_ledger_detail_form">
|
||||
<field name="name">General Ledger Detail</field>
|
||||
<field name="res_model">account.general_ledger.detail</field>
|
||||
<field name="context_model">account.general_ledger.detail.context</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view"
|
||||
id="act_general_ledger_detail_form_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="general_ledger_detail_view_list"/>
|
||||
<field name="act_window" ref="act_general_ledger_detail_form"/>
|
||||
</record>
|
||||
<record model="ir.action.report" id="report_general_ledger_detail">
|
||||
<field name="name">General Ledger Detail</field>
|
||||
<field name="records">listed</field>
|
||||
<field name="model">account.general_ledger.detail</field>
|
||||
<field name="report_name">account.general_ledger.detail.report</field>
|
||||
<field name="report">account/general_ledger_detail.fodt</field>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="report_general_ledger_detail_keyword">
|
||||
<field name="keyword">form_print</field>
|
||||
<field name="model">account.general_ledger.detail,-1</field>
|
||||
<field name="action" ref="report_general_ledger_detail"/>
|
||||
</record>
|
||||
<menuitem
|
||||
name="General Ledger Detail"
|
||||
parent="menu_reporting"
|
||||
action="act_general_ledger_detail_form"
|
||||
sequence="11"
|
||||
id="menu_general_ledger_detail"/>
|
||||
|
||||
<record model="ir.model.access" id="access_general_ledger_detail">
|
||||
<field name="model">account.general_ledger.detail</field>
|
||||
<field name="perm_read" eval="False"/>
|
||||
<field name="perm_write" eval="False"/>
|
||||
<field name="perm_create" eval="False"/>
|
||||
<field name="perm_delete" eval="False"/>
|
||||
</record>
|
||||
<record model="ir.model.access" id="access_general_ledger_detail_account">
|
||||
<field name="model">account.general_ledger.detail</field>
|
||||
<field name="group" ref="group_account"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="False"/>
|
||||
<field name="perm_create" eval="False"/>
|
||||
<field name="perm_delete" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule.group" id="rule_group_general_ledger_detail_companies">
|
||||
<field name="name">User in companies</field>
|
||||
<field name="model">account.general_ledger.detail</field>
|
||||
<field name="global_p" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="rule_general_ledger_detail_companies">
|
||||
<field name="domain"
|
||||
eval="[('company', 'in', Eval('companies', []))]"
|
||||
pyson="1"/>
|
||||
<field name="rule_group" ref="rule_group_general_ledger_detail_companies"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="general_ledger_account_context_view_form">
|
||||
<field name="model">account.general_ledger.account.context</field>
|
||||
<field name="type">form</field>
|
||||
@@ -817,6 +881,11 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="inherit" ref="general_ledger_account_context_view_form"/>
|
||||
<field name="name">general_ledger_line_context_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="general_ledger_detail_context_view_form">
|
||||
<field name="model">account.general_ledger.detail.context</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">general_ledger_detail_context_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="account_balance_sheet_view_tree">
|
||||
<field name="model">account.account.type</field>
|
||||
|
||||
825
modules/account/general_ledger_detail.fodt
Normal file
825
modules/account/general_ledger_detail.fodt
Normal file
@@ -0,0 +1,825 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<office:document xmlns:officeooo="http://openoffice.org/2009/office" xmlns:css3t="http://www.w3.org/TR/css3-text/" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rpt="http://openoffice.org/2005/report" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:xforms="http://www.w3.org/2002/xforms" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
|
||||
<office:meta><meta:generator>LibreOffice/7.3.5.2$Linux_X86_64 LibreOffice_project/30$Build-2</meta:generator><meta:creation-date>2008-06-07T15:26:41</meta:creation-date><dc:date>2008-08-27T17:03:36</dc:date><meta:editing-cycles>1</meta:editing-cycles><meta:editing-duration>PT0S</meta:editing-duration><meta:document-statistic meta:table-count="4" meta:image-count="0" meta:object-count="0" meta:page-count="3" meta:paragraph-count="51" meta:word-count="145" meta:character-count="1808" meta:non-whitespace-character-count="1714"/><meta:user-defined meta:name="Info 1"/><meta:user-defined meta:name="Info 2"/><meta:user-defined meta:name="Info 3"/><meta:user-defined meta:name="Info 4"/></office:meta>
|
||||
<office:settings>
|
||||
<config:config-item-set config:name="ooo:view-settings">
|
||||
<config:config-item config:name="ViewAreaTop" config:type="long">24130</config:config-item>
|
||||
<config:config-item config:name="ViewAreaLeft" config:type="long">0</config:config-item>
|
||||
<config:config-item config:name="ViewAreaWidth" config:type="long">48129</config:config-item>
|
||||
<config:config-item config:name="ViewAreaHeight" config:type="long">22994</config:config-item>
|
||||
<config:config-item config:name="ShowRedlineChanges" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="InBrowseMode" config:type="boolean">false</config:config-item>
|
||||
<config:config-item-map-indexed config:name="Views">
|
||||
<config:config-item-map-entry>
|
||||
<config:config-item config:name="ViewId" config:type="string">view2</config:config-item>
|
||||
<config:config-item config:name="ViewLeft" config:type="long">19627</config:config-item>
|
||||
<config:config-item config:name="ViewTop" config:type="long">29242</config:config-item>
|
||||
<config:config-item config:name="VisibleLeft" config:type="long">0</config:config-item>
|
||||
<config:config-item config:name="VisibleTop" config:type="long">24130</config:config-item>
|
||||
<config:config-item config:name="VisibleRight" config:type="long">48128</config:config-item>
|
||||
<config:config-item config:name="VisibleBottom" config:type="long">47122</config:config-item>
|
||||
<config:config-item config:name="ZoomType" config:type="short">0</config:config-item>
|
||||
<config:config-item config:name="ViewLayoutColumns" config:type="short">0</config:config-item>
|
||||
<config:config-item config:name="ViewLayoutBookMode" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ZoomFactor" config:type="short">100</config:config-item>
|
||||
<config:config-item config:name="IsSelectedFrame" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="KeepRatio" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="AnchoredTextOverflowLegacy" config:type="boolean">false</config:config-item>
|
||||
</config:config-item-map-entry>
|
||||
</config:config-item-map-indexed>
|
||||
</config:config-item-set>
|
||||
<config:config-item-set config:name="ooo:configuration-settings">
|
||||
<config:config-item config:name="PrintProspect" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintReversed" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintSingleJobs" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintLeftPages" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrintTables" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrintControls" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrintPageBackground" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrintDrawings" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrintBlackFonts" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintAnnotationMode" config:type="short">0</config:config-item>
|
||||
<config:config-item config:name="PrintTextPlaceholder" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ProtectFields" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ProtectBookmarks" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="EmptyDbFieldHidesPara" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="DisableOffPagePositioning" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="SubtractFlysAnchoredAtFlys" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PropLineSpacingShrinksFirstLine" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ApplyParagraphMarkFormatToNumbering" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="GutterAtTop" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="TreatSingleColumnBreakAsPageBreak" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="EmbedSystemFonts" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="EmbedComplexScriptFonts" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="EmbedAsianScriptFonts" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="EmbedLatinScriptFonts" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="EmbedOnlyUsedFonts" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ContinuousEndnotes" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="EmbedFonts" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ClippedPictures" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="FloattableNomargins" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="UnbreakableNumberings" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="HeaderSpacingBelowLastPara" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="AllowPrintJobCancel" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="UseOldPrinterMetrics" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="TabOverMargin" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="TabsRelativeToIndent" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="UseOldNumbering" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="InvertBorderSpacing" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintPaperFromSetup" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="UpdateFromTemplate" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="CurrentDatabaseCommandType" config:type="int">0</config:config-item>
|
||||
<config:config-item config:name="LinkUpdateMode" config:type="short">1</config:config-item>
|
||||
<config:config-item config:name="AddParaSpacingToTableCells" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="FrameAutowidthWithMorePara" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="CurrentDatabaseCommand" config:type="string"/>
|
||||
<config:config-item config:name="PrinterIndependentLayout" config:type="string">high-resolution</config:config-item>
|
||||
<config:config-item config:name="ApplyUserData" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintFaxName" config:type="string"/>
|
||||
<config:config-item config:name="CurrentDatabaseDataSource" config:type="string"/>
|
||||
<config:config-item config:name="ClipAsCharacterAnchoredWriterFlyFrames" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="IsKernAsianPunctuation" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="SaveThumbnail" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="UseFormerTextWrapping" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="AddExternalLeading" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="AddParaTableSpacing" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="StylesNoDefault" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ChartAutoUpdate" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrinterSetup" config:type="base64Binary"/>
|
||||
<config:config-item config:name="AddParaTableSpacingAtStart" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="Rsid" config:type="int">4069242</config:config-item>
|
||||
<config:config-item config:name="EmbeddedDatabaseName" config:type="string"/>
|
||||
<config:config-item config:name="FieldAutoUpdate" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="OutlineLevelYieldsNumbering" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="FootnoteInColumnToPageEnd" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="AlignTabStopPosition" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="CharacterCompressionType" config:type="short">0</config:config-item>
|
||||
<config:config-item config:name="PrinterName" config:type="string"/>
|
||||
<config:config-item config:name="SaveGlobalDocumentLinks" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrinterPaperFromSetup" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="UseFormerLineSpacing" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="AddParaLineSpacingToTableCells" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="UseFormerObjectPositioning" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintGraphics" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="SurroundTextWrapSmall" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ConsiderTextWrapOnObjPos" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="MsWordCompTrailingBlanks" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="TabAtLeftIndentForParagraphsInList" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintRightPages" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="TabOverSpacing" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="IgnoreFirstLineIndentInNumbering" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="RedlineProtectionKey" config:type="base64Binary"/>
|
||||
<config:config-item config:name="DoNotJustifyLinesWithManualBreak" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintProspectRTL" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintEmptyPages" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="DoNotResetParaAttrsForNumFont" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="AddFrameOffsets" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="IgnoreTabsAndBlanksForLineCalculation" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="LoadReadonly" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="DoNotCaptureDrawObjsOnPage" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="AddVerticalFrameOffsets" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="UnxForceZeroExtLeading" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="IsLabelDocument" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="TableRowKeep" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="RsidRoot" config:type="int">2070049</config:config-item>
|
||||
<config:config-item config:name="PrintHiddenText" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ProtectForm" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="MsWordCompMinLineHeightByFly" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="BackgroundParaOverDrawings" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="SaveVersionOnClose" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="MathBaselineAlignment" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="SmallCapsPercentage66" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="CollapseEmptyCellPara" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="TabOverflow" config:type="boolean">false</config:config-item>
|
||||
</config:config-item-set>
|
||||
</office:settings>
|
||||
<office:scripts>
|
||||
<office:script script:language="ooo:Basic">
|
||||
<ooo:libraries xmlns:ooo="http://openoffice.org/2004/office" xmlns:xlink="http://www.w3.org/1999/xlink"/>
|
||||
</office:script>
|
||||
</office:scripts>
|
||||
<office:font-face-decls>
|
||||
<style:font-face style:name="Andale Sans UI" svg:font-family="'Andale Sans UI'" style:font-family-generic="system" style:font-pitch="variable"/>
|
||||
<style:font-face style:name="DejaVu Sans" svg:font-family="'DejaVu Sans'" style:font-family-generic="system" style:font-pitch="variable"/>
|
||||
<style:font-face style:name="Liberation Sans" svg:font-family="'Liberation Sans'" style:font-adornments="Regular" style:font-family-generic="swiss" style:font-pitch="variable"/>
|
||||
<style:font-face style:name="Liberation Serif" svg:font-family="'Liberation Serif'" style:font-family-generic="roman" style:font-pitch="variable"/>
|
||||
<style:font-face style:name="Liberation Serif1" svg:font-family="'Liberation Serif'" style:font-adornments="Bold" style:font-family-generic="roman" style:font-pitch="variable"/>
|
||||
<style:font-face style:name="Liberation Serif2" svg:font-family="'Liberation Serif'" style:font-adornments="Regular" style:font-family-generic="roman" style:font-pitch="variable"/>
|
||||
<style:font-face style:name="StarSymbol" svg:font-family="StarSymbol"/>
|
||||
<style:font-face style:name="Thorndale AMT" svg:font-family="'Thorndale AMT'" style:font-family-generic="roman" style:font-pitch="variable"/>
|
||||
</office:font-face-decls>
|
||||
<office:styles>
|
||||
<style:default-style style:family="graphic">
|
||||
<style:graphic-properties svg:stroke-color="#000000" draw:fill-color="#99ccff" fo:wrap-option="no-wrap" draw:shadow-offset-x="0.3cm" draw:shadow-offset-y="0.3cm" draw:start-line-spacing-horizontal="0.283cm" draw:start-line-spacing-vertical="0.283cm" draw:end-line-spacing-horizontal="0.283cm" draw:end-line-spacing-vertical="0.283cm" style:flow-with-text="false"/>
|
||||
<style:paragraph-properties style:text-autospace="ideograph-alpha" style:line-break="strict" style:writing-mode="lr-tb" style:font-independent-line-spacing="false">
|
||||
<style:tab-stops/>
|
||||
</style:paragraph-properties>
|
||||
<style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Thorndale AMT" fo:font-size="12pt" fo:language="en" fo:country="US" style:letter-kerning="true" style:font-name-asian="Andale Sans UI" style:font-size-asian="10.5pt" style:language-asian="zxx" style:country-asian="none" style:font-name-complex="Andale Sans UI" style:font-size-complex="12pt" style:language-complex="zxx" style:country-complex="none"/>
|
||||
</style:default-style>
|
||||
<style:default-style style:family="paragraph">
|
||||
<style:paragraph-properties fo:hyphenation-ladder-count="no-limit" style:text-autospace="ideograph-alpha" style:punctuation-wrap="hanging" style:line-break="strict" style:tab-stop-distance="1.251cm" style:writing-mode="lr-tb"/>
|
||||
<style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Thorndale AMT" fo:font-size="12pt" fo:language="en" fo:country="US" style:letter-kerning="true" style:font-name-asian="Andale Sans UI" style:font-size-asian="10.5pt" style:language-asian="zxx" style:country-asian="none" style:font-name-complex="Andale Sans UI" style:font-size-complex="12pt" style:language-complex="zxx" style:country-complex="none" fo:hyphenate="false" fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2" loext:hyphenation-no-caps="false"/>
|
||||
</style:default-style>
|
||||
<style:default-style style:family="table">
|
||||
<style:table-properties table:border-model="collapsing"/>
|
||||
</style:default-style>
|
||||
<style:default-style style:family="table-row">
|
||||
<style:table-row-properties fo:keep-together="auto"/>
|
||||
</style:default-style>
|
||||
<style:style style:name="Standard" style:family="paragraph" style:class="text">
|
||||
<style:text-properties style:font-name="Liberation Sans" fo:font-family="'Liberation Sans'" style:font-style-name="Regular" style:font-family-generic="swiss" style:font-pitch="variable" style:font-size-asian="10.5pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="Heading" style:family="paragraph" style:parent-style-name="Standard" style:next-style-name="Text_20_body" style:class="text">
|
||||
<style:paragraph-properties fo:margin-top="0.423cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" fo:keep-with-next="always"/>
|
||||
<style:text-properties style:font-name="Liberation Serif2" fo:font-family="'Liberation Serif'" style:font-style-name="Regular" style:font-family-generic="roman" style:font-pitch="variable" fo:font-size="16pt" style:font-name-asian="DejaVu Sans" style:font-family-asian="'DejaVu Sans'" style:font-family-generic-asian="system" style:font-pitch-asian="variable" style:font-size-asian="14pt" style:font-name-complex="DejaVu Sans" style:font-family-complex="'DejaVu Sans'" style:font-family-generic-complex="system" style:font-pitch-complex="variable" style:font-size-complex="14pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="Text_20_body" style:display-name="Text body" style:family="paragraph" style:parent-style-name="Standard" style:class="text">
|
||||
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false"/>
|
||||
<style:text-properties style:font-name="Liberation Sans" fo:font-family="'Liberation Sans'" style:font-style-name="Regular" style:font-family-generic="swiss" style:font-pitch="variable" style:font-size-asian="10.5pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="List" style:family="paragraph" style:parent-style-name="Text_20_body" style:class="list">
|
||||
<style:text-properties style:font-size-asian="12pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="Caption" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
|
||||
<style:paragraph-properties fo:margin-top="0.212cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" text:number-lines="false" text:line-number="0"/>
|
||||
<style:text-properties fo:font-size="12pt" fo:font-style="italic" style:font-size-asian="12pt" style:font-style-asian="italic" style:font-size-complex="12pt" style:font-style-complex="italic"/>
|
||||
</style:style>
|
||||
<style:style style:name="Index" style:family="paragraph" style:parent-style-name="Standard" style:class="index">
|
||||
<style:paragraph-properties text:number-lines="false" text:line-number="0"/>
|
||||
<style:text-properties style:font-size-asian="12pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table_20_Contents" style:display-name="Table Contents" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
|
||||
<style:paragraph-properties text:number-lines="false" text:line-number="0"/>
|
||||
<style:text-properties fo:font-size="11pt" style:font-size-asian="10.5pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="Heading_20_1" style:display-name="Heading 1" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="text">
|
||||
<style:text-properties fo:font-size="16pt" fo:font-weight="bold" style:font-size-asian="115%" style:font-weight-asian="bold" style:font-size-complex="115%" style:font-weight-complex="bold"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table_20_Heading" style:display-name="Table Heading" style:family="paragraph" style:parent-style-name="Table_20_Contents" style:class="extra">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" text:number-lines="false" text:line-number="0"/>
|
||||
<style:text-properties style:font-name="Liberation Serif1" fo:font-family="'Liberation Serif'" style:font-style-name="Bold" style:font-family-generic="roman" style:font-pitch="variable" fo:font-size="12pt" fo:font-weight="bold" style:font-size-asian="10.5pt" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
|
||||
</style:style>
|
||||
<style:style style:name="Horizontal_20_Line" style:display-name="Horizontal Line" style:family="paragraph" style:parent-style-name="Standard" style:next-style-name="Text_20_body" style:class="html">
|
||||
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0.499cm" style:contextual-spacing="false" style:border-line-width-bottom="0.002cm 0.035cm 0.002cm" fo:padding="0cm" fo:border-left="none" fo:border-right="none" fo:border-top="none" fo:border-bottom="1.11pt double #808080" text:number-lines="false" text:line-number="0" style:join-border="false"/>
|
||||
<style:text-properties fo:font-size="6pt" style:font-size-asian="6pt" style:font-size-complex="6pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="Header_20_and_20_Footer" style:display-name="Header and Footer" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
|
||||
<style:paragraph-properties text:number-lines="false" text:line-number="0">
|
||||
<style:tab-stops>
|
||||
<style:tab-stop style:position="8.5cm" style:type="center"/>
|
||||
<style:tab-stop style:position="17cm" style:type="right"/>
|
||||
</style:tab-stops>
|
||||
</style:paragraph-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Header" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
|
||||
<style:paragraph-properties text:number-lines="false" text:line-number="0">
|
||||
<style:tab-stops>
|
||||
<style:tab-stop style:position="8.795cm" style:type="center"/>
|
||||
<style:tab-stop style:position="17.59cm" style:type="right"/>
|
||||
</style:tab-stops>
|
||||
</style:paragraph-properties>
|
||||
<style:text-properties fo:font-size="9pt" style:font-size-asian="10.5pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="Heading_20_2" style:display-name="Heading 2" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="text">
|
||||
<style:text-properties fo:font-size="14pt" fo:font-style="italic" fo:font-weight="bold" style:font-size-asian="14pt" style:font-style-asian="italic" style:font-weight-asian="bold" style:font-size-complex="14pt" style:font-style-complex="italic" style:font-weight-complex="bold"/>
|
||||
</style:style>
|
||||
<style:style style:name="Footer" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
|
||||
<style:paragraph-properties text:number-lines="false" text:line-number="0">
|
||||
<style:tab-stops>
|
||||
<style:tab-stop style:position="8.795cm" style:type="center"/>
|
||||
<style:tab-stop style:position="17.59cm" style:type="right"/>
|
||||
</style:tab-stops>
|
||||
</style:paragraph-properties>
|
||||
<style:text-properties fo:font-size="9pt" style:font-size-asian="10.5pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="Heading_20_3" style:display-name="Heading 3" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="text">
|
||||
<style:text-properties fo:font-size="14pt" fo:font-weight="bold" style:font-size-asian="14pt" style:font-weight-asian="bold" style:font-size-complex="14pt" style:font-weight-complex="bold"/>
|
||||
</style:style>
|
||||
<style:style style:name="Text_20_body_20_indent" style:display-name="Text body indent" style:family="paragraph" style:parent-style-name="Text_20_body" style:class="text">
|
||||
<style:paragraph-properties fo:margin-left="0.499cm" fo:margin-right="0cm" fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" fo:text-indent="0cm" style:auto-text-indent="false"/>
|
||||
</style:style>
|
||||
<style:style style:name="Quotations" style:family="paragraph" style:parent-style-name="Standard" style:class="html">
|
||||
<style:paragraph-properties fo:margin-left="1cm" fo:margin-right="1cm" fo:margin-top="0cm" fo:margin-bottom="0.499cm" style:contextual-spacing="false" fo:text-indent="0cm" style:auto-text-indent="false"/>
|
||||
</style:style>
|
||||
<style:style style:name="Title" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="chapter">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
|
||||
<style:text-properties fo:font-size="28pt" fo:font-weight="bold" style:font-size-asian="28pt" style:font-weight-asian="bold" style:font-size-complex="28pt" style:font-weight-complex="bold"/>
|
||||
</style:style>
|
||||
<style:style style:name="Subtitle" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="chapter">
|
||||
<style:paragraph-properties fo:margin-top="0.106cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" fo:text-align="center" style:justify-single-word="false"/>
|
||||
<style:text-properties fo:font-size="18pt" style:font-size-asian="18pt" style:font-size-complex="18pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="Placeholder" style:family="text">
|
||||
<style:text-properties fo:font-variant="small-caps" fo:color="#008080" loext:opacity="100%" style:text-underline-style="dotted" style:text-underline-width="auto" style:text-underline-color="font-color"/>
|
||||
</style:style>
|
||||
<style:style style:name="Bullet_20_Symbols" style:display-name="Bullet Symbols" style:family="text">
|
||||
<style:text-properties style:font-name="StarSymbol" fo:font-family="StarSymbol" fo:font-size="9pt" style:font-name-asian="StarSymbol" style:font-family-asian="StarSymbol" style:font-size-asian="9pt" style:font-name-complex="StarSymbol" style:font-family-complex="StarSymbol" style:font-size-complex="9pt"/>
|
||||
</style:style>
|
||||
<text:outline-style style:name="Outline">
|
||||
<text:outline-level-style text:level="1" loext:num-list-format="%1%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="2" loext:num-list-format="%2%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="3" loext:num-list-format="%3%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="4" loext:num-list-format="%4%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="5" loext:num-list-format="%5%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="6" loext:num-list-format="%6%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="7" loext:num-list-format="%7%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="8" loext:num-list-format="%8%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="9" loext:num-list-format="%9%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="10" loext:num-list-format="%10%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
</text:outline-style>
|
||||
<text:notes-configuration text:note-class="footnote" style:num-format="1" text:start-value="0" text:footnotes-position="page" text:start-numbering-at="document"/>
|
||||
<text:notes-configuration text:note-class="endnote" style:num-format="i" text:start-value="0"/>
|
||||
<text:linenumbering-configuration text:number-lines="false" text:offset="0.499cm" style:num-format="1" text:number-position="left" text:increment="5"/>
|
||||
<style:style style:name="Default_20_Style.1" style:display-name="Default Style.1" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-top="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.2" style:display-name="Default Style.2" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.3" style:display-name="Default Style.3" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.4" style:display-name="Default Style.4" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-right="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.5" style:display-name="Default Style.5" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.6" style:display-name="Default Style.6" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.7" style:display-name="Default Style.7" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.8" style:display-name="Default Style.8" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.9" style:display-name="Default Style.9" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.10" style:display-name="Default Style.10" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-right="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.11" style:display-name="Default Style.11" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-top="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.12" style:display-name="Default Style.12" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-right="0.51pt solid #000000" fo:border-top="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.13" style:display-name="Default Style.13" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.14" style:display-name="Default Style.14" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-right="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.15" style:display-name="Default Style.15" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-top="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.16" style:display-name="Default Style.16" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<table:table-template table:name="Default Style" table:first-row-end-column="row" table:first-row-start-column="row" table:last-row-end-column="row" table:last-row-start-column="row">
|
||||
<table:first-row table:style-name="Default_20_Style.1"/>
|
||||
<table:last-row table:style-name="Default_20_Style.2"/>
|
||||
<table:first-column table:style-name="Default_20_Style.3"/>
|
||||
<table:last-column table:style-name="Default_20_Style.4"/>
|
||||
<table:body table:style-name="Default_20_Style.9"/>
|
||||
<table:even-rows table:style-name="Default_20_Style.5"/>
|
||||
<table:odd-rows table:style-name="Default_20_Style.6"/>
|
||||
<table:even-columns table:style-name="Default_20_Style.7"/>
|
||||
<table:odd-columns table:style-name="Default_20_Style.8"/>
|
||||
<table:background table:style-name="Default_20_Style.10"/>
|
||||
<loext:first-row-even-column table:style-name="Default_20_Style.15"/>
|
||||
<loext:last-row-even-column table:style-name="Default_20_Style.16"/>
|
||||
<loext:first-row-end-column table:style-name="Default_20_Style.12"/>
|
||||
<loext:first-row-start-column table:style-name="Default_20_Style.11"/>
|
||||
<loext:last-row-end-column table:style-name="Default_20_Style.14"/>
|
||||
<loext:last-row-start-column table:style-name="Default_20_Style.13"/>
|
||||
</table:table-template>
|
||||
</office:styles>
|
||||
<office:automatic-styles>
|
||||
<style:style style:name="Table2" style:family="table">
|
||||
<style:table-properties style:width="23.94cm" table:align="margins"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table2.A" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="7.98cm" style:rel-column-width="21845*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table2.A1" style:family="table-cell">
|
||||
<style:table-cell-properties fo:background-color="transparent" fo:padding="0.097cm" fo:border="none">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Table1" style:family="table">
|
||||
<style:table-properties style:width="23.94cm" table:align="margins"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table1.A" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="11.97cm" style:rel-column-width="32768*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table1.B" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="11.97cm" style:rel-column-width="32767*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table1.A1" style:family="table-cell">
|
||||
<style:table-cell-properties fo:padding="0.097cm" fo:border="none"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table2" style:family="table">
|
||||
<style:table-properties style:width="23.94cm" table:align="margins"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table2.A" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="7.98cm" style:rel-column-width="21845*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table2.A1" style:family="table-cell">
|
||||
<style:table-cell-properties fo:background-color="transparent" fo:padding="0.097cm" fo:border="none">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Table1" style:family="table">
|
||||
<style:table-properties style:width="23.94cm" table:align="margins"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table1.A" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="11.97cm" style:rel-column-width="32768*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table1.B" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="11.97cm" style:rel-column-width="32767*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table1.A1" style:family="table-cell">
|
||||
<style:table-cell-properties fo:padding="0.097cm" fo:border="none"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines" style:family="table">
|
||||
<style:table-properties style:width="23.94cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:break-before="auto" fo:break-after="auto" table:align="margins" fo:background-color="transparent" fo:keep-with-next="always" style:may-break-between-rows="true" style:writing-mode="lr-tb">
|
||||
<style:background-image/>
|
||||
</style:table-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.A" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="2.499cm" style:rel-column-width="1417*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.B" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="3.358cm" style:rel-column-width="1904*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.C" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="3.36cm" style:rel-column-width="1905*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.F" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="4.002cm" style:rel-column-width="2269*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.1" style:family="table-row">
|
||||
<style:table-row-properties fo:keep-together="always"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.A1" style:family="table-cell">
|
||||
<style:table-cell-properties fo:background-color="#b3b3b3" fo:padding="0.097cm" fo:border="0.05pt solid #000000">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.A2" style:family="table-cell">
|
||||
<style:table-cell-properties fo:background-color="#b3b3b3" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.A3" style:family="table-cell">
|
||||
<style:table-cell-properties fo:background-color="#e6e6e6" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="0.05pt solid #000000" fo:border-bottom="0.05pt solid #000000">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.G3" style:family="table-cell">
|
||||
<style:table-cell-properties fo:background-color="#e6e6e6" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.A4" style:family="table-cell">
|
||||
<style:table-cell-properties fo:background-color="#cccccc" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="0.05pt solid #000000" fo:border-bottom="0.05pt solid #000000">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.C4" style:family="table-cell">
|
||||
<style:table-cell-properties fo:background-color="#cccccc" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.G4" style:family="table-cell">
|
||||
<style:table-cell-properties fo:background-color="#cccccc" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.A5" style:family="table-cell">
|
||||
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.A6" style:family="table-cell">
|
||||
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.B6" style:family="table-cell">
|
||||
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.C6" style:family="table-cell">
|
||||
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.D6" style:family="table-cell">
|
||||
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.E6" style:family="table-cell">
|
||||
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.F6" style:family="table-cell">
|
||||
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.G6" style:family="table-cell">
|
||||
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.A7" style:family="table-cell">
|
||||
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.A8" style:family="table-cell">
|
||||
<style:table-cell-properties fo:background-color="#e6e6e6" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Table3" style:family="table" style:master-page-name="">
|
||||
<style:table-properties style:width="23.94cm" style:page-number="auto" fo:break-before="auto" fo:break-after="auto" table:align="margins" fo:background-color="transparent" style:shadow="none" fo:keep-with-next="always" style:may-break-between-rows="true" style:writing-mode="lr-tb">
|
||||
<style:background-image/>
|
||||
</style:table-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Table3.A" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="5.851cm" style:rel-column-width="3317*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table3.B" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="3.362cm" style:rel-column-width="1906*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table3.E" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="4.001cm" style:rel-column-width="2268*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table3.1" style:family="table-row">
|
||||
<style:table-row-properties fo:background-color="transparent" fo:keep-together="always">
|
||||
<style:background-image/>
|
||||
</style:table-row-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Table3.A1" style:family="table-cell">
|
||||
<style:table-cell-properties style:vertical-align="" fo:background-color="#b3b3b3" fo:padding="0.097cm" fo:border="0.05pt solid #000000" style:writing-mode="lr-tb">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Table3.A2" style:family="table-cell">
|
||||
<style:table-cell-properties style:vertical-align="" fo:background-color="#b3b3b3" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000" style:writing-mode="lr-tb">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Table3.A3" style:family="table-cell">
|
||||
<style:table-cell-properties style:vertical-align="" fo:background-color="#cccccc" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000" style:writing-mode="lr-tb">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Table3.F3" style:family="table-cell">
|
||||
<style:table-cell-properties style:vertical-align="" fo:background-color="#cccccc" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000" style:writing-mode="lr-tb">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="P1" style:family="paragraph" style:parent-style-name="Header">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
|
||||
</style:style>
|
||||
<style:style style:name="P2" style:family="paragraph" style:parent-style-name="Header">
|
||||
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false"/>
|
||||
</style:style>
|
||||
<style:style style:name="P3" style:family="paragraph" style:parent-style-name="Footer">
|
||||
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false"/>
|
||||
</style:style>
|
||||
<style:style style:name="P4" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
</style:style>
|
||||
<style:style style:name="P5" style:family="paragraph" style:parent-style-name="Table_20_Heading">
|
||||
<style:paragraph-properties fo:keep-together="always" style:shadow="none" fo:keep-with-next="always"/>
|
||||
</style:style>
|
||||
<style:style style:name="P6" style:family="paragraph" style:parent-style-name="Table_20_Heading">
|
||||
<style:paragraph-properties fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
</style:style>
|
||||
<style:style style:name="P7" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" style:shadow="none" fo:keep-with-next="always"/>
|
||||
<style:text-properties fo:font-size="12pt" fo:font-weight="bold" style:font-size-asian="12pt" style:font-weight-asian="bold" style:font-size-complex="12pt" style:font-weight-complex="bold"/>
|
||||
</style:style>
|
||||
<style:style style:name="P8" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
|
||||
<style:text-properties fo:font-size="12pt" style:text-underline-style="none" fo:font-weight="normal" style:font-size-asian="10.5pt" style:font-weight-asian="normal" style:font-size-complex="12pt" style:font-weight-complex="normal"/>
|
||||
</style:style>
|
||||
<style:style style:name="P9" style:family="paragraph" style:parent-style-name="Table_20_Heading">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
<style:text-properties style:font-size-asian="10.5pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P10" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
<style:text-properties style:font-size-asian="10.5pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P11" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
<style:text-properties officeooo:paragraph-rsid="002aca3c" style:font-size-asian="10.5pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P12" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
</style:style>
|
||||
<style:style style:name="P13" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
<style:text-properties fo:font-size="11pt" style:font-size-asian="11pt" style:font-size-complex="11pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P14" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
<style:text-properties fo:font-size="11pt" style:font-size-asian="11pt" style:font-size-complex="11pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P15" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
<style:text-properties fo:font-size="11pt" style:text-underline-style="none" style:font-size-asian="11pt" style:font-size-complex="11pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P16" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
<style:text-properties style:text-underline-style="none"/>
|
||||
</style:style>
|
||||
<style:style style:name="P17" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
<style:text-properties style:text-underline-style="none" officeooo:paragraph-rsid="00251ac8"/>
|
||||
</style:style>
|
||||
<style:style style:name="P18" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
|
||||
<style:text-properties style:text-underline-style="none"/>
|
||||
</style:style>
|
||||
<style:style style:name="P19" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
<style:text-properties style:text-underline-style="none" officeooo:paragraph-rsid="00251ac8" fo:background-color="transparent"/>
|
||||
</style:style>
|
||||
<style:style style:name="P20" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:text-properties style:text-underline-style="none"/>
|
||||
</style:style>
|
||||
<style:style style:name="P21" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:paragraph-properties fo:break-before="page"/>
|
||||
<style:text-properties style:text-underline-style="none"/>
|
||||
</style:style>
|
||||
<style:style style:name="P22" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:text-properties officeooo:paragraph-rsid="0037c304"/>
|
||||
</style:style>
|
||||
<style:style style:name="P23" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:text-properties officeooo:paragraph-rsid="003a045d"/>
|
||||
</style:style>
|
||||
<style:style style:name="P24" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:text-properties officeooo:rsid="003955ec" officeooo:paragraph-rsid="003a045d"/>
|
||||
</style:style>
|
||||
<style:style style:name="P25" style:family="paragraph" style:parent-style-name="Heading_20_1">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
|
||||
<style:text-properties style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color"/>
|
||||
</style:style>
|
||||
<style:style style:name="P26" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
|
||||
<style:text-properties style:font-name="Liberation Serif1" fo:font-weight="bold" style:font-size-asian="10.5pt" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
|
||||
</style:style>
|
||||
<style:style style:name="P27" style:family="paragraph" style:parent-style-name="Header">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
|
||||
<style:text-properties style:text-underline-style="none" officeooo:paragraph-rsid="003e177a"/>
|
||||
</style:style>
|
||||
<style:style style:name="P28" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
</style:style>
|
||||
<style:style style:name="P29" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
|
||||
<style:text-properties fo:color="#000000" loext:opacity="100%" style:text-outline="false" style:text-line-through-style="none" style:text-line-through-type="none" style:font-name="Liberation Serif" fo:font-size="12pt" fo:font-style="normal" fo:text-shadow="none" style:text-underline-style="none" fo:font-weight="normal" style:font-size-asian="12pt" style:font-style-asian="normal" style:font-weight-asian="normal" style:font-size-complex="12pt" style:font-style-complex="normal" style:font-weight-complex="normal" style:text-overline-style="none" style:text-overline-color="font-color"/>
|
||||
</style:style>
|
||||
<style:style style:name="P30" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
<style:text-properties officeooo:paragraph-rsid="002aca3c" style:font-size-asian="10.5pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P31" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
<style:text-properties style:font-size-asian="10.5pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P32" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false"/>
|
||||
</style:style>
|
||||
<style:style style:name="P33" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
|
||||
</style:style>
|
||||
<style:style style:name="P34" style:family="paragraph" style:parent-style-name="Table_20_Heading">
|
||||
<style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
|
||||
</style:style>
|
||||
<style:style style:name="P35" style:family="paragraph" style:parent-style-name="Table_20_Heading">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
|
||||
<style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
|
||||
</style:style>
|
||||
<style:style style:name="P36" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:break-before="page" fo:keep-with-next="always"/>
|
||||
<style:text-properties style:text-underline-style="none" officeooo:paragraph-rsid="003e177a"/>
|
||||
</style:style>
|
||||
<style:style style:name="P37" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
<style:text-properties style:text-underline-style="none" officeooo:paragraph-rsid="003e177a"/>
|
||||
</style:style>
|
||||
<style:style style:name="P38" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
<style:text-properties fo:font-size="12pt" style:text-underline-style="none" fo:font-weight="normal" officeooo:paragraph-rsid="003e177a" fo:background-color="transparent" style:font-size-asian="10.5pt" style:font-weight-asian="normal" style:font-size-complex="12pt" style:font-weight-complex="normal"/>
|
||||
</style:style>
|
||||
<style:style style:name="T1" style:family="text">
|
||||
<style:text-properties officeooo:rsid="0037c304"/>
|
||||
</style:style>
|
||||
<style:style style:name="T2" style:family="text">
|
||||
<style:text-properties officeooo:rsid="0039b016"/>
|
||||
</style:style>
|
||||
<style:style style:name="T3" style:family="text">
|
||||
<style:text-properties officeooo:rsid="003e177a"/>
|
||||
</style:style>
|
||||
<style:style style:name="T4" style:family="text">
|
||||
<style:text-properties style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color"/>
|
||||
</style:style>
|
||||
<style:style style:name="T5" style:family="text">
|
||||
<style:text-properties style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color" officeooo:rsid="003e177a"/>
|
||||
</style:style>
|
||||
<style:style style:name="T6" style:family="text">
|
||||
<style:text-properties style:text-underline-style="none"/>
|
||||
</style:style>
|
||||
<style:style style:name="T7" style:family="text">
|
||||
<style:text-properties style:text-underline-style="none" officeooo:rsid="003e177a"/>
|
||||
</style:style>
|
||||
<style:page-layout style:name="pm1">
|
||||
<style:page-layout-properties fo:page-width="27.94cm" fo:page-height="21.59cm" style:num-format="1" style:print-orientation="landscape" fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" fo:margin-right="2cm" style:writing-mode="lr-tb" style:layout-grid-color="#c0c0c0" style:layout-grid-lines="44" style:layout-grid-base-height="0.55cm" style:layout-grid-ruby-height="0cm" style:layout-grid-mode="none" style:layout-grid-ruby-below="false" style:layout-grid-print="true" style:layout-grid-display="true" style:footnote-max-height="0cm" loext:margin-gutter="0cm">
|
||||
<style:footnote-sep style:width="0.018cm" style:distance-before-sep="0.101cm" style:distance-after-sep="0.101cm" style:line-style="none" style:adjustment="left" style:rel-width="25%" style:color="#000000"/>
|
||||
</style:page-layout-properties>
|
||||
<style:header-style>
|
||||
<style:header-footer-properties fo:min-height="0cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-bottom="0.499cm"/>
|
||||
</style:header-style>
|
||||
<style:footer-style>
|
||||
<style:header-footer-properties fo:min-height="0cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-top="0.499cm"/>
|
||||
</style:footer-style>
|
||||
</style:page-layout>
|
||||
<style:style style:name="dp1" style:family="drawing-page">
|
||||
<style:drawing-page-properties draw:background-size="full"/>
|
||||
</style:style>
|
||||
</office:automatic-styles>
|
||||
<office:master-styles>
|
||||
<style:master-page style:name="Standard" style:page-layout-name="pm1" draw:style-name="dp1">
|
||||
<style:header>
|
||||
<table:table table:name="Table2" table:style-name="Table2">
|
||||
<table:table-column table:style-name="Table2.A" table:number-columns-repeated="3"/>
|
||||
<table:table-row table:style-name="TableLine94368948362784">
|
||||
<table:table-cell table:style-name="Table2.A1" office:value-type="string">
|
||||
<text:p text:style-name="Header">Company: <text:placeholder text:placeholder-type="text"><company.rec_name></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Table2.A1" office:value-type="string">
|
||||
<text:p text:style-name="P27">General Ledger Detail<text:placeholder text:placeholder-type="text"><if test="not context.get('posted')"></text:placeholder> <text:span text:style-name="T3">Draft</text:span><text:span text:style-name="T3"><text:placeholder text:placeholder-type="text"></if></text:placeholder></text:span></text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Table2.A1" office:value-type="string">
|
||||
<text:p text:style-name="P2">Print Date: <text:placeholder text:placeholder-type="text"><format_date(datetime.date.today(), user.language)></text:placeholder><text:s/>at <text:placeholder text:placeholder-type="text"><datetime.datetime.now().strftime('%H:%M:%S')></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
</table:table-row>
|
||||
</table:table>
|
||||
</style:header>
|
||||
<style:footer>
|
||||
<table:table table:name="Table1" table:style-name="Table1">
|
||||
<table:table-column table:style-name="Table1.A"/>
|
||||
<table:table-column table:style-name="Table1.B"/>
|
||||
<table:table-row table:style-name="TableLine94368949141280">
|
||||
<table:table-cell table:style-name="Table1.A1" office:value-type="string">
|
||||
<text:p text:style-name="Footer">User: <text:placeholder text:placeholder-type="text"><user.rec_name></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Table1.A1" office:value-type="string">
|
||||
<text:p text:style-name="P3"><text:page-number text:select-page="current">1</text:page-number>/<text:page-count>3</text:page-count></text:p>
|
||||
</table:table-cell>
|
||||
</table:table-row>
|
||||
</table:table>
|
||||
</style:footer>
|
||||
</style:master-page>
|
||||
</office:master-styles>
|
||||
<office:body>
|
||||
<office:text text:use-soft-page-breaks="true">
|
||||
<office:forms form:automatic-focus="false" form:apply-design-mode="false"/>
|
||||
<text:sequence-decls>
|
||||
<text:sequence-decl text:display-outline-level="0" text:name="Illustration"/>
|
||||
<text:sequence-decl text:display-outline-level="0" text:name="Table"/>
|
||||
<text:sequence-decl text:display-outline-level="0" text:name="Text"/>
|
||||
<text:sequence-decl text:display-outline-level="0" text:name="Drawing"/>
|
||||
<text:sequence-decl text:display-outline-level="0" text:name="Figure"/>
|
||||
</text:sequence-decls>
|
||||
<text:p text:style-name="P25">General Ledger Detail<text:placeholder text:placeholder-type="text"><if test="not context.get('posted')"></text:placeholder> <text:span text:style-name="T3">Draft</text:span><text:span text:style-name="T3"><text:placeholder text:placeholder-type="text"></if></text:placeholder></text:span></text:p>
|
||||
<text:p text:style-name="P23"><text:span text:style-name="T1">Fiscal Year: </text:span><text:placeholder text:placeholder-type="text"><fiscalyear.rec_name></text:placeholder></text:p>
|
||||
<text:p text:style-name="P23"><text:placeholder text:placeholder-type="text"><if test="start_period or end_period"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P23"><text:span text:style-name="T1">From Period</text:span>: <text:placeholder text:placeholder-type="text"><start_period.name if start_period else ''></text:placeholder><text:s/><text:span text:style-name="T2">To</text:span> <text:placeholder text:placeholder-type="text"><end_period.name if end_period else ''></text:placeholder></text:p>
|
||||
<text:p text:style-name="P23"><text:placeholder text:placeholder-type="text"></if></text:placeholder></text:p>
|
||||
<text:p text:style-name="P23"><text:placeholder text:placeholder-type="text"><if test="from_date or to_date"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P24">From Date: <text:placeholder text:placeholder-type="text"><format_date(from_date, user.language) if from_date else ''></text:placeholder><text:s/>To <text:placeholder text:placeholder-type="text"><format_date(to_date, user.language) if to_date else ''></text:placeholder></text:p>
|
||||
<text:p text:style-name="P23"><text:placeholder text:placeholder-type="text"></if></text:placeholder></text:p>
|
||||
<text:p text:style-name="P20"><text:placeholder text:placeholder-type="text"><for each="group in groups"></text:placeholder></text:p>
|
||||
<table:table table:name="Lines" table:style-name="Lines">
|
||||
<table:table-column table:style-name="Lines.A"/>
|
||||
<table:table-column table:style-name="Lines.B"/>
|
||||
<table:table-column table:style-name="Lines.C"/>
|
||||
<table:table-column table:style-name="Lines.B" table:number-columns-repeated="2"/>
|
||||
<table:table-column table:style-name="Lines.F" table:number-columns-repeated="2"/>
|
||||
<table:table-header-rows>
|
||||
<table:table-row table:style-name="Lines.1">
|
||||
<table:table-cell table:style-name="Lines.A1" table:number-columns-spanned="7" office:value-type="string">
|
||||
<text:p text:style-name="P5">Account / Currency</text:p>
|
||||
</table:table-cell>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
</table:table-row>
|
||||
<table:table-row table:style-name="Lines.1">
|
||||
<table:table-cell table:style-name="Lines.A2" table:number-columns-spanned="7" office:value-type="string">
|
||||
<text:p text:style-name="P7"><text:placeholder text:placeholder-type="text"><group['account']></text:placeholder><text:s/>/<text:s/><text:placeholder text:placeholder-type="text"><group['currency'].rec_name if group['currency'] else ''></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
</table:table-row>
|
||||
<table:table-row table:style-name="Lines.1">
|
||||
<table:table-cell table:style-name="Lines.A3" office:value-type="string"><text:p text:style-name="P6">Date</text:p></table:table-cell>
|
||||
<table:table-cell table:style-name="Lines.A3" office:value-type="string"><text:p text:style-name="P6">Journal</text:p></table:table-cell>
|
||||
<table:table-cell table:style-name="Lines.A3" office:value-type="string"><text:p text:style-name="P6">Entry / Doc.</text:p></table:table-cell>
|
||||
<table:table-cell table:style-name="Lines.A3" office:value-type="string"><text:p text:style-name="Table_20_Heading">Debit</text:p></table:table-cell>
|
||||
<table:table-cell table:style-name="Lines.A3" office:value-type="string"><text:p text:style-name="P6">Credit</text:p></table:table-cell>
|
||||
<table:table-cell table:style-name="Lines.A3" office:value-type="string"><text:p text:style-name="P6">Running</text:p></table:table-cell>
|
||||
<table:table-cell table:style-name="Lines.G3" office:value-type="string"><text:p text:style-name="P6">Description / Ref.</text:p></table:table-cell>
|
||||
</table:table-row>
|
||||
</table:table-header-rows>
|
||||
<table:table-row table:style-name="Lines.1">
|
||||
<table:table-cell table:style-name="Lines.A5" table:number-columns-spanned="7" office:value-type="string">
|
||||
<text:p text:style-name="P16"><text:placeholder text:placeholder-type="text"><for each="line in group['lines']"></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:covered-table-cell/><table:covered-table-cell/><table:covered-table-cell/><table:covered-table-cell/><table:covered-table-cell/><table:covered-table-cell/>
|
||||
</table:table-row>
|
||||
<table:table-row table:style-name="Lines.1">
|
||||
<table:table-cell table:style-name="Lines.A6" office:value-type="string"><text:p text:style-name="P33"><text:placeholder text:placeholder-type="text"><format_date(line.posting_date, user.language) if line.posting_date else ''></text:placeholder></text:p></table:table-cell>
|
||||
<table:table-cell table:style-name="Lines.B6" office:value-type="string"><text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text"><line.journal.rec_name if line.journal else ''></text:placeholder></text:p></table:table-cell>
|
||||
<table:table-cell table:style-name="Lines.C6" office:value-type="string"><text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text"><line.journal_entry_number or ''></text:placeholder><text:line-break/><text:placeholder text:placeholder-type="text"><line.document_number or line.voucher_number or ''></text:placeholder></text:p></table:table-cell>
|
||||
<table:table-cell table:style-name="Lines.D6" office:value-type="string"><text:p text:style-name="P32"><text:placeholder text:placeholder-type="text"><format_currency(line.debit_base_currency or 0, user.language, line.base_currency or company.currency)></text:placeholder></text:p></table:table-cell>
|
||||
<table:table-cell table:style-name="Lines.E6" office:value-type="string"><text:p text:style-name="P32"><text:placeholder text:placeholder-type="text"><format_currency(line.credit_base_currency or 0, user.language, line.base_currency or company.currency)></text:placeholder></text:p></table:table-cell>
|
||||
<table:table-cell table:style-name="Lines.F6" office:value-type="string"><text:p text:style-name="P32"><text:placeholder text:placeholder-type="text"><format_currency(line.running_balance_base_currency or 0, user.language, line.base_currency or company.currency)></text:placeholder></text:p></table:table-cell>
|
||||
<table:table-cell table:style-name="Lines.G6" office:value-type="string"><text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text"><line.description or line.move_description or ''></text:placeholder><text:line-break/><text:placeholder text:placeholder-type="text"><line.party.rec_name if line.party else ''></text:placeholder><text:line-break/><text:placeholder text:placeholder-type="text"><line.reference or ''></text:placeholder></text:p></table:table-cell>
|
||||
</table:table-row>
|
||||
<text:soft-page-break/>
|
||||
<table:table-row table:style-name="Lines.1">
|
||||
<table:table-cell table:style-name="Lines.A7" table:number-columns-spanned="7" office:value-type="string">
|
||||
<text:p text:style-name="P18"><text:placeholder text:placeholder-type="text"></for></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:covered-table-cell/><table:covered-table-cell/><table:covered-table-cell/><table:covered-table-cell/><table:covered-table-cell/><table:covered-table-cell/>
|
||||
</table:table-row>
|
||||
</table:table>
|
||||
<text:p text:style-name="P8"><text:placeholder text:placeholder-type="text"></for></text:placeholder></text:p>
|
||||
</office:text>
|
||||
</office:body>
|
||||
</office:document>
|
||||
@@ -1013,7 +1013,9 @@ class Line(DescriptionOriginMixin, MoveLineMixin, ModelSQL, ModelView):
|
||||
|
||||
journal = fields.Function(fields.Many2One('account.journal',"Journal"),'get_journal')
|
||||
|
||||
rate = fields.Function(fields.Numeric("Rate",digits=(1,6)),'get_rate')
|
||||
rate = fields.Numeric("Rate", digits=(10, 6), states={
|
||||
'readonly': _states['readonly'],
|
||||
})
|
||||
|
||||
del _states
|
||||
|
||||
@@ -1117,46 +1119,84 @@ class Line(DescriptionOriginMixin, MoveLineMixin, ModelSQL, ModelView):
|
||||
return line.move.id
|
||||
|
||||
def get_rate(self,name=None):
|
||||
if self.amount_second_currency:
|
||||
return round((self.credit if self.credit else self.debit) / abs(self.amount_second_currency),6)
|
||||
|
||||
@fields.depends(
|
||||
'debit', 'credit',
|
||||
'date','second_currency','amount_second_currency')
|
||||
def on_change_amount_second_currency(self):
|
||||
Currency = Pool().get('currency.currency')
|
||||
Date = Pool().get('ir.date')
|
||||
if self.second_currency != None and self.amount_second_currency != None and (not self.credit and not self.debit):
|
||||
tdate = Date.today()
|
||||
if self.date:
|
||||
tdate = self.date
|
||||
rate = Currency._get_rate([self.second_currency],tdate)
|
||||
if rate:
|
||||
rate = rate[self.second_currency.id]
|
||||
if self.amount_second_currency > 0:
|
||||
self.debit = round(rate * self.amount_second_currency,2)
|
||||
elif self.amount_second_currency > 0:
|
||||
self.credit = round(rate * abs(self.amount_second_currency),2)
|
||||
self.rate = self.get_rate()
|
||||
amount = self.credit if self.credit else self.debit
|
||||
amount_second_currency = getattr(self, 'amount_second_currency', None)
|
||||
if amount_second_currency and amount:
|
||||
return round(abs(amount_second_currency) / amount, 6)
|
||||
|
||||
def _set_rate_from_amounts(self):
|
||||
if (getattr(self, 'amount_second_currency', None)
|
||||
and not getattr(self, 'rate', None)):
|
||||
self.rate = self.get_rate()
|
||||
|
||||
@fields.depends(
|
||||
'debit', 'credit',
|
||||
'date','second_currency','amount_second_currency')
|
||||
'date','second_currency','amount_second_currency', 'rate',
|
||||
'origin', 'move_origin', 'move', '_parent_move.origin',
|
||||
'company', 'account')
|
||||
def on_change_second_currency(self):
|
||||
if self._manual_rate_mode():
|
||||
self.rate = self._get_second_currency_rate()
|
||||
self._compute_amount_from_second_currency()
|
||||
|
||||
@fields.depends(
|
||||
'debit', 'credit',
|
||||
'date','second_currency','amount_second_currency', 'rate',
|
||||
'origin', 'move_origin', 'move', '_parent_move.origin',
|
||||
'company', 'account')
|
||||
def on_change_rate(self):
|
||||
if self._manual_rate_mode() and not self.debit and not self.credit:
|
||||
self._compute_amount_from_second_currency()
|
||||
|
||||
def _manual_rate_mode(self):
|
||||
if getattr(self, 'origin', None) or getattr(self, 'move_origin', None):
|
||||
return False
|
||||
move = getattr(self, 'move', None)
|
||||
if move and getattr(move, 'origin', None):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _get_second_currency_rate(self):
|
||||
Currency = Pool().get('currency.currency')
|
||||
Date = Pool().get('ir.date')
|
||||
if self.second_currency != None and self.amount_second_currency != None and (not self.credit and not self.debit):
|
||||
tdate = Date.today()
|
||||
if self.date:
|
||||
tdate = self.date
|
||||
rate = Currency._get_rate([self.second_currency],tdate)
|
||||
if rate:
|
||||
rate = rate[self.second_currency.id]
|
||||
if self.second_currency is None:
|
||||
return None
|
||||
tdate = Date.today()
|
||||
if self.date:
|
||||
tdate = self.date
|
||||
rates = Currency._get_rate([self.second_currency], tdate)
|
||||
return rates.get(self.second_currency.id) if rates else None
|
||||
|
||||
def _get_company_currency(self):
|
||||
Company = Pool().get('company.company')
|
||||
for record in (self, getattr(self, 'move', None),
|
||||
getattr(self, 'account', None)):
|
||||
company = getattr(record, 'company', None)
|
||||
if company:
|
||||
return company.currency
|
||||
company_id = Transaction().context.get('company')
|
||||
if company_id:
|
||||
return Company(company_id).currency
|
||||
|
||||
def _compute_amount_from_second_currency(self):
|
||||
if self.second_currency is not None and self.amount_second_currency is not None:
|
||||
if not self.rate:
|
||||
self.rate = self._get_second_currency_rate()
|
||||
if self.rate:
|
||||
company_currency = self._get_company_currency()
|
||||
if not company_currency:
|
||||
return
|
||||
amount = company_currency.round(
|
||||
abs(self.amount_second_currency) / self.rate)
|
||||
if self.amount_second_currency > 0:
|
||||
self.debit = round(rate * self.amount_second_currency,2)
|
||||
elif self.amount_second_currency > 0:
|
||||
self.credit = round(rate * abs(self.amount_second_currency),2)
|
||||
self.rate = self.get_rate()
|
||||
self.debit = amount
|
||||
self.credit = Decimal(0)
|
||||
elif self.amount_second_currency < 0:
|
||||
self.debit = Decimal(0)
|
||||
self.credit = amount
|
||||
else:
|
||||
self.debit = Decimal(0)
|
||||
self.credit = Decimal(0)
|
||||
|
||||
@fields.depends(
|
||||
'move', 'debit', 'credit',
|
||||
@@ -1223,15 +1263,26 @@ class Line(DescriptionOriginMixin, MoveLineMixin, ModelSQL, ModelView):
|
||||
if self.debit:
|
||||
self.credit = Decimal(0)
|
||||
self._amount_second_currency_sign()
|
||||
self.rate = self.get_rate()
|
||||
|
||||
@fields.depends('debit', 'credit', 'amount_second_currency')
|
||||
def on_change_credit(self):
|
||||
if self.credit:
|
||||
self.debit = Decimal(0)
|
||||
self._amount_second_currency_sign()
|
||||
self.rate = self.get_rate()
|
||||
|
||||
@fields.depends('amount_second_currency', 'debit', 'credit')
|
||||
@fields.depends(
|
||||
'amount_second_currency', 'debit', 'credit', 'date', 'second_currency',
|
||||
'rate', 'origin', 'move_origin', 'move', '_parent_move.origin',
|
||||
'company', 'account')
|
||||
def on_change_amount_second_currency(self):
|
||||
if self._manual_rate_mode():
|
||||
if not self.rate:
|
||||
self.rate = self._get_second_currency_rate()
|
||||
self._compute_amount_from_second_currency()
|
||||
elif not self.rate:
|
||||
self.rate = self.get_rate()
|
||||
self._amount_second_currency_sign()
|
||||
|
||||
def _amount_second_currency_sign(self):
|
||||
@@ -1241,6 +1292,7 @@ class Line(DescriptionOriginMixin, MoveLineMixin, ModelSQL, ModelView):
|
||||
self.amount_second_currency = \
|
||||
self.amount_second_currency.copy_sign(
|
||||
(self.debit or 0) - (self.credit or 0))
|
||||
self._set_rate_from_amounts()
|
||||
|
||||
@fields.depends('account')
|
||||
def on_change_account(self):
|
||||
@@ -1545,6 +1597,14 @@ class Line(DescriptionOriginMixin, MoveLineMixin, ModelSQL, ModelView):
|
||||
for fname in move_fields(move_name=False):
|
||||
vals.setdefault(fname, None)
|
||||
lines = super(Line, cls).create(vlist)
|
||||
to_save = []
|
||||
for line in lines:
|
||||
if (getattr(line, 'amount_second_currency', None)
|
||||
and not getattr(line, 'rate', None)):
|
||||
line._set_rate_from_amounts()
|
||||
to_save.append(line)
|
||||
if to_save:
|
||||
cls.save(to_save)
|
||||
period_and_journals = set((line.period, line.journal)
|
||||
for line in lines)
|
||||
for period, journal in period_and_journals:
|
||||
|
||||
@@ -12,7 +12,7 @@ from trytond.modules.account.exceptions import (
|
||||
from trytond.modules.account.tax import TaxableMixin
|
||||
from trytond.modules.company.tests import (
|
||||
CompanyTestMixin, PartyCompanyCheckEraseMixin, create_company, set_company)
|
||||
from trytond.modules.currency.tests import create_currency
|
||||
from trytond.modules.currency.tests import add_currency_rate, create_currency
|
||||
from trytond.pool import Pool
|
||||
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
|
||||
from trytond.transaction import Transaction, inactive_records
|
||||
@@ -576,6 +576,54 @@ class AccountTestCase(
|
||||
self.assertEqual(
|
||||
cash_cur.amount_second_currency, Decimal(50))
|
||||
|
||||
@with_transaction()
|
||||
def test_move_line_second_currency_amount_on_change(self):
|
||||
'Test account move line computes amount from second currency'
|
||||
pool = Pool()
|
||||
Account = pool.get('account.account')
|
||||
Line = pool.get('account.move.line')
|
||||
|
||||
company = create_company()
|
||||
with set_company(company):
|
||||
create_chart(company)
|
||||
second_currency = create_currency('sec')
|
||||
add_currency_rate(second_currency, Decimal('0.939506'))
|
||||
|
||||
expense, = Account.search([
|
||||
('type.expense', '=', True),
|
||||
])
|
||||
|
||||
line = Line(
|
||||
account=expense,
|
||||
second_currency=second_currency,
|
||||
amount_second_currency=Decimal('100.00'))
|
||||
line.on_change_amount_second_currency()
|
||||
|
||||
self.assertEqual(line.debit, Decimal('106.44'))
|
||||
self.assertEqual(line.credit, Decimal(0))
|
||||
self.assertEqual(line.rate, Decimal('0.939506'))
|
||||
self.assertEqual(line.get_rate(), Decimal('0.939496'))
|
||||
|
||||
line = Line(
|
||||
account=expense,
|
||||
credit=Decimal('7.61'),
|
||||
second_currency=second_currency,
|
||||
amount_second_currency=Decimal('-108.10'))
|
||||
line.on_change_amount_second_currency()
|
||||
|
||||
self.assertEqual(line.debit, Decimal(0))
|
||||
self.assertEqual(line.credit, Decimal('115.06'))
|
||||
self.assertEqual(line.rate, Decimal('0.939506'))
|
||||
self.assertEqual(line.get_rate(), Decimal('0.939510'))
|
||||
|
||||
line.debit = line.credit = Decimal(0)
|
||||
line.rate = Decimal('1.100000')
|
||||
line.on_change_rate()
|
||||
|
||||
self.assertEqual(line.debit, Decimal(0))
|
||||
self.assertEqual(line.credit, Decimal('98.27'))
|
||||
self.assertEqual(line.rate, Decimal('1.100000'))
|
||||
|
||||
@with_transaction()
|
||||
def test_account_type_amount(self):
|
||||
"Test account type amount"
|
||||
@@ -2049,5 +2097,104 @@ class AccountTestCase(
|
||||
|
||||
self.assertEqual(account.parent, new_account)
|
||||
|
||||
@with_transaction()
|
||||
def test_general_ledger_detail_models_are_registered(self):
|
||||
"Test General Ledger Detail models are registered with expected fields"
|
||||
pool = Pool()
|
||||
Detail = pool.get('account.general_ledger.detail')
|
||||
DetailContext = pool.get('account.general_ledger.detail.context')
|
||||
DetailReport = pool.get(
|
||||
'account.general_ledger.detail.report', type='report')
|
||||
|
||||
for name in [
|
||||
'row_type', 'account_code', 'transaction_currency',
|
||||
'journal_entry_number', 'document_number',
|
||||
'debit_base_currency', 'credit_base_currency',
|
||||
'running_balance_base_currency',
|
||||
'debit_transaction_currency',
|
||||
'credit_transaction_currency',
|
||||
'running_balance_transaction_currency', 'payable_qty']:
|
||||
self.assertIn(name, Detail._fields)
|
||||
for name in [
|
||||
'debit_base_currency', 'credit_base_currency',
|
||||
'balance_base_currency', 'running_balance_base_currency',
|
||||
'debit_transaction_currency',
|
||||
'credit_transaction_currency',
|
||||
'balance_transaction_currency',
|
||||
'running_balance_transaction_currency']:
|
||||
self.assertEqual(Detail._fields[name].digits, (16, 2))
|
||||
self.assertEqual(Detail._fields['payable_qty'].digits, (16, 5))
|
||||
|
||||
for name in [
|
||||
'account', 'account_from', 'account_to', 'currency', 'party',
|
||||
'document_number']:
|
||||
self.assertIn(name, DetailContext._fields)
|
||||
|
||||
account = pool.get('account.account').__table__()
|
||||
account_label = Detail._account_label(account)
|
||||
self.assertIn('||', str(account_label))
|
||||
self.assertNotIn(' + ', str(account_label))
|
||||
self.assertEqual(account_label.params, ('', ' - ', ''))
|
||||
self.assertEqual(
|
||||
DetailReport.__name__, 'account.general_ledger.detail.report')
|
||||
|
||||
@with_transaction()
|
||||
def test_general_ledger_detail_report_groups_by_account_currency(self):
|
||||
"Test General Ledger Detail report groups lines by account and currency"
|
||||
DetailReport = Pool().get(
|
||||
'account.general_ledger.detail.report', type='report')
|
||||
currency = type('Currency', (), {'id': 1})()
|
||||
other_currency = type('Currency', (), {'id': 2})()
|
||||
first = type('Line', (), {
|
||||
'account_code': '1000 - Cash',
|
||||
'account_name': 'Cash',
|
||||
'transaction_currency': currency,
|
||||
})()
|
||||
second = type('Line', (), {
|
||||
'account_code': '1000 - Cash',
|
||||
'account_name': 'Cash',
|
||||
'transaction_currency': currency,
|
||||
})()
|
||||
third = type('Line', (), {
|
||||
'account_code': '2000 - Revenue',
|
||||
'account_name': 'Revenue',
|
||||
'transaction_currency': other_currency,
|
||||
})()
|
||||
|
||||
groups = DetailReport._groups([first, second, third])
|
||||
|
||||
self.assertEqual(len(groups), 2)
|
||||
self.assertEqual(groups[0]['account'], '1000 - Cash')
|
||||
self.assertEqual(groups[0]['currency'], currency)
|
||||
self.assertEqual(groups[0]['lines'], [first, second])
|
||||
self.assertEqual(groups[1]['account'], '2000 - Revenue')
|
||||
self.assertEqual(groups[1]['currency'], other_currency)
|
||||
self.assertEqual(groups[1]['lines'], [third])
|
||||
|
||||
@with_transaction()
|
||||
def test_general_ledger_detail_date_contexts(self):
|
||||
"Test General Ledger Detail opening and movement date contexts"
|
||||
Detail = Pool().get('account.general_ledger.detail')
|
||||
context = {
|
||||
'from_date': datetime.date(2026, 1, 10),
|
||||
'to_date': datetime.date(2026, 1, 31),
|
||||
}
|
||||
|
||||
self.assertEqual(Detail._query_context('opening', context), {
|
||||
'periods': None,
|
||||
'from_date': None,
|
||||
'to_date': datetime.date(2026, 1, 9),
|
||||
})
|
||||
self.assertEqual(Detail._query_context('movement', context), {
|
||||
'periods': None,
|
||||
'from_date': datetime.date(2026, 1, 10),
|
||||
'to_date': datetime.date(2026, 1, 31),
|
||||
})
|
||||
self.assertEqual(Detail._query_context('closing', context), {
|
||||
'periods': None,
|
||||
'from_date': None,
|
||||
'to_date': datetime.date(2026, 1, 31),
|
||||
})
|
||||
|
||||
|
||||
del ModuleTestCase
|
||||
|
||||
41
modules/account/view/general_ledger_detail_context_form.xml
Normal file
41
modules/account/view/general_ledger_detail_context_form.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<form>
|
||||
<label name="fiscalyear"/>
|
||||
<field name="fiscalyear"/>
|
||||
<label name="company"/>
|
||||
<field name="company"/>
|
||||
|
||||
<label name="journal"/>
|
||||
<field name="journal" widget="selection"/>
|
||||
<group col="-1" colspan="2" id="checkboxes">
|
||||
<label name="posted"/>
|
||||
<field name="posted"/>
|
||||
</group>
|
||||
|
||||
<label name="start_period"/>
|
||||
<field name="start_period"/>
|
||||
<label name="end_period"/>
|
||||
<field name="end_period"/>
|
||||
|
||||
<label name="from_date"/>
|
||||
<field name="from_date"/>
|
||||
<label name="to_date"/>
|
||||
<field name="to_date"/>
|
||||
|
||||
<label name="account"/>
|
||||
<field name="account"/>
|
||||
<label name="currency"/>
|
||||
<field name="currency"/>
|
||||
|
||||
<label name="account_from"/>
|
||||
<field name="account_from"/>
|
||||
<label name="account_to"/>
|
||||
<field name="account_to"/>
|
||||
|
||||
<label name="party"/>
|
||||
<field name="party"/>
|
||||
<label name="document_number"/>
|
||||
<field name="document_number"/>
|
||||
</form>
|
||||
39
modules/account/view/general_ledger_detail_list.xml
Normal file
39
modules/account/view/general_ledger_detail_list.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<tree>
|
||||
<field name="row_type"/>
|
||||
<field name="account_code" expand="1"/>
|
||||
<field name="transaction_currency"/>
|
||||
<field name="base_currency" optional="1"/>
|
||||
<field name="posting_date"/>
|
||||
<field name="journal"/>
|
||||
<field name="journal_entry_number"/>
|
||||
<field name="document_number"/>
|
||||
<field name="voucher_number" optional="1"/>
|
||||
<field name="document_type" optional="1"/>
|
||||
<field name="document_date" optional="1"/>
|
||||
<field name="posting_status" optional="1"/>
|
||||
<field name="party" optional="1"/>
|
||||
<field name="description" expand="1" optional="1"/>
|
||||
<field name="move_description" expand="1" optional="1"/>
|
||||
<field name="reference" optional="1"/>
|
||||
<field name="origin" optional="1"/>
|
||||
<field name="debit_base_currency" sum="1"/>
|
||||
<field name="credit_base_currency" sum="1"/>
|
||||
<field name="balance_base_currency" sum="1"/>
|
||||
<field name="running_balance_base_currency" optional="1"/>
|
||||
<field name="debit_transaction_currency" sum="1" optional="1"/>
|
||||
<field name="credit_transaction_currency" sum="1" optional="1"/>
|
||||
<field name="balance_transaction_currency" sum="1" optional="1"/>
|
||||
<field name="running_balance_transaction_currency" optional="1"/>
|
||||
<field name="payable_qty" sum="1" optional="1"/>
|
||||
<field name="uom" optional="1"/>
|
||||
<field name="entered_by" optional="1"/>
|
||||
<field name="entered_date" optional="1"/>
|
||||
<field name="modified_by" optional="1"/>
|
||||
<field name="modified_date" optional="1"/>
|
||||
<field name="move" optional="1"/>
|
||||
<field name="move_line" optional="1"/>
|
||||
<field name="company" optional="1"/>
|
||||
</tree>
|
||||
@@ -26,6 +26,8 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="amount_second_currency" symbol=""/>
|
||||
<label name="second_currency"/>
|
||||
<field name="second_currency"/>
|
||||
<label name="rate"/>
|
||||
<field name="rate"/>
|
||||
</page>
|
||||
<page name="tax_lines">
|
||||
<field name="tax_lines" colspan="4"/>
|
||||
|
||||
15
modules/account_ch_os/__init__.py
Normal file
15
modules/account_ch_os/__init__.py
Normal file
@@ -0,0 +1,15 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
from trytond.pool import Pool
|
||||
|
||||
from . import account
|
||||
|
||||
|
||||
def register():
|
||||
Pool.register(
|
||||
account.AccountTemplate,
|
||||
module='account_ch_os', type_='model')
|
||||
Pool.register(
|
||||
account.CreateChart,
|
||||
module='account_ch_os', type_='wizard')
|
||||
27
modules/account_ch_os/account.py
Normal file
27
modules/account_ch_os/account.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
|
||||
|
||||
class AccountTemplate(metaclass=PoolMeta):
|
||||
__name__ = 'account.account.template'
|
||||
|
||||
|
||||
class CreateChart(metaclass=PoolMeta):
|
||||
__name__ = 'account.create_chart'
|
||||
|
||||
def default_properties(self, fields):
|
||||
pool = Pool()
|
||||
ModelData = pool.get('ir.model.data')
|
||||
defaults = super().default_properties(fields)
|
||||
try:
|
||||
template_id = ModelData.get_id('account_ch_os', 'root')
|
||||
except KeyError:
|
||||
return defaults
|
||||
if self.account.account_template.id == template_id:
|
||||
defaults['account_receivable'] = self.get_account(
|
||||
'account_ch_os.acct_110000')
|
||||
defaults['account_payable'] = self.get_account(
|
||||
'account_ch_os.acct_200000')
|
||||
return defaults
|
||||
2395
modules/account_ch_os/account_ch_os.xml
Normal file
2395
modules/account_ch_os/account_ch_os.xml
Normal file
File diff suppressed because it is too large
Load Diff
2
modules/account_ch_os/tests/__init__.py
Normal file
2
modules/account_ch_os/tests/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
12
modules/account_ch_os/tests/test_module.py
Normal file
12
modules/account_ch_os/tests/test_module.py
Normal file
@@ -0,0 +1,12 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
from trytond.tests.test_tryton import ModuleTestCase
|
||||
|
||||
|
||||
class AccountCHOSTestCase(ModuleTestCase):
|
||||
'Test Account CH OS module'
|
||||
module = 'account_ch_os'
|
||||
|
||||
|
||||
del ModuleTestCase
|
||||
6
modules/account_ch_os/tryton.cfg
Normal file
6
modules/account_ch_os/tryton.cfg
Normal file
@@ -0,0 +1,6 @@
|
||||
[tryton]
|
||||
version=7.2.3
|
||||
depends:
|
||||
account
|
||||
xml:
|
||||
account_ch_os.xml
|
||||
3886
modules/account_invoice/commission__ict_final.fodt
Normal file
3886
modules/account_invoice/commission__ict_final.fodt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -485,7 +485,7 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
||||
})
|
||||
cls.__rpc__.update({
|
||||
'post': RPC(
|
||||
readonly=False, instantiate=0, fresh_session=True),
|
||||
readonly=False, instantiate=0, fresh_session=False),
|
||||
})
|
||||
|
||||
@classmethod
|
||||
@@ -515,7 +515,9 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
||||
~((table.state == 'cancelled') & (table.number == Null)),
|
||||
CharLength(table.number), table.number]
|
||||
|
||||
@fields.depends('selection_rate','rate_date')
|
||||
@fields.depends(
|
||||
'selection_rate', 'rate_date', 'currency', 'company', 'invoice_date',
|
||||
'lines')
|
||||
def on_change_with_rate(self, name=None):
|
||||
return self.get_selected_rate()
|
||||
|
||||
@@ -528,8 +530,8 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
||||
def get_selected_rate(self,name=None):
|
||||
Currency = Pool().get('currency.currency')
|
||||
Date = Pool().get('ir.date')
|
||||
company = self.company
|
||||
currency = self.currency
|
||||
company = getattr(self, 'company', None)
|
||||
currency = getattr(self, 'currency', None)
|
||||
|
||||
if not currency or not company:
|
||||
return None
|
||||
@@ -563,6 +565,16 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
||||
if rate:
|
||||
return round(1/rate,6) or 1
|
||||
|
||||
def _compute_company_currency_amount(self, amount, date=None):
|
||||
Currency = Pool().get('currency.currency')
|
||||
if self.currency == self.company.currency:
|
||||
return amount
|
||||
if self.rate:
|
||||
return self.company.currency.round(amount / self.rate)
|
||||
with Transaction().set_context(date=date or self.currency_date):
|
||||
return Currency.compute(
|
||||
self.currency, amount, self.company.currency)
|
||||
|
||||
@staticmethod
|
||||
def default_type():
|
||||
return Transaction().context.get('type', 'out')
|
||||
@@ -1218,6 +1230,7 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
||||
line.amount_second_currency = (
|
||||
line.amount_second_currency.copy_sign(
|
||||
line.debit - line.credit))
|
||||
line.rate = line.get_rate()
|
||||
line.account = self.account
|
||||
logger.info("_GET_MOVE_LINE_ACCOUNT:%s",line.account)
|
||||
if self.account.party_required:
|
||||
@@ -1286,14 +1299,16 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
||||
remainder = sum(l.debit - l.credit for l in move_lines)
|
||||
if self.payment_term:
|
||||
payment_date = self.payment_term_date or self.invoice_date or today
|
||||
model = str(self.lines[0].origin).split(",")[0] if self.lines[0].origin else None
|
||||
origin = self.lines[0].origin if self.lines and self.lines[0].origin else None
|
||||
model = str(origin).split(",")[0] if origin else None
|
||||
logger.info("MODEL:%s",model)
|
||||
line = None
|
||||
if model:
|
||||
Line = Pool().get(model)
|
||||
line = Line(int(str(self.lines[0].origin).split(",")[1]))
|
||||
line = Line(int(str(origin).split(",")[1]))
|
||||
logger.info("LINE:%s",line)
|
||||
term_lines = self.payment_term.compute(
|
||||
self.total_amount, self.currency, payment_date, line)
|
||||
term_lines = self.payment_term.compute(
|
||||
self.total_amount, self.currency, payment_date, line)
|
||||
else:
|
||||
term_lines = [(self.payment_term_date or today, self.total_amount)]
|
||||
past_payment_term_dates = []
|
||||
@@ -1890,17 +1905,17 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
||||
cls._check_taxes(invoices)
|
||||
# cls._check_similar(invoices)
|
||||
|
||||
invoices_in = cls.browse([i for i in invoices if i.type == 'in'])
|
||||
cls.set_number(invoices_in)
|
||||
cls.set_number(invoices)
|
||||
cls._store_cache(invoices)
|
||||
|
||||
moves = []
|
||||
for invoice in invoices:
|
||||
if invoice.type == 'in':
|
||||
move = invoice.get_move()
|
||||
if move != invoice.move:
|
||||
invoice.move = move
|
||||
moves.append(move)
|
||||
if invoice.currency != invoice.company.currency and not invoice.rate:
|
||||
invoice.rate = invoice.get_selected_rate()
|
||||
move = invoice.get_move()
|
||||
if move != invoice.move:
|
||||
invoice.move = move
|
||||
moves.append(move)
|
||||
invoice.do_lot_invoicing()
|
||||
if moves:
|
||||
Move.save(moves)
|
||||
@@ -1963,23 +1978,35 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
||||
move_line_.amount_second_currency = -original_amount
|
||||
|
||||
if amount < 0:
|
||||
lot_has_sale_invoice = (
|
||||
move_line.lot
|
||||
and (move_line.lot.sale_invoice_line
|
||||
or move_line.lot.sale_invoice_line_prov))
|
||||
move_line.debit = Decimal(0)
|
||||
move_line.credit = -amount
|
||||
move_line.account = gl.product.account_stock_used if not (move_line.lot.sale_invoice_line or move_line.lot.sale_invoice_line_prov) else gl.product.account_stock_out_used
|
||||
move_line.account = gl.product.account_stock_used if not lot_has_sale_invoice else gl.product.account_stock_out_used
|
||||
move_line.account = gl.product.account_cogs_used if gl.fee else move_line.account
|
||||
move_line_.credit = Decimal(0)
|
||||
move_line_.debit = -amount
|
||||
move_line_.account = gl.product.account_stock_in_used
|
||||
else:
|
||||
lot_has_sale_invoice = (
|
||||
move_line.lot
|
||||
and (move_line.lot.sale_invoice_line
|
||||
or move_line.lot.sale_invoice_line_prov))
|
||||
move_line.debit = amount
|
||||
move_line.credit = Decimal(0)
|
||||
move_line.account = gl.product.account_stock_used if not (move_line.lot.sale_invoice_line or move_line.lot.sale_invoice_line_prov) else gl.product.account_stock_out_used
|
||||
move_line.account = gl.product.account_stock_used if not lot_has_sale_invoice else gl.product.account_stock_out_used
|
||||
move_line.account = gl.product.account_cogs_used if gl.fee else move_line.account
|
||||
move_line_.debit = Decimal(0)
|
||||
move_line_.credit = amount
|
||||
move_line_.account = gl.product.account_stock_in_used
|
||||
|
||||
move_lines = [move_line,move_line_]
|
||||
if getattr(move_line, 'amount_second_currency', None):
|
||||
move_line.rate = move_line.get_rate()
|
||||
if getattr(move_line_, 'amount_second_currency', None):
|
||||
move_line_.rate = move_line_.get_rate()
|
||||
|
||||
if drop:
|
||||
drop_line = AccountMoveLine()
|
||||
@@ -2017,6 +2044,10 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
||||
drop_line_.second_currency = self.currency
|
||||
drop_line_.amount_second_currency = -original_amount
|
||||
move_lines = [drop_line,drop_line_]
|
||||
if getattr(drop_line, 'amount_second_currency', None):
|
||||
drop_line.rate = drop_line.get_rate()
|
||||
if getattr(drop_line_, 'amount_second_currency', None):
|
||||
drop_line_.rate = drop_line_.get_rate()
|
||||
|
||||
return move_lines
|
||||
|
||||
@@ -2038,10 +2069,16 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
||||
var_qt = sum([i.quantity for i in gl])
|
||||
logger.info("LOT_TO_PROCESS:%s",lot)
|
||||
logger.info("FEE_TO_PROCESS:%s",gl[0].fee)
|
||||
if (gl[0].fee and not gl[0].product.landed_cost):
|
||||
fee_correction = (
|
||||
gl[0].fee
|
||||
and any(Decimal(str(i.quantity or 0)) < 0 for i in gl)
|
||||
and any(Decimal(str(i.quantity or 0)) > 0 for i in gl))
|
||||
if (gl[0].fee and not gl[0].product.landed_cost
|
||||
and not fee_correction):
|
||||
diff = gl[0].fee.amount - gl[0].fee.get_non_cog(lot)
|
||||
account_move = gl[0].fee._get_account_move_fee(lot,'in',diff)
|
||||
Move.save([account_move])
|
||||
if account_move:
|
||||
Move.save([account_move])
|
||||
if (lot and not gl[0].fee) or (gl[0].fee and gl[0].product.landed_cost):
|
||||
adjust_move_lines = []
|
||||
mov = None
|
||||
@@ -3174,7 +3211,6 @@ class InvoiceLine(sequence_ordered(), ModelSQL, ModelView, TaxableMixin):
|
||||
|
||||
def _compute_taxes(self):
|
||||
pool = Pool()
|
||||
Currency = pool.get('currency.currency')
|
||||
TaxLine = pool.get('account.tax.line')
|
||||
|
||||
tax_lines = []
|
||||
@@ -3183,11 +3219,7 @@ class InvoiceLine(sequence_ordered(), ModelSQL, ModelView, TaxableMixin):
|
||||
taxes = self._get_taxes().values()
|
||||
for tax in taxes:
|
||||
amount = tax['base']
|
||||
with Transaction().set_context(
|
||||
date=self.invoice.currency_date):
|
||||
amount = Currency.compute(
|
||||
self.invoice.currency, amount,
|
||||
self.invoice.company.currency)
|
||||
amount = self.invoice._compute_company_currency_amount(amount)
|
||||
tax_line = TaxLine()
|
||||
tax_line.amount = amount
|
||||
tax_line.type = 'base'
|
||||
@@ -3252,6 +3284,7 @@ class InvoiceLine(sequence_ordered(), ModelSQL, ModelView, TaxableMixin):
|
||||
line.amount_second_currency = (
|
||||
line.amount_second_currency.copy_sign(
|
||||
line.debit - line.credit))
|
||||
line.rate = line.get_rate()
|
||||
line.account = self.account
|
||||
logger.info("GET_MOVE_LINES_ACCOUNT:%s",line.account)
|
||||
if self.account.party_required:
|
||||
@@ -3511,9 +3544,7 @@ class InvoiceTax(sequence_ordered(), ModelSQL, ModelView):
|
||||
'''
|
||||
Return a list of move lines instances for invoice tax
|
||||
'''
|
||||
Currency = Pool().get('currency.currency')
|
||||
pool = Pool()
|
||||
Currency = pool.get('currency.currency')
|
||||
MoveLine = pool.get('account.move.line')
|
||||
TaxLine = pool.get('account.tax.line')
|
||||
line = MoveLine()
|
||||
@@ -3521,11 +3552,8 @@ class InvoiceTax(sequence_ordered(), ModelSQL, ModelView):
|
||||
return []
|
||||
line.description = self.description
|
||||
if self.invoice.currency != self.invoice.company.currency:
|
||||
with Transaction().set_context(date=self.invoice.currency_date):
|
||||
amount = Currency.compute(self.invoice.currency, self.amount,
|
||||
self.invoice.company.currency)
|
||||
base = Currency.compute(self.invoice.currency, self.base,
|
||||
self.invoice.company.currency)
|
||||
amount = self.invoice._compute_company_currency_amount(self.amount)
|
||||
base = self.invoice._compute_company_currency_amount(self.base)
|
||||
line.amount_second_currency = self.amount
|
||||
line.second_currency = self.invoice.currency
|
||||
else:
|
||||
@@ -3547,6 +3575,7 @@ class InvoiceTax(sequence_ordered(), ModelSQL, ModelView):
|
||||
line.amount_second_currency = (
|
||||
line.amount_second_currency.copy_sign(
|
||||
line.debit - line.credit))
|
||||
line.rate = line.get_rate()
|
||||
line.account = self.account
|
||||
if self.account.party_required:
|
||||
line.party = self.invoice.party
|
||||
|
||||
@@ -288,7 +288,7 @@ this repository contains the full copyright notices and license terms. -->
|
||||
</record>
|
||||
|
||||
<record model="ir.action.report" id="report_invoice">
|
||||
<field name="name">Provisional Invoice</field>
|
||||
<field name="name">Invoice</field>
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="report_name">account.invoice</field>
|
||||
<field name="report">account_invoice/invoice.fodt</field>
|
||||
@@ -314,7 +314,7 @@ this repository contains the full copyright notices and license terms. -->
|
||||
</record>
|
||||
|
||||
<record model="ir.action.report" id="report_invoice_ict_final">
|
||||
<field name="name">Final Invoice</field>
|
||||
<field name="name">CN/DN</field>
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="report_name">account.invoice</field>
|
||||
<field name="report">account_invoice/invoice_ict_final.fodt</field>
|
||||
@@ -326,6 +326,45 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="action" ref="report_invoice_ict_final"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.report" id="report_invoice_ict_commission_final">
|
||||
<field name="name">CN/DN Commission</field>
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="report_name">account.invoice</field>
|
||||
<field name="report">account_invoice/commission__ict_final.fodt</field>
|
||||
<field name="single" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="report_invoice_ict_commission_final_keyword">
|
||||
<field name="keyword">form_print</field>
|
||||
<field name="model">account.invoice,-1</field>
|
||||
<field name="action" ref="report_invoice_ict_commission_final"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.report" id="report_sale_commission_ict">
|
||||
<field name="name">Commission invoice Sale</field>
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="report_name">account.invoice</field>
|
||||
<field name="report">account_invoice/sale_commission_ict.fodt</field>
|
||||
<field name="single" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="report_sale_commission_ict_keyword">
|
||||
<field name="keyword">form_print</field>
|
||||
<field name="model">account.invoice,-1</field>
|
||||
<field name="action" ref="report_sale_commission_ict"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.report" id="report_purchase_commission_ict">
|
||||
<field name="name">Commission invoice Purchase</field>
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="report_name">account.invoice</field>
|
||||
<field name="report">account_invoice/purchase_commission_ict.fodt</field>
|
||||
<field name="single" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="report_purchase_commission_ict_keyword">
|
||||
<field name="keyword">form_print</field>
|
||||
<field name="model">account.invoice,-1</field>
|
||||
<field name="action" ref="report_purchase_commission_ict"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.sequence.type" id="sequence_type_account_invoice">
|
||||
<field name="name">Invoice</field>
|
||||
</record>
|
||||
|
||||
@@ -2,19 +2,19 @@
|
||||
|
||||
<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:rpt="http://openoffice.org/2005/report" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:css3t="http://www.w3.org/TR/css3-text/" xmlns:officeooo="http://openoffice.org/2009/office" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
|
||||
<office:meta>
|
||||
<dc:title>Provisional Invoice</dc:title>
|
||||
<dc:title>Invoice</dc:title>
|
||||
<meta:initial-creator>willen</meta:initial-creator>
|
||||
|
||||
|
||||
|
||||
<meta:generator>LibreOffice/7.6.0.3$Windows_X86_64 LibreOffice_project/69edd8b8ebc41d00b4de3915dc82f8f0fc3b6265</meta:generator>
|
||||
<meta:editing-duration>PT3H16M29S</meta:editing-duration>
|
||||
<meta:editing-cycles>9</meta:editing-cycles>
|
||||
<meta:editing-duration>PT3H20M59S</meta:editing-duration>
|
||||
<meta:editing-cycles>12</meta:editing-cycles>
|
||||
|
||||
<dc:date>2026-03-27T07:35:45.157000000</dc:date><meta:document-statistic meta:table-count="10" meta:image-count="2" meta:object-count="0" meta:page-count="2" meta:paragraph-count="61" meta:word-count="210" meta:character-count="2997" meta:non-whitespace-character-count="2429"/></office:meta>
|
||||
<dc:date>2026-04-10T10:30:16.567000000</dc:date><meta:document-statistic meta:table-count="10" meta:image-count="2" meta:object-count="0" meta:page-count="2" meta:paragraph-count="66" meta:word-count="168" meta:character-count="2416" meta:non-whitespace-character-count="1896"/></office:meta>
|
||||
<office:settings>
|
||||
<config:config-item-set config:name="ooo:view-settings">
|
||||
<config:config-item config:name="ViewAreaTop" config:type="long">28575</config:config-item>
|
||||
<config:config-item config:name="ViewAreaTop" config:type="long">6879</config:config-item>
|
||||
<config:config-item config:name="ViewAreaLeft" config:type="long">0</config:config-item>
|
||||
<config:config-item config:name="ViewAreaWidth" config:type="long">27264</config:config-item>
|
||||
<config:config-item config:name="ViewAreaHeight" config:type="long">13187</config:config-item>
|
||||
@@ -23,12 +23,12 @@
|
||||
<config:config-item-map-indexed config:name="Views">
|
||||
<config:config-item-map-entry>
|
||||
<config:config-item config:name="ViewId" config:type="string">view2</config:config-item>
|
||||
<config:config-item config:name="ViewLeft" config:type="long">7583</config:config-item>
|
||||
<config:config-item config:name="ViewTop" config:type="long">33203</config:config-item>
|
||||
<config:config-item config:name="ViewLeft" config:type="long">13275</config:config-item>
|
||||
<config:config-item config:name="ViewTop" config:type="long">15563</config:config-item>
|
||||
<config:config-item config:name="VisibleLeft" config:type="long">0</config:config-item>
|
||||
<config:config-item config:name="VisibleTop" config:type="long">28575</config:config-item>
|
||||
<config:config-item config:name="VisibleTop" config:type="long">6879</config:config-item>
|
||||
<config:config-item config:name="VisibleRight" config:type="long">27263</config:config-item>
|
||||
<config:config-item config:name="VisibleBottom" config:type="long">41760</config:config-item>
|
||||
<config:config-item config:name="VisibleBottom" config:type="long">20064</config:config-item>
|
||||
<config:config-item config:name="ZoomType" config:type="short">0</config:config-item>
|
||||
<config:config-item config:name="ViewLayoutColumns" config:type="short">0</config:config-item>
|
||||
<config:config-item config:name="ViewLayoutBookMode" config:type="boolean">false</config:config-item>
|
||||
@@ -101,7 +101,7 @@
|
||||
<config:config-item config:name="LoadReadonly" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ClipAsCharacterAnchoredWriterFlyFrames" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="UseOldPrinterMetrics" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="Rsid" config:type="int">297701</config:config-item>
|
||||
<config:config-item config:name="Rsid" config:type="int">523469</config:config-item>
|
||||
<config:config-item config:name="RsidRoot" config:type="int">84510</config:config-item>
|
||||
<config:config-item config:name="ProtectForm" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="MsWordCompTrailingBlanks" config:type="boolean">false</config:config-item>
|
||||
@@ -168,19 +168,20 @@
|
||||
<style:font-face style:name="Lucida Sans1" svg:font-family="'Lucida Sans'" style:font-family-generic="system" style:font-pitch="variable"/>
|
||||
<style:font-face style:name="Microsoft YaHei" svg:font-family="'Microsoft YaHei'" style:font-family-generic="system" style:font-pitch="variable"/>
|
||||
<style:font-face style:name="Tahoma" svg:font-family="Tahoma, arial" style:font-family-generic="swiss" style:font-pitch="variable"/>
|
||||
<style:font-face style:name="Times New Roman" svg:font-family="'Times New Roman', Times" style:font-family-generic="roman" style:font-pitch="variable"/>
|
||||
<style:font-face style:name="Times New Roman" svg:font-family="'Times New Roman'" style:font-family-generic="roman" style:font-pitch="variable"/>
|
||||
<style:font-face style:name="Times New Roman1" svg:font-family="'Times New Roman', Times" style:font-family-generic="roman" style:font-pitch="variable"/>
|
||||
</office:font-face-decls>
|
||||
<office:styles>
|
||||
<style:default-style style:family="graphic">
|
||||
<style:graphic-properties svg:stroke-color="#808080" draw:fill-color="#cfe7f5" fo:wrap-option="no-wrap" draw:shadow-offset-x="0.3cm" draw:shadow-offset-y="0.3cm" draw:start-line-spacing-horizontal="0.283cm" draw:start-line-spacing-vertical="0.283cm" draw:end-line-spacing-horizontal="0.283cm" draw:end-line-spacing-vertical="0.283cm" style:writing-mode="lr-tb" style:flow-with-text="true"/>
|
||||
<style:paragraph-properties style:text-autospace="none" style:line-break="strict" loext:tab-stop-distance="0cm" style:font-independent-line-spacing="false">
|
||||
<style:paragraph-properties style:text-autospace="none" style:line-break="strict" loext:tab-stop-distance="0cm" style:writing-mode="lr-tb" style:font-independent-line-spacing="false">
|
||||
<style:tab-stops/>
|
||||
</style:paragraph-properties>
|
||||
<style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Times New Roman" fo:font-size="12pt" fo:language="fr" fo:country="CH" style:letter-kerning="true" style:font-name-asian="Times New Roman" style:font-size-asian="12pt" style:language-asian="fr" style:country-asian="CH" style:font-name-complex="Times New Roman" style:font-size-complex="12pt" style:language-complex="fr" style:country-complex="CH"/>
|
||||
<style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Times New Roman1" fo:font-size="12pt" fo:language="fr" fo:country="CH" style:letter-kerning="true" style:font-name-asian="Times New Roman1" style:font-size-asian="12pt" style:language-asian="fr" style:country-asian="CH" style:font-name-complex="Times New Roman1" style:font-size-complex="12pt" style:language-complex="fr" style:country-complex="CH"/>
|
||||
</style:default-style>
|
||||
<style:default-style style:family="paragraph">
|
||||
<style:paragraph-properties fo:hyphenation-ladder-count="no-limit" style:text-autospace="none" style:punctuation-wrap="hanging" style:line-break="strict" style:tab-stop-distance="1.249cm" style:writing-mode="lr-tb"/>
|
||||
<style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Times New Roman" fo:font-size="12pt" fo:language="fr" fo:country="CH" style:letter-kerning="true" style:font-name-asian="Times New Roman" style:font-size-asian="12pt" style:language-asian="fr" style:country-asian="CH" style:font-name-complex="Times New Roman" style:font-size-complex="12pt" style:language-complex="fr" style:country-complex="CH" fo:hyphenate="false" fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2" loext:hyphenation-no-caps="false" loext:hyphenation-no-last-word="false" loext:hyphenation-word-char-count="no-limit" loext:hyphenation-zone="no-limit"/>
|
||||
<style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Times New Roman1" fo:font-size="12pt" fo:language="fr" fo:country="CH" style:letter-kerning="true" style:font-name-asian="Times New Roman1" style:font-size-asian="12pt" style:language-asian="fr" style:country-asian="CH" style:font-name-complex="Times New Roman1" style:font-size-complex="12pt" style:language-complex="fr" style:country-complex="CH" fo:hyphenate="false" fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2" loext:hyphenation-no-caps="false" loext:hyphenation-no-last-word="false" loext:hyphenation-word-char-count="no-limit" loext:hyphenation-zone="no-limit"/>
|
||||
</style:default-style>
|
||||
<style:default-style style:family="table">
|
||||
<style:table-properties table:border-model="collapsing"/>
|
||||
@@ -245,7 +246,7 @@
|
||||
</style:style>
|
||||
<style:style style:name="No_20_Spacing" style:display-name="No Spacing" style:family="paragraph">
|
||||
<style:paragraph-properties fo:margin-left="0cm" fo:margin-right="0cm" fo:text-align="start" style:justify-single-word="false" fo:text-indent="0cm" style:auto-text-indent="false" style:text-autospace="ideograph-alpha" style:vertical-align="auto"/>
|
||||
<style:text-properties style:font-name="Calibri1" fo:font-family="Calibri, 'Century Gothic'" style:font-family-generic="swiss" style:font-pitch="variable" fo:font-size="11pt" fo:language="it" fo:country="IT" style:font-name-asian="Times New Roman" style:font-family-asian="'Times New Roman', Times" style:font-family-generic-asian="roman" style:font-pitch-asian="variable" style:font-size-asian="11pt" style:language-asian="en" style:country-asian="US" style:font-name-complex="Calibri1" style:font-family-complex="Calibri, 'Century Gothic'" style:font-family-generic-complex="swiss" style:font-pitch-complex="variable" style:font-size-complex="11pt" style:language-complex="ar" style:country-complex="SA"/>
|
||||
<style:text-properties style:font-name="Calibri1" fo:font-family="Calibri, 'Century Gothic'" style:font-family-generic="swiss" style:font-pitch="variable" fo:font-size="11pt" fo:language="it" fo:country="IT" style:font-name-asian="Times New Roman1" style:font-family-asian="'Times New Roman', Times" style:font-family-generic-asian="roman" style:font-pitch-asian="variable" style:font-size-asian="11pt" style:language-asian="en" style:country-asian="US" style:font-name-complex="Calibri1" style:font-family-complex="Calibri, 'Century Gothic'" style:font-family-generic-complex="swiss" style:font-pitch-complex="variable" style:font-size-complex="11pt" style:language-complex="ar" style:country-complex="SA"/>
|
||||
</style:style>
|
||||
<style:style style:name="Header_20_and_20_Footer" style:display-name="Header and Footer" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
|
||||
<style:paragraph-properties text:number-lines="false" text:line-number="0">
|
||||
@@ -295,30 +296,35 @@
|
||||
</style:paragraph-properties>
|
||||
<style:text-properties fo:font-size="10pt" style:font-size-asian="10pt" style:font-size-complex="10pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="Normal" style:family="paragraph">
|
||||
<style:paragraph-properties fo:hyphenation-ladder-count="no-limit"/>
|
||||
<style:text-properties fo:hyphenate="false" fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2" loext:hyphenation-no-caps="false" loext:hyphenation-no-last-word="false" loext:hyphenation-word-char-count="no-limit" loext:hyphenation-zone="no-limit"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Paragraph_20_Font" style:display-name="Default Paragraph Font" style:family="text"/>
|
||||
<style:style style:name="Titolo_20_1_20_Carattere" style:display-name="Titolo 1 Carattere" style:family="text" style:parent-style-name="Default_20_Paragraph_20_Font">
|
||||
<style:text-properties style:font-name="Arial1" fo:font-family="Arial, Arial" style:font-family-generic="swiss" style:font-pitch="variable" fo:font-size="12pt" fo:language="en" fo:country="US" style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color" fo:font-weight="bold" style:font-name-asian="Arial1" style:font-family-asian="Arial, Arial" style:font-family-generic-asian="swiss" style:font-pitch-asian="variable" style:font-size-asian="12pt" style:language-asian="en" style:country-asian="US" style:font-weight-asian="bold" style:font-name-complex="Arial1" style:font-family-complex="Arial, Arial" style:font-family-generic-complex="swiss" style:font-pitch-complex="variable" style:font-size-complex="12pt" style:font-weight-complex="bold"/>
|
||||
</style:style>
|
||||
<style:style style:name="Intestazione_20_Carattere" style:display-name="Intestazione Carattere" style:family="text" style:parent-style-name="Default_20_Paragraph_20_Font">
|
||||
<style:text-properties style:font-name="Times New Roman" fo:font-family="'Times New Roman', Times" style:font-family-generic="roman" style:font-pitch="variable" fo:font-size="10pt" style:font-name-asian="Times New Roman" style:font-family-asian="'Times New Roman', Times" style:font-family-generic-asian="roman" style:font-pitch-asian="variable" style:font-size-asian="10pt" style:font-name-complex="Times New Roman" style:font-family-complex="'Times New Roman', Times" style:font-family-generic-complex="roman" style:font-pitch-complex="variable" style:font-size-complex="10pt"/>
|
||||
<style:text-properties style:font-name="Times New Roman1" fo:font-family="'Times New Roman', Times" style:font-family-generic="roman" style:font-pitch="variable" fo:font-size="10pt" style:font-name-asian="Times New Roman1" style:font-family-asian="'Times New Roman', Times" style:font-family-generic-asian="roman" style:font-pitch-asian="variable" style:font-size-asian="10pt" style:font-name-complex="Times New Roman1" style:font-family-complex="'Times New Roman', Times" style:font-family-generic-complex="roman" style:font-pitch-complex="variable" style:font-size-complex="10pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="Placeholder_20_Text" style:display-name="Placeholder Text" style:family="text" style:parent-style-name="Default_20_Paragraph_20_Font">
|
||||
<style:text-properties fo:color="#808080" loext:opacity="100%" style:font-name="Times New Roman" fo:font-family="'Times New Roman', Times" style:font-family-generic="roman" style:font-pitch="variable" style:font-name-asian="Times New Roman" style:font-family-asian="'Times New Roman', Times" style:font-family-generic-asian="roman" style:font-pitch-asian="variable" style:font-name-complex="Times New Roman" style:font-family-complex="'Times New Roman', Times" style:font-family-generic-complex="roman" style:font-pitch-complex="variable"/>
|
||||
<style:text-properties fo:color="#808080" loext:opacity="100%" style:font-name="Times New Roman1" fo:font-family="'Times New Roman', Times" style:font-family-generic="roman" style:font-pitch="variable" style:font-name-asian="Times New Roman1" style:font-family-asian="'Times New Roman', Times" style:font-family-generic-asian="roman" style:font-pitch-asian="variable" style:font-name-complex="Times New Roman1" style:font-family-complex="'Times New Roman', Times" style:font-family-generic-complex="roman" style:font-pitch-complex="variable"/>
|
||||
</style:style>
|
||||
<style:style style:name="Testo_20_fumetto_20_Carattere" style:display-name="Testo fumetto Carattere" style:family="text" style:parent-style-name="Default_20_Paragraph_20_Font">
|
||||
<style:text-properties style:font-name="Tahoma" fo:font-family="Tahoma, arial" style:font-family-generic="swiss" style:font-pitch="variable" fo:font-size="8pt" style:font-name-asian="Tahoma" style:font-family-asian="Tahoma, arial" style:font-family-generic-asian="swiss" style:font-pitch-asian="variable" style:font-size-asian="8pt" style:font-name-complex="Tahoma" style:font-family-complex="Tahoma, arial" style:font-family-generic-complex="swiss" style:font-pitch-complex="variable" style:font-size-complex="8pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="PiÃ_a8__20_di_20_pagina_20_Carattere" style:display-name="Piè di pagina Carattere" style:family="text" style:parent-style-name="Default_20_Paragraph_20_Font">
|
||||
<style:text-properties style:font-name="Times New Roman" fo:font-family="'Times New Roman', Times" style:font-family-generic="roman" style:font-pitch="variable" style:font-name-asian="Times New Roman" style:font-family-asian="'Times New Roman', Times" style:font-family-generic-asian="roman" style:font-pitch-asian="variable" style:font-name-complex="Times New Roman" style:font-family-complex="'Times New Roman', Times" style:font-family-generic-complex="roman" style:font-pitch-complex="variable"/>
|
||||
<style:text-properties style:font-name="Times New Roman1" fo:font-family="'Times New Roman', Times" style:font-family-generic="roman" style:font-pitch="variable" style:font-name-asian="Times New Roman1" style:font-family-asian="'Times New Roman', Times" style:font-family-generic-asian="roman" style:font-pitch-asian="variable" style:font-name-complex="Times New Roman1" style:font-family-complex="'Times New Roman', Times" style:font-family-generic-complex="roman" style:font-pitch-complex="variable"/>
|
||||
</style:style>
|
||||
<style:style style:name="Nessuna_20_spaziatura_20_Carattere" style:display-name="Nessuna spaziatura Carattere" style:family="text" style:parent-style-name="Default_20_Paragraph_20_Font">
|
||||
<style:text-properties style:font-name="Calibri1" fo:font-family="Calibri, 'Century Gothic'" style:font-family-generic="swiss" style:font-pitch="variable" fo:language="it" fo:country="IT" style:font-name-asian="Times New Roman" style:font-family-asian="'Times New Roman', Times" style:font-family-generic-asian="roman" style:font-pitch-asian="variable" style:language-asian="en" style:country-asian="US" style:font-name-complex="Calibri1" style:font-family-complex="Calibri, 'Century Gothic'" style:font-family-generic-complex="swiss" style:font-pitch-complex="variable"/>
|
||||
<style:text-properties style:font-name="Calibri1" fo:font-family="Calibri, 'Century Gothic'" style:font-family-generic="swiss" style:font-pitch="variable" fo:language="it" fo:country="IT" style:font-name-asian="Times New Roman1" style:font-family-asian="'Times New Roman', Times" style:font-family-generic-asian="roman" style:font-pitch-asian="variable" style:language-asian="en" style:country-asian="US" style:font-name-complex="Calibri1" style:font-family-complex="Calibri, 'Century Gothic'" style:font-family-generic-complex="swiss" style:font-pitch-complex="variable"/>
|
||||
</style:style>
|
||||
<style:style style:name="Placeholder" style:family="text">
|
||||
<style:text-properties fo:font-variant="small-caps" fo:color="#008080" loext:opacity="100%" style:text-underline-style="dotted" style:text-underline-width="auto" style:text-underline-color="font-color"/>
|
||||
</style:style>
|
||||
<style:style style:name="Police_20_par_20_dÃ_a9_faut" style:display-name="Police par défaut" style:family="text"/>
|
||||
<style:style style:name="Default_20_Paragraph_20_Font_20__28_WW_29_" style:display-name="Default Paragraph Font (WW)" style:family="text"/>
|
||||
<style:style style:name="Police_20_par_20_défaut" style:display-name="Police par défaut" style:family="text"/>
|
||||
<style:style style:name="Frame" style:family="graphic">
|
||||
<style:graphic-properties text:anchor-type="paragraph" svg:x="0cm" svg:y="0cm" style:wrap="parallel" style:number-wrapped-paragraphs="no-limit" style:wrap-contour="false" style:vertical-pos="top" style:vertical-rel="paragraph-content" style:horizontal-pos="center" style:horizontal-rel="paragraph-content" fo:background-color="transparent" draw:fill="none" draw:fill-color="#cfe7f5"/>
|
||||
</style:style>
|
||||
@@ -664,7 +670,7 @@
|
||||
</style:style>
|
||||
<style:style style:name="P21" style:family="paragraph" style:parent-style-name="Standard">
|
||||
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" fo:line-height="100%" style:writing-mode="lr-tb"/>
|
||||
<style:text-properties fo:font-size="10pt" fo:language="en" fo:country="GB" style:font-size-asian="10pt" style:font-name-complex="Times New Roman" style:font-size-complex="10pt"/>
|
||||
<style:text-properties fo:font-size="10pt" fo:language="en" fo:country="GB" style:font-size-asian="10pt" style:font-name-complex="Times New Roman1" style:font-size-complex="10pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P22" style:family="paragraph" style:parent-style-name="Standard">
|
||||
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" fo:line-height="100%" style:writing-mode="lr-tb"/>
|
||||
@@ -684,7 +690,7 @@
|
||||
</style:style>
|
||||
<style:style style:name="P26" style:family="paragraph" style:parent-style-name="Standard">
|
||||
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" fo:line-height="100%" style:writing-mode="lr-tb"/>
|
||||
<style:text-properties fo:font-size="10pt" fo:language="en" fo:country="US" style:font-size-asian="10pt" style:font-name-complex="Times New Roman" style:font-size-complex="10pt"/>
|
||||
<style:text-properties fo:font-size="10pt" fo:language="en" fo:country="US" style:font-size-asian="10pt" style:font-name-complex="Times New Roman1" style:font-size-complex="10pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P27" style:family="paragraph" style:parent-style-name="Standard">
|
||||
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" fo:line-height="100%" style:writing-mode="lr-tb"/>
|
||||
@@ -696,7 +702,7 @@
|
||||
</style:style>
|
||||
<style:style style:name="P29" style:family="paragraph" style:parent-style-name="Standard">
|
||||
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" fo:line-height="100%" style:writing-mode="lr-tb"/>
|
||||
<style:text-properties fo:font-size="10pt" style:font-size-asian="10pt" style:font-name-complex="Times New Roman" style:font-size-complex="10pt"/>
|
||||
<style:text-properties fo:font-size="10pt" style:font-size-asian="10pt" style:font-name-complex="Times New Roman1" style:font-size-complex="10pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P30" style:family="paragraph" style:parent-style-name="Standard">
|
||||
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" fo:line-height="100%" fo:text-align="center" style:justify-single-word="false" style:writing-mode="lr-tb"/>
|
||||
@@ -708,7 +714,7 @@
|
||||
</style:style>
|
||||
<style:style style:name="P32" style:family="paragraph" style:parent-style-name="Standard">
|
||||
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" fo:line-height="100%" style:writing-mode="lr-tb"/>
|
||||
<style:text-properties fo:font-size="10pt" fo:language="fr" fo:country="CH" style:font-size-asian="10pt" style:font-name-complex="Times New Roman" style:font-size-complex="10pt"/>
|
||||
<style:text-properties fo:font-size="10pt" fo:language="fr" fo:country="CH" style:font-size-asian="10pt" style:font-name-complex="Times New Roman1" style:font-size-complex="10pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P33" style:family="paragraph" style:parent-style-name="Standard">
|
||||
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" fo:line-height="100%" fo:text-align="end" style:justify-single-word="false" style:writing-mode="lr-tb"/>
|
||||
@@ -724,7 +730,7 @@
|
||||
</style:style>
|
||||
<style:style style:name="P36" style:family="paragraph" style:parent-style-name="Standard">
|
||||
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" fo:line-height="100%" style:writing-mode="lr-tb"/>
|
||||
<style:text-properties fo:font-size="1pt" fo:language="en" fo:country="US" style:font-size-asian="1pt" style:font-name-complex="Times New Roman" style:font-size-complex="1pt" text:display="none"/>
|
||||
<style:text-properties fo:font-size="1pt" fo:language="en" fo:country="US" style:font-size-asian="1pt" style:font-name-complex="Times New Roman1" style:font-size-complex="1pt" text:display="none"/>
|
||||
</style:style>
|
||||
<style:style style:name="P37" style:family="paragraph" style:parent-style-name="Standard">
|
||||
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" fo:line-height="100%" style:writing-mode="lr-tb"/>
|
||||
@@ -759,14 +765,34 @@
|
||||
</style:paragraph-properties>
|
||||
<style:text-properties officeooo:paragraph-rsid="00022035"/>
|
||||
</style:style>
|
||||
<style:style style:name="P44" style:family="paragraph" style:parent-style-name="Standard">
|
||||
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" fo:line-height="100%" style:writing-mode="lr-tb"/>
|
||||
<style:text-properties fo:font-size="10pt" fo:language="en" fo:country="GB" officeooo:paragraph-rsid="000391f3" style:font-size-asian="10pt" style:font-name-complex="Arial1" style:font-size-complex="10pt"/>
|
||||
<style:style style:name="P44" style:family="paragraph" style:parent-style-name="Normal">
|
||||
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" fo:line-height="100%"/>
|
||||
<style:text-properties fo:color="#000000" loext:opacity="100%" style:font-name="Arial" fo:font-size="10pt" officeooo:paragraph-rsid="00056da2" style:letter-kerning="false" style:font-name-asian="Times New Roman" style:font-size-asian="10pt" style:font-name-complex="Arial" style:font-size-complex="10pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P45" style:family="paragraph" style:parent-style-name="footer">
|
||||
<style:style style:name="P45" style:family="paragraph" style:parent-style-name="Normal">
|
||||
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" fo:line-height="100%"/>
|
||||
<style:text-properties fo:color="#000000" loext:opacity="100%" style:font-name="Calibri" fo:font-size="10pt" officeooo:paragraph-rsid="00056da2" style:letter-kerning="false" style:font-name-asian="Times New Roman" style:font-size-asian="10pt" style:font-name-complex="Arial" style:font-size-complex="10pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P46" style:family="paragraph" style:parent-style-name="Normal">
|
||||
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" fo:line-height="100%"/>
|
||||
<style:text-properties officeooo:paragraph-rsid="00056da2"/>
|
||||
</style:style>
|
||||
<style:style style:name="P47" style:family="paragraph" style:parent-style-name="Normal">
|
||||
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" fo:line-height="100%"/>
|
||||
<style:text-properties style:font-name="Calibri" fo:font-size="10pt" officeooo:paragraph-rsid="00056da2" style:font-size-asian="10pt" style:font-size-complex="10pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P48" style:family="paragraph" style:parent-style-name="Standard">
|
||||
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" fo:line-height="100%" style:writing-mode="lr-tb"/>
|
||||
<style:text-properties style:font-name="Calibri" fo:font-size="10pt" fo:language="fr" fo:country="CH" style:font-size-asian="10pt" style:font-name-complex="Arial1" style:font-size-complex="10pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P49" style:family="paragraph" style:parent-style-name="Standard">
|
||||
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" fo:line-height="100%" style:writing-mode="lr-tb"/>
|
||||
<style:text-properties fo:font-size="10pt" style:font-size-asian="10pt" style:font-name-complex="Times New Roman1" style:font-size-complex="10pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P50" style:family="paragraph" style:parent-style-name="footer">
|
||||
<style:paragraph-properties style:writing-mode="lr-tb"/>
|
||||
</style:style>
|
||||
<style:style style:name="P46" style:family="paragraph" style:parent-style-name="header">
|
||||
<style:style style:name="P51" style:family="paragraph" style:parent-style-name="header">
|
||||
<style:paragraph-properties style:writing-mode="lr-tb"/>
|
||||
</style:style>
|
||||
<style:style style:name="T1" style:family="text">
|
||||
@@ -790,6 +816,24 @@
|
||||
<style:style style:name="T7" style:family="text">
|
||||
<style:text-properties fo:language="fr" fo:country="CH"/>
|
||||
</style:style>
|
||||
<style:style style:name="T8" style:family="text">
|
||||
<style:text-properties fo:color="#000000" loext:opacity="100%" style:font-name="Arial" fo:font-size="9pt" style:letter-kerning="false" style:font-name-asian="Times New Roman" style:font-size-asian="9pt" style:font-name-complex="Arial" style:font-size-complex="9pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="T9" style:family="text">
|
||||
<style:text-properties fo:color="#000000" loext:opacity="100%" style:font-name="Arial" fo:font-size="9pt" officeooo:rsid="00056da2" style:letter-kerning="false" style:font-name-asian="Times New Roman" style:font-size-asian="9pt" style:font-name-complex="Arial" style:font-size-complex="9pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="T10" style:family="text">
|
||||
<style:text-properties fo:color="#000000" loext:opacity="100%" fo:font-size="9pt" style:letter-kerning="false" style:font-name-asian="Times New Roman" style:font-size-asian="9pt" style:font-name-complex="Arial" style:font-size-complex="9pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="T11" style:family="text">
|
||||
<style:text-properties fo:color="#000000" loext:opacity="100%" fo:font-size="9pt" officeooo:rsid="00056da2" style:letter-kerning="false" style:font-name-asian="Times New Roman" style:font-size-asian="9pt" style:font-name-complex="Arial" style:font-size-complex="9pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="T12" style:family="text">
|
||||
<style:text-properties fo:color="#000000" loext:opacity="100%" style:letter-kerning="false" style:font-name-asian="Times New Roman" style:font-name-complex="Arial"/>
|
||||
</style:style>
|
||||
<style:style style:name="T13" style:family="text">
|
||||
<style:text-properties fo:color="#000000" loext:opacity="100%" officeooo:rsid="00056da2" style:letter-kerning="false" style:font-name-asian="Times New Roman" style:font-name-complex="Arial"/>
|
||||
</style:style>
|
||||
<style:style style:name="fr1" style:family="graphic" style:parent-style-name="Graphics">
|
||||
<style:graphic-properties style:run-through="background" style:wrap="run-through" style:number-wrapped-paragraphs="no-limit" style:vertical-pos="top" style:vertical-rel="baseline" style:mirror="none" fo:clip="rect(0cm, 0cm, 0cm, 0cm)" draw:luminance="0%" draw:contrast="0%" draw:red="0%" draw:green="0%" draw:blue="0%" draw:gamma="100%" draw:color-inversion="false" draw:image-opacity="100%" draw:color-mode="standard"/>
|
||||
</style:style>
|
||||
@@ -3866,7 +3910,7 @@
|
||||
<table:table-row table:style-name="Tableau3.1">
|
||||
<table:table-cell table:style-name="Tableau3.A1" office:value-type="string">
|
||||
<text:p text:style-name="P22"/>
|
||||
<text:p text:style-name="P22">Provisional Invoice</text:p>
|
||||
<text:p text:style-name="P22">Invoice</text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Tableau3.A1" office:value-type="string">
|
||||
<text:p text:style-name="P22"/>
|
||||
@@ -3930,15 +3974,19 @@
|
||||
<text:p text:style-name="P25">Goods description</text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Tableau5.A1" office:value-type="string">
|
||||
<text:p text:style-name="P26">QUANTITY: <text:placeholder text:placeholder-type="text"><format_number(invoice.report_lbs, invoice.party.lang) if invoice.report_lbs != '' else ''></text:placeholder><text:s/>LBS (<text:placeholder text:placeholder-type="text"><format_number(invoice.report_net, invoice.party.lang) if invoice.report_net != '' else ''></text:placeholder> MTS)</text:p>
|
||||
<text:p text:style-name="P21"><text:placeholder text:placeholder-type="text"><invoice.report_description_upper or invoice.report_product_description></text:placeholder><text:s/>CROP <text:placeholder text:placeholder-type="text"><invoice.report_crop_name></text:placeholder></text:p>
|
||||
<text:p text:style-name="P26">QUANTITY: <text:placeholder text:placeholder-type="text"><invoice.report_lbs_display></text:placeholder><text:s/>LBS (<text:placeholder text:placeholder-type="text"><invoice.report_net_display></text:placeholder> <text:placeholder text:placeholder-type="text"><invoice.report_weight_unit_upper></text:placeholder>)</text:p>
|
||||
<text:p text:style-name="P26"/>
|
||||
<text:p text:style-name="P21"><text:placeholder text:placeholder-type="text"><invoice.report_description_upper or invoice.report_product_description></text:placeholder><text:placeholder text:placeholder-type="text"><' CROP ' + invoice.report_crop_name if invoice.report_crop_name else ''></text:placeholder></text:p>
|
||||
<text:p text:style-name="P21"><text:placeholder text:placeholder-type="text"><invoice.report_attributes_name></text:placeholder></text:p>
|
||||
<text:p text:style-name="P18">At <text:placeholder text:placeholder-type="text"><invoice.report_rate_currency_upper></text:placeholder><text:s/><text:placeholder text:placeholder-type="text"><invoice.report_rate_value></text:placeholder><text:s/>PER <text:placeholder text:placeholder-type="text"><invoice.report_rate_unit_upper></text:placeholder><text:s/>(<text:placeholder text:placeholder-type="text"><invoice.report_rate_price_words></text:placeholder>) <text:placeholder text:placeholder-type="text"><invoice.report_rate_pricing_text></text:placeholder></text:p>
|
||||
<text:p text:style-name="P26"><text:placeholder text:placeholder-type="text"><for each="block in invoice.report_trade_blocks"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P26"><text:placeholder text:placeholder-type="text"><block[0]></text:placeholder></text:p>
|
||||
<text:p text:style-name="P18">At <text:placeholder text:placeholder-type="text"><block[1]></text:placeholder></text:p>
|
||||
<text:p text:style-name="P18"/>
|
||||
<text:p text:style-name="P26"><text:placeholder text:placeholder-type="text"></for></text:placeholder></text:p>
|
||||
<text:p text:style-name="P18"/>
|
||||
<text:p text:style-name="P18"/>
|
||||
<text:p text:style-name="P32"><text:placeholder text:placeholder-type="text"><invoice.report_incoterm></text:placeholder></text:p>
|
||||
<text:p text:style-name="P29"><text:span text:style-name="T1">ALL DETAILS AND SPECIFICATIONS AS PER</text:span> <text:span text:style-name="T3">BENEFICIARY </text:span></text:p>
|
||||
<text:p text:style-name="P26">PROFORMA INVOICE NO. <text:placeholder text:placeholder-type="text"><invoice.report_proforma_invoice_number></text:placeholder><text:s/>DATED <text:placeholder text:placeholder-type="text"><format_date(invoice.report_proforma_invoice_date, invoice.party.lang) if invoice.report_proforma_invoice_date else ''></text:placeholder>.</text:p>
|
||||
<text:p text:style-name="P29"><text:span text:style-name="T7"><text:placeholder text:placeholder-type="text"><invoice.report_incoterm></text:placeholder></text:span><text:span text:style-name="T3"><text:s/></text:span></text:p>
|
||||
<text:p text:style-name="P26"/>
|
||||
<text:p text:style-name="P26"/>
|
||||
<text:p text:style-name="P12"/>
|
||||
</table:table-cell>
|
||||
@@ -3952,10 +4000,10 @@
|
||||
<text:p text:style-name="P15"><text:s text:c="19"/>BALES</text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Tableau6.A1" office:value-type="string">
|
||||
<text:p text:style-name="P41"><text:s text:c="13"/>Gross KGS</text:p>
|
||||
<text:p text:style-name="P41"><text:s text:c="13"/>Gross <text:placeholder text:placeholder-type="text"><invoice.report_weight_unit_upper></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Tableau6.A1" office:value-type="string">
|
||||
<text:p text:style-name="P15"><text:s text:c="13"/>NET KGS</text:p>
|
||||
<text:p text:style-name="P15"><text:s text:c="13"/>NET <text:placeholder text:placeholder-type="text"><invoice.report_weight_unit_upper></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Tableau6.A1" office:value-type="string">
|
||||
<text:p text:style-name="P41"><text:s text:c="10"/></text:p>
|
||||
@@ -3969,10 +4017,10 @@
|
||||
<text:p text:style-name="P15"><text:placeholder text:placeholder-type="text"><invoice.report_nb_bale></text:placeholder><text:s/></text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Tableau6.A2" office:value-type="string">
|
||||
<text:p text:style-name="P15"><text:placeholder text:placeholder-type="text"><format_number(invoice.report_gross, invoice.party.lang) if invoice.report_gross != '' else ''></text:placeholder><text:s/></text:p>
|
||||
<text:p text:style-name="P15"><text:placeholder text:placeholder-type="text"><invoice.report_gross_display></text:placeholder><text:s/></text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Tableau6.A2" office:value-type="string">
|
||||
<text:p text:style-name="P30"><text:placeholder text:placeholder-type="text"><format_number(invoice.report_net, invoice.party.lang) if invoice.report_net != '' else ''></text:placeholder><text:s/></text:p>
|
||||
<text:p text:style-name="P30"><text:placeholder text:placeholder-type="text"><invoice.report_net_display></text:placeholder><text:s/></text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Tableau6.A2" office:value-type="string">
|
||||
<text:p text:style-name="P16"/>
|
||||
@@ -3996,7 +4044,7 @@
|
||||
<text:p text:style-name="P16">Equivalent to LBS</text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Tableau7.A1" office:value-type="string">
|
||||
<text:p text:style-name="P16"><text:placeholder text:placeholder-type="text"><format_number(invoice.report_lbs, invoice.party.lang) if invoice.report_lbs != '' else ''></text:placeholder><text:s/></text:p>
|
||||
<text:p text:style-name="P16"><text:placeholder text:placeholder-type="text"><invoice.report_lbs_display></text:placeholder><text:s/></text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Tableau7.A1" office:value-type="string">
|
||||
<text:p text:style-name="P20"/>
|
||||
@@ -4009,13 +4057,18 @@
|
||||
<table:table-column table:style-name="Tableau8.B"/>
|
||||
<table:table-row table:style-name="Tableau8.1">
|
||||
<table:table-cell table:style-name="Tableau8.A1" office:value-type="string">
|
||||
<text:p text:style-name="P14">At <text:placeholder text:placeholder-type="text"><invoice.report_rate_currency_upper></text:placeholder><text:s/><text:placeholder text:placeholder-type="text"><invoice.report_rate_value></text:placeholder><text:s/>PER <text:placeholder text:placeholder-type="text"><invoice.report_rate_unit_upper></text:placeholder><text:s/>(<text:placeholder text:placeholder-type="text"><invoice.report_rate_price_words></text:placeholder>) <text:placeholder text:placeholder-type="text"><invoice.report_rate_pricing_text></text:placeholder></text:p>
|
||||
<text:p text:style-name="P14"><text:placeholder text:placeholder-type="text"><for each="line in invoice.report_rate_lines.splitlines()"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P14">At <text:placeholder text:placeholder-type="text"><line></text:placeholder></text:p>
|
||||
<text:p text:style-name="P14"><text:placeholder text:placeholder-type="text"></for></text:placeholder></text:p>
|
||||
<text:p text:style-name="P14"/>
|
||||
<text:p text:style-name="P14">FREIGHT VALUE: <text:placeholder text:placeholder-type="text"><invoice.report_freight_currency_symbol></text:placeholder><text:s/><text:placeholder text:placeholder-type="text"><format_number(invoice.report_freight_amount, invoice.party.lang) if invoice.report_freight_amount != '' else ''></text:placeholder></text:p>
|
||||
<text:p text:style-name="P14"/>
|
||||
<text:p text:style-name="P17">WE CERTIFY THAT THE MERCHANDISE IS OF <text:span text:style-name="T7"><text:placeholder text:placeholder-type="text"><invoice.report_origin or ''></text:placeholder></text:span><text:s/>ORIGIN</text:p>
|
||||
<text:p text:style-name="P31"><text:soft-page-break/>L/C NUMBER </text:p>
|
||||
<text:p text:style-name="P31"/>
|
||||
<text:p text:style-name="P17"/>
|
||||
<text:p text:style-name="P47"><text:span text:style-name="T12">B</text:span><text:span text:style-name="T13">ANK</text:span><text:span text:style-name="T12">: EFG BANK SA</text:span></text:p>
|
||||
<text:p text:style-name="P45">IBAN : CH8808667007168111027</text:p>
|
||||
<text:p text:style-name="P45">SwifT Code: EFGBCHZZ</text:p>
|
||||
<text:p text:style-name="P48"/>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Tableau8.A1" office:value-type="string">
|
||||
<text:p text:style-name="P34"><text:s text:c="3"/><text:placeholder text:placeholder-type="text"><format_currency(invoice.total_amount, invoice.party.lang, invoice.currency)></text:placeholder><text:s/></text:p>
|
||||
@@ -4027,6 +4080,7 @@
|
||||
<text:p text:style-name="P11"/>
|
||||
<table:table table:name="Tableau9" table:style-name="Tableau9">
|
||||
<table:table-column table:style-name="Tableau9.A"/>
|
||||
<text:soft-page-break/>
|
||||
<table:table-row table:style-name="Tableau9.1">
|
||||
<table:table-cell table:style-name="Tableau9.A1" office:value-type="string">
|
||||
<text:p text:style-name="P21">NET LANDED WEIGHTS, ACTUAL TARE, NO FRANCHISE</text:p>
|
||||
@@ -4052,7 +4106,7 @@
|
||||
<text:p text:style-name="P13">Controller Name</text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Tableau10.A1" office:value-type="string">
|
||||
<text:p text:style-name="P25"><text:placeholder text:placeholder-type="text"><invoice.report_si_number></text:placeholder></text:p>
|
||||
<text:p text:style-name="P25"><text:placeholder text:placeholder-type="text"><invoice.report_si_reference></text:placeholder></text:p>
|
||||
<text:p text:style-name="P25"/>
|
||||
<text:p text:style-name="P25"><text:placeholder text:placeholder-type="text"><invoice.report_controller_name></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
@@ -4100,4 +4154,4 @@
|
||||
<text:p text:style-name="P38"/>
|
||||
</office:text>
|
||||
</office:body>
|
||||
</office:document>
|
||||
</office:document>
|
||||
File diff suppressed because it is too large
Load Diff
4180
modules/account_invoice/invoice_ict_final.fodt.bak
Normal file
4180
modules/account_invoice/invoice_ict_final.fodt.bak
Normal file
File diff suppressed because it is too large
Load Diff
1775
modules/account_invoice/invoice_melya.fodt
Normal file
1775
modules/account_invoice/invoice_melya.fodt
Normal file
File diff suppressed because it is too large
Load Diff
1498
modules/account_invoice/packing_list.fodt
Normal file
1498
modules/account_invoice/packing_list.fodt
Normal file
File diff suppressed because it is too large
Load Diff
1872
modules/account_invoice/payment_order_melya.fodt
Normal file
1872
modules/account_invoice/payment_order_melya.fodt
Normal file
File diff suppressed because it is too large
Load Diff
3892
modules/account_invoice/purchase_commission_ict.fodt
Normal file
3892
modules/account_invoice/purchase_commission_ict.fodt
Normal file
File diff suppressed because it is too large
Load Diff
3730
modules/account_invoice/sale_commission_ict.fodt
Normal file
3730
modules/account_invoice/sale_commission_ict.fodt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,7 @@
|
||||
|
||||
import datetime
|
||||
from decimal import Decimal
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from trytond.modules.account_invoice.exceptions import (
|
||||
PaymentTermValidationError)
|
||||
@@ -251,5 +252,70 @@ class AccountInvoiceTestCase(
|
||||
(datetime.date(2012, 1, 14), Decimal('-1.0')),
|
||||
])
|
||||
|
||||
def test_post_rpc_does_not_require_fresh_session(self):
|
||||
'posting invoices does not force a fresh session'
|
||||
Invoice = Pool().get('account.invoice')
|
||||
|
||||
self.assertFalse(Invoice.__rpc__['post'].fresh_session)
|
||||
|
||||
@with_transaction()
|
||||
def test_validate_invoice_creates_move_for_customer_invoice(self):
|
||||
'validating customer invoices now creates the account move'
|
||||
Invoice = Pool().get('account.invoice')
|
||||
|
||||
move = Mock()
|
||||
invoice = Invoice()
|
||||
invoice.type = 'out'
|
||||
invoice.move = None
|
||||
invoice.get_move = Mock(return_value=move)
|
||||
invoice.do_lot_invoicing = Mock()
|
||||
|
||||
move_model = Mock()
|
||||
|
||||
with patch.object(Invoice, '_check_taxes'), patch.object(
|
||||
Invoice, '_store_cache'), patch.object(
|
||||
Invoice, 'browse', return_value=[]), patch.object(
|
||||
Invoice, 'cleanMoves') as clean_moves, patch.object(
|
||||
Invoice, 'save') as save_invoices, patch(
|
||||
'trytond.modules.account_invoice.invoice.Pool'
|
||||
) as PoolMock:
|
||||
PoolMock.return_value.get.return_value = move_model
|
||||
|
||||
Invoice.validate_invoice([invoice])
|
||||
|
||||
self.assertIs(invoice.move, move)
|
||||
invoice.get_move.assert_called_once_with()
|
||||
invoice.do_lot_invoicing.assert_called_once_with()
|
||||
move_model.save.assert_called_once_with([move])
|
||||
clean_moves.assert_called_once_with([move])
|
||||
save_invoices.assert_called()
|
||||
|
||||
@with_transaction()
|
||||
def test_validate_invoice_sets_number_for_customer_invoice(self):
|
||||
'validating customer invoices now assigns the invoice number'
|
||||
Invoice = Pool().get('account.invoice')
|
||||
|
||||
move = Mock()
|
||||
invoice = Invoice()
|
||||
invoice.type = 'out'
|
||||
invoice.move = None
|
||||
invoice.get_move = Mock(return_value=move)
|
||||
invoice.do_lot_invoicing = Mock()
|
||||
|
||||
move_model = Mock()
|
||||
|
||||
with patch.object(Invoice, '_check_taxes'), patch.object(
|
||||
Invoice, '_store_cache'), patch.object(
|
||||
Invoice, 'set_number') as set_number, patch.object(
|
||||
Invoice, 'cleanMoves'), patch.object(
|
||||
Invoice, 'save'), patch(
|
||||
'trytond.modules.account_invoice.invoice.Pool'
|
||||
) as PoolMock:
|
||||
PoolMock.return_value.get.return_value = move_model
|
||||
|
||||
Invoice.validate_invoice([invoice])
|
||||
|
||||
set_number.assert_called_once_with([invoice])
|
||||
|
||||
|
||||
del ModuleTestCase
|
||||
|
||||
@@ -181,7 +181,11 @@ class InvoiceLine(metaclass=PoolMeta):
|
||||
company=self.invoice.company.id, date=accounting_date):
|
||||
anglo_saxon_move_lines = self._get_anglo_saxon_move_lines(
|
||||
cost, type_)
|
||||
if type_ == 'in_supplier' and (self.lot.sale_invoice_line_prov or self.lot.sale_invoice_line) and not self.fee:
|
||||
lot_has_sale_invoice = (
|
||||
self.lot
|
||||
and (self.lot.sale_invoice_line_prov
|
||||
or self.lot.sale_invoice_line))
|
||||
if type_ == 'in_supplier' and lot_has_sale_invoice and not self.fee:
|
||||
anglo_saxon_move_lines_ = self._get_anglo_saxon_move_lines(cost, 'out_customer')
|
||||
result.extend(anglo_saxon_move_lines)
|
||||
result.extend(anglo_saxon_move_lines_)
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<data>
|
||||
<xpath expr="/form/notebook" position="inside">
|
||||
<page string="Analytic" id="analytic_accounts">
|
||||
<xpath expr="/form/notebook/page[@id='invoice']" position="before">
|
||||
<page string="Analytics" id="analytic_accounts">
|
||||
<field name="analytic_accounts" colspan="4"/>
|
||||
</page>
|
||||
</xpath>
|
||||
|
||||
@@ -248,29 +248,10 @@ class AutomationDocument(ModelSQL, ModelView, Workflow):
|
||||
ShipmentWR.save([swr])
|
||||
doc.notes = (doc.notes or "") + f"Shipment found: {sh[0].number}\n"
|
||||
logger.info("BL_NUMBER:%s",sh[0].bl_number)
|
||||
if sh[0].incoming_moves:
|
||||
factor_net = wr.net_landed_kg / wr.bales if wr.bales else 1
|
||||
factor_gross = wr.gross_landed_kg / wr.bales if wr.bales else 1
|
||||
for move in sh[0].incoming_moves:
|
||||
lot = move.lot
|
||||
if lot.lot_type == 'physic':
|
||||
wr_payload = {
|
||||
"chunk_key": lot.lot_chunk_key,
|
||||
"gross_weight": float(round(Decimal(lot.lot_qt) * factor_gross,5)),
|
||||
"net_weight": float(round(Decimal(lot.lot_qt) * factor_net,5)),
|
||||
"tare_total": float(round(wr.tare_kg * (Decimal(lot.lot_qt) / wr.bales),5)) ,
|
||||
"bags": int(lot.lot_qt),
|
||||
"surveyor_code": sh[0].controller.get_alf(),
|
||||
"place_key": sh[0].to_location.get_places(),
|
||||
"report_date": int(wr.report_date.strftime("%Y%m%d")),#wr.report_date.isoformat() if wr.report_date else None,
|
||||
"weight_date": int(wr.weight_date.strftime("%Y%m%d")),#wr.weight_date.isoformat() if wr.weight_date else None,
|
||||
"agent": sh[0].agent.get_alf(),
|
||||
"forwarder_ref": sh[0].returned_id
|
||||
}
|
||||
logger.info("PAYLOAD:%s",wr_payload)
|
||||
data = doc.create_weight_report(wr_payload)
|
||||
doc.notes = (doc.notes or "") + f"WR created in Fintrade: {data.get('success')}\n"
|
||||
doc.notes = (doc.notes or "") + f"WR key: {data.get('weight_report_key')}\n"
|
||||
doc.notes = (
|
||||
(doc.notes or "")
|
||||
+ "Global WR linked to shipment. "
|
||||
+ "Create remote lot WRs from the weight report form.\n")
|
||||
|
||||
# if cls.rule_set.ocr_required:[]
|
||||
# cls.run_ocr([doc])
|
||||
@@ -293,4 +274,4 @@ class AutomationDocument(ModelSQL, ModelView, Workflow):
|
||||
# except Exception as e:
|
||||
# doc.state = "error"
|
||||
# doc.notes = (doc.notes or "") + f"Pipeline error: {e}\n"
|
||||
doc.save()
|
||||
doc.save()
|
||||
|
||||
@@ -11,6 +11,7 @@ import traceback
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Cron(metaclass=PoolMeta):
|
||||
__name__ = 'ir.cron'
|
||||
|
||||
@@ -18,9 +19,11 @@ class Cron(metaclass=PoolMeta):
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls.method.selection.append(
|
||||
('automation.cron|update_shipment', "Update Shipment from freight booking info")
|
||||
('automation.cron|update_shipment',
|
||||
"Update Shipment from freight booking info")
|
||||
)
|
||||
|
||||
|
||||
class AutomationCron(ModelSQL, ModelView):
|
||||
"Automation Cron"
|
||||
__name__ = 'automation.cron'
|
||||
@@ -30,7 +33,7 @@ class AutomationCron(ModelSQL, ModelView):
|
||||
('weekly', "Weekly"),
|
||||
('monthly', "Monthly"),
|
||||
], "Frequency", required=True,
|
||||
help="How frequently rates must be updated.")
|
||||
help="How frequently rates must be updated.")
|
||||
|
||||
last_update = fields.Date("Last Update", required=True)
|
||||
|
||||
@@ -70,22 +73,23 @@ class AutomationCron(ModelSQL, ModelView):
|
||||
))
|
||||
|
||||
rows = cursor.fetchall()
|
||||
logger.info(f"Nombre total de lignes à traiter : {len(rows)}")
|
||||
logger.info(f"Nombre total de lignes a traiter : {len(rows)}")
|
||||
|
||||
# ---- PREMIÈRE TRANSACTION : Création des objets de référence ----
|
||||
# Premiere transaction : creation des objets de reference
|
||||
with Transaction().new_transaction() as trans1:
|
||||
try:
|
||||
logger.info("Début de la création des objets de référence...")
|
||||
|
||||
logger.info(
|
||||
"Debut de la creation des objets de reference...")
|
||||
|
||||
parties_to_save = []
|
||||
vessels_to_save = []
|
||||
locations_to_save = []
|
||||
|
||||
|
||||
parties_cache = {}
|
||||
vessels_cache = {}
|
||||
locations_cache = {}
|
||||
|
||||
# Collecter les données des objets de référence
|
||||
|
||||
# Collecter les donnees des objets de reference
|
||||
for row in rows:
|
||||
(
|
||||
si_number, si_date, si_quantity, si_unit,
|
||||
@@ -97,47 +101,44 @@ class AutomationCron(ModelSQL, ModelView):
|
||||
comments, fintrade_booking_key
|
||||
) = row
|
||||
|
||||
# Fonction pour obtenir ou créer un Party
|
||||
def get_or_create_party(name):
|
||||
if not name:
|
||||
return None
|
||||
name_upper = str(name).strip().upper()
|
||||
if name_upper in parties_cache:
|
||||
return parties_cache[name_upper]
|
||||
|
||||
# Chercher d'abord dans la base
|
||||
existing = Party.search([('name', '=', name_upper)], limit=1)
|
||||
|
||||
existing = Party.search(
|
||||
[('name', '=', name_upper)], limit=1)
|
||||
if existing:
|
||||
parties_cache[name_upper] = existing[0]
|
||||
return existing[0]
|
||||
|
||||
# Créer un nouveau
|
||||
|
||||
new_p = Party()
|
||||
new_p.name = name_upper
|
||||
parties_cache[name_upper] = new_p
|
||||
parties_to_save.append(new_p)
|
||||
return new_p
|
||||
|
||||
# Fonction pour obtenir ou créer un Vessel
|
||||
def get_or_create_vessel(name):
|
||||
if not name:
|
||||
return None
|
||||
name_upper = str(name).strip().upper()
|
||||
if name_upper in vessels_cache:
|
||||
return vessels_cache[name_upper]
|
||||
|
||||
existing = Vessel.search([('vessel_name', '=', name_upper)], limit=1)
|
||||
|
||||
existing = Vessel.search(
|
||||
[('vessel_name', '=', name_upper)], limit=1)
|
||||
if existing:
|
||||
vessels_cache[name_upper] = existing[0]
|
||||
return existing[0]
|
||||
|
||||
|
||||
new_v = Vessel()
|
||||
new_v.vessel_name = name_upper
|
||||
vessels_cache[name_upper] = new_v
|
||||
vessels_to_save.append(new_v)
|
||||
return new_v
|
||||
|
||||
# Fonction pour obtenir ou créer une Location
|
||||
def get_or_create_location(name, type_):
|
||||
if not name:
|
||||
return None
|
||||
@@ -145,16 +146,15 @@ class AutomationCron(ModelSQL, ModelView):
|
||||
key = f"{name_upper}_{type_}"
|
||||
if key in locations_cache:
|
||||
return locations_cache[key]
|
||||
|
||||
|
||||
existing = Location.search([
|
||||
('name', '=', name_upper),
|
||||
('type', '=', type_)
|
||||
('type', '=', type_),
|
||||
], limit=1)
|
||||
|
||||
if existing:
|
||||
locations_cache[key] = existing[0]
|
||||
return existing[0]
|
||||
|
||||
|
||||
new_loc = Location()
|
||||
new_loc.name = name_upper
|
||||
new_loc.type = type_
|
||||
@@ -162,40 +162,40 @@ class AutomationCron(ModelSQL, ModelView):
|
||||
locations_to_save.append(new_loc)
|
||||
return new_loc
|
||||
|
||||
# Collecter les objets à créer
|
||||
_ = get_or_create_party(carrier_name)
|
||||
_ = get_or_create_party(agent_name)
|
||||
_ = get_or_create_vessel(vessel_name)
|
||||
_ = get_or_create_location(loading_name, 'supplier')
|
||||
_ = get_or_create_location(destination_name, 'customer')
|
||||
|
||||
# Sauvegarder tous les objets de référence
|
||||
if parties_to_save:
|
||||
logger.info(f"Création de {len(parties_to_save)} parties...")
|
||||
logger.info(f"Creation de {len(parties_to_save)} parties...")
|
||||
Party.save(parties_to_save)
|
||||
|
||||
|
||||
if vessels_to_save:
|
||||
logger.info(f"Création de {len(vessels_to_save)} vessels...")
|
||||
logger.info(f"Creation de {len(vessels_to_save)} vessels...")
|
||||
Vessel.save(vessels_to_save)
|
||||
|
||||
|
||||
if locations_to_save:
|
||||
logger.info(f"Création de {len(locations_to_save)} locations...")
|
||||
logger.info(
|
||||
f"Creation de {len(locations_to_save)} locations...")
|
||||
Location.save(locations_to_save)
|
||||
|
||||
|
||||
trans1.commit()
|
||||
logger.info("Première transaction commitée : objets de référence créés")
|
||||
|
||||
logger.info(
|
||||
"Premiere transaction commitee : objets de reference crees")
|
||||
|
||||
except Exception as e:
|
||||
trans1.rollback()
|
||||
logger.error(f"Erreur dans la création des objets de référence : {e}")
|
||||
logger.error(
|
||||
f"Erreur dans la creation des objets de reference : {e}")
|
||||
logger.error(traceback.format_exc())
|
||||
raise
|
||||
|
||||
# ---- TRANSACTIONS INDIVIDUELLES pour chaque shipment ----
|
||||
# Transactions individuelles pour chaque shipment
|
||||
successful_shipments = 0
|
||||
failed_shipments = []
|
||||
|
||||
# Recréer le curseur après la nouvelle transaction
|
||||
|
||||
cursor2 = Transaction().connection.cursor()
|
||||
cursor2.execute(*t.select(
|
||||
t.ShippingInstructionNumber,
|
||||
@@ -218,7 +218,7 @@ class AutomationCron(ModelSQL, ModelView):
|
||||
))
|
||||
|
||||
rows2 = cursor2.fetchall()
|
||||
|
||||
|
||||
for i, row in enumerate(rows2, 1):
|
||||
(
|
||||
si_number, si_date, si_quantity, si_unit,
|
||||
@@ -229,46 +229,79 @@ class AutomationCron(ModelSQL, ModelView):
|
||||
etd_date, bl_date, controller,
|
||||
comments, fintrade_booking_key
|
||||
) = row
|
||||
|
||||
|
||||
logger.info(f"Traitement shipment {i}/{len(rows2)} : SI {si_number}")
|
||||
|
||||
# ---- TRANSACTION INDIVIDUELLE pour ce shipment ----
|
||||
|
||||
try:
|
||||
with Transaction().new_transaction() as trans_shipment:
|
||||
logger.info(f"Début transaction pour SI {si_number}")
|
||||
|
||||
# Vérifier si le shipment existe déjà
|
||||
logger.info(f"Debut transaction pour SI {si_number}")
|
||||
|
||||
existing_shipment = ShipmentIn.search([
|
||||
('reference', '=', si_number)
|
||||
], limit=1)
|
||||
|
||||
|
||||
if existing_shipment:
|
||||
logger.info(f"Shipment {si_number} existe déjà, ignoré")
|
||||
shipment = existing_shipment[0]
|
||||
if shipment.incoming_moves:
|
||||
logger.info(
|
||||
"Shipment %s existe deja avec lots, ignore",
|
||||
si_number)
|
||||
trans_shipment.commit()
|
||||
continue
|
||||
|
||||
logger.info(
|
||||
"Shipment %s existe deja sans lots, verification freight_booking_lots",
|
||||
si_number)
|
||||
inv_date, inv_nb = shipment._create_lots_from_fintrade()
|
||||
shipment = ShipmentIn(shipment.id)
|
||||
if shipment.incoming_moves:
|
||||
shipment.controller = shipment.get_controller()
|
||||
shipment.controller_target = controller
|
||||
if not shipment.fees:
|
||||
shipment.create_fee(shipment.controller)
|
||||
shipment.instructions = shipment.get_instructions_html(
|
||||
inv_date, inv_nb)
|
||||
ShipmentIn.save([shipment])
|
||||
logger.info(
|
||||
"Shipment %s mis a jour avec %s incoming move(s)",
|
||||
si_number, len(shipment.incoming_moves))
|
||||
else:
|
||||
logger.info(
|
||||
"Shipment %s existe sans lots et aucun lot disponible pour l'instant",
|
||||
si_number)
|
||||
trans_shipment.commit()
|
||||
continue
|
||||
|
||||
# Récupérer les objets (maintenant ils existent dans la base)
|
||||
carrier = None
|
||||
if carrier_name:
|
||||
carrier_list = Party.search([('name', '=', str(carrier_name).strip().upper())], limit=1)
|
||||
carrier_list = Party.search([
|
||||
('name', '=',
|
||||
str(carrier_name).strip().upper())
|
||||
], limit=1)
|
||||
if carrier_list:
|
||||
carrier = carrier_list[0]
|
||||
logger.info(f"Carrier trouvé pour {si_number}: {carrier.name}")
|
||||
logger.info(
|
||||
f"Carrier trouve pour {si_number}: {carrier.name}")
|
||||
else:
|
||||
logger.warning(f"Carrier NON TROUVÉ pour {si_number}: '{carrier_name}'")
|
||||
|
||||
logger.warning(
|
||||
f"Carrier NON TROUVE pour {si_number}: '{carrier_name}'")
|
||||
|
||||
agent = None
|
||||
|
||||
agent_list = Party.search([('name', '=', str(agent_name or 'TBN').strip().upper())], limit=1)
|
||||
agent_list = Party.search([
|
||||
('name', '=', str(agent_name or 'TBN').strip().upper())
|
||||
], limit=1)
|
||||
if agent_list:
|
||||
agent = agent_list[0]
|
||||
|
||||
|
||||
vessel = None
|
||||
if vessel_name:
|
||||
vessel_list = Vessel.search([('vessel_name', '=', str(vessel_name).strip().upper())], limit=1)
|
||||
vessel_list = Vessel.search([
|
||||
('vessel_name', '=',
|
||||
str(vessel_name).strip().upper())
|
||||
], limit=1)
|
||||
if vessel_list:
|
||||
vessel = vessel_list[0]
|
||||
|
||||
|
||||
loc_from = None
|
||||
if loading_name:
|
||||
loc_from_list = Location.search([
|
||||
@@ -277,7 +310,7 @@ class AutomationCron(ModelSQL, ModelView):
|
||||
], limit=1)
|
||||
if loc_from_list:
|
||||
loc_from = loc_from_list[0]
|
||||
|
||||
|
||||
loc_to = None
|
||||
if destination_name:
|
||||
loc_to_list = Location.search([
|
||||
@@ -286,19 +319,19 @@ class AutomationCron(ModelSQL, ModelView):
|
||||
], limit=1)
|
||||
if loc_to_list:
|
||||
loc_to = loc_to_list[0]
|
||||
|
||||
# Vérification critique du carrier
|
||||
|
||||
if not carrier:
|
||||
error_msg = f"ERREUR CRITIQUE: Carrier manquant pour SI {si_number} (valeur: '{carrier_name}')"
|
||||
error_msg = (
|
||||
f"ERREUR CRITIQUE: Carrier manquant pour SI {si_number} "
|
||||
f"(valeur: '{carrier_name}')")
|
||||
logger.error(error_msg)
|
||||
raise ValueError(error_msg)
|
||||
|
||||
# Créer le shipment
|
||||
|
||||
shipment = ShipmentIn()
|
||||
shipment.reference = si_number
|
||||
shipment.from_location = loc_from
|
||||
shipment.to_location = loc_to
|
||||
shipment.carrier = None #carrier
|
||||
shipment.carrier = None # carrier
|
||||
shipment.supplier = agent
|
||||
shipment.agent = agent
|
||||
shipment.vessel = vessel
|
||||
@@ -307,21 +340,20 @@ class AutomationCron(ModelSQL, ModelView):
|
||||
shipment.bl_date = bl_date
|
||||
shipment.etd = etd_date
|
||||
shipment.etad = shipment.bl_date + timedelta(days=20)
|
||||
|
||||
# Sauvegarder ce shipment uniquement
|
||||
|
||||
ShipmentIn.save([shipment])
|
||||
inv_date,inv_nb = shipment._create_lots_from_fintrade()
|
||||
inv_date, inv_nb = shipment._create_lots_from_fintrade()
|
||||
shipment.controller = shipment.get_controller()
|
||||
shipment.controller_target = controller
|
||||
shipment.create_fee(shipment.controller)
|
||||
shipment.instructions = shipment.get_instructions_html(inv_date,inv_nb)
|
||||
shipment.instructions = shipment.get_instructions_html(
|
||||
inv_date, inv_nb)
|
||||
ShipmentIn.save([shipment])
|
||||
trans_shipment.commit()
|
||||
successful_shipments += 1
|
||||
logger.info(f"✓ Shipment {si_number} créé avec succès")
|
||||
|
||||
logger.info(f"Shipment {si_number} cree avec succes")
|
||||
|
||||
except Exception as e:
|
||||
# Cette transaction échoue mais les autres continuent
|
||||
error_details = {
|
||||
'si_number': si_number,
|
||||
'carrier_name': carrier_name,
|
||||
@@ -329,8 +361,8 @@ class AutomationCron(ModelSQL, ModelView):
|
||||
'traceback': traceback.format_exc()
|
||||
}
|
||||
failed_shipments.append(error_details)
|
||||
|
||||
logger.error(f"✗ ERREUR pour shipment {si_number}: {e}")
|
||||
|
||||
logger.error(f"ERREUR pour shipment {si_number}: {e}")
|
||||
logger.error(f" Carrier: '{carrier_name}'")
|
||||
logger.error(f" Agent: '{agent_name}'")
|
||||
logger.error(f" Vessel: '{vessel_name}'")
|
||||
@@ -338,24 +370,22 @@ class AutomationCron(ModelSQL, ModelView):
|
||||
for line in traceback.format_exc().split('\n'):
|
||||
if line.strip():
|
||||
logger.error(f" {line}")
|
||||
|
||||
# ---- RÉSUMÉ FINAL ----
|
||||
|
||||
logger.info("=" * 60)
|
||||
logger.info("RÉSUMÉ DE L'EXÉCUTION")
|
||||
logger.info("RESUME DE L'EXECUTION")
|
||||
logger.info("=" * 60)
|
||||
logger.info(f"Total de shipments à traiter : {len(rows2)}")
|
||||
logger.info(f"Shipments créés avec succès : {successful_shipments}")
|
||||
logger.info(f"Shipments en échec : {len(failed_shipments)}")
|
||||
|
||||
logger.info(f"Total de shipments a traiter : {len(rows2)}")
|
||||
logger.info(f"Shipments crees avec succes : {successful_shipments}")
|
||||
logger.info(f"Shipments en echec : {len(failed_shipments)}")
|
||||
|
||||
if failed_shipments:
|
||||
logger.info("\nDétail des échecs :")
|
||||
logger.info("\nDetail des echecs :")
|
||||
for i, error in enumerate(failed_shipments, 1):
|
||||
logger.info(f" {i}. SI {error['si_number']}:")
|
||||
logger.info(f" Carrier: '{error['carrier_name']}'")
|
||||
logger.info(f" Erreur: {error['error']}")
|
||||
|
||||
# Log supplémentaire pour debug
|
||||
logger.info("\nAnalyse des carriers problématiques :")
|
||||
|
||||
logger.info("\nAnalyse des carriers problematiques :")
|
||||
problematic_carriers = {}
|
||||
for error in failed_shipments:
|
||||
carrier = error['carrier_name']
|
||||
@@ -363,15 +393,16 @@ class AutomationCron(ModelSQL, ModelView):
|
||||
problematic_carriers[carrier] += 1
|
||||
else:
|
||||
problematic_carriers[carrier] = 1
|
||||
|
||||
|
||||
for carrier, count in problematic_carriers.items():
|
||||
logger.info(f" Carrier '{carrier}' : {count} échec(s)")
|
||||
|
||||
# Vérifier si ce carrier existe dans la base
|
||||
existing = Party.search([('name', '=', str(carrier).strip().upper())], limit=1)
|
||||
logger.info(f" Carrier '{carrier}' : {count} echec(s)")
|
||||
existing = Party.search([
|
||||
('name', '=', str(carrier).strip().upper())
|
||||
], limit=1)
|
||||
if existing:
|
||||
logger.info(f" → EXISTE DANS LA BASE (ID: {existing[0].id})")
|
||||
logger.info(
|
||||
f" -> EXISTE DANS LA BASE (ID: {existing[0].id})")
|
||||
else:
|
||||
logger.info(f" → N'EXISTE PAS DANS LA BASE")
|
||||
|
||||
logger.info("=" * 60)
|
||||
logger.info(" -> N'EXISTE PAS DANS LA BASE")
|
||||
|
||||
logger.info("=" * 60)
|
||||
|
||||
@@ -2,9 +2,4 @@
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<data>
|
||||
<xpath expr="//page[@id='costs']" position="inside">
|
||||
<field name="before_carriages" colspan="4"/>
|
||||
|
||||
<field name="after_carriages" colspan="4"/>
|
||||
</xpath>
|
||||
</data>
|
||||
|
||||
@@ -18,6 +18,13 @@ class IncomingConfiguration(metaclass=PoolMeta):
|
||||
class Incoming(metaclass=PoolMeta):
|
||||
__name__ = 'document.incoming'
|
||||
|
||||
created_at = fields.Function(
|
||||
fields.Date("Created At"),
|
||||
'get_created_at')
|
||||
result_notes = fields.Function(
|
||||
fields.Text("Result Notes"),
|
||||
'get_result_notes')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
@@ -25,6 +32,7 @@ class Incoming(metaclass=PoolMeta):
|
||||
('weight_report', "Weight Report"))
|
||||
cls.type.selection.append(
|
||||
('controller', "Controller"))
|
||||
cls._order = [('create_date', 'DESC')]
|
||||
|
||||
@classmethod
|
||||
def _get_results(cls):
|
||||
@@ -39,7 +47,7 @@ class Incoming(metaclass=PoolMeta):
|
||||
WR.save([wr])
|
||||
WR.run_ocr([wr])
|
||||
WR.run_metadata([wr])
|
||||
|
||||
WR.run_pipeline([wr])
|
||||
return wr
|
||||
|
||||
def _process_controller(self):
|
||||
@@ -54,6 +62,25 @@ class Incoming(metaclass=PoolMeta):
|
||||
|
||||
return wr
|
||||
|
||||
def get_created_at(self, name=None):
|
||||
create_date = getattr(self, 'create_date', None)
|
||||
if not create_date:
|
||||
return None
|
||||
return create_date.date()
|
||||
|
||||
@classmethod
|
||||
def order_created_at(cls, tables):
|
||||
table, _ = tables[None]
|
||||
return [table.create_date]
|
||||
|
||||
def get_result_notes(self, name=None):
|
||||
result = getattr(self, 'result', None)
|
||||
if not result:
|
||||
return ''
|
||||
if getattr(result, '__name__', None) == 'automation.document':
|
||||
return getattr(result, 'notes', '') or ''
|
||||
return ''
|
||||
|
||||
# @property
|
||||
# def supplier_invoice_company(self):
|
||||
# pass
|
||||
|
||||
@@ -8,5 +8,10 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="inherit" ref="document_incoming.document_incoming_configuration_view_form"/>
|
||||
<field name="name">document_incoming_configuration_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="document_incoming_view_list_wr">
|
||||
<field name="model">document.incoming</field>
|
||||
<field name="inherit" ref="document_incoming.document_incoming_view_list"/>
|
||||
<field name="name">document_incoming_list</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
version=7.2.0
|
||||
depends:
|
||||
document_incoming
|
||||
automation
|
||||
ir
|
||||
party
|
||||
xml:
|
||||
document.xml
|
||||
view/document_incoming_list.xml
|
||||
|
||||
11
modules/document_incoming_wr/view/document_incoming_list.xml
Normal file
11
modules/document_incoming_wr/view/document_incoming_list.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<data>
|
||||
<xpath expr="/tree/field[@name='name']" position="before">
|
||||
<field name="created_at" expand="2"/>
|
||||
</xpath>
|
||||
<xpath expr="/tree/field[@name='result']" position="after">
|
||||
<field name="result_notes" expand="2"/>
|
||||
</xpath>
|
||||
</data>
|
||||
@@ -24,8 +24,14 @@ class Lot(ModelSQL, ModelView):
|
||||
|
||||
lot_name = fields.Char("Lot")
|
||||
number = fields.Char("Number", readonly=True)
|
||||
lot_qt = fields.Float("Quantity",required=False)
|
||||
lot_unit = fields.Many2One('product.uom', "Unit",required=False)
|
||||
lot_qt = fields.Float("Quantity", required=False,
|
||||
states={
|
||||
'readonly': Eval('lot_type') == 'virtual',
|
||||
})
|
||||
lot_unit = fields.Many2One('product.uom', "Unit", required=False,
|
||||
states={
|
||||
'readonly': Eval('lot_type') == 'virtual',
|
||||
})
|
||||
lot_product = fields.Many2One('product.product', "Product")
|
||||
lot_type = fields.Selection([
|
||||
('virtual', 'Open'),
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<notebook colspan="4">
|
||||
<page string="General" id="general">
|
||||
<field name="lot_childs" colspan="4" mode="tree,form" view_ids="lot.lot_view_tree_sequence2,lot.lot_view_form"/>
|
||||
<field name="lot_hist" colspan="4"/>
|
||||
<field name="lot_hist" colspan="4" readonly="1"/>
|
||||
</page>
|
||||
<page string="Accounting" id="accounting">
|
||||
<field name="pivot" widget="html_viewer" height="600" colspan="4"/>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0"?>
|
||||
<form col="4">
|
||||
<label name="lot"/>
|
||||
<field name="lot"/>
|
||||
<field name="lot" readonly="1"/>
|
||||
<label name="quantity_type"/>
|
||||
<field name="quantity_type"/>
|
||||
<field name="quantity_type" readonly="1"/>
|
||||
<label name="quantity"/>
|
||||
<field name="quantity"/>
|
||||
<field name="quantity" readonly="1"/>
|
||||
<label name="gross_quantity"/>
|
||||
<field name="gross_quantity"/>
|
||||
<field name="gross_quantity" readonly="1"/>
|
||||
</form>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<tree editable="1">
|
||||
<tree>
|
||||
<field name="lot"/>
|
||||
<field name="quantity_type"/>
|
||||
<field name="quantity"/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<tree editable="1">
|
||||
<tree>
|
||||
<field name="lot"/>
|
||||
<field name="quantity_type"/>
|
||||
<field name="quantity"/>
|
||||
|
||||
@@ -68,17 +68,22 @@ class Price(
|
||||
amount = Decimal(self.get_price_per_qt(price,unit,currency)) * Decimal(Uom.compute_qty(self.price_unit, float(self.price_ct_size * nb_ct), unit))
|
||||
return round(amount,4)
|
||||
|
||||
def get_price(self,dt,unit,currency,last=False):
|
||||
price = float(0)
|
||||
PV = Pool().get('price.price_value')
|
||||
if self.price_values:
|
||||
dt = dt.strftime("%Y-%m-%d")
|
||||
pv = PV.search([('price','=',self.id),('price_date','=',dt)])
|
||||
if not pv and last:
|
||||
pv = PV.search([('price','=',self.id)],order=[('price_date', 'DESC')])
|
||||
if pv:
|
||||
price = self.get_price_per_qt(pv[0].price_value,unit,currency)
|
||||
return round(price,4)
|
||||
def get_price(self,dt,unit,currency,last=False, relative_last=False):
|
||||
price = float(0)
|
||||
PV = Pool().get('price.price_value')
|
||||
if self.price_values:
|
||||
dt = dt.strftime("%Y-%m-%d")
|
||||
pv = PV.search([('price','=',self.id),('price_date','=',dt)])
|
||||
if not pv and last:
|
||||
pv = PV.search([('price','=',self.id)],order=[('price_date', 'DESC')])
|
||||
elif not pv and relative_last:
|
||||
pv = PV.search([
|
||||
('price','=',self.id),
|
||||
('price_date','<=',dt),
|
||||
], order=[('price_date', 'DESC')])
|
||||
if pv:
|
||||
price = self.get_price_per_qt(pv[0].price_value,unit,currency)
|
||||
return round(price,4)
|
||||
|
||||
class FixType(ModelSQL,ModelView):
|
||||
"Fixation type"
|
||||
|
||||
@@ -35,10 +35,11 @@ class PriceValue(
|
||||
}), 'get_price_index')
|
||||
|
||||
price_date = fields.Date("Price date")
|
||||
price_value = fields.Float("Price value")
|
||||
open_price = fields.Float("Open price")
|
||||
low_price = fields.Float("Low price")
|
||||
high_price = fields.Float("High price")
|
||||
price_value = fields.Float("Price value")
|
||||
open_price = fields.Float("Open price")
|
||||
low_price = fields.Float("Low price")
|
||||
mid_price = fields.Float("Mid price")
|
||||
high_price = fields.Float("High price")
|
||||
|
||||
def get_price_index(self, name):
|
||||
if self.price:
|
||||
@@ -55,9 +56,10 @@ class PriceValueReport(
|
||||
r_price_desc = fields.Char("Description")
|
||||
r_price_date = fields.Date("Price date")
|
||||
r_price_value = fields.Float("Price")
|
||||
r_open_price = fields.Float("Open price")
|
||||
r_low_price = fields.Float("Low price")
|
||||
r_high_price = fields.Float("High price")
|
||||
r_open_price = fields.Float("Open price")
|
||||
r_low_price = fields.Float("Low price")
|
||||
r_mid_price = fields.Float("Mid price")
|
||||
r_high_price = fields.Float("High price")
|
||||
|
||||
@classmethod
|
||||
def table_query(cls):
|
||||
@@ -86,10 +88,11 @@ class PriceValueReport(
|
||||
pp.price_desc.as_('r_price_desc'),
|
||||
pv.price_date.as_('r_price_date'),
|
||||
pv.price_value.as_('r_price_value'),
|
||||
pv.open_price.as_('r_open_price'),
|
||||
pv.low_price.as_('r_low_price'),
|
||||
pv.high_price.as_('r_high_price'),
|
||||
where=wh)
|
||||
pv.open_price.as_('r_open_price'),
|
||||
pv.low_price.as_('r_low_price'),
|
||||
pv.mid_price.as_('r_mid_price'),
|
||||
pv.high_price.as_('r_high_price'),
|
||||
where=wh)
|
||||
|
||||
return query
|
||||
|
||||
@@ -116,9 +119,10 @@ class PriceValueReport2(
|
||||
r_price_desc = fields.Char("Description")
|
||||
r_price_date = fields.Date("Price date")
|
||||
r_price_value = fields.Float("Price")
|
||||
r_open_price = fields.Float("Open price")
|
||||
r_low_price = fields.Float("Low price")
|
||||
r_high_price = fields.Float("High price")
|
||||
r_open_price = fields.Float("Open price")
|
||||
r_low_price = fields.Float("Low price")
|
||||
r_mid_price = fields.Float("Mid price")
|
||||
r_high_price = fields.Float("High price")
|
||||
r_naphta_fob_low = fields.Float("Naph F Med l", digits=(1,2), help="Naphta Fob PAAAI00 low")
|
||||
r_naphta_fob_high = fields.Float("Naph F Med h", digits=(1,2), help="Naphta Fob PAAAI00 high")
|
||||
r_naphta_fob_mid = fields.Float("Naph F Med m", digits=(1,2), help="Naphta Fob PAAAI00 mid")
|
||||
@@ -171,9 +175,10 @@ class PriceValueReport2(
|
||||
Max(pp.price_desc).as_('r_price_desc'),
|
||||
pv.price_date.as_('r_price_date'),
|
||||
Max(pv.price_value).as_('r_price_value'),
|
||||
Max(pv.open_price).as_('r_open_price'),
|
||||
Max(pv.low_price).as_('r_low_price'),
|
||||
Max(pv.high_price).as_('r_high_price'),
|
||||
Max(pv.open_price).as_('r_open_price'),
|
||||
Max(pv.low_price).as_('r_low_price'),
|
||||
Max(pv.mid_price).as_('r_mid_price'),
|
||||
Max(pv.high_price).as_('r_high_price'),
|
||||
Max(Case((pv.price==15, pv.low_price),else_=0)).as_('r_naphta_fob_low'),
|
||||
Max(Case((pv.price==15, pv.high_price),else_=0)).as_('r_naphta_fob_high'),
|
||||
Max(Case((pv.price==15, (pv.high_price-pv.low_price)/2 + pv.low_price),else_=0)).as_('r_naphta_fob_mid'),
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
<tree>
|
||||
<field name="r_price_index"/>
|
||||
<field name="r_price_desc"/>
|
||||
<field name="r_price_date"/>
|
||||
<field name="r_price_value"/>
|
||||
<field name="r_low_price"/>
|
||||
<field name="r_high_price"/>
|
||||
</tree>
|
||||
<field name="r_price_date"/>
|
||||
<field name="r_price_value"/>
|
||||
<field name="r_low_price"/>
|
||||
<field name="r_mid_price"/>
|
||||
<field name="r_high_price"/>
|
||||
</tree>
|
||||
|
||||
@@ -8,10 +8,12 @@
|
||||
<label name="price_value"/>
|
||||
<field name="price_value"/>
|
||||
<label name="open_price"/>
|
||||
<field name="open_price"/>
|
||||
<label name="low_price"/>
|
||||
<field name="low_price"/>
|
||||
<label name="high_price"/>
|
||||
<field name="high_price"/>
|
||||
<field name="open_price"/>
|
||||
<label name="low_price"/>
|
||||
<field name="low_price"/>
|
||||
<label name="mid_price"/>
|
||||
<field name="mid_price"/>
|
||||
<label name="high_price"/>
|
||||
<field name="high_price"/>
|
||||
</group>
|
||||
</form>
|
||||
|
||||
@@ -9,6 +9,7 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="price_value" fill="1" empty="0"/>
|
||||
<field name="open_price" fill="1" empty="0"/>
|
||||
<field name="low_price" fill="1" empty="0"/>
|
||||
<field name="mid_price" fill="1" empty="0"/>
|
||||
<field name="high_price" fill="1" empty="0"/>
|
||||
</y>
|
||||
</graph>
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
<tree>
|
||||
<field name="price_index"/>
|
||||
<field name="price_date"/>
|
||||
<field name="price_value"/>
|
||||
<field name="open_price"/>
|
||||
<field name="low_price"/>
|
||||
<field name="high_price"/>
|
||||
</tree>
|
||||
<field name="price_value"/>
|
||||
<field name="open_price"/>
|
||||
<field name="low_price"/>
|
||||
<field name="mid_price"/>
|
||||
<field name="high_price"/>
|
||||
</tree>
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
<tree editable="1">
|
||||
<field name="price_index"/>
|
||||
<field name="price_date"/>
|
||||
<field name="price_value"/>
|
||||
<field name="open_price"/>
|
||||
<field name="low_price"/>
|
||||
<field name="high_price"/>
|
||||
</tree>
|
||||
<field name="price_value"/>
|
||||
<field name="open_price"/>
|
||||
<field name="low_price"/>
|
||||
<field name="mid_price"/>
|
||||
<field name="high_price"/>
|
||||
</tree>
|
||||
|
||||
@@ -953,19 +953,22 @@ class Purchase(
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('confirmed')
|
||||
@set_employee('confirmed_by')
|
||||
# @Workflow.transition('confirmed')
|
||||
# @set_employee('confirmed_by')
|
||||
def confirm(cls, purchases):
|
||||
pool = Pool()
|
||||
Line = pool.get('purchase.line')
|
||||
transaction = Transaction()
|
||||
context = transaction.context
|
||||
cls.set_purchase_date(purchases)
|
||||
|
||||
cls.write(purchases, {'state': 'confirmed'})
|
||||
lines = list(sum((p.lines for p in purchases), ()))
|
||||
Line._validate(lines, ['unit_price'])
|
||||
|
||||
# Confirm is intentionally disabled: clicking the button must not move
|
||||
# the purchase to Confirmed or trigger downstream processing.
|
||||
pass
|
||||
# pool = Pool()
|
||||
# Line = pool.get('purchase.line')
|
||||
# cls.set_purchase_date(purchases)
|
||||
#
|
||||
# cls.write(purchases, {'state': 'confirmed'})
|
||||
# lines = list(sum((p.lines for p in purchases), ()))
|
||||
# Line._validate(lines, ['unit_price'])
|
||||
#
|
||||
# transaction = Transaction()
|
||||
# context = transaction.context
|
||||
# cls.store_cache(purchases)
|
||||
# for process_after, sub_purchases in groupby(
|
||||
# purchases, lambda p: p.process_after):
|
||||
@@ -1013,16 +1016,22 @@ class Purchase(
|
||||
for purchase in purchases:
|
||||
logger.info("PROCESS_INVOICE:%s",action)
|
||||
invoice = purchase.create_invoice(lots,action)
|
||||
if not invoice:
|
||||
logger.info(
|
||||
"PROCESS_INVOICE_NO_INVOICE: purchase=%s action=%s",
|
||||
getattr(purchase, 'id', None), action)
|
||||
continue
|
||||
if action == 'prov':
|
||||
invoice.reference = 'Provisional'
|
||||
elif action == 'service':
|
||||
invoice.reference = 'Service'
|
||||
else:
|
||||
invoice.reference = 'Final'
|
||||
if invoice:
|
||||
invoices[purchase] = invoice
|
||||
invoices[purchase] = invoice
|
||||
|
||||
cls._save_invoice(invoices,prepayment)
|
||||
if invoices:
|
||||
cls._save_invoice(invoices,prepayment)
|
||||
return invoices
|
||||
|
||||
@classmethod
|
||||
def _save_invoice(cls, invoices,prepayment=None):
|
||||
@@ -1851,6 +1860,83 @@ class Line(sequence_ordered(), ModelSQL, ModelView):
|
||||
def on_change_with_currency(self, name=None):
|
||||
return self.purchase.currency if self.purchase else None
|
||||
|
||||
@staticmethod
|
||||
def _record_id(record):
|
||||
return getattr(record, 'id', record)
|
||||
|
||||
@classmethod
|
||||
def _get_last_fee_invoice_line(cls, fee, lot):
|
||||
fee_id = cls._record_id(fee)
|
||||
lot_id = cls._record_id(lot)
|
||||
if not fee_id or not lot_id:
|
||||
return
|
||||
InvoiceLine = Pool().get('account.invoice.line')
|
||||
lines = InvoiceLine.search([
|
||||
('fee', '=', fee_id),
|
||||
('lot', '=', lot_id),
|
||||
('quantity', '>', 0),
|
||||
('invoice.state', '!=', 'cancelled'),
|
||||
], order=[
|
||||
('invoice.invoice_date', 'DESC'),
|
||||
('invoice.id', 'DESC'),
|
||||
('id', 'DESC'),
|
||||
], limit=1)
|
||||
return lines[0] if lines else None
|
||||
|
||||
@staticmethod
|
||||
def _fee_invoice_line_changed(previous_line, invoice_line):
|
||||
if not previous_line:
|
||||
return True
|
||||
previous_quantity = Decimal(str(previous_line.quantity or 0))
|
||||
quantity = Decimal(str(invoice_line.quantity or 0))
|
||||
previous_price = Decimal(str(previous_line.unit_price or 0))
|
||||
price = Decimal(str(invoice_line.unit_price or 0))
|
||||
return previous_quantity != quantity or previous_price != price
|
||||
|
||||
@classmethod
|
||||
def _get_fee_reversal_invoice_line(cls, previous_line, origin):
|
||||
if not previous_line:
|
||||
return
|
||||
InvoiceLine = Pool().get('account.invoice.line')
|
||||
reversal_line, = InvoiceLine.copy([previous_line], default={
|
||||
'invoice': None,
|
||||
'quantity': -previous_line.quantity,
|
||||
'unit_price': previous_line.unit_price,
|
||||
'party': previous_line.invoice.party,
|
||||
'origin': str(origin),
|
||||
})
|
||||
return reversal_line
|
||||
|
||||
def _get_service_fee_invoice_lines(self, invoice_line, lot):
|
||||
Fee = Pool().get('fee.fee')
|
||||
fee = Fee.search(['purchase','=',self.purchase.id])
|
||||
if not fee:
|
||||
return [invoice_line]
|
||||
|
||||
fee = fee[0]
|
||||
invoice_line.fee = fee
|
||||
fee.warn_percent_price_partial_lots()
|
||||
if fee.mode == 'lumpsum':
|
||||
invoice_line.quantity = 1
|
||||
elif fee.mode == 'pprice':
|
||||
invoice_line.quantity = 1
|
||||
elif fee.mode == 'ppack':
|
||||
invoice_line.quantity = fee.quantity
|
||||
else:
|
||||
invoice_line.quantity = fee.get_fee_lots_qt()
|
||||
|
||||
if getattr(fee, 'state', None) != 'invoiced':
|
||||
return [invoice_line]
|
||||
|
||||
previous_line = self._get_last_fee_invoice_line(fee, lot)
|
||||
if not self._fee_invoice_line_changed(previous_line, invoice_line):
|
||||
return []
|
||||
reversal_line = self._get_fee_reversal_invoice_line(
|
||||
previous_line, self)
|
||||
if reversal_line:
|
||||
return [reversal_line, invoice_line]
|
||||
return [invoice_line]
|
||||
|
||||
def get_invoice_line(self,lots=None,action=None):
|
||||
'Return a list of invoice line for purchase line'
|
||||
pool = Pool()
|
||||
@@ -1930,21 +2016,9 @@ class Line(sequence_ordered(), ModelSQL, ModelView):
|
||||
invoice_line.unit_price = self.unit_price
|
||||
invoice_line.product = self.product
|
||||
invoice_line.stock_moves = []
|
||||
Fee = Pool().get('fee.fee')
|
||||
fee = Fee.search(['purchase','=',self.purchase.id])
|
||||
if fee:
|
||||
invoice_line.fee = fee[0]
|
||||
if fee[0].mode == 'lumpsum':
|
||||
invoice_line.quantity = 1
|
||||
elif fee[0].mode == 'ppack':
|
||||
invoice_line.quantity = fee[0].quantity
|
||||
else:
|
||||
state_id = 0
|
||||
LotQtType = Pool().get('lot.qt.type')
|
||||
lqt = LotQtType.search([('name','=','BL')])
|
||||
if lqt:
|
||||
state_id = lqt[0].id
|
||||
invoice_line.quantity = fee[0].get_fee_lots_qt(state_id)
|
||||
lines.extend(
|
||||
self._get_service_fee_invoice_lines(invoice_line, l))
|
||||
continue
|
||||
|
||||
lines.append(invoice_line)
|
||||
logger.info("GETINVLINE:%s",self.product.type)
|
||||
|
||||
@@ -91,26 +91,10 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="view" ref="purchase_view_form"/>
|
||||
<field name="act_window" ref="act_purchase_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain"
|
||||
id="act_purchase_form_domain_service">
|
||||
<field name="name">Services</field>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="domain" eval="[('line_type', '=', 'service')]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_purchase_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain"
|
||||
id="act_purchase_form_domain_draft">
|
||||
<field name="name">Draft</field>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="domain" eval="['AND',('state', '=', 'draft'),('line_type', '=', 'goods')]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_purchase_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain"
|
||||
id="act_purchase_form_domain_quotation">
|
||||
<field name="name">Validation</field>
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="name">Validated</field>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="domain" eval="['AND',('state', '=', 'quotation'),('line_type', '=', 'goods')]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_purchase_form"/>
|
||||
@@ -118,7 +102,7 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<record model="ir.action.act_window.domain"
|
||||
id="act_purchase_form_domain_confirmed">
|
||||
<field name="name">Confirmed</field>
|
||||
<field name="sequence" eval="30"/>
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="domain" eval="['AND',('state', '=', 'confirmed'),('line_type', '=', 'goods')]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_purchase_form"/>
|
||||
@@ -126,15 +110,31 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<record model="ir.action.act_window.domain"
|
||||
id="act_purchase_form_domain_processing">
|
||||
<field name="name">Processing</field>
|
||||
<field name="sequence" eval="40"/>
|
||||
<field name="sequence" eval="30"/>
|
||||
<field name="domain" eval="['AND',('state', '=', 'processing'),('line_type', '=', 'goods')]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_purchase_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain"
|
||||
id="act_purchase_form_domain_draft">
|
||||
<field name="name">Draft</field>
|
||||
<field name="sequence" eval="40"/>
|
||||
<field name="domain" eval="['AND',('state', '=', 'draft'),('line_type', '=', 'goods')]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_purchase_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain"
|
||||
id="act_purchase_form_domain_service">
|
||||
<field name="name">Services</field>
|
||||
<field name="sequence" eval="50"/>
|
||||
<field name="domain" eval="[('line_type', '=', 'service')]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_purchase_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain"
|
||||
id="act_purchase_form_domain_exception">
|
||||
<field name="name">Exception</field>
|
||||
<field name="sequence" eval="50"/>
|
||||
<field name="sequence" eval="60"/>
|
||||
<field name="domain"
|
||||
eval="['OR', ('invoice_state', '=', 'exception'), ('shipment_state', '=', 'exception')]"
|
||||
pyson="1"/>
|
||||
@@ -153,7 +153,6 @@ this repository contains the full copyright notices and license terms. -->
|
||||
action="act_purchase_form"
|
||||
sequence="10"
|
||||
id="menu_purchase_form"/>
|
||||
|
||||
<record model="ir.action.act_window" id="act_purchase_invoice_relate">
|
||||
<field name="name">Purchases</field>
|
||||
<field name="res_model">purchase.purchase</field>
|
||||
|
||||
@@ -85,7 +85,7 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<button name="handle_invoice_exception" icon="tryton-forward"/>
|
||||
<button name="handle_shipment_exception" icon="tryton-forward"/>
|
||||
<button name="confirm" icon="tryton-ok"/>
|
||||
<button name="process"/>
|
||||
<button name="process" invisible="1"/>
|
||||
<button name="manual_invoice" icon="tryton-forward"/>
|
||||
</group>
|
||||
<field name="party_lang" invisible="1" colspan="6"/>
|
||||
|
||||
@@ -2,18 +2,4 @@
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<data>
|
||||
<xpath expr="//field[@name='warehouse']" position="after">
|
||||
<label name="carrier"/>
|
||||
<field name="carrier"/>
|
||||
</xpath>
|
||||
<xpath expr="//page[@id='other']" position="before">
|
||||
<page id="costs" string="Costs">
|
||||
<label name="cost_used"/>
|
||||
<group id="cost_used" col="-1" colspan="3">
|
||||
<field name="cost_used" xexpand="0"/>
|
||||
<field name="cost_currency_used"/>
|
||||
<field name="cost_edit" xexpand="0" xalign="0"/>
|
||||
</group>
|
||||
</page>
|
||||
</xpath>
|
||||
</data>
|
||||
|
||||
254
modules/purchase_trade/AGENTS.md
Normal file
254
modules/purchase_trade/AGENTS.md
Normal file
@@ -0,0 +1,254 @@
|
||||
# AGENTS.md - Module `purchase_trade`
|
||||
|
||||
Ce guide complete le `AGENTS.md` racine.
|
||||
Pour ce module, les regles locales ci-dessous priment.
|
||||
|
||||
## 1) Perimetre metier
|
||||
|
||||
Le module `purchase_trade` etend les flux achat/vente Tryton avec une logique
|
||||
de negoce physique:
|
||||
|
||||
- contrats d'achat (`purchase.purchase`, `purchase.line`)
|
||||
- contrats de vente (`sale.sale`, `sale.line`)
|
||||
- lots physiques et virtuels
|
||||
- matching achat/vente
|
||||
- shipments et execution logistique
|
||||
- frais (`fee.fee`)
|
||||
- templates de documents metier et facture
|
||||
|
||||
## 2) Fichiers pivots
|
||||
|
||||
- Contrats achat:
|
||||
- `modules/purchase_trade/purchase.py`
|
||||
- Contrats vente:
|
||||
- `modules/purchase_trade/sale.py`
|
||||
- Lots / matching / invoicing:
|
||||
- `modules/purchase_trade/lot.py`
|
||||
- Shipments / lien facture-lot:
|
||||
- `modules/purchase_trade/stock.py`
|
||||
- Fees:
|
||||
- `modules/purchase_trade/fee.py`
|
||||
- Bridge facture / templates:
|
||||
- `modules/purchase_trade/invoice.py`
|
||||
- Vues:
|
||||
- `modules/purchase_trade/view/*.xml`
|
||||
- Actions module:
|
||||
- `modules/purchase_trade/*.xml`
|
||||
- Manifest:
|
||||
- `modules/purchase_trade/tryton.cfg`
|
||||
|
||||
## 3) Documentation locale a lire en priorite
|
||||
|
||||
- Regles metier:
|
||||
- `modules/purchase_trade/docs/business-rules.md`
|
||||
- Documentation business publiee dans le wiki:
|
||||
- `modules/purchase_trade/docs/business/*.md`
|
||||
- Sources de verite de la documentation business generee:
|
||||
- `modules/purchase_trade/docs_source/business/*.md`
|
||||
- Regles templates:
|
||||
- `modules/purchase_trade/docs/template-rules.md`
|
||||
- Catalogue des proprietes templates:
|
||||
- `modules/purchase_trade/docs/template-properties.md`
|
||||
- Padding facture provisoire vente / validation comptable:
|
||||
- `modules/purchase_trade/docs/padding-invoice-accounting.md`
|
||||
|
||||
## 4) Invariants metier a preserver
|
||||
|
||||
- Un lot `virtual` est la reference d'ouverture de quantite pour une `purchase.line`.
|
||||
- Une `sale.line` doit aussi avoir au minimum un lot `virtual`; une valuation
|
||||
cote sale ne doit donc pas disparaitre juste parce que le lot est `open`.
|
||||
- Le lot physique est le pont principal entre:
|
||||
- `purchase.line`
|
||||
- `sale.line`
|
||||
- shipment
|
||||
- facture
|
||||
- Pour remonter d'une facture vers shipment / BL / controller / fret:
|
||||
- privilegier le lot physique
|
||||
- ne pas multiplier des chemins d'acces concurrents
|
||||
- Pour les champs de colis (`NB BALES`) dans les templates facture:
|
||||
- la source de verite est `line.lot.lot_qt`
|
||||
- sur une facture, sommer les `lot_qt` des lignes de facture
|
||||
- tenir compte du signe de la ligne de facture pour les notes finales
|
||||
- ne pas proratiser depuis le poids (`net` / `gross`)
|
||||
- Le `FREIGHT VALUE` d'un template facture vient du `fee.fee` du shipment
|
||||
dont le produit est `Maritime freight`.
|
||||
- Pour `stock/insurance.fodt`, le `Amount insured` doit venir en priorite de
|
||||
`110%` du total des `incoming_moves` (fallback fee `Insurance` si aucun
|
||||
montant incoming calculable).
|
||||
- Pour le surveyor du certificat d'assurance shipment, la priorite est:
|
||||
`shipment.surveyor` -> `shipment.controller` -> fournisseur du fee
|
||||
`Insurance`.
|
||||
- Pour `payment_order.fodt`, utiliser des proprietes
|
||||
`invoice.report_payment_order_*` plutot que des tokens legacy `<...>`.
|
||||
- Ajouter un champ de template dans `Document Templates` ne rend pas le report
|
||||
visible dans la fiche: il faut aussi l'action `ir.action.report` +
|
||||
`ir.action.keyword` (`form_print`) cote `account.invoice`.
|
||||
- Le wizard `Create contracts` en mode `matched` peut maintenant partir de
|
||||
plusieurs `lot.qt`, mais doit conserver un matching par lot source et laisser
|
||||
`created_by_code = True` sur les lignes creees pour ne pas declencher les
|
||||
creations automatiques de lots dans les validations.
|
||||
- En valuation / PnL:
|
||||
- la valeur stockee dans `type` est la cle technique (`pur. priced`,
|
||||
`sale priced`, `pur. fee`, etc.), pas le label affiche dans l'UI
|
||||
- les references doivent rester coherentes avec le type de lot:
|
||||
`Purchase/Open`, `Purchase/Physic`, `Sale/Open`, `Sale/Physic`
|
||||
- pour une sale matchee, les lignes de valuation purchase generees sur un lot
|
||||
physique doivent aussi renseigner `sale` et `sale_line` afin de remonter
|
||||
dans l'onglet PnL de la sale
|
||||
- une sale non matchee doit etre valorisable "sale-first" et alimenter
|
||||
`valuation.valuation` / `valuation.valuation.line`
|
||||
- si une `sale.line` `basis` n'a ni `price_summary` ni `lot_price_sale`,
|
||||
creer quand meme une ligne `sale priced` avec `price = 0` et `amount = 0`
|
||||
plutot que de ne rien generer
|
||||
- le MTM ne doit etre renseigne que pour `pur. priced`, `sale priced` et
|
||||
`derivative`; jamais pour les fees
|
||||
- `mtm_price` doit afficher le prix brut de valorisation (sans ratio), alors
|
||||
que `mtm` reste le montant calcule selon la logique de strategie
|
||||
- En pricing:
|
||||
- le `unit_price` doit rester un prix de base, hors `premium`
|
||||
- le `premium` doit impacter le prix total economique et donc le `amount`,
|
||||
aussi bien en `priced` qu'en `basis`
|
||||
- dans `pricing.pricing` en saisie manuelle, l'utilisateur renseigne
|
||||
seulement `quantity` et `settl_price`
|
||||
- `fixed_qt`, `fixed_qt_price`, `unfixed_qt`, `unfixed_qt_price` et
|
||||
`eod_price` sont des valeurs derivees et ne doivent pas etre saisies a la
|
||||
main
|
||||
- en manuel, `fixed_qt` = cumul des `quantity` du groupe trie par
|
||||
`pricing_date`
|
||||
- en manuel, `fixed_qt_price` = moyenne ponderee cumulee des `settl_price`
|
||||
- en manuel, `unfixed_qt_price` = `settl_price` de la ligne
|
||||
- pour les documents commerciaux / facture, une ligne `basis` affiche le
|
||||
`premium` comme prix visible, pas le prix economique total
|
||||
- si `linked currency` est active, le `premium` est saisi dans la devise /
|
||||
unite liee (ex: `USC/LB`) puis converti vers le repere de la ligne pour le
|
||||
calcul du `amount`
|
||||
- en `basis + linked currency`, le `linked_price` doit representer le prix
|
||||
basis brut (hors premium) dans la devise liee; le `unit_price` reste ce
|
||||
prix brut converti, et le `premium` converti est ajoute seulement dans
|
||||
l'`amount`
|
||||
- si `linked currency` est cochee, `linked_price`, `linked_currency` et
|
||||
`linked_unit` sont requis
|
||||
- dans les forms, presenter le bloc prix dans l'ordre:
|
||||
`price_type` -> linked fields -> `premium` -> `unit_price` -> `amount`
|
||||
- en valuation `basis`, le premium s'applique a chaque composant, pas
|
||||
uniquement a une ligne de resume
|
||||
- pour une ligne `basis` sans `price_summary`, la valuation fallback doit
|
||||
utiliser `unit_price + premium` (et pas `unit_price` seul)
|
||||
- a la validation d'une `sale.line`, si un lot virtuel est cree et qu'aucun
|
||||
matching purchase n'existe, il faut lancer `generate_from_sale_line()` pour
|
||||
alimenter le PnL sale-first
|
||||
- En padding de facture provisoire vente:
|
||||
- le padding est saisi globalement dans `lot.invoice`
|
||||
- il est uniquement applique aux factures provisoires cote vente
|
||||
- il est reparti par lot et stocke dans `lot.sale_invoice_padding`
|
||||
- la ligne facture affiche `Inc. padding` depuis le lot
|
||||
- le padding augmente `account.invoice.line.quantity`, donc le move principal
|
||||
de facture inclut deja le montant padding
|
||||
- les comptes de padding viennent de `account.configuration`:
|
||||
`Default Sale Padding` et `Default Accrual Padding`
|
||||
- la section `Padding` doit rester avant la section `Invoice` dans
|
||||
`Financial / Configuration`
|
||||
- la provisoire cree un `additional_move` debit `Default Sale Padding` /
|
||||
credit `Default Accrual Padding`
|
||||
- la finale cree l'inverse en reprenant le montant depuis
|
||||
`lot.sale_invoice_line_prov`, donc avec le prix, la devise, la date et le
|
||||
taux de la provisoire
|
||||
- le wizard final doit calculer son delta depuis la quantite provisoire hors
|
||||
padding: `sale_invoice_line_prov.quantity - sale_invoice_padding`
|
||||
- En modification de quantite contractuelle:
|
||||
- `quantity_theorical` est la source contractuelle; `quantity` peut refleter
|
||||
l'execute physique.
|
||||
- le `lot.qt` libre doit etre resynchronise sur le solde ouvert reel:
|
||||
`quantity_theorical - lots physiques - lot.qt deja matches/shippes`.
|
||||
- ne jamais ajuster le `lot.qt` libre uniquement par delta si des lots
|
||||
physiques existent deja.
|
||||
- les fees suivent les lots effectifs: physiques s'il en existe dans
|
||||
`fee.lots`, sinon virtuel.
|
||||
- conserver le lien `fee.lots` vers le virtuel comme fallback; ne pas le
|
||||
supprimer lors de la creation d'un physique.
|
||||
- le PnL fee applique la meme regle: full open tant qu'il n'y a que le
|
||||
virtuel, puis uniquement les physiques.
|
||||
- detail durable:
|
||||
`modules/purchase_trade/docs/business-rules.md` BR-PT-020 / BR-PT-021.
|
||||
- En execution controller / SLA:
|
||||
- les objectifs de repartition controller utilisent `party.execution.area`
|
||||
(`country.region`).
|
||||
- les couts SLA utilisent `party.execution.place` et peuvent matcher par
|
||||
`country`, par `location`, ou par couple `country + location`.
|
||||
- la creation du fee controller depuis shipment part de
|
||||
`shipment.to_location`; le pays de matching est
|
||||
`shipment.to_location.country`.
|
||||
- priorite cout SLA: couple pays+location, puis location seule, puis pays
|
||||
seul.
|
||||
|
||||
## 5) Conventions de modification
|
||||
|
||||
### Documentation business
|
||||
|
||||
- Ne pas modifier directement une page generee sous
|
||||
`modules/purchase_trade/docs/business/` si elle contient le commentaire
|
||||
`Generated from docs_source/business`.
|
||||
- Toute regle business nouvelle ou modifiee doit etre editee dans
|
||||
`modules/purchase_trade/docs_source/business/`.
|
||||
- Apres modification des sources business, regenerer le wiki avec:
|
||||
`python modules/purchase_trade/docs/tools/render_business_docs.py`
|
||||
- Avant de rendre une modification documentaire, verifier que le rendu publie
|
||||
est synchronise avec:
|
||||
`python modules/purchase_trade/docs/tools/render_business_docs.py --check`
|
||||
- Les pages francaises et anglaises miroir doivent rester synchronisees.
|
||||
- Le rendu publie doit rester lisible dans MkDocs meme sans extensions
|
||||
optionnelles: eviter les marqueurs bruts `!!!` et `:material-...:` dans les
|
||||
fichiers publies.
|
||||
|
||||
1. Modifier la logique metier dans le fichier pivot le plus proche.
|
||||
2. Si un template `.fodt` devient complexe, deplacer la logique dans une
|
||||
propriete Python `report_*`.
|
||||
3. Pour une facture trade, preferer enrichir `modules/purchase_trade/invoice.py`
|
||||
plutot que surcharger lourdement le `.fodt`.
|
||||
4. Si une regle metier durable change, mettre a jour
|
||||
`docs/business-rules.md`.
|
||||
5. Si une convention de template change, mettre a jour
|
||||
`docs/template-rules.md`.
|
||||
6. Pour les vues XML Tryton de ce module, utiliser `editable="1"` sur les
|
||||
`<tree>` editables; ne pas utiliser `editable="bottom"`.
|
||||
7. Ne pas ajouter `keyword_open` / `open_keyword` sur les vues `<tree>`:
|
||||
ces attributs ne sont pas supportes par Tryton.
|
||||
8. Si une regle de texte par defaut durable est demandee sur achat/vente,
|
||||
preferer un singleton de configuration expose dans un menu fonctionnel
|
||||
existant plutot qu'un menu technique `purchase_trade`.
|
||||
|
||||
## 6) Pieges connus
|
||||
|
||||
- Plusieurs actions de report `account.invoice` peuvent sembler rendre le meme
|
||||
document a cause du cache `invoice_report_cache`.
|
||||
- Les reports alternatifs (`Final Invoice`, `Prepayment`, etc.) ne doivent pas
|
||||
reutiliser le cache du report standard sans verification.
|
||||
- Pour les donnees achat/vente partagees, ne pas supposer qu'une facture de
|
||||
vente doit lire directement sur la `sale.line`: souvent, la verite metier
|
||||
passe par le lot physique et/ou la `account.invoice.line`.
|
||||
- Les templates `invoice_ict*` peuvent partager les memes proprietes Python;
|
||||
si une regle doit valoir pour provisional et final, la mettre dans
|
||||
`modules/purchase_trade/invoice.py` plutot que dupliquer dans les `.fodt`.
|
||||
- Dans les ecrans PnL, le label `Sale price` correspond au type stocke
|
||||
`sale priced`; idem pour `Pur. price` / `pur. priced`.
|
||||
- Une ligne `basis` sans resume de pricing peut sinon disparaitre de la
|
||||
valuation si aucun fallback explicite a `0` n'est prevu.
|
||||
- Le calcul du prix peut diverger entre `unit_price`, `linked_price`,
|
||||
`lot_price` et valuation si le premium n'est pas traite explicitement dans
|
||||
chaque maillon.
|
||||
- Sur `account.invoice`, le workflow `Validate` doit maintenant aligner
|
||||
fournisseur et client pour:
|
||||
- creation du `account.move`
|
||||
- attribution du `number`
|
||||
- `Post` ne doit pas reintroduire une difference de session/fresh login cote
|
||||
client
|
||||
- Pour le padding provisoire vente, ne pas changer la quantite physique du lot:
|
||||
l'ecart doit rester porte par `lot.sale_invoice_padding` et visible sur la
|
||||
facture via `Inc. padding`.
|
||||
|
||||
## 7) Definition of done (module `purchase_trade`)
|
||||
|
||||
- Le flux achat/vente/lot cible reste coherent.
|
||||
- Les impacts templates/facture ont ete verifies conceptuellement.
|
||||
- Les docs locales ont ete mises a jour si une nouvelle regle durable a emerge.
|
||||
- Le patch reste minimal et local au domaine demande.
|
||||
@@ -5,9 +5,11 @@ from trytond.pool import Pool
|
||||
|
||||
from . import (
|
||||
account,
|
||||
configuration,
|
||||
purchase,
|
||||
sale,
|
||||
global_reporting,
|
||||
ctrm_reporting,
|
||||
stock,
|
||||
derivative,
|
||||
lot,
|
||||
@@ -32,9 +34,11 @@ from . import (
|
||||
dimension,
|
||||
weight_report,
|
||||
backtoback,
|
||||
service,
|
||||
invoice,
|
||||
)
|
||||
service,
|
||||
invoice,
|
||||
duplicate,
|
||||
coffee,
|
||||
)
|
||||
|
||||
def register():
|
||||
Pool.register(
|
||||
@@ -56,14 +60,68 @@ def register():
|
||||
lc.LCMessage,
|
||||
lc.CreateLCStart,
|
||||
global_reporting.GRConfiguration,
|
||||
module='purchase_trade', type_='model')
|
||||
ctrm_reporting.CTRMPhysicalPosition,
|
||||
ctrm_reporting.CTRMPhysicalPositionContext,
|
||||
ctrm_reporting.CTRMFinancialPosition,
|
||||
ctrm_reporting.CTRMFinancialPositionContext,
|
||||
ctrm_reporting.CTRMNetPosition,
|
||||
ctrm_reporting.CTRMNetPositionContext,
|
||||
ctrm_reporting.CTRMRealizedPnl,
|
||||
ctrm_reporting.CTRMRealizedPnlContext,
|
||||
ctrm_reporting.CTRMMtmPnl,
|
||||
ctrm_reporting.CTRMMtmPnlContext,
|
||||
ctrm_reporting.CTRMPnlExplain,
|
||||
ctrm_reporting.CTRMPnlExplainContext,
|
||||
ctrm_reporting.CTRMPnlDimension,
|
||||
ctrm_reporting.CTRMPnlDimensionContext,
|
||||
ctrm_reporting.CTRMCreditRisk,
|
||||
ctrm_reporting.CTRMCreditRiskContext,
|
||||
ctrm_reporting.CTRMShippingLogistics,
|
||||
ctrm_reporting.CTRMShippingLogisticsContext,
|
||||
ctrm_reporting.CTRMInventory,
|
||||
ctrm_reporting.CTRMInventoryContext,
|
||||
ctrm_reporting.CTRMContractPerformance,
|
||||
ctrm_reporting.CTRMContractPerformanceContext,
|
||||
ctrm_reporting.CTRMScheduling,
|
||||
ctrm_reporting.CTRMSchedulingContext,
|
||||
ctrm_reporting.CTRMAccruals,
|
||||
ctrm_reporting.CTRMAccrualsContext,
|
||||
ctrm_reporting.CTRMSettlements,
|
||||
ctrm_reporting.CTRMSettlementsContext,
|
||||
ctrm_reporting.CTRMCashFlow,
|
||||
ctrm_reporting.CTRMCashFlowContext,
|
||||
ctrm_reporting.CTRMGLReconciliation,
|
||||
ctrm_reporting.CTRMGLReconciliationContext,
|
||||
configuration.Configuration,
|
||||
pricing.ImportPricesStart,
|
||||
pricing.ImportPricesResult,
|
||||
coffee.CoffeeSample,
|
||||
coffee.CoffeeCuppingCriterion,
|
||||
coffee.CoffeeCuppingSession,
|
||||
coffee.CoffeeCuppingSessionSample,
|
||||
coffee.CoffeeCuppingCup,
|
||||
coffee.CoffeeCuppingResult,
|
||||
coffee.CoffeeCuppingResultLine,
|
||||
coffee.CoffeeLabAnalysis,
|
||||
coffee.PurchaseLine,
|
||||
coffee.SaleLine,
|
||||
duplicate.TradeCustomDuplicateStart,
|
||||
module='purchase_trade', type_='model')
|
||||
Pool.register(
|
||||
configuration.PurchaseConfiguration,
|
||||
module='purchase', type_='model')
|
||||
Pool.register(
|
||||
configuration.SaleConfiguration,
|
||||
module='sale', type_='model')
|
||||
Pool.register(
|
||||
incoming.ImportSwift,
|
||||
incoming.PrepareDocuments,
|
||||
incoming.AnalyzeConditions,
|
||||
lc.CreateLCWizard,
|
||||
module='purchase_trade', type_='wizard'
|
||||
)
|
||||
incoming.ImportSwift,
|
||||
incoming.PrepareDocuments,
|
||||
incoming.AnalyzeConditions,
|
||||
lc.CreateLCWizard,
|
||||
pricing.ImportPrices,
|
||||
duplicate.TradeCustomDuplicate,
|
||||
module='purchase_trade', type_='wizard'
|
||||
)
|
||||
Pool.register(
|
||||
credit_risk.Party,
|
||||
credit_risk.CreditRiskRule,
|
||||
@@ -83,6 +141,15 @@ def register():
|
||||
party.PartyExecutionPlace,
|
||||
payment_term.PaymentTerm,
|
||||
payment_term.PaymentTermLine,
|
||||
stock.CharterRateType,
|
||||
stock.CharterConditionRate,
|
||||
stock.CharterConditionLaytimeStartRule,
|
||||
stock.CharterCondition,
|
||||
stock.CharterParty,
|
||||
purchase.ContractClause,
|
||||
purchase.ContractTemplate,
|
||||
purchase.ContractTemplateLine,
|
||||
purchase.ContractClauseSelection,
|
||||
purchase.Purchase,
|
||||
purchase.Line,
|
||||
purchase.Estimated,
|
||||
@@ -101,6 +168,7 @@ def register():
|
||||
open_position.OpenPositionContext,
|
||||
optional.OptionalScenario,
|
||||
fee.Fee,
|
||||
fee.FeeRule,
|
||||
fee.FeeLots,
|
||||
purchase.FeeLots,
|
||||
valuation.Valuation,
|
||||
@@ -108,6 +176,9 @@ def register():
|
||||
valuation.ValuationDyn,
|
||||
valuation.ValuationReport,
|
||||
valuation.ValuationReportContext,
|
||||
valuation.ValuationProcessDimension,
|
||||
valuation.ValuationProcessStart,
|
||||
valuation.ValuationProcessResult,
|
||||
derivative.Derivative,
|
||||
derivative.DerivativeMatch,
|
||||
derivative.MatchWizardStart,
|
||||
@@ -115,12 +186,13 @@ def register():
|
||||
derivative.DerivativeReportContext,
|
||||
fee.FeeReport,
|
||||
fee.FeeContext,
|
||||
forex.ForexCoverPhysicalContract,
|
||||
forex.PForex,
|
||||
forex.ForexBI,
|
||||
purchase.PnlBI,
|
||||
purchase.PositionBI,
|
||||
stock.Move,
|
||||
forex.ForexCoverPhysicalContract,
|
||||
forex.PForex,
|
||||
forex.ForexBI,
|
||||
purchase.PurchasePnlGraph,
|
||||
purchase.PnlBI,
|
||||
purchase.PositionBI,
|
||||
stock.Move,
|
||||
stock.Location,
|
||||
stock.InvoiceLine,
|
||||
stock.ShipmentIn,
|
||||
@@ -132,16 +204,18 @@ def register():
|
||||
stock.ImportSoFStart,
|
||||
stock.RevaluateStart,
|
||||
stock.Account,
|
||||
stock.AccountMoveLine,
|
||||
stock.AccountMove,
|
||||
stock.ContainerType,
|
||||
stock.ShipmentContainer,
|
||||
lot.Lot,
|
||||
lot.LotQt,
|
||||
lot.LotReport,
|
||||
lot.LotContext,
|
||||
lot.LotShippingStart,
|
||||
lot.LotMatchingStart,
|
||||
stock.AccountMoveLine,
|
||||
stock.AccountMove,
|
||||
stock.ContainerType,
|
||||
stock.ShipmentContainer,
|
||||
lot.Lot,
|
||||
lot.LotQt,
|
||||
lot.LotReport,
|
||||
lot.LotContext,
|
||||
lot.LotShippingStart,
|
||||
lot.LotPlanTransportStart,
|
||||
lot.LotPlanTransportLine,
|
||||
lot.LotMatchingStart,
|
||||
lot.LotWeighingStart,
|
||||
lot.LotAddLot,
|
||||
lot.LotInvoicingLot,
|
||||
@@ -170,17 +244,18 @@ def register():
|
||||
purchase.DocTypeTemplate,
|
||||
purchase.PurchaseStrategy,
|
||||
purchase.PriceComposition,
|
||||
purchase.PremiumComposition,
|
||||
purchase.QualityAnalysis,
|
||||
purchase.Assay,
|
||||
purchase.AssayLine,
|
||||
purchase.AssayElement,
|
||||
purchase.AssayUnit,
|
||||
purchase.PayableRule,
|
||||
purchase.PenaltyRule,
|
||||
purchase.PenaltyRuleTier,
|
||||
purchase.ConcentrateTerm,
|
||||
backtoback.Backtoback,
|
||||
dimension.AnalyticDimension,
|
||||
purchase.PenaltyRule,
|
||||
purchase.PenaltyRuleTier,
|
||||
purchase.ConcentrateTerm,
|
||||
backtoback.Backtoback,
|
||||
dimension.AnalyticDimension,
|
||||
dimension.AnalyticDimensionValue,
|
||||
dimension.AnalyticDimensionAssignment,
|
||||
weight_report.WeightReport,
|
||||
@@ -188,6 +263,13 @@ def register():
|
||||
Pool.register(
|
||||
account.PhysicalTradeIFRS,
|
||||
module='purchase_trade', type_='model')
|
||||
Pool.register(
|
||||
configuration.AccountConfiguration,
|
||||
configuration.AccountConfigurationDefaultAccount,
|
||||
invoice.InvoiceLineLotWeight,
|
||||
invoice.InvoicePaddingReport,
|
||||
invoice.InvoicePaddingContext,
|
||||
module='purchase_trade', type_='model')
|
||||
Pool.register(
|
||||
invoice.Invoice,
|
||||
invoice.InvoiceLine,
|
||||
@@ -213,6 +295,8 @@ def register():
|
||||
purchase.PurchaseCrop,
|
||||
module='lot', type_='model')
|
||||
Pool.register(
|
||||
sale.ContractClauseSelection,
|
||||
sale.CharterCondition,
|
||||
sale.Sale,
|
||||
sale.SaleLine,
|
||||
sale.SaleCreatePurchaseInput,
|
||||
@@ -235,11 +319,14 @@ def register():
|
||||
sale.OpenPosition,
|
||||
sale.Backtoback,
|
||||
sale.AnalyticDimensionAssignment,
|
||||
sale.PriceComposition,
|
||||
module='sale', type_='model')
|
||||
sale.PriceComposition,
|
||||
sale.PremiumComposition,
|
||||
module='sale', type_='model')
|
||||
Pool.register(
|
||||
lot.LotShipping,
|
||||
lot.LotMatching,
|
||||
lot.LotShipping,
|
||||
lot.LotPlanTransport,
|
||||
lot.LotMatching,
|
||||
lot.LotGoMatching,
|
||||
#lot.LotMatchingUnit,
|
||||
lot.LotWeighing,
|
||||
lot.CreateContracts,
|
||||
@@ -256,16 +343,28 @@ def register():
|
||||
purchase_prepayment.CreatePrepaymentWizard,
|
||||
purchase.PurchaseAllocationsWizard,
|
||||
purchase.InvoicePayment,
|
||||
stock.ImportSoFWizard,
|
||||
dashboard.BotWizard,
|
||||
dashboard.DashboardLoader,
|
||||
forex.ForexReport,
|
||||
purchase.PnlReport,
|
||||
stock.ImportSoFWizard,
|
||||
dashboard.BotWizard,
|
||||
dashboard.DashboardLoader,
|
||||
forex.ForexReport,
|
||||
purchase.PnlReport,
|
||||
purchase.PositionReport,
|
||||
valuation.ValuationProcess,
|
||||
derivative.DerivativeMatchWizard,
|
||||
module='purchase', type_='wizard')
|
||||
Pool.register(
|
||||
sale.SaleCreatePurchase,
|
||||
sale.SaleAllocationsWizard,
|
||||
module='sale', type_='wizard')
|
||||
Pool.register(
|
||||
invoice.InvoiceReport,
|
||||
invoice.SaleReport,
|
||||
invoice.PurchaseReport,
|
||||
stock.ShipmentShippingReport,
|
||||
stock.ShipmentInsuranceReport,
|
||||
stock.ShipmentCOOReport,
|
||||
stock.ShipmentPackingListReport,
|
||||
stock.ShipmentLinkageReport,
|
||||
stock.LotReportLinkageReport,
|
||||
module='purchase_trade', type_='report')
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# account.py
|
||||
from trytond.model import ModelSQL, ModelView, fields
|
||||
from trytond.pool import PoolMeta
|
||||
from trytond.pyson import Eval
|
||||
|
||||
__all__ = ['PhysicalTradeIFRS']
|
||||
__all__ = [
|
||||
'PhysicalTradeIFRS',
|
||||
]
|
||||
__metaclass__ = PoolMeta
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
<tryton>
|
||||
<data>
|
||||
<record model="ir.ui.view" id="account_configuration_view_form">
|
||||
<field name="model">account.configuration</field>
|
||||
<field name="inherit" ref="account.configuration_view_form"/>
|
||||
<field name="name">account_configuration_form</field>
|
||||
</record>
|
||||
|
||||
<record model="res.group" id="group_physical_trade_ifrs">
|
||||
<field name="name">Physical Trade IFRS</field>
|
||||
</record>
|
||||
|
||||
1170
modules/purchase_trade/coffee.py
Normal file
1170
modules/purchase_trade/coffee.py
Normal file
File diff suppressed because it is too large
Load Diff
390
modules/purchase_trade/coffee.xml
Normal file
390
modules/purchase_trade/coffee.xml
Normal file
@@ -0,0 +1,390 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<tryton>
|
||||
<data>
|
||||
<record model="ir.ui.view" id="coffee_sample_view_tree">
|
||||
<field name="model">coffee.sample</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">coffee_sample_tree</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="coffee_sample_view_form">
|
||||
<field name="model">coffee.sample</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">coffee_sample_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="coffee_cupping_criterion_view_tree">
|
||||
<field name="model">coffee.cupping.criterion</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">coffee_cupping_criterion_tree</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="coffee_cupping_criterion_view_form">
|
||||
<field name="model">coffee.cupping.criterion</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">coffee_cupping_criterion_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="coffee_cupping_session_view_tree">
|
||||
<field name="model">coffee.cupping.session</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">coffee_cupping_session_tree</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="coffee_cupping_session_view_form">
|
||||
<field name="model">coffee.cupping.session</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">coffee_cupping_session_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="coffee_cupping_session_sample_view_tree">
|
||||
<field name="model">coffee.cupping.session.sample</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">coffee_cupping_session_sample_tree</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="coffee_cupping_session_sample_view_form">
|
||||
<field name="model">coffee.cupping.session.sample</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">coffee_cupping_session_sample_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="coffee_cupping_cup_view_tree">
|
||||
<field name="model">coffee.cupping.cup</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">coffee_cupping_cup_tree</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="coffee_cupping_cup_view_form">
|
||||
<field name="model">coffee.cupping.cup</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">coffee_cupping_cup_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="coffee_cupping_result_view_tree">
|
||||
<field name="model">coffee.cupping.result</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">coffee_cupping_result_tree</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="coffee_cupping_result_view_form">
|
||||
<field name="model">coffee.cupping.result</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">coffee_cupping_result_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="coffee_cupping_result_line_view_tree">
|
||||
<field name="model">coffee.cupping.result.line</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">coffee_cupping_result_line_tree</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="coffee_lab_analysis_view_tree">
|
||||
<field name="model">coffee.lab.analysis</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">coffee_lab_analysis_tree</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="coffee_lab_analysis_view_form">
|
||||
<field name="model">coffee.lab.analysis</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">coffee_lab_analysis_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_coffee_sample_form">
|
||||
<field name="name">Coffee Samples</field>
|
||||
<field name="res_model">coffee.sample</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_coffee_sample_form_view_tree">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="coffee_sample_view_tree"/>
|
||||
<field name="act_window" ref="act_coffee_sample_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_coffee_sample_form_view_form">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="coffee_sample_view_form"/>
|
||||
<field name="act_window" ref="act_coffee_sample_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_coffee_sample_domain_pending">
|
||||
<field name="name">Pending</field>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="domain" eval="[('state', 'in', ['requested', 'received', 'sent_to_lab', 'under_review'])]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_coffee_sample_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_coffee_sample_domain_expiring">
|
||||
<field name="name">Expiring soon</field>
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="domain" eval="[('expires_soon', '=', True)]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_coffee_sample_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_coffee_sample_domain_expired">
|
||||
<field name="name">Expired</field>
|
||||
<field name="sequence" eval="30"/>
|
||||
<field name="domain" eval="[('is_expired', '=', True)]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_coffee_sample_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_coffee_sample_domain_approved">
|
||||
<field name="name">Approved</field>
|
||||
<field name="sequence" eval="40"/>
|
||||
<field name="domain" eval="[('state', '=', 'approved')]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_coffee_sample_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_coffee_sample_domain_all">
|
||||
<field name="name">All</field>
|
||||
<field name="sequence" eval="9999"/>
|
||||
<field name="domain"></field>
|
||||
<field name="act_window" ref="act_coffee_sample_form"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_coffee_cupping_session_form">
|
||||
<field name="name">Cupping Sessions</field>
|
||||
<field name="res_model">coffee.cupping.session</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_coffee_cupping_session_form_view_tree">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="coffee_cupping_session_view_tree"/>
|
||||
<field name="act_window" ref="act_coffee_cupping_session_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_coffee_cupping_session_form_view_form">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="coffee_cupping_session_view_form"/>
|
||||
<field name="act_window" ref="act_coffee_cupping_session_form"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_coffee_cupping_criterion_form">
|
||||
<field name="name">Cupping Criteria</field>
|
||||
<field name="res_model">coffee.cupping.criterion</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_coffee_cupping_criterion_form_view_tree">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="coffee_cupping_criterion_view_tree"/>
|
||||
<field name="act_window" ref="act_coffee_cupping_criterion_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_coffee_cupping_criterion_form_view_form">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="coffee_cupping_criterion_view_form"/>
|
||||
<field name="act_window" ref="act_coffee_cupping_criterion_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window" id="act_coffee_lab_analysis_form">
|
||||
<field name="name">Lab Analyses</field>
|
||||
<field name="res_model">coffee.lab.analysis</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_coffee_lab_analysis_form_view_tree">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="coffee_lab_analysis_view_tree"/>
|
||||
<field name="act_window" ref="act_coffee_lab_analysis_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_coffee_lab_analysis_form_view_form">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="coffee_lab_analysis_view_form"/>
|
||||
<field name="act_window" ref="act_coffee_lab_analysis_form"/>
|
||||
</record>
|
||||
|
||||
<menuitem name="Coffee" parent="purchase.menu_purchase" sequence="18"
|
||||
id="menu_coffee_purchase"/>
|
||||
<menuitem name="Samples" parent="menu_coffee_purchase" sequence="10"
|
||||
action="act_coffee_sample_form" id="menu_coffee_sample_purchase"/>
|
||||
<menuitem name="Lab Analyses" parent="menu_coffee_purchase"
|
||||
sequence="15" action="act_coffee_lab_analysis_form"
|
||||
id="menu_coffee_lab_analysis_purchase"/>
|
||||
<menuitem name="Cupping Sessions" parent="menu_coffee_purchase"
|
||||
sequence="20" action="act_coffee_cupping_session_form"
|
||||
id="menu_coffee_cupping_session_purchase"/>
|
||||
<menuitem name="Cupping Criteria" parent="menu_coffee_purchase"
|
||||
sequence="30" action="act_coffee_cupping_criterion_form"
|
||||
id="menu_coffee_cupping_criterion_purchase"/>
|
||||
<menuitem name="Coffee" parent="sale.menu_sale" sequence="18"
|
||||
id="menu_coffee_sale"/>
|
||||
<menuitem name="Samples" parent="menu_coffee_sale" sequence="10"
|
||||
action="act_coffee_sample_form" id="menu_coffee_sample_sale"/>
|
||||
<menuitem name="Lab Analyses" parent="menu_coffee_sale"
|
||||
sequence="15" action="act_coffee_lab_analysis_form"
|
||||
id="menu_coffee_lab_analysis_sale"/>
|
||||
<menuitem name="Cupping Sessions" parent="menu_coffee_sale"
|
||||
sequence="20" action="act_coffee_cupping_session_form"
|
||||
id="menu_coffee_cupping_session_sale"/>
|
||||
<menuitem name="Cupping Criteria" parent="menu_coffee_sale"
|
||||
sequence="30" action="act_coffee_cupping_criterion_form"
|
||||
id="menu_coffee_cupping_criterion_sale"/>
|
||||
<record model="ir.ui.menu" id="menu_coffee_purchase">
|
||||
<field name="active" eval="False"/>
|
||||
</record>
|
||||
<record model="ir.ui.menu" id="menu_coffee_sample_purchase">
|
||||
<field name="active" eval="False"/>
|
||||
</record>
|
||||
<record model="ir.ui.menu" id="menu_coffee_lab_analysis_purchase">
|
||||
<field name="active" eval="False"/>
|
||||
</record>
|
||||
<record model="ir.ui.menu" id="menu_coffee_cupping_session_purchase">
|
||||
<field name="active" eval="False"/>
|
||||
</record>
|
||||
<record model="ir.ui.menu" id="menu_coffee_cupping_criterion_purchase">
|
||||
<field name="active" eval="False"/>
|
||||
</record>
|
||||
<record model="ir.ui.menu" id="menu_coffee_sale">
|
||||
<field name="active" eval="False"/>
|
||||
</record>
|
||||
<record model="ir.ui.menu" id="menu_coffee_sample_sale">
|
||||
<field name="active" eval="False"/>
|
||||
</record>
|
||||
<record model="ir.ui.menu" id="menu_coffee_lab_analysis_sale">
|
||||
<field name="active" eval="False"/>
|
||||
</record>
|
||||
<record model="ir.ui.menu" id="menu_coffee_cupping_session_sale">
|
||||
<field name="active" eval="False"/>
|
||||
</record>
|
||||
<record model="ir.ui.menu" id="menu_coffee_cupping_criterion_sale">
|
||||
<field name="active" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button" id="coffee_sample_receive_button">
|
||||
<field name="model">coffee.sample</field>
|
||||
<field name="name">receive</field>
|
||||
<field name="string">Receive</field>
|
||||
</record>
|
||||
<record model="ir.model.button" id="coffee_sample_send_to_lab_button">
|
||||
<field name="model">coffee.sample</field>
|
||||
<field name="name">send_to_lab</field>
|
||||
<field name="string">Send to lab</field>
|
||||
</record>
|
||||
<record model="ir.model.button" id="coffee_sample_review_button">
|
||||
<field name="model">coffee.sample</field>
|
||||
<field name="name">review</field>
|
||||
<field name="string">Review</field>
|
||||
</record>
|
||||
<record model="ir.model.button" id="coffee_sample_approve_button">
|
||||
<field name="model">coffee.sample</field>
|
||||
<field name="name">approve</field>
|
||||
<field name="string">Approve</field>
|
||||
</record>
|
||||
<record model="ir.model.button" id="coffee_sample_reject_button">
|
||||
<field name="model">coffee.sample</field>
|
||||
<field name="name">reject</field>
|
||||
<field name="string">Reject</field>
|
||||
</record>
|
||||
<record model="ir.model.button" id="coffee_sample_evaluate_quality_button">
|
||||
<field name="model">coffee.sample</field>
|
||||
<field name="name">evaluate_quality</field>
|
||||
<field name="string">Evaluate quality</field>
|
||||
</record>
|
||||
<record model="ir.model.button" id="coffee_sample_expire_button">
|
||||
<field name="model">coffee.sample</field>
|
||||
<field name="name">expire</field>
|
||||
<field name="string">Expire</field>
|
||||
</record>
|
||||
<record model="ir.model.button" id="coffee_sample_archive_button">
|
||||
<field name="model">coffee.sample</field>
|
||||
<field name="name">archive</field>
|
||||
<field name="string">Archive</field>
|
||||
</record>
|
||||
<record model="ir.model.button" id="coffee_sample_reset_to_requested_button">
|
||||
<field name="model">coffee.sample</field>
|
||||
<field name="name">reset_to_requested</field>
|
||||
<field name="string">Reset</field>
|
||||
</record>
|
||||
<record model="ir.model.button" id="coffee_cupping_session_prepare_button">
|
||||
<field name="model">coffee.cupping.session</field>
|
||||
<field name="name">prepare</field>
|
||||
<field name="string">Prepare</field>
|
||||
</record>
|
||||
<record model="ir.model.button" id="coffee_cupping_session_start_button">
|
||||
<field name="model">coffee.cupping.session</field>
|
||||
<field name="name">start</field>
|
||||
<field name="string">Start</field>
|
||||
</record>
|
||||
<record model="ir.model.button" id="coffee_cupping_session_done_button">
|
||||
<field name="model">coffee.cupping.session</field>
|
||||
<field name="name">done</field>
|
||||
<field name="string">Done</field>
|
||||
</record>
|
||||
<record model="ir.model.button" id="coffee_cupping_session_cancel_button">
|
||||
<field name="model">coffee.cupping.session</field>
|
||||
<field name="name">cancel</field>
|
||||
<field name="string">Cancel</field>
|
||||
</record>
|
||||
<record model="ir.model.button" id="coffee_cupping_session_draft_button">
|
||||
<field name="model">coffee.cupping.session</field>
|
||||
<field name="name">draft</field>
|
||||
<field name="string">Draft</field>
|
||||
</record>
|
||||
<record model="ir.model.button" id="coffee_lab_analysis_send_button">
|
||||
<field name="model">coffee.lab.analysis</field>
|
||||
<field name="name">send</field>
|
||||
<field name="string">Send</field>
|
||||
</record>
|
||||
<record model="ir.model.button" id="coffee_lab_analysis_receive_button">
|
||||
<field name="model">coffee.lab.analysis</field>
|
||||
<field name="name">receive</field>
|
||||
<field name="string">Receive</field>
|
||||
</record>
|
||||
<record model="ir.model.button" id="coffee_lab_analysis_accept_button">
|
||||
<field name="model">coffee.lab.analysis</field>
|
||||
<field name="name">accept</field>
|
||||
<field name="string">Accept</field>
|
||||
</record>
|
||||
<record model="ir.model.button" id="coffee_lab_analysis_reject_button">
|
||||
<field name="model">coffee.lab.analysis</field>
|
||||
<field name="name">reject</field>
|
||||
<field name="string">Reject</field>
|
||||
</record>
|
||||
<record model="ir.model.button" id="coffee_lab_analysis_cancel_button">
|
||||
<field name="model">coffee.lab.analysis</field>
|
||||
<field name="name">cancel</field>
|
||||
<field name="string">Cancel</field>
|
||||
</record>
|
||||
<record model="ir.model.button" id="coffee_lab_analysis_draft_button">
|
||||
<field name="model">coffee.lab.analysis</field>
|
||||
<field name="name">draft</field>
|
||||
<field name="string">Draft</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_coffee_sample">
|
||||
<field name="model">coffee.sample</field>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.model.access" id="access_coffee_cupping_criterion">
|
||||
<field name="model">coffee.cupping.criterion</field>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.model.access" id="access_coffee_cupping_session">
|
||||
<field name="model">coffee.cupping.session</field>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.model.access" id="access_coffee_cupping_session_sample">
|
||||
<field name="model">coffee.cupping.session.sample</field>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.model.access" id="access_coffee_cupping_cup">
|
||||
<field name="model">coffee.cupping.cup</field>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.model.access" id="access_coffee_cupping_result">
|
||||
<field name="model">coffee.cupping.result</field>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.model.access" id="access_coffee_cupping_result_line">
|
||||
<field name="model">coffee.cupping.result.line</field>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.model.access" id="access_coffee_lab_analysis">
|
||||
<field name="model">coffee.lab.analysis</field>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
144
modules/purchase_trade/company_defaults.py
Normal file
144
modules/purchase_trade/company_defaults.py
Normal file
@@ -0,0 +1,144 @@
|
||||
from trytond.pool import Pool
|
||||
from trytond.transaction import Transaction
|
||||
import datetime
|
||||
|
||||
|
||||
ITSA_COMPANY_NAME = 'ITSA'
|
||||
|
||||
|
||||
def record_id(record):
|
||||
return getattr(record, 'id', record)
|
||||
|
||||
|
||||
def is_itsa_company(company=None):
|
||||
if company is None:
|
||||
company_id = Transaction().context.get('company')
|
||||
if not company_id:
|
||||
return False
|
||||
Company = Pool().get('company.company')
|
||||
company = Company(company_id)
|
||||
party = getattr(company, 'party', None)
|
||||
return bool(party and party.name == ITSA_COMPANY_NAME)
|
||||
|
||||
|
||||
def _search_first(model_name, domains):
|
||||
Model = Pool().get(model_name)
|
||||
for domain in domains:
|
||||
try:
|
||||
records = Model.search(domain, limit=1)
|
||||
except Exception:
|
||||
continue
|
||||
if records:
|
||||
return records[0]
|
||||
|
||||
|
||||
def default_itsa_currency():
|
||||
currency = _search_first('currency.currency', [
|
||||
[('code', '=', 'USD')],
|
||||
[('name', '=', 'USD')],
|
||||
[('symbol', '=', 'USD')],
|
||||
])
|
||||
return record_id(currency) if currency else None
|
||||
|
||||
|
||||
def default_itsa_price_fix_type():
|
||||
fix_type = _search_first('price.fixtype', [
|
||||
[('name', '=', 'Market Price')],
|
||||
[('name', '=', 'Market price')],
|
||||
[('name', 'ilike', 'Market Price')],
|
||||
])
|
||||
return record_id(fix_type) if fix_type else None
|
||||
|
||||
|
||||
def default_itsa_price_calendar():
|
||||
calendar = _search_first('price.calendar', [
|
||||
[('name', '=', 'Argus Sulphuric Acid')],
|
||||
[('name', 'ilike', 'Argus Sulphuric Acid')],
|
||||
])
|
||||
return record_id(calendar) if calendar else None
|
||||
|
||||
|
||||
def default_itsa_unit():
|
||||
unit = _search_first('product.uom', [
|
||||
[('symbol', '=', 'Mt')],
|
||||
[('name', '=', 'Mt')],
|
||||
[('symbol', '=', 'MT')],
|
||||
[('name', '=', 'MT')],
|
||||
])
|
||||
return record_id(unit) if unit else None
|
||||
|
||||
|
||||
def _company_from_values(values):
|
||||
company = values.get('company')
|
||||
if company and hasattr(company, 'party'):
|
||||
return company
|
||||
company_id = record_id(company) or Transaction().context.get('company')
|
||||
if not company_id:
|
||||
return None
|
||||
return Pool().get('company.company')(company_id)
|
||||
|
||||
|
||||
def _dimension(name):
|
||||
return _search_first('analytic.dimension', [
|
||||
[('code', '=', name)],
|
||||
[('name', '=', name)],
|
||||
[('code', 'ilike', '%%%s%%' % name)],
|
||||
[('name', 'ilike', '%%%s%%' % name)],
|
||||
])
|
||||
|
||||
|
||||
def _book_year_suffix(today=None):
|
||||
if today is None:
|
||||
today = Pool().get('ir.date').today()
|
||||
year = today.year
|
||||
if today < datetime.date(year, 4, 1):
|
||||
year -= 1
|
||||
return '%02d' % (year % 100)
|
||||
|
||||
|
||||
def _book_value(dimension, today=None):
|
||||
if not dimension:
|
||||
return None
|
||||
suffix = _book_year_suffix(today)
|
||||
return _search_first('analytic.dimension.value', [
|
||||
[
|
||||
('dimension', '=', record_id(dimension)),
|
||||
('code', 'ilike', '%%%s%%' % suffix),
|
||||
],
|
||||
[
|
||||
('dimension', '=', record_id(dimension)),
|
||||
('name', 'ilike', '%%%s%%' % suffix),
|
||||
],
|
||||
])
|
||||
|
||||
|
||||
def default_itsa_analytic_dimension_assignments(company=None, today=None):
|
||||
if not is_itsa_company(company):
|
||||
return []
|
||||
|
||||
assignments = []
|
||||
book = _dimension('Book')
|
||||
if book:
|
||||
value = _book_value(book, today=today)
|
||||
assignments.append({
|
||||
'dimension': record_id(book),
|
||||
'value': record_id(value) if value else None,
|
||||
})
|
||||
|
||||
strategy = _dimension('Strategy')
|
||||
if strategy:
|
||||
assignments.append({
|
||||
'dimension': record_id(strategy),
|
||||
'value': None,
|
||||
})
|
||||
|
||||
return assignments
|
||||
|
||||
|
||||
def add_itsa_analytic_dimension_defaults(values):
|
||||
if values.get('analytic_dimensions'):
|
||||
return
|
||||
company = _company_from_values(values)
|
||||
assignments = default_itsa_analytic_dimension_assignments(company=company)
|
||||
if assignments:
|
||||
values['analytic_dimensions'] = [('create', assignments)]
|
||||
272
modules/purchase_trade/configuration.py
Normal file
272
modules/purchase_trade/configuration.py
Normal file
@@ -0,0 +1,272 @@
|
||||
from sql import Table
|
||||
from sql.operators import Exists
|
||||
|
||||
from trytond.model import ModelSingleton, ModelSQL, ModelView, fields
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pyson import Eval
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
|
||||
class PurchaseConfiguration(metaclass=PoolMeta):
|
||||
__name__ = 'purchase.configuration'
|
||||
|
||||
allow_modification_after_validation = fields.Boolean(
|
||||
"Autorise modification after validation")
|
||||
|
||||
@classmethod
|
||||
def default_allow_modification_after_validation(cls):
|
||||
return False
|
||||
|
||||
|
||||
class SaleConfiguration(metaclass=PoolMeta):
|
||||
__name__ = 'sale.configuration'
|
||||
|
||||
allow_modification_after_validation = fields.Boolean(
|
||||
"Autorise modification after validation")
|
||||
|
||||
@classmethod
|
||||
def default_allow_modification_after_validation(cls):
|
||||
return False
|
||||
|
||||
|
||||
class AccountConfiguration(metaclass=PoolMeta):
|
||||
__name__ = 'account.configuration'
|
||||
|
||||
default_sale_padding_account = fields.MultiValue(fields.Many2One(
|
||||
'account.account', "Default Sale Padding",
|
||||
domain=[
|
||||
('closed', '!=', True),
|
||||
('type.revenue', '=', True),
|
||||
('company', '=', Eval('context', {}).get('company', -1)),
|
||||
]))
|
||||
default_accrual_padding_account = fields.MultiValue(fields.Many2One(
|
||||
'account.account', "Default Accrual Padding",
|
||||
domain=[
|
||||
('closed', '!=', True),
|
||||
('type.statement', '=', 'balance'),
|
||||
('company', '=', Eval('context', {}).get('company', -1)),
|
||||
]))
|
||||
|
||||
@classmethod
|
||||
def multivalue_model(cls, field):
|
||||
pool = Pool()
|
||||
if field in {
|
||||
'default_sale_padding_account',
|
||||
'default_accrual_padding_account',
|
||||
}:
|
||||
return pool.get('account.configuration.default_account')
|
||||
return super().multivalue_model(field)
|
||||
|
||||
|
||||
class AccountConfigurationDefaultAccount(metaclass=PoolMeta):
|
||||
__name__ = 'account.configuration.default_account'
|
||||
|
||||
default_sale_padding_account = fields.Many2One(
|
||||
'account.account', "Default Sale Padding",
|
||||
domain=[
|
||||
('closed', '!=', True),
|
||||
('type.revenue', '=', True),
|
||||
('company', '=', Eval('company', -1)),
|
||||
])
|
||||
default_accrual_padding_account = fields.Many2One(
|
||||
'account.account', "Default Accrual Padding",
|
||||
domain=[
|
||||
('closed', '!=', True),
|
||||
('type.statement', '=', 'balance'),
|
||||
('company', '=', Eval('company', -1)),
|
||||
])
|
||||
|
||||
|
||||
class Configuration(ModelSingleton, ModelSQL, ModelView):
|
||||
"Purchase Trade Configuration"
|
||||
__name__ = 'purchase_trade.configuration'
|
||||
|
||||
_REPORT_LABELS = (
|
||||
('sale_report_label', 'sale', 'report_sale', 'Proforma'),
|
||||
('sale_commission_report_label', 'account_invoice',
|
||||
'report_sale_commission_ict', 'Commission invoice Sale'),
|
||||
('sale_bill_report_label', 'sale', 'report_bill', 'Draft'),
|
||||
('invoice_report_label', 'account_invoice', 'report_invoice',
|
||||
'Invoice'),
|
||||
('invoice_cndn_report_label', 'account_invoice',
|
||||
'report_invoice_ict_final', 'CN/DN'),
|
||||
('invoice_commission_cndn_report_label', 'account_invoice',
|
||||
'report_invoice_ict_commission_final', 'CN/DN Commission'),
|
||||
('invoice_prepayment_report_label', 'account_invoice',
|
||||
'report_prepayment', 'Prepayment'),
|
||||
('invoice_packing_list_report_label', 'purchase_trade',
|
||||
'report_invoice_packing_list', 'Packing List'),
|
||||
('invoice_payment_order_report_label', 'purchase_trade',
|
||||
'report_payment_order', 'Payment Order'),
|
||||
('purchase_report_label', 'purchase', 'report_purchase', 'Purchase'),
|
||||
('purchase_commission_report_label', 'account_invoice',
|
||||
'report_purchase_commission_ict', 'Commission invoice Purchase'),
|
||||
('shipment_shipping_report_label', 'stock',
|
||||
'report_shipment_in_shipping', 'Shipping instructions'),
|
||||
('shipment_insurance_report_label', 'purchase_trade',
|
||||
'report_shipment_in_insurance', 'Insurance'),
|
||||
('shipment_coo_report_label', 'purchase_trade',
|
||||
'report_shipment_in_coo', 'COO'),
|
||||
('shipment_packing_list_report_label', 'purchase_trade',
|
||||
'report_shipment_in_packing_list', 'Packing List'),
|
||||
('shipment_linkage_report_label', 'purchase_trade',
|
||||
'report_shipment_in_linkage', 'Linkage'),
|
||||
)
|
||||
_COFFEE_MENU_XML_IDS = (
|
||||
'menu_coffee_purchase',
|
||||
'menu_coffee_sample_purchase',
|
||||
'menu_coffee_lab_analysis_purchase',
|
||||
'menu_coffee_cupping_session_purchase',
|
||||
'menu_coffee_cupping_criterion_purchase',
|
||||
'menu_coffee_sale',
|
||||
'menu_coffee_sample_sale',
|
||||
'menu_coffee_lab_analysis_sale',
|
||||
'menu_coffee_cupping_session_sale',
|
||||
'menu_coffee_cupping_criterion_sale',
|
||||
)
|
||||
_ORPHAN_ACT_WINDOW_XML_IDS = (
|
||||
'act_itsa_operations_workflow',
|
||||
'act_tradon_processes',
|
||||
)
|
||||
|
||||
pricing_rule = fields.Text("Pricing Rule")
|
||||
active_coffee_compatibility = fields.Boolean(
|
||||
"Active coffee compatibility")
|
||||
sale_report_template = fields.Char("Sale Template")
|
||||
sale_report_label = fields.Char("Sale Menu Label")
|
||||
sale_commission_report_template = fields.Char(
|
||||
"Sale Commission Template")
|
||||
sale_commission_report_label = fields.Char(
|
||||
"Sale Commission Menu Label")
|
||||
sale_bill_report_template = fields.Char("Sale Bill Template")
|
||||
sale_bill_report_label = fields.Char("Sale Bill Menu Label")
|
||||
sale_final_report_template = fields.Char("Sale Final Template")
|
||||
invoice_report_template = fields.Char("Invoice Template")
|
||||
invoice_report_label = fields.Char("Invoice Menu Label")
|
||||
invoice_cndn_report_template = fields.Char("CN/DN Template")
|
||||
invoice_cndn_report_label = fields.Char("CN/DN Menu Label")
|
||||
invoice_commission_cndn_report_template = fields.Char(
|
||||
"CN/DN Commission Template")
|
||||
invoice_commission_cndn_report_label = fields.Char(
|
||||
"CN/DN Commission Menu Label")
|
||||
invoice_prepayment_report_template = fields.Char("Prepayment Template")
|
||||
invoice_prepayment_report_label = fields.Char("Prepayment Menu Label")
|
||||
invoice_packing_list_report_template = fields.Char("Packing List Template")
|
||||
invoice_packing_list_report_label = fields.Char("Packing List Menu Label")
|
||||
invoice_payment_order_report_template = fields.Char("Payment Order Template")
|
||||
invoice_payment_order_report_label = fields.Char(
|
||||
"Payment Order Menu Label")
|
||||
purchase_report_template = fields.Char("Purchase Template")
|
||||
purchase_report_label = fields.Char("Purchase Menu Label")
|
||||
purchase_commission_report_template = fields.Char(
|
||||
"Purchase Commission Template")
|
||||
purchase_commission_report_label = fields.Char(
|
||||
"Purchase Commission Menu Label")
|
||||
shipment_shipping_report_template = fields.Char("Shipping Template")
|
||||
shipment_shipping_report_label = fields.Char("Shipping Menu Label")
|
||||
shipment_insurance_report_template = fields.Char("Insurance Template")
|
||||
shipment_insurance_report_label = fields.Char("Insurance Menu Label")
|
||||
shipment_coo_report_template = fields.Char("COO Template")
|
||||
shipment_coo_report_label = fields.Char("COO Menu Label")
|
||||
shipment_packing_list_report_template = fields.Char("Packing List Template")
|
||||
shipment_packing_list_report_label = fields.Char(
|
||||
"Packing List Menu Label")
|
||||
shipment_linkage_report_template = fields.Char("Linkage Template")
|
||||
shipment_linkage_report_label = fields.Char("Linkage Menu Label")
|
||||
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
super().__register__(module_name)
|
||||
|
||||
if module_name == 'purchase_trade':
|
||||
cls._cleanup_orphan_act_window_xml_ids()
|
||||
|
||||
@classmethod
|
||||
def _cleanup_orphan_act_window_xml_ids(cls):
|
||||
cursor = Transaction().connection.cursor()
|
||||
model_data = Table('ir_model_data')
|
||||
act_window = Table('ir_action_act_window')
|
||||
|
||||
cursor.execute(*model_data.delete(
|
||||
where=(model_data.module == 'purchase_trade')
|
||||
& (model_data.model == 'ir.action.act_window')
|
||||
& model_data.fs_id.in_(cls._ORPHAN_ACT_WINDOW_XML_IDS)
|
||||
& ~Exists(act_window.select(
|
||||
act_window.id,
|
||||
where=act_window.id == model_data.db_id))))
|
||||
|
||||
@classmethod
|
||||
def default_active_coffee_compatibility(cls):
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
records = super().create(vlist)
|
||||
cls._sync_report_labels(records)
|
||||
active = any(
|
||||
bool(values.get(
|
||||
'active_coffee_compatibility',
|
||||
cls.default_active_coffee_compatibility()))
|
||||
for values in vlist)
|
||||
cls._sync_coffee_menu_visibility(records, active=active)
|
||||
cls._clear_coffee_view_cache()
|
||||
return records
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
actions = list(zip(args[::2], args[1::2]))
|
||||
active = None
|
||||
for records, values in actions:
|
||||
if 'active_coffee_compatibility' in values:
|
||||
active = bool(values['active_coffee_compatibility'])
|
||||
super().write(*args)
|
||||
records = sum(args[::2], [])
|
||||
cls._sync_report_labels(records)
|
||||
cls._sync_coffee_menu_visibility(records, active=active)
|
||||
if active is not None:
|
||||
cls._clear_coffee_view_cache()
|
||||
|
||||
@classmethod
|
||||
def _sync_report_labels(cls, records):
|
||||
if not records:
|
||||
return
|
||||
pool = Pool()
|
||||
ModelData = pool.get('ir.model.data')
|
||||
ActionReport = pool.get('ir.action.report')
|
||||
to_write = []
|
||||
for record in records:
|
||||
for field_name, module, xml_id, default_label in cls._REPORT_LABELS:
|
||||
label = (getattr(record, field_name, '') or '').strip()
|
||||
action_id = ModelData.get_id(module, xml_id)
|
||||
action = ActionReport(action_id)
|
||||
target_label = label or default_label
|
||||
if getattr(action, 'name', '') != target_label:
|
||||
to_write.extend(([action], {'name': target_label}))
|
||||
if to_write:
|
||||
with Transaction().set_user(0):
|
||||
ActionReport.write(*to_write)
|
||||
|
||||
@classmethod
|
||||
def _sync_coffee_menu_visibility(cls, records=None, active=None):
|
||||
if active is None:
|
||||
configurations = cls.search([], limit=1)
|
||||
active = bool(
|
||||
configurations
|
||||
and configurations[0].active_coffee_compatibility)
|
||||
pool = Pool()
|
||||
ModelData = pool.get('ir.model.data')
|
||||
Menu = pool.get('ir.ui.menu')
|
||||
menus = []
|
||||
for xml_id in cls._COFFEE_MENU_XML_IDS:
|
||||
try:
|
||||
menu_id = ModelData.get_id('purchase_trade', xml_id)
|
||||
except Exception:
|
||||
continue
|
||||
menus.append(Menu(menu_id))
|
||||
if menus:
|
||||
with Transaction().set_user(0):
|
||||
Menu.write(menus, {'active': active})
|
||||
|
||||
@classmethod
|
||||
def _clear_coffee_view_cache(cls):
|
||||
ModelView._fields_view_get_cache.clear()
|
||||
72
modules/purchase_trade/configuration.xml
Normal file
72
modules/purchase_trade/configuration.xml
Normal file
@@ -0,0 +1,72 @@
|
||||
<tryton>
|
||||
<data>
|
||||
<record model="ir.ui.view" id="purchase_trade_configuration_view_form">
|
||||
<field name="model">purchase_trade.configuration</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">configuration_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="purchase_trade_template_configuration_view_form">
|
||||
<field name="model">purchase_trade.configuration</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">template_configuration_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="purchase_configuration_view_form">
|
||||
<field name="model">purchase.configuration</field>
|
||||
<field name="inherit" ref="purchase.purchase_configuration_view_form"/>
|
||||
<field name="name">purchase_configuration_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="sale_configuration_view_form">
|
||||
<field name="model">sale.configuration</field>
|
||||
<field name="inherit" ref="sale.sale_configuration_view_form"/>
|
||||
<field name="name">sale_configuration_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_purchase_trade_configuration_form">
|
||||
<field name="name">Trade Configuration</field>
|
||||
<field name="res_model">purchase_trade.configuration</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_purchase_trade_configuration_form_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="purchase_trade_configuration_view_form"/>
|
||||
<field name="act_window" ref="act_purchase_trade_configuration_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window" id="act_purchase_trade_template_configuration_form">
|
||||
<field name="name">Document Templates</field>
|
||||
<field name="res_model">purchase_trade.configuration</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_purchase_trade_template_configuration_form_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="purchase_trade_template_configuration_view_form"/>
|
||||
<field name="act_window" ref="act_purchase_trade_template_configuration_form"/>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
name="Trade Configuration"
|
||||
parent="price.menu_price"
|
||||
action="act_purchase_trade_configuration_form"
|
||||
sequence="10"
|
||||
id="menu_purchase_trade_configuration"
|
||||
icon="tryton-settings"/>
|
||||
<menuitem
|
||||
name="Trade Configuration"
|
||||
parent="purchase.menu_configuration"
|
||||
action="act_purchase_trade_configuration_form"
|
||||
sequence="90"
|
||||
id="menu_purchase_trade_configuration_purchase"
|
||||
icon="tryton-settings"/>
|
||||
<menuitem
|
||||
name="Trade Configuration"
|
||||
parent="sale.menu_configuration"
|
||||
action="act_purchase_trade_configuration_form"
|
||||
sequence="90"
|
||||
id="menu_purchase_trade_configuration_sale"
|
||||
icon="tryton-settings"/>
|
||||
<menuitem
|
||||
name="Document Templates"
|
||||
parent="document_incoming.menu_configuration"
|
||||
action="act_purchase_trade_template_configuration_form"
|
||||
sequence="20"
|
||||
id="menu_purchase_trade_template_configuration"
|
||||
icon="tryton-settings"/>
|
||||
</data>
|
||||
</tryton>
|
||||
2469
modules/purchase_trade/contract_clause_reference.xml
Normal file
2469
modules/purchase_trade/contract_clause_reference.xml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -12,9 +12,15 @@ class Cron(metaclass=PoolMeta):
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls.method.selection.append(
|
||||
('forex.cron|update_forex', "Update Forex Prices"))
|
||||
super().__setup__()
|
||||
cls.method.selection.append(
|
||||
('forex.cron|update_forex', "Update Forex Prices"))
|
||||
cls.method.selection.append(
|
||||
('valuation.valuation|update_daily_snapshot',
|
||||
"Update Valuation Snapshot"))
|
||||
cls.method.selection.append(
|
||||
('pricing.pricing|update_daily_pricing',
|
||||
"Update Pricing"))
|
||||
|
||||
class PriceCron(ModelSQL, ModelView):
|
||||
"Price Cron"
|
||||
|
||||
@@ -36,10 +36,22 @@
|
||||
<field name="model" search="[('model', '=', 'forex.cron')]"/>
|
||||
</record> -->
|
||||
|
||||
<record model="ir.cron" id="cron_cron">
|
||||
<field name="method">forex.cron|update_forex</field>
|
||||
<field name="interval_number" eval="1"/>
|
||||
<field name="interval_type">days</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
<record model="ir.cron" id="cron_cron">
|
||||
<field name="method">forex.cron|update_forex</field>
|
||||
<field name="interval_number" eval="1"/>
|
||||
<field name="interval_type">days</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.cron" id="valuation_daily_snapshot_cron">
|
||||
<field name="method">valuation.valuation|update_daily_snapshot</field>
|
||||
<field name="interval_number" eval="1"/>
|
||||
<field name="interval_type">days</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.cron" id="pricing_daily_update_cron">
|
||||
<field name="method">pricing.pricing|update_daily_pricing</field>
|
||||
<field name="interval_number" eval="1"/>
|
||||
<field name="interval_type">days</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
|
||||
2002
modules/purchase_trade/ctrm_reporting.py
Normal file
2002
modules/purchase_trade/ctrm_reporting.py
Normal file
File diff suppressed because it is too large
Load Diff
486
modules/purchase_trade/ctrm_reporting.xml
Normal file
486
modules/purchase_trade/ctrm_reporting.xml
Normal file
@@ -0,0 +1,486 @@
|
||||
<tryton>
|
||||
<data>
|
||||
<record model="ir.ui.view" id="ctrm_position_physical_context_view_form">
|
||||
<field name="model">ctrm.reporting.position.physical.context</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">ctrm_position_physical_context_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_position_physical_view_list">
|
||||
<field name="model">ctrm.reporting.position.physical</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">ctrm_position_physical_list</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window" id="act_ctrm_position_physical">
|
||||
<field name="name">Physical Position</field>
|
||||
<field name="res_model">ctrm.reporting.position.physical</field>
|
||||
<field name="context_model">ctrm.reporting.position.physical.context</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_ctrm_position_physical_view">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="ctrm_position_physical_view_list"/>
|
||||
<field name="act_window" ref="act_ctrm_position_physical"/>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_position_financial_context_view_form">
|
||||
<field name="model">ctrm.reporting.position.financial.context</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">ctrm_position_financial_context_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_position_financial_view_list">
|
||||
<field name="model">ctrm.reporting.position.financial</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">ctrm_position_financial_list</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window" id="act_ctrm_position_financial">
|
||||
<field name="name">Financial Position</field>
|
||||
<field name="res_model">ctrm.reporting.position.financial</field>
|
||||
<field name="context_model">ctrm.reporting.position.financial.context</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_ctrm_position_financial_view">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="ctrm_position_financial_view_list"/>
|
||||
<field name="act_window" ref="act_ctrm_position_financial"/>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_position_net_context_view_form">
|
||||
<field name="model">ctrm.reporting.position.net.context</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">ctrm_position_net_context_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_position_net_view_list">
|
||||
<field name="model">ctrm.reporting.position.net</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">ctrm_position_net_list</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window" id="act_ctrm_position_net">
|
||||
<field name="name">Net Consolidated Position</field>
|
||||
<field name="res_model">ctrm.reporting.position.net</field>
|
||||
<field name="context_model">ctrm.reporting.position.net.context</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_ctrm_position_net_view">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="ctrm_position_net_view_list"/>
|
||||
<field name="act_window" ref="act_ctrm_position_net"/>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_pnl_realized_context_view_form">
|
||||
<field name="model">ctrm.reporting.pnl.realized.context</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">ctrm_pnl_realized_context_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_pnl_realized_view_list">
|
||||
<field name="model">ctrm.reporting.pnl.realized</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">ctrm_pnl_realized_list</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window" id="act_ctrm_pnl_realized">
|
||||
<field name="name">Realized P&L</field>
|
||||
<field name="res_model">ctrm.reporting.pnl.realized</field>
|
||||
<field name="context_model">ctrm.reporting.pnl.realized.context</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_ctrm_pnl_realized_view">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="ctrm_pnl_realized_view_list"/>
|
||||
<field name="act_window" ref="act_ctrm_pnl_realized"/>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_pnl_mtm_context_view_form">
|
||||
<field name="model">ctrm.reporting.pnl.mtm.context</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">ctrm_pnl_mtm_context_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_pnl_mtm_view_list">
|
||||
<field name="model">ctrm.reporting.pnl.mtm</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">ctrm_pnl_mtm_list</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window" id="act_ctrm_pnl_mtm">
|
||||
<field name="name">Mark-to-Market</field>
|
||||
<field name="res_model">ctrm.reporting.pnl.mtm</field>
|
||||
<field name="context_model">ctrm.reporting.pnl.mtm.context</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_ctrm_pnl_mtm_view">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="ctrm_pnl_mtm_view_list"/>
|
||||
<field name="act_window" ref="act_ctrm_pnl_mtm"/>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_pnl_explain_context_view_form">
|
||||
<field name="model">ctrm.reporting.pnl.explain.context</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">ctrm_pnl_explain_context_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_pnl_explain_view_list">
|
||||
<field name="model">ctrm.reporting.pnl.explain</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">ctrm_pnl_explain_list</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window" id="act_ctrm_pnl_explain">
|
||||
<field name="name">P&L Explain</field>
|
||||
<field name="res_model">ctrm.reporting.pnl.explain</field>
|
||||
<field name="context_model">ctrm.reporting.pnl.explain.context</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_ctrm_pnl_explain_view">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="ctrm_pnl_explain_view_list"/>
|
||||
<field name="act_window" ref="act_ctrm_pnl_explain"/>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_pnl_dimension_context_view_form">
|
||||
<field name="model">ctrm.reporting.pnl.dimension.context</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">ctrm_pnl_dimension_context_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_pnl_dimension_view_list">
|
||||
<field name="model">ctrm.reporting.pnl.dimension</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">ctrm_pnl_dimension_list</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window" id="act_ctrm_pnl_dimension">
|
||||
<field name="name">P&L by Dimension</field>
|
||||
<field name="res_model">ctrm.reporting.pnl.dimension</field>
|
||||
<field name="context_model">ctrm.reporting.pnl.dimension.context</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_ctrm_pnl_dimension_view">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="ctrm_pnl_dimension_view_list"/>
|
||||
<field name="act_window" ref="act_ctrm_pnl_dimension"/>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_credit_risk_context_view_form">
|
||||
<field name="model">ctrm.reporting.risk.credit.context</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">ctrm_credit_risk_context_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_credit_risk_view_list">
|
||||
<field name="model">ctrm.reporting.risk.credit</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">ctrm_credit_risk_list</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window" id="act_ctrm_credit_risk">
|
||||
<field name="name">Credit Risk</field>
|
||||
<field name="res_model">ctrm.reporting.risk.credit</field>
|
||||
<field name="context_model">ctrm.reporting.risk.credit.context</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_ctrm_credit_risk_view">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="ctrm_credit_risk_view_list"/>
|
||||
<field name="act_window" ref="act_ctrm_credit_risk"/>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_shipping_logistics_context_view_form">
|
||||
<field name="model">ctrm.reporting.operations.shipping.context</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">ctrm_shipping_logistics_context_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_shipping_logistics_view_list">
|
||||
<field name="model">ctrm.reporting.operations.shipping</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">ctrm_shipping_logistics_list</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window" id="act_ctrm_shipping_logistics">
|
||||
<field name="name">Shipping Logistics</field>
|
||||
<field name="res_model">ctrm.reporting.operations.shipping</field>
|
||||
<field name="context_model">ctrm.reporting.operations.shipping.context</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_ctrm_shipping_logistics_view">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="ctrm_shipping_logistics_view_list"/>
|
||||
<field name="act_window" ref="act_ctrm_shipping_logistics"/>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_inventory_context_view_form">
|
||||
<field name="model">ctrm.reporting.operations.inventory.context</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">ctrm_inventory_context_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_inventory_view_list">
|
||||
<field name="model">ctrm.reporting.operations.inventory</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">ctrm_inventory_list</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window" id="act_ctrm_inventory">
|
||||
<field name="name">Inventory</field>
|
||||
<field name="res_model">ctrm.reporting.operations.inventory</field>
|
||||
<field name="context_model">ctrm.reporting.operations.inventory.context</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_ctrm_inventory_view">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="ctrm_inventory_view_list"/>
|
||||
<field name="act_window" ref="act_ctrm_inventory"/>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_contract_performance_context_view_form">
|
||||
<field name="model">ctrm.reporting.operations.contract.context</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">ctrm_contract_performance_context_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_contract_performance_view_list">
|
||||
<field name="model">ctrm.reporting.operations.contract</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">ctrm_contract_performance_list</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window" id="act_ctrm_contract_performance">
|
||||
<field name="name">Contract Performance</field>
|
||||
<field name="res_model">ctrm.reporting.operations.contract</field>
|
||||
<field name="context_model">ctrm.reporting.operations.contract.context</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_ctrm_contract_performance_view">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="ctrm_contract_performance_view_list"/>
|
||||
<field name="act_window" ref="act_ctrm_contract_performance"/>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_scheduling_context_view_form">
|
||||
<field name="model">ctrm.reporting.operations.scheduling.context</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">ctrm_scheduling_context_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_scheduling_view_list">
|
||||
<field name="model">ctrm.reporting.operations.scheduling</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">ctrm_scheduling_list</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window" id="act_ctrm_scheduling">
|
||||
<field name="name">Scheduling</field>
|
||||
<field name="res_model">ctrm.reporting.operations.scheduling</field>
|
||||
<field name="context_model">ctrm.reporting.operations.scheduling.context</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_ctrm_scheduling_view">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="ctrm_scheduling_view_list"/>
|
||||
<field name="act_window" ref="act_ctrm_scheduling"/>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_accruals_context_view_form">
|
||||
<field name="model">ctrm.reporting.finance.accruals.context</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">ctrm_accruals_context_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_accruals_view_list">
|
||||
<field name="model">ctrm.reporting.finance.accruals</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">ctrm_accruals_list</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window" id="act_ctrm_accruals">
|
||||
<field name="name">Accruals</field>
|
||||
<field name="res_model">ctrm.reporting.finance.accruals</field>
|
||||
<field name="context_model">ctrm.reporting.finance.accruals.context</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_ctrm_accruals_view">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="ctrm_accruals_view_list"/>
|
||||
<field name="act_window" ref="act_ctrm_accruals"/>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_settlements_context_view_form">
|
||||
<field name="model">ctrm.reporting.finance.settlements.context</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">ctrm_settlements_context_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_settlements_view_list">
|
||||
<field name="model">ctrm.reporting.finance.settlements</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">ctrm_settlements_list</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window" id="act_ctrm_settlements">
|
||||
<field name="name">Settlements and Invoicing</field>
|
||||
<field name="res_model">ctrm.reporting.finance.settlements</field>
|
||||
<field name="context_model">ctrm.reporting.finance.settlements.context</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_ctrm_settlements_view">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="ctrm_settlements_view_list"/>
|
||||
<field name="act_window" ref="act_ctrm_settlements"/>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_cashflow_context_view_form">
|
||||
<field name="model">ctrm.reporting.finance.cashflow.context</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">ctrm_cashflow_context_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_cashflow_view_list">
|
||||
<field name="model">ctrm.reporting.finance.cashflow</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">ctrm_cashflow_list</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window" id="act_ctrm_cashflow">
|
||||
<field name="name">Cash Flow Forecast</field>
|
||||
<field name="res_model">ctrm.reporting.finance.cashflow</field>
|
||||
<field name="context_model">ctrm.reporting.finance.cashflow.context</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_ctrm_cashflow_view">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="ctrm_cashflow_view_list"/>
|
||||
<field name="act_window" ref="act_ctrm_cashflow"/>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_gl_reconciliation_context_view_form">
|
||||
<field name="model">ctrm.reporting.finance.gl.context</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">ctrm_gl_reconciliation_context_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="ctrm_gl_reconciliation_view_list">
|
||||
<field name="model">ctrm.reporting.finance.gl</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">ctrm_gl_reconciliation_list</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window" id="act_ctrm_gl_reconciliation">
|
||||
<field name="name">GL Reconciliation</field>
|
||||
<field name="res_model">ctrm.reporting.finance.gl</field>
|
||||
<field name="context_model">ctrm.reporting.finance.gl.context</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_ctrm_gl_reconciliation_view">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="ctrm_gl_reconciliation_view_list"/>
|
||||
<field name="act_window" ref="act_ctrm_gl_reconciliation"/>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
name="Positions"
|
||||
parent="purchase_trade.menu_global_reporting"
|
||||
sequence="10"
|
||||
id="menu_ctrm_positions"
|
||||
icon="tradon-reporting-positions"/>
|
||||
<menuitem
|
||||
name="Physical Position"
|
||||
parent="menu_ctrm_positions"
|
||||
sequence="10"
|
||||
action="act_ctrm_position_physical"
|
||||
id="menu_ctrm_position_physical"/>
|
||||
<menuitem
|
||||
name="Financial Position"
|
||||
parent="menu_ctrm_positions"
|
||||
sequence="20"
|
||||
action="act_ctrm_position_financial"
|
||||
id="menu_ctrm_position_financial"/>
|
||||
<menuitem
|
||||
name="Net Consolidated Position"
|
||||
parent="menu_ctrm_positions"
|
||||
sequence="30"
|
||||
action="act_ctrm_position_net"
|
||||
id="menu_ctrm_position_net"/>
|
||||
<menuitem
|
||||
name="Hedge Coverage"
|
||||
parent="menu_ctrm_positions"
|
||||
sequence="40"
|
||||
id="menu_ctrm_position_hedge"/>
|
||||
|
||||
<menuitem
|
||||
name="P&L"
|
||||
parent="purchase_trade.menu_global_reporting"
|
||||
sequence="20"
|
||||
id="menu_ctrm_pnl"
|
||||
icon="tradon-reporting-pnl"/>
|
||||
<menuitem
|
||||
name="Realized P&L"
|
||||
parent="menu_ctrm_pnl"
|
||||
sequence="10"
|
||||
action="act_ctrm_pnl_realized"
|
||||
id="menu_ctrm_pnl_realized"/>
|
||||
<menuitem
|
||||
name="Mark-to-Market"
|
||||
parent="menu_ctrm_pnl"
|
||||
sequence="20"
|
||||
action="act_ctrm_pnl_mtm"
|
||||
id="menu_ctrm_pnl_mtm"/>
|
||||
<menuitem
|
||||
name="P&L Explain"
|
||||
parent="menu_ctrm_pnl"
|
||||
sequence="30"
|
||||
action="act_ctrm_pnl_explain"
|
||||
id="menu_ctrm_pnl_explain"/>
|
||||
<menuitem
|
||||
name="P&L by Dimension"
|
||||
parent="menu_ctrm_pnl"
|
||||
sequence="40"
|
||||
action="act_ctrm_pnl_dimension"
|
||||
id="menu_ctrm_pnl_dimension"/>
|
||||
|
||||
<menuitem
|
||||
name="Risk"
|
||||
parent="purchase_trade.menu_global_reporting"
|
||||
sequence="30"
|
||||
id="menu_ctrm_risk"
|
||||
icon="tradon-reporting-risk"/>
|
||||
<menuitem
|
||||
name="VaR"
|
||||
parent="menu_ctrm_risk"
|
||||
sequence="10"
|
||||
action="risk.act_pos_report_form"
|
||||
id="menu_ctrm_risk_var"/>
|
||||
<menuitem
|
||||
name="Stress Tests"
|
||||
parent="menu_ctrm_risk"
|
||||
sequence="20"
|
||||
id="menu_ctrm_risk_stress"/>
|
||||
<menuitem
|
||||
name="Sensitivities"
|
||||
parent="menu_ctrm_risk"
|
||||
sequence="30"
|
||||
id="menu_ctrm_risk_sensitivities"/>
|
||||
<menuitem
|
||||
name="Limit Monitoring"
|
||||
parent="menu_ctrm_risk"
|
||||
sequence="40"
|
||||
id="menu_ctrm_risk_limits"/>
|
||||
<menuitem
|
||||
name="Credit Risk"
|
||||
parent="menu_ctrm_risk"
|
||||
sequence="50"
|
||||
action="act_ctrm_credit_risk"
|
||||
id="menu_ctrm_risk_credit"/>
|
||||
|
||||
<menuitem
|
||||
name="Operations & Logistics"
|
||||
parent="purchase_trade.menu_global_reporting"
|
||||
sequence="40"
|
||||
id="menu_ctrm_operations"
|
||||
icon="tradon-reporting-operations"/>
|
||||
<menuitem
|
||||
name="Shipping Logistics"
|
||||
parent="menu_ctrm_operations"
|
||||
sequence="10"
|
||||
action="act_ctrm_shipping_logistics"
|
||||
id="menu_ctrm_operations_shipping"/>
|
||||
<menuitem
|
||||
name="Inventory"
|
||||
parent="menu_ctrm_operations"
|
||||
sequence="20"
|
||||
action="act_ctrm_inventory"
|
||||
id="menu_ctrm_operations_inventory"/>
|
||||
<menuitem
|
||||
name="Contract Performance"
|
||||
parent="menu_ctrm_operations"
|
||||
sequence="30"
|
||||
action="act_ctrm_contract_performance"
|
||||
id="menu_ctrm_operations_contract"/>
|
||||
<menuitem
|
||||
name="Scheduling"
|
||||
parent="menu_ctrm_operations"
|
||||
sequence="40"
|
||||
action="act_ctrm_scheduling"
|
||||
id="menu_ctrm_operations_scheduling"/>
|
||||
|
||||
<menuitem
|
||||
name="Finance & Accounting"
|
||||
parent="purchase_trade.menu_global_reporting"
|
||||
sequence="50"
|
||||
id="menu_ctrm_finance"
|
||||
icon="tradon-reporting-finance"/>
|
||||
<menuitem
|
||||
name="Accruals"
|
||||
parent="menu_ctrm_finance"
|
||||
sequence="10"
|
||||
action="act_ctrm_accruals"
|
||||
id="menu_ctrm_finance_accruals"/>
|
||||
<menuitem
|
||||
name="Hedge Accounting"
|
||||
parent="menu_ctrm_finance"
|
||||
sequence="20"
|
||||
id="menu_ctrm_finance_hedge_accounting"/>
|
||||
<menuitem
|
||||
name="Settlements and Invoicing"
|
||||
parent="menu_ctrm_finance"
|
||||
sequence="30"
|
||||
action="act_ctrm_settlements"
|
||||
id="menu_ctrm_finance_settlements"/>
|
||||
<menuitem
|
||||
name="Cash Flow Forecast"
|
||||
parent="menu_ctrm_finance"
|
||||
sequence="40"
|
||||
action="act_ctrm_cashflow"
|
||||
id="menu_ctrm_finance_cash_flow"/>
|
||||
<menuitem
|
||||
name="GL Reconciliation"
|
||||
parent="menu_ctrm_finance"
|
||||
sequence="50"
|
||||
action="act_ctrm_gl_reconciliation"
|
||||
id="menu_ctrm_finance_gl_reconciliation"/>
|
||||
</data>
|
||||
</tryton>
|
||||
@@ -193,16 +193,29 @@ class Dashboard(ModelSQL, ModelView):
|
||||
self.chatbot = 'chatbot:' + json.dumps(dial, ensure_ascii=False)
|
||||
logger.info("EXITONCHANGE",self.chatbot)
|
||||
|
||||
@staticmethod
|
||||
def _empty_fx_rates():
|
||||
return (None, None, None, None, None, None, None, None, None, None)
|
||||
|
||||
@classmethod
|
||||
def _find_currency_by_code(cls, code):
|
||||
Currency = Pool().get('currency.currency')
|
||||
currencies = Currency.search([('name', '=', code)], limit=1)
|
||||
if not currencies:
|
||||
currencies = Currency.search([('code', '=', code)], limit=1)
|
||||
return currencies[0] if currencies else None
|
||||
|
||||
def get_last_five_fx_rates(self, from_code='USD', to_code='EUR'):
|
||||
"""
|
||||
Retourne (dernier_taux, avant_dernier_taux) pour le couple de devises.
|
||||
"""
|
||||
Currency = Pool().get('currency.currency')
|
||||
CurrencyRate = Pool().get('currency.currency.rate')
|
||||
|
||||
# Récupérer les devises EUR et USD
|
||||
from_currency = Currency.search([('name', '=', from_code)])[0]
|
||||
to_currency = Currency.search([('name', '=', to_code)])[0]
|
||||
from_currency = self._find_currency_by_code(from_code)
|
||||
to_currency = self._find_currency_by_code(to_code)
|
||||
if not from_currency or not to_currency:
|
||||
return self._empty_fx_rates()
|
||||
|
||||
# Recherche des taux de la devise de base (ex: USD)
|
||||
rates = CurrencyRate.search(
|
||||
@@ -212,7 +225,7 @@ class Dashboard(ModelSQL, ModelView):
|
||||
)
|
||||
|
||||
if not rates:
|
||||
return None, None, None, None, None
|
||||
return self._empty_fx_rates()
|
||||
|
||||
# Calcul du taux EUR/USD
|
||||
# Si la devise principale de la société est EUR, et que le taux stocké est
|
||||
@@ -232,7 +245,13 @@ class Dashboard(ModelSQL, ModelView):
|
||||
# last_rate = 1 / last_rate if last_rate else None
|
||||
# prev_rate = 1 / prev_rate if prev_rate else None
|
||||
|
||||
return round(1/f1,6), round(1/f2,6) if f2 else None, round(1/f3,6) if f3 else None, round(1/f4,6) if f4 else None, round(1/f5,6) if f5 else None, d1, d2, d3, d4, d5
|
||||
return (
|
||||
round(1/f1,6) if f1 else None,
|
||||
round(1/f2,6) if f2 else None,
|
||||
round(1/f3,6) if f3 else None,
|
||||
round(1/f4,6) if f4 else None,
|
||||
round(1/f5,6) if f5 else None,
|
||||
d1, d2, d3, d4, d5)
|
||||
|
||||
def get_tremor(self,name):
|
||||
Date = Pool().get('ir.date')
|
||||
@@ -430,7 +449,7 @@ class Dashboard(ModelSQL, ModelView):
|
||||
News = Pool().get('news.news')
|
||||
Date = Pool().get('ir.date')
|
||||
news_list = News.search([('active', '=', True)], limit=5, order=[('publish_date', 'DESC')])
|
||||
last_rate,prev_rate, = self.get_last_five_fx_rates()
|
||||
last_rate, prev_rate = self.get_last_five_fx_rates()[:2]
|
||||
if last_rate and prev_rate:
|
||||
variation = ((last_rate - prev_rate) / prev_rate) * 100 if prev_rate else 0
|
||||
direction = "📈" if variation > 0 else "📉"
|
||||
@@ -749,7 +768,11 @@ class BotWizard(Wizard):
|
||||
lms.lot_r_id = ls.getLotQt()[0].id
|
||||
lms.lot_matched_qt = lp.lot_quantity
|
||||
lms.lot_sale = s
|
||||
LotQt.match_lots([lmp],[lms])
|
||||
Lot = Pool().get('lot.lot')
|
||||
with Lot.skip_quantity_consistency():
|
||||
LotQt.match_lots([lmp],[lms])
|
||||
Lot.assert_lines_quantity_consistency(
|
||||
[p.lines[0], s.lines[0]])
|
||||
Sale._process_shipment([s])
|
||||
|
||||
elif do_ == "add":
|
||||
@@ -876,4 +899,4 @@ class BotAction(ModelSQL, ModelView):
|
||||
|
||||
@classmethod
|
||||
def state_dashboard(cls):
|
||||
return 1
|
||||
return 1
|
||||
|
||||
@@ -65,7 +65,6 @@ class AnalyticDimensionAssignment(ModelSQL, ModelView):
|
||||
value = fields.Many2One(
|
||||
'analytic.dimension.value',
|
||||
'Value',
|
||||
required=True,
|
||||
domain=[
|
||||
('dimension', '=', Eval('dimension')),
|
||||
],
|
||||
@@ -76,4 +75,4 @@ class AnalyticDimensionAssignment(ModelSQL, ModelView):
|
||||
'purchase.purchase',
|
||||
'Purchase',
|
||||
ondelete='CASCADE'
|
||||
)
|
||||
)
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
# Proposition d'architecture des regles business CTRM
|
||||
|
||||
Statut: `proposal`
|
||||
Date: `2026-05-11`
|
||||
Scope: documentation metier `purchase_trade` / CTRM
|
||||
|
||||
## Objectif
|
||||
|
||||
Cette note garde la proposition de structure documentaire pour les regles
|
||||
business trading. L'objectif est de pouvoir s'appuyer sur une source de verite
|
||||
exhaustive sans charger trop de contexte quand une demande concerne seulement
|
||||
un theme precis.
|
||||
|
||||
## Etat des lieux
|
||||
|
||||
- `modules/AGENTS.md` porte les regles courtes communes aux modules.
|
||||
- `modules/purchase_trade/AGENTS.md` porte les invariants critiques du module
|
||||
trading et les fichiers pivots.
|
||||
- `modules/purchase_trade/docs/business-rules.md` est la base metier la plus
|
||||
riche, mais elle melange regles canoniques, notes de session et quelques
|
||||
doublons de numerotation.
|
||||
- `notes/business_rules.md` sert de journal transverse de sessions.
|
||||
- `notes/accounting/` documente plutot le standard comptable Tryton, les gaps
|
||||
de reporting et les extensions comptables `purchase_trade`.
|
||||
- Les fichiers specialises comme `fees.md`,
|
||||
`padding-invoice-accounting.md`, `template-rules.md` et
|
||||
`template-properties.md` sont de bons exemples de documentation ciblee.
|
||||
|
||||
## Principe recommande
|
||||
|
||||
Garder `purchase_trade` comme source de verite canonique pour le metier CTRM,
|
||||
mais decouper les regles par theme.
|
||||
|
||||
`AGENTS.md` doit rester court et servir de routeur:
|
||||
|
||||
- charger seulement les invariants toujours utiles;
|
||||
- indiquer quel fichier thematique lire selon la demande;
|
||||
- eviter d'embarquer toute la documentation business a chaque intervention.
|
||||
|
||||
Les notes de session doivent rester historiques. Une decision devient
|
||||
canonique seulement quand elle est promue dans un fichier thematique.
|
||||
|
||||
## Structure cible proposee
|
||||
|
||||
```text
|
||||
modules/purchase_trade/docs/business/
|
||||
README.md
|
||||
INDEX.md
|
||||
glossary.md
|
||||
invariants.md
|
||||
sessions.md
|
||||
|
||||
contracts.md
|
||||
lots-and-quantities.md
|
||||
matching.md
|
||||
shipments-execution.md
|
||||
pricing.md
|
||||
fees.md
|
||||
valuation-pnl-mtm.md
|
||||
invoicing.md
|
||||
accounting-bridge.md
|
||||
payments-banking.md
|
||||
reports-templates.md
|
||||
risk-credit-forex.md
|
||||
lots-management.md
|
||||
```
|
||||
|
||||
## Role des fichiers
|
||||
|
||||
- `README.md`: explique la convention documentaire et le statut des fichiers.
|
||||
- `INDEX.md`: routeur par theme, avec les fichiers a lire selon le sujet.
|
||||
- `glossary.md`: vocabulaire stable du CTRM Tradon.
|
||||
- `invariants.md`: principes structurants toujours vrais.
|
||||
- `sessions.md`: journal historique non canonique.
|
||||
- fichiers thematiques: source canonique exhaustive pour chaque domaine.
|
||||
|
||||
## Grands themes metier
|
||||
|
||||
- `contracts.md`: achats, ventes, lignes, dates, lieux, banques,
|
||||
counterparties, quantite contractuelle, statut finished.
|
||||
- `lots-and-quantities.md`: lots virtuels/physiques, `lot.qt`, historique,
|
||||
weighing, split/merge, suppression de lot physique, solde ouvert.
|
||||
- `matching.md`: matching purchase/sale, open matching via `lot.qt`, wizard
|
||||
`Create contracts`, back-to-back, multi-lots.
|
||||
- `shipments-execution.md`: shipments, dropship/inbound, controller, SLA,
|
||||
locations, surveyor, weight reports, BL.
|
||||
- `pricing.md`: `priced`, `basis`, components, manual pricing, summary,
|
||||
linked currency, premium, settlement price.
|
||||
- `fees.md`: fees ligne/shipment, lots effectifs, `% rate`, `ppack`,
|
||||
maritime freight, insurance, declencheurs PnL.
|
||||
- `valuation-pnl-mtm.md`: valuation achat/vente, sale-first, references,
|
||||
MTM strategies, derivatives, exclusions fees.
|
||||
- `invoicing.md`: provisional/final, facture achat/vente, padding, quantites
|
||||
facturees, liens lot/facture, templates invoice.
|
||||
- `accounting-bridge.md`: effets sur `account_invoice`, `account.move`,
|
||||
taxes, devises, additional moves, validate/post.
|
||||
- `payments-banking.md`: payment terms, echeances, comptes bancaires
|
||||
party/company, payment order.
|
||||
- `reports-templates.md`: Relatorio, proprietes `report_*`, documents trade,
|
||||
regles XML, cache report.
|
||||
- `risk-credit-forex.md`: credit risk, forex, FX revaluation, exposition.
|
||||
- `lots-management.md`: rapports operationnels, filtres matching/side/shipping,
|
||||
dimension, strategy.
|
||||
|
||||
## Format conseille pour une regle
|
||||
|
||||
```md
|
||||
### BR-PT-VAL-007 - Le MTM ne s'applique pas aux fees
|
||||
|
||||
Theme: valuation-pnl-mtm
|
||||
Status: active
|
||||
Scope: purchase_trade
|
||||
Code: valuation.py, fee.py
|
||||
Depends on: BR-PT-FEE-002
|
||||
|
||||
Summary: Les lignes de fee ne portent jamais mtm, mtm_price ou strategy.
|
||||
|
||||
Rule:
|
||||
- MTM autorise seulement pour `pur. priced`, `sale priced`, `derivative`.
|
||||
- Fees: `pur. fee`, `sale fee`, `shipment fee`, `line fee` restent hors MTM.
|
||||
|
||||
Tests:
|
||||
- valuation fee sans mtm
|
||||
- valuation priced avec mtm
|
||||
```
|
||||
|
||||
Les identifiants devraient encoder le theme (`LOT`, `MAT`, `PRI`, `FEE`,
|
||||
`VAL`, `INV`, `ACC`, etc.) plutot qu'une numerotation globale unique. Cela
|
||||
evite les collisions quand les regles sont ajoutees au fil des sessions.
|
||||
|
||||
## Routeur de contexte a ajouter plus tard dans `AGENTS.md`
|
||||
|
||||
```md
|
||||
Si la demande concerne:
|
||||
- lots, quantites, matching: lire `docs/business/lots-and-quantities.md` et
|
||||
`docs/business/matching.md`
|
||||
- pricing, premium, basis: lire `docs/business/pricing.md`
|
||||
- fees, freight, insurance: lire `docs/business/fees.md`
|
||||
- valuation, PnL, MTM: lire `docs/business/valuation-pnl-mtm.md`
|
||||
- facture, padding, payment order: lire `docs/business/invoicing.md` et
|
||||
`docs/business/accounting-bridge.md`
|
||||
- templates `.fodt`: lire `docs/business/reports-templates.md` et
|
||||
`docs/template-rules.md`
|
||||
```
|
||||
|
||||
## Migration proposee
|
||||
|
||||
1. Creer `docs/business/INDEX.md`, `invariants.md` et `glossary.md`.
|
||||
2. Decouper `docs/business-rules.md` par theme, sans changer le fond.
|
||||
3. Deplacer les notes de session dans `sessions.md`.
|
||||
4. Promouvoir uniquement les decisions durables dans les fichiers thematiques.
|
||||
5. Reduire `modules/purchase_trade/AGENTS.md` a un memo court + routeur.
|
||||
6. Garder `notes/accounting/` comme documentation comptable Tryton et comme
|
||||
vue specialisee des extensions comptables `purchase_trade`.
|
||||
|
||||
## Risque actuel a traiter lors de la migration
|
||||
|
||||
Le risque principal n'est pas le manque de regles, mais leur dilution entre
|
||||
plusieurs fichiers. Certaines decisions existent a la fois dans `AGENTS.md`,
|
||||
`notes/business_rules.md`, `business-rules.md`, `fees.md` et les notes
|
||||
accounting. La migration devra designer une source canonique par theme et
|
||||
laisser les autres fichiers jouer leur role d'index, de journal ou de vue
|
||||
specialisee.
|
||||
@@ -1,8 +1,8 @@
|
||||
# Business Rules - Purchase Trade
|
||||
|
||||
Statut: `draft`
|
||||
Version: `v0.2`
|
||||
Derniere mise a jour: `2026-03-27`
|
||||
Version: `v0.8`
|
||||
Derniere mise a jour: `2026-05-10`
|
||||
Owner metier: `a completer`
|
||||
Owner technique: `a completer`
|
||||
|
||||
@@ -100,6 +100,685 @@ Owner technique: `a completer`
|
||||
- Priorite:
|
||||
- `importante`
|
||||
|
||||
### BR-PT-004 - La valuation doit couvrir les flux purchase et sale, y compris les sales non matchees
|
||||
|
||||
- Intent: obtenir un PnL coherent cote achat et cote vente, meme lorsqu'une
|
||||
sale n'est pas encore matchee a une purchase.
|
||||
- Description:
|
||||
- Le flux historique de valuation part de `purchase.line` puis remonte vers
|
||||
les ventes via les lots/lots matchants.
|
||||
- Le systeme doit egalement savoir valoriser directement une `sale.line`
|
||||
non matchee ("sale-first").
|
||||
- Une sale non matchee doit creer des lignes dans
|
||||
`valuation.valuation` et `valuation.valuation.line` afin d'apparaitre dans
|
||||
l'onglet PnL de la sale.
|
||||
- Resultat attendu:
|
||||
- pour une `sale.line` non matchee, generer au minimum les types:
|
||||
- `sale priced`
|
||||
- `sale fee`
|
||||
- `derivative` si la ligne porte des derives
|
||||
- si la sale est matchee via un lot physique, les lignes purchase portees par
|
||||
ce lot physique doivent aussi renseigner `sale` et `sale_line`
|
||||
- une sale matchee doit donc voir:
|
||||
- ses lignes `sale *`
|
||||
- les lignes purchase portees par le lot physique partage
|
||||
- avant creation du lot physique, si le matching existe seulement via
|
||||
`lot.qt` (`lot_p` purchase ouvert -> `lot_s` sale ouvert), les lignes PnL
|
||||
purchase-side doivent aussi renseigner `sale` et `sale_line` afin
|
||||
d'apparaitre dans l'onglet PnL de la sale matchee
|
||||
- un lot ouvert / virtuel avec quantite courante a zero ne doit pas generer
|
||||
de lignes de fees PnL residuelles
|
||||
- si plusieurs sales differentes sont matchees au meme lot ouvert, ne pas
|
||||
attacher arbitrairement une sale unique aux lignes purchase-side
|
||||
- Priorite:
|
||||
- `structurante`
|
||||
|
||||
### BR-PT-005 - Les references de valuation doivent decrire la nature du lot de la ligne
|
||||
|
||||
- Intent: eviter les ambiguïtes dans les ecrans PnL entre lots `open` et lots
|
||||
`physic`.
|
||||
- Description:
|
||||
- La reference affichee dans la valuation doit decrire la ligne elle-meme,
|
||||
pas son vis-a-vis.
|
||||
- Les references autorisees pour les lignes de prix sont:
|
||||
- `Purchase/Open`
|
||||
- `Purchase/Physic`
|
||||
- `Sale/Open`
|
||||
- `Sale/Physic`
|
||||
- Resultat attendu:
|
||||
- un lot `virtual` cote purchase ne doit jamais sortir avec la reference
|
||||
`Purchase/Physic`
|
||||
- un lot `virtual` cote sale ne doit jamais sortir avec la reference
|
||||
`Sale/Physic`
|
||||
- un lot physique matche peut produire:
|
||||
- une ligne purchase en `Purchase/Physic`
|
||||
- une ligne sale en `Sale/Physic`
|
||||
- un open sale matche a un open purchase peut produire des quantites egales
|
||||
tout en gardant des references differentes (`Purchase/Open` vs `Sale/Open`)
|
||||
- Priorite:
|
||||
- `importante`
|
||||
|
||||
### BR-PT-006 - Une sale basis sans prix detaille doit quand meme apparaitre en valuation
|
||||
|
||||
- Intent: ne pas perdre les lignes de PnL lorsque le detail de pricing n'est
|
||||
pas encore renseigne.
|
||||
- Description:
|
||||
- Une `sale.line` de type `basis` peut exister avec un lot `virtual`, sans
|
||||
`price_summary` et sans `lot_price_sale`.
|
||||
- Dans ce cas, la valuation doit quand meme creer une ligne `sale priced`.
|
||||
- Resultat attendu:
|
||||
- si `price_summary` est vide:
|
||||
- creer une ligne `sale priced`
|
||||
- avec `price = 0`
|
||||
- avec `amount = 0`
|
||||
- avec un `state` de type `unfixed`
|
||||
- si `lot_price_sale` est vide sur un lot sale, utiliser `sale_line.unit_price`
|
||||
comme fallback quand il existe
|
||||
- Priorite:
|
||||
- `importante`
|
||||
|
||||
### BR-PT-007 - Le MTM de valuation ne s'applique pas aux fees
|
||||
|
||||
- Intent: distinguer les lignes de prix marquables au marche des lignes de
|
||||
frais qui ne doivent pas etre mark-to-market.
|
||||
- Description:
|
||||
- Le systeme peut renseigner `mtm_price`, `mtm` et `strategy` uniquement pour:
|
||||
- `pur. priced`
|
||||
- `sale priced`
|
||||
- `derivative`
|
||||
- Les fees (`pur. fee`, `sale fee`, `shipment fee`, `line fee`) ne doivent
|
||||
jamais porter de valorisation MTM.
|
||||
- Resultat attendu:
|
||||
- les lignes de fee doivent conserver:
|
||||
- `mtm_price = NULL`
|
||||
- `mtm = NULL`
|
||||
- `strategy = NULL`
|
||||
- `mtm_price` doit representer le prix brut de valorisation sans appliquer le
|
||||
ratio de composant
|
||||
- `mtm` reste le montant calcule selon la logique de strategie
|
||||
- Priorite:
|
||||
- `structurante`
|
||||
|
||||
### BR-PT-007-bis - Mark as finished ignore seulement le reliquat ouvert
|
||||
|
||||
- Intent: conserver le PnL reel des lots executes tout en masquant le reliquat
|
||||
ouvert d'une ligne terminee.
|
||||
- Description:
|
||||
- Sur une `purchase.line` ou une `sale.line`, le champ `finished`
|
||||
(`Mark as finished`) ne signifie pas que la ligne ne doit plus etre
|
||||
valorisee.
|
||||
- Il signifie seulement que les lots ouverts / virtuels restants ne doivent
|
||||
plus alimenter la valuation.
|
||||
- Dans `Lots Management`, la meme regle s'applique aux lignes `lot.qt`:
|
||||
une ligne `lot.qt` doit etre masquee si son lot virtuel purchase
|
||||
(`lot_p`) ou son lot virtuel sale (`lot_s`) est rattache a une ligne
|
||||
marquee finie.
|
||||
- Cette exclusion vaut meme si la ligne `lot.qt` est deja matchee ou liee a
|
||||
un shipment.
|
||||
- Resultat attendu:
|
||||
- les lots physiques continuent de produire:
|
||||
- PnL prix
|
||||
- PnL fees
|
||||
- les derivatives continuent de produire du PnL meme si la ligne est
|
||||
marquee finie
|
||||
- les lots virtuels / ouverts d'une ligne finie sont ignores
|
||||
- les lots physiques restent visibles dans `Lots Management`, meme si leur
|
||||
ligne purchase ou sale est marquee finie
|
||||
- Priorite:
|
||||
- `structurante`
|
||||
|
||||
### BR-PT-008 - Le premium fait partie du prix contractuel en `priced` et en `basis`
|
||||
|
||||
- Intent: garantir que le montant total valorise et facture reflete toujours le
|
||||
premium/discount saisi sur la ligne.
|
||||
- Description:
|
||||
- Le `premium` d'une `purchase.line` ou `sale.line` doit impacter le prix
|
||||
total quelle que soit la `price_type`.
|
||||
- Cette regle vaut pour:
|
||||
- les calculs de `amount`
|
||||
- la valuation / PnL
|
||||
- Resultat attendu:
|
||||
- le `unit_price` reste le prix de base, hors premium
|
||||
- en `priced`, le montant economique = `unit_price + premium`
|
||||
- en `basis`, le premium s'ajoute aussi au prix total economique
|
||||
- en valuation `basis`, le premium s'applique a chaque composant valorise
|
||||
(ex: meme premium repete sur chaque bloc ICE)
|
||||
- Exemple metier:
|
||||
- `8.30 USC/LB 500 TONS ON ICE MCH'26`
|
||||
- `8.30 USC/LB 500 TONS ON ICE MAY 26`
|
||||
- le premium `8.30 USC/LB` s'applique a chaque composant
|
||||
- Priorite:
|
||||
- `structurante`
|
||||
|
||||
### BR-PT-009 - En linked currency, le premium est exprime dans la devise/unite liee
|
||||
|
||||
- Intent: respecter la facon dont les traders saisissent les prix sur certains
|
||||
produits (ex: coton en `USC/LB`).
|
||||
- Description:
|
||||
- Quand `enable_linked_currency` est coche, le `premium` est saisi dans la
|
||||
devise / unite liee, pas dans la devise / unite native de la ligne.
|
||||
- Le systeme doit convertir ce premium vers le repere de la ligne pour les
|
||||
calculs internes de montant et de valuation.
|
||||
- Resultat attendu:
|
||||
- `premium` est interprete dans le repere `linked_currency` / `linked_unit`
|
||||
- le `unit_price` ne doit pas absorber ce premium
|
||||
- les `amount` et valuations doivent refleter ce premium converti
|
||||
- si `linked currency` est cochee, `linked_price`, `linked_currency` et
|
||||
`linked_unit` sont obligatoires
|
||||
- Priorite:
|
||||
- `structurante`
|
||||
|
||||
### BR-PT-010 - En `basis + linked currency`, le linked price suit le basis brut
|
||||
|
||||
- Intent: rendre lisible la decomposition entre prix basis de marche et premium.
|
||||
- Description:
|
||||
- Quand une ligne est en `basis` et `linked currency`, le bloc
|
||||
`linked_price` doit etre recalcule automatiquement.
|
||||
- Ce `linked_price` doit representer le prix basis brut, hors premium.
|
||||
- Le `unit_price` de la ligne doit rester ce prix brut converti.
|
||||
- Le premium converti n'est ajoute qu'au niveau du `amount`.
|
||||
- Resultat attendu:
|
||||
- modification du basis -> mise a jour automatique du `linked_price`
|
||||
- `linked_price` = base market / basis
|
||||
- `unit_price` = `linked_price` converti
|
||||
- `amount` = quantite * (`unit_price` + premium converti)
|
||||
- Priorite:
|
||||
- `importante`
|
||||
|
||||
### BR-PT-011 - Une sale line non matchee avec lot virtuel doit generer une valuation sale-first des la validation
|
||||
|
||||
- Intent: ne pas attendre un matching purchase pour afficher le PnL d'une sale
|
||||
ouverte.
|
||||
- Description:
|
||||
- Lors de la validation d'une `sale.line`, le systeme peut creer un lot
|
||||
`virtual`.
|
||||
- Si aucun `lot.qt` ne relie ce lot a une `purchase.line`, il faut tout de
|
||||
meme generer la valuation cote sale.
|
||||
|
||||
### BR-PT-012 - Le wizard Create contracts peut creer un seul achat matche a plusieurs open sales
|
||||
|
||||
- Intent: permettre la creation d'un contrat achat unique a partir de plusieurs
|
||||
`lot.qt` de vente selectionnes.
|
||||
- Description:
|
||||
- En mode `matched`, le wizard `Create contracts` peut recevoir plusieurs
|
||||
`lot.qt` selectionnes.
|
||||
- Il doit creer un seul contrat, avec une ligne par lot source selectionne.
|
||||
- Chaque ligne doit conserver son lot d'origine pour le matching.
|
||||
- Resultat attendu:
|
||||
- le wizard agrege les quantites de la selection
|
||||
- il refuse une quantite saisie differente du total selectionne
|
||||
- il conserve `created_by_code = True` sur les lignes creees pour ne pas
|
||||
declencher les creations automatiques parasites lors des validations
|
||||
- Priorite:
|
||||
- `importante`
|
||||
|
||||
### BR-PT-013 - Le texte par defaut de pricing_rule est configure globalement
|
||||
|
||||
- Intent: centraliser un texte metier recurrent reutilise a la creation des
|
||||
lignes achat et vente.
|
||||
- Description:
|
||||
- Le module expose un singleton `purchase_trade.configuration` avec un champ
|
||||
texte `pricing_rule`.
|
||||
- Toute nouvelle `purchase.line` et `sale.line` doit prendre ce texte comme
|
||||
valeur par defaut de `pricing_rule`.
|
||||
- Resultat attendu:
|
||||
- la configuration est accessible depuis le menu `Prices`
|
||||
- la valeur sert de defaut a la creation des lignes
|
||||
- les lignes existantes ne sont pas modifiees retroactivement
|
||||
- Priorite:
|
||||
- `importante`
|
||||
|
||||
### BR-PT-014 - L'affectation d'un controller doit suivre l'ecart a l'objectif regional
|
||||
|
||||
- Intent: repartir les controllers selon les cibles definies dans l'onglet
|
||||
`Execution` des `party.party`.
|
||||
- Description:
|
||||
- chaque ligne `party.execution` fixe une cible `% targeted` pour un
|
||||
controller sur une `country.region`
|
||||
- le `% achieved` est calcule a partir des `stock.shipment.in` deja affectes
|
||||
a un controller dans cette zone
|
||||
- la zone d'un shipment est determinee par `shipment.to_location.country`
|
||||
- une region parente couvre aussi ses sous-regions
|
||||
- Resultat attendu:
|
||||
- pour une ligne `party.execution`, `achieved_percent` =
|
||||
`shipments de la zone avec ce controller / shipments controles de la zone`
|
||||
- le denominateur ne compte que les `stock.shipment.in` qui ont deja un
|
||||
`controller`; les shipments encore non affectes ne biaisent donc pas la
|
||||
statistique affichee
|
||||
- lors d'un choix automatique de controller, la priorite va a la regle dont
|
||||
l'ecart `targeted - achieved` est le plus eleve
|
||||
- un controller a `80%` cible et `40%` reel doit donc passer avant un
|
||||
controller a `50%` cible et `45%` reel sur la meme zone
|
||||
- l'appartenance a la zone se lit depuis `shipment.to_location.country`, et
|
||||
une region parente couvre aussi ses sous-regions
|
||||
- Priorite:
|
||||
- `importante`
|
||||
|
||||
### BR-PT-014-bis - Les couts SLA controller peuvent cibler pays et/ou lieu
|
||||
|
||||
- Intent: permettre de definir le cout d'un controller soit pour un pays, soit
|
||||
pour une location, soit pour un couple pays + location.
|
||||
- Description:
|
||||
- Dans l'onglet `Execution` de `party.party`, les lignes SLA
|
||||
(`party.execution.place`) peuvent porter:
|
||||
- `country`
|
||||
- `location`
|
||||
- ou les deux.
|
||||
- Lors de la creation automatique du fee controller sur un
|
||||
`stock.shipment.in`, le systeme continue de partir de
|
||||
`shipment.to_location`.
|
||||
- Le pays utilise pour le matching est `shipment.to_location.country`.
|
||||
- Priorite de matching:
|
||||
- couple `country + location`
|
||||
- puis `location` seule
|
||||
- puis `country` seul
|
||||
- Resultat attendu:
|
||||
- un cout defini uniquement sur un pays s'applique a toutes les destinations
|
||||
de ce pays.
|
||||
- un cout defini uniquement sur une location s'applique a cette destination,
|
||||
quel que soit le pays porte par la location.
|
||||
- un cout defini sur le couple pays + location est le plus specifique et
|
||||
prime les deux autres.
|
||||
- Priorite:
|
||||
- `importante`
|
||||
|
||||
### BR-PT-015 - Les weight reports distants par lot partent du weight report global attache au shipment
|
||||
|
||||
- Intent: separer la creation du `weight.report` global et l'export detaille
|
||||
par lot vers le systeme distant.
|
||||
- Description:
|
||||
- l'automation cree le `weight.report` global et l'attache au
|
||||
`stock.shipment.in`
|
||||
- l'export FastAPI par lot ne part plus directement de l'automation
|
||||
- l'utilisateur ouvre le `weight.report` voulu depuis le shipment et lance
|
||||
l'action d'export depuis ce rapport
|
||||
- Resultat attendu:
|
||||
- le rapport choisi sert de base unique pour calculer les payloads par lot
|
||||
- seuls les lots physiques des `incoming_moves` du shipment sont exportes
|
||||
- l'action exige au minimum un `controller` et un `returned_id` sur le
|
||||
shipment
|
||||
- les cles renvoyees par le systeme distant et la date d'envoi sont
|
||||
conservees sur le `weight.report` local
|
||||
- Priorite:
|
||||
- `importante`
|
||||
|
||||
### BR-PT-016 - En pricing manuel, seules la quantite fixee du jour et le prix de marche sont saisis
|
||||
|
||||
- Intent: simplifier la saisie utilisateur et garantir une coherence unique
|
||||
entre les colonnes de `pricing.pricing`.
|
||||
- Description:
|
||||
- Pour une ligne de `pricing.pricing` en mode manuel, l'utilisateur ne doit
|
||||
saisir que:
|
||||
- `quantity`
|
||||
- `settl_price`
|
||||
- Les autres colonnes de suivi sont derivees automatiquement sur tout le
|
||||
groupe metier (`line + component` ou `sale_line + component`) trie par
|
||||
`pricing_date`.
|
||||
- Resultat attendu:
|
||||
- `fixed_qt` = cumul des `quantity`
|
||||
- `fixed_qt_price` = moyenne ponderee cumulee des `settl_price`
|
||||
- `unfixed_qt` = quantite de base de la ligne - `fixed_qt`
|
||||
- `unfixed_qt_price` = dernier prix disponible de la courbe du composant
|
||||
quand le composant est lie a une courbe; sinon fallback sur `settl_price`
|
||||
de la ligne
|
||||
- `eod_price` = moyenne ponderee entre jambe fixee et non fixee
|
||||
- `last=True` reste unique par groupe et suit la plus grande `pricing_date`
|
||||
- Hors scope:
|
||||
- la generation automatique des lignes quand `pricing.component.auto = True`
|
||||
ne doit pas changer de comportement
|
||||
- Priorite:
|
||||
- `structurante`
|
||||
|
||||
### BR-PT-017 - Le workflow Validate des factures client doit aussi attribuer le numero
|
||||
|
||||
- Intent: aligner le comportement des factures client et fournisseur au moment
|
||||
de `Validate`.
|
||||
- Description:
|
||||
- Lors du workflow `Validate` sur `account.invoice`, une facture client
|
||||
(`type = out`) doit maintenant:
|
||||
- creer son `account.move`
|
||||
- recevoir son `number`
|
||||
- La numerotation ne doit plus etre repoussee au `Post` cote client.
|
||||
- Resultat attendu:
|
||||
- a l'issue de `Validate`, une facture fournisseur ou client possede deja:
|
||||
- son `account.move`
|
||||
- son `number`
|
||||
- `Post` conserve son role de posting comptable sans reintroduire de
|
||||
difference de session/fresh login cote client
|
||||
- Priorite:
|
||||
- `importante`
|
||||
- Resultat attendu:
|
||||
- apres creation du lot virtuel, si aucun matching purchase n'existe:
|
||||
- appeler `Valuation.generate_from_sale_line(line)`
|
||||
- creer au moins la ligne `sale priced` fallback si la ligne porte un prix
|
||||
economique via le premium
|
||||
- Priorite:
|
||||
- `importante`
|
||||
|
||||
### BR-PT-018 - Les contrats distinguent le compte bancaire tiers du compte bancaire compagnie
|
||||
|
||||
- Intent: eviter de confondre le compte bancaire du client/fournisseur avec le
|
||||
compte bancaire de la compagnie courante utilise pour encaisser ou payer.
|
||||
- Description:
|
||||
- Sur `sale.sale` et `purchase.purchase`, `bank_account` represente le compte
|
||||
bancaire propre a la `party` du contrat.
|
||||
- Sur `sale.sale` et `purchase.purchase`, `our_bank_account` represente le
|
||||
compte bancaire utilise par la compagnie courante pour encaisser ou payer.
|
||||
- `bank_account` est limite aux comptes bancaires de la party du contrat.
|
||||
- `our_bank_account` reste librement selectionnable parmi les comptes
|
||||
bancaires disponibles.
|
||||
- Resultat attendu:
|
||||
- si plusieurs comptes existent, le compte dont la devise correspond a la
|
||||
devise du contrat est propose en priorite
|
||||
- si aucun compte ne matche la devise, le premier compte disponible est
|
||||
propose
|
||||
- le champ `Our Bank Account` est pre-rempli depuis les comptes de la
|
||||
compagnie quand possible, mais sa recherche n'est pas limitee a ces comptes
|
||||
- Priorite:
|
||||
- `importante`
|
||||
|
||||
### BR-PT-019 - Le padding de facture provisoire vente augmente la quantite facturee sans modifier le lot physique
|
||||
|
||||
- Intent: permettre de constituer une provision sur une facture provisoire
|
||||
vente tout en gardant la trace de l'ecart avec la quantite reelle du lot.
|
||||
- Description:
|
||||
- Le wizard `lot.invoice` expose un padding global uniquement pour les
|
||||
factures provisoires cote vente.
|
||||
- Ce padding global est reparti egalement entre les lots selectionnes.
|
||||
- La quantite de chaque ligne de facture provisoire vente est augmentee de la
|
||||
part de padding du lot.
|
||||
- Le padding ne modifie pas la quantite physique du lot.
|
||||
- Resultat attendu:
|
||||
- deux lots factures ensemble avec un padding global de `1000` recoivent
|
||||
chacun `500` de padding
|
||||
- la ligne facture affiche la quantite augmentee
|
||||
- la ligne facture expose `Inc. padding`
|
||||
- le lot conserve sa part de padding dans `sale_invoice_padding`
|
||||
- Validation comptable:
|
||||
- au `Validate`, le move principal de facture inclut deja le padding car il
|
||||
est integre a `account.invoice.line.quantity`
|
||||
- la provisoire cree un `additional_move` avec le couple de comptes configure
|
||||
`Default Sale Padding` / `Default Accrual Padding`
|
||||
- montant provisoire:
|
||||
`lot.sale_invoice_padding * account.invoice.line.unit_price`
|
||||
- la finale cree l'ecriture inverse pour exactement le montant padding
|
||||
comptabilise lors de la provisoire
|
||||
- le montant d'extourne finale doit etre relu depuis
|
||||
`lot.sale_invoice_line_prov`, afin de reprendre le prix, la devise, la date
|
||||
et le taux de la provisoire
|
||||
- le calcul de quantite finale doit retirer le padding de la quantite
|
||||
provisoire avant de calculer le delta
|
||||
- voir `modules/purchase_trade/docs/padding-invoice-accounting.md`
|
||||
- Priorite:
|
||||
- `importante`
|
||||
|
||||
### BR-PT-012 - Fallback valuation basis sans summary: utiliser le prix economique de la ligne
|
||||
|
||||
- Intent: eviter qu'une valuation `basis` ouverte sorte a zero alors que la
|
||||
ligne a bien une valeur economique via le premium.
|
||||
- Description:
|
||||
- Une ligne `basis` peut ne pas avoir encore de `price_summary`.
|
||||
- Dans ce cas, la valuation fallback ne doit pas prendre `unit_price` seul si
|
||||
celui-ci est brut et hors premium.
|
||||
- Resultat attendu:
|
||||
- le fallback valuation `basis` doit utiliser:
|
||||
- `unit_price + premium converti`
|
||||
- cette regle vaut au minimum pour:
|
||||
- `sale.line` non matchee
|
||||
- `purchase.line` sans summary
|
||||
- Priorite:
|
||||
- `importante`
|
||||
|
||||
### BR-PT-013 - Create Contracts multi-lots doit conserver un matching par lot source
|
||||
|
||||
- Intent: permettre la creation d'un seul contrat mirror a partir de plusieurs
|
||||
open quantities sans perdre le lien lot-a-lot.
|
||||
- Description:
|
||||
- Le wizard `Create contracts` peut etre lance avec plusieurs `lot.qt`
|
||||
selectionnes.
|
||||
- En creation `matched`, le systeme doit creer un seul contrat avec une ligne
|
||||
par lot source selectionne, et chaque ligne doit etre matchee avec son lot
|
||||
d'origine.
|
||||
- Resultat attendu:
|
||||
- la quantite totale du wizard = somme des open quantities selectionnees
|
||||
- le contrat cree porte plusieurs lignes si plusieurs lots source sont
|
||||
selectionnes
|
||||
- chaque ligne creee reutilise le `shipment_origin` et le lot source qui lui
|
||||
correspondent
|
||||
- `created_by_code` doit rester positionne sur les lignes creees par wizard
|
||||
pour eviter la recreation automatique de lots virtuels dans les `validate`
|
||||
de `purchase.line`, `sale.line` et `lot.lot`
|
||||
- Priorite:
|
||||
- `importante`
|
||||
|
||||
### BR-PT-014 - Delivery period: From doit rester inferieur ou egal a To
|
||||
|
||||
- Intent: eviter les periodes de livraison incoherentes sur les lignes achat et
|
||||
vente.
|
||||
- Description:
|
||||
- Les champs `from_del` et `to_del` sont presents sur `purchase.line` et
|
||||
`sale.line`.
|
||||
- Si les deux dates sont renseignees, `from_del` ne doit jamais etre
|
||||
posterieur a `to_del`.
|
||||
- Resultat attendu:
|
||||
- la sauvegarde d'une `purchase.line` ou `sale.line` est bloquee si
|
||||
`from_del > to_del`
|
||||
- une date ouverte reste autorisee si seulement une des deux bornes est
|
||||
renseignee
|
||||
- Priorite:
|
||||
- `importante`
|
||||
|
||||
### BR-PT-015 - Pricing manuel: composant limite a la ligne courante
|
||||
|
||||
- Intent: eviter qu'une ligne de pricing saisie manuellement utilise un
|
||||
composant rattache a une autre ligne de contrat.
|
||||
- Description:
|
||||
- Dans l'onglet `Pricing dates` d'une `purchase.line`, le champ
|
||||
`pricing.pricing.price_component` doit proposer uniquement les composants
|
||||
dont `pricing.component.line` est la ligne achat courante.
|
||||
- Dans l'onglet `Pricing dates` d'une `sale.line`, il doit proposer
|
||||
uniquement les composants dont `pricing.component.sale_line` est la ligne
|
||||
vente courante.
|
||||
- Une ligne de pricing sans composant reste possible pour le mode manuel sans
|
||||
component.
|
||||
- Resultat attendu:
|
||||
- le domaine UI filtre les composants sur la ligne courante
|
||||
- une validation serveur bloque aussi un composant appartenant a une autre
|
||||
ligne
|
||||
- Priorite:
|
||||
- `importante`
|
||||
|
||||
### BR-PT-016 - Les fees `% rate` utilisent le delta de financement
|
||||
|
||||
- Intent: aligner le calcul des frais financiers `% rate` entre achat et vente.
|
||||
- Description:
|
||||
- Pour un `fee.fee` en mode `rate`, le calcul ne depend pas du
|
||||
`payment_term`, ni de la date du jour, ni de `fee_date`.
|
||||
- La periode de calcul est directement le champ `fin_int_delta` de la ligne
|
||||
`Estimated date` avec `trigger = bldate`.
|
||||
- Resultat attendu:
|
||||
- purchase et sale appliquent la meme formule:
|
||||
`amount = unit_price * quantity * (price / 100) * fin_int_delta / 360`
|
||||
- le montant affiche sur `fee.fee` doit rester non signe: un
|
||||
`fin_int_delta` negatif ne doit pas afficher un fee negatif
|
||||
- le PnL applique seul le signe metier:
|
||||
- `PAY` => montant negatif
|
||||
- `REC` => montant positif
|
||||
- si aucune Estimated Date `bldate` n'est renseignee, aucun montant `% rate`
|
||||
n'est calcule
|
||||
- Priorite:
|
||||
- `importante`
|
||||
|
||||
### BR-PT-020 - Le solde `lot.qt` ouvert suit les lots physiques existants
|
||||
|
||||
- Intent: eviter qu'une hausse ou baisse de quantite contractuelle double le
|
||||
reliquat ouvert quand des lots physiques existent deja.
|
||||
- Description:
|
||||
- Lorsqu'une `purchase.line.quantity_theorical` ou
|
||||
`sale.line.quantity_theorical` est modifiee, le `lot.qt` libre ne doit pas
|
||||
etre ajuste uniquement par delta.
|
||||
- Le systeme doit recalculer le solde ouvert cible depuis la quantite
|
||||
contractuelle, les lots physiques deja crees et les quantites deja
|
||||
allouees dans des `lot.qt` matches ou shippes.
|
||||
- Resultat attendu:
|
||||
- quantite virtuelle cible =
|
||||
`quantity_theorical - somme(lots physiques convertis dans l'unite ligne)`
|
||||
- `lot.qt` libre non matche / non shippe =
|
||||
`quantite virtuelle cible - somme(lot.qt deja matches ou shippes)`
|
||||
- si le resultat devient negatif, bloquer avec
|
||||
`Please unlink or unmatch lot`
|
||||
- exemple: une ligne achat passee de `10000` a `20000` avec deja `10000`
|
||||
physiques doit afficher `10000` ouverts et `10000` physiques, pas
|
||||
`20000` ouverts plus `10000` physiques.
|
||||
- Impacts fees:
|
||||
- apres toute modification de `quantity_theorical`, les fees de la ligne sont
|
||||
resynchronises avec la regle BR-PT-021.
|
||||
- si le fee est encore uniquement porte par le lot virtuel, sa quantite suit
|
||||
donc la nouvelle quantite virtuelle.
|
||||
- si le fee possede deja des lots physiques, une hausse ou baisse uniquement
|
||||
ouverte n'impacte pas sa quantite.
|
||||
- Priorite:
|
||||
- `structurante`
|
||||
|
||||
### BR-PT-021 - Les fees lies aux lots privilegient les physiques
|
||||
|
||||
- Intent: eviter qu'un fee cree sur un lot virtuel reste calcule sur la
|
||||
quantite contractuelle totale apres creation de lots physiques.
|
||||
- Detail implementation / PnL:
|
||||
- voir `modules/purchase_trade/docs/fees.md`
|
||||
- Description:
|
||||
- Le lien `fee.lots` avec le lot virtuel est conserve comme fallback.
|
||||
- Des qu'un fee possede au moins un lot physique dans `fee.lots`, les lots
|
||||
physiques deviennent la base effective du fee et le virtuel est ignore pour
|
||||
la quantite.
|
||||
- Resultat attendu:
|
||||
- si aucun physique n'existe, `fee.quantity` suit le lot virtuel rattache au
|
||||
fee.
|
||||
- si des physiques existent, `fee.quantity` suit uniquement la somme des lots
|
||||
physiques rattaches au fee.
|
||||
- pour `ppack`, la somme se fait sur `lot.lot_qt` des physiques.
|
||||
- pour les modes quantitatifs (`perqt`, `rate`, `pprice`, `pcost`), la somme
|
||||
se fait sur les quantites courantes converties des lots physiques.
|
||||
- supprimer le dernier physique fait retomber le fee sur son lot virtuel,
|
||||
puisque le lien virtuel est conserve.
|
||||
- le PnL fee applique la meme selection: full open tant qu'il n'y a que le
|
||||
virtuel, puis uniquement les lots physiques effectifs.
|
||||
- Points de synchronisation obligatoires:
|
||||
- creation d'un fee lie a une ligne ou a un shipment
|
||||
- ajout d'un lot physique dans `fee.lots`
|
||||
- modification de `purchase.line.quantity_theorical` ou
|
||||
`sale.line.quantity_theorical`
|
||||
- weighing / modification de quantite d'un lot physique
|
||||
- suppression d'un lot physique ou suppression d'un lien `fee.lots`
|
||||
- Regle de conception:
|
||||
- ne pas supprimer le lien `fee.lots` vers le lot virtuel lors de la creation
|
||||
de physiques.
|
||||
- le virtuel reste le fallback permettant de revenir au cas ouvert si tous
|
||||
les physiques sont supprimes.
|
||||
- toute logique metier, comptable ou PnL doit utiliser les lots effectifs du
|
||||
fee: physiques s'il y en a, sinon virtuels.
|
||||
- Priorite:
|
||||
- `structurante`
|
||||
|
||||
### BR-PT-022 - Remove physical lot restaure le lot.qt ouvert avec son contexte
|
||||
|
||||
- Intent: permettre d'annuler un lot physique cree par erreur sans perdre le
|
||||
contexte metier de shipment ou de matching deja porte par ce lot.
|
||||
- Description:
|
||||
- L'action `Remove physical lot` peut supprimer un lot physique meme s'il est
|
||||
shippe, matche, ou les deux.
|
||||
- Dans ces cas, l'utilisateur doit recevoir un warning confirmable avant la
|
||||
suppression.
|
||||
- La suppression n'est autorisee que si le `stock.move` lie au lot physique
|
||||
est encore en etat `draft`.
|
||||
- Si le `stock.move` n'est pas en `draft`, l'action est bloquee car le flux
|
||||
peut deja avoir des impacts stock/comptables.
|
||||
- Resultat attendu:
|
||||
- le `lot.move` et le `stock.move` correspondant sont supprimes avec le lot
|
||||
physique.
|
||||
- la quantite courante convertie du lot physique est restauree dans `lot.qt`.
|
||||
- si le lot etait shippe, la ligne `lot.qt` restauree conserve le shipment
|
||||
(`lot_shipment_in`, `lot_shipment_internal` ou `lot_shipment_out`).
|
||||
- si le lot etait matche, la ligne `lot.qt` restauree conserve le lot sale
|
||||
virtuel (`lot_s`).
|
||||
- si une ligne `lot.qt` existe deja avec le meme lot purchase virtuel, le
|
||||
meme shipment et le meme `lot_s`, la quantite est agregee sur cette ligne
|
||||
plutot que de creer un doublon.
|
||||
- si aucune ligne compatible n'existe, une nouvelle ligne `lot.qt` est creee.
|
||||
- Priorite:
|
||||
- `importante`
|
||||
|
||||
### BR-PT-023 - Lots Management separe matching, side et shipping status
|
||||
|
||||
- Intent: rendre le rapport `lot.report` exploitable sans melanger le statut de
|
||||
matching, le sens achat/vente et l'avancement logistique.
|
||||
- Description:
|
||||
- Le filtre `Matching status` ne porte que le matching commercial:
|
||||
`All`, `Matched`, `Not Matched`.
|
||||
- Le filtre `Side` permet de lire:
|
||||
`All`, `Purchase`, `Sale`.
|
||||
- Le filtre `Shipping status` porte uniquement le lien `shipment_in` du
|
||||
`lot.lot` ou du `lot.qt`.
|
||||
- Les dates de contexte `As of` et `To` filtrent les contrats via
|
||||
`purchase.purchase_date` et `sale.sale_date`.
|
||||
- Si le filtre `Side` vaut `Purchase`, les bornes de dates s'appliquent au
|
||||
`purchase_date`.
|
||||
- Si le filtre `Side` vaut `Sale`, les bornes de dates s'appliquent au
|
||||
`sale_date`.
|
||||
- Si le filtre `Side` vaut `All`, une ligne est conservee si son
|
||||
`purchase_date` ou son `sale_date` entre dans les bornes.
|
||||
- Le filtre `Dimension` du rapport cible une valeur de dimension analytique
|
||||
rattachee au contrat achat ou vente via `analytic.dimension.assignment`.
|
||||
- Le filtre `Strategy` cible les strategies MTM rattachees aux lignes achat
|
||||
ou vente (`purchase.strategy` / `sale.strategy`).
|
||||
- Resultat attendu:
|
||||
- `Unshipped`: aucun `shipment_in` lie au `lot.lot` ou au `lot.qt`.
|
||||
- `Scheduled`: un `shipment_in` est lie et son etat est `draft`.
|
||||
- `Shipped`: un `shipment_in` est lie et son etat est `started`.
|
||||
- `Received`: un `shipment_in` est lie et son etat est `received` ou `done`.
|
||||
- Le rapport affiche une colonne `Shipping status`.
|
||||
- Le rapport affiche `Purchase Delivery Period` depuis la ligne achat et
|
||||
`Sale Delivery Period` depuis la ligne vente; l'ancien champ mixte
|
||||
`Del Period` ne doit plus prendre la periode sale comme fallback achat.
|
||||
- Shipment type:
|
||||
- Sur `stock.shipment.in` et dans `lot.report`, `Shipment Type` vaut
|
||||
`Dropship` si `from_location.type = supplier` et
|
||||
`to_location.type = customer`.
|
||||
- Dans tous les autres cas, `Shipment Type` vaut `Inbound`.
|
||||
- Priorite:
|
||||
- `importante`
|
||||
|
||||
### BR-PT-024 - Create Contracts propage les lieux stock selon le flux miroir
|
||||
|
||||
- Intent: eviter une ressaisie des lieux logistiques quand un contrat miroir
|
||||
est cree depuis une quantite ouverte.
|
||||
- Description:
|
||||
- Les champs concernes sont les `stock.location` `from_location` et
|
||||
`to_location` des contrats achat et vente.
|
||||
- Si le contrat source est en flux direct fournisseur -> client
|
||||
(`from_location.type = supplier` et `to_location.type = customer`), le
|
||||
contrat cree reprend le meme couple `from_location` / `to_location`.
|
||||
- Ce flux correspond au mode `Dropship` affiche sur les shipments.
|
||||
- Si un contrat vente est cree depuis un achat dont `to_location.type =
|
||||
storage`, le `from_location` de la vente est pre-rempli avec ce
|
||||
`to_location` achat.
|
||||
- Si un contrat achat est cree depuis une vente dont `from_location.type =
|
||||
storage`, le `to_location` de l'achat est pre-rempli avec ce
|
||||
`from_location` vente.
|
||||
- Resultat attendu:
|
||||
- achat supplier -> customer vers vente:
|
||||
`sale.from_location = purchase.from_location` et
|
||||
`sale.to_location = purchase.to_location`.
|
||||
- vente supplier -> customer vers achat:
|
||||
`purchase.from_location = sale.from_location` et
|
||||
`purchase.to_location = sale.to_location`.
|
||||
- achat vers stock puis vente:
|
||||
`sale.from_location = purchase.to_location`.
|
||||
- vente depuis stock puis achat:
|
||||
`purchase.to_location = sale.from_location`.
|
||||
- Priorite:
|
||||
- `importante`
|
||||
|
||||
## 4) Exemples concrets
|
||||
|
||||
### Exemple E1 - Augmentation simple
|
||||
@@ -146,6 +825,8 @@ Owner technique: `a completer`
|
||||
- Fichiers Python concernes:
|
||||
- `modules/purchase_trade/purchase.py`
|
||||
- `modules/purchase_trade/lot.py`
|
||||
- `modules/purchase_trade/valuation.py`
|
||||
- `modules/purchase_trade/sale.py`
|
||||
|
||||
## 6) Strategie de tests
|
||||
|
||||
@@ -155,3 +836,91 @@ Pour cette regle, couvrir au minimum:
|
||||
- augmentation sans `lot.qt` ouvert
|
||||
- diminution possible
|
||||
- diminution impossible avec erreur
|
||||
- valuation purchase/sale sur lot physique matche
|
||||
- valuation sale-first sur sale non matchee avec lot virtual
|
||||
- valuation sale `basis` sans `price_summary`
|
||||
- absence de MTM sur les fees
|
||||
- premium en `priced`
|
||||
- premium en `basis`
|
||||
- premium en `linked currency`
|
||||
- synchro `basis` -> `linked_price` -> `unit_price`
|
||||
|
||||
## 7) Notes de fin de session
|
||||
|
||||
### Session 2026-04-30 - PnL fees ouverts et `% rate`
|
||||
|
||||
- Les fees PnL ne doivent pas etre generes pour un lot ouvert / virtuel dont
|
||||
la quantite courante est a zero.
|
||||
- Cette regle evite les lignes PnL residuelles avec `quantity = 0` mais
|
||||
`amount != 0`, notamment pour les fees `rate`, `ppack` et `lumpsum`.
|
||||
- La logique est volontairement proche de `Mark as finished`: quand le reliquat
|
||||
ouvert/virtuel n'est plus valorisable, ses fees ne le sont pas non plus.
|
||||
- Les lots physiques restent hors de ce filtre.
|
||||
- Pour les fees `% rate`, `fin_int_delta` est la periode absolue de calcul.
|
||||
- Le calcul `% rate` ne depend plus de la date du jour, de `fee_date`, ni de
|
||||
`BL date + delta` comme date de fin.
|
||||
- Formule commune purchase/sale:
|
||||
`amount = unit_price * quantity * (price / 100) * fin_int_delta / 360`.
|
||||
- La ligne `Estimated date` avec `trigger = bldate` sert a porter
|
||||
`fin_int_delta`; la date estimee n'entre pas dans le calcul du montant.
|
||||
- Le montant fee affiche reste toujours non signe, meme si `fin_int_delta` est
|
||||
negatif; le signe est applique uniquement en PnL via `PAY` / `REC`.
|
||||
|
||||
### Session 2026-05-09 - Lots Management et Apply matching
|
||||
|
||||
- Le rapport `Lots Management` separe maintenant les filtres:
|
||||
`Matching status`, `Shipping status`, `Side`, `Dimension` et `Strategy`.
|
||||
- Les bornes `As of` / `To` filtrent sur `purchase.purchase_date` et
|
||||
`sale.sale_date` uniquement quand elles sont renseignees; elles ne doivent
|
||||
pas filtrer par defaut a l'ouverture du report.
|
||||
- Le filtre `Dimension` cible une valeur dynamique de dimension analytique via
|
||||
`analytic.dimension.assignment.value`, pour les achats comme pour les ventes.
|
||||
- Le filtre `Strategy` cible les strategies MTM rattachees aux lignes achat ou
|
||||
vente (`purchase.strategy` / `sale.strategy`).
|
||||
- `Apply matching` doit proposer les reliquats ouverts meme si le meme lot
|
||||
purchase ou sale possede deja une autre ligne `lot.qt` matchee.
|
||||
- Une ligne est exclue ou bloquee dans `Apply matching` seulement si la ligne
|
||||
`lot.qt` selectionnee est elle-meme deja matchee:
|
||||
- cote purchase: `lot_p` renseigne et `lot_s` vide
|
||||
- cote sale: `lot_s` renseigne et `lot_p` vide
|
||||
- Exemple: un achat de `1000` avec `800` deja matches et `200` encore ouverts
|
||||
doit laisser le reliquat `200` selectionnable dans `Apply matching`.
|
||||
- Dans `Link to transport`, le type de shipment reste impose a
|
||||
`Shipment In`; l'utilisateur ne doit pas pouvoir basculer vers Out/Internal
|
||||
depuis cette action.
|
||||
- `Mark as finished` dans `Lots Management` masque uniquement les reliquats
|
||||
ouverts / virtuels portes par `lot.qt`.
|
||||
- Le filtre doit regarder les deux cotes presents sur la ligne `lot.qt`:
|
||||
`lot_p.line.finished` cote purchase et `lot_s.sale_line.finished` cote sale,
|
||||
meme quand le filtre `Side` vaut `All`.
|
||||
- Les lots physiques ne doivent pas etre masques par ce flag; ils restent
|
||||
visibles pour conserver l'historique execute.
|
||||
|
||||
### Session 2026-05-17 - Tolerances, jauges et Go to matching
|
||||
|
||||
- `Apply matching` est remplace cote utilisateur par `Go to matching`; l'action
|
||||
legacy est conservee mais masquee.
|
||||
- `Go to matching` precharge les lignes `lot.qt` ouvertes selectionnees depuis
|
||||
`Lots Management`.
|
||||
- Le matching ouvert peut depasser le solde ouvert strict si la quantite
|
||||
projetee reste dans la tolerance de la ligne (`Qt max`).
|
||||
- Les lignes de matching affichent `Qt min`, `Qt max` et une jauge
|
||||
`Tolerance used`.
|
||||
- Dans `Go to matching`, la jauge est dynamique: elle projette
|
||||
`deja matche + Qt to match` contre `quantity_theorical`, avec bornes
|
||||
`-tol_min` / `tol_max`.
|
||||
- Les jauges de `purchase.line` et `sale.line` utilisent:
|
||||
- la somme des lots physiques s'il en existe sur la ligne;
|
||||
- sinon la somme des `lot.qt` rattaches a la ligne.
|
||||
- Les jauges header `purchase.purchase` / `sale.sale` representent la moyenne
|
||||
ponderee des jauges de lignes, au prorata de `quantity_theorical`.
|
||||
- Cote `sale`, les quantites `lot.qt` sont lues en valeur absolue pour les
|
||||
jauges de tolerance.
|
||||
- Le widget SAO `tolerance_gauge` est autorise dans les schemas de vues
|
||||
`form` et `tree` avec `min`, `max`, `min_field`, `max_field`, `center` et
|
||||
`digits`.
|
||||
- En formulaire de ligne (`purchase.line` / `sale.line`), ne pas encapsuler
|
||||
`tolerance_gauge` et `targeted_qt` dans un sous-groupe `colspan="4"`: cela
|
||||
decale visuellement le bloc vers la droite dans SAO. Les placer directement
|
||||
dans la grille principale et forcer `xalign="0"` sur la jauge et sur
|
||||
`targeted_qt` pour garder un alignement a gauche stable.
|
||||
|
||||
99
modules/purchase_trade/docs/business/INDEX.md
Normal file
99
modules/purchase_trade/docs/business/INDEX.md
Normal file
@@ -0,0 +1,99 @@
|
||||
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
|
||||
|
||||
# Index thematique des regles business
|
||||
|
||||
Statut: `migration partielle`
|
||||
|
||||
## Comment chercher une regle
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Contrats, dates, lieux, banques: <a href="contracts.md">contracts.md</a>
|
||||
</li>
|
||||
<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>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Matching, Create Contracts, back-to-back: <a href="matching.md">matching.md</a>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Shipments, controllers, SLA, weight reports: <a href="shipments-execution.md">shipments-execution.md</a>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Pricing manuel, basis, premium, linked currency: <a href="pricing.md">pricing.md</a>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Trading cafe, echantillons, cupping, qualite: <a href="coffee.md">FR</a> / <a href="coffee.en.md">EN</a>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Fees, freight, lots effectifs, quantités, <code>% rate</code>: <a href="fees.md">FR</a> / <a href="fees.en.md">EN</a>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Valuation, PnL, MTM, derivatives: <a href="valuation-pnl-mtm.md">FR</a> / <a href="valuation-pnl-mtm.en.md">EN</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
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;"><code>BR-PT-CON-001</code>: texte par defaut de pricing rule.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>BR-PT-CON-002</code>: delivery period coherent.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>BR-PT-CON-003</code>: lieux stock propages dans Create Contracts.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>BR-PT-LOT-001</code>: cycle de vie des lots et des quantites.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>BR-PT-LOT-002</code>: quantity contractuelle, execute physique et ligne finie.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>BR-PT-LOT-003</code>: garde-fous Python et diagnostics SQL des invariants de quantite.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>BR-PT-MAT-001</code>: Create Contracts multi-lots.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>BR-PT-SHP-001</code>: affectation controller.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>BR-PT-SHP-002</code>: couts SLA controller.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>BR-PT-SHP-003</code>: weight reports distants.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>BR-PT-PRI-001</code>: premium dans priced et basis.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>BR-PT-PRI-002</code>: linked currency.
|
||||
</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-COF-001</code>: echantillons cafe phase 1.
|
||||
</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>: quantité du fee alignée sur les lots effectifs.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>BR-PT-FEE-004</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-VAL-004</code>: snapshot courant PnL et identite economique.
|
||||
</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>
|
||||
137
modules/purchase_trade/docs/business/README.md
Normal file
137
modules/purchase_trade/docs/business/README.md
Normal file
@@ -0,0 +1,137 @@
|
||||
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
|
||||
|
||||
# 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:
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">les consultants, qui ont besoin d'une règle fonctionnelle stable sans détail de code inutile;
|
||||
</li>
|
||||
<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'interpréter.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
## Convention de langues
|
||||
|
||||
Chaque page thématique durable doit exister en deux versions maintenues
|
||||
ensemble:
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<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;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">une page anglaise miroir, portant le même contenu fonctionnel et technique.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
Convention de nommage:
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<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
|
||||
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:
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
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:
|
||||
|
||||
<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
|
||||
|
||||
Pour chaque règle durable, utiliser autant que possible ce format:
|
||||
|
||||
<pre style="background:#263238; color:#eef7ff; padding:1rem; border-radius:0.35rem; overflow:auto;"><code>### 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:</code></pre>
|
||||
|
||||
## 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:
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<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;
|
||||
</li>
|
||||
<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
|
||||
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:
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;"><code>modules/purchase_trade/docs/business-rules.md</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>modules/purchase_trade/docs/fees.md</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>modules/purchase_trade/docs/padding-invoice-accounting.md</code>
|
||||
</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>
|
||||
30
modules/purchase_trade/docs/business/accounting-bridge.md
Normal file
30
modules/purchase_trade/docs/business/accounting-bridge.md
Normal file
@@ -0,0 +1,30 @@
|
||||
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
|
||||
|
||||
# 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
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Cible: <code>account.invoice</code> avec <code>type = out</code>.
|
||||
</li>
|
||||
<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
|
||||
|
||||
Les notes comptables detaillees restent dans `notes/accounting/` tant qu'elles
|
||||
n'ont pas ete promues ici.
|
||||
170
modules/purchase_trade/docs/business/coffee.en.md
Normal file
170
modules/purchase_trade/docs/business/coffee.en.md
Normal file
@@ -0,0 +1,170 @@
|
||||
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
|
||||
|
||||
# Coffee trading - samples and quality
|
||||
|
||||
Mirror page: [coffee.md](coffee.md)
|
||||
|
||||
## BR-PT-COF-001 - Coffee sample phase 1
|
||||
|
||||
Status: active
|
||||
Source: project decision on 2026-06-29
|
||||
|
||||
### Business rule
|
||||
|
||||
Coffee trading must track samples linked to purchase and sale lines.
|
||||
|
||||
Each sample stores:
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">a sample type: offer, pre-shipment, shipment, arrival, stock, customer or retained;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">a purchase or sale direction;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">a lifecycle: requested, received, sent to lab, under review, approved, rejected, expired, archived;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">an expiry date computed from the request/receipt date and the sample shelf life;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">physical quality values and links to cupping sessions.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
Coffee compatibility is disabled by default and must be enabled on
|
||||
`purchase_trade.configuration` with the `Active coffee compatibility` field.
|
||||
While the option is disabled, coffee menus and coffee quality pages on lines
|
||||
must not be visible.
|
||||
|
||||
Cupping results are entered through sessions:
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">cupping properties/criteria are configurable;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">a session defines the room cup capacity and the number of cups per sample;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">each sample added to the session generates several blind-coded cups;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">multiple cuppers can enter a result for each cup;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">cuppers see only the origin and coffee type, not the contract or supplier;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">the preparation order proposes arabicas first, blends/other coffees next and robustas at the end of the session;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">the final decision remains global on the session sample: approved, rejected or re-cup.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
`purchase.line` and `sale.line` store the target coffee quality specifications:
|
||||
origin, process, variety, crop year, screen size, maximum moisture, maximum
|
||||
defect count and minimum cup score.
|
||||
|
||||
### Developer notes
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Models/fields:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;"><code>coffee.sample</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>coffee.cupping.criterion</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>coffee.cupping.session</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>coffee.cupping.session.sample</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>coffee.cupping.cup</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>coffee.cupping.result</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>coffee.cupping.result.line</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>purchase_trade.configuration.active_coffee_compatibility</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>purchase.line.coffee_samples</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>sale.line.coffee_samples</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>coffee_*</code> fields on <code>purchase.line</code> and <code>sale.line</code>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Files:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;"><code>modules/purchase_trade/coffee.py</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>modules/purchase_trade/coffee.xml</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>modules/purchase_trade/view/coffee_sample_*.xml</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>modules/purchase_trade/view/coffee_cupping_*.xml</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>modules/purchase_trade/view/purchase_line_form.xml</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>modules/purchase_trade/view/sale_line_form.xml</code>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Caveats:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">expiry is monitored through the <code>Expired</code> and <code>Expiring soon</code> domains;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">phase 1 does not block shipment or contract validation;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">lab delegation is a tracking field, not a complete lab workflow yet;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">coffee menus are inactive by default and synchronized when the configuration is saved.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
## BR-PT-COF-002 - Phase 2 lab delegation
|
||||
|
||||
Status: active
|
||||
Source: project decision on 2026-06-29
|
||||
|
||||
### Business rule
|
||||
|
||||
A coffee sample can be delegated to a laboratory. The delegation creates and
|
||||
tracks lab analyses linked to the sample:
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">analysis lifecycle: draft, sent, received, accepted, rejected, cancelled;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">request, sent, due and received dates;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">lab report reference;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">physical and organoleptic results: moisture, screen size, defect count and cup score;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">compliance check against the target purchase/sale line thresholds.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
The sample `Send to lab` button creates an active lab request when a lab is set
|
||||
and no open analysis already exists.
|
||||
|
||||
## BR-PT-COF-003 - Phase 3 consolidated quality decision
|
||||
|
||||
Status: active
|
||||
Source: project decision on 2026-06-29
|
||||
|
||||
### Business rule
|
||||
|
||||
The quality decision of a sample is consolidated from:
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">the target line quality thresholds;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">the latest non-cancelled lab analysis;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">the average cupping results;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">cupping session decisions, including <code>re-cup</code> and <code>rejected</code>.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
The sample exposes a quality status (`pending`, `pass`, `warning`, `fail`) and
|
||||
a recommended decision (`approve`, `re-cup`, `reject`). The `Evaluate quality`
|
||||
button applies the recommendation: approval, rejection or review when the
|
||||
sample must be cupped again.
|
||||
|
||||
These features remain under `Active coffee compatibility` and do not add a
|
||||
cross-flow blocking rule for non-coffee flows.
|
||||
171
modules/purchase_trade/docs/business/coffee.md
Normal file
171
modules/purchase_trade/docs/business/coffee.md
Normal file
@@ -0,0 +1,171 @@
|
||||
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
|
||||
|
||||
# Trading cafe - echantillons et qualite
|
||||
|
||||
Page miroir: [coffee.en.md](coffee.en.md)
|
||||
|
||||
## BR-PT-COF-001 - Phase 1 echantillons cafe
|
||||
|
||||
Statut: active
|
||||
Source: decision projet du 2026-06-29
|
||||
|
||||
### Regle consultant
|
||||
|
||||
Le trading cafe doit pouvoir piloter les echantillons rattaches aux lignes
|
||||
d'achat et de vente.
|
||||
|
||||
Chaque echantillon porte:
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">un type d'echantillon: offer, pre-shipment, shipment, arrival, stock, customer ou retained;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">un sens purchase ou sale;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">un cycle de vie: requested, received, sent to lab, under review, approved, rejected, expired, archived;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">une date d'expiration calculee depuis la date de demande/reception et la duree de vie de l'echantillon;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">des donnees de qualite physique et des liens vers les seances de cupping.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
La compatibilite cafe est inactive par defaut et doit etre activee dans la
|
||||
configuration `purchase_trade.configuration` avec le champ `Active coffee
|
||||
compatibility`. Tant que l'option est inactive, les menus cafe et les pages
|
||||
qualite cafe des lignes ne doivent pas etre visibles.
|
||||
|
||||
Les resultats de cupping sont saisis dans des seances:
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">les proprietes/criteres de cupping sont parametrables;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">une seance definit une capacite de salle en nombre de tasses et un nombre de tasses par echantillon;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">chaque echantillon inscrit a la seance genere plusieurs tasses avec un code aveugle;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">plusieurs personnes peuvent saisir un resultat par tasse;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">les degustateurs voient uniquement l'origine et le type de cafe, pas le contrat ni le fournisseur;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">l'ordre de preparation propose les arabicas avant les blends/autres cafes et garde les robustas en fin de seance;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">la decision finale reste globale a l'echantillon de seance: approved, rejected ou re-cup.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
Les lignes `purchase.line` et `sale.line` portent les specifications qualite
|
||||
cibles du cafe: origine, process, variete, crop year, screen size, humidite
|
||||
maximum, nombre de defauts maximum et score cup minimum.
|
||||
|
||||
### Notes developpeur
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Modeles/champs:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;"><code>coffee.sample</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>coffee.cupping.criterion</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>coffee.cupping.session</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>coffee.cupping.session.sample</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>coffee.cupping.cup</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>coffee.cupping.result</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>coffee.cupping.result.line</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>purchase_trade.configuration.active_coffee_compatibility</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>purchase.line.coffee_samples</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>sale.line.coffee_samples</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">champs <code>coffee_*</code> sur <code>purchase.line</code> et <code>sale.line</code>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Fichiers:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;"><code>modules/purchase_trade/coffee.py</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>modules/purchase_trade/coffee.xml</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>modules/purchase_trade/view/coffee_sample_*.xml</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>modules/purchase_trade/view/coffee_cupping_*.xml</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>modules/purchase_trade/view/purchase_line_form.xml</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>modules/purchase_trade/view/sale_line_form.xml</code>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Points de vigilance:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">l'expiration est pilotee par les domaines <code>Expired</code> et <code>Expiring soon</code>;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">aucun blocage shipment/validation n'est introduit en phase 1;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">la delegation labo reste une information de suivi, sans workflow labo complet;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">les menus cafe sont inactifs par defaut et synchronises quand la configuration est sauvegardee.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
## BR-PT-COF-002 - Phase 2 delegation labo
|
||||
|
||||
Statut: active
|
||||
Source: decision projet du 2026-06-29
|
||||
|
||||
### Regle consultant
|
||||
|
||||
Un echantillon cafe peut etre delegue a un laboratoire. La delegation cree et
|
||||
pilote des analyses labo rattachees a l'echantillon:
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">cycle de vie analyse: draft, sent, received, accepted, rejected, cancelled;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">dates de demande, envoi, echeance et reception;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">reference de rapport labo;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">resultats physiques et organoleptiques: humidite, screen size, nombre de defauts, cup score;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">controle de conformite par rapport aux seuils de la ligne purchase/sale.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
Le bouton `Send to lab` de l'echantillon cree une demande labo active si un
|
||||
labo est renseigne et s'il n'existe pas deja une analyse ouverte.
|
||||
|
||||
## BR-PT-COF-003 - Phase 3 decision qualite consolidee
|
||||
|
||||
Statut: active
|
||||
Source: decision projet du 2026-06-29
|
||||
|
||||
### Regle consultant
|
||||
|
||||
La decision qualite d'un echantillon est consolidee depuis:
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">les seuils qualite de la ligne cible;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">la derniere analyse labo non annulee;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">les resultats moyens de cupping;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">les decisions de seance de cupping, dont <code>re-cup</code> et <code>rejected</code>.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
L'echantillon expose un statut qualite (`pending`, `pass`, `warning`, `fail`)
|
||||
et une decision recommandee (`approve`, `re-cup`, `reject`). Le bouton
|
||||
`Evaluate quality` applique la recommandation: approbation, rejet ou retour en
|
||||
revue si l'echantillon doit etre re-deguste.
|
||||
|
||||
Ces fonctions restent sous `Active coffee compatibility` et n'ajoutent pas de
|
||||
blocage transverse sur les flux non cafe.
|
||||
65
modules/purchase_trade/docs/business/contracts.md
Normal file
65
modules/purchase_trade/docs/business/contracts.md
Normal file
@@ -0,0 +1,65 @@
|
||||
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
|
||||
|
||||
# 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
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Configuration: <code>purchase_trade.configuration.pricing_rule</code>.
|
||||
</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
|
||||
|
||||
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
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<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>.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Validation attendue: bloquer si <code>from_del > to_del</code>.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
## 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
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Champs: <code>from_location</code>, <code>to_location</code>.
|
||||
</li>
|
||||
<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>
|
||||
393
modules/purchase_trade/docs/business/fees.en.md
Normal file
393
modules/purchase_trade/docs/business/fees.en.md
Normal file
@@ -0,0 +1,393 @@
|
||||
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
|
||||
|
||||
# Fees
|
||||
|
||||
Language: `en`
|
||||
Mirror page: [fees.md](fees.md)
|
||||
Status: `partial migration`
|
||||
|
||||
See also the historical technical page: `../fees.md`.
|
||||
|
||||
<div style="border-left:0.28rem solid #4051b5; background:#f5f7ff; padding:0.85rem 1rem; margin:1rem 0 1.4rem 0; border-radius:0.35rem;"><strong style="display:block; margin-bottom:0.35rem;">Operational summary</strong><span>A fee carries an amount, but its quantity must remain aligned with the lots it applies to. <code>Per packing</code> is the only exception: it follows packing units, not lot weight.</span></div>
|
||||
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Marker</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Topic</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Short rule</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Quantity</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>fee.quantity</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Sum of linked lots in <code>fee.lots</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">State</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">empty <code>fee.qt_state</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Uses the contract <code>weight basis</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">State</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">filled <code>fee.qt_state</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Uses that state, capped by the <code>weight basis</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Fallback</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">state missing on lot</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Uses the closest previous state by <code>sequence</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Net / gross</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>fee.weight_type</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>net</code> reads <code>quantity</code>, <code>brut</code> reads <code>gross_quantity</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Packing</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>ppack</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Packing quantity, decoupled from weight.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Control</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Python + SQL</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Application guard and SQL diagnostic.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## Consultant Rules
|
||||
|
||||
### BR-PT-FEE-001 - Freight value from shipment fee
|
||||
|
||||
Source: `BR-PT-003`
|
||||
|
||||
#### Consultant Rule
|
||||
|
||||
The freight value printed on invoice documents comes from the maritime freight
|
||||
fee on the shipment, not from a direct invoice field.
|
||||
|
||||
#### Developer Notes
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Find the physical lot from the invoice.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Find its <code>shipment_in</code>.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Search the <code>fee.fee</code> with <code>product.name = 'Maritime freight'</code>.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Use <code>fee.get_amount()</code>.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
### BR-PT-FEE-002 - Effective lots for fees
|
||||
|
||||
Source: `BR-PT-021`
|
||||
|
||||
#### Consultant Rule
|
||||
|
||||
A fee follows the virtual lot until a physical lot is linked. As soon as a
|
||||
physical lot is linked, physical lots become the calculation basis for the fee.
|
||||
|
||||
#### Developer Notes
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Do not remove the virtual lot link: it remains the fallback.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Effective lots are:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">physical lots if at least one physical lot is linked;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">otherwise virtual lots.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">The same selection applies to fee PnL.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Synchronization points:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">fee creation;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>fee.lots</code> link;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>quantity_theorical</code> change;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">weighing;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">physical lot removal.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
### BR-PT-FEE-003 - Fee quantity
|
||||
|
||||
#### Consultant Rule
|
||||
|
||||
The quantity of a fee follows the quantity life cycle of its linked lots. It
|
||||
must represent the sum of those lots in the authorized contractual quantity
|
||||
state.
|
||||
|
||||
#### Short Rule
|
||||
|
||||
<pre style="background:#263238; color:#eef7ff; padding:1rem; border-radius:0.35rem; overflow:auto;"><code>fee.quantity = sum(applicable quantity of effective fee.lots)</code></pre>
|
||||
|
||||
#### Quantity State Selection
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Case</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Target state</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">empty <code>fee.qt_state</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>weight basis</code> of the fee carrier contract</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">filled <code>fee.qt_state</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>fee.qt_state</code>, unless later than the <code>weight basis</code></td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>fee.qt_state</code> later than <code>weight basis</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>weight basis</code></td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">target state missing on lot</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">closest previous state by <code>lot.qt.type.sequence</code></td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">no target state available</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">current lot weight, only when no <code>weight basis</code> applies</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
#### Weight Basis Source
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Fee</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;"><code>weight basis</code></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Purchase fee</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>fee.line.purchase.wb.qt_type</code></td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Sale fee</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>fee.sale_line.sale.wb.qt_type</code></td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Shipment fee</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">purchase weight basis of the lot when present</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Shipment fee without purchase</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">sale weight basis of the lot when present</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
#### Net / Gross
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">If <code>fee.weight_type = net</code>:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">read <code>lot.qt.hist.quantity</code>.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">If <code>fee.weight_type = brut</code>:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">read <code>lot.qt.hist.gross_quantity</code>.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
#### Per Packing
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;"><code>mode = ppack</code> is excluded from the weight rule.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>fee.quantity</code> represents a packing quantity.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">This quantity may be decoupled from net or gross weight.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
#### Lump Sum
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;"><code>mode = lumpsum</code> also follows the quantity rule.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">The amount remains fixed.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">The quantity gives a more accurate per-ton price.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
### BR-PT-FEE-004 - `% rate` fees from financing delta
|
||||
|
||||
Source: historical `BR-PT-016` and `2026-04-30` notes
|
||||
|
||||
#### Consultant Rule
|
||||
|
||||
Percentage financial fees are calculated with the financing delta from the
|
||||
`BL date` estimated line, not with the current date.
|
||||
|
||||
#### Developer Notes
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Formula: <code>amount = unit_price * quantity * (price / 100) * fin_int_delta / 360</code>.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Delta source: <code>Estimated date</code> line with <code>trigger = bldate</code>.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">If no <code>bldate</code> line exists, do not calculate a <code>% rate</code> amount.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
## Developer Section
|
||||
|
||||
### Key Fields
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Fee: <code>fee.fee</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Fee lots: <code>fee.lots</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Fee quantity: <code>fee.fee.quantity</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Mode: <code>fee.fee.mode</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Optional quantity state: <code>fee.fee.qt_state</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Net / gross: <code>fee.fee.weight_type</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Lot quantity state: <code>lot.qt.hist.quantity_type</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Net quantity: <code>lot.qt.hist.quantity</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Gross quantity: <code>lot.qt.hist.gross_quantity</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">State order: <code>lot.qt.type.sequence</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Purchase / sale weight basis: <code>purchase.weight.basis.qt_type</code>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
### Calculation Functions
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;"><code>Fee._get_effective_fee_lots()</code>:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">selects physical lots, then virtual lots.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>Fee._target_qt_type_for_lot()</code>:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">chooses <code>fee.qt_state</code> or the <code>weight basis</code>;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">caps the state at the <code>weight basis</code>.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>Fee._select_lot_qt_type()</code>:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">takes the exact state when it exists;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">otherwise takes the closest previous state by <code>sequence</code>.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>Fee._get_lot_fee_quantity()</code>:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">reads net or gross depending on <code>weight_type</code>.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>Fee.sync_quantity_from_lots()</code>:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">resynchronizes <code>fee.quantity</code>.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
### Python Guards
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Central check:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;"><code>fee.fee.assert_quantity_consistency()</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>fee.fee.assert_quantities_consistency()</code>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Trigger points:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">fee creation;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">fee modification;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>fee.lots</code> creation / modification / deletion;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">weighing through linked fee resynchronization.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Exception:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;"><code>mode = ppack</code> is not controlled as weight.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
### Known Gap
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Lot split / merge:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">the workflow may clone or create physical lots;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">rewiring <code>fee.lots</code> to the new lots is not guaranteed yet;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">affected fees must therefore be checked with the SQL diagnostic after this workflow is used.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
### SQL Diagnostic
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Script:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;"><a href="sql/fee_quantity_consistency_checks.sql">sql/fee_quantity_consistency_checks.sql</a>
|
||||
</li>
|
||||
</ul>
|
||||
</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 test databases;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">audit historical data;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">qualify data before correction.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">The script returns non-<code>ppack</code> fees where <code>fee.quantity</code> does not match the sum of effective lots according to the applicable quantity state.
|
||||
</li>
|
||||
</ul>
|
||||
393
modules/purchase_trade/docs/business/fees.md
Normal file
393
modules/purchase_trade/docs/business/fees.md
Normal file
@@ -0,0 +1,393 @@
|
||||
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
|
||||
|
||||
# Fees
|
||||
|
||||
Langue : `fr`
|
||||
Page miroir : [fees.en.md](fees.en.md)
|
||||
Statut : `migration partielle`
|
||||
|
||||
Voir aussi la page technique historique : `../fees.md`.
|
||||
|
||||
<div style="border-left:0.28rem solid #4051b5; background:#f5f7ff; padding:0.85rem 1rem; margin:1rem 0 1.4rem 0; border-radius:0.35rem;"><strong style="display:block; margin-bottom:0.35rem;">Résumé opérationnel</strong><span>Un fee porte un montant, mais sa quantité doit rester alignée avec les lots qui le composent. Le mode <code>Per packing</code> est la seule exception : il suit une quantité de colisage, pas le poids du lot.</span></div>
|
||||
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Repère</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Sujet</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Règle courte</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Quantité</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>fee.quantity</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Somme des lots liés dans <code>fee.lots</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">État</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>fee.qt_state</code> vide</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Utilise le <code>weight basis</code> du contrat.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">État</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>fee.qt_state</code> renseigné</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Utilise cet état, plafonné au <code>weight basis</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Fallback</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">état absent sur le lot</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Prend l'état antérieur le plus proche par <code>sequence</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Net / brut</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>fee.weight_type</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>net</code> lit <code>quantity</code>, <code>brut</code> lit <code>gross_quantity</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Packing</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>ppack</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Quantité de packing, décorrélée du poids.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Contrôle</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Python + SQL</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Check applicatif et diagnostic SQL.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## Règles consultant
|
||||
|
||||
### BR-PT-FEE-001 - Freight value depuis fee shipment
|
||||
|
||||
Source : `BR-PT-003`
|
||||
|
||||
#### Règle consultant
|
||||
|
||||
La valeur de fret affichée sur les documents facture vient du fee maritime du
|
||||
shipment, pas d'un champ direct de la facture.
|
||||
|
||||
#### Notes développeur
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Retrouver le lot physique depuis la facture.
|
||||
</li>
|
||||
<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 = 'Maritime freight'</code>.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Utiliser <code>fee.get_amount()</code>.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
### BR-PT-FEE-002 - Lots effectifs des fees
|
||||
|
||||
Source : `BR-PT-021`
|
||||
|
||||
#### Règle consultant
|
||||
|
||||
Un fee suit le lot virtuel tant qu'aucun lot physique n'est lié. Dès qu'un lot
|
||||
physique est lié, les lots physiques deviennent la base de calcul du fee.
|
||||
|
||||
#### Notes développeur
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Ne pas supprimer le lien virtuel : il reste le fallback.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Les lots effectifs sont :
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">les physiques si au moins un physique est lié ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">sinon les virtuels.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">La même sélection s'applique au PnL fee.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Points de synchronisation :
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">création fee ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">lien <code>fee.lots</code> ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">changement de <code>quantity_theorical</code> ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">weighing ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">suppression de physique.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
### BR-PT-FEE-003 - Quantité du fee
|
||||
|
||||
#### Règle consultant
|
||||
|
||||
La quantité d'un fee suit la vie de la quantité des lots qui lui sont liés. Elle
|
||||
doit représenter la somme des quantités de ces lots dans l'état contractuel
|
||||
autorisé.
|
||||
|
||||
#### Règle courte
|
||||
|
||||
<pre style="background:#263238; color:#eef7ff; padding:1rem; border-radius:0.35rem; overflow:auto;"><code>fee.quantity = somme(quantité applicable des lots effectifs fee.lots)</code></pre>
|
||||
|
||||
#### Sélection de l'état de quantité
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Cas</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">État cible</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>fee.qt_state</code> vide</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>weight basis</code> du contrat porteur du fee</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>fee.qt_state</code> renseigné</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>fee.qt_state</code>, sauf s'il est postérieur au <code>weight basis</code></td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>fee.qt_state</code> postérieur au <code>weight basis</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>weight basis</code></td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">état cible absent du lot</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">état antérieur le plus proche par <code>lot.qt.type.sequence</code></td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">aucun état cible disponible</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">poids courant du lot, seulement si aucun <code>weight basis</code> ne s'applique</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
#### Source du `weight basis`
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Fee</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;"><code>weight basis</code></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Fee achat</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>fee.line.purchase.wb.qt_type</code></td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Fee vente</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>fee.sale_line.sale.wb.qt_type</code></td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Fee shipment</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">weight basis achat du lot si présent</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Fee shipment sans achat</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">weight basis vente du lot si présent</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
#### Net / brut
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Si <code>fee.weight_type = net</code> :
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">lire <code>lot.qt.hist.quantity</code>.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Si <code>fee.weight_type = brut</code> :
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">lire <code>lot.qt.hist.gross_quantity</code>.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
#### Per packing
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;"><code>mode = ppack</code> est exclu de la règle poids.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>fee.quantity</code> représente une quantité de packing.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Cette quantité peut être décorrélée du poids net ou brut.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
#### Lump sum
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;"><code>mode = lumpsum</code> suit aussi la règle de quantité.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Le montant reste forfaitaire.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">La quantité permet de calculer un prix par tonne plus précis.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
### BR-PT-FEE-004 - Fees `% rate` via delta de financement
|
||||
|
||||
Source : `BR-PT-016` historique et notes `2026-04-30`
|
||||
|
||||
#### Règle 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 développeur
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Formule : <code>amount = unit_price * quantity * (price / 100) * fin_int_delta / 360</code>.
|
||||
</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'existe, ne pas calculer de montant <code>% rate</code>.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
## Section développeur
|
||||
|
||||
### Champs clés
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Fee : <code>fee.fee</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Lots du fee : <code>fee.lots</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Quantité du fee : <code>fee.fee.quantity</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Mode : <code>fee.fee.mode</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">État de quantité optionnel : <code>fee.fee.qt_state</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Net / brut : <code>fee.fee.weight_type</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">État de quantité lot : <code>lot.qt.hist.quantity_type</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Quantité nette : <code>lot.qt.hist.quantity</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Quantité brute : <code>lot.qt.hist.gross_quantity</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Ordre des états : <code>lot.qt.type.sequence</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Weight basis achat / vente : <code>purchase.weight.basis.qt_type</code>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
### Fonctions de calcul
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;"><code>Fee._get_effective_fee_lots()</code> :
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">sélectionne physiques puis virtuels.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>Fee._target_qt_type_for_lot()</code> :
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">choisit <code>fee.qt_state</code> ou le <code>weight basis</code> ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">plafonne l'état au <code>weight basis</code>.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>Fee._select_lot_qt_type()</code> :
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">prend l'état exact s'il existe ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">sinon prend l'état antérieur le plus proche par <code>sequence</code>.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>Fee._get_lot_fee_quantity()</code> :
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">lit net ou brut selon <code>weight_type</code>.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>Fee.sync_quantity_from_lots()</code> :
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">resynchronise <code>fee.quantity</code>.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
### Garde-fous Python
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Check central :
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;"><code>fee.fee.assert_quantity_consistency()</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>fee.fee.assert_quantities_consistency()</code>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Points de déclenchement :
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">création de fee ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">modification de fee ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">création / modification / suppression de <code>fee.lots</code> ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">weighing via resynchronisation des fees liés.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Exception :
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;"><code>mode = ppack</code> n'est pas contrôlé comme un poids.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
### Gap connu
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Split / merge de lots :
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">le flux peut cloner ou créer des lots physiques ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">le recâblage de <code>fee.lots</code> vers les nouveaux lots n'est pas encore garanti ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">les fees concernés doivent donc être contrôlés par le diagnostic SQL après utilisation de ce flux.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
### Diagnostic SQL
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Script :
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;"><a href="sql/fee_quantity_consistency_checks.sql">sql/fee_quantity_consistency_checks.sql</a>
|
||||
</li>
|
||||
</ul>
|
||||
</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 remonte les fees non <code>ppack</code> dont <code>fee.quantity</code> ne correspond pas à la somme des lots effectifs selon l'état de quantité applicable.
|
||||
</li>
|
||||
</ul>
|
||||
42
modules/purchase_trade/docs/business/glossary.md
Normal file
42
modules/purchase_trade/docs/business/glossary.md
Normal file
@@ -0,0 +1,42 @@
|
||||
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
|
||||
|
||||
# Glossaire purchase_trade
|
||||
|
||||
Statut: `migration partielle`
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;"><code>Purchase Line</code>: ligne d'achat.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>Sale Line</code>: ligne de vente.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>quantity_theorical</code>: quantite contractuelle theorique d'une ligne.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>Virtual Lot</code>: lot de type <code>virtual</code>, representant un reliquat ouvert.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>Physical Lot</code>: lot de type <code>physic</code>, representant une quantite executee.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>lot.qt</code>: ligne de quantite ouverte, matchee ou rattachee a un shipment.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>lot.qt ouvert</code>: <code>lot.qt</code> libre, sans lot oppose et sans shipment.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>Shipment In</code>: shipment entrant utilise aussi pour les flux dropship dans ce module.
|
||||
</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'un prix de marche et d'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'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>
|
||||
93
modules/purchase_trade/docs/business/invariants.md
Normal file
93
modules/purchase_trade/docs/business/invariants.md
Normal file
@@ -0,0 +1,93 @@
|
||||
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
|
||||
|
||||
# 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
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Source historique: <code>BR-PT-002</code>.
|
||||
</li>
|
||||
<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>.
|
||||
</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
|
||||
|
||||
### 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
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Source historique: <code>BR-PT-020</code>.
|
||||
</li>
|
||||
<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.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Regle de conservation: <code>sum(lots physiques) + lot virtuel = quantity_theorical</code>.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Regle du forecast ouvert minimal: <code>sum(lot.qt non zero) >= max(lot virtuel, 0)</code>.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Un surplus de forecast ouvert est accepte apres surconsommation physique: <code>lot.qt</code> s'arrete a zero, alors que le lot virtuel absorbe toute la quantite physique.
|
||||
</li>
|
||||
<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'une prevision consommee.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Le check applicatif est centralise dans <code>lot.lot.assert_lines_quantity_consistency()</code>.
|
||||
</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
|
||||
|
||||
### 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
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Source historique: <code>BR-PT-021</code>.
|
||||
</li>
|
||||
<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
|
||||
|
||||
### Regle consultant
|
||||
|
||||
Les documents doivent afficher des informations metier stables, meme si le
|
||||
chemin technique pour les retrouver est complexe.
|
||||
|
||||
### Notes developpeur
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Preferer des proprietes Python <code>report_*</code> aux expressions Genshi complexes.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Ne pas supposer qu'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>
|
||||
29
modules/purchase_trade/docs/business/invoicing.md
Normal file
29
modules/purchase_trade/docs/business/invoicing.md
Normal file
@@ -0,0 +1,29 @@
|
||||
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
|
||||
|
||||
# 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
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Le padding global du wizard <code>lot.invoice</code> est reparti entre les lots selectionnes.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">La ligne facture expose <code>Inc. padding</code>.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Le lot conserve sa part dans <code>sale_invoice_padding</code>.
|
||||
</li>
|
||||
<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'extourne doivent relire la provisoire depuis <code>lot.sale_invoice_line_prov</code>.
|
||||
</li>
|
||||
</ul>
|
||||
762
modules/purchase_trade/docs/business/lots-and-quantities.en.md
Normal file
762
modules/purchase_trade/docs/business/lots-and-quantities.en.md
Normal file
@@ -0,0 +1,762 @@
|
||||
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
|
||||
|
||||
# Lots and Quantities
|
||||
|
||||
Language: `en`
|
||||
Mirror page: [lots-and-quantities.md](lots-and-quantities.md)
|
||||
Status: `partial migration`
|
||||
Last code verification: `2026-05-13`
|
||||
|
||||
This page consolidates `BR-PT-LOT-001`, `BR-PT-LOT-002`, and
|
||||
`BR-PT-LOT-003`.
|
||||
Goal: drive quantity rules from a readable business definition, then secure
|
||||
them with Python guards and SQL diagnostics.
|
||||
|
||||
## Key Points
|
||||
|
||||
<div style="border-left:0.28rem solid #4051b5; background:#f5f7ff; padding:0.85rem 1rem; margin:1rem 0 1.4rem 0; border-radius:0.35rem;"><strong style="display:block; margin-bottom:0.35rem;">Operational Summary</strong><span>A trade line has exactly one virtual lot. The virtual lot carries the global open balance, <code>lot.qt</code> carries the operational forecast, and physical lots consume that forecast.</span></div>
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Topic</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Short Rule</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Entered quantity</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>quantity_theorical</code> is the business quantity.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Standard quantity</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>quantity</code> is a read-only technical counter.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Before physical lots</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>quantity</code> follows <code>quantity_theorical</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">After physical lots</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>quantity</code> reflects physical execution.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Open line</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Line amount uses <code>quantity_theorical</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Finished line</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Line amount may switch back to physical execution.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Weight basis</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Purchase and sale may read two different states of the same lot.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Invoicing</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">It chooses states from <code>lot.qt.hist</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Controls</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Invariants are blocked in Python and auditable with SQL.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<pre style="background:#263238; color:#eef7ff; padding:1rem; border-radius:0.35rem; overflow:auto;"><code>quantity_theorical
|
||||
|
|
||||
v
|
||||
virtual lot P1 ---> lot.qt forecast ---> physical lot
|
||||
^ |
|
||||
| v
|
||||
+------ recalculation after consumption</code></pre>
|
||||
|
||||
## Consultant Rules
|
||||
|
||||
### BR-PT-LOT-001 - Virtual lot / forecast / physical life cycle
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Moment</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Business Effect</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Line creation</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">One unique virtual lot is created.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Initialization</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">The virtual lot takes <code>quantity_theorical</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Forecast</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">One open line is created in <code>lot.qt</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Planning</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>lot.qt</code> may be split by sale, matching, transport, or shipment.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Physical add</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">The physical lot consumes a precise <code>lot.qt</code> line.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">After add</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>lot.qt</code> decreases and the virtual lot is recalculated.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div style="border-left:0.28rem solid #4051b5; background:#f5f7ff; padding:0.85rem 1rem; margin:1rem 0 1.4rem 0; border-radius:0.35rem;"><strong style="display:block; margin-bottom:0.35rem;">Split of an open P1 balance</strong><span><code>P1S1T1</code>, <code>P1S1T2</code>, <code>P1S2T3</code>, <code>P1S2T4</code></span></div>
|
||||
|
||||
<div style="border-left:0.28rem solid #4051b5; background:#f5f7ff; padding:0.85rem 1rem; margin:1rem 0 1.4rem 0; border-radius:0.35rem;"><strong style="display:block; margin-bottom:0.35rem;">Key Point</strong><span>These splits do not create several virtual lots. They only describe the forecast allocation of the open balance.</span></div>
|
||||
|
||||
### BR-PT-LOT-002 - Contractual quantity, counter quantity, finished line
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Situation</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Reference Quantity</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">User entry</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>quantity_theorical</code></td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">No physical lot</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>quantity = quantity_theorical</code></td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Physical lots exist</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>quantity = sum of physical lots</code></td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>finished = False</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Amount based on <code>quantity_theorical</code></td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>finished = True</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Amount based on physical execution</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Weight basis available</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Amount based on the contract <code>wb.qt_type</code> state</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div style="border-left:0.28rem solid #4051b5; background:#f5f7ff; padding:0.85rem 1rem; margin:1rem 0 1.4rem 0; border-radius:0.35rem;"><strong style="display:block; margin-bottom:0.35rem;">What <code>finished</code> Does Not Do</strong><span><code>finished</code> does not erase the contractual quantity, delete physical lots, or hide their PnL. It only means that the open balance may be ignored for execution calculations.</span></div>
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Reading of the Same Physical Lot</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Possible State</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Purchase</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">BL through <code>purchase.purchase.wb.qt_type</code></td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Sale</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">LR or Weight Report through <code>sale.sale.wb.qt_type</code></td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Invoicing</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Independent choice in <code>lot.qt.hist</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
### BR-PT-LOT-003 - Quantity Invariants
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Invariant</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Formula</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Conservation</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>sum(physical lots) + virtual lot = quantity_theorical</code></td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Minimum open forecast</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>sum(non-zero lot.qt) >= max(virtual lot, 0)</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Case</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Rule</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Purchase virtual lot</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Sum all <code>lot.qt</code> where <code>lot_p = virtual lot</code>, with or without <code>lot_s</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Sale virtual lot</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Sum all <code>lot.qt</code> where <code>lot_s = virtual lot</code>, with or without <code>lot_p</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>lot.qt = 0</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Ignored by checks; possible memory of a consumed forecast.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Forecast overconsumption</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Allowed after tolerance confirmation: <code>lot.qt</code> stops at zero, but the virtual lot absorbs the whole physical lot.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>lot.qt</code> greater than virtual lot</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Accepted when remaining open forecast exceeds the positive virtual lot because of physical overconsumption.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Negative virtual lot</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Allowed to compensate theoretical / executed gap; expected forecast = zero.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Non-zero orphan <code>lot.qt</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Forbidden if neither <code>lot_p</code> nor <code>lot_s</code> is set.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
### BR-PT-LOT-004 - Quantity history and weighing
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Element</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Rule</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>lot.qt.hist</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Carries lot quantity states.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>lot.lot</code> form</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">No direct state entry.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Update</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Only through <code>Do weighing</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Virtual lot</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">No manual packing.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Virtual packing fields</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>lot_qt</code> and <code>lot_unit</code> are not editable.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
### Tolerances
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Point</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Rule</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Level</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Tolerance is defined on the <code>purchase</code> / <code>sale</code> header.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Inheritance</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Lines inherit the header tolerance by default.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Transport</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">No independent tolerance per transport.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Physical lot addition</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">The check is performed during <code>Add physical lots</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Matched forecast</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">If <code>lot_qt</code> carries both <code>lot_p</code> and <code>lot_s</code>, the check applies to both purchase and sale sides.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Line excess</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Confirmable warning in English when the projected physical quantity exceeds the current line tolerance. The message names the blocking side: <code>purchase</code>, <code>sale</code>, or both if both checks are triggered successively.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Global envelope</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">A line excess is accepted after confirmation and consumes the contract-level envelope.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Remaining tolerance</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Contract lines are recalculated with <code>inherit_tol = False</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Over-execution</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">A line exceeding the header tolerance keeps a <code>+</code> tolerance at least equal to its actual excess.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Other lines</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Their <code>+</code> tolerance is reduced according to the remaining envelope.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Under-execution</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Mechanically restores tolerance available to other lines on the next recalculation.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Open matching</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>Go to matching</code> may match above the strict open balance if the projected quantity remains within <code>Qt max</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Line gauge</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">On a line with physical lots, the gauge uses the sum of physical lots; without physical lots, it uses the sum of linked <code>lot.qt</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Header gauge</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">The purchase/sale gauge is the weighted average of line gauges, weighted by <code>quantity_theorical</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Matching gauge</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">In <code>Go to matching</code>, the gauge projects <code>already matched + Qt to match</code> against <code>quantity_theorical</code>, with <code>-tol_min</code> / <code>tol_max</code> bounds.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Line gauge layout</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">On <code>purchase.line</code> / <code>sale.line</code> forms, place the gauge and <code>targeted_qt</code> directly in the main grid, without a <code>colspan="4"</code> subgroup, and use <code>xalign="0"</code> to avoid a double right shift.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## Developer Section
|
||||
|
||||
### Key Fields
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Purchase line: <code>purchase.line</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Sale line: <code>sale.line</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Lot: <code>lot.lot</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Forecast: <code>lot.qt</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">History: <code>lot.qt.hist</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Purchase business quantity: <code>purchase.line.quantity_theorical</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Sale business quantity: <code>sale.line.quantity_theorical</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Technical counter: <code>quantity</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Finished line: <code>purchase.line.finished</code>, <code>sale.line.finished</code>
|
||||
</li>
|
||||
<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>, <code>tolerance_used</code>, <code>tolerance_min</code>, <code>tolerance_max</code>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
### Line / Virtual Lot Creation
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Purchase: <code>purchase.py</code>, <code>Line.validate</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Sale: <code>sale.py</code>, <code>SaleLine.validate</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">If <code>quantity_theorical</code> is entered and <code>quantity</code> is empty or zero:
|
||||
<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> is initialized from <code>quantity_theorical</code>;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">only if no physical lot exists.
|
||||
</li>
|
||||
</ul>
|
||||
</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>
|
||||
<li style="margin:0.38rem 0;">If the line is <code>created_by_code</code>:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">it comes from a business workflow (<code>Create contracts</code>, matching, etc.);
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>Lot.validate</code> does not create an automatic open <code>lot.qt</code>;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">the workflow attaches or splits its source <code>lot.qt</code>;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">the blocking check applies only after the workflow is stable.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
### Updating `quantity_theorical`
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Purchase: <code>purchase.py</code>, <code>Line.write</code>
|
||||
</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>
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">If <code>target_quantity < 0</code>:
|
||||
<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>
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">If <code>free_quantity < 0</code>:
|
||||
<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;">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 > 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
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Wizard: <code>lot.add</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Methods:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;"><code>LotQt.add_physical_lots</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>LotQt.add_physical_lot</code>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Mandatory source: one <code>lot.qt</code> line.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Direct add from a physical lot is refused.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Physical add on sale side through this wizard is refused: use <code>Apply matching</code>.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">The physical lot inherits:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">purchase line;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">matched sale, if any;
|
||||
</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
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Wizard: <code>lot.remove</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Open lot: removal forbidden.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Lot with <code>stock.move</code>:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">move must be <code>draft</code>.
|
||||
</li>
|
||||
</ul>
|
||||
</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
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Wizard: <code>lot.weighing</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">UI action: <code>Do weighing</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Writes or updates <code>lot.qt.hist</code>.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">May update <code>lot_state</code>.
|
||||
</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`
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Method: <code>Lot._recalc_line_quantity</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Without 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> 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
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Purchase: <code>purchase.line.on_change_with_amount()</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Sale: <code>sale.line.on_change_with_amount()</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Helpers:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;"><code>_get_amount_quantity()</code>
|
||||
</li>
|
||||
<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
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Central check:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;"><code>lot.lot.assert_lines_quantity_consistency()</code>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Trigger rule:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">block inconsistent final states;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">do not block internal transient workflow states;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">use <code>Lot.skip_quantity_consistency()</code> only around a sequence that restores the invariants afterwards;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">call an explicit final check after the sequence.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Non-zero orphan <code>lot.qt</code> block:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<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;">matched <code>Create contracts</code>;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">weighing.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">The <code>created_by_code</code> case is intentionally excluded from the immediate <code>Lot.validate</code> check:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">the virtual lot is saved before the matched <code>lot.qt</code> is attached;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">the invariant is checked by the final workflow guard.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
### SQL Diagnostic
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Script:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;"><a href="sql/quantity_consistency_checks.sql">sql/quantity_consistency_checks.sql</a>
|
||||
</li>
|
||||
</ul>
|
||||
</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
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;"><code>modules/purchase_trade/tests/test_module.py</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Existing coverage:
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">readonly <code>quantity</code>;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">initialization from <code>quantity_theorical</code>;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">protection when physical lots exist;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">amount on theoretical / physical / Weight basis;
|
||||
</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>
|
||||
761
modules/purchase_trade/docs/business/lots-and-quantities.md
Normal file
761
modules/purchase_trade/docs/business/lots-and-quantities.md
Normal file
@@ -0,0 +1,761 @@
|
||||
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
|
||||
|
||||
# Lots et quantités
|
||||
|
||||
Langue : `fr`
|
||||
Page miroir : [lots-and-quantities.en.md](lots-and-quantities.en.md)
|
||||
Statut : `migration partielle`
|
||||
Dernière vérification code : `2026-05-13`
|
||||
|
||||
Cette page consolide `BR-PT-LOT-001`, `BR-PT-LOT-002` et `BR-PT-LOT-003`.
|
||||
Objectif : piloter les règles de quantité depuis une définition métier lisible,
|
||||
puis les sécuriser par des checks Python et des diagnostics SQL.
|
||||
|
||||
## À retenir
|
||||
|
||||
<div style="border-left:0.28rem solid #4051b5; background:#f5f7ff; padding:0.85rem 1rem; margin:1rem 0 1.4rem 0; border-radius:0.35rem;"><strong style="display:block; margin-bottom:0.35rem;">Résumé opérationnel</strong><span>Une ligne trade possède un seul lot virtuel. Le lot virtuel porte le solde ouvert global, <code>lot.qt</code> porte le forecast opérationnel, et les lots physiques consomment ce forecast.</span></div>
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Sujet</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Règle courte</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Quantité saisie</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>quantity_theorical</code> est la quantité métier.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Quantité standard</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>quantity</code> est un compteur technique non éditable.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Avant physique</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>quantity</code> suit <code>quantity_theorical</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Après physique</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>quantity</code> reflète l'exécuté physique.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Ligne non finie</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Le montant de ligne utilise <code>quantity_theorical</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Ligne finie</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Le montant peut revenir à l'exécuté physique.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Weight basis</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Achat et vente peuvent lire deux états différents du même lot.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Facturation</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Elle choisit ses états dans <code>lot.qt.hist</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Contrôles</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Invariants bloqués en Python et auditables en SQL.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<pre style="background:#263238; color:#eef7ff; padding:1rem; border-radius:0.35rem; overflow:auto;"><code>quantity_theorical
|
||||
|
|
||||
v
|
||||
lot virtuel P1 ---> lot.qt forecast ---> lot physique
|
||||
^ |
|
||||
| v
|
||||
+------ recalcul après consommation</code></pre>
|
||||
|
||||
## Règles consultant
|
||||
|
||||
### BR-PT-LOT-001 - Cycle de vie lot virtuel / forecast / physique
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Moment</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Effet métier</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Création de ligne</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Création d'un lot virtuel unique.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Initialisation</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Le lot virtuel reprend <code>quantity_theorical</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Forecast</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Une ligne ouverte est créée dans <code>lot.qt</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Planification</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>lot.qt</code> peut être subdivisé par vente, matching, transport ou shipment.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Ajout physique</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Le lot physique consomme une ligne <code>lot.qt</code> précise.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Après ajout</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>lot.qt</code> diminue et le lot virtuel est recalculé.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div style="border-left:0.28rem solid #4051b5; background:#f5f7ff; padding:0.85rem 1rem; margin:1rem 0 1.4rem 0; border-radius:0.35rem;"><strong style="display:block; margin-bottom:0.35rem;">Découpage d'un solde P1</strong><span><code>P1S1T1</code>, <code>P1S1T2</code>, <code>P1S2T3</code>, <code>P1S2T4</code></span></div>
|
||||
|
||||
<div style="border-left:0.28rem solid #4051b5; background:#f5f7ff; padding:0.85rem 1rem; margin:1rem 0 1.4rem 0; border-radius:0.35rem;"><strong style="display:block; margin-bottom:0.35rem;">Point clé</strong><span>Ces découpages ne créent pas plusieurs lots virtuels. Ils décrivent seulement la répartition prévisionnelle du solde ouvert.</span></div>
|
||||
|
||||
### BR-PT-LOT-002 - Quantité contractuelle, compteur, ligne finie
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Situation</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Quantité de référence</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Saisie utilisateur</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>quantity_theorical</code></td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Aucun lot physique</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>quantity = quantity_theorical</code></td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Lots physiques présents</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>quantity = somme des lots physiques</code></td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>finished = False</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Montant basé sur <code>quantity_theorical</code></td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>finished = True</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Montant basé sur l'exécuté physique</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Weight basis disponible</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Montant basé sur l'état <code>wb.qt_type</code> du contrat</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div style="border-left:0.28rem solid #4051b5; background:#f5f7ff; padding:0.85rem 1rem; margin:1rem 0 1.4rem 0; border-radius:0.35rem;"><strong style="display:block; margin-bottom:0.35rem;">Ce que <code>finished</code> ne fait pas</strong><span><code>finished</code> n'efface pas la quantité contractuelle, ne supprime pas les lots physiques et ne masque pas leur PnL. Il signifie seulement que le reliquat ouvert peut être ignoré pour les calculs d'exécution.</span></div>
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Lecture du même lot physique</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">État possible</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Achat</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">BL via <code>purchase.purchase.wb.qt_type</code></td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Vente</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">LR ou Weight Report via <code>sale.sale.wb.qt_type</code></td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Facturation</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Choix indépendant dans <code>lot.qt.hist</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
### BR-PT-LOT-003 - Invariants de quantité
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Invariant</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Formule</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Conservation</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>somme(lots physiques) + lot virtuel = quantity_theorical</code></td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Forecast ouvert minimal</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>somme(lot.qt non zéro) >= max(lot virtuel, 0)</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Cas</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Règle</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Lot virtuel achat</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Sommer tous les <code>lot.qt</code> où <code>lot_p = lot virtuel</code>, avec ou sans <code>lot_s</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Lot virtuel vente</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Sommer tous les <code>lot.qt</code> où <code>lot_s = lot virtuel</code>, avec ou sans <code>lot_p</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>lot.qt = 0</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Ignoré par les checks ; mémoire possible d'une prévision vidée.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Surconsommation forecast</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Autorisée après confirmation de tolérance : <code>lot.qt</code> s'arrête à zéro, mais le lot virtuel absorbe tout le physique.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>lot.qt</code> supérieur au virtuel</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Accepté si le forecast ouvert restant dépasse le virtuel positif à cause d'une surconsommation physique.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Lot virtuel négatif</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Autorisé pour compenser l'écart théorique / exécuté ; forecast attendu = zéro.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>lot.qt</code> non zéro orphelin</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Interdit si ni <code>lot_p</code> ni <code>lot_s</code> n'est renseigné.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
### BR-PT-LOT-004 - Historique de quantité et weighing
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Élément</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Règle</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>lot.qt.hist</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Porte les états de quantité d'un lot.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Fiche <code>lot.lot</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Pas de saisie directe des états.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Modification</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Uniquement via <code>Do weighing</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Lot virtuel</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Pas de packing manuel.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Champs packing virtuel</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>lot_qt</code> et <code>lot_unit</code> non éditables.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
### Tolérances
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Point</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Règle</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Niveau</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">La tolérance est définie au header <code>purchase</code> / <code>sale</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Héritage</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Les lignes héritent de la tolérance header par défaut.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Transport</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Pas de tolérance indépendante par transport.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Ajout physique</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Le contrôle se fait au moment de <code>Add physical lots</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Forecast matché</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Si <code>lot_qt</code> porte <code>lot_p</code> et <code>lot_s</code>, le contrôle porte sur les deux côtés achat et vente.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Dépassement ligne</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Warning confirmable en anglais si la quantité physique projetée dépasse la tolérance courante de la ligne. Le message indique le côté bloquant : <code>purchase</code>, <code>sale</code>, ou les deux si les deux checks déclenchent successivement.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Enveloppe globale</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Le dépassement ponctuel d'une ligne est accepté après confirmation et consomme l'enveloppe globale du contrat.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Tolérance restante</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Les lignes du contrat sont recalculées avec <code>inherit_tol = False</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Surconsommation</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Une ligne qui dépasse la tolérance header garde une tolérance <code>+</code> au moins égale à son dépassement réel.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Autres lignes</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Leur tolérance <code>+</code> est réduite selon l'enveloppe restante.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Sous-consommation</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Restitue mécaniquement de la tolérance disponible aux autres lignes lors du recalcul suivant.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Matching ouvert</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>Go to matching</code> peut matcher au-delà du solde ouvert strict si la quantité projetée reste dans <code>Qt max</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Jauge ligne</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Sur une ligne avec lots physiques, la jauge utilise la somme des physiques ; sans physique, elle utilise la somme des <code>lot.qt</code> liés.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Jauge header</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">La jauge purchase/sale est la moyenne pondérée des jauges de lignes au prorata de <code>quantity_theorical</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Jauge matching</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Dans <code>Go to matching</code>, la jauge projette <code>déjà matché + Qt to match</code> contre <code>quantity_theorical</code>, avec bornes <code>-tol_min</code> / <code>tol_max</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Layout jauge ligne</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">En formulaire <code>purchase.line</code> / <code>sale.line</code>, placer la jauge et <code>targeted_qt</code> directement dans la grille principale, sans sous-groupe <code>colspan="4"</code>, et utiliser <code>xalign="0"</code> pour éviter un double décalage vers la droite.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## Section développeur
|
||||
|
||||
### Champs clés
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Ligne achat : <code>purchase.line</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Ligne vente : <code>sale.line</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Lot : <code>lot.lot</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Forecast : <code>lot.qt</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Historique : <code>lot.qt.hist</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Quantité métier achat : <code>purchase.line.quantity_theorical</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Quantité métier vente : <code>sale.line.quantity_theorical</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Compteur technique : <code>quantity</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Ligne finie : <code>purchase.line.finished</code>, <code>sale.line.finished</code>
|
||||
</li>
|
||||
<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>, <code>tolerance_used</code>, <code>tolerance_min</code>, <code>tolerance_max</code>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
### Création ligne / lot virtuel
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Achat : <code>purchase.py</code>, <code>Line.validate</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Vente : <code>sale.py</code>, <code>SaleLine.validate</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Si <code>quantity_theorical</code> est saisi et que <code>quantity</code> est vide ou zéro :
|
||||
<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> est initialisée depuis <code>quantity_theorical</code> ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">seulement si aucun lot physique n'existe.
|
||||
</li>
|
||||
</ul>
|
||||
</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'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>
|
||||
<li style="margin:0.38rem 0;">Si la ligne est <code>created_by_code</code> :
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">elle provient d'un workflow métier (<code>Create contracts</code>, matching, etc.) ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>Lot.validate</code> ne crée pas de <code>lot.qt</code> ouvert automatique ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">le workflow rattache ou subdivise son <code>lot.qt</code> source ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">le check bloquant s'applique seulement après stabilisation du workflow.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
### Modification de `quantity_theorical`
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Achat : <code>purchase.py</code>, <code>Line.write</code>
|
||||
</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>
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Si <code>target_quantity < 0</code> :
|
||||
<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>
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Si <code>free_quantity < 0</code> :
|
||||
<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;">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'existe et <code>free_quantity > 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'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
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Wizard : <code>lot.add</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Méthodes :
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;"><code>LotQt.add_physical_lots</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>LotQt.add_physical_lot</code>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Source obligatoire : une ligne <code>lot.qt</code>.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Ajout direct depuis un lot physique refusé.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Ajout physique côté vente par ce wizard refusé : utiliser <code>Apply matching</code>.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Le lot physique reprend :
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">ligne achat ;
|
||||
</li>
|
||||
<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
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Wizard : <code>lot.remove</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Lot ouvert : retrait interdit.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Lot avec <code>stock.move</code> :
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">move obligatoire en <code>draft</code>.
|
||||
</li>
|
||||
</ul>
|
||||
</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é
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Wizard : <code>lot.weighing</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Action UI : <code>Do weighing</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Écrit ou met à jour <code>lot.qt.hist</code>.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Peut mettre à jour <code>lot_state</code>.
|
||||
</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`
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Méthode : <code>Lot._recalc_line_quantity</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Sans physique :
|
||||
<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> 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
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Achat : <code>purchase.line.on_change_with_amount()</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Vente : <code>sale.line.on_change_with_amount()</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Helper :
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;"><code>_get_amount_quantity()</code>
|
||||
</li>
|
||||
<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
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Check central :
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;"><code>lot.lot.assert_lines_quantity_consistency()</code>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Règle de déclenchement :
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">bloquer les états finaux incohérents ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">ne pas bloquer les états transitoires internes d'un workflow ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">utiliser <code>Lot.skip_quantity_consistency()</code> uniquement autour d'une séquence qui rétablit ensuite les invariants ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">appeler un check final explicite après la séquence.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Blocage <code>lot.qt</code> orphelin non zéro :
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<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;"><code>Create contracts</code> en mode matched ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">weighing.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Le cas <code>created_by_code</code> est volontairement exclu du check immédiat <code>Lot.validate</code> :
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">le lot virtuel est sauvegardé avant que le <code>lot.qt</code> matched soit rattaché ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">l'invariant est contrôlé par le check final du workflow.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
### Diagnostic SQL
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Script :
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;"><a href="sql/quantity_consistency_checks.sql">sql/quantity_consistency_checks.sql</a>
|
||||
</li>
|
||||
</ul>
|
||||
</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
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;"><code>modules/purchase_trade/tests/test_module.py</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Couverture existante :
|
||||
<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> readonly ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">initialisation depuis <code>quantity_theorical</code> ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">protection si lots physiques ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">amount sur théorique / physique / Weight basis ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">resynchronisation des lots virtuels ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">blocages quand l'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>
|
||||
35
modules/purchase_trade/docs/business/lots-management.md
Normal file
35
modules/purchase_trade/docs/business/lots-management.md
Normal file
@@ -0,0 +1,35 @@
|
||||
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
|
||||
|
||||
# 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
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Filtres: <code>Matching status</code>, <code>Side</code>, <code>Shipping status</code>, <code>Dimension</code>, <code>Strategy</code>.
|
||||
</li>
|
||||
<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>.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>Unshipped</code>: aucun <code>shipment_in</code>.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>Scheduled</code>: shipment <code>draft</code>.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>Shipped</code>: shipment <code>started</code>.
|
||||
</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>
|
||||
31
modules/purchase_trade/docs/business/matching.md
Normal file
31
modules/purchase_trade/docs/business/matching.md
Normal file
@@ -0,0 +1,31 @@
|
||||
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
|
||||
|
||||
# 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
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">La quantite du wizard doit correspondre a la somme des quantites ouvertes selectionnees.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Creer une ligne par <code>lot.qt</code> source.
|
||||
</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
|
||||
|
||||
Les regles sur `Apply matching` presentes dans les notes de session du
|
||||
`2026-05-09` doivent encore etre promues ici.
|
||||
27
modules/purchase_trade/docs/business/payments-banking.md
Normal file
27
modules/purchase_trade/docs/business/payments-banking.md
Normal file
@@ -0,0 +1,27 @@
|
||||
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
|
||||
|
||||
# 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
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Contrats: <code>sale.sale</code>, <code>purchase.purchase</code>.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>bank_account</code>: compte de la party du contrat.
|
||||
</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>
|
||||
488
modules/purchase_trade/docs/business/pricing.md
Normal file
488
modules/purchase_trade/docs/business/pricing.md
Normal file
@@ -0,0 +1,488 @@
|
||||
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
|
||||
|
||||
# Pricing, basis et fixing
|
||||
|
||||
Langue : `fr`
|
||||
Statut : `migration partielle`
|
||||
Derniere verification code : `2026-05-25`
|
||||
|
||||
Cette page consolide les regles de pricing trade autour des composants, des
|
||||
periodes de prix, des periodes d'application, du fixing manuel et du prix final
|
||||
des lignes achat / vente.
|
||||
|
||||
## A retenir
|
||||
|
||||
<div style="border-left:0.28rem solid #4051b5; background:#f5f7ff; padding:0.85rem 1rem; margin:1rem 0 1.4rem 0; border-radius:0.35rem;"><strong style="display:block; margin-bottom:0.35rem;">Resume operationnel</strong><span>Une ligne trade peut etre en prix fixe, basis ou EFP. En basis, le prix de base vient d'un ou plusieurs composants de fixing. Chaque composant lit une courbe de prix, construit des dates de prix, applique ces prix sur des dates d'application, puis alimente le prix de la ligne via un summary.</span></div>
|
||||
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Sujet</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Regle courte</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Composant</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Un <code>pricing.component</code> appartient a une ligne achat ou vente.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Courbe</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">La courbe principale est <code>price_index</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Matrix</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Une matrix peut alimenter un composant de fixing, avec ligne generique ou conditions.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Pricing period</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Definit les dates ou lire les prix sur la courbe.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Application period</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Definit les dates ou appliquer les tranches de fixing.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">AVG</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Utilise la moyenne progressive des prix lus.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">LAST</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Autorise la courbe a reprendre le dernier prix connu si la date n'a pas de prix.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Pricing rows</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Les lignes <code>pricing.pricing</code> portent quantite fixee, non fixee et prix EOD.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Summary</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Le summary remonte la derniere ligne de pricing du groupe.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Prix ligne</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">En <code>basis</code>, <code>unit_price</code> est le prix base hors premium.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<pre style="background:#263238; color:#eef7ff; padding:1rem; border-radius:0.35rem; overflow:auto;"><code>pricing.component
|
||||
|
|
||||
v
|
||||
pricing.trigger
|
||||
|
|
||||
+--> pricing period --> dates de lecture curve
|
||||
|
|
||||
+--> application period --> dates de fixing
|
||||
|
|
||||
v
|
||||
pricing.pricing
|
||||
|
|
||||
v
|
||||
purchase/sale pricing summary
|
||||
|
|
||||
v
|
||||
line.unit_price</code></pre>
|
||||
|
||||
## Regles consultant
|
||||
|
||||
### BR-PT-PRI-001 - Le premium fait partie du prix economique
|
||||
|
||||
Source: `BR-PT-008`
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Point</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Regle</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Prix visible</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>unit_price</code> reste le prix de base hors premium.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Prix economique</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Le montant utilise <code>unit_price + premium</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Basis</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Le premium s'ajoute aussi quand le prix base vient du fixing.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Documents commerciaux</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Une ligne <code>basis</code> peut afficher le premium comme prix visible selon le contexte document.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Valuation</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Le premium doit etre pris en compte dans les lignes valorisees.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div style="border-left:0.28rem solid #4051b5; background:#f5f7ff; padding:0.85rem 1rem; margin:1rem 0 1.4rem 0; border-radius:0.35rem;"><strong style="display:block; margin-bottom:0.35rem;">Point cle</strong><span>Le premium n'est pas un composant de fixing. Il reste porte par la ligne commerciale, mais il fait partie du prix economique utilise pour les montants.</span></div>
|
||||
|
||||
|
||||
### BR-PT-PRI-002 - Linked currency
|
||||
|
||||
Source: `BR-PT-009` et `BR-PT-010`
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Point</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Regle</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Activation</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>linked_price</code>, <code>linked_currency</code> et <code>linked_unit</code> deviennent obligatoires.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Saisie</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Le prix et le premium peuvent etre saisis dans le repere lie.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Conversion</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Les calculs internes convertissent vers la devise / unite de la ligne.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Basis</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">En <code>basis + linked currency</code>, <code>linked_price</code> represente le basis brut hors premium.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Amount</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Le montant ajoute le premium converti.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
### BR-PT-PRI-003 - Pricing manuel
|
||||
|
||||
Source: `BR-PT-016` et doublon historique `BR-PT-015`
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Colonne</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Role</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>pricing_date</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Date de saisie du fixing.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>quantity</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Quantite fixee ce jour.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>settl_price</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Prix de marche saisi.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>fixed_qt</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Cumul des quantites fixees.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>fixed_qt_price</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Prix moyen pondere du cumul fixe.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>unfixed_qt</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Solde restant a fixer.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>unfixed_qt_price</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Dernier prix disponible de la courbe du composant pour le solde non fixe, avec fallback sur <code>settl_price</code> si aucune courbe exploitable n'est disponible.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>eod_price</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Prix economique courant de la ligne de fixing.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>last</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Derniere ligne du groupe, utilisee par le summary.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div style="border-left:0.28rem solid #4051b5; background:#f5f7ff; padding:0.85rem 1rem; margin:1rem 0 1.4rem 0; border-radius:0.35rem;"><strong style="display:block; margin-bottom:0.35rem;">Saisie utilisateur</strong><span>En manuel, l'utilisateur renseigne seulement <code>quantity</code> et <code>settl_price</code>. Les cumuls, le prix moyen, le solde non fixe et <code>last</code> sont recalcules par le systeme.</span></div>
|
||||
|
||||
|
||||
### BR-PT-PRI-004 - Pricing automatique par composant
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Etape</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Effet metier</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Composant</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">L'utilisateur choisit une courbe, un calendrier et active <code>Auto</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Source curve</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Le systeme lit les prix depuis <code>price_index</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Source matrix</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Le systeme lit les prix depuis <code>price_matrix.line</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Trigger</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">L'utilisateur definit une periode de prix et une periode d'application.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Lecture prix</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Le systeme lit les prix de la courbe sur les dates de pricing.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Application</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Le systeme cree une ligne de pricing par date d'application.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Quota</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">La quantite fixee par date correspond a la quantite de ligne divisee par le nombre de dates d'application.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Moyenne</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Si <code>AVG</code> est coche, le prix fixe utilise la moyenne progressive.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Fallback prix</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Si <code>LAST</code> est coche, la courbe peut reprendre le dernier prix disponible.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Cut-off</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>Pricing date max</code> arrete la generation apres cette date.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Ratio negatif</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Un ratio <code>-100%</code> soustrait le composant du prix basis.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<pre style="background:#263238; color:#eef7ff; padding:1rem; border-radius:0.35rem; overflow:auto;"><code>Prix lus: P1 P2 P3 P4
|
||||
Application: A1 A2 A3 A4
|
||||
Quantite fixee: Q/4 Q/4 Q/4 Q/4
|
||||
Summary: derniere ligne marquee last</code></pre>
|
||||
|
||||
### BR-PT-PRI-005 - Mode From / To manuel
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Cas</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Regle</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Pas de <code>pricing_period</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">L'utilisateur peut saisir <code>From</code> / <code>To</code> cote pricing.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Pas de <code>application_period</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">L'utilisateur peut saisir <code>From</code> / <code>To</code> cote application.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Application vide</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Par defaut, l'application reprend la meme fourchette que le pricing manuel.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Calendrier present</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Les jours fermes du calendrier sont exclus.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Calendrier absent</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Tous les jours calendaires de la fourchette sont utilises.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Lecture curve</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Les prix sont lus sur les dates de la fourchette pricing.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Application fixing</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Les lignes de fixing sont creees sur les dates de la fourchette application.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div style="border-left:0.28rem solid #4051b5; background:#f5f7ff; padding:0.85rem 1rem; margin:1rem 0 1.4rem 0; border-radius:0.35rem;"><strong style="display:block; margin-bottom:0.35rem;">Exemple simple</strong><span>Pricing <code>01/04 -> 05/04</code>, Application vide. Le systeme lit les prix du 1 au 5 avril et cree les applications sur ces memes dates, sauf jours exclus par le calendrier si un calendrier est renseigne.</span></div>
|
||||
|
||||
|
||||
### BR-PT-PRI-006 - Matrix de premium ou spread
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Cas</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Regle</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Matrix generique</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Une ligne matrix sans origin, destination, product ni quality s'applique comme fallback global.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Matrix conditionnelle</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Une ligne avec origin, destination, product ou quality est prioritaire si elle correspond a la ligne trade.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Prix</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>price_value</code> est converti vers l'unite / devise de la ligne si la matrix les renseigne.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Validite</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>valid_from</code> et <code>valid_to</code> limitent la periode d'utilisation de la matrix.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Calendrier</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Si le composant n'a pas de calendrier, le calendrier de la matrix est utilise.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Soustraction</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Pour soustraire un premium, mettre le ratio du composant a <code>-100%</code>.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div style="border-left:0.28rem solid #4051b5; background:#f5f7ff; padding:0.85rem 1rem; margin:1rem 0 1.4rem 0; border-radius:0.35rem;"><strong style="display:block; margin-bottom:0.35rem;">Usage premium simple</strong><span>Creer une matrix avec une ligne sans condition et <code>price_value = 45</code>, puis ajouter un composant de type <code>Matrix</code> sur la ligne trade. Avec un ratio <code>-100%</code>, ce composant diminue le prix basis de 45 dans le repere de la ligne.</span></div>
|
||||
|
||||
|
||||
## Section developpeur
|
||||
|
||||
### Modeles pivots
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Composant : <code>pricing.component</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Trigger : <code>pricing.trigger</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Periode relative : <code>pricing.period</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Ligne de fixing : <code>pricing.pricing</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Summary achat : <code>purchase.pricing.summary</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Summary vente : <code>sale.pricing.summary</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Ligne achat : <code>purchase.line</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Ligne vente : <code>sale.line</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Courbe : <code>price.price</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Valeur de courbe : <code>price.price_value</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Calendrier : <code>price.calendar</code>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
### Methodes principales
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;"><code>pricing.trigger.getPricingListDates()</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>pricing.trigger.getApplicationListDates()</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>pricing.trigger.getListDates()</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>pricing.trigger.getprice()</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>purchase.line.check_pricing()</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>sale.line.check_pricing()</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>purchase.line.generate_pricing()</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>sale.line.generate_pricing()</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>pricing.pricing._sync_manual_values()</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>pricing.pricing._sync_manual_last()</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>purchase.pricing.summary.table_query()</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>sale.pricing.summary.table_query()</code>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
### Points d'attention actuels
|
||||
|
||||
<table style="width:100%; border-collapse:collapse; margin:1rem 0 1.5rem 0; font-size:0.95rem;">
|
||||
<thead>
|
||||
<tr style="background:#eef3ff;">
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Sujet</th>
|
||||
<th style="text-align:left; padding:0.55rem 0.7rem; border-bottom:2px solid #4051b5;">Etat</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>fallback</code> composant</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Champ present, logique metier encore portee par <code>trigger.last</code>.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>last</code> trigger</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Signifie dernier prix disponible sur la curve.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>last</code> pricing</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Signifie derniere ligne du groupe pour le summary.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Matrix</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Disponible dans le fixing auto, avec fallback generique et priorite aux lignes conditionnelles.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>prDate</code> / <code>apPrice</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Variables intermediaires historiques peu exploitees.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">AVG</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Moyenne progressive calculee sur la liste des prix lus.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## Tests proches
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;"><code>modules/purchase_trade/tests/test_module.py</code>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Couverture existante :
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">champs manuels readonly / editable ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">recalcul du pricing manuel ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">unicite logique du <code>last</code> par groupe ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">fallback <code>quantity</code> si <code>quantity_theorical</code> est vide ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">controle que le composant appartient a la ligne courante.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Tests a maintenir :
|
||||
<ul style="margin:0.65rem 0 1rem 1.35rem; padding-left:1rem; list-style-type:circle;">
|
||||
<li style="margin:0.38rem 0;">From / To manuel sans calendrier ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">From / To manuel avec calendrier ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">application vide qui reprend la fourchette pricing ;
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">comportement AVG / LAST.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
55
modules/purchase_trade/docs/business/reports-templates.md
Normal file
55
modules/purchase_trade/docs/business/reports-templates.md
Normal file
@@ -0,0 +1,55 @@
|
||||
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
|
||||
|
||||
# Reports et templates
|
||||
|
||||
Statut: `migration partielle`
|
||||
|
||||
Voir aussi:
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;"><code>../template-rules.md</code>
|
||||
</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
|
||||
|
||||
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
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;">Preferer des proprietes Python simples, souvent prefixees <code>report_*</code>.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Dans les placeholders XML, utiliser <code>&quot;</code> et <code>&apos;</code> plutot que des antislashs.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Pour les factures liees a vente/achat/shipment, privilegier le lot physique comme pont.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;">Verifier le cache <code>invoice_report_cache</code> avant de conclure qu'une action report pointe vers le mauvais <code>.fodt</code>.
|
||||
</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'une variable locale supposee.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
## Decisions deja documentees a migrer ensuite
|
||||
|
||||
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
||||
<li style="margin:0.38rem 0;"><code>insurance.fodt</code>: compagnie courante, amount insured a 110%, surveyor.
|
||||
</li>
|
||||
<li style="margin:0.38rem 0;"><code>packing_list.fodt</code>: date du jour, unites depuis <code>purchase.line</code>.
|
||||
</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>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user