Compare commits
671 Commits
7f2e61c059
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 0ac2e8514a | |||
| 3bb2811e76 | |||
| 2bcbf169cf | |||
| a007ed2895 | |||
| 792733c93b | |||
| 321eae8d91 | |||
| 7d7bd33b97 | |||
| 7fcc90d0a4 | |||
| d81a8750d3 | |||
| 94d0f2f828 | |||
| d76a9a8dd6 | |||
| 705a1706c3 | |||
| 275008c160 | |||
| 1d7a058d68 | |||
| baf1641e1e | |||
| 058bbc7612 | |||
| de1156e5bc | |||
| 3235a1ba57 | |||
| 27d2a81b31 | |||
| e5b986efc5 | |||
| 7dcb68ab69 | |||
| 03c294ad8c | |||
| aaa1490389 | |||
|
|
3994154cad | ||
| 979bbec013 | |||
| d0bafebbbe | |||
| 9b9daf5467 | |||
| 4f9d46cd4a | |||
| 07ba685c0c | |||
| 89d35b5499 | |||
| dfbd8db2b8 | |||
| c5633bdeb8 | |||
| a181e9cfaa | |||
| 1ba95fd636 | |||
| 34dd8eaf54 | |||
| a1bb0e8a04 | |||
| c62cee33af | |||
| f8edbed1eb | |||
| 5596fed28b | |||
| 0ffd43149b | |||
| 9429f7d2e3 | |||
| 33d742ffce | |||
| 2afa454a03 | |||
| 439e9002aa | |||
| ada4c445c8 | |||
| b1f97ade55 | |||
| df4570aff8 | |||
| e284f193fb | |||
| 3f14cd6323 | |||
| fa6198296b | |||
| 94e60fef6c | |||
| b282fe9bc0 | |||
| ffd673f9ef | |||
| 8088fd5254 | |||
| 42d02cc6f8 | |||
| af344b6fc7 | |||
| 264ff534cf | |||
| 64390eaecb | |||
| 99763f10f2 | |||
| 9fa91f1255 | |||
| 6b29088c31 | |||
| 5515bfa15b | |||
| e767f4543e | |||
| f05046b8c9 | |||
| 28a9a334c3 | |||
| da93139bcc | |||
| 12d9b046d2 | |||
| 914fecf3a0 | |||
| 94bbed79f0 | |||
| 0f8e620b0d | |||
| 5a5a0699f2 | |||
| 8118d3190a | |||
| 493dd54606 | |||
| ab80f8d909 | |||
| 2fe13c1e76 | |||
| a70c2c4853 | |||
| d6fa273d11 | |||
| 816d1fcaec | |||
| 8973f8a6d0 | |||
| 572f6ef902 | |||
| bd72f8cef3 | |||
| a94f36e26f | |||
| 669320c976 | |||
| facd627f5e | |||
| b2bd777ec8 | |||
| 71751a96c0 | |||
| c655427be5 | |||
| 0272fc70bc | |||
| 39afd9dc32 | |||
| 0e30f9ac30 | |||
| 8be68db6db | |||
| aba25d62dc | |||
| 4c85f80412 | |||
| a0bd6f0e28 | |||
| cda8b4067b | |||
| 728e2c7d60 | |||
| 4652a1a94a | |||
| b53d7a216d | |||
| 0e004027df | |||
| df8439f3f5 | |||
| cb35de91ba | |||
| 59171ec07b | |||
| 9b0dd0e7c1 | |||
| cf0c0923a6 | |||
| bdf13f4fac | |||
| e184da898f | |||
|
|
b3e8dc9c33 | ||
|
|
fab46dde2f | ||
| 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 |
@@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"permissions": {
|
|
||||||
"allow": [
|
|
||||||
"Bash(cd /c/Users/SylvainDUVERNAY/Documents/Visual Studio Code/Tradon DEV/tradon/modules)",
|
|
||||||
"Bash(ls -d */)",
|
|
||||||
"Bash(cd /c/Users/SylvainDUVERNAY/Documents/Visual Studio Code/Tradon DEV/tradon/modules/purchase_trade)",
|
|
||||||
"Bash(ls -1d */)",
|
|
||||||
"Bash(for f:*)",
|
|
||||||
"Bash(do echo:*)",
|
|
||||||
"Read(//c/Users/SylvainDUVERNAY/Documents/Visual Studio Code/Tradon DEV/tradon/**)",
|
|
||||||
"Bash(done)",
|
|
||||||
"Bash(cd /c/Users/SylvainDUVERNAY/Documents/Visual Studio Code/Tradon DEV/tradon/modules/purchase_trade/view)",
|
|
||||||
"Bash(ls -1 *.xml)",
|
|
||||||
"Bash(py --version)"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
deployment/vps-TradonDev_Instructions.md
|
*.pyc
|
||||||
deployment/vps/46.202.173.47-credentials.md
|
~$*
|
||||||
|
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`.
|
- Lire `wsgi.py`, `rpc.py`, `protocols/*`, `tests/test_rpc.py`, `tests/test_wsgi.py`.
|
||||||
- Si bug metier:
|
- Si bug metier:
|
||||||
- Modifier uniquement `modules/<module>/` + ses tests.
|
- 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`):
|
- Si bug template Relatorio (`.fodt`):
|
||||||
- Lire d'abord le template standard voisin du meme domaine (`invoice.fodt`, `sale.fodt`, etc.).
|
- 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.
|
- 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 `\'`.
|
- 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.
|
- 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`.
|
- 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.
|
- 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.
|
- 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()`.
|
- 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)
|
## 5) Workflow de modification (obligatoire)
|
||||||
|
|
||||||
@@ -105,3 +145,18 @@ Toujours fournir:
|
|||||||
3. Proposer le patch minimal.
|
3. Proposer le patch minimal.
|
||||||
4. Implementer + tester cible.
|
4. Implementer + tester cible.
|
||||||
5. Rendre avec le contrat de sortie (section 8).
|
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)
|
||||||
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`
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
Serveur: 'VPS-62.72.36.116'
|
|
||||||
Alias Name: 'VPS DEV'
|
|
||||||
IP Address:'62.72.36.116'
|
|
||||||
170
ir/model.py
170
ir/model.py
@@ -1538,6 +1538,10 @@ class Log(ResourceAccessMixin, ModelSQL, ModelView):
|
|||||||
})
|
})
|
||||||
action = fields.Function(
|
action = fields.Function(
|
||||||
fields.Char("Action"), 'get_action', searcher='search_action')
|
fields.Char("Action"), 'get_action', searcher='search_action')
|
||||||
|
old_values = fields.Text("Old Values", readonly=True)
|
||||||
|
new_values = fields.Text("New Values", readonly=True)
|
||||||
|
change_summary = fields.Function(
|
||||||
|
fields.Text("Changes"), 'get_change_summary')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def __setup__(cls):
|
def __setup__(cls):
|
||||||
@@ -1583,6 +1587,172 @@ class Log(ResourceAccessMixin, ModelSQL, ModelView):
|
|||||||
return f'{field_name} : {state}'
|
return f'{field_name} : {state}'
|
||||||
return self.target
|
return self.target
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _load_change_values(value):
|
||||||
|
if not value:
|
||||||
|
return {}
|
||||||
|
try:
|
||||||
|
return json.loads(value)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _format_change_value(value):
|
||||||
|
if isinstance(value, list):
|
||||||
|
if len(value) > 6:
|
||||||
|
return '[%s items]' % len(value)
|
||||||
|
return ', '.join(Log._format_change_value(v) for v in value)
|
||||||
|
if isinstance(value, dict):
|
||||||
|
return json.dumps(value, sort_keys=True, ensure_ascii=False)
|
||||||
|
if value in (None, ''):
|
||||||
|
return ''
|
||||||
|
return str(value)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _singular_label(label):
|
||||||
|
if label.endswith('ies'):
|
||||||
|
return label[:-3] + 'y'
|
||||||
|
if label.endswith('s'):
|
||||||
|
return label[:-1]
|
||||||
|
return label
|
||||||
|
|
||||||
|
def _nested_record_label(self, field, field_label, record_id):
|
||||||
|
record_label = self._singular_label(field_label)
|
||||||
|
if not self.resource:
|
||||||
|
return '%s %s' % (record_label, record_id)
|
||||||
|
records = list(getattr(self.resource, field.name, None) or [])
|
||||||
|
line_records = [
|
||||||
|
record for record in records
|
||||||
|
if getattr(record, 'type', 'line') == 'line']
|
||||||
|
ordered_records = line_records or records
|
||||||
|
for index, record in enumerate(ordered_records, 1):
|
||||||
|
if str(getattr(record, 'id', '')) == str(record_id):
|
||||||
|
return '%s %s' % (record_label, index)
|
||||||
|
return '%s %s' % (record_label, record_id)
|
||||||
|
|
||||||
|
def _format_nested_change_summary(
|
||||||
|
self, field, field_label, target_model, old_value, new_value):
|
||||||
|
pool = Pool()
|
||||||
|
Field = pool.get('ir.model.field')
|
||||||
|
lines = []
|
||||||
|
if not isinstance(old_value, dict) or not isinstance(new_value, dict):
|
||||||
|
return lines
|
||||||
|
def record_sort_key(value):
|
||||||
|
try:
|
||||||
|
return int(value)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return value
|
||||||
|
for record_id in sorted(
|
||||||
|
set(old_value) | set(new_value), key=record_sort_key):
|
||||||
|
record_old = old_value.get(record_id, {})
|
||||||
|
record_new = new_value.get(record_id, {})
|
||||||
|
if not isinstance(record_old, dict) or not isinstance(
|
||||||
|
record_new, dict):
|
||||||
|
continue
|
||||||
|
for child_field in sorted(set(record_old) | set(record_new)):
|
||||||
|
child_old = record_old.get(child_field)
|
||||||
|
child_new = record_new.get(child_field)
|
||||||
|
if child_old == child_new:
|
||||||
|
continue
|
||||||
|
child_label = Field.get_name(target_model, child_field)
|
||||||
|
lines.append('%s %s %s => %s' % (
|
||||||
|
self._nested_record_label(
|
||||||
|
field, field_label, record_id),
|
||||||
|
child_label,
|
||||||
|
self._format_change_value(child_old),
|
||||||
|
self._format_change_value(child_new)))
|
||||||
|
return lines
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _iter_change_commands(value):
|
||||||
|
if not isinstance(value, list):
|
||||||
|
return
|
||||||
|
for item in value:
|
||||||
|
if isinstance(item, list) and item and isinstance(item[0], str):
|
||||||
|
command = item[0]
|
||||||
|
if len(item) >= 3:
|
||||||
|
yield command, item[1], item[2]
|
||||||
|
elif len(item) >= 2:
|
||||||
|
yield command, item[1], None
|
||||||
|
if value and all(isinstance(item, list) for item in value):
|
||||||
|
return
|
||||||
|
index = 0
|
||||||
|
while index < len(value):
|
||||||
|
command = value[index]
|
||||||
|
if not isinstance(command, str):
|
||||||
|
index += 1
|
||||||
|
continue
|
||||||
|
if command == 'write':
|
||||||
|
if index + 2 >= len(value):
|
||||||
|
break
|
||||||
|
yield command, value[index + 1], value[index + 2]
|
||||||
|
index += 3
|
||||||
|
continue
|
||||||
|
if index + 1 >= len(value):
|
||||||
|
break
|
||||||
|
yield command, value[index + 1], None
|
||||||
|
index += 2
|
||||||
|
|
||||||
|
def _format_command_change_summary(
|
||||||
|
self, field, field_label, target_model, new_value):
|
||||||
|
pool = Pool()
|
||||||
|
Field = pool.get('ir.model.field')
|
||||||
|
lines = []
|
||||||
|
for command, record_ids, values in self._iter_change_commands(
|
||||||
|
new_value) or []:
|
||||||
|
if command != 'write' or not isinstance(values, dict):
|
||||||
|
continue
|
||||||
|
for record_id in record_ids:
|
||||||
|
for child_field, child_value in sorted(values.items()):
|
||||||
|
child_label = Field.get_name(target_model, child_field)
|
||||||
|
lines.append('%s %s => %s' % (
|
||||||
|
self._nested_record_label(
|
||||||
|
field, field_label, record_id),
|
||||||
|
child_label,
|
||||||
|
self._format_change_value(child_value)))
|
||||||
|
return lines
|
||||||
|
|
||||||
|
def get_change_summary(self, name):
|
||||||
|
if self.event != 'write':
|
||||||
|
return ''
|
||||||
|
old_values = self._load_change_values(self.old_values)
|
||||||
|
new_values = self._load_change_values(self.new_values)
|
||||||
|
if not old_values and not new_values:
|
||||||
|
return ''
|
||||||
|
pool = Pool()
|
||||||
|
Field = pool.get('ir.model.field')
|
||||||
|
model = self.resource.__name__ if self.resource else None
|
||||||
|
lines = []
|
||||||
|
for field_name in sorted(set(old_values) | set(new_values)):
|
||||||
|
old_value = old_values.get(field_name)
|
||||||
|
new_value = new_values.get(field_name)
|
||||||
|
if old_value == new_value:
|
||||||
|
continue
|
||||||
|
if model:
|
||||||
|
field_label = Field.get_name(model, field_name)
|
||||||
|
else:
|
||||||
|
field_label = field_name
|
||||||
|
field = (
|
||||||
|
getattr(self.resource.__class__, field_name, None)
|
||||||
|
if self.resource else None)
|
||||||
|
if field and hasattr(field, 'get_target'):
|
||||||
|
target_model = field.get_target().__name__
|
||||||
|
nested_lines = self._format_nested_change_summary(
|
||||||
|
field, field_label, target_model, old_value, new_value)
|
||||||
|
if nested_lines:
|
||||||
|
lines.extend(nested_lines)
|
||||||
|
continue
|
||||||
|
command_lines = self._format_command_change_summary(
|
||||||
|
field, field_label, target_model, new_value)
|
||||||
|
if command_lines:
|
||||||
|
lines.extend(command_lines)
|
||||||
|
continue
|
||||||
|
lines.append('%s: %s -> %s' % (
|
||||||
|
field_label,
|
||||||
|
self._format_change_value(old_value),
|
||||||
|
self._format_change_value(new_value)))
|
||||||
|
return '\n'.join(lines)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def search_action(cls, name, clause):
|
def search_action(cls, name, clause):
|
||||||
return [('target', *clause[1:])]
|
return [('target', *clause[1:])]
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ attlist.field &=
|
|||||||
| "text"
|
| "text"
|
||||||
| "time"
|
| "time"
|
||||||
| "timedelta"
|
| "timedelta"
|
||||||
|
| "tolerance_gauge"
|
||||||
| "url"
|
| "url"
|
||||||
}?
|
}?
|
||||||
attlist.field &= attribute fill { "0" | "1" }?
|
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 = "1"] attribute grouping { "0" | "1" }?
|
||||||
attlist.field &= [a:defaultValue = "square"] attribute border { "square" | "circle" | "rounded" }?
|
attlist.field &= [a:defaultValue = "square"] attribute border { "square" | "circle" | "rounded" }?
|
||||||
attlist.field &= attribute loading { "lazy" | "eager" }?
|
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 }
|
image = element image { attlist.image, empty }
|
||||||
attlist.image &= attribute name { text }
|
attlist.image &= attribute name { text }
|
||||||
attlist.image &= [a:defaultValue = "icon"] attribute type { "icon" | "url" }?
|
attlist.image &= [a:defaultValue = "icon"] attribute type { "icon" | "url" }?
|
||||||
|
|||||||
@@ -251,6 +251,7 @@
|
|||||||
<value>text</value>
|
<value>text</value>
|
||||||
<value>time</value>
|
<value>time</value>
|
||||||
<value>timedelta</value>
|
<value>timedelta</value>
|
||||||
|
<value>tolerance_gauge</value>
|
||||||
<value>url</value>
|
<value>url</value>
|
||||||
</choice>
|
</choice>
|
||||||
</attribute>
|
</attribute>
|
||||||
@@ -577,6 +578,54 @@
|
|||||||
</attribute>
|
</attribute>
|
||||||
</optional>
|
</optional>
|
||||||
</define>
|
</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">
|
<define name="attlist.field" combine="interleave">
|
||||||
<optional>
|
<optional>
|
||||||
<attribute>
|
<attribute>
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ attlist.field &=
|
|||||||
| "text"
|
| "text"
|
||||||
| "time"
|
| "time"
|
||||||
| "timedelta"
|
| "timedelta"
|
||||||
|
| "tolerance_gauge"
|
||||||
| "url"
|
| "url"
|
||||||
}?
|
}?
|
||||||
attlist.field &=
|
attlist.field &=
|
||||||
@@ -75,6 +76,12 @@ attlist.field &= attribute help_field { text }?
|
|||||||
attlist.field &= attribute view_ids { text }?
|
attlist.field &= attribute view_ids { text }?
|
||||||
attlist.field &= attribute symbol { text }?
|
attlist.field &= attribute symbol { text }?
|
||||||
attlist.field &= [a:defaultValue = "1"] attribute grouping { "0" | "1" }?
|
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 }
|
prefix = element prefix { attlist.affix, empty }
|
||||||
suffix = element suffix { attlist.affix, empty }
|
suffix = element suffix { attlist.affix, empty }
|
||||||
attlist.affix &= attribute string { text }?
|
attlist.affix &= attribute string { text }?
|
||||||
|
|||||||
105
ir/ui/tree.rng
105
ir/ui/tree.rng
@@ -117,6 +117,7 @@
|
|||||||
<name ns="">widget</name>
|
<name ns="">widget</name>
|
||||||
<choice>
|
<choice>
|
||||||
<value>binary</value>
|
<value>binary</value>
|
||||||
|
<value>badge</value>
|
||||||
<value>boolean</value>
|
<value>boolean</value>
|
||||||
<value>callto</value>
|
<value>callto</value>
|
||||||
<value>char</value>
|
<value>char</value>
|
||||||
@@ -139,11 +140,21 @@
|
|||||||
<value>text</value>
|
<value>text</value>
|
||||||
<value>time</value>
|
<value>time</value>
|
||||||
<value>timedelta</value>
|
<value>timedelta</value>
|
||||||
|
<value>tolerance_gauge</value>
|
||||||
|
<value>variation</value>
|
||||||
<value>url</value>
|
<value>url</value>
|
||||||
</choice>
|
</choice>
|
||||||
</attribute>
|
</attribute>
|
||||||
</optional>
|
</optional>
|
||||||
</define>
|
</define>
|
||||||
|
<define name="attlist.field" combine="interleave">
|
||||||
|
<optional>
|
||||||
|
<attribute>
|
||||||
|
<name ns="">badge_colors</name>
|
||||||
|
<text/>
|
||||||
|
</attribute>
|
||||||
|
</optional>
|
||||||
|
</define>
|
||||||
<define name="attlist.field" combine="interleave">
|
<define name="attlist.field" combine="interleave">
|
||||||
<optional>
|
<optional>
|
||||||
<attribute a:defaultValue="0">
|
<attribute a:defaultValue="0">
|
||||||
@@ -198,6 +209,52 @@
|
|||||||
</attribute>
|
</attribute>
|
||||||
</optional>
|
</optional>
|
||||||
</define>
|
</define>
|
||||||
|
<define name="attlist.field" combine="interleave">
|
||||||
|
<optional>
|
||||||
|
<attribute>
|
||||||
|
<name ns="">sum_previous</name>
|
||||||
|
<text/>
|
||||||
|
</attribute>
|
||||||
|
</optional>
|
||||||
|
</define>
|
||||||
|
<define name="attlist.field" combine="interleave">
|
||||||
|
<optional>
|
||||||
|
<attribute a:defaultValue="0">
|
||||||
|
<name ns="">sum_variation</name>
|
||||||
|
<choice>
|
||||||
|
<value>0</value>
|
||||||
|
<value>1</value>
|
||||||
|
</choice>
|
||||||
|
</attribute>
|
||||||
|
</optional>
|
||||||
|
</define>
|
||||||
|
<define name="attlist.field" combine="interleave">
|
||||||
|
<optional>
|
||||||
|
<attribute>
|
||||||
|
<name ns="">variation_previous</name>
|
||||||
|
<text/>
|
||||||
|
</attribute>
|
||||||
|
</optional>
|
||||||
|
</define>
|
||||||
|
<define name="attlist.field" combine="interleave">
|
||||||
|
<optional>
|
||||||
|
<attribute>
|
||||||
|
<name ns="">variation_group_by</name>
|
||||||
|
<text/>
|
||||||
|
</attribute>
|
||||||
|
</optional>
|
||||||
|
</define>
|
||||||
|
<define name="attlist.field" combine="interleave">
|
||||||
|
<optional>
|
||||||
|
<attribute a:defaultValue="0">
|
||||||
|
<name ns="">variation_summary</name>
|
||||||
|
<choice>
|
||||||
|
<value>0</value>
|
||||||
|
<value>1</value>
|
||||||
|
</choice>
|
||||||
|
</attribute>
|
||||||
|
</optional>
|
||||||
|
</define>
|
||||||
<define name="attlist.field" combine="interleave">
|
<define name="attlist.field" combine="interleave">
|
||||||
<optional>
|
<optional>
|
||||||
<attribute>
|
<attribute>
|
||||||
@@ -330,6 +387,54 @@
|
|||||||
</attribute>
|
</attribute>
|
||||||
</optional>
|
</optional>
|
||||||
</define>
|
</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">
|
<define name="prefix">
|
||||||
<element>
|
<element>
|
||||||
<name ns="">prefix</name>
|
<name ns="">prefix</name>
|
||||||
|
|||||||
@@ -13,4 +13,10 @@ this repository contains the full copyright notices and license terms. -->
|
|||||||
<field name="event"/>
|
<field name="event"/>
|
||||||
<field name="action"/>
|
<field name="action"/>
|
||||||
</group>
|
</group>
|
||||||
|
<label name="change_summary"/>
|
||||||
|
<field name="change_summary"/>
|
||||||
|
<label name="old_values"/>
|
||||||
|
<field name="old_values"/>
|
||||||
|
<label name="new_values"/>
|
||||||
|
<field name="new_values"/>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -8,4 +8,5 @@ this repository contains the full copyright notices and license terms. -->
|
|||||||
<field name="user" expand="1"/>
|
<field name="user" expand="1"/>
|
||||||
<field name="event"/>
|
<field name="event"/>
|
||||||
<field name="action" expand="1"/>
|
<field name="action" expand="1"/>
|
||||||
|
<field name="change_summary" optional="1"/>
|
||||||
</tree>
|
</tree>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import base64
|
|||||||
import csv
|
import csv
|
||||||
import datetime
|
import datetime
|
||||||
import decimal
|
import decimal
|
||||||
|
import json
|
||||||
import random
|
import random
|
||||||
import time
|
import time
|
||||||
import warnings
|
import warnings
|
||||||
@@ -193,6 +194,99 @@ class ModelStorage(Model):
|
|||||||
resource=record, event=event, target=target, user=user,
|
resource=record, event=event, target=target, user=user,
|
||||||
**extra))
|
**extra))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _json_log_value(value):
|
||||||
|
if isinstance(value, (datetime.date, datetime.datetime, datetime.time)):
|
||||||
|
return value.isoformat()
|
||||||
|
if isinstance(value, decimal.Decimal):
|
||||||
|
return str(value)
|
||||||
|
if isinstance(value, bytes):
|
||||||
|
return '<binary>'
|
||||||
|
if isinstance(value, Model):
|
||||||
|
return str(value)
|
||||||
|
if isinstance(value, (list, tuple)):
|
||||||
|
return [ModelStorage._json_log_value(v) for v in value]
|
||||||
|
if isinstance(value, dict):
|
||||||
|
return {
|
||||||
|
str(k): ModelStorage._json_log_value(v)
|
||||||
|
for k, v in value.items()
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _log_value_text(cls, values):
|
||||||
|
values = {
|
||||||
|
name: cls._json_log_value(value)
|
||||||
|
for name, value in values.items()
|
||||||
|
}
|
||||||
|
return json.dumps(values, sort_keys=True, ensure_ascii=False)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _iter_log_commands(value):
|
||||||
|
if not isinstance(value, (list, tuple)):
|
||||||
|
return
|
||||||
|
for item in value:
|
||||||
|
if (isinstance(item, (list, tuple))
|
||||||
|
and item and isinstance(item[0], str)):
|
||||||
|
command = item[0]
|
||||||
|
if len(item) >= 3:
|
||||||
|
yield command, item[1], item[2]
|
||||||
|
elif len(item) >= 2:
|
||||||
|
yield command, item[1], None
|
||||||
|
if value and all(isinstance(item, (list, tuple)) for item in value):
|
||||||
|
return
|
||||||
|
index = 0
|
||||||
|
while index < len(value):
|
||||||
|
command = value[index]
|
||||||
|
if not isinstance(command, str):
|
||||||
|
index += 1
|
||||||
|
continue
|
||||||
|
if command == 'write':
|
||||||
|
if index + 2 >= len(value):
|
||||||
|
break
|
||||||
|
yield command, value[index + 1], value[index + 2]
|
||||||
|
index += 3
|
||||||
|
continue
|
||||||
|
if index + 1 >= len(value):
|
||||||
|
break
|
||||||
|
yield command, value[index + 1], None
|
||||||
|
index += 2
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _nested_log_values(cls, values):
|
||||||
|
nested_old = {}
|
||||||
|
nested_new = {}
|
||||||
|
for field_name, value in values.items():
|
||||||
|
field = cls._fields.get(field_name)
|
||||||
|
if not field or not hasattr(field, 'get_target'):
|
||||||
|
continue
|
||||||
|
commands = list(cls._iter_log_commands(value) or [])
|
||||||
|
write_commands = [
|
||||||
|
(ids, command_values)
|
||||||
|
for command, ids, command_values in commands
|
||||||
|
if command == 'write' and command_values]
|
||||||
|
if not write_commands:
|
||||||
|
continue
|
||||||
|
Target = field.get_target()
|
||||||
|
field_old = {}
|
||||||
|
field_new = {}
|
||||||
|
for ids, command_values in write_commands:
|
||||||
|
target_ids = [int(i) for i in ids]
|
||||||
|
field_names = list(sorted(command_values.keys()))
|
||||||
|
with without_check_access():
|
||||||
|
rows = Target.read(target_ids, field_names)
|
||||||
|
for row in rows:
|
||||||
|
record_id = str(row['id'])
|
||||||
|
field_old.setdefault(record_id, {}).update({
|
||||||
|
name: row.get(name)
|
||||||
|
for name in field_names
|
||||||
|
})
|
||||||
|
field_new.setdefault(record_id, {}).update(command_values)
|
||||||
|
if field_old or field_new:
|
||||||
|
nested_old[field_name] = field_old
|
||||||
|
nested_new[field_name] = field_new
|
||||||
|
return nested_old, nested_new
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, vlist):
|
def create(cls, vlist):
|
||||||
'''
|
'''
|
||||||
@@ -267,7 +361,25 @@ class ModelStorage(Model):
|
|||||||
all_records += records
|
all_records += records
|
||||||
all_fields.update(values.keys())
|
all_fields.update(values.keys())
|
||||||
if transaction.check_access and values:
|
if transaction.check_access and values:
|
||||||
cls.log(records, 'write', ','.join(sorted(values.keys())))
|
fields_names = list(sorted(values.keys()))
|
||||||
|
with without_check_access():
|
||||||
|
old_values = {
|
||||||
|
row['id']: {
|
||||||
|
name: row.get(name)
|
||||||
|
for name in fields_names
|
||||||
|
}
|
||||||
|
for row in cls.read(
|
||||||
|
[r.id for r in records], fields_names)}
|
||||||
|
nested_old, nested_new = cls._nested_log_values(values)
|
||||||
|
for record in records:
|
||||||
|
record_old_values = old_values.get(record.id, {})
|
||||||
|
record_new_values = values.copy()
|
||||||
|
record_old_values.update(nested_old)
|
||||||
|
record_new_values.update(nested_new)
|
||||||
|
cls.log([record], 'write', ','.join(fields_names),
|
||||||
|
old_values=cls._log_value_text(
|
||||||
|
record_old_values),
|
||||||
|
new_values=cls._log_value_text(record_new_values))
|
||||||
|
|
||||||
ModelAccess.check(cls.__name__, 'write')
|
ModelAccess.check(cls.__name__, 'write')
|
||||||
ModelFieldAccess.check(cls.__name__, all_fields, 'write')
|
ModelFieldAccess.check(cls.__name__, all_fields, 'write')
|
||||||
|
|||||||
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.GeneralLedgerAccountParty,
|
||||||
account.GeneralLedgerLine,
|
account.GeneralLedgerLine,
|
||||||
account.GeneralLedgerLineContext,
|
account.GeneralLedgerLineContext,
|
||||||
|
account.GeneralLedgerDetail,
|
||||||
|
account.GeneralLedgerDetailContext,
|
||||||
account.BalanceSheetContext,
|
account.BalanceSheetContext,
|
||||||
account.BalanceSheetComparisionContext,
|
account.BalanceSheetComparisionContext,
|
||||||
account.IncomeStatementContext,
|
account.IncomeStatementContext,
|
||||||
@@ -118,6 +120,7 @@ def register():
|
|||||||
Pool.register(
|
Pool.register(
|
||||||
account.AccountTypeStatement,
|
account.AccountTypeStatement,
|
||||||
account.GeneralLedger,
|
account.GeneralLedger,
|
||||||
|
account.GeneralLedgerDetailReport,
|
||||||
account.TrialBalance,
|
account.TrialBalance,
|
||||||
account.AgedBalanceReport,
|
account.AgedBalanceReport,
|
||||||
move.GeneralJournal,
|
move.GeneralJournal,
|
||||||
|
|||||||
@@ -7,9 +7,10 @@ from decimal import Decimal
|
|||||||
from itertools import zip_longest
|
from itertools import zip_longest
|
||||||
|
|
||||||
from dateutil.relativedelta import relativedelta
|
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.aggregate import Count, Max, Min, Sum
|
||||||
from sql.conditionals import Case, Coalesce
|
from sql.conditionals import Case, Coalesce
|
||||||
|
from sql.operators import Concat
|
||||||
|
|
||||||
from trytond import backend
|
from trytond import backend
|
||||||
from trytond.i18n import gettext
|
from trytond.i18n import gettext
|
||||||
@@ -2583,6 +2584,562 @@ class GeneralLedgerLineContext(GeneralLedgerAccountContext):
|
|||||||
return super().get_context(fields_names=fields_names)
|
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
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _resolve_reference(value):
|
||||||
|
if not value or hasattr(value, '__name__'):
|
||||||
|
return value
|
||||||
|
if not isinstance(value, str):
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
model_name, record_id = value.split(',', 1)
|
||||||
|
record_id = int(record_id)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
Model = Pool().get(model_name)
|
||||||
|
except KeyError:
|
||||||
|
return None
|
||||||
|
return Model(record_id)
|
||||||
|
|
||||||
|
def get_document_field(self, name):
|
||||||
|
origin = self._resolve_reference(
|
||||||
|
self.origin or (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):
|
class GeneralLedger(Report):
|
||||||
__name__ = 'account.general_ledger'
|
__name__ = 'account.general_ledger'
|
||||||
|
|
||||||
@@ -2611,6 +3168,52 @@ class GeneralLedger(Report):
|
|||||||
return report_context
|
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):
|
class TrialBalance(Report):
|
||||||
__name__ = 'account.trial_balance'
|
__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"/>
|
<field name="rule_group" ref="rule_group_general_ledger_line_companies"/>
|
||||||
</record>
|
</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">
|
<record model="ir.ui.view" id="general_ledger_account_context_view_form">
|
||||||
<field name="model">account.general_ledger.account.context</field>
|
<field name="model">account.general_ledger.account.context</field>
|
||||||
<field name="type">form</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="inherit" ref="general_ledger_account_context_view_form"/>
|
||||||
<field name="name">general_ledger_line_context_form</field>
|
<field name="name">general_ledger_line_context_form</field>
|
||||||
</record>
|
</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">
|
<record model="ir.ui.view" id="account_balance_sheet_view_tree">
|
||||||
<field name="model">account.account.type</field>
|
<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')
|
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
|
del _states
|
||||||
|
|
||||||
@@ -1117,46 +1119,84 @@ class Line(DescriptionOriginMixin, MoveLineMixin, ModelSQL, ModelView):
|
|||||||
return line.move.id
|
return line.move.id
|
||||||
|
|
||||||
def get_rate(self,name=None):
|
def get_rate(self,name=None):
|
||||||
if self.amount_second_currency:
|
amount = self.credit if self.credit else self.debit
|
||||||
return round((self.credit if self.credit else self.debit) / abs(self.amount_second_currency),6)
|
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(
|
@fields.depends(
|
||||||
'debit', 'credit',
|
'debit', 'credit',
|
||||||
'date','second_currency','amount_second_currency')
|
'date','second_currency','amount_second_currency', 'rate',
|
||||||
def on_change_amount_second_currency(self):
|
'origin', 'move_origin', 'move', '_parent_move.origin',
|
||||||
Currency = Pool().get('currency.currency')
|
'company', 'account')
|
||||||
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()
|
|
||||||
|
|
||||||
@fields.depends(
|
|
||||||
'debit', 'credit',
|
|
||||||
'date','second_currency','amount_second_currency')
|
|
||||||
def on_change_second_currency(self):
|
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')
|
Currency = Pool().get('currency.currency')
|
||||||
Date = Pool().get('ir.date')
|
Date = Pool().get('ir.date')
|
||||||
if self.second_currency != None and self.amount_second_currency != None and (not self.credit and not self.debit):
|
if self.second_currency is None:
|
||||||
tdate = Date.today()
|
return None
|
||||||
if self.date:
|
tdate = Date.today()
|
||||||
tdate = self.date
|
if self.date:
|
||||||
rate = Currency._get_rate([self.second_currency],tdate)
|
tdate = self.date
|
||||||
if rate:
|
rates = Currency._get_rate([self.second_currency], tdate)
|
||||||
rate = rate[self.second_currency.id]
|
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:
|
if self.amount_second_currency > 0:
|
||||||
self.debit = round(rate * self.amount_second_currency,2)
|
self.debit = amount
|
||||||
elif self.amount_second_currency > 0:
|
self.credit = Decimal(0)
|
||||||
self.credit = round(rate * abs(self.amount_second_currency),2)
|
elif self.amount_second_currency < 0:
|
||||||
self.rate = self.get_rate()
|
self.debit = Decimal(0)
|
||||||
|
self.credit = amount
|
||||||
|
else:
|
||||||
|
self.debit = Decimal(0)
|
||||||
|
self.credit = Decimal(0)
|
||||||
|
|
||||||
@fields.depends(
|
@fields.depends(
|
||||||
'move', 'debit', 'credit',
|
'move', 'debit', 'credit',
|
||||||
@@ -1223,15 +1263,26 @@ class Line(DescriptionOriginMixin, MoveLineMixin, ModelSQL, ModelView):
|
|||||||
if self.debit:
|
if self.debit:
|
||||||
self.credit = Decimal(0)
|
self.credit = Decimal(0)
|
||||||
self._amount_second_currency_sign()
|
self._amount_second_currency_sign()
|
||||||
|
self.rate = self.get_rate()
|
||||||
|
|
||||||
@fields.depends('debit', 'credit', 'amount_second_currency')
|
@fields.depends('debit', 'credit', 'amount_second_currency')
|
||||||
def on_change_credit(self):
|
def on_change_credit(self):
|
||||||
if self.credit:
|
if self.credit:
|
||||||
self.debit = Decimal(0)
|
self.debit = Decimal(0)
|
||||||
self._amount_second_currency_sign()
|
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):
|
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()
|
self._amount_second_currency_sign()
|
||||||
|
|
||||||
def _amount_second_currency_sign(self):
|
def _amount_second_currency_sign(self):
|
||||||
@@ -1241,6 +1292,7 @@ class Line(DescriptionOriginMixin, MoveLineMixin, ModelSQL, ModelView):
|
|||||||
self.amount_second_currency = \
|
self.amount_second_currency = \
|
||||||
self.amount_second_currency.copy_sign(
|
self.amount_second_currency.copy_sign(
|
||||||
(self.debit or 0) - (self.credit or 0))
|
(self.debit or 0) - (self.credit or 0))
|
||||||
|
self._set_rate_from_amounts()
|
||||||
|
|
||||||
@fields.depends('account')
|
@fields.depends('account')
|
||||||
def on_change_account(self):
|
def on_change_account(self):
|
||||||
@@ -1545,6 +1597,14 @@ class Line(DescriptionOriginMixin, MoveLineMixin, ModelSQL, ModelView):
|
|||||||
for fname in move_fields(move_name=False):
|
for fname in move_fields(move_name=False):
|
||||||
vals.setdefault(fname, None)
|
vals.setdefault(fname, None)
|
||||||
lines = super(Line, cls).create(vlist)
|
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)
|
period_and_journals = set((line.period, line.journal)
|
||||||
for line in lines)
|
for line in lines)
|
||||||
for period, journal in period_and_journals:
|
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.account.tax import TaxableMixin
|
||||||
from trytond.modules.company.tests import (
|
from trytond.modules.company.tests import (
|
||||||
CompanyTestMixin, PartyCompanyCheckEraseMixin, create_company, set_company)
|
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.pool import Pool
|
||||||
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
|
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
|
||||||
from trytond.transaction import Transaction, inactive_records
|
from trytond.transaction import Transaction, inactive_records
|
||||||
@@ -576,6 +576,54 @@ class AccountTestCase(
|
|||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
cash_cur.amount_second_currency, Decimal(50))
|
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()
|
@with_transaction()
|
||||||
def test_account_type_amount(self):
|
def test_account_type_amount(self):
|
||||||
"Test account type amount"
|
"Test account type amount"
|
||||||
@@ -2049,5 +2097,121 @@ class AccountTestCase(
|
|||||||
|
|
||||||
self.assertEqual(account.parent, new_account)
|
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),
|
||||||
|
})
|
||||||
|
|
||||||
|
@with_transaction()
|
||||||
|
def test_general_ledger_detail_document_field_reference_string(self):
|
||||||
|
"Test General Ledger Detail document fields resolve reference strings"
|
||||||
|
pool = Pool()
|
||||||
|
Detail = pool.get('account.general_ledger.detail')
|
||||||
|
Party = pool.get('party.party')
|
||||||
|
|
||||||
|
party = Party(name='Customer')
|
||||||
|
party.save()
|
||||||
|
|
||||||
|
detail = Detail()
|
||||||
|
detail.origin = '%s,%s' % (Party.__name__, party.id)
|
||||||
|
|
||||||
|
self.assertIsNone(detail.get_document_field('document_number'))
|
||||||
|
self.assertEqual(detail.get_document_field('document_type'),
|
||||||
|
party.rec_name)
|
||||||
|
|
||||||
|
|
||||||
del ModuleTestCase
|
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=""/>
|
<field name="amount_second_currency" symbol=""/>
|
||||||
<label name="second_currency"/>
|
<label name="second_currency"/>
|
||||||
<field name="second_currency"/>
|
<field name="second_currency"/>
|
||||||
|
<label name="rate"/>
|
||||||
|
<field name="rate"/>
|
||||||
</page>
|
</page>
|
||||||
<page name="tax_lines">
|
<page name="tax_lines">
|
||||||
<field name="tax_lines" colspan="4"/>
|
<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({
|
cls.__rpc__.update({
|
||||||
'post': RPC(
|
'post': RPC(
|
||||||
readonly=False, instantiate=0, fresh_session=True),
|
readonly=False, instantiate=0, fresh_session=False),
|
||||||
})
|
})
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -515,7 +515,9 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
|||||||
~((table.state == 'cancelled') & (table.number == Null)),
|
~((table.state == 'cancelled') & (table.number == Null)),
|
||||||
CharLength(table.number), table.number]
|
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):
|
def on_change_with_rate(self, name=None):
|
||||||
return self.get_selected_rate()
|
return self.get_selected_rate()
|
||||||
|
|
||||||
@@ -528,8 +530,8 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
|||||||
def get_selected_rate(self,name=None):
|
def get_selected_rate(self,name=None):
|
||||||
Currency = Pool().get('currency.currency')
|
Currency = Pool().get('currency.currency')
|
||||||
Date = Pool().get('ir.date')
|
Date = Pool().get('ir.date')
|
||||||
company = self.company
|
company = getattr(self, 'company', None)
|
||||||
currency = self.currency
|
currency = getattr(self, 'currency', None)
|
||||||
|
|
||||||
if not currency or not company:
|
if not currency or not company:
|
||||||
return None
|
return None
|
||||||
@@ -563,6 +565,16 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
|||||||
if rate:
|
if rate:
|
||||||
return round(1/rate,6) or 1
|
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
|
@staticmethod
|
||||||
def default_type():
|
def default_type():
|
||||||
return Transaction().context.get('type', 'out')
|
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 = (
|
||||||
line.amount_second_currency.copy_sign(
|
line.amount_second_currency.copy_sign(
|
||||||
line.debit - line.credit))
|
line.debit - line.credit))
|
||||||
|
line.rate = line.get_rate()
|
||||||
line.account = self.account
|
line.account = self.account
|
||||||
logger.info("_GET_MOVE_LINE_ACCOUNT:%s",line.account)
|
logger.info("_GET_MOVE_LINE_ACCOUNT:%s",line.account)
|
||||||
if self.account.party_required:
|
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)
|
remainder = sum(l.debit - l.credit for l in move_lines)
|
||||||
if self.payment_term:
|
if self.payment_term:
|
||||||
payment_date = self.payment_term_date or self.invoice_date or today
|
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)
|
logger.info("MODEL:%s",model)
|
||||||
|
line = None
|
||||||
if model:
|
if model:
|
||||||
Line = Pool().get(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)
|
logger.info("LINE:%s",line)
|
||||||
term_lines = self.payment_term.compute(
|
term_lines = self.payment_term.compute(
|
||||||
self.total_amount, self.currency, payment_date, line)
|
self.total_amount, self.currency, payment_date, line)
|
||||||
else:
|
else:
|
||||||
term_lines = [(self.payment_term_date or today, self.total_amount)]
|
term_lines = [(self.payment_term_date or today, self.total_amount)]
|
||||||
past_payment_term_dates = []
|
past_payment_term_dates = []
|
||||||
@@ -1890,17 +1905,17 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
|||||||
cls._check_taxes(invoices)
|
cls._check_taxes(invoices)
|
||||||
# cls._check_similar(invoices)
|
# cls._check_similar(invoices)
|
||||||
|
|
||||||
invoices_in = cls.browse([i for i in invoices if i.type == 'in'])
|
cls.set_number(invoices)
|
||||||
cls.set_number(invoices_in)
|
|
||||||
cls._store_cache(invoices)
|
cls._store_cache(invoices)
|
||||||
|
|
||||||
moves = []
|
moves = []
|
||||||
for invoice in invoices:
|
for invoice in invoices:
|
||||||
if invoice.type == 'in':
|
if invoice.currency != invoice.company.currency and not invoice.rate:
|
||||||
move = invoice.get_move()
|
invoice.rate = invoice.get_selected_rate()
|
||||||
if move != invoice.move:
|
move = invoice.get_move()
|
||||||
invoice.move = move
|
if move != invoice.move:
|
||||||
moves.append(move)
|
invoice.move = move
|
||||||
|
moves.append(move)
|
||||||
invoice.do_lot_invoicing()
|
invoice.do_lot_invoicing()
|
||||||
if moves:
|
if moves:
|
||||||
Move.save(moves)
|
Move.save(moves)
|
||||||
@@ -1963,23 +1978,35 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
|||||||
move_line_.amount_second_currency = -original_amount
|
move_line_.amount_second_currency = -original_amount
|
||||||
|
|
||||||
if amount < 0:
|
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.debit = Decimal(0)
|
||||||
move_line.credit = -amount
|
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.account = gl.product.account_cogs_used if gl.fee else move_line.account
|
||||||
move_line_.credit = Decimal(0)
|
move_line_.credit = Decimal(0)
|
||||||
move_line_.debit = -amount
|
move_line_.debit = -amount
|
||||||
move_line_.account = gl.product.account_stock_in_used
|
move_line_.account = gl.product.account_stock_in_used
|
||||||
else:
|
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.debit = amount
|
||||||
move_line.credit = Decimal(0)
|
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.account = gl.product.account_cogs_used if gl.fee else move_line.account
|
||||||
move_line_.debit = Decimal(0)
|
move_line_.debit = Decimal(0)
|
||||||
move_line_.credit = amount
|
move_line_.credit = amount
|
||||||
move_line_.account = gl.product.account_stock_in_used
|
move_line_.account = gl.product.account_stock_in_used
|
||||||
|
|
||||||
move_lines = [move_line,move_line_]
|
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:
|
if drop:
|
||||||
drop_line = AccountMoveLine()
|
drop_line = AccountMoveLine()
|
||||||
@@ -2017,6 +2044,10 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
|||||||
drop_line_.second_currency = self.currency
|
drop_line_.second_currency = self.currency
|
||||||
drop_line_.amount_second_currency = -original_amount
|
drop_line_.amount_second_currency = -original_amount
|
||||||
move_lines = [drop_line,drop_line_]
|
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
|
return move_lines
|
||||||
|
|
||||||
@@ -2038,10 +2069,16 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
|||||||
var_qt = sum([i.quantity for i in gl])
|
var_qt = sum([i.quantity for i in gl])
|
||||||
logger.info("LOT_TO_PROCESS:%s",lot)
|
logger.info("LOT_TO_PROCESS:%s",lot)
|
||||||
logger.info("FEE_TO_PROCESS:%s",gl[0].fee)
|
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)
|
diff = gl[0].fee.amount - gl[0].fee.get_non_cog(lot)
|
||||||
account_move = gl[0].fee._get_account_move_fee(lot,'in',diff)
|
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):
|
if (lot and not gl[0].fee) or (gl[0].fee and gl[0].product.landed_cost):
|
||||||
adjust_move_lines = []
|
adjust_move_lines = []
|
||||||
mov = None
|
mov = None
|
||||||
@@ -3174,7 +3211,6 @@ class InvoiceLine(sequence_ordered(), ModelSQL, ModelView, TaxableMixin):
|
|||||||
|
|
||||||
def _compute_taxes(self):
|
def _compute_taxes(self):
|
||||||
pool = Pool()
|
pool = Pool()
|
||||||
Currency = pool.get('currency.currency')
|
|
||||||
TaxLine = pool.get('account.tax.line')
|
TaxLine = pool.get('account.tax.line')
|
||||||
|
|
||||||
tax_lines = []
|
tax_lines = []
|
||||||
@@ -3183,11 +3219,7 @@ class InvoiceLine(sequence_ordered(), ModelSQL, ModelView, TaxableMixin):
|
|||||||
taxes = self._get_taxes().values()
|
taxes = self._get_taxes().values()
|
||||||
for tax in taxes:
|
for tax in taxes:
|
||||||
amount = tax['base']
|
amount = tax['base']
|
||||||
with Transaction().set_context(
|
amount = self.invoice._compute_company_currency_amount(amount)
|
||||||
date=self.invoice.currency_date):
|
|
||||||
amount = Currency.compute(
|
|
||||||
self.invoice.currency, amount,
|
|
||||||
self.invoice.company.currency)
|
|
||||||
tax_line = TaxLine()
|
tax_line = TaxLine()
|
||||||
tax_line.amount = amount
|
tax_line.amount = amount
|
||||||
tax_line.type = 'base'
|
tax_line.type = 'base'
|
||||||
@@ -3252,6 +3284,7 @@ class InvoiceLine(sequence_ordered(), ModelSQL, ModelView, TaxableMixin):
|
|||||||
line.amount_second_currency = (
|
line.amount_second_currency = (
|
||||||
line.amount_second_currency.copy_sign(
|
line.amount_second_currency.copy_sign(
|
||||||
line.debit - line.credit))
|
line.debit - line.credit))
|
||||||
|
line.rate = line.get_rate()
|
||||||
line.account = self.account
|
line.account = self.account
|
||||||
logger.info("GET_MOVE_LINES_ACCOUNT:%s",line.account)
|
logger.info("GET_MOVE_LINES_ACCOUNT:%s",line.account)
|
||||||
if self.account.party_required:
|
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
|
Return a list of move lines instances for invoice tax
|
||||||
'''
|
'''
|
||||||
Currency = Pool().get('currency.currency')
|
|
||||||
pool = Pool()
|
pool = Pool()
|
||||||
Currency = pool.get('currency.currency')
|
|
||||||
MoveLine = pool.get('account.move.line')
|
MoveLine = pool.get('account.move.line')
|
||||||
TaxLine = pool.get('account.tax.line')
|
TaxLine = pool.get('account.tax.line')
|
||||||
line = MoveLine()
|
line = MoveLine()
|
||||||
@@ -3521,11 +3552,8 @@ class InvoiceTax(sequence_ordered(), ModelSQL, ModelView):
|
|||||||
return []
|
return []
|
||||||
line.description = self.description
|
line.description = self.description
|
||||||
if self.invoice.currency != self.invoice.company.currency:
|
if self.invoice.currency != self.invoice.company.currency:
|
||||||
with Transaction().set_context(date=self.invoice.currency_date):
|
amount = self.invoice._compute_company_currency_amount(self.amount)
|
||||||
amount = Currency.compute(self.invoice.currency, self.amount,
|
base = self.invoice._compute_company_currency_amount(self.base)
|
||||||
self.invoice.company.currency)
|
|
||||||
base = Currency.compute(self.invoice.currency, self.base,
|
|
||||||
self.invoice.company.currency)
|
|
||||||
line.amount_second_currency = self.amount
|
line.amount_second_currency = self.amount
|
||||||
line.second_currency = self.invoice.currency
|
line.second_currency = self.invoice.currency
|
||||||
else:
|
else:
|
||||||
@@ -3547,6 +3575,7 @@ class InvoiceTax(sequence_ordered(), ModelSQL, ModelView):
|
|||||||
line.amount_second_currency = (
|
line.amount_second_currency = (
|
||||||
line.amount_second_currency.copy_sign(
|
line.amount_second_currency.copy_sign(
|
||||||
line.debit - line.credit))
|
line.debit - line.credit))
|
||||||
|
line.rate = line.get_rate()
|
||||||
line.account = self.account
|
line.account = self.account
|
||||||
if self.account.party_required:
|
if self.account.party_required:
|
||||||
line.party = self.invoice.party
|
line.party = self.invoice.party
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ this repository contains the full copyright notices and license terms. -->
|
|||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.action.report" id="report_invoice">
|
<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="model">account.invoice</field>
|
||||||
<field name="report_name">account.invoice</field>
|
<field name="report_name">account.invoice</field>
|
||||||
<field name="report">account_invoice/invoice.fodt</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>
|
||||||
|
|
||||||
<record model="ir.action.report" id="report_invoice_ict_final">
|
<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="model">account.invoice</field>
|
||||||
<field name="report_name">account.invoice</field>
|
<field name="report_name">account.invoice</field>
|
||||||
<field name="report">account_invoice/invoice_ict_final.fodt</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"/>
|
<field name="action" ref="report_invoice_ict_final"/>
|
||||||
</record>
|
</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">
|
<record model="ir.sequence.type" id="sequence_type_account_invoice">
|
||||||
<field name="name">Invoice</field>
|
<field name="name">Invoice</field>
|
||||||
</record>
|
</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: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>
|
<office:meta>
|
||||||
<dc:title>Provisional Invoice</dc:title>
|
<dc:title>Invoice</dc:title>
|
||||||
<meta:initial-creator>willen</meta:initial-creator>
|
<meta:initial-creator>willen</meta:initial-creator>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<meta:generator>LibreOffice/7.6.0.3$Windows_X86_64 LibreOffice_project/69edd8b8ebc41d00b4de3915dc82f8f0fc3b6265</meta:generator>
|
<meta:generator>LibreOffice/7.6.0.3$Windows_X86_64 LibreOffice_project/69edd8b8ebc41d00b4de3915dc82f8f0fc3b6265</meta:generator>
|
||||||
<meta:editing-duration>PT3H16M29S</meta:editing-duration>
|
<meta:editing-duration>PT3H20M59S</meta:editing-duration>
|
||||||
<meta:editing-cycles>9</meta:editing-cycles>
|
<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>
|
<office:settings>
|
||||||
<config:config-item-set config:name="ooo:view-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="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="ViewAreaWidth" config:type="long">27264</config:config-item>
|
||||||
<config:config-item config:name="ViewAreaHeight" config:type="long">13187</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-indexed config:name="Views">
|
||||||
<config:config-item-map-entry>
|
<config:config-item-map-entry>
|
||||||
<config:config-item config:name="ViewId" config:type="string">view2</config:config-item>
|
<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="ViewLeft" config:type="long">13275</config:config-item>
|
||||||
<config:config-item config:name="ViewTop" config:type="long">33203</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="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="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="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="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="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="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="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="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="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="ProtectForm" 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="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="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="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="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:font-face-decls>
|
||||||
<office:styles>
|
<office:styles>
|
||||||
<style:default-style style:family="graphic">
|
<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: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:tab-stops/>
|
||||||
</style:paragraph-properties>
|
</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:default-style style:family="paragraph">
|
<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: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:default-style style:family="table">
|
<style:default-style style:family="table">
|
||||||
<style:table-properties table:border-model="collapsing"/>
|
<style:table-properties table:border-model="collapsing"/>
|
||||||
@@ -245,7 +246,7 @@
|
|||||||
</style:style>
|
</style:style>
|
||||||
<style:style style:name="No_20_Spacing" style:display-name="No Spacing" style:family="paragraph">
|
<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: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: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: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:paragraph-properties text:number-lines="false" text:line-number="0">
|
||||||
@@ -295,30 +296,35 @@
|
|||||||
</style:paragraph-properties>
|
</style:paragraph-properties>
|
||||||
<style:text-properties fo:font-size="10pt" style:font-size-asian="10pt" style:font-size-complex="10pt"/>
|
<style:text-properties fo:font-size="10pt" style:font-size-asian="10pt" style:font-size-complex="10pt"/>
|
||||||
</style:style>
|
</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="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: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: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:style style:name="Intestazione_20_Carattere" style:display-name="Intestazione Carattere" style:family="text" style:parent-style-name="Default_20_Paragraph_20_Font">
|
<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:style style:name="Placeholder_20_Text" style:display-name="Placeholder Text" style:family="text" style:parent-style-name="Default_20_Paragraph_20_Font">
|
<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: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: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: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: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: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: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: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:style style:name="Placeholder" style:family="text">
|
<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: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:style style:name="Police_20_par_20_dÃ_a9_faut" style:display-name="Police par défaut" style:family="text"/>
|
<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="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: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: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>
|
</style:style>
|
||||||
@@ -664,7 +670,7 @@
|
|||||||
</style:style>
|
</style:style>
|
||||||
<style:style style:name="P21" style:family="paragraph" style:parent-style-name="Standard">
|
<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: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:style style:name="P22" style:family="paragraph" style:parent-style-name="Standard">
|
<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"/>
|
<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:style style:name="P26" style:family="paragraph" style:parent-style-name="Standard">
|
<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: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:style style:name="P27" style:family="paragraph" style:parent-style-name="Standard">
|
<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"/>
|
<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:style style:name="P29" style:family="paragraph" style:parent-style-name="Standard">
|
<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: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:style style:name="P30" style:family="paragraph" style:parent-style-name="Standard">
|
<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"/>
|
<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:style style:name="P32" style:family="paragraph" style:parent-style-name="Standard">
|
<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: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:style style:name="P33" style:family="paragraph" style:parent-style-name="Standard">
|
<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"/>
|
<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:style style:name="P36" style:family="paragraph" style:parent-style-name="Standard">
|
<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: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:style style:name="P37" style:family="paragraph" style:parent-style-name="Standard">
|
<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"/>
|
<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:paragraph-properties>
|
||||||
<style:text-properties officeooo:paragraph-rsid="00022035"/>
|
<style:text-properties officeooo:paragraph-rsid="00022035"/>
|
||||||
</style:style>
|
</style:style>
|
||||||
<style:style style:name="P44" style:family="paragraph" style:parent-style-name="Standard">
|
<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:writing-mode="lr-tb"/>
|
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" fo:line-height="100%"/>
|
||||||
<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: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: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:paragraph-properties style:writing-mode="lr-tb"/>
|
||||||
</style:style>
|
</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:paragraph-properties style:writing-mode="lr-tb"/>
|
||||||
</style:style>
|
</style:style>
|
||||||
<style:style style:name="T1" style:family="text">
|
<style:style style:name="T1" style:family="text">
|
||||||
@@ -790,6 +816,24 @@
|
|||||||
<style:style style:name="T7" style:family="text">
|
<style:style style:name="T7" style:family="text">
|
||||||
<style:text-properties fo:language="fr" fo:country="CH"/>
|
<style:text-properties fo:language="fr" fo:country="CH"/>
|
||||||
</style:style>
|
</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: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: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>
|
</style:style>
|
||||||
@@ -3866,7 +3910,7 @@
|
|||||||
<table:table-row table:style-name="Tableau3.1">
|
<table:table-row table:style-name="Tableau3.1">
|
||||||
<table:table-cell table:style-name="Tableau3.A1" office:value-type="string">
|
<table:table-cell table:style-name="Tableau3.A1" office:value-type="string">
|
||||||
<text:p text:style-name="P22"/>
|
<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:table-cell table:style-name="Tableau3.A1" office:value-type="string">
|
<table:table-cell table:style-name="Tableau3.A1" office:value-type="string">
|
||||||
<text:p text:style-name="P22"/>
|
<text:p text:style-name="P22"/>
|
||||||
@@ -3930,15 +3974,19 @@
|
|||||||
<text:p text:style-name="P25">Goods description</text:p>
|
<text:p text:style-name="P25">Goods description</text:p>
|
||||||
</table:table-cell>
|
</table:table-cell>
|
||||||
<table:table-cell table:style-name="Tableau5.A1" office:value-type="string">
|
<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="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="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"/>
|
||||||
|
<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="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="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="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="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"/>
|
||||||
<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="P26"/>
|
<text:p text:style-name="P26"/>
|
||||||
<text:p text:style-name="P12"/>
|
<text:p text:style-name="P12"/>
|
||||||
</table:table-cell>
|
</table:table-cell>
|
||||||
@@ -3952,10 +4000,10 @@
|
|||||||
<text:p text:style-name="P15"><text:s text:c="19"/>BALES</text:p>
|
<text:p text:style-name="P15"><text:s text:c="19"/>BALES</text:p>
|
||||||
</table:table-cell>
|
</table:table-cell>
|
||||||
<table:table-cell table:style-name="Tableau6.A1" office:value-type="string">
|
<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:table-cell table:style-name="Tableau6.A1" office:value-type="string">
|
<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:table-cell table:style-name="Tableau6.A1" office:value-type="string">
|
<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>
|
<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>
|
<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:table-cell table:style-name="Tableau6.A2" office:value-type="string">
|
<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:table-cell table:style-name="Tableau6.A2" office:value-type="string">
|
<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:table-cell table:style-name="Tableau6.A2" office:value-type="string">
|
<table:table-cell table:style-name="Tableau6.A2" office:value-type="string">
|
||||||
<text:p text:style-name="P16"/>
|
<text:p text:style-name="P16"/>
|
||||||
@@ -3996,7 +4044,7 @@
|
|||||||
<text:p text:style-name="P16">Equivalent to LBS</text:p>
|
<text:p text:style-name="P16">Equivalent to LBS</text:p>
|
||||||
</table:table-cell>
|
</table:table-cell>
|
||||||
<table:table-cell table:style-name="Tableau7.A1" office:value-type="string">
|
<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:table-cell table:style-name="Tableau7.A1" office:value-type="string">
|
<table:table-cell table:style-name="Tableau7.A1" office:value-type="string">
|
||||||
<text:p text:style-name="P20"/>
|
<text:p text:style-name="P20"/>
|
||||||
@@ -4009,13 +4057,18 @@
|
|||||||
<table:table-column table:style-name="Tableau8.B"/>
|
<table:table-column table:style-name="Tableau8.B"/>
|
||||||
<table:table-row table:style-name="Tableau8.1">
|
<table:table-row table:style-name="Tableau8.1">
|
||||||
<table:table-cell table:style-name="Tableau8.A1" office:value-type="string">
|
<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"/>
|
||||||
<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">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="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="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="P17"/>
|
||||||
<text:p text:style-name="P31"/>
|
<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:table-cell table:style-name="Tableau8.A1" office:value-type="string">
|
<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>
|
<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"/>
|
<text:p text:style-name="P11"/>
|
||||||
<table:table table:name="Tableau9" table:style-name="Tableau9">
|
<table:table table:name="Tableau9" table:style-name="Tableau9">
|
||||||
<table:table-column table:style-name="Tableau9.A"/>
|
<table:table-column table:style-name="Tableau9.A"/>
|
||||||
|
<text:soft-page-break/>
|
||||||
<table:table-row table:style-name="Tableau9.1">
|
<table:table-row table:style-name="Tableau9.1">
|
||||||
<table:table-cell table:style-name="Tableau9.A1" office:value-type="string">
|
<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>
|
<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>
|
<text:p text:style-name="P13">Controller Name</text:p>
|
||||||
</table:table-cell>
|
</table:table-cell>
|
||||||
<table:table-cell table:style-name="Tableau10.A1" office:value-type="string">
|
<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:p text:style-name="P25"><text:placeholder text:placeholder-type="text"><invoice.report_controller_name></text:placeholder></text:p>
|
<text:p text:style-name="P25"><text:placeholder text:placeholder-type="text"><invoice.report_controller_name></text:placeholder></text:p>
|
||||||
</table:table-cell>
|
</table:table-cell>
|
||||||
|
|||||||
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
1835
modules/account_invoice/invoice_melya.fodt
Normal file
1835
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
|
import datetime
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
from unittest.mock import Mock, patch
|
||||||
|
|
||||||
from trytond.modules.account_invoice.exceptions import (
|
from trytond.modules.account_invoice.exceptions import (
|
||||||
PaymentTermValidationError)
|
PaymentTermValidationError)
|
||||||
@@ -251,5 +252,70 @@ class AccountInvoiceTestCase(
|
|||||||
(datetime.date(2012, 1, 14), Decimal('-1.0')),
|
(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
|
del ModuleTestCase
|
||||||
|
|||||||
@@ -181,7 +181,11 @@ class InvoiceLine(metaclass=PoolMeta):
|
|||||||
company=self.invoice.company.id, date=accounting_date):
|
company=self.invoice.company.id, date=accounting_date):
|
||||||
anglo_saxon_move_lines = self._get_anglo_saxon_move_lines(
|
anglo_saxon_move_lines = self._get_anglo_saxon_move_lines(
|
||||||
cost, type_)
|
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')
|
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)
|
||||||
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 file is part of Tryton. The COPYRIGHT file at the top level of
|
||||||
this repository contains the full copyright notices and license terms. -->
|
this repository contains the full copyright notices and license terms. -->
|
||||||
<data>
|
<data>
|
||||||
<xpath expr="/form/notebook" position="inside">
|
<xpath expr="/form/notebook/page[@id='invoice']" position="before">
|
||||||
<page string="Analytic" id="analytic_accounts">
|
<page string="Analytics" id="analytic_accounts">
|
||||||
<field name="analytic_accounts" colspan="4"/>
|
<field name="analytic_accounts" colspan="4"/>
|
||||||
</page>
|
</page>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
from trytond.pool import Pool
|
from trytond.pool import Pool
|
||||||
from . import automation,rules,freight_booking,cron #, document
|
from . import automation,rules,freight_booking,booking_report,cron #, document
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
Pool.register(
|
Pool.register(
|
||||||
automation.AutomationDocument,
|
automation.AutomationDocument,
|
||||||
rules.AutomationRuleSet,
|
rules.AutomationRuleSet,
|
||||||
freight_booking.FreightBookingInfo,
|
freight_booking.FreightBookingInfo,
|
||||||
|
booking_report.FreightBookingShipmentReport,
|
||||||
|
booking_report.FreightBookingShipmentReportLinkStart,
|
||||||
cron.Cron,
|
cron.Cron,
|
||||||
cron.AutomationCron,
|
cron.AutomationCron,
|
||||||
module='automation', type_='model')
|
module='automation', type_='model')
|
||||||
|
Pool.register(
|
||||||
|
booking_report.FreightBookingShipmentReportLink,
|
||||||
|
module='automation', type_='wizard')
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import io
|
|||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
import traceback
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -62,25 +63,57 @@ class AutomationDocument(ModelSQL, ModelView, Workflow):
|
|||||||
@ModelView.button
|
@ModelView.button
|
||||||
def run_ocr(cls, docs):
|
def run_ocr(cls, docs):
|
||||||
for doc in docs:
|
for doc in docs:
|
||||||
|
logger.info(
|
||||||
|
"RUN_OCR_START doc=%s type=%s state=%s incoming=%s",
|
||||||
|
getattr(doc, 'id', None), getattr(doc, 'type', None),
|
||||||
|
getattr(doc, 'state', None),
|
||||||
|
getattr(getattr(doc, 'document', None), 'id', None))
|
||||||
try:
|
try:
|
||||||
if doc.type == 'weight_report':
|
if doc.type == 'weight_report':
|
||||||
# Décoder le fichier depuis le champ Binary
|
# Décoder le fichier depuis le champ Binary
|
||||||
file_data = doc.document.data or b""
|
if not doc.document:
|
||||||
logger.info(f"File size: {len(file_data)} bytes")
|
raise ValueError("No incoming document linked")
|
||||||
logger.info(f"First 20 bytes: {file_data[:20]}")
|
|
||||||
logger.info(f"Last 20 bytes: {file_data[-20:]}")
|
|
||||||
|
|
||||||
|
file_data = doc.document.data or b""
|
||||||
file_name = doc.document.name or "document"
|
file_name = doc.document.name or "document"
|
||||||
|
url = "http://automation-service:8006/ocr"
|
||||||
|
logger.info(
|
||||||
|
"RUN_OCR_FILE doc=%s incoming=%s name=%s size=%s "
|
||||||
|
"first_20=%r last_20=%r",
|
||||||
|
getattr(doc, 'id', None), doc.document.id,
|
||||||
|
file_name, len(file_data), file_data[:20],
|
||||||
|
file_data[-20:])
|
||||||
|
|
||||||
|
if not file_data:
|
||||||
|
raise ValueError("Incoming document has no binary data")
|
||||||
|
|
||||||
# Envoyer le fichier au service OCR
|
# Envoyer le fichier au service OCR
|
||||||
|
logger.info(
|
||||||
|
"RUN_OCR_HTTP_REQUEST doc=%s url=%s file_name=%s "
|
||||||
|
"size=%s",
|
||||||
|
getattr(doc, 'id', None), url, file_name,
|
||||||
|
len(file_data))
|
||||||
response = requests.post(
|
response = requests.post(
|
||||||
"http://automation-service:8006/ocr",
|
url,
|
||||||
files={"file": (file_name, io.BytesIO(file_data))}
|
files={"file": (file_name, io.BytesIO(file_data))},
|
||||||
|
timeout=120,
|
||||||
)
|
)
|
||||||
|
logger.info(
|
||||||
|
"RUN_OCR_HTTP_RESPONSE doc=%s status=%s reason=%s "
|
||||||
|
"elapsed=%s content_type=%s body_start=%r",
|
||||||
|
getattr(doc, 'id', None), response.status_code,
|
||||||
|
response.reason, response.elapsed,
|
||||||
|
response.headers.get('content-type'),
|
||||||
|
response.text[:1000])
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
data = response.json()
|
data = response.json()
|
||||||
logger.info("RUN_OCR_RESPONSE:%s",data)
|
ocr_text = data.get("ocr_text", "") or ""
|
||||||
doc.ocr_text = data.get("ocr_text", "")
|
logger.info(
|
||||||
|
"RUN_OCR_JSON doc=%s keys=%s ocr_text_len=%s "
|
||||||
|
"ocr_text_start=%r",
|
||||||
|
getattr(doc, 'id', None), list(data.keys()),
|
||||||
|
len(ocr_text), ocr_text[:500])
|
||||||
|
doc.ocr_text = ocr_text
|
||||||
doc.state = "ocr_done"
|
doc.state = "ocr_done"
|
||||||
doc.notes = (doc.notes or "") + "OCR done\n"
|
doc.notes = (doc.notes or "") + "OCR done\n"
|
||||||
else:
|
else:
|
||||||
@@ -112,9 +145,17 @@ class AutomationDocument(ModelSQL, ModelView, Workflow):
|
|||||||
doc.notes = (doc.notes or "") + " SO updated"
|
doc.notes = (doc.notes or "") + " SO updated"
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
logger.error(
|
||||||
|
"RUN_OCR_FAILED doc=%s type=%s error=%s traceback=%s",
|
||||||
|
getattr(doc, 'id', None), getattr(doc, 'type', None),
|
||||||
|
e, traceback.format_exc())
|
||||||
doc.state = "error"
|
doc.state = "error"
|
||||||
doc.notes = (doc.notes or "") + f"OCR error: {e}\n"
|
doc.notes = (doc.notes or "") + f"OCR error: {e}\n"
|
||||||
doc.save()
|
doc.save()
|
||||||
|
logger.info(
|
||||||
|
"RUN_OCR_END doc=%s state=%s ocr_text_len=%s",
|
||||||
|
getattr(doc, 'id', None), getattr(doc, 'state', None),
|
||||||
|
len(getattr(doc, 'ocr_text', None) or ""))
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
# STRUCTURE (doctr)
|
# STRUCTURE (doctr)
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
@@ -248,29 +289,10 @@ class AutomationDocument(ModelSQL, ModelView, Workflow):
|
|||||||
ShipmentWR.save([swr])
|
ShipmentWR.save([swr])
|
||||||
doc.notes = (doc.notes or "") + f"Shipment found: {sh[0].number}\n"
|
doc.notes = (doc.notes or "") + f"Shipment found: {sh[0].number}\n"
|
||||||
logger.info("BL_NUMBER:%s",sh[0].bl_number)
|
logger.info("BL_NUMBER:%s",sh[0].bl_number)
|
||||||
if sh[0].incoming_moves:
|
doc.notes = (
|
||||||
factor_net = wr.net_landed_kg / wr.bales if wr.bales else 1
|
(doc.notes or "")
|
||||||
factor_gross = wr.gross_landed_kg / wr.bales if wr.bales else 1
|
+ "Global WR linked to shipment. "
|
||||||
for move in sh[0].incoming_moves:
|
+ "Create remote lot WRs from the weight report form.\n")
|
||||||
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"
|
|
||||||
|
|
||||||
# if cls.rule_set.ocr_required:[]
|
# if cls.rule_set.ocr_required:[]
|
||||||
# cls.run_ocr([doc])
|
# cls.run_ocr([doc])
|
||||||
|
|||||||
193
modules/automation/booking_report.py
Normal file
193
modules/automation/booking_report.py
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
from sql import Literal, Null
|
||||||
|
from sql.functions import CurrentTimestamp
|
||||||
|
|
||||||
|
from trytond.model import ModelSQL, ModelView, fields
|
||||||
|
from trytond.pool import Pool
|
||||||
|
from trytond.transaction import Transaction
|
||||||
|
from trytond.wizard import Button, StateTransition, StateView, Wizard
|
||||||
|
from trytond.exceptions import UserError
|
||||||
|
|
||||||
|
|
||||||
|
class FreightBookingShipmentReport(ModelSQL, ModelView):
|
||||||
|
"Freight Booking Shipment Report"
|
||||||
|
__name__ = 'freight.booking.shipment.report'
|
||||||
|
|
||||||
|
automation_document = fields.Many2One(
|
||||||
|
'automation.document', "Automation Document")
|
||||||
|
bl_number = fields.Function(fields.Char("BL Number"), 'get_bl_number')
|
||||||
|
shipment = fields.Function(fields.Many2One(
|
||||||
|
'stock.shipment.in', "Shipment"), 'get_shipment')
|
||||||
|
status = fields.Function(fields.Selection([
|
||||||
|
('found', "Found"),
|
||||||
|
('error', "Error"),
|
||||||
|
], "Status"), 'get_status')
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def __setup__(cls):
|
||||||
|
super().__setup__()
|
||||||
|
cls._order = [
|
||||||
|
('id', 'DESC'),
|
||||||
|
]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def table_query(cls):
|
||||||
|
AutomationDocument = Pool().get('automation.document')
|
||||||
|
document = AutomationDocument.__table__()
|
||||||
|
|
||||||
|
return document.select(
|
||||||
|
Literal(None).as_('create_uid'),
|
||||||
|
CurrentTimestamp().as_('create_date'),
|
||||||
|
Literal(None).as_('write_uid'),
|
||||||
|
Literal(None).as_('write_date'),
|
||||||
|
document.id.as_('id'),
|
||||||
|
document.id.as_('automation_document'),
|
||||||
|
where=(
|
||||||
|
(document.type == 'weight_report')
|
||||||
|
& (document.metadata_json != Null)))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _extract_bl_number(cls, metadata_json):
|
||||||
|
if not metadata_json:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
metadata = json.loads(str(metadata_json))
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def find_value(value):
|
||||||
|
if isinstance(value, dict):
|
||||||
|
for key in (
|
||||||
|
'bl_no', 'bl_number', 'bl', 'BL_Number',
|
||||||
|
'BL number', 'B/L No'):
|
||||||
|
bl_number = value.get(key)
|
||||||
|
if bl_number:
|
||||||
|
return str(bl_number).strip()
|
||||||
|
for item in value.values():
|
||||||
|
bl_number = find_value(item)
|
||||||
|
if bl_number:
|
||||||
|
return bl_number
|
||||||
|
elif isinstance(value, list):
|
||||||
|
for item in value:
|
||||||
|
bl_number = find_value(item)
|
||||||
|
if bl_number:
|
||||||
|
return bl_number
|
||||||
|
return None
|
||||||
|
|
||||||
|
return find_value(metadata)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _get_report_data(cls, reports):
|
||||||
|
ShipmentIn = Pool().get('stock.shipment.in')
|
||||||
|
bl_numbers = {}
|
||||||
|
|
||||||
|
for report in reports:
|
||||||
|
document = report.automation_document
|
||||||
|
bl_number = cls._extract_bl_number(
|
||||||
|
document.metadata_json if document else None)
|
||||||
|
bl_numbers[report.id] = bl_number
|
||||||
|
|
||||||
|
shipments_by_bl = {}
|
||||||
|
for bl_number in {b for b in bl_numbers.values() if b}:
|
||||||
|
shipments = ShipmentIn.search([
|
||||||
|
('bl_number', '=', bl_number),
|
||||||
|
], limit=1)
|
||||||
|
if not shipments:
|
||||||
|
shipments = ShipmentIn.search([
|
||||||
|
('bl_number', 'ilike', bl_number),
|
||||||
|
], limit=1)
|
||||||
|
shipments_by_bl[bl_number] = shipments[0] if shipments else None
|
||||||
|
|
||||||
|
return bl_numbers, shipments_by_bl
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_bl_number(cls, reports, name):
|
||||||
|
bl_numbers, _ = cls._get_report_data(reports)
|
||||||
|
return bl_numbers
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_shipment(cls, reports, name):
|
||||||
|
bl_numbers, shipments_by_bl = cls._get_report_data(reports)
|
||||||
|
return {
|
||||||
|
report.id: (
|
||||||
|
shipments_by_bl[bl_numbers[report.id]].id
|
||||||
|
if bl_numbers[report.id]
|
||||||
|
and shipments_by_bl.get(bl_numbers[report.id])
|
||||||
|
else None)
|
||||||
|
for report in reports
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_status(cls, reports, name):
|
||||||
|
bl_numbers, shipments_by_bl = cls._get_report_data(reports)
|
||||||
|
return {
|
||||||
|
report.id: (
|
||||||
|
'found'
|
||||||
|
if bl_numbers[report.id]
|
||||||
|
and shipments_by_bl.get(bl_numbers[report.id])
|
||||||
|
else 'error')
|
||||||
|
for report in reports
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class FreightBookingShipmentReportLinkStart(ModelView):
|
||||||
|
"Link Freight Booking Shipment Report"
|
||||||
|
__name__ = 'freight.booking.shipment.report.link.start'
|
||||||
|
|
||||||
|
bl_number = fields.Char("BL Number", readonly=True)
|
||||||
|
shipment = fields.Many2One(
|
||||||
|
'stock.shipment.in', "Shipment", required=True)
|
||||||
|
|
||||||
|
|
||||||
|
class FreightBookingShipmentReportLink(Wizard):
|
||||||
|
"Link Freight Booking Shipment Report"
|
||||||
|
__name__ = 'freight.booking.shipment.report.link'
|
||||||
|
|
||||||
|
start = StateView(
|
||||||
|
'freight.booking.shipment.report.link.start',
|
||||||
|
'automation.freight_booking_shipment_report_link_start_form',
|
||||||
|
[
|
||||||
|
Button('Cancel', 'end', 'tryton-cancel'),
|
||||||
|
Button('Link', 'link', 'tryton-ok', default=True),
|
||||||
|
])
|
||||||
|
link = StateTransition()
|
||||||
|
|
||||||
|
def default_start(self, fields):
|
||||||
|
Report = Pool().get('freight.booking.shipment.report')
|
||||||
|
active_ids = Transaction().context.get('active_ids') or []
|
||||||
|
reports = Report.browse(active_ids)
|
||||||
|
error_reports = [
|
||||||
|
report for report in reports
|
||||||
|
if report.status == 'error' and report.bl_number]
|
||||||
|
if len(error_reports) > 1:
|
||||||
|
raise UserError(
|
||||||
|
"Link one error line at a time to avoid overwriting the "
|
||||||
|
"shipment BL number.")
|
||||||
|
return {
|
||||||
|
'bl_number': error_reports[0].bl_number if error_reports else None,
|
||||||
|
}
|
||||||
|
|
||||||
|
def transition_link(self):
|
||||||
|
Report = Pool().get('freight.booking.shipment.report')
|
||||||
|
ShipmentIn = Pool().get('stock.shipment.in')
|
||||||
|
active_ids = Transaction().context.get('active_ids') or []
|
||||||
|
reports = Report.browse(active_ids)
|
||||||
|
error_reports = [
|
||||||
|
report for report in reports
|
||||||
|
if report.status == 'error' and report.bl_number]
|
||||||
|
if not error_reports:
|
||||||
|
raise UserError(
|
||||||
|
"Select at least one error line with a BL number.")
|
||||||
|
if len(error_reports) > 1:
|
||||||
|
raise UserError(
|
||||||
|
"Link one error line at a time to avoid overwriting the "
|
||||||
|
"shipment BL number.")
|
||||||
|
|
||||||
|
shipment = self.start.shipment
|
||||||
|
shipment.bl_number = error_reports[0].bl_number
|
||||||
|
ShipmentIn.save([shipment])
|
||||||
|
return 'end'
|
||||||
|
|
||||||
|
def end(self):
|
||||||
|
return 'reload'
|
||||||
73
modules/automation/booking_report.xml
Normal file
73
modules/automation/booking_report.xml
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<tryton>
|
||||||
|
<data>
|
||||||
|
<record model="ir.ui.view" id="freight_booking_shipment_report_tree">
|
||||||
|
<field name="model">freight.booking.shipment.report</field>
|
||||||
|
<field name="type">tree</field>
|
||||||
|
<field name="name">freight_booking_shipment_report_tree</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.action.act_window" id="act_freight_booking_shipment_report">
|
||||||
|
<field name="name">Freight Booking Shipment Report</field>
|
||||||
|
<field name="res_model">freight.booking.shipment.report</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.action.act_window.view" id="act_freight_booking_shipment_report_view1">
|
||||||
|
<field name="sequence" eval="10"/>
|
||||||
|
<field name="view" ref="freight_booking_shipment_report_tree"/>
|
||||||
|
<field name="act_window" ref="act_freight_booking_shipment_report"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="freight_booking_shipment_report_link_start_form">
|
||||||
|
<field name="model">freight.booking.shipment.report.link.start</field>
|
||||||
|
<field name="type">form</field>
|
||||||
|
<field name="name">freight_booking_shipment_report_link_start_form</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.action.wizard" id="act_freight_booking_shipment_report_link">
|
||||||
|
<field name="name">Link to Shipment</field>
|
||||||
|
<field name="wiz_name">freight.booking.shipment.report.link</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.action.keyword" id="keyword_freight_booking_shipment_report_link">
|
||||||
|
<field name="keyword">form_action</field>
|
||||||
|
<field name="model">freight.booking.shipment.report,-1</field>
|
||||||
|
<field name="action" ref="act_freight_booking_shipment_report_link"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.model.access" id="access_freight_booking_shipment_report">
|
||||||
|
<field name="model">freight.booking.shipment.report</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_freight_booking_shipment_report_link_start">
|
||||||
|
<field name="model">freight.booking.shipment.report.link.start</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_freight_booking_shipment_report_link_start_automation">
|
||||||
|
<field name="model">freight.booking.shipment.report.link.start</field>
|
||||||
|
<field name="group" ref="group_automation"/>
|
||||||
|
<field name="perm_read" eval="True"/>
|
||||||
|
<field name="perm_write" eval="True"/>
|
||||||
|
<field name="perm_create" eval="True"/>
|
||||||
|
<field name="perm_delete" eval="False"/>
|
||||||
|
</record>
|
||||||
|
<record model="ir.model.access" id="access_freight_booking_shipment_report_automation">
|
||||||
|
<field name="model">freight.booking.shipment.report</field>
|
||||||
|
<field name="group" ref="group_automation"/>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
name="Freight Booking Shipment Report"
|
||||||
|
action="act_freight_booking_shipment_report"
|
||||||
|
parent="menu_automation"
|
||||||
|
sequence="11"
|
||||||
|
id="menu_freight_booking_shipment_report" />
|
||||||
|
</data>
|
||||||
|
</tryton>
|
||||||
@@ -11,6 +11,7 @@ import traceback
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Cron(metaclass=PoolMeta):
|
class Cron(metaclass=PoolMeta):
|
||||||
__name__ = 'ir.cron'
|
__name__ = 'ir.cron'
|
||||||
|
|
||||||
@@ -18,9 +19,11 @@ class Cron(metaclass=PoolMeta):
|
|||||||
def __setup__(cls):
|
def __setup__(cls):
|
||||||
super().__setup__()
|
super().__setup__()
|
||||||
cls.method.selection.append(
|
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):
|
class AutomationCron(ModelSQL, ModelView):
|
||||||
"Automation Cron"
|
"Automation Cron"
|
||||||
__name__ = 'automation.cron'
|
__name__ = 'automation.cron'
|
||||||
@@ -30,7 +33,7 @@ class AutomationCron(ModelSQL, ModelView):
|
|||||||
('weekly', "Weekly"),
|
('weekly', "Weekly"),
|
||||||
('monthly', "Monthly"),
|
('monthly', "Monthly"),
|
||||||
], "Frequency", required=True,
|
], "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)
|
last_update = fields.Date("Last Update", required=True)
|
||||||
|
|
||||||
@@ -70,12 +73,13 @@ class AutomationCron(ModelSQL, ModelView):
|
|||||||
))
|
))
|
||||||
|
|
||||||
rows = cursor.fetchall()
|
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:
|
with Transaction().new_transaction() as trans1:
|
||||||
try:
|
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 = []
|
parties_to_save = []
|
||||||
vessels_to_save = []
|
vessels_to_save = []
|
||||||
@@ -85,7 +89,7 @@ class AutomationCron(ModelSQL, ModelView):
|
|||||||
vessels_cache = {}
|
vessels_cache = {}
|
||||||
locations_cache = {}
|
locations_cache = {}
|
||||||
|
|
||||||
# Collecter les données des objets de référence
|
# Collecter les donnees des objets de reference
|
||||||
for row in rows:
|
for row in rows:
|
||||||
(
|
(
|
||||||
si_number, si_date, si_quantity, si_unit,
|
si_number, si_date, si_quantity, si_unit,
|
||||||
@@ -97,7 +101,6 @@ class AutomationCron(ModelSQL, ModelView):
|
|||||||
comments, fintrade_booking_key
|
comments, fintrade_booking_key
|
||||||
) = row
|
) = row
|
||||||
|
|
||||||
# Fonction pour obtenir ou créer un Party
|
|
||||||
def get_or_create_party(name):
|
def get_or_create_party(name):
|
||||||
if not name:
|
if not name:
|
||||||
return None
|
return None
|
||||||
@@ -105,20 +108,18 @@ class AutomationCron(ModelSQL, ModelView):
|
|||||||
if name_upper in parties_cache:
|
if name_upper in parties_cache:
|
||||||
return parties_cache[name_upper]
|
return parties_cache[name_upper]
|
||||||
|
|
||||||
# Chercher d'abord dans la base
|
existing = Party.search(
|
||||||
existing = Party.search([('name', '=', name_upper)], limit=1)
|
[('name', '=', name_upper)], limit=1)
|
||||||
if existing:
|
if existing:
|
||||||
parties_cache[name_upper] = existing[0]
|
parties_cache[name_upper] = existing[0]
|
||||||
return existing[0]
|
return existing[0]
|
||||||
|
|
||||||
# Créer un nouveau
|
|
||||||
new_p = Party()
|
new_p = Party()
|
||||||
new_p.name = name_upper
|
new_p.name = name_upper
|
||||||
parties_cache[name_upper] = new_p
|
parties_cache[name_upper] = new_p
|
||||||
parties_to_save.append(new_p)
|
parties_to_save.append(new_p)
|
||||||
return new_p
|
return new_p
|
||||||
|
|
||||||
# Fonction pour obtenir ou créer un Vessel
|
|
||||||
def get_or_create_vessel(name):
|
def get_or_create_vessel(name):
|
||||||
if not name:
|
if not name:
|
||||||
return None
|
return None
|
||||||
@@ -126,7 +127,8 @@ class AutomationCron(ModelSQL, ModelView):
|
|||||||
if name_upper in vessels_cache:
|
if name_upper in vessels_cache:
|
||||||
return vessels_cache[name_upper]
|
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:
|
if existing:
|
||||||
vessels_cache[name_upper] = existing[0]
|
vessels_cache[name_upper] = existing[0]
|
||||||
return existing[0]
|
return existing[0]
|
||||||
@@ -137,7 +139,6 @@ class AutomationCron(ModelSQL, ModelView):
|
|||||||
vessels_to_save.append(new_v)
|
vessels_to_save.append(new_v)
|
||||||
return new_v
|
return new_v
|
||||||
|
|
||||||
# Fonction pour obtenir ou créer une Location
|
|
||||||
def get_or_create_location(name, type_):
|
def get_or_create_location(name, type_):
|
||||||
if not name:
|
if not name:
|
||||||
return None
|
return None
|
||||||
@@ -148,9 +149,8 @@ class AutomationCron(ModelSQL, ModelView):
|
|||||||
|
|
||||||
existing = Location.search([
|
existing = Location.search([
|
||||||
('name', '=', name_upper),
|
('name', '=', name_upper),
|
||||||
('type', '=', type_)
|
('type', '=', type_),
|
||||||
], limit=1)
|
], limit=1)
|
||||||
|
|
||||||
if existing:
|
if existing:
|
||||||
locations_cache[key] = existing[0]
|
locations_cache[key] = existing[0]
|
||||||
return existing[0]
|
return existing[0]
|
||||||
@@ -162,40 +162,40 @@ class AutomationCron(ModelSQL, ModelView):
|
|||||||
locations_to_save.append(new_loc)
|
locations_to_save.append(new_loc)
|
||||||
return new_loc
|
return new_loc
|
||||||
|
|
||||||
# Collecter les objets à créer
|
|
||||||
_ = get_or_create_party(carrier_name)
|
_ = get_or_create_party(carrier_name)
|
||||||
_ = get_or_create_party(agent_name)
|
_ = get_or_create_party(agent_name)
|
||||||
_ = get_or_create_vessel(vessel_name)
|
_ = get_or_create_vessel(vessel_name)
|
||||||
_ = get_or_create_location(loading_name, 'supplier')
|
_ = get_or_create_location(loading_name, 'supplier')
|
||||||
_ = get_or_create_location(destination_name, 'customer')
|
_ = get_or_create_location(destination_name, 'customer')
|
||||||
|
|
||||||
# Sauvegarder tous les objets de référence
|
|
||||||
if parties_to_save:
|
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)
|
Party.save(parties_to_save)
|
||||||
|
|
||||||
if vessels_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)
|
Vessel.save(vessels_to_save)
|
||||||
|
|
||||||
if locations_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)
|
Location.save(locations_to_save)
|
||||||
|
|
||||||
trans1.commit()
|
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:
|
except Exception as e:
|
||||||
trans1.rollback()
|
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())
|
logger.error(traceback.format_exc())
|
||||||
raise
|
raise
|
||||||
|
|
||||||
# ---- TRANSACTIONS INDIVIDUELLES pour chaque shipment ----
|
# Transactions individuelles pour chaque shipment
|
||||||
successful_shipments = 0
|
successful_shipments = 0
|
||||||
failed_shipments = []
|
failed_shipments = []
|
||||||
|
|
||||||
# Recréer le curseur après la nouvelle transaction
|
|
||||||
cursor2 = Transaction().connection.cursor()
|
cursor2 = Transaction().connection.cursor()
|
||||||
cursor2.execute(*t.select(
|
cursor2.execute(*t.select(
|
||||||
t.ShippingInstructionNumber,
|
t.ShippingInstructionNumber,
|
||||||
@@ -219,6 +219,75 @@ class AutomationCron(ModelSQL, ModelView):
|
|||||||
|
|
||||||
rows2 = cursor2.fetchall()
|
rows2 = cursor2.fetchall()
|
||||||
|
|
||||||
|
def normalize_bl_number(value):
|
||||||
|
value = str(value or '').strip()
|
||||||
|
if value.lower() in {'none', 'null'}:
|
||||||
|
return ''
|
||||||
|
if value.upper() in {
|
||||||
|
'MULTIPLE BL NUMBERS FOUND',
|
||||||
|
'MULTIPLE B/L NUMBERS FOUND',
|
||||||
|
'NO BL NUMBER FOUND',
|
||||||
|
'NO B/L NUMBER FOUND',
|
||||||
|
}:
|
||||||
|
return ''
|
||||||
|
return value
|
||||||
|
|
||||||
|
def normalize_booking_number(value):
|
||||||
|
value = str(value or '').strip()
|
||||||
|
if value.lower() in {'none', 'null'}:
|
||||||
|
return ''
|
||||||
|
return value
|
||||||
|
|
||||||
|
def sync_shipment_from_booking(shipment, bl_number, bl_date, etd_date,
|
||||||
|
controller):
|
||||||
|
changed = False
|
||||||
|
columns = []
|
||||||
|
values = []
|
||||||
|
booking_bl_number = normalize_bl_number(bl_number)
|
||||||
|
shipment_bl_number = normalize_bl_number(shipment.bl_number)
|
||||||
|
if booking_bl_number and booking_bl_number != shipment_bl_number:
|
||||||
|
logger.info(
|
||||||
|
"FREIGHT_BOOKING_SYNC_BL shipment=%s reference=%s "
|
||||||
|
"old_bl=%s new_bl=%s",
|
||||||
|
shipment.id, shipment.reference,
|
||||||
|
shipment.bl_number, booking_bl_number)
|
||||||
|
shipment.bl_number = booking_bl_number
|
||||||
|
columns.append('bl_number')
|
||||||
|
values.append(booking_bl_number)
|
||||||
|
changed = True
|
||||||
|
if bl_date and shipment.bl_date != bl_date:
|
||||||
|
logger.info(
|
||||||
|
"FREIGHT_BOOKING_SYNC_BL_DATE shipment=%s reference=%s "
|
||||||
|
"old_bl_date=%s new_bl_date=%s",
|
||||||
|
shipment.id, shipment.reference,
|
||||||
|
shipment.bl_date, bl_date)
|
||||||
|
shipment.bl_date = bl_date
|
||||||
|
columns.append('bl_date')
|
||||||
|
values.append(bl_date)
|
||||||
|
changed = True
|
||||||
|
if etd_date and shipment.etd != etd_date:
|
||||||
|
logger.info(
|
||||||
|
"FREIGHT_BOOKING_SYNC_ETD shipment=%s reference=%s "
|
||||||
|
"old_etd=%s new_etd=%s",
|
||||||
|
shipment.id, shipment.reference,
|
||||||
|
shipment.etd, etd_date)
|
||||||
|
shipment.etd = etd_date
|
||||||
|
columns.append('etd')
|
||||||
|
values.append(etd_date)
|
||||||
|
changed = True
|
||||||
|
if controller and shipment.controller_target != controller:
|
||||||
|
shipment.controller_target = controller
|
||||||
|
columns.append('controller_target')
|
||||||
|
values.append(controller)
|
||||||
|
changed = True
|
||||||
|
if changed:
|
||||||
|
table = ShipmentIn.__table__()
|
||||||
|
table_columns = [getattr(table, column) for column in columns]
|
||||||
|
cursor = Transaction().connection.cursor()
|
||||||
|
cursor.execute(*table.update(
|
||||||
|
table_columns, values, where=table.id == shipment.id))
|
||||||
|
return changed
|
||||||
|
|
||||||
for i, row in enumerate(rows2, 1):
|
for i, row in enumerate(rows2, 1):
|
||||||
(
|
(
|
||||||
si_number, si_date, si_quantity, si_unit,
|
si_number, si_date, si_quantity, si_unit,
|
||||||
@@ -229,43 +298,95 @@ class AutomationCron(ModelSQL, ModelView):
|
|||||||
etd_date, bl_date, controller,
|
etd_date, bl_date, controller,
|
||||||
comments, fintrade_booking_key
|
comments, fintrade_booking_key
|
||||||
) = row
|
) = row
|
||||||
|
si_reference = normalize_booking_number(si_number)
|
||||||
|
|
||||||
logger.info(f"Traitement shipment {i}/{len(rows2)} : SI {si_number}")
|
logger.info(
|
||||||
|
"Traitement shipment %s/%s : SI %s reference=%s",
|
||||||
|
i, len(rows2), si_number, si_reference)
|
||||||
|
|
||||||
# ---- TRANSACTION INDIVIDUELLE pour ce shipment ----
|
|
||||||
try:
|
try:
|
||||||
with Transaction().new_transaction() as trans_shipment:
|
with Transaction().new_transaction() as trans_shipment:
|
||||||
logger.info(f"Début transaction pour SI {si_number}")
|
logger.info(
|
||||||
|
"Debut transaction pour SI %s reference=%s",
|
||||||
|
si_number, si_reference)
|
||||||
|
|
||||||
# Vérifier si le shipment existe déjà
|
existing_shipments = ShipmentIn.search([
|
||||||
existing_shipment = ShipmentIn.search([
|
('reference', '=', si_reference)
|
||||||
('reference', '=', si_number)
|
])
|
||||||
], limit=1)
|
|
||||||
|
|
||||||
if existing_shipment:
|
if existing_shipments:
|
||||||
logger.info(f"Shipment {si_number} existe déjà, ignoré")
|
if len(existing_shipments) > 1:
|
||||||
|
logger.info(
|
||||||
|
"FREIGHT_BOOKING_DUPLICATE_SHIPMENT_REFERENCE "
|
||||||
|
"reference=%s shipment_ids=%s",
|
||||||
|
si_reference,
|
||||||
|
[shipment.id for shipment in existing_shipments])
|
||||||
|
for existing in existing_shipments:
|
||||||
|
sync_shipment_from_booking(
|
||||||
|
existing, bl_number, bl_date, etd_date,
|
||||||
|
controller)
|
||||||
|
shipment = existing_shipments[0]
|
||||||
|
if shipment.incoming_moves:
|
||||||
|
logger.info(
|
||||||
|
"Shipment %s existe deja avec lots, "
|
||||||
|
"booking data synchronized",
|
||||||
|
si_reference)
|
||||||
|
trans_shipment.commit()
|
||||||
|
continue
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
"Shipment %s existe deja sans lots, verification freight_booking_lots",
|
||||||
|
si_reference)
|
||||||
|
with Transaction().set_context(
|
||||||
|
_purchase_trade_skip_physical_lot_tolerance_warning=True):
|
||||||
|
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_reference, len(shipment.incoming_moves))
|
||||||
|
else:
|
||||||
|
logger.info(
|
||||||
|
"Shipment %s existe sans lots et aucun lot disponible pour l'instant",
|
||||||
|
si_reference)
|
||||||
trans_shipment.commit()
|
trans_shipment.commit()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Récupérer les objets (maintenant ils existent dans la base)
|
|
||||||
carrier = None
|
carrier = None
|
||||||
if carrier_name:
|
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:
|
if carrier_list:
|
||||||
carrier = carrier_list[0]
|
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:
|
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 = None
|
||||||
|
agent_list = Party.search([
|
||||||
agent_list = Party.search([('name', '=', str(agent_name or 'TBN').strip().upper())], limit=1)
|
('name', '=', str(agent_name or 'TBN').strip().upper())
|
||||||
|
], limit=1)
|
||||||
if agent_list:
|
if agent_list:
|
||||||
agent = agent_list[0]
|
agent = agent_list[0]
|
||||||
|
|
||||||
vessel = None
|
vessel = None
|
||||||
if vessel_name:
|
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:
|
if vessel_list:
|
||||||
vessel = vessel_list[0]
|
vessel = vessel_list[0]
|
||||||
|
|
||||||
@@ -287,18 +408,18 @@ class AutomationCron(ModelSQL, ModelView):
|
|||||||
if loc_to_list:
|
if loc_to_list:
|
||||||
loc_to = loc_to_list[0]
|
loc_to = loc_to_list[0]
|
||||||
|
|
||||||
# Vérification critique du carrier
|
|
||||||
if not 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)
|
logger.error(error_msg)
|
||||||
raise ValueError(error_msg)
|
raise ValueError(error_msg)
|
||||||
|
|
||||||
# Créer le shipment
|
|
||||||
shipment = ShipmentIn()
|
shipment = ShipmentIn()
|
||||||
shipment.reference = si_number
|
shipment.reference = si_reference
|
||||||
shipment.from_location = loc_from
|
shipment.from_location = loc_from
|
||||||
shipment.to_location = loc_to
|
shipment.to_location = loc_to
|
||||||
shipment.carrier = None #carrier
|
shipment.carrier = None # carrier
|
||||||
shipment.supplier = agent
|
shipment.supplier = agent
|
||||||
shipment.agent = agent
|
shipment.agent = agent
|
||||||
shipment.vessel = vessel
|
shipment.vessel = vessel
|
||||||
@@ -308,20 +429,29 @@ class AutomationCron(ModelSQL, ModelView):
|
|||||||
shipment.etd = etd_date
|
shipment.etd = etd_date
|
||||||
shipment.etad = shipment.bl_date + timedelta(days=20)
|
shipment.etad = shipment.bl_date + timedelta(days=20)
|
||||||
|
|
||||||
# Sauvegarder ce shipment uniquement
|
|
||||||
ShipmentIn.save([shipment])
|
|
||||||
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)
|
|
||||||
ShipmentIn.save([shipment])
|
ShipmentIn.save([shipment])
|
||||||
|
with Transaction().set_context(
|
||||||
|
_purchase_trade_skip_physical_lot_tolerance_warning=True):
|
||||||
|
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
|
||||||
|
shipment.create_fee(shipment.controller)
|
||||||
|
shipment.instructions = shipment.get_instructions_html(
|
||||||
|
inv_date, inv_nb)
|
||||||
|
ShipmentIn.save([shipment])
|
||||||
|
else:
|
||||||
|
logger.info(
|
||||||
|
"Shipment %s cree sans lots : fees/instructions "
|
||||||
|
"non crees, voir FINTRADE_CLEANUP_NEEDED",
|
||||||
|
si_number)
|
||||||
trans_shipment.commit()
|
trans_shipment.commit()
|
||||||
successful_shipments += 1
|
if shipment.incoming_moves:
|
||||||
logger.info(f"✓ Shipment {si_number} créé avec succès")
|
successful_shipments += 1
|
||||||
|
logger.info(f"Shipment {si_number} cree avec succes")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Cette transaction échoue mais les autres continuent
|
|
||||||
error_details = {
|
error_details = {
|
||||||
'si_number': si_number,
|
'si_number': si_number,
|
||||||
'carrier_name': carrier_name,
|
'carrier_name': carrier_name,
|
||||||
@@ -330,7 +460,7 @@ class AutomationCron(ModelSQL, ModelView):
|
|||||||
}
|
}
|
||||||
failed_shipments.append(error_details)
|
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" Carrier: '{carrier_name}'")
|
||||||
logger.error(f" Agent: '{agent_name}'")
|
logger.error(f" Agent: '{agent_name}'")
|
||||||
logger.error(f" Vessel: '{vessel_name}'")
|
logger.error(f" Vessel: '{vessel_name}'")
|
||||||
@@ -339,23 +469,21 @@ class AutomationCron(ModelSQL, ModelView):
|
|||||||
if line.strip():
|
if line.strip():
|
||||||
logger.error(f" {line}")
|
logger.error(f" {line}")
|
||||||
|
|
||||||
# ---- RÉSUMÉ FINAL ----
|
|
||||||
logger.info("=" * 60)
|
logger.info("=" * 60)
|
||||||
logger.info("RÉSUMÉ DE L'EXÉCUTION")
|
logger.info("RESUME DE L'EXECUTION")
|
||||||
logger.info("=" * 60)
|
logger.info("=" * 60)
|
||||||
logger.info(f"Total de shipments à traiter : {len(rows2)}")
|
logger.info(f"Total de shipments a traiter : {len(rows2)}")
|
||||||
logger.info(f"Shipments créés avec succès : {successful_shipments}")
|
logger.info(f"Shipments crees avec succes : {successful_shipments}")
|
||||||
logger.info(f"Shipments en échec : {len(failed_shipments)}")
|
logger.info(f"Shipments en echec : {len(failed_shipments)}")
|
||||||
|
|
||||||
if failed_shipments:
|
if failed_shipments:
|
||||||
logger.info("\nDétail des échecs :")
|
logger.info("\nDetail des echecs :")
|
||||||
for i, error in enumerate(failed_shipments, 1):
|
for i, error in enumerate(failed_shipments, 1):
|
||||||
logger.info(f" {i}. SI {error['si_number']}:")
|
logger.info(f" {i}. SI {error['si_number']}:")
|
||||||
logger.info(f" Carrier: '{error['carrier_name']}'")
|
logger.info(f" Carrier: '{error['carrier_name']}'")
|
||||||
logger.info(f" Erreur: {error['error']}")
|
logger.info(f" Erreur: {error['error']}")
|
||||||
|
|
||||||
# Log supplémentaire pour debug
|
logger.info("\nAnalyse des carriers problematiques :")
|
||||||
logger.info("\nAnalyse des carriers problématiques :")
|
|
||||||
problematic_carriers = {}
|
problematic_carriers = {}
|
||||||
for error in failed_shipments:
|
for error in failed_shipments:
|
||||||
carrier = error['carrier_name']
|
carrier = error['carrier_name']
|
||||||
@@ -365,13 +493,14 @@ class AutomationCron(ModelSQL, ModelView):
|
|||||||
problematic_carriers[carrier] = 1
|
problematic_carriers[carrier] = 1
|
||||||
|
|
||||||
for carrier, count in problematic_carriers.items():
|
for carrier, count in problematic_carriers.items():
|
||||||
logger.info(f" Carrier '{carrier}' : {count} échec(s)")
|
logger.info(f" Carrier '{carrier}' : {count} echec(s)")
|
||||||
|
existing = Party.search([
|
||||||
# Vérifier si ce carrier existe dans la base
|
('name', '=', str(carrier).strip().upper())
|
||||||
existing = Party.search([('name', '=', str(carrier).strip().upper())], limit=1)
|
], limit=1)
|
||||||
if existing:
|
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:
|
else:
|
||||||
logger.info(f" → N'EXISTE PAS DANS LA BASE")
|
logger.info(" -> N'EXISTE PAS DANS LA BASE")
|
||||||
|
|
||||||
logger.info("=" * 60)
|
logger.info("=" * 60)
|
||||||
@@ -7,4 +7,5 @@ depends:
|
|||||||
xml:
|
xml:
|
||||||
automation.xml
|
automation.xml
|
||||||
freight_booking.xml
|
freight_booking.xml
|
||||||
|
booking_report.xml
|
||||||
cron.xml
|
cron.xml
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<form>
|
||||||
|
<label name="bl_number"/>
|
||||||
|
<field name="bl_number"/>
|
||||||
|
<label name="shipment"/>
|
||||||
|
<field name="shipment"/>
|
||||||
|
</form>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<tree>
|
||||||
|
<field name="bl_number"/>
|
||||||
|
<field name="status" widget="badge"
|
||||||
|
badge_colors="found:#16a34a,Found:#16a34a,error:#ef4444,Error:#ef4444"/>
|
||||||
|
<field name="shipment"/>
|
||||||
|
<field name="automation_document"/>
|
||||||
|
</tree>
|
||||||
@@ -133,6 +133,19 @@ class Account(DeactivableMixin, ModelSQL, ModelView):
|
|||||||
if number.type == 'iban':
|
if number.type == 'iban':
|
||||||
return number.number
|
return number.number
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _bic_compact(bic):
|
||||||
|
return getattr(bic, 'compact', bic)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _bic_matches(cls, first, second):
|
||||||
|
def normalize(value):
|
||||||
|
value = cls._bic_compact(value)
|
||||||
|
if len(value) == 11 and value.endswith('XXX'):
|
||||||
|
value = value[:8]
|
||||||
|
return value
|
||||||
|
return normalize(first) == normalize(second)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate(cls, accounts):
|
def validate(cls, accounts):
|
||||||
super().validate(accounts)
|
super().validate(accounts)
|
||||||
@@ -146,7 +159,7 @@ class Account(DeactivableMixin, ModelSQL, ModelView):
|
|||||||
iban = IBAN(self.iban)
|
iban = IBAN(self.iban)
|
||||||
bic = BIC(self.bank.bic)
|
bic = BIC(self.bank.bic)
|
||||||
if (iban.bic
|
if (iban.bic
|
||||||
and iban.bic != bic
|
and not self._bic_matches(iban.bic, bic)
|
||||||
and (
|
and (
|
||||||
iban.country_code != bic.country_code
|
iban.country_code != bic.country_code
|
||||||
or (iban.bank_code or iban.branch_code)
|
or (iban.bank_code or iban.branch_code)
|
||||||
|
|||||||
@@ -41,6 +41,16 @@ class BankTestCase(ModuleTestCase):
|
|||||||
bank.bic = 'foo'
|
bank.bic = 'foo'
|
||||||
bank.save()
|
bank.save()
|
||||||
|
|
||||||
|
@with_transaction()
|
||||||
|
def test_bic_matches_primary_branch(self):
|
||||||
|
"Test BIC matches main branch"
|
||||||
|
pool = Pool()
|
||||||
|
Account = pool.get('bank.account')
|
||||||
|
|
||||||
|
self.assertTrue(Account._bic_matches('CIALCHBBXXX', 'CIALCHBB'))
|
||||||
|
self.assertTrue(Account._bic_matches('CIALCHBB', 'CIALCHBBXXX'))
|
||||||
|
self.assertFalse(Account._bic_matches('CIALCHBB123', 'CIALCHBB'))
|
||||||
|
|
||||||
@with_transaction()
|
@with_transaction()
|
||||||
def test_iban_format(self):
|
def test_iban_format(self):
|
||||||
'Test IBAN format'
|
'Test IBAN format'
|
||||||
|
|||||||
@@ -2,9 +2,4 @@
|
|||||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||||
this repository contains the full copyright notices and license terms. -->
|
this repository contains the full copyright notices and license terms. -->
|
||||||
<data>
|
<data>
|
||||||
<xpath expr="//page[@id='costs']" position="inside">
|
|
||||||
<field name="before_carriages" colspan="4"/>
|
|
||||||
|
|
||||||
<field name="after_carriages" colspan="4"/>
|
|
||||||
</xpath>
|
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -295,23 +295,60 @@ class Incoming(DeactivableMixin, Workflow, ModelSQL, ModelView):
|
|||||||
@ModelView.button
|
@ModelView.button
|
||||||
@Workflow.transition('processing')
|
@Workflow.transition('processing')
|
||||||
def process(cls, documents, with_children=False):
|
def process(cls, documents, with_children=False):
|
||||||
|
logger.info(
|
||||||
|
"DOCUMENT_INCOMING_PROCESS_CLICK documents=%s with_children=%s",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
'id': document.id,
|
||||||
|
'name': document.name,
|
||||||
|
'type': document.type,
|
||||||
|
'state': document.state,
|
||||||
|
}
|
||||||
|
for document in documents
|
||||||
|
],
|
||||||
|
with_children)
|
||||||
transaction = Transaction()
|
transaction = Transaction()
|
||||||
context = transaction.context
|
context = transaction.context
|
||||||
with transaction.set_context(
|
with transaction.set_context(
|
||||||
queue_batch=context.get('queue_batch', True)):
|
queue_batch=context.get('queue_batch', True)):
|
||||||
|
logger.info(
|
||||||
|
"DOCUMENT_INCOMING_PROCESS_QUEUE documents=%s queue_batch=%s",
|
||||||
|
[document.id for document in documents],
|
||||||
|
transaction.context.get('queue_batch'))
|
||||||
cls.__queue__.proceed(documents, with_children=with_children)
|
cls.__queue__.proceed(documents, with_children=with_children)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ModelView.button
|
@ModelView.button
|
||||||
@Workflow.transition('done')
|
@Workflow.transition('done')
|
||||||
def proceed(cls, documents, with_children=False):
|
def proceed(cls, documents, with_children=False):
|
||||||
|
logger.info(
|
||||||
|
"DOCUMENT_INCOMING_PROCEED_START documents=%s with_children=%s",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
'id': document.id,
|
||||||
|
'name': document.name,
|
||||||
|
'type': document.type,
|
||||||
|
'state': document.state,
|
||||||
|
'active': document.active,
|
||||||
|
'result': str(document.result) if document.result else None,
|
||||||
|
}
|
||||||
|
for document in documents
|
||||||
|
],
|
||||||
|
with_children)
|
||||||
pool = Pool()
|
pool = Pool()
|
||||||
Attachment = pool.get('ir.attachment')
|
Attachment = pool.get('ir.attachment')
|
||||||
results = defaultdict(list)
|
results = defaultdict(list)
|
||||||
attachments = []
|
attachments = []
|
||||||
for document in documents:
|
for document in documents:
|
||||||
if document.result or not document.active:
|
if document.result or not document.active:
|
||||||
|
logger.info(
|
||||||
|
"DOCUMENT_INCOMING_PROCEED_SKIP document=%s "
|
||||||
|
"result=%s active=%s",
|
||||||
|
document.id, document.result, document.active)
|
||||||
continue
|
continue
|
||||||
|
logger.info(
|
||||||
|
"DOCUMENT_INCOMING_PROCEED_DISPATCH document=%s method=%s",
|
||||||
|
document.id, f'_process_{document.type}')
|
||||||
document.result = getattr(document, f'_process_{document.type}')()
|
document.result = getattr(document, f'_process_{document.type}')()
|
||||||
results[document.result.__class__].append(document.result)
|
results[document.result.__class__].append(document.result)
|
||||||
attachment = Attachment(
|
attachment = Attachment(
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ from trytond.modules.document_incoming.exceptions import (
|
|||||||
DocumentIncomingProcessError)
|
DocumentIncomingProcessError)
|
||||||
from trytond.pool import Pool, PoolMeta
|
from trytond.pool import Pool, PoolMeta
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class IncomingConfiguration(metaclass=PoolMeta):
|
class IncomingConfiguration(metaclass=PoolMeta):
|
||||||
@@ -18,6 +21,13 @@ class IncomingConfiguration(metaclass=PoolMeta):
|
|||||||
class Incoming(metaclass=PoolMeta):
|
class Incoming(metaclass=PoolMeta):
|
||||||
__name__ = 'document.incoming'
|
__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
|
@classmethod
|
||||||
def __setup__(cls):
|
def __setup__(cls):
|
||||||
super().__setup__()
|
super().__setup__()
|
||||||
@@ -25,21 +35,32 @@ class Incoming(metaclass=PoolMeta):
|
|||||||
('weight_report', "Weight Report"))
|
('weight_report', "Weight Report"))
|
||||||
cls.type.selection.append(
|
cls.type.selection.append(
|
||||||
('controller', "Controller"))
|
('controller', "Controller"))
|
||||||
|
cls._order = [('create_date', 'DESC')]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_results(cls):
|
def _get_results(cls):
|
||||||
return super()._get_results() | {'automation.document'}
|
return super()._get_results() | {'automation.document'}
|
||||||
|
|
||||||
def _process_weight_report(self):
|
def _process_weight_report(self):
|
||||||
|
logger.info(
|
||||||
|
"DOCUMENT_INCOMING_WR_START incoming=%s name=%s type=%s "
|
||||||
|
"data_size=%s",
|
||||||
|
getattr(self, 'id', None), getattr(self, 'name', None),
|
||||||
|
getattr(self, 'type', None),
|
||||||
|
len(getattr(self, 'data', None) or b""))
|
||||||
WR = Pool().get('automation.document')
|
WR = Pool().get('automation.document')
|
||||||
wr = WR()
|
wr = WR()
|
||||||
wr.document = self.id
|
wr.document = self.id
|
||||||
wr.type = 'weight_report'
|
wr.type = 'weight_report'
|
||||||
wr.state = 'draft'
|
wr.state = 'draft'
|
||||||
WR.save([wr])
|
WR.save([wr])
|
||||||
|
logger.info(
|
||||||
|
"DOCUMENT_INCOMING_WR_AUTOMATION_CREATED incoming=%s "
|
||||||
|
"automation_document=%s",
|
||||||
|
getattr(self, 'id', None), getattr(wr, 'id', None))
|
||||||
WR.run_ocr([wr])
|
WR.run_ocr([wr])
|
||||||
WR.run_metadata([wr])
|
WR.run_metadata([wr])
|
||||||
|
WR.run_pipeline([wr])
|
||||||
return wr
|
return wr
|
||||||
|
|
||||||
def _process_controller(self):
|
def _process_controller(self):
|
||||||
@@ -54,6 +75,25 @@ class Incoming(metaclass=PoolMeta):
|
|||||||
|
|
||||||
return wr
|
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
|
# @property
|
||||||
# def supplier_invoice_company(self):
|
# def supplier_invoice_company(self):
|
||||||
# pass
|
# 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="inherit" ref="document_incoming.document_incoming_configuration_view_form"/>
|
||||||
<field name="name">document_incoming_configuration_form</field>
|
<field name="name">document_incoming_configuration_form</field>
|
||||||
</record>
|
</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>
|
</data>
|
||||||
</tryton>
|
</tryton>
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
version=7.2.0
|
version=7.2.0
|
||||||
depends:
|
depends:
|
||||||
document_incoming
|
document_incoming
|
||||||
|
automation
|
||||||
ir
|
ir
|
||||||
party
|
party
|
||||||
xml:
|
xml:
|
||||||
document.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")
|
lot_name = fields.Char("Lot")
|
||||||
number = fields.Char("Number", readonly=True)
|
number = fields.Char("Number", readonly=True)
|
||||||
lot_qt = fields.Float("Quantity",required=False)
|
lot_qt = fields.Float("Quantity", required=False,
|
||||||
lot_unit = fields.Many2One('product.uom', "Unit",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_product = fields.Many2One('product.product', "Product")
|
||||||
lot_type = fields.Selection([
|
lot_type = fields.Selection([
|
||||||
('virtual', 'Open'),
|
('virtual', 'Open'),
|
||||||
@@ -486,7 +492,6 @@ class Lot(ModelSQL, ModelView):
|
|||||||
st = state_id
|
st = state_id
|
||||||
else:
|
else:
|
||||||
st = self.lot_state.id
|
st = self.lot_state.id
|
||||||
logger.info("GET_HIST_QT:%s",st)
|
|
||||||
lot = [e for e in self.lot_hist if e.quantity_type.id == st][0]
|
lot = [e for e in self.lot_hist if e.quantity_type.id == st][0]
|
||||||
qt = round(lot.quantity,5)
|
qt = round(lot.quantity,5)
|
||||||
gross_qt = round(lot.gross_quantity,5)
|
gross_qt = round(lot.gross_quantity,5)
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
<notebook colspan="4">
|
<notebook colspan="4">
|
||||||
<page string="General" id="general">
|
<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_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>
|
||||||
<page string="Accounting" id="accounting">
|
<page string="Accounting" id="accounting">
|
||||||
<field name="pivot" widget="html_viewer" height="600" colspan="4"/>
|
<field name="pivot" widget="html_viewer" height="600" colspan="4"/>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<form col="4">
|
<form col="4">
|
||||||
<label name="lot"/>
|
<label name="lot"/>
|
||||||
<field name="lot"/>
|
<field name="lot" readonly="1"/>
|
||||||
<label name="quantity_type"/>
|
<label name="quantity_type"/>
|
||||||
<field name="quantity_type"/>
|
<field name="quantity_type" readonly="1"/>
|
||||||
<label name="quantity"/>
|
<label name="quantity"/>
|
||||||
<field name="quantity"/>
|
<field name="quantity" readonly="1"/>
|
||||||
<label name="gross_quantity"/>
|
<label name="gross_quantity"/>
|
||||||
<field name="gross_quantity"/>
|
<field name="gross_quantity" readonly="1"/>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<tree editable="1">
|
<tree>
|
||||||
<field name="lot"/>
|
<field name="lot"/>
|
||||||
<field name="quantity_type"/>
|
<field name="quantity_type"/>
|
||||||
<field name="quantity"/>
|
<field name="quantity"/>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<tree editable="1">
|
<tree>
|
||||||
<field name="lot"/>
|
<field name="lot"/>
|
||||||
<field name="quantity_type"/>
|
<field name="quantity_type"/>
|
||||||
<field name="quantity"/>
|
<field name="quantity"/>
|
||||||
|
|||||||
@@ -3,9 +3,6 @@
|
|||||||
this repository contains the full copyright notices and license terms. -->
|
this repository contains the full copyright notices and license terms. -->
|
||||||
<tryton>
|
<tryton>
|
||||||
<data grouped="1">
|
<data grouped="1">
|
||||||
<record model="ir.message" id="msg_price_value_unique">
|
|
||||||
<field name="text">Only one price value is allowed for each price index and price date.</field>
|
|
||||||
</record>
|
|
||||||
<record model="ir.message" id="msg_party_code_unique">
|
<record model="ir.message" id="msg_party_code_unique">
|
||||||
<field name="text">The code on party must be unique.</field>
|
<field name="text">The code on party must be unique.</field>
|
||||||
</record>
|
</record>
|
||||||
|
|||||||
@@ -38,6 +38,15 @@ class Price(
|
|||||||
price_type = fields.Many2One('price.fixtype', "Fixation type")
|
price_type = fields.Many2One('price.fixtype', "Fixation type")
|
||||||
price_unit = fields.Many2One('product.uom', "Unit")
|
price_unit = fields.Many2One('product.uom', "Unit")
|
||||||
price_currency = fields.Many2One('currency.currency', "Currency")
|
price_currency = fields.Many2One('currency.currency', "Currency")
|
||||||
|
enable_linked_currency = fields.Boolean("Linked currency")
|
||||||
|
linked_currency = fields.Many2One('currency.linked', "Linked currency",
|
||||||
|
domain=[
|
||||||
|
('currency', '=', Eval('price_currency')),
|
||||||
|
],
|
||||||
|
states={
|
||||||
|
'invisible': ~Eval('enable_linked_currency'),
|
||||||
|
'required': Eval('enable_linked_currency'),
|
||||||
|
}, depends=['enable_linked_currency', 'price_currency'])
|
||||||
price_area = fields.Many2One('price.area',"Market area")
|
price_area = fields.Many2One('price.area',"Market area")
|
||||||
price_calendar = fields.Many2One('price.calendar',"Calendar")
|
price_calendar = fields.Many2One('price.calendar',"Calendar")
|
||||||
price_values = fields.One2Many('price.price_value', 'price', "Prices Values")
|
price_values = fields.One2Many('price.price_value', 'price', "Prices Values")
|
||||||
@@ -49,16 +58,28 @@ class Price(
|
|||||||
Uom = Pool().get('product.uom')
|
Uom = Pool().get('product.uom')
|
||||||
return round(Decimal(Uom.compute_qty(self.price_unit, float(self.price_ct_size * nb_ct), unit)),4)
|
return round(Decimal(Uom.compute_qty(self.price_unit, float(self.price_ct_size * nb_ct), unit)),4)
|
||||||
|
|
||||||
|
def _price_in_main_currency(self, price):
|
||||||
|
price = Decimal(str(price or 0))
|
||||||
|
if self.enable_linked_currency and self.linked_currency:
|
||||||
|
factor = Decimal(self.linked_currency.factor or 0)
|
||||||
|
if factor:
|
||||||
|
price *= factor
|
||||||
|
return price
|
||||||
|
|
||||||
def get_price_per_qt(self,price,unit,currency):
|
def get_price_per_qt(self,price,unit,currency):
|
||||||
price_qt = float(0)
|
price_qt = Decimal(0)
|
||||||
Uom = Pool().get('product.uom')
|
Uom = Pool().get('product.uom')
|
||||||
Currency = Pool().get('currency.currency')
|
Currency = Pool().get('currency.currency')
|
||||||
|
price = self._price_in_main_currency(price)
|
||||||
|
unit_factor = Decimal(str(
|
||||||
|
Uom.compute_qty(unit, float(1), self.price_unit) or 0))
|
||||||
if currency != self.price_currency:
|
if currency != self.price_currency:
|
||||||
rates = Currency._get_rate([self.price_currency])
|
rates = Currency._get_rate([self.price_currency])
|
||||||
if rates[self.price_currency.id]:
|
if rates[self.price_currency.id]:
|
||||||
price_qt = float(price) * Uom.compute_qty(unit, float(1), self.price_unit) * float(rates[self.price_currency.id])
|
price_qt = price * unit_factor * Decimal(str(
|
||||||
|
rates[self.price_currency.id]))
|
||||||
else:
|
else:
|
||||||
price_qt = float(price) * Uom.compute_qty(unit, float(1), self.price_unit)
|
price_qt = price * unit_factor
|
||||||
return round(price_qt,4)
|
return round(price_qt,4)
|
||||||
|
|
||||||
def get_amount_nb_ct(self,price,nb_ct,unit,currency):
|
def get_amount_nb_ct(self,price,nb_ct,unit,currency):
|
||||||
@@ -68,7 +89,7 @@ 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))
|
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)
|
return round(amount,4)
|
||||||
|
|
||||||
def get_price(self,dt,unit,currency,last=False):
|
def get_price(self,dt,unit,currency,last=False, relative_last=False):
|
||||||
price = float(0)
|
price = float(0)
|
||||||
PV = Pool().get('price.price_value')
|
PV = Pool().get('price.price_value')
|
||||||
if self.price_values:
|
if self.price_values:
|
||||||
@@ -76,6 +97,11 @@ class Price(
|
|||||||
pv = PV.search([('price','=',self.id),('price_date','=',dt)])
|
pv = PV.search([('price','=',self.id),('price_date','=',dt)])
|
||||||
if not pv and last:
|
if not pv and last:
|
||||||
pv = PV.search([('price','=',self.id)],order=[('price_date', 'DESC')])
|
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:
|
if pv:
|
||||||
price = self.get_price_per_qt(pv[0].price_value,unit,currency)
|
price = self.get_price_per_qt(pv[0].price_value,unit,currency)
|
||||||
return round(price,4)
|
return round(price,4)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from trytond.i18n import gettext
|
|||||||
from trytond.model import (
|
from trytond.model import (
|
||||||
DeactivableMixin, Index, ModelSQL, ModelView, MultiValueMixin, Unique,
|
DeactivableMixin, Index, ModelSQL, ModelView, MultiValueMixin, Unique,
|
||||||
ValueMixin, convert_from, fields, sequence_ordered)
|
ValueMixin, convert_from, fields, sequence_ordered)
|
||||||
from trytond.model.exceptions import AccessError, ValidationError
|
from trytond.model.exceptions import AccessError
|
||||||
from trytond.pool import Pool
|
from trytond.pool import Pool
|
||||||
from trytond.pyson import Bool, Eval
|
from trytond.pyson import Bool, Eval
|
||||||
from trytond.tools import is_full_text, lstrip_wildcard
|
from trytond.tools import is_full_text, lstrip_wildcard
|
||||||
@@ -38,30 +38,9 @@ class PriceValue(
|
|||||||
price_value = fields.Float("Price value")
|
price_value = fields.Float("Price value")
|
||||||
open_price = fields.Float("Open price")
|
open_price = fields.Float("Open price")
|
||||||
low_price = fields.Float("Low price")
|
low_price = fields.Float("Low price")
|
||||||
|
mid_price = fields.Float("Mid price")
|
||||||
high_price = fields.Float("High price")
|
high_price = fields.Float("High price")
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def validate(cls, price_values):
|
|
||||||
super().validate(price_values)
|
|
||||||
cls.check_unique_price_date(price_values)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def check_unique_price_date(cls, price_values):
|
|
||||||
domains = []
|
|
||||||
for price_value in price_values:
|
|
||||||
if not price_value.price or not price_value.price_date:
|
|
||||||
continue
|
|
||||||
domain = [
|
|
||||||
('price', '=', price_value.price.id),
|
|
||||||
('price_date', '=', price_value.price_date),
|
|
||||||
]
|
|
||||||
if price_value.id:
|
|
||||||
domain.append(('id', '!=', price_value.id))
|
|
||||||
domains.append(['AND'] + domain)
|
|
||||||
if domains and cls.search(['OR'] + domains, limit=1):
|
|
||||||
raise ValidationError(
|
|
||||||
gettext('price.msg_price_value_unique'))
|
|
||||||
|
|
||||||
def get_price_index(self, name):
|
def get_price_index(self, name):
|
||||||
if self.price:
|
if self.price:
|
||||||
return self.price.price_index
|
return self.price.price_index
|
||||||
@@ -79,6 +58,7 @@ class PriceValueReport(
|
|||||||
r_price_value = fields.Float("Price")
|
r_price_value = fields.Float("Price")
|
||||||
r_open_price = fields.Float("Open price")
|
r_open_price = fields.Float("Open price")
|
||||||
r_low_price = fields.Float("Low price")
|
r_low_price = fields.Float("Low price")
|
||||||
|
r_mid_price = fields.Float("Mid price")
|
||||||
r_high_price = fields.Float("High price")
|
r_high_price = fields.Float("High price")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -110,6 +90,7 @@ class PriceValueReport(
|
|||||||
pv.price_value.as_('r_price_value'),
|
pv.price_value.as_('r_price_value'),
|
||||||
pv.open_price.as_('r_open_price'),
|
pv.open_price.as_('r_open_price'),
|
||||||
pv.low_price.as_('r_low_price'),
|
pv.low_price.as_('r_low_price'),
|
||||||
|
pv.mid_price.as_('r_mid_price'),
|
||||||
pv.high_price.as_('r_high_price'),
|
pv.high_price.as_('r_high_price'),
|
||||||
where=wh)
|
where=wh)
|
||||||
|
|
||||||
@@ -140,6 +121,7 @@ class PriceValueReport2(
|
|||||||
r_price_value = fields.Float("Price")
|
r_price_value = fields.Float("Price")
|
||||||
r_open_price = fields.Float("Open price")
|
r_open_price = fields.Float("Open price")
|
||||||
r_low_price = fields.Float("Low price")
|
r_low_price = fields.Float("Low price")
|
||||||
|
r_mid_price = fields.Float("Mid price")
|
||||||
r_high_price = fields.Float("High 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_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_high = fields.Float("Naph F Med h", digits=(1,2), help="Naphta Fob PAAAI00 high")
|
||||||
@@ -195,6 +177,7 @@ class PriceValueReport2(
|
|||||||
Max(pv.price_value).as_('r_price_value'),
|
Max(pv.price_value).as_('r_price_value'),
|
||||||
Max(pv.open_price).as_('r_open_price'),
|
Max(pv.open_price).as_('r_open_price'),
|
||||||
Max(pv.low_price).as_('r_low_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(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.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),else_=0)).as_('r_naphta_fob_high'),
|
||||||
|
|||||||
@@ -14,6 +14,10 @@
|
|||||||
<field name="price_unit"/>
|
<field name="price_unit"/>
|
||||||
<label name="price_currency"/>
|
<label name="price_currency"/>
|
||||||
<field name="price_currency"/>
|
<field name="price_currency"/>
|
||||||
|
<label name="enable_linked_currency"/>
|
||||||
|
<field name="enable_linked_currency"/>
|
||||||
|
<label name="linked_currency"/>
|
||||||
|
<field name="linked_currency"/>
|
||||||
<label name="price_area"/>
|
<label name="price_area"/>
|
||||||
<field name="price_area"/>
|
<field name="price_area"/>
|
||||||
<label name="price_calendar"/>
|
<label name="price_calendar"/>
|
||||||
|
|||||||
@@ -5,5 +5,6 @@
|
|||||||
<field name="r_price_date"/>
|
<field name="r_price_date"/>
|
||||||
<field name="r_price_value"/>
|
<field name="r_price_value"/>
|
||||||
<field name="r_low_price"/>
|
<field name="r_low_price"/>
|
||||||
|
<field name="r_mid_price"/>
|
||||||
<field name="r_high_price"/>
|
<field name="r_high_price"/>
|
||||||
</tree>
|
</tree>
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
<field name="open_price"/>
|
<field name="open_price"/>
|
||||||
<label name="low_price"/>
|
<label name="low_price"/>
|
||||||
<field name="low_price"/>
|
<field name="low_price"/>
|
||||||
|
<label name="mid_price"/>
|
||||||
|
<field name="mid_price"/>
|
||||||
<label name="high_price"/>
|
<label name="high_price"/>
|
||||||
<field name="high_price"/>
|
<field name="high_price"/>
|
||||||
</group>
|
</group>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ this repository contains the full copyright notices and license terms. -->
|
|||||||
<field name="price_value" fill="1" empty="0"/>
|
<field name="price_value" fill="1" empty="0"/>
|
||||||
<field name="open_price" fill="1" empty="0"/>
|
<field name="open_price" fill="1" empty="0"/>
|
||||||
<field name="low_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"/>
|
<field name="high_price" fill="1" empty="0"/>
|
||||||
</y>
|
</y>
|
||||||
</graph>
|
</graph>
|
||||||
|
|||||||
@@ -5,5 +5,6 @@
|
|||||||
<field name="price_value"/>
|
<field name="price_value"/>
|
||||||
<field name="open_price"/>
|
<field name="open_price"/>
|
||||||
<field name="low_price"/>
|
<field name="low_price"/>
|
||||||
|
<field name="mid_price"/>
|
||||||
<field name="high_price"/>
|
<field name="high_price"/>
|
||||||
</tree>
|
</tree>
|
||||||
|
|||||||
@@ -5,5 +5,6 @@
|
|||||||
<field name="price_value"/>
|
<field name="price_value"/>
|
||||||
<field name="open_price"/>
|
<field name="open_price"/>
|
||||||
<field name="low_price"/>
|
<field name="low_price"/>
|
||||||
|
<field name="mid_price"/>
|
||||||
<field name="high_price"/>
|
<field name="high_price"/>
|
||||||
</tree>
|
</tree>
|
||||||
|
|||||||
@@ -519,6 +519,8 @@ class Purchase(
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def taxable_lines(self):
|
def taxable_lines(self):
|
||||||
|
Date = Pool().get('ir.date')
|
||||||
|
tax_date = getattr(self, 'purchase_date', None) or Date.today()
|
||||||
taxable_lines = []
|
taxable_lines = []
|
||||||
# In case we're called from an on_change we have to use some sensible
|
# In case we're called from an on_change we have to use some sensible
|
||||||
# defaults
|
# defaults
|
||||||
@@ -529,7 +531,7 @@ class Purchase(
|
|||||||
getattr(line, 'taxes', None) or [],
|
getattr(line, 'taxes', None) or [],
|
||||||
getattr(line, 'unit_price', None) or Decimal(0),
|
getattr(line, 'unit_price', None) or Decimal(0),
|
||||||
getattr(line, 'quantity', None) or 0,
|
getattr(line, 'quantity', None) or 0,
|
||||||
None,
|
tax_date,
|
||||||
))
|
))
|
||||||
return taxable_lines
|
return taxable_lines
|
||||||
|
|
||||||
@@ -953,19 +955,22 @@ class Purchase(
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ModelView.button
|
@ModelView.button
|
||||||
@Workflow.transition('confirmed')
|
# @Workflow.transition('confirmed')
|
||||||
@set_employee('confirmed_by')
|
# @set_employee('confirmed_by')
|
||||||
def confirm(cls, purchases):
|
def confirm(cls, purchases):
|
||||||
pool = Pool()
|
# Confirm is intentionally disabled: clicking the button must not move
|
||||||
Line = pool.get('purchase.line')
|
# the purchase to Confirmed or trigger downstream processing.
|
||||||
transaction = Transaction()
|
pass
|
||||||
context = transaction.context
|
# pool = Pool()
|
||||||
cls.set_purchase_date(purchases)
|
# Line = pool.get('purchase.line')
|
||||||
|
# cls.set_purchase_date(purchases)
|
||||||
cls.write(purchases, {'state': 'confirmed'})
|
#
|
||||||
lines = list(sum((p.lines for p in purchases), ()))
|
# cls.write(purchases, {'state': 'confirmed'})
|
||||||
Line._validate(lines, ['unit_price'])
|
# lines = list(sum((p.lines for p in purchases), ()))
|
||||||
|
# Line._validate(lines, ['unit_price'])
|
||||||
|
#
|
||||||
|
# transaction = Transaction()
|
||||||
|
# context = transaction.context
|
||||||
# cls.store_cache(purchases)
|
# cls.store_cache(purchases)
|
||||||
# for process_after, sub_purchases in groupby(
|
# for process_after, sub_purchases in groupby(
|
||||||
# purchases, lambda p: p.process_after):
|
# purchases, lambda p: p.process_after):
|
||||||
@@ -1013,16 +1018,22 @@ class Purchase(
|
|||||||
for purchase in purchases:
|
for purchase in purchases:
|
||||||
logger.info("PROCESS_INVOICE:%s",action)
|
logger.info("PROCESS_INVOICE:%s",action)
|
||||||
invoice = purchase.create_invoice(lots,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':
|
if action == 'prov':
|
||||||
invoice.reference = 'Provisional'
|
invoice.reference = 'Provisional'
|
||||||
elif action == 'service':
|
elif action == 'service':
|
||||||
invoice.reference = 'Service'
|
invoice.reference = 'Service'
|
||||||
else:
|
else:
|
||||||
invoice.reference = 'Final'
|
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
|
@classmethod
|
||||||
def _save_invoice(cls, invoices,prepayment=None):
|
def _save_invoice(cls, invoices,prepayment=None):
|
||||||
@@ -1851,6 +1862,83 @@ class Line(sequence_ordered(), ModelSQL, ModelView):
|
|||||||
def on_change_with_currency(self, name=None):
|
def on_change_with_currency(self, name=None):
|
||||||
return self.purchase.currency if self.purchase else 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):
|
def get_invoice_line(self,lots=None,action=None):
|
||||||
'Return a list of invoice line for purchase line'
|
'Return a list of invoice line for purchase line'
|
||||||
pool = Pool()
|
pool = Pool()
|
||||||
@@ -1930,21 +2018,9 @@ class Line(sequence_ordered(), ModelSQL, ModelView):
|
|||||||
invoice_line.unit_price = self.unit_price
|
invoice_line.unit_price = self.unit_price
|
||||||
invoice_line.product = self.product
|
invoice_line.product = self.product
|
||||||
invoice_line.stock_moves = []
|
invoice_line.stock_moves = []
|
||||||
Fee = Pool().get('fee.fee')
|
lines.extend(
|
||||||
fee = Fee.search(['purchase','=',self.purchase.id])
|
self._get_service_fee_invoice_lines(invoice_line, l))
|
||||||
if fee:
|
continue
|
||||||
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.append(invoice_line)
|
lines.append(invoice_line)
|
||||||
logger.info("GETINVLINE:%s",self.product.type)
|
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="view" ref="purchase_view_form"/>
|
||||||
<field name="act_window" ref="act_purchase_form"/>
|
<field name="act_window" ref="act_purchase_form"/>
|
||||||
</record>
|
</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"
|
<record model="ir.action.act_window.domain"
|
||||||
id="act_purchase_form_domain_quotation">
|
id="act_purchase_form_domain_quotation">
|
||||||
<field name="name">Validation</field>
|
<field name="name">Validated</field>
|
||||||
<field name="sequence" eval="20"/>
|
<field name="sequence" eval="10"/>
|
||||||
<field name="domain" eval="['AND',('state', '=', 'quotation'),('line_type', '=', 'goods')]" pyson="1"/>
|
<field name="domain" eval="['AND',('state', '=', 'quotation'),('line_type', '=', 'goods')]" pyson="1"/>
|
||||||
<field name="count" eval="True"/>
|
<field name="count" eval="True"/>
|
||||||
<field name="act_window" ref="act_purchase_form"/>
|
<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"
|
<record model="ir.action.act_window.domain"
|
||||||
id="act_purchase_form_domain_confirmed">
|
id="act_purchase_form_domain_confirmed">
|
||||||
<field name="name">Confirmed</field>
|
<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="domain" eval="['AND',('state', '=', 'confirmed'),('line_type', '=', 'goods')]" pyson="1"/>
|
||||||
<field name="count" eval="True"/>
|
<field name="count" eval="True"/>
|
||||||
<field name="act_window" ref="act_purchase_form"/>
|
<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"
|
<record model="ir.action.act_window.domain"
|
||||||
id="act_purchase_form_domain_processing">
|
id="act_purchase_form_domain_processing">
|
||||||
<field name="name">Processing</field>
|
<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="domain" eval="['AND',('state', '=', 'processing'),('line_type', '=', 'goods')]" pyson="1"/>
|
||||||
<field name="count" eval="True"/>
|
<field name="count" eval="True"/>
|
||||||
<field name="act_window" ref="act_purchase_form"/>
|
<field name="act_window" ref="act_purchase_form"/>
|
||||||
</record>
|
</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"
|
<record model="ir.action.act_window.domain"
|
||||||
id="act_purchase_form_domain_exception">
|
id="act_purchase_form_domain_exception">
|
||||||
<field name="name">Exception</field>
|
<field name="name">Exception</field>
|
||||||
<field name="sequence" eval="50"/>
|
<field name="sequence" eval="60"/>
|
||||||
<field name="domain"
|
<field name="domain"
|
||||||
eval="['OR', ('invoice_state', '=', 'exception'), ('shipment_state', '=', 'exception')]"
|
eval="['OR', ('invoice_state', '=', 'exception'), ('shipment_state', '=', 'exception')]"
|
||||||
pyson="1"/>
|
pyson="1"/>
|
||||||
@@ -153,7 +153,6 @@ this repository contains the full copyright notices and license terms. -->
|
|||||||
action="act_purchase_form"
|
action="act_purchase_form"
|
||||||
sequence="10"
|
sequence="10"
|
||||||
id="menu_purchase_form"/>
|
id="menu_purchase_form"/>
|
||||||
|
|
||||||
<record model="ir.action.act_window" id="act_purchase_invoice_relate">
|
<record model="ir.action.act_window" id="act_purchase_invoice_relate">
|
||||||
<field name="name">Purchases</field>
|
<field name="name">Purchases</field>
|
||||||
<field name="res_model">purchase.purchase</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_invoice_exception" icon="tryton-forward"/>
|
||||||
<button name="handle_shipment_exception" icon="tryton-forward"/>
|
<button name="handle_shipment_exception" icon="tryton-forward"/>
|
||||||
<button name="confirm" icon="tryton-ok"/>
|
<button name="confirm" icon="tryton-ok"/>
|
||||||
<button name="process"/>
|
<button name="process" invisible="1"/>
|
||||||
<button name="manual_invoice" icon="tryton-forward"/>
|
<button name="manual_invoice" icon="tryton-forward"/>
|
||||||
</group>
|
</group>
|
||||||
<field name="party_lang" invisible="1" colspan="6"/>
|
<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 file is part of Tryton. The COPYRIGHT file at the top level of
|
||||||
this repository contains the full copyright notices and license terms. -->
|
this repository contains the full copyright notices and license terms. -->
|
||||||
<data>
|
<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>
|
</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 (
|
from . import (
|
||||||
account,
|
account,
|
||||||
|
configuration,
|
||||||
purchase,
|
purchase,
|
||||||
sale,
|
sale,
|
||||||
global_reporting,
|
global_reporting,
|
||||||
|
ctrm_reporting,
|
||||||
stock,
|
stock,
|
||||||
derivative,
|
derivative,
|
||||||
lot,
|
lot,
|
||||||
@@ -34,6 +36,8 @@ from . import (
|
|||||||
backtoback,
|
backtoback,
|
||||||
service,
|
service,
|
||||||
invoice,
|
invoice,
|
||||||
|
duplicate,
|
||||||
|
coffee,
|
||||||
)
|
)
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
@@ -56,15 +60,75 @@ def register():
|
|||||||
lc.LCMessage,
|
lc.LCMessage,
|
||||||
lc.CreateLCStart,
|
lc.CreateLCStart,
|
||||||
global_reporting.GRConfiguration,
|
global_reporting.GRConfiguration,
|
||||||
|
ctrm_reporting.CTRMPhysicalPosition,
|
||||||
|
ctrm_reporting.CTRMPhysicalPositionContext,
|
||||||
|
ctrm_reporting.CTRMFinancialPosition,
|
||||||
|
ctrm_reporting.CTRMFinancialPositionContext,
|
||||||
|
ctrm_reporting.CTRMNetPosition,
|
||||||
|
ctrm_reporting.CTRMNetPositionContext,
|
||||||
|
ctrm_reporting.CTRMOpenPosition,
|
||||||
|
ctrm_reporting.CTRMOpenPositionContext,
|
||||||
|
ctrm_reporting.CTRMLongShortProductStrategy,
|
||||||
|
ctrm_reporting.CTRMLongShortCommodityDirection,
|
||||||
|
ctrm_reporting.CTRMLongShortPeriodProduct,
|
||||||
|
ctrm_reporting.CTRMLongShortPricingStatus,
|
||||||
|
ctrm_reporting.CTRMLongShortDetail,
|
||||||
|
ctrm_reporting.CTRMPositionExposure,
|
||||||
|
ctrm_reporting.CTRMPositionExposureContext,
|
||||||
|
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.ImportPricesStart,
|
||||||
pricing.ImportPricesResult,
|
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')
|
module='purchase_trade', type_='model')
|
||||||
|
Pool.register(
|
||||||
|
configuration.PurchaseConfiguration,
|
||||||
|
module='purchase', type_='model')
|
||||||
|
Pool.register(
|
||||||
|
configuration.SaleConfiguration,
|
||||||
|
module='sale', type_='model')
|
||||||
Pool.register(
|
Pool.register(
|
||||||
incoming.ImportSwift,
|
incoming.ImportSwift,
|
||||||
incoming.PrepareDocuments,
|
incoming.PrepareDocuments,
|
||||||
incoming.AnalyzeConditions,
|
incoming.AnalyzeConditions,
|
||||||
lc.CreateLCWizard,
|
lc.CreateLCWizard,
|
||||||
pricing.ImportPrices,
|
pricing.ImportPrices,
|
||||||
|
duplicate.TradeCustomDuplicate,
|
||||||
module='purchase_trade', type_='wizard'
|
module='purchase_trade', type_='wizard'
|
||||||
)
|
)
|
||||||
Pool.register(
|
Pool.register(
|
||||||
@@ -86,6 +150,15 @@ def register():
|
|||||||
party.PartyExecutionPlace,
|
party.PartyExecutionPlace,
|
||||||
payment_term.PaymentTerm,
|
payment_term.PaymentTerm,
|
||||||
payment_term.PaymentTermLine,
|
payment_term.PaymentTermLine,
|
||||||
|
stock.CharterRateType,
|
||||||
|
stock.CharterConditionRate,
|
||||||
|
stock.CharterConditionLaytimeStartRule,
|
||||||
|
stock.CharterCondition,
|
||||||
|
stock.CharterParty,
|
||||||
|
purchase.ContractClause,
|
||||||
|
purchase.ContractTemplate,
|
||||||
|
purchase.ContractTemplateLine,
|
||||||
|
purchase.ContractClauseSelection,
|
||||||
purchase.Purchase,
|
purchase.Purchase,
|
||||||
purchase.Line,
|
purchase.Line,
|
||||||
purchase.Estimated,
|
purchase.Estimated,
|
||||||
@@ -104,6 +177,7 @@ def register():
|
|||||||
open_position.OpenPositionContext,
|
open_position.OpenPositionContext,
|
||||||
optional.OptionalScenario,
|
optional.OptionalScenario,
|
||||||
fee.Fee,
|
fee.Fee,
|
||||||
|
fee.FeeRule,
|
||||||
fee.FeeLots,
|
fee.FeeLots,
|
||||||
purchase.FeeLots,
|
purchase.FeeLots,
|
||||||
valuation.Valuation,
|
valuation.Valuation,
|
||||||
@@ -111,6 +185,9 @@ def register():
|
|||||||
valuation.ValuationDyn,
|
valuation.ValuationDyn,
|
||||||
valuation.ValuationReport,
|
valuation.ValuationReport,
|
||||||
valuation.ValuationReportContext,
|
valuation.ValuationReportContext,
|
||||||
|
valuation.ValuationProcessDimension,
|
||||||
|
valuation.ValuationProcessStart,
|
||||||
|
valuation.ValuationProcessResult,
|
||||||
derivative.Derivative,
|
derivative.Derivative,
|
||||||
derivative.DerivativeMatch,
|
derivative.DerivativeMatch,
|
||||||
derivative.MatchWizardStart,
|
derivative.MatchWizardStart,
|
||||||
@@ -121,12 +198,14 @@ def register():
|
|||||||
forex.ForexCoverPhysicalContract,
|
forex.ForexCoverPhysicalContract,
|
||||||
forex.PForex,
|
forex.PForex,
|
||||||
forex.ForexBI,
|
forex.ForexBI,
|
||||||
|
purchase.PurchasePnlGraph,
|
||||||
purchase.PnlBI,
|
purchase.PnlBI,
|
||||||
purchase.PositionBI,
|
purchase.PositionBI,
|
||||||
stock.Move,
|
stock.Move,
|
||||||
stock.Location,
|
stock.Location,
|
||||||
stock.InvoiceLine,
|
stock.InvoiceLine,
|
||||||
stock.ShipmentIn,
|
stock.ShipmentIn,
|
||||||
|
stock.ShipmentControllerEmailStart,
|
||||||
stock.ShipmentWR,
|
stock.ShipmentWR,
|
||||||
stock.ShipmentInternal,
|
stock.ShipmentInternal,
|
||||||
stock.ShipmentOut,
|
stock.ShipmentOut,
|
||||||
@@ -144,6 +223,9 @@ def register():
|
|||||||
lot.LotReport,
|
lot.LotReport,
|
||||||
lot.LotContext,
|
lot.LotContext,
|
||||||
lot.LotShippingStart,
|
lot.LotShippingStart,
|
||||||
|
lot.LotPlanTransportStart,
|
||||||
|
lot.LotPlanTransportLine,
|
||||||
|
lot.LotMarkFinishedStart,
|
||||||
lot.LotMatchingStart,
|
lot.LotMatchingStart,
|
||||||
lot.LotWeighingStart,
|
lot.LotWeighingStart,
|
||||||
lot.LotAddLot,
|
lot.LotAddLot,
|
||||||
@@ -157,6 +239,8 @@ def register():
|
|||||||
lot.LotWeighingLot,
|
lot.LotWeighingLot,
|
||||||
lot.ContractsStart,
|
lot.ContractsStart,
|
||||||
lot.ContractDetail,
|
lot.ContractDetail,
|
||||||
|
lot.MirrorStart,
|
||||||
|
lot.MirrorLine,
|
||||||
lot.LotFCR,
|
lot.LotFCR,
|
||||||
lot.LotMove,
|
lot.LotMove,
|
||||||
lot.LotAccountingGraph,
|
lot.LotAccountingGraph,
|
||||||
@@ -173,6 +257,7 @@ def register():
|
|||||||
purchase.DocTypeTemplate,
|
purchase.DocTypeTemplate,
|
||||||
purchase.PurchaseStrategy,
|
purchase.PurchaseStrategy,
|
||||||
purchase.PriceComposition,
|
purchase.PriceComposition,
|
||||||
|
purchase.PremiumComposition,
|
||||||
purchase.QualityAnalysis,
|
purchase.QualityAnalysis,
|
||||||
purchase.Assay,
|
purchase.Assay,
|
||||||
purchase.AssayLine,
|
purchase.AssayLine,
|
||||||
@@ -191,6 +276,13 @@ def register():
|
|||||||
Pool.register(
|
Pool.register(
|
||||||
account.PhysicalTradeIFRS,
|
account.PhysicalTradeIFRS,
|
||||||
module='purchase_trade', type_='model')
|
module='purchase_trade', type_='model')
|
||||||
|
Pool.register(
|
||||||
|
configuration.AccountConfiguration,
|
||||||
|
configuration.AccountConfigurationDefaultAccount,
|
||||||
|
invoice.InvoiceLineLotWeight,
|
||||||
|
invoice.InvoicePaddingReport,
|
||||||
|
invoice.InvoicePaddingContext,
|
||||||
|
module='purchase_trade', type_='model')
|
||||||
Pool.register(
|
Pool.register(
|
||||||
invoice.Invoice,
|
invoice.Invoice,
|
||||||
invoice.InvoiceLine,
|
invoice.InvoiceLine,
|
||||||
@@ -216,6 +308,8 @@ def register():
|
|||||||
purchase.PurchaseCrop,
|
purchase.PurchaseCrop,
|
||||||
module='lot', type_='model')
|
module='lot', type_='model')
|
||||||
Pool.register(
|
Pool.register(
|
||||||
|
sale.ContractClauseSelection,
|
||||||
|
sale.CharterCondition,
|
||||||
sale.Sale,
|
sale.Sale,
|
||||||
sale.SaleLine,
|
sale.SaleLine,
|
||||||
sale.SaleCreatePurchaseInput,
|
sale.SaleCreatePurchaseInput,
|
||||||
@@ -239,19 +333,25 @@ def register():
|
|||||||
sale.Backtoback,
|
sale.Backtoback,
|
||||||
sale.AnalyticDimensionAssignment,
|
sale.AnalyticDimensionAssignment,
|
||||||
sale.PriceComposition,
|
sale.PriceComposition,
|
||||||
|
sale.PremiumComposition,
|
||||||
module='sale', type_='model')
|
module='sale', type_='model')
|
||||||
Pool.register(
|
Pool.register(
|
||||||
lot.LotShipping,
|
lot.LotShipping,
|
||||||
|
lot.LotPlanTransport,
|
||||||
lot.LotMatching,
|
lot.LotMatching,
|
||||||
|
lot.LotGoMatching,
|
||||||
#lot.LotMatchingUnit,
|
#lot.LotMatchingUnit,
|
||||||
lot.LotWeighing,
|
lot.LotWeighing,
|
||||||
lot.CreateContracts,
|
lot.CreateContracts,
|
||||||
|
lot.CreateMirror,
|
||||||
lot.LotUnmatch,
|
lot.LotUnmatch,
|
||||||
|
lot.LotMarkFinished,
|
||||||
lot.LotUnship,
|
lot.LotUnship,
|
||||||
lot.LotRemove,
|
lot.LotRemove,
|
||||||
lot.LotInvoice,
|
lot.LotInvoice,
|
||||||
lot.LotAdding,
|
lot.LotAdding,
|
||||||
lot.LotImporting,
|
lot.LotImporting,
|
||||||
|
stock.ShipmentControllerEmail,
|
||||||
stock.FindVessel,
|
stock.FindVessel,
|
||||||
stock.SofUpdate,
|
stock.SofUpdate,
|
||||||
stock.Revaluate,
|
stock.Revaluate,
|
||||||
@@ -265,10 +365,23 @@ def register():
|
|||||||
forex.ForexReport,
|
forex.ForexReport,
|
||||||
purchase.PnlReport,
|
purchase.PnlReport,
|
||||||
purchase.PositionReport,
|
purchase.PositionReport,
|
||||||
|
valuation.ValuationProcess,
|
||||||
derivative.DerivativeMatchWizard,
|
derivative.DerivativeMatchWizard,
|
||||||
module='purchase', type_='wizard')
|
module='purchase', type_='wizard')
|
||||||
Pool.register(
|
Pool.register(
|
||||||
sale.SaleCreatePurchase,
|
sale.SaleCreatePurchase,
|
||||||
sale.SaleAllocationsWizard,
|
sale.SaleAllocationsWizard,
|
||||||
module='sale', type_='wizard')
|
module='sale', type_='wizard')
|
||||||
|
Pool.register(
|
||||||
|
invoice.InvoiceReport,
|
||||||
|
invoice.SaleReport,
|
||||||
|
invoice.PurchaseReport,
|
||||||
|
stock.ShipmentShippingReport,
|
||||||
|
stock.ShipmentInsuranceReport,
|
||||||
|
stock.ShipmentCOOReport,
|
||||||
|
stock.ShipmentPackingListReport,
|
||||||
|
stock.ShipmentLinkageReport,
|
||||||
|
stock.LotReportLinkageReport,
|
||||||
|
stock.LotReportLinkageFinalReport,
|
||||||
|
module='purchase_trade', type_='report')
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
# account.py
|
|
||||||
from trytond.model import ModelSQL, ModelView, fields
|
from trytond.model import ModelSQL, ModelView, fields
|
||||||
from trytond.pool import PoolMeta
|
from trytond.pool import PoolMeta
|
||||||
from trytond.pyson import Eval
|
from trytond.pyson import Eval
|
||||||
|
|
||||||
__all__ = ['PhysicalTradeIFRS']
|
__all__ = [
|
||||||
|
'PhysicalTradeIFRS',
|
||||||
|
]
|
||||||
__metaclass__ = PoolMeta
|
__metaclass__ = PoolMeta
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<tryton>
|
<tryton>
|
||||||
<data>
|
<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">
|
<record model="res.group" id="group_physical_trade_ifrs">
|
||||||
<field name="name">Physical Trade IFRS</field>
|
<field name="name">Physical Trade IFRS</field>
|
||||||
</record>
|
</record>
|
||||||
|
|||||||
1206
modules/purchase_trade/coffee.py
Normal file
1206
modules/purchase_trade/coffee.py
Normal file
File diff suppressed because it is too large
Load Diff
395
modules/purchase_trade/coffee.xml
Normal file
395
modules/purchase_trade/coffee.xml
Normal file
@@ -0,0 +1,395 @@
|
|||||||
|
<?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_cupping_result_line_view_form">
|
||||||
|
<field name="model">coffee.cupping.result.line</field>
|
||||||
|
<field name="type">form</field>
|
||||||
|
<field name="name">coffee_cupping_result_line_form</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>
|
||||||
154
modules/purchase_trade/company_defaults.py
Normal file
154
modules/purchase_trade/company_defaults.py
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
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 default_itsa_bulk_unit():
|
||||||
|
unit = _search_first('product.uom', [
|
||||||
|
[('symbol', '=', 'Bulk')],
|
||||||
|
[('name', '=', 'Bulk')],
|
||||||
|
[('symbol', '=', 'bulk')],
|
||||||
|
[('name', '=', 'bulk')],
|
||||||
|
])
|
||||||
|
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)]
|
||||||
292
modules/purchase_trade/configuration.py
Normal file
292
modules/purchase_trade/configuration.py
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
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")
|
||||||
|
auto_hedging = fields.Boolean("Auto hedge")
|
||||||
|
auto_hedging_over = fields.Boolean("Over hedge")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default_allow_modification_after_validation(cls):
|
||||||
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default_auto_hedging(cls):
|
||||||
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default_auto_hedging_over(cls):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class SaleConfiguration(metaclass=PoolMeta):
|
||||||
|
__name__ = 'sale.configuration'
|
||||||
|
|
||||||
|
allow_modification_after_validation = fields.Boolean(
|
||||||
|
"Autorise modification after validation")
|
||||||
|
auto_hedging = fields.Boolean("Auto hedge")
|
||||||
|
auto_hedging_over = fields.Boolean("Over hedge")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default_allow_modification_after_validation(cls):
|
||||||
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default_auto_hedging(cls):
|
||||||
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default_auto_hedging_over(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
@@ -15,6 +15,12 @@ class Cron(metaclass=PoolMeta):
|
|||||||
super().__setup__()
|
super().__setup__()
|
||||||
cls.method.selection.append(
|
cls.method.selection.append(
|
||||||
('forex.cron|update_forex', "Update Forex Prices"))
|
('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):
|
class PriceCron(ModelSQL, ModelView):
|
||||||
"Price Cron"
|
"Price Cron"
|
||||||
|
|||||||
@@ -41,5 +41,17 @@
|
|||||||
<field name="interval_number" eval="1"/>
|
<field name="interval_number" eval="1"/>
|
||||||
<field name="interval_type">days</field>
|
<field name="interval_type">days</field>
|
||||||
</record>
|
</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>
|
</data>
|
||||||
</tryton>
|
</tryton>
|
||||||
|
|||||||
3470
modules/purchase_trade/ctrm_reporting.py
Normal file
3470
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_physical_group"
|
||||||
|
sequence="10"
|
||||||
|
action="act_ctrm_position_physical"
|
||||||
|
id="menu_ctrm_position_physical"/>
|
||||||
|
<menuitem
|
||||||
|
name="Financial Position"
|
||||||
|
parent="menu_ctrm_positions_financial_group"
|
||||||
|
sequence="10"
|
||||||
|
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)
|
self.chatbot = 'chatbot:' + json.dumps(dial, ensure_ascii=False)
|
||||||
logger.info("EXITONCHANGE",self.chatbot)
|
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'):
|
def get_last_five_fx_rates(self, from_code='USD', to_code='EUR'):
|
||||||
"""
|
"""
|
||||||
Retourne (dernier_taux, avant_dernier_taux) pour le couple de devises.
|
Retourne (dernier_taux, avant_dernier_taux) pour le couple de devises.
|
||||||
"""
|
"""
|
||||||
Currency = Pool().get('currency.currency')
|
|
||||||
CurrencyRate = Pool().get('currency.currency.rate')
|
CurrencyRate = Pool().get('currency.currency.rate')
|
||||||
|
|
||||||
# Récupérer les devises EUR et USD
|
# Récupérer les devises EUR et USD
|
||||||
from_currency = Currency.search([('name', '=', from_code)])[0]
|
from_currency = self._find_currency_by_code(from_code)
|
||||||
to_currency = Currency.search([('name', '=', to_code)])[0]
|
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)
|
# Recherche des taux de la devise de base (ex: USD)
|
||||||
rates = CurrencyRate.search(
|
rates = CurrencyRate.search(
|
||||||
@@ -212,7 +225,7 @@ class Dashboard(ModelSQL, ModelView):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if not rates:
|
if not rates:
|
||||||
return None, None, None, None, None
|
return self._empty_fx_rates()
|
||||||
|
|
||||||
# Calcul du taux EUR/USD
|
# Calcul du taux EUR/USD
|
||||||
# Si la devise principale de la société est EUR, et que le taux stocké est
|
# 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
|
# last_rate = 1 / last_rate if last_rate else None
|
||||||
# prev_rate = 1 / prev_rate if prev_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):
|
def get_tremor(self,name):
|
||||||
Date = Pool().get('ir.date')
|
Date = Pool().get('ir.date')
|
||||||
@@ -430,7 +449,7 @@ class Dashboard(ModelSQL, ModelView):
|
|||||||
News = Pool().get('news.news')
|
News = Pool().get('news.news')
|
||||||
Date = Pool().get('ir.date')
|
Date = Pool().get('ir.date')
|
||||||
news_list = News.search([('active', '=', True)], limit=5, order=[('publish_date', 'DESC')])
|
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:
|
if last_rate and prev_rate:
|
||||||
variation = ((last_rate - prev_rate) / prev_rate) * 100 if prev_rate else 0
|
variation = ((last_rate - prev_rate) / prev_rate) * 100 if prev_rate else 0
|
||||||
direction = "📈" if variation > 0 else "📉"
|
direction = "📈" if variation > 0 else "📉"
|
||||||
@@ -749,7 +768,11 @@ class BotWizard(Wizard):
|
|||||||
lms.lot_r_id = ls.getLotQt()[0].id
|
lms.lot_r_id = ls.getLotQt()[0].id
|
||||||
lms.lot_matched_qt = lp.lot_quantity
|
lms.lot_matched_qt = lp.lot_quantity
|
||||||
lms.lot_sale = s
|
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])
|
Sale._process_shipment([s])
|
||||||
|
|
||||||
elif do_ == "add":
|
elif do_ == "add":
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ class AnalyticDimensionAssignment(ModelSQL, ModelView):
|
|||||||
value = fields.Many2One(
|
value = fields.Many2One(
|
||||||
'analytic.dimension.value',
|
'analytic.dimension.value',
|
||||||
'Value',
|
'Value',
|
||||||
required=True,
|
|
||||||
domain=[
|
domain=[
|
||||||
('dimension', '=', Eval('dimension')),
|
('dimension', '=', Eval('dimension')),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
# Backlog - Market Price Import
|
|
||||||
|
|
||||||
Scope: code changes related only to `BR-PT-004 - Market Price Import`.
|
|
||||||
|
|
||||||
## PT-BL-001 - Enforce uniqueness at model level
|
|
||||||
|
|
||||||
- Status: to be tested
|
|
||||||
- Implementation commit: `eaae2e5`
|
|
||||||
- Priority: high
|
|
||||||
- Source: [rules/market-price-import.md](../rules/market-price-import.md)
|
|
||||||
- Requirement: enforce uniqueness of `(price, price_date)` at model level without adding a database constraint yet.
|
|
||||||
- Work done:
|
|
||||||
- added model-level validation on `price.price_value`
|
|
||||||
- added Tryton message `price.msg_price_value_unique`
|
|
||||||
- added targeted tests for duplicate create and duplicate write validation
|
|
||||||
- Validation status:
|
|
||||||
- `git diff --check` passed
|
|
||||||
- automated Python tests still need to be run in a working Tryton Python environment
|
|
||||||
- Expected code impact:
|
|
||||||
- `modules/purchase_trade/pricing.py`
|
|
||||||
- possibly `modules/price/price_value.py` if the validation belongs on the target model
|
|
||||||
- Expected tests:
|
|
||||||
- importing a row for an existing `(price, price_date)` skips or updates according to `Overwrite existing price`
|
|
||||||
- creating/editing duplicate `price.price_value` records at model level is rejected if validation is implemented on `price.price_value`
|
|
||||||
|
|
||||||
## PT-BL-002 - Use locale-aware slash date parsing
|
|
||||||
|
|
||||||
- Status: open
|
|
||||||
- Priority: medium
|
|
||||||
- Source: [rules/market-price-import.md](../rules/market-price-import.md)
|
|
||||||
- Requirement: prefer the user's/default locale date format for ambiguous slash dates, then fall back to the current parsing order only if no locale preference is available.
|
|
||||||
- Current behavior: `DD/MM/YYYY` is tried before `MM/DD/YYYY`.
|
|
||||||
- Expected code impact:
|
|
||||||
- `modules/purchase_trade/pricing.py`
|
|
||||||
- Expected tests:
|
|
||||||
- slash dates follow the configured/default locale when available
|
|
||||||
- slash dates keep a deterministic fallback when locale is unavailable
|
|
||||||
- existing accepted formats still work
|
|
||||||
|
|
||||||
## PT-BL-003 - Reject missing price_value
|
|
||||||
|
|
||||||
- Status: open
|
|
||||||
- Priority: high
|
|
||||||
- Source: [rules/market-price-import.md](../rules/market-price-import.md)
|
|
||||||
- Requirement: `price_value` is mandatory for imported rows. Missing `price_value` must be reported as a row error.
|
|
||||||
- Current behavior: empty `price_value` is imported as an empty value.
|
|
||||||
- Expected code impact:
|
|
||||||
- `modules/purchase_trade/pricing.py`
|
|
||||||
- Expected tests:
|
|
||||||
- missing `price_value` is reported in `errors`
|
|
||||||
- missing `high_price`, `low_price`, and `open_price` remain allowed
|
|
||||||
- valid `price_value` still imports and updates correctly
|
|
||||||
|
|
||||||
## PT-BL-004 - Detect duplicate rows inside the same Excel file
|
|
||||||
|
|
||||||
- Status: open
|
|
||||||
- Priority: high
|
|
||||||
- Source: [rules/market-price-import.md](../rules/market-price-import.md)
|
|
||||||
- Requirement: duplicate rows for the same `(price_index, price_date)` inside the same Excel file must be reported as row errors. The duplicate row must not import or update data.
|
|
||||||
- Expected code impact:
|
|
||||||
- `modules/purchase_trade/pricing.py`
|
|
||||||
- Expected tests:
|
|
||||||
- duplicate `(price_index, price_date)` rows in the same import produce row errors
|
|
||||||
- first occurrence behavior remains unchanged
|
|
||||||
- duplicate rows do not overwrite previous imported or updated values
|
|
||||||
|
|
||||||
## PT-BL-005 - Categorize import result errors
|
|
||||||
|
|
||||||
- Status: open
|
|
||||||
- Priority: medium
|
|
||||||
- Source: [rules/market-price-import.md](../rules/market-price-import.md)
|
|
||||||
- Requirement: distinguish business validation errors from file, parsing, and technical errors in the import result.
|
|
||||||
- Current behavior: row errors are grouped under one `Errors` section.
|
|
||||||
- Expected code impact:
|
|
||||||
- `modules/purchase_trade/pricing.py`
|
|
||||||
- `modules/purchase_trade/view/import_prices_result_form.xml` only if the result display needs more structure
|
|
||||||
- Expected tests:
|
|
||||||
- result message separates business validation errors from parsing/technical errors
|
|
||||||
- invalid workbook and missing required columns remain blocking `UserError`
|
|
||||||
- row-level errors still let the import continue
|
|
||||||
@@ -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
|
# Business Rules - Purchase Trade
|
||||||
|
|
||||||
Statut: `draft`
|
Statut: `draft`
|
||||||
Version: `v0.3`
|
Version: `v0.8`
|
||||||
Derniere mise a jour: `2026-05-07`
|
Derniere mise a jour: `2026-05-10`
|
||||||
Owner metier: `a completer`
|
Owner metier: `a completer`
|
||||||
Owner technique: `a completer`
|
Owner technique: `a completer`
|
||||||
|
|
||||||
@@ -13,45 +13,914 @@ Owner technique: `a completer`
|
|||||||
- Modules impactes:
|
- Modules impactes:
|
||||||
- `purchase_trade`
|
- `purchase_trade`
|
||||||
- `lot`
|
- `lot`
|
||||||
- `price`
|
|
||||||
|
|
||||||
## 2) Glossaire
|
## 2) Glossaire
|
||||||
|
|
||||||
- `Purchase Line`: ligne d'achat.
|
- `Purchase Line`: ligne d'achat.
|
||||||
- `quantity_theorical`: quantite theorique contractuelle de la ligne.
|
- `quantity_theorical`: quantite theorique contractuelle de la ligne.
|
||||||
- `Virtual Lot`: lot unique de type `virtual` rattache a une `purchase.line`.
|
- `Virtual Lot`: lot unique de type `virtual` rattache a une `purchase.line`.
|
||||||
- `Physical Lot`: lot physique de type `physic` utilise comme pont entre achat, vente et shipment.
|
- `lot.qt`: table des quantites ouvertes, matchées ou shippées par lot.
|
||||||
- `lot.qt`: table des quantites ouvertes, matchees ou shippees par lot.
|
|
||||||
- `lot.qt ouvert`: enregistrement `lot.qt` avec `lot_p = virtual lot`, `lot_s = None` et sans shipment.
|
- `lot.qt ouvert`: enregistrement `lot.qt` avec `lot_p = virtual lot`, `lot_s = None` et sans shipment.
|
||||||
- `price.price`: index de prix marche.
|
|
||||||
- `price.price_value`: valeur datee d'un index de prix marche.
|
|
||||||
|
|
||||||
## 3) Catalogue des regles
|
## 3) Regles metier
|
||||||
|
|
||||||
| ID | Titre | Domaine | Detail |
|
### BR-PT-001 - Ajustement de la quantite theorique apres creation du contrat
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| BR-PT-001 | Ajustement de la quantite theorique apres creation du contrat | Lot / Purchase | [rules/lot-quantity.md](rules/lot-quantity.md) |
|
|
||||||
| BR-PT-002 | Le lot physique est le pont metier entre purchase, sale et shipment | Lot / Navigation | [rules/lot-navigation.md](rules/lot-navigation.md) |
|
|
||||||
| BR-PT-003 | Le freight amount des templates facture vient du fee de shipment | Invoice / Freight | [rules/invoice-freight.md](rules/invoice-freight.md) |
|
|
||||||
| BR-PT-004 | Market Price Import | Pricing | [rules/market-price-import.md](rules/market-price-import.md) |
|
|
||||||
|
|
||||||
## 4) Convention pour les nouvelles regles
|
- Intent: conserver la coherence entre la quantite theorique de la ligne d'achat, le lot virtuel associe et les quantites ouvertes stockees dans `lot.qt`.
|
||||||
|
- Description:
|
||||||
|
- Quand `purchase.line.quantity_theorical` est modifiee apres creation du contrat, le systeme doit recalculer le delta entre l'ancienne et la nouvelle valeur.
|
||||||
|
- La regle s'applique au lot unique de type `virtual` rattache a la `purchase.line`.
|
||||||
|
- Conditions d'entree:
|
||||||
|
- Une `purchase.line` existe deja.
|
||||||
|
- Son champ `quantity_theorical` est modifie via `write`.
|
||||||
|
- Un lot `virtual` est rattache a la ligne.
|
||||||
|
- Resultat attendu:
|
||||||
|
- Si `delta > 0`:
|
||||||
|
- augmenter la quantite courante du lot `virtual` via `set_current_quantity` pour conserver l'historique `lot.qt.hist`
|
||||||
|
- augmenter le `lot.qt` ouvert existant
|
||||||
|
- si aucun `lot.qt` ouvert n'existe, en creer un nouveau avec le delta
|
||||||
|
- Si `delta < 0`:
|
||||||
|
- diminuer le `lot.qt` ouvert uniquement si la quantite ouverte disponible est suffisante
|
||||||
|
- diminuer la quantite courante du lot `virtual` du meme delta
|
||||||
|
- si aucun `lot.qt` ouvert n'existe ou si sa quantite est insuffisante, bloquer avec l'erreur `Please unlink or unmatch lot`
|
||||||
|
- Definition du `lot.qt` ouvert:
|
||||||
|
- `lot_p = virtual lot`
|
||||||
|
- `lot_s = None`
|
||||||
|
- `lot_shipment_in = None`
|
||||||
|
- `lot_shipment_internal = None`
|
||||||
|
- `lot_shipment_out = None`
|
||||||
|
- Exceptions:
|
||||||
|
- si aucun lot `virtual` n'est trouve sur la ligne, la regle ne fait rien
|
||||||
|
- Priorite:
|
||||||
|
- `bloquante`
|
||||||
|
- Source:
|
||||||
|
- `Decision metier documentee dans les commentaires de purchase_trade.purchase.Line.write`
|
||||||
|
|
||||||
Voir aussi [documentation-management.md](documentation-management.md) pour le workflow complet de gestion documentaire.
|
### BR-PT-002 - Le lot physique est le pont metier entre purchase, sale et shipment
|
||||||
|
|
||||||
Ajouter une ligne au catalogue puis creer une fiche detaillee dans `docs/rules/`.
|
- Intent: disposer d'un chemin unique et stable pour retrouver les informations logistiques et de facturation reliees a un contrat d'achat ou de vente.
|
||||||
|
- Description:
|
||||||
|
- Le lot physique (`lot_type = physic`) porte simultanement le lien vers:
|
||||||
|
- la `purchase.line` via `lot.line`
|
||||||
|
- la `sale.line` via `lot.sale_line`
|
||||||
|
- le shipment via `lot.lot_shipment_in` / `lot.lot_shipment_internal` / `lot.lot_shipment_out`
|
||||||
|
- Pour toute logique qui doit naviguer entre achat, vente, shipment et facture, il faut privilegier ce lot physique comme source de verite.
|
||||||
|
- Resultat attendu:
|
||||||
|
- depuis une facture d'achat:
|
||||||
|
- remonter a la `purchase.line`
|
||||||
|
- puis au lot physique de la ligne
|
||||||
|
- puis au shipment et aux donnees logistiques associees
|
||||||
|
- depuis une facture de vente:
|
||||||
|
- remonter a la `sale.line`
|
||||||
|
- puis au lot physique matchant qui porte aussi la `purchase.line`
|
||||||
|
- puis au shipment et aux donnees logistiques associees
|
||||||
|
- Cas d'usage typiques:
|
||||||
|
- recuperer `bl_date`, `bl_number`, `controller`, `from_location`, `to_location`
|
||||||
|
- retrouver une facture provisoire liee au lot
|
||||||
|
- retrouver des fees rattaches au shipment
|
||||||
|
- Priorite:
|
||||||
|
- `structurante`
|
||||||
|
|
||||||
Structure recommandee:
|
### BR-PT-003 - Le freight amount des templates facture vient du fee de shipment
|
||||||
|
|
||||||
- Intent
|
- Intent: afficher dans les documents facture la vraie valeur de fret maritime rattachee au shipment du lot physique.
|
||||||
- Scope
|
- Description:
|
||||||
- Inputs
|
- Le `FREIGHT VALUE` d'une facture ne doit pas etre pris sur la facture elle-meme.
|
||||||
- Expected Behavior
|
- Il doit etre calcule a partir du `fee.fee` rattache au shipment (`shipment_in`) du lot physique relie a la facture.
|
||||||
- Edge Cases
|
- Regle de navigation:
|
||||||
- Impacted Files
|
- retrouver le lot physique pertinent depuis la facture
|
||||||
- Tests
|
- retrouver son shipment
|
||||||
- Open Questions
|
- chercher le `fee.fee` avec:
|
||||||
|
- `shipment_in = shipment.id`
|
||||||
|
- `product.name = 'Maritime freight'`
|
||||||
|
- utiliser `fee.get_amount()` comme montant de fret
|
||||||
|
- Portee:
|
||||||
|
- s'applique aussi bien aux factures d'achat qu'aux factures de vente
|
||||||
|
- cote vente, la remontee doit passer par le lot physique qui fait le lien entre `purchase.line` et `sale.line`
|
||||||
|
- Priorite:
|
||||||
|
- `importante`
|
||||||
|
|
||||||
Les regles courtes peuvent rester dans ce catalogue temporairement, mais toute regle avec impact code, tests, edge cases ou flux transverse doit avoir son fichier dedie.
|
### BR-PT-004 - La valuation doit couvrir les flux purchase et sale, y compris les sales non matchees
|
||||||
|
|
||||||
Dans les sections `Open Questions`, prefixer chaque question avec `Q:` et chaque reponse documentee avec `A:`.
|
- 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
|
||||||
|
|
||||||
|
- Donnees:
|
||||||
|
- `ancienne quantity_theorical = 100`
|
||||||
|
- `nouvelle quantity_theorical = 120`
|
||||||
|
- `lot.qt ouvert = 40`
|
||||||
|
- Attendu:
|
||||||
|
- lot `virtual` augmente de `20`
|
||||||
|
- `lot.qt ouvert` passe de `40` a `60`
|
||||||
|
|
||||||
|
### Exemple E2 - Augmentation sans lot.qt ouvert
|
||||||
|
|
||||||
|
- Donnees:
|
||||||
|
- `ancienne quantity_theorical = 100`
|
||||||
|
- `nouvelle quantity_theorical = 110`
|
||||||
|
- aucun `lot.qt` ouvert
|
||||||
|
- Attendu:
|
||||||
|
- lot `virtual` augmente de `10`
|
||||||
|
- creation d'un `lot.qt` ouvert a `10`
|
||||||
|
|
||||||
|
### Exemple E3 - Diminution possible
|
||||||
|
|
||||||
|
- Donnees:
|
||||||
|
- `ancienne quantity_theorical = 100`
|
||||||
|
- `nouvelle quantity_theorical = 90`
|
||||||
|
- `lot.qt ouvert = 25`
|
||||||
|
- Attendu:
|
||||||
|
- lot `virtual` diminue de `10`
|
||||||
|
- `lot.qt ouvert` passe de `25` a `15`
|
||||||
|
|
||||||
|
### Exemple E4 - Diminution impossible
|
||||||
|
|
||||||
|
- Donnees:
|
||||||
|
- `ancienne quantity_theorical = 100`
|
||||||
|
- `nouvelle quantity_theorical = 80`
|
||||||
|
- `lot.qt ouvert = 5`
|
||||||
|
- Attendu:
|
||||||
|
- blocage avec `Please unlink or unmatch lot`
|
||||||
|
|
||||||
|
## 5) Impact code attendu
|
||||||
|
|
||||||
|
- 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
|
||||||
|
|
||||||
|
Pour cette regle, couvrir au minimum:
|
||||||
|
|
||||||
|
- augmentation avec `lot.qt` ouvert existant
|
||||||
|
- 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.
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user