Compare commits
1235 Commits
v1.0.0
...
3dfbb25281
| Author | SHA1 | Date | |
|---|---|---|---|
| 3dfbb25281 | |||
| eb38977dca | |||
| ac3198ee70 | |||
| cf4bd94d03 | |||
| 07ee8b4f99 | |||
| 7fc134f7a6 | |||
| e4590e6177 | |||
| 5a644d430b | |||
|
|
808e75daa6 | ||
|
|
febe29c6b4 | ||
|
|
b312ea9bd3 | ||
| 3321830b5e | |||
| f2fe626d72 | |||
| ddd88cd840 | |||
| 47d0f025b3 | |||
| 8719a63fa5 | |||
| 27b865184c | |||
| c2a523430e | |||
| 8d386e6754 | |||
| 62eed772fc | |||
| b0bcaf293d | |||
| 9cf1bc3604 | |||
| 858b7258d9 | |||
| 3122255bc7 | |||
| 5c3464da5c | |||
| 82143ba87d | |||
| 1b292e4594 | |||
| 0f4edc16ce | |||
| 7ac1e118ae | |||
| 94fd181c11 | |||
| 4ea701d669 | |||
| b3b14039e0 | |||
| ae39ff2f7d | |||
| 2ffd99b03a | |||
| 7d5ac651ab | |||
| e8b3fe5bc4 | |||
| ea85927a17 | |||
| 99d5d2a849 | |||
| c0ac8fac68 | |||
| 2743a7bc14 | |||
| cd49703879 | |||
| cc4a48c38c | |||
| 6fedb92b1a | |||
| cad7f7235e | |||
| b08d2f4dab | |||
| 2a30b03cbe | |||
| eb244b1e3e | |||
| f2947c9f86 | |||
| f9ade37eab | |||
| 5635856711 | |||
| edc20fa546 | |||
| d4f9d02cb4 | |||
| 780f3dcbdb | |||
| d5c6d5c4b5 | |||
| 104d5c616c | |||
| f45cdb0807 | |||
| b7b54c8b7b | |||
| c9dceb9c06 | |||
| cf7aa7f5b5 | |||
| 2998d8d570 | |||
| 83b879d200 | |||
| 50b262dbd2 | |||
| fbf3842e6c | |||
| ef8d914820 | |||
| 423d9476fc | |||
| d08510f084 | |||
| ed818c2287 | |||
| 703868001b | |||
| 6af3de0ffb | |||
| 95dba725a8 | |||
| a880535bc4 | |||
| 8f676c2396 | |||
| dfbc006efb | |||
| 4f331d5c8e | |||
| 227a6c14a8 | |||
| 2b0381c2ae | |||
| 2a1512a436 | |||
| 1c20181d06 | |||
| 6c2061e779 | |||
| 1f24646227 | |||
| 2d737b2628 | |||
| 903b42f730 | |||
| f8d53a27fa | |||
| 4b5e49f264 | |||
| 3b3f9c0988 | |||
| 248e410d88 | |||
| a754e15e32 | |||
| ee183e23c5 | |||
| 8441bf98e7 | |||
| 02e5c43f3d | |||
| 6ba5361269 | |||
| f0714d5670 | |||
| b8bfcc4308 | |||
| c268c3b460 | |||
| 04b3c1d924 | |||
| d8012bd2da | |||
| dd2acc2d09 | |||
| a8102d513f | |||
| ee1cb3e7fb | |||
| d0dd8965cd | |||
| 0833baa2ab | |||
| 8eba0cd066 | |||
| 38af948374 | |||
| 36ee92cbb2 | |||
| fdf855f7db | |||
| 31f617ae1c | |||
| 07e48944b6 | |||
| 3a0edea8cd | |||
| 9b7ccf1feb | |||
| dd8e3e3056 | |||
| d8a0dd43cf | |||
| 7b427813be | |||
| d705d86e80 | |||
| 667b66487d | |||
| 9c92904b3e | |||
| 8a60a8e67f | |||
| e6de614507 | |||
| a7c061f52d | |||
| e4e2087463 | |||
| c15b13cb91 | |||
| e981f96cad | |||
| aac7bad852 | |||
| 1e4d9d6e17 | |||
| a0363fa9c7 | |||
| e19dcafcb7 | |||
| 6a5a267cfd | |||
| 5c9f437945 | |||
| ec90d19cc7 | |||
| 77b1ab333b | |||
| 1f3e32fbe8 | |||
| a435631289 | |||
| 1a5e2e4033 | |||
| 7107625664 | |||
| dbfd6a0d89 | |||
| 6ac19a1334 | |||
| 855dc3a4dd | |||
| 52459a267b | |||
| 9d13cfd587 | |||
| de8310891b | |||
| 925fdfd32b | |||
| 96ad6dd1c6 | |||
| 68bd105e59 | |||
| 90f64cf55f | |||
| f722c6fa1c | |||
| 217344d3d5 | |||
| 5cb4317742 | |||
| cf3ad4bb31 | |||
| fcc5c3866a | |||
| f59150d1a9 | |||
| 67cb5491e2 | |||
| 734c0c8a34 | |||
| fd04810f0c | |||
| c8e8e4f866 | |||
| 3a6f426033 | |||
| 5838d848c2 | |||
| 79c701b711 | |||
| c36c6a411f | |||
| 745325eb20 | |||
| dbd5e5b77f | |||
| c2f3d3995f | |||
| 02a15c93de | |||
| abbcdfd2ec | |||
| dc067fb812 | |||
| 8691ba430f | |||
| e2e0e8cece | |||
| d182b0eceb | |||
| 74d1465470 | |||
| 1abfbbbc3d | |||
| 72501ab625 | |||
| 65e9f4d7d6 | |||
| 0a72076346 | |||
| 3977e138e2 | |||
| 45acff11bc | |||
| 1b15ab4c69 | |||
| 7735306f9c | |||
| 76b34ba280 | |||
| eb82211da7 | |||
| c2495bb6ea | |||
| 9e5cd39629 | |||
| 05a90f64a5 | |||
| 4d3c5e26e2 | |||
| 6ac1904a0c | |||
| 8e82986bd5 | |||
| 6ffc159255 | |||
| 4ebfbe6ddd | |||
| 700adb87f9 | |||
| 126225ea6a | |||
| 1339f9b2f1 | |||
| b3b7803313 | |||
| 24b4e00080 | |||
| 846c2d6a93 | |||
| 801e53f953 | |||
| 06f9a62dbc | |||
| eceeca997e | |||
| f37d21c761 | |||
| 1b97899091 | |||
| afee4243cc | |||
| 743c76c131 | |||
| acf0d4b687 | |||
| d659f14e12 | |||
| b74389f9a6 | |||
| b3c33ed314 | |||
| 73006db98a | |||
| 3599570d5e | |||
| 2ae512e074 | |||
| 90932a290d | |||
| 77922997eb | |||
| 574c1a67f4 | |||
| 1de1d15064 | |||
| 25800aa8a4 | |||
| 5a377fda13 | |||
| 3ab58e63f8 | |||
| a114c14e89 | |||
| a98415fe62 | |||
| c272630a8a | |||
| 9cc0f0715b | |||
| 6860ba9e60 | |||
| e9bd6ff57f | |||
| 0efa60f3b5 | |||
| d3a118db07 | |||
| 48d0b6a3f6 | |||
| 4e1d6895f8 | |||
| 4a0461df44 | |||
| adcce718fd | |||
| 5763ca64a5 | |||
| 0094bd6974 | |||
| c096b39259 | |||
| 654a8418bf | |||
| dd4b8055a7 | |||
| 9556f870ec | |||
|
|
4c08b95b3b | ||
|
|
a6e90f6e8b | ||
| 7df570c860 | |||
| 31d0e00f89 | |||
| 9a1673c6cd | |||
| fb17c13a61 | |||
| 671251d23e | |||
| 4e7911b0ac | |||
| c233e8cb77 | |||
| de0ef9fdf4 | |||
| c95b273291 | |||
| 542246baac | |||
| 0415ced262 | |||
| b767f7648d | |||
| 405c2c2bf0 | |||
| f39675543a | |||
| 2fecf60295 | |||
| 3b9a6ca943 | |||
| fcd24af41b | |||
| b0d69207e4 | |||
| 91bf2a3de4 | |||
| e9a8355587 | |||
| d483e7cf69 | |||
| 2dd8360d62 | |||
| 779e1aa361 | |||
| 2223927d34 | |||
| a26d0516f0 | |||
| 5605b19164 | |||
| f8d7f1e9d9 | |||
| 735c5431af | |||
| 38c614af98 | |||
| fcd809a3e3 | |||
| 83f7e43a6e | |||
| a9b048d4cf | |||
| 7da99b3bf7 | |||
| 242406588c | |||
| 3c4261ccf6 | |||
| 2428978d90 | |||
| c9028f1590 | |||
| 1af7ae99a4 | |||
| 26f43c2438 | |||
| 5b5685ae12 | |||
| 28c4fb3ff8 | |||
| 181c4b3d34 | |||
| 50ac9f297c | |||
| c2d4e34516 | |||
| 5b5f1d77ff | |||
| 0fffb341fe | |||
| 8b4daeac84 | |||
| 4da56e4b8a | |||
| e7a1a83c90 | |||
| c6d7b0da2b | |||
| 12727e53cb | |||
| 436d0b9a0c | |||
| 7a39754a63 | |||
| 55f342f106 | |||
| e78977dfa5 | |||
| ddca542bfc | |||
| 1e5611f726 | |||
| aed68ba6f4 | |||
| 54309a1980 | |||
| 1d398ab4b5 | |||
| 2a5eec0e62 | |||
| 20f51de50f | |||
| a22d185fb0 | |||
| 390207a371 | |||
| 01c23c1ec2 | |||
| c7146dd8a7 | |||
| 7569d06254 | |||
| f0ed5b9bd3 | |||
| 1075f49bd1 | |||
| 1efaad8c38 | |||
| 1dc8803287 | |||
| 50636c1294 | |||
| 6dfb12c125 | |||
| 96653be311 | |||
| 488fb15498 | |||
| f56f394ec3 | |||
| 4b9621a5f0 | |||
| 900d733fcd | |||
| 1adc4248ee | |||
| 3c5c20276d | |||
| f16b334089 | |||
| f29664fd77 | |||
| f7e70e0438 | |||
| 2cf1b25dad | |||
| 27e081228f | |||
| ea3cb65d7d | |||
| 9ad0217a69 | |||
| 1f3616858a | |||
| 072960307a | |||
| 83612eeea3 | |||
| 8e16259ab2 | |||
| 3a1b9f2d90 | |||
| 22a083a853 | |||
| c3d768d5a8 | |||
| 133ca49eda | |||
| f8aded48af | |||
| c6a2d48e4c | |||
| 22861dbc91 | |||
| 8ff380e229 | |||
| 80b2fed34b | |||
| 74e6cd0bf2 | |||
| 70f4bc291c | |||
| a231f32646 | |||
| d1c0f4a738 | |||
| ce883fa950 | |||
| 149306ff4e | |||
| 23fbcfa988 | |||
| 3a31cc354a | |||
| 62941a5c3b | |||
| d2ae65af82 | |||
| b35fa829e2 | |||
| 9d436d6ea0 | |||
| 9a6bb83c19 | |||
| ba79f88b43 | |||
| 4d2f203469 | |||
| c907a87b09 | |||
| ff75f9ab33 | |||
| 3877ff0b6f | |||
| 74b6425e93 | |||
| b20c3d54a4 | |||
| 60f26ddc9d | |||
| 07ddb212e3 | |||
| 4395884754 | |||
| 160ec1544b | |||
| 18c0329a94 | |||
| cdfc117220 | |||
| a613f59a2d | |||
| 4925cc8bfd | |||
| dd2b6952bc | |||
| 30b9da1a78 | |||
| 1a041fdcbf | |||
| 5f159b06dd | |||
| 30e0aa6543 | |||
| 20a4e20e91 | |||
| 82a30f246d | |||
| e683ce7e62 | |||
| 3c788d4764 | |||
| b4d944b10c | |||
| 24826bec50 | |||
| 9677d0dae2 | |||
| 2b3c823743 | |||
| 355831c76d | |||
| e6bbf2f1c1 | |||
| 0d034816f7 | |||
| b0a0d31528 | |||
| a85ab97d46 | |||
| 14ba0361d2 | |||
| 26c955ef53 | |||
| a71ce107f0 | |||
| 343fc8527c | |||
| de78da846d | |||
| 50e25138fd | |||
| f2c7adc2b3 | |||
| c017aac9a7 | |||
| 09c78395f1 | |||
| 4ae64269f4 | |||
| 90593b70c2 | |||
| 4d5bbc5523 | |||
| a23aac4527 | |||
| e37970a43d | |||
| fe3a3ea71e | |||
| 8e890f3c43 | |||
| a684c7b9c1 | |||
| 28d3400651 | |||
| 000cf54fbc | |||
| 65fe45bae2 | |||
| d8ac25dcda | |||
| c27c915700 | |||
| d4da45344c | |||
| bfa874da43 | |||
| 199caa2632 | |||
| 0f218374a7 | |||
| e03dee7def | |||
| 83aa474073 | |||
| 75bd34b673 | |||
| 03d65c253c | |||
| adac3ca630 | |||
| 221e701507 | |||
| 5a7b5845b6 | |||
| 616cf6d73c | |||
| 589ff16401 | |||
| a7d360b584 | |||
| 56b173d225 | |||
| 27450d51f6 | |||
| ea31706ff8 | |||
| 35fe191939 | |||
| 062b42f83f | |||
| debe374f1b | |||
| b5b554bad0 | |||
| 1d6f3158c0 | |||
| ff2b897b7a | |||
| 30d4362c09 | |||
| aa9a18fb93 | |||
| 4faa7fd83c | |||
| ca20d418cc | |||
| 3314d31fcd | |||
| f6db19d1d9 | |||
| 257916bd6c | |||
| 2cdcbe7303 | |||
| 98893647f6 | |||
| 2e276e2b1f | |||
| 384c1285b1 | |||
| 308252215f | |||
| 7965b22954 | |||
| 8ae2c7ca9c | |||
| 16151381cc | |||
| 4db0395a11 | |||
| fbfa73110f | |||
| 9544418897 | |||
| e40a32e796 | |||
| 530f2f9d97 | |||
| 9879926f08 | |||
| 04e9d7dc03 | |||
| 7a123b1af6 | |||
| a2b053c565 | |||
| 6018429369 | |||
| 4d4e7ab625 | |||
| 68ce1f3a62 | |||
| d8a56637c9 | |||
| a1486175f2 | |||
| 228ee07a9c | |||
| 036a2801e5 | |||
| c2838dcfd3 | |||
| 6e26f178ce | |||
| 4458871423 | |||
| 99782b5b0e | |||
| d09d183cdc | |||
| ec45440400 | |||
| edb23b008c | |||
| fca279a038 | |||
| 512862113a | |||
| 1ed79a2363 | |||
| 05a75f000a | |||
| 77a5366622 | |||
| c203e1b300 | |||
| 22ad8547e4 | |||
| 2ea2165653 | |||
| 5e72d4f98d | |||
| b1680cc96c | |||
| cfb719a7da | |||
| 9e4a12605c | |||
| 4013f9d74c | |||
| 280cff5fdb | |||
| 897c6f6824 | |||
| 8906f00d36 | |||
| 29a719c117 | |||
| d71257720e | |||
| aee9c3277e | |||
| b68f475e22 | |||
| 90eab73430 | |||
| 472806ef06 | |||
| 0def187750 | |||
| 229b6037fb | |||
| 8a90216357 | |||
| 4bbd7a5e76 | |||
| 9c8d7f11ae | |||
| b39607d987 | |||
| 65482b4a8b | |||
| 8b9787d4c0 | |||
| a1ab7dec82 | |||
| 5ae8af84fb | |||
| c90b14fcc1 | |||
| e9ff9c76ab | |||
| 63d8266a9c | |||
| add4cdc137 | |||
| 4d94aa78ed | |||
| da01249f66 | |||
|
|
5cbb57c657 | ||
| 50a8c6328f | |||
| eaa5c8b544 | |||
| 78e9e06a8b | |||
| 9f06398b2c | |||
| 51a84f1f2e | |||
| 00330008d1 | |||
| 3480eb8a7a | |||
| 5179d98289 | |||
| 2109d7a3e4 | |||
| 1f350e6207 | |||
| 7722292482 | |||
| ec359f6b8a | |||
| 845b9cf749 | |||
| 48b941b109 | |||
| 18ece66cdb | |||
| acfc2fe88a | |||
| 888b880bd6 | |||
| 1f62ae91dd | |||
| 05e68636ad | |||
| bfb9bb3188 | |||
| b78e64f9f1 | |||
| 199b8aec12 | |||
| 1757075f2b | |||
| 172d38479d | |||
| 4902368b15 | |||
| 7b4f757cb5 | |||
| b37f132cdf | |||
| 15f791bd92 | |||
| 58cd66e543 | |||
| cc6ce82ec1 | |||
| 11526ef3ee | |||
| 6d52317804 | |||
| a99efcfc5b | |||
| 0d5cf7dffc | |||
| 613b679908 | |||
| 51ced23ab8 | |||
| 2958e1fb9e | |||
| 346a34951d | |||
| b644aea007 | |||
| 5dbaba5f32 | |||
| d133665fc7 | |||
| c2cb2a874c | |||
| 408970c339 | |||
| ea2627c9ae | |||
| ac988a714a | |||
| 97eae6e4a6 | |||
| 3976b387d7 | |||
| 9b8e8127a1 | |||
| f53a9bce27 | |||
| a7753b974f | |||
| c687828ba5 | |||
| 5054b64cd0 | |||
| 06922973b7 | |||
| 18ebf7f06c | |||
| 44c4560f24 | |||
| 7643bf21fb | |||
| 97677025d7 | |||
| 02fe5b3e5d | |||
| 6e529deca0 | |||
| efee365fc6 | |||
| 6bf245ac64 | |||
| 238869989a | |||
|
|
2fa541e962 | ||
|
|
ad2f7e6f78 | ||
| 43c62607a8 | |||
| 5cff728d79 | |||
|
|
d6382f624b | ||
|
|
806e374ceb | ||
| 08febb904f | |||
| 20d733e787 | |||
| 984b2ba56f | |||
| f67e5d8ccc | |||
| 2bf02e687c | |||
| 3c45ebd50f | |||
| 22d186f0ac | |||
| 0979021f41 | |||
| af4ae99dc0 | |||
| 11c489f79d | |||
| 620d6bb604 | |||
| 97cfd13da2 | |||
| 126455bf0f | |||
| 97cc447e0a | |||
| e2bbd0e522 | |||
| 51778bda9e | |||
| c06ea3bd99 | |||
| f0b0666773 | |||
| edca5fed55 | |||
| 8b6b93171f | |||
| 841f7a1c20 | |||
| 91acaba3dc | |||
| 3e5320cf9e | |||
| f9010ddefd | |||
| d722b58f4e | |||
| 3e646ea035 | |||
| 90f97daa7f | |||
| e1488e9677 | |||
| 0bcec5d3c4 | |||
| 10848ed533 | |||
| b90d65d245 | |||
| 357478e74c | |||
| 1f49c00cff | |||
| d38d0324d5 | |||
| b8ec7a3d2d | |||
| 0ac261b670 | |||
| 51411faff2 | |||
| a94906bb53 | |||
| 2262e79361 | |||
| 5327ca4f21 | |||
| 03997db3e4 | |||
| 74e4ac0c1b | |||
| 353cf17af7 | |||
| 412f8f00e0 | |||
| 6b841db4ce | |||
| 4e3818d46a | |||
| 8bb34aa4fb | |||
| 0392b191c5 | |||
| f98b0fd010 | |||
| 97dd672e6a | |||
| bde600e86a | |||
| 0e7cabdc00 | |||
| 15f9f4b724 | |||
| c24de18fcd | |||
| cb9c868e88 | |||
| 0e76aa6312 | |||
| 062ca10340 | |||
| 175c7a5e30 | |||
| e6519d30ee | |||
| f9ee416d99 | |||
| 80deac62f8 | |||
| b5d771341d | |||
| afdbd05375 | |||
| 36b94b98af | |||
| c655eb4170 | |||
| ed11b23f79 | |||
| 140eae06a0 | |||
| 234084f073 | |||
| 2fc6fbbd76 | |||
| 0b9c85f5ad | |||
| 717b51ad19 | |||
| dfdaf7a1cd | |||
| 7dba25c6d7 | |||
| 765d90512d | |||
| 090b4ea5c6 | |||
| 1326c8df4c | |||
| d10d805753 | |||
| 85bb272edf | |||
| 5f475d5714 | |||
| 8cc091ef57 | |||
| 7ba170e072 | |||
| 931b5b9ea2 | |||
| eb98ef87bb | |||
| 834ae46a14 | |||
| 7c746ad931 | |||
| 95f5c4af57 | |||
| f83c62ffa8 | |||
| 1f4b36633b | |||
| 36dc48bee0 | |||
| fc52aa8bbb | |||
| 5c516465e2 | |||
| d827fa6140 | |||
| 93d27059af | |||
| d2a13b3c01 | |||
| 22c4766e66 | |||
| 73eeee0f72 | |||
| 260300681f | |||
| a2e0c36a3c | |||
| 4f6641ceeb | |||
| cabd032bc6 | |||
| b2e095aad5 | |||
| 8c3ba0a23d | |||
| 13072de28f | |||
| a63a46cc40 | |||
| bf6e9f6a78 | |||
| 6c7947c7c1 | |||
| 6954044f7e | |||
| d6207855a0 | |||
| a41ec4412d | |||
| 8c3c224c07 | |||
| 3581b7ea80 | |||
| f7d87d3a78 | |||
| 5a52f09f97 | |||
| 420577cd72 | |||
| 01777f1536 | |||
| 24c8cd8075 | |||
| 7ace327d3f | |||
| a8d37bd766 | |||
| 6b96cdbc88 | |||
| 5bb1618b30 | |||
| 3ef819bf86 | |||
| 7ae54f9977 | |||
| 410604d890 | |||
| c8bbf9a12e | |||
| f32cce6177 | |||
| 32b48cf6d1 | |||
| eebcf936fe | |||
| 127a38f857 | |||
| 1cedd11304 | |||
| 8881caccd0 | |||
| 59a6554fa3 | |||
| 3d80a6ba5e | |||
| 07816b9cfe | |||
| 5bbd68448a | |||
| 185f26f31b | |||
| bbb88dbd22 | |||
| 3ad2ae2624 | |||
| e78eb80d08 | |||
| 498da9a728 | |||
| eac35b849f | |||
| 13770cc299 | |||
| 72b71675ae | |||
| 5cf5ca520a | |||
| 5fe0be5c59 | |||
| c504b4b2d7 | |||
| bbd3d30b37 | |||
| b4d09d3a69 | |||
| 8e7f9648fc | |||
| 2183d8d5a4 | |||
| 8398a8c212 | |||
| 8859ccad1a | |||
| c2adc96fbf | |||
| 9330662b30 | |||
| eadbbbbbc8 | |||
| 5b12e5f8db | |||
| 1d10591cf6 | |||
| fd3ce2188a | |||
| 4695a93e44 | |||
| 93f0e6b0af | |||
| 319d8a4afb | |||
| b6e85ee710 | |||
| c349441874 | |||
| 73fdbe9eba | |||
| b20b0c8df6 | |||
| cc5f4da38c | |||
| 016112a355 | |||
| 0cf03a75b0 | |||
| 7f6b93094f | |||
| ad58d40da7 | |||
| f0c1b8909e | |||
| 8e8afe39d0 | |||
| 8da50c72c7 | |||
| 683d3600ac | |||
| e2cb840844 | |||
| 19a4363d10 | |||
| 23c4edfec5 | |||
| b07ad57a36 | |||
| 6d06125360 | |||
| 4a14a78f78 | |||
| 2fba795b11 | |||
| b23dba865f | |||
| 0b1bb2ffa5 | |||
| 6890d0de07 | |||
| c180e8926f | |||
| 0fe16df326 | |||
| 3e15052520 | |||
| dee1896a6c | |||
| faf5fa605e | |||
| a7846b359c | |||
| c0cffde079 | |||
| 814071e4c1 | |||
| 30ae86a987 | |||
| 1de11a846b | |||
| 93acacb955 | |||
| 6d053dfe03 | |||
| 5f49e01495 | |||
| d609864822 | |||
| aff4459942 | |||
| 840f03e5d8 | |||
| 039b278757 | |||
| 265b41b206 | |||
| 5235b381c6 | |||
| 52c58df547 | |||
| cc8540ee91 | |||
| d70a784db8 | |||
| e6984e3393 | |||
| f4ae7ebd7c | |||
| 5f744ec25c | |||
| 1dc69008d8 | |||
| 96aab15a7a | |||
| dd06fc523c | |||
| 7cafd8381f | |||
| c009b15ed0 | |||
| 6717feb22d | |||
| 7998467adf | |||
| 83ef3c6ae9 | |||
| 372d04b30f | |||
| 4e6ddc39c1 | |||
| a3f15c30e6 | |||
| cdb0bf7254 | |||
| 843ade6f3d | |||
| 62f731530b | |||
| 14c2548c16 | |||
| 13000f110d | |||
| 915fd17d83 | |||
| 12de27d105 | |||
| 1a1f675cf6 | |||
| 3b67f3c251 | |||
| 551a040df0 | |||
| b6c3279917 | |||
| 2b956e6142 | |||
| ae0817e0ac | |||
| 7451e19125 | |||
| 52418b6244 | |||
| a577595ee6 | |||
| 8db31ddaf0 | |||
| e371b116f7 | |||
| 3212d551d2 | |||
| 8d557a63a0 | |||
| ee9866c28e | |||
| 8d9dd6c275 | |||
| 0841fb4609 | |||
| 5b059b70e6 | |||
| 3319563b22 | |||
| ddc2884f61 | |||
| 5dfd24b5de | |||
| 7ae145b6c1 | |||
| 7909b10636 | |||
| bab6c14499 | |||
| c816ecdbb7 | |||
| 024f8f78bd | |||
| 94595346b7 | |||
| 8a4d730c5a | |||
| 3226f55a7d | |||
| 8eba77b26c | |||
| a0c1408f8e | |||
| 7e726010b8 | |||
| 4abd9b7e85 | |||
| e984c026b9 | |||
| 4affffaf0a | |||
| c55338e879 | |||
| 1c288d9646 | |||
| d37bad643d | |||
| f493901e8d | |||
| 934f3511f7 | |||
| 62a1bc69f7 | |||
| 741389b71c | |||
| 9dd4543fc1 | |||
| af89743b40 | |||
| 7f0400400c | |||
| d9cf6c9a49 | |||
| 1a9114da7e | |||
| b5bc559b11 | |||
| 8e67c7aecb | |||
| 6b884af9cc | |||
| e768fe71a3 | |||
| 5f8d081b0e | |||
| 20c3dacded | |||
| 6fdf5e1994 | |||
| 92210a381a | |||
| cd2a82f61c | |||
| 4eae444c93 | |||
| 02993336de | |||
| 2c38d2742b | |||
| a724e0a086 | |||
| 399d9da9e5 | |||
| 6f0e0d65ee | |||
| 20f2b87d99 | |||
| 56f2c3a4a6 | |||
| a1a13a6846 | |||
| c244cf658c | |||
| 6fc0e5982a | |||
| 84fa03bf77 | |||
| 11cabcee32 | |||
| 1cdc54e59e | |||
| d2288e009f | |||
| d81b19bfc6 | |||
| b2fb1e9c4c | |||
| cac84abbef | |||
| 191abf022f | |||
| 0011a7f943 | |||
| 697b53e68a | |||
| 0e5c40e9a2 | |||
| bb313d29bf | |||
| 3f54cbef6a | |||
| 00cae2f82b | |||
| d1d37ee4b3 | |||
| 08a66bc219 | |||
| 35c9731a85 | |||
| d458e9849d | |||
| b94d6e911d | |||
| 4fb39d564e | |||
| 10b370f11b | |||
| 479e0d4d5a | |||
| 4059cd591e | |||
| 9565e82850 | |||
| 29ada1899e | |||
| 67d3ce545e | |||
| 9b8d372fa6 | |||
| dda6a63e74 | |||
| 2fc89282b9 | |||
| cca82dd9f7 | |||
| 1e51061ddc | |||
| 267e5a3509 | |||
| e040a4fc11 | |||
| 6f4649c778 | |||
| 177f862263 | |||
| 93456e9530 | |||
| 8ee9354327 | |||
| 6f4ad3723f | |||
| f3370e89ea | |||
| 330fc8d320 | |||
| f0b979ac8c | |||
| f3ebeb7cd3 | |||
| 981a97ed05 | |||
| 9ed4d5f6bb | |||
| e43189f052 | |||
| 0fcaca2b5a | |||
| 3db2f8c96e | |||
| b0ebc02434 | |||
| 0be0194ab5 | |||
| 7d99f56fa5 | |||
| 6eec004781 | |||
| 70603d95d4 | |||
| c78f8b1079 | |||
| 9ec76410f7 | |||
| e47023c4ba | |||
| 133805eab5 | |||
| 878588d567 | |||
| d88a9f204c | |||
| 1ab2b56f32 | |||
| b41fe0fa54 | |||
| 1326669f01 | |||
| e37cd4a1cc | |||
| 1eb86892de | |||
| 91095f28e9 | |||
| e41ad51634 | |||
| 2eabfe2587 | |||
| a71d666619 | |||
| 38254fee48 | |||
| 5cc66b3a3a | |||
| d007db7a1b | |||
| 935862a66c | |||
| 0c83fcf35e | |||
| bc5aa57319 | |||
| 231b733222 | |||
| c02d7212bb | |||
| df5ef074ad | |||
| 04179fde77 | |||
| 0cd9d63b15 | |||
| b5b2f1c362 | |||
| 14dd557e58 | |||
| 1e7cd1c7be | |||
| 0a04774fac | |||
| 720ad804a4 | |||
| 175b10e59d | |||
| 03d923441e | |||
| 3da61070eb | |||
| 6a0acb1c20 | |||
| 3195994444 | |||
| 7ab361c2b8 | |||
| 894375c143 | |||
| 225df5c6da | |||
| 1a2d595303 | |||
| cccff5726c | |||
| 9a354181b0 | |||
| c7dce8bedc | |||
| adecdbdb4f | |||
| 06d40ddbae | |||
| e009a9e1e3 | |||
| d22610abb3 | |||
| 8c033f4eab | |||
| cb6c1819f4 | |||
| 762cfc66c1 | |||
| 1d653b2756 | |||
| b7927b787d | |||
| e57019b39c | |||
| a09a88ff5f | |||
| c1a4b442a3 | |||
| 34166dedc6 | |||
| 9095f3b470 | |||
| 5255959614 | |||
| 1b92b1d207 | |||
| 67eca35a9a | |||
| a35e304561 | |||
| f93c81624d | |||
| d47c2cb5fb | |||
| 45ab8e8904 | |||
| 0f3497e867 | |||
| b0e19226eb | |||
| 95589bcf73 | |||
| 03c7f41457 | |||
| cd0c068b3f | |||
| 05d5d85bee | |||
| d213328fd9 | |||
| 9235c22e04 | |||
| c1ca8c707c | |||
| 6c4ded50d0 | |||
| 86b8d3e250 | |||
| 7b94c56e7c | |||
| c6f963b6be | |||
| 181217e755 | |||
| 00a6a5debe | |||
| 91f85ea7c1 | |||
| aaf4a264a1 | |||
| b03a97d02d | |||
| a69a9dcb57 | |||
| 0a88b26160 | |||
| 3a79652c59 | |||
| a0ed097b6c | |||
| a9d6f8fa58 | |||
| 2ebad01847 | |||
| 71716c1ad5 | |||
| 240f35af26 | |||
| 1ebbb874ef | |||
| eef972c679 | |||
| 011af03e8a | |||
| 24843ab72d | |||
| 65c0053df4 | |||
| f391e17fb6 | |||
| 44fc7dc855 | |||
| 99d895c951 | |||
| 39e59b5b8b | |||
| 7bc3e350ba | |||
| fe7c10d527 | |||
| 272875eb69 | |||
| 16df1c99a6 | |||
| 403db5133e | |||
| 43312ba412 | |||
| f4a53f2705 | |||
| 0a41765551 | |||
| 28789200c3 | |||
| 350e41714f | |||
| 84b70b73c5 | |||
| 68e0000afc | |||
| 33eeabf5e1 | |||
| a4f0a08469 | |||
| 7f7b103945 | |||
| 8fddb599ef | |||
| 313d574a08 | |||
| f7d6ed00fd | |||
| 6c570acfe6 | |||
| 2ba836c284 | |||
| 5b32a402a6 | |||
| 10aaff4cc8 | |||
| 40025169d4 | |||
| 38a903469c | |||
| 70a0a41787 | |||
| 318f9e5564 | |||
| d73816285e | |||
| 86ac172cfd | |||
| 703c7e46fa | |||
| 747c2e4a0e | |||
| 7d89418874 | |||
| 64806b143b | |||
| 7b669a27a8 | |||
| 62c2080fee | |||
| f5d6d4ef55 | |||
| 4513ce8ac7 | |||
| 88eb890c46 | |||
| 9a66952f50 | |||
| d0db0abdd4 | |||
| d9cb2b7961 | |||
| 3eedc0b30e | |||
| b51ee12331 | |||
| 744e147a1a | |||
| 8cc0bffcae | |||
| 2b3cf5c095 | |||
| 94f15d66da | |||
| 8edd5ea901 | |||
| 1e23528258 | |||
| 9447a685f2 | |||
| 7155708d4b | |||
| a76b8798ae | |||
| 016b92e5aa | |||
| 6085879d6f | |||
| 42652d0fcb | |||
| 5e8917430b | |||
| cdcefe6fbc | |||
| 0678263f3e | |||
| aa69b8157e | |||
| 47ce23f4e8 | |||
| f6fe3fb2bd | |||
| fabc380784 | |||
| 7c845d0f9a | |||
| 50da3d7a68 | |||
| da1b268862 | |||
| 73e46b399b | |||
| 431062f123 | |||
| 3846932517 | |||
| aa10a58b60 | |||
| 9db6d2cb45 | |||
| 509dcac22e | |||
| f3102ee835 | |||
| 4c3056326b | |||
| 1339f51256 | |||
| 408c5d82e4 | |||
| 71a58814b4 | |||
| 870f2e93f4 | |||
| 842a2b1da6 | |||
| bf7de1e5d1 | |||
| ba0dd0e0fb | |||
| e0c7480dd2 | |||
| 9b324ca7cd | |||
| 0cb824076b | |||
| 02320ce9c8 | |||
| 6d82621a34 | |||
| 0dae857364 | |||
| 9a974365a7 | |||
| e9e57803ae | |||
| a3b7d11ae3 | |||
| 2248f49f9d | |||
| f96e73d7b9 | |||
| ece0e55f98 | |||
| db207f4995 | |||
| f94f8d3abe | |||
| 017eb92c6a | |||
| 765d526c16 | |||
| e71c6b14d0 | |||
| 210adad3f6 | |||
| 72b1d4ffcd | |||
| e2fc7178c4 | |||
| 58b360ed6c | |||
| 985f4dc19d | |||
| 757b9f724d | |||
| fbfc943301 | |||
| 4c70f0bc5f | |||
| ef27c020e0 | |||
| 23d9d88492 | |||
| fffbb9708a | |||
| e7ff1fd56c | |||
| 5b962eeb1d | |||
| 08581f24ea | |||
| 5e9141f03a | |||
| 2269fc7709 | |||
| b966366bc4 | |||
| e2b0695cb8 | |||
| 000547ae21 | |||
| 8cc19b67f9 | |||
| fcb6377b21 | |||
| 8a54b6fcbc | |||
| ecdc4b283d | |||
| d9ab3cfe47 | |||
| 72fc0c5431 | |||
| 044096c8d7 | |||
| 96b6be3398 | |||
| 42fb2e0d22 | |||
| 227eb3cf46 | |||
| 0e007afed3 | |||
| ddf023f078 | |||
| a9d1fe7baf | |||
| aeadca6f60 | |||
| 17233d8ded | |||
| 797783b59e | |||
| e5e76e2dcb | |||
| 0fc80958af | |||
| b43ab082b4 | |||
| fcf61605d6 | |||
| c5053638df | |||
| c5fbd3e528 | |||
| 117c33a2ea | |||
| 327de18f1a | |||
| 437904bcd8 | |||
| 6d75b4660e | |||
| a110019345 | |||
| da530a2211 | |||
| 51a512e568 | |||
| 5df6378e80 | |||
| a479dc718b | |||
| c39bb1d6d1 | |||
| 5c1ca6a895 | |||
| 24c1186061 | |||
| 8ae4aeb3e8 | |||
| 9daa701a28 | |||
| 336c2415b3 | |||
| 49731501d6 | |||
| 661d38404a | |||
| e869338703 | |||
| b55c1e3c8f | |||
| 3820f00182 | |||
| 178fb25a2f | |||
| 6b2d5aebf8 | |||
| 0d3bfda08c | |||
| 11f33236cf | |||
| db7288c709 | |||
| a31e51152b | |||
| 97b8632b74 | |||
| 2ebbe334bf | |||
| 7571a1cb80 | |||
| b4f794c275 | |||
| 2f1adffba3 | |||
| 0047e6e879 | |||
| e8453c76a7 | |||
| 5d7d0ffe5b | |||
| 39c9d83f1b | |||
| b38f7553a6 | |||
| 9b887a6b4c | |||
| b43db20bc4 | |||
| 8e38ceb13e | |||
| 18c3cf21c6 | |||
| a73564a24d | |||
| 9c6029d152 | |||
| 7a35413da5 | |||
| 660e714983 | |||
| 9edfcd5058 | |||
| cd8f785881 | |||
| 24895173d6 | |||
| 6a1c26ce70 | |||
| ba06fb60c2 | |||
| 3204eee7ac | |||
| 815b8696d2 | |||
| 2e80c19e0e | |||
| 170cc09627 | |||
| 8fd8de607f | |||
| ebdfd7f499 | |||
| 19669281d5 | |||
| ccbf545fe2 | |||
| 550ad57354 | |||
| dbb488ba52 | |||
| 49d2438ea0 | |||
| 92ba297bb7 | |||
| cad3d04d08 | |||
| f1e002998e | |||
| c42edc4efd | |||
| e5056c6ea1 | |||
| 56a4bd9e82 | |||
| 9449f4f0ec | |||
| 5111265019 | |||
| 750db2fe4d | |||
| 36f9fb48a8 | |||
| b857ab3eed | |||
| dfc2ea3a4e | |||
| dfc2ae4896 | |||
| 272b91609c | |||
| f47036b6df | |||
| bed9c9deac | |||
| 837c75f8f0 | |||
| dee3d2ff90 | |||
| 02b99b4622 | |||
| 37d73b6962 | |||
| 35c56538dd | |||
| 13b7553641 | |||
| bb117d4ee6 | |||
| b0b2741422 | |||
| ca83f0ec7b | |||
| 1ab5e57017 | |||
| a34f02db00 |
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
*.pyc
|
||||
~$*
|
||||
notes/accounting/excel_web_api/.env
|
||||
162
AGENTS.md
Normal file
162
AGENTS.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# AGENTS.md
|
||||
|
||||
Guide rapide pour les agents qui codent dans ce repository.
|
||||
|
||||
## 1) Contexte du projet
|
||||
|
||||
- Codebase Tryton monolithique (coeur + modules metier).
|
||||
- Noyau serveur a la racine: `application.py`, `wsgi.py`, `admin.py`, `worker.py`, `cron.py`.
|
||||
- Couches framework importantes:
|
||||
- ORM: `model/`
|
||||
- Meta/systeme (`ir`): `ir/`
|
||||
- Protocoles RPC: `protocols/`
|
||||
- Backend DB: `backend/`
|
||||
- Modules metier: `modules/<module_name>/` (~220 modules).
|
||||
|
||||
## 2) Regles de travail pour agent
|
||||
|
||||
- Ne jamais toucher des fichiers sans rapport avec la demande.
|
||||
- Limiter le scope de modif au minimum necessaire.
|
||||
- Respecter le style existant du module cible.
|
||||
- Ne pas supprimer du code legacy sans verifier les usages.
|
||||
- Si comportement incertain: preferer un patch conservateur + test.
|
||||
|
||||
## 3) Zones de bruit a ignorer pendant l'exploration
|
||||
|
||||
- `.venv/`
|
||||
- `__pycache__/`
|
||||
- `build/` (quand present dans des sous-modules)
|
||||
- Fichiers temporaires editeur (ex: `*.swp`)
|
||||
|
||||
## 4) Comment choisir ou coder selon le besoin
|
||||
|
||||
- Si bug ORM/champs:
|
||||
- Lire `model/fields/*.py` et les tests `tests/test_field_*.py`.
|
||||
- Si bug transaction/DB:
|
||||
- Lire `transaction.py`, `backend/*/database.py`, `tests/test_backend.py`.
|
||||
- Si bug API/RPC/HTTP:
|
||||
- Lire `wsgi.py`, `rpc.py`, `protocols/*`, `tests/test_rpc.py`, `tests/test_wsgi.py`.
|
||||
- Si bug metier:
|
||||
- Modifier uniquement `modules/<module>/` + ses tests.
|
||||
- Conventions de champs dates:
|
||||
- Dans ce projet, ne pas introduire de `fields.DateTime`.
|
||||
- Utiliser `fields.Date` pour les dates metier et les champs de suivi UI, sauf demande explicite deja existante dans le module cible.
|
||||
- Si bug template Relatorio (`.fodt`):
|
||||
- Lire d'abord le template standard voisin du meme domaine (`invoice.fodt`, `sale.fodt`, etc.).
|
||||
- Preferer des proprietes Python simples exposees par le modele plutot que des expressions Genshi complexes dans le template.
|
||||
- Dans les placeholders XML, utiliser `"` et `'` plutot que des antislashs type `\'`.
|
||||
- Si un document facture depend fortement d'une vente/achat, ajouter au besoin un petit pont Python pour exposer des `report_*` stables au template.
|
||||
- Pour les templates `stock.shipment.in`, preferer aussi des proprietes `report_*` sur le shipment plutot que des contextes ad hoc (`si_*`) quand le document devient metier ou client-specifique.
|
||||
- Si plusieurs actions de report pointent vers `report_name = 'account.invoice'`, verifier aussi le cache `invoice_report_cache` dans `modules/account_invoice/invoice.py`: un mauvais cache peut faire croire que plusieurs actions utilisent le meme `.fodt`.
|
||||
- Avant de conclure qu'un template ou une action est faux, verifier si le report alternatif doit bypasser le cache standard.
|
||||
- Pour les templates shipment, ne pas supposer qu'une variable locale comme `shipment` sera definie partout dans Genshi, surtout dans les headers/footers; preferer `records[0]....` ou des placeholders alignes sur le scope reel du report.
|
||||
- Dans `purchase_trade`, pour remonter d'une facture vers shipment, pro forma, freight ou autres donnees logistiques, privilegier le lot physique comme pont entre `purchase.line`, `sale.line` et shipment.
|
||||
- Pour `FREIGHT VALUE`, ne pas lire un champ direct sur la facture: retrouver le fee de shipment (`shipment_in`) dont le produit est `Maritime freight`, puis utiliser `fee.get_amount()`.
|
||||
- Rappels session templates (2026-04-08):
|
||||
- `insurance.fodt`: le texte "insured for account of" doit afficher la compagnie courante (shipment.company.party), pas le client.
|
||||
- `insurance.fodt`: exposer des proprietes Python `report_*` sur `stock.shipment.in` pour les montants (incoming moves) et les zones client-specifiques.
|
||||
- `insurance.fodt`: "Amount insured" suit la regle metier 110% du montant incoming (base calculee via lot -> purchase.line.unit_price * quantite courante convertie).
|
||||
- `insurance.fodt`: zone "Contact the following surveyor" alimentee par une propriete dediee, avec champ `surveyor` (party.party) cote shipment.
|
||||
- `packing_list.fodt`: date en haut a droite = date du jour; unites Net/Gross = unite de `purchase.line`.
|
||||
- `bill.fodt` (sale): la 2eme date doit etre une vraie maturity date (depuis `invoice.lines_to_pay.maturity_date`), pas `payment_term.rec_name`.
|
||||
- `bill.fodt` (sale): le montant en lettres doit provenir du montant du bill (facture/total), pas du `unit_price` de ligne.
|
||||
- Quand un template affiche les placeholders en brut, verifier que les champs sont bien des placeholders Relatorio dans le XML (pas du texte litteral).
|
||||
- Eviter les apostrophes echappees style `\'` dans placeholders; preferer `"` et `'`.
|
||||
|
||||
## 4.bis) Memos metier et templates
|
||||
|
||||
- Regles metier transverses:
|
||||
- `notes/business_rules.md`
|
||||
- Regles metier locales `purchase_trade`:
|
||||
- `modules/purchase_trade/docs/business-rules.md`
|
||||
- `modules/purchase_trade/docs_source/business/` pour les sources de verite
|
||||
des pages business publiees dans le wiki
|
||||
- `modules/purchase_trade/docs/business/` pour les pages generees lues par le
|
||||
wiki
|
||||
- Decisions templates / reports:
|
||||
- `notes/template_business_rules.md`
|
||||
- Documentation comptable et reporting:
|
||||
- `notes/accounting/README.md`
|
||||
- `notes/accounting/gap_analysis_workflow.md`
|
||||
- `notes/accounting/reporting.md`
|
||||
- `notes/accounting/business_rules.md`
|
||||
- `notes/accounting/purchase_trade_extensions.md`
|
||||
- Regles sensibles `purchase_trade` a relire avant de toucher lots, quantites
|
||||
ou fees:
|
||||
- `modules/purchase_trade/AGENTS.md`
|
||||
- `modules/purchase_trade/docs_source/business/lots-and-quantities.md`
|
||||
- `modules/purchase_trade/docs/business-rules.md` BR-PT-020 / BR-PT-021
|
||||
(`quantity_theorical`, `lot.qt`, lots physiques, fees et PnL fee).
|
||||
|
||||
## 5) Workflow de modification (obligatoire)
|
||||
|
||||
1. Identifier le module et le flux impacte.
|
||||
2. Localiser un test existant proche du comportement a changer.
|
||||
3. Implementer le plus petit patch possible.
|
||||
4. Ajouter/adapter les tests au plus pres du changement.
|
||||
5. Lancer la validation ciblee (pas toute la suite si inutile).
|
||||
6. Donner un resume du risque residuel.
|
||||
|
||||
## 6) Checklist avant de rendre une modif
|
||||
|
||||
- Le changement est-il limite au domaine demande ?
|
||||
- Le comportement existant non cible est-il preserve ?
|
||||
- Les droits/regles (`ir.rule`, acces) sont-ils impactes ?
|
||||
- Les vues XML et labels sont-ils coherents si un champ change ?
|
||||
- Les tests modifies couvrent-ils le bug/la feature ?
|
||||
- Le message de commit (si demande) explique clairement le pourquoi ?
|
||||
|
||||
## 7) Tests: point de depart pratique
|
||||
|
||||
- Suite coeur: `tests/test_tryton.py`
|
||||
- Tests coeur par domaine: `tests/test_*.py`
|
||||
- Tests module:
|
||||
- `modules/<module>/tests/test_module.py`
|
||||
- `modules/<module>/tests/test_scenario.py`
|
||||
- `modules/<module>/tests/scenario_*.rst`
|
||||
|
||||
Quand possible, lancer d'abord la cible minimale:
|
||||
|
||||
- fichier de test touche
|
||||
- puis fichier voisin de regression
|
||||
- puis suite plus large uniquement si necessaire
|
||||
|
||||
## 8) Contrat de sortie attendu de l'agent
|
||||
|
||||
Toujours fournir:
|
||||
|
||||
- Liste des fichiers modifies
|
||||
- Resume fonctionnel (ce qui change)
|
||||
- Resume technique (pourquoi ce design)
|
||||
- Tests executes + resultat
|
||||
- Risques residuels et impacts potentiels
|
||||
|
||||
## 9) Cas sensibles (demander confirmation humaine)
|
||||
|
||||
- Changement schema/structure de donnees
|
||||
- Changement de logique de securite/acces
|
||||
- Changement de comportement transverse (transaction, pool, RPC, worker)
|
||||
- Refactor multi-modules sans ticket explicite
|
||||
|
||||
## 10) Raccourci de demarrage pour agent
|
||||
|
||||
1. Lire ce fichier.
|
||||
2. Lire le(s) fichier(s) touche(s) et leurs tests.
|
||||
3. Proposer le patch minimal.
|
||||
4. Implementer + tester cible.
|
||||
5. Rendre avec le contrat de sortie (section 8).
|
||||
- Rappels session 2026-04-09:
|
||||
- `invoice_ict.fodt` / `invoice_ict_final.fodt`: poids et unites depuis `lot.qt.hist` / `lot_unit_line`, priorite lots `physic`, sinon lot `virtual` unique.
|
||||
- `invoice_ict.fodt` / `invoice_ict_final.fodt`: infos shipment depuis les lots reels des lignes facture; ne rien afficher si plusieurs shipments differents.
|
||||
- `invoice_ict.fodt` / `invoice_ict_final.fodt`: `S/I` = `shipment.reference`; `NB BALES: 0` => `Unchanged` sur le final.
|
||||
- `invoice_ict.fodt` / `invoice_ict_final.fodt`: quantites uniformisees a `2` decimales; conversion `LBS` via UoM, jamais via un facteur fixe aveugle.
|
||||
- `invoice_ict.fodt` / `invoice_ict_final.fodt`: si plusieurs lignes reutilisent le meme lot, les lignes detaillees suivent la quantite facturee convertie, mais le `GROSS` global doit rester le vrai delta historique du lot.
|
||||
- `sale_ict.fodt`: meme priorite lots; les mots suivent l'unite reelle; le total convertit vers une unite commune, qui est celle du lot virtuel seulement s'il y a un seul lot virtuel sur tout le report.
|
||||
- `lot.report.r_del_period`: utiliser `sale.line.del_period` pour `lot_s` sans `lot_p`, sinon `purchase.line.del_period`.
|
||||
- `lot.do_weighing`: `lot_qt` editable et ecrasement direct de `lot.lot_qt`.
|
||||
- `account.invoice`: `Validate` cree aussi le `account.move` pour les factures client, attribue aussi le `number` a ce stade pour les factures client comme fournisseur; `Post` ne doit plus forcer une fresh session sur ce flux.
|
||||
- `pricing.pricing`: saisie manuelle autorisee meme sans composant; en manuel, l'utilisateur saisit seulement `Qt` et `Settl. price`; `fixed_qt`, `fixed_qt_price`, `unfixed_qt`, `unfixed_qt_price` et `eod_price` sont derives automatiquement.
|
||||
- `pricing.pricing`: en manuel, `fixed_qt` = cumul des `quantity`, `fixed_qt_price` = moyenne ponderee cumulee des `settl_price`, `unfixed_qt` = reste a fixer, `unfixed_qt_price` = `settl_price` de la ligne.
|
||||
- `pricing.pricing`: `eod_price` reste non editable et calcule en prix moyen pondere; `last=True` gere par groupe `line + component`, choisi sur la `pricing_date` la plus grande.
|
||||
- `purchase_trade`: `trader` filtre sur `TRADER`, `operator` sur `OPERATOR`; fallback sur `quantity` si `quantity_theorical` est vide dans les quotas/pricings.
|
||||
- `sale.line` / `purchase.line`: en mode `basis`, sans `price_component`, le `Price` et le `Fix. progress` de la ligne doivent remonter depuis la ligne `Summary` sans component.
|
||||
1
debug.log
Normal file
1
debug.log
Normal file
@@ -0,0 +1 @@
|
||||
[0407/143111.471:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: Accès refusé. (0x5)
|
||||
5
deployment/README.md
Normal file
5
deployment/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Deployment Notes
|
||||
|
||||
- Runbook onboarding SSH: `deployment/runbooks/vps-onboarding.md`
|
||||
- VPS 46.202.173.47 credentials: `deployment/vps/46.202.173.47-credentials.md`
|
||||
- VPS 46.202.173.47 quickstart: `deployment/vps/46.202.173.47-quickstart.md`
|
||||
57
deployment/runbooks/vps-onboarding.md
Normal file
57
deployment/runbooks/vps-onboarding.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# Procedure - Ajouter un nouveau VPS (SSH)
|
||||
|
||||
Date de reference: 2026-04-07
|
||||
|
||||
## 1) Preparation locale (Windows)
|
||||
|
||||
1. Creer le dossier SSH local si absent:
|
||||
`New-Item -ItemType Directory -Force $env:USERPROFILE\.ssh`
|
||||
|
||||
2. Generer une cle dediee VPS:
|
||||
`ssh-keygen -t ed25519 -C "vps-deploy" -f $env:USERPROFILE\.ssh\vps_deploy_key`
|
||||
|
||||
3. Lire la cle publique:
|
||||
`Get-Content $env:USERPROFILE\.ssh\vps_deploy_key.pub`
|
||||
|
||||
## 2) Installer la cle sur le VPS
|
||||
|
||||
1. Se connecter au VPS avec mot de passe (premiere fois):
|
||||
`ssh <user>@<ip_vps>`
|
||||
|
||||
2. Sur le VPS, preparer le dossier SSH:
|
||||
`mkdir -p ~/.ssh`
|
||||
`chmod 700 ~/.ssh`
|
||||
|
||||
3. Ajouter la cle publique (une ligne complete):
|
||||
`echo "ssh-ed25519 ... vps-deploy" >> ~/.ssh/authorized_keys`
|
||||
`chmod 600 ~/.ssh/authorized_keys`
|
||||
|
||||
## 3) Tester la connexion par cle
|
||||
|
||||
Depuis Windows:
|
||||
`ssh -i $env:USERPROFILE\.ssh\vps_deploy_key <user>@<ip_vps>`
|
||||
|
||||
## 4) Test operationnel minimal
|
||||
|
||||
Creer un dossier distant:
|
||||
`ssh -i $env:USERPROFILE\.ssh\vps_deploy_key <user>@<ip_vps> "mkdir -p ~/test_codex_deploy && ls -ld ~/test_codex_deploy"`
|
||||
|
||||
## 5) Durcissement recommande
|
||||
|
||||
- Desactiver l'authentification par mot de passe apres validation de la cle.
|
||||
- Utiliser une cle dediee par environnement (dev/staging/prod).
|
||||
- Documenter user + IP + chemin de cle dans une fiche VPS separee.
|
||||
|
||||
## 6) Note importante - Contrainte sandbox Codex
|
||||
|
||||
- Si une commande SSH/SCP echoue avec un message proche de:
|
||||
- `Identity file ... not accessible: Permission denied`
|
||||
- Cause probable:
|
||||
- la session est en sandbox et ne peut pas lire la cle locale dans
|
||||
`C:\Users\<user>\.ssh\...`.
|
||||
- Action:
|
||||
- relancer la commande en mode `require_escalated` (hors sandbox) pour
|
||||
autoriser l'acces a la cle locale.
|
||||
- Exemple observe le 2026-04-07:
|
||||
- creation du dossier `/root/test` sur `46.202.173.47` reussie uniquement
|
||||
apres escalation.
|
||||
36
deployment/vps/46.202.173.47-credentials.md
Normal file
36
deployment/vps/46.202.173.47-credentials.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Fiche VPS - 46.202.173.47
|
||||
|
||||
Date de reference: 2026-04-07
|
||||
|
||||
## Identite serveur
|
||||
|
||||
- IP: `46.202.173.47`
|
||||
- Hostname alias conseille: `vps3`
|
||||
|
||||
## Acces
|
||||
|
||||
- Cle publique Laurent Barontini (vps-deploy):
|
||||
`ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEm8JMCYsk6I1IoYhIHXNrdyERHdh+eeDCJagOHaRAEK vps-deploy`
|
||||
|
||||
- Cle publique Sylvain Duvernay (s.duvernay@singa-associates.com):
|
||||
`ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG6Xsp/v6q6JO04ETv1880qoSPptUMxlWQvgcBz67o63 s.duvernay@singa-associates.com`
|
||||
- Fichier local: `$env:USERPROFILE\.ssh\id_ed25519`
|
||||
|
||||
- Mot de passe fourni:
|
||||
`!!OpenSquared!!`
|
||||
|
||||
- Utilisateur SSH:
|
||||
'root'
|
||||
- Port SSH:
|
||||
'22'
|
||||
|
||||
## Commande de connexion type
|
||||
|
||||
- Laurent Barontini (cle vps-deploy):
|
||||
`ssh -i $env:USERPROFILE\.ssh\vps_deploy_key <user>@46.202.173.47`
|
||||
|
||||
- Sylvain Duvernay (cle id_ed25519):
|
||||
`ssh -i $env:USERPROFILE\.ssh\id_ed25519 <user>@46.202.173.47`
|
||||
|
||||
- Avec port custom:
|
||||
`ssh -i $env:USERPROFILE\.ssh\id_ed25519 -p <port> <user>@46.202.173.47`
|
||||
39
deployment/vps/46.202.173.47-quickstart.md
Normal file
39
deployment/vps/46.202.173.47-quickstart.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Commandes Rapides - VPS 46.202.173.47
|
||||
|
||||
Date de reference: 2026-04-07
|
||||
|
||||
## Cles SSH par utilisateur
|
||||
|
||||
| Utilisateur | Fichier cle locale |
|
||||
|---|---|
|
||||
| Laurent Barontini | `$env:USERPROFILE\.ssh\vps_deploy_key` |
|
||||
| Sylvain Duvernay | `$env:USERPROFILE\.ssh\id_ed25519` |
|
||||
|
||||
> Les commandes ci-dessous utilisent `id_ed25519` (Sylvain Duvernay).
|
||||
|
||||
## 1) Test SSH
|
||||
|
||||
`ssh -i $env:USERPROFILE\.ssh\id_ed25519 <user>@46.202.173.47 "echo ok"`
|
||||
|
||||
## 2) Creer un dossier test distant
|
||||
|
||||
`ssh -i $env:USERPROFILE\.ssh\id_ed25519 <user>@46.202.173.47 "mkdir -p ~/test_codex_deploy && ls -ld ~/test_codex_deploy"`
|
||||
|
||||
## 3) Lister home distant
|
||||
|
||||
`ssh -i $env:USERPROFILE\.ssh\id_ed25519 <user>@46.202.173.47 "ls -la ~"`
|
||||
|
||||
## 4) Copier un fichier local vers le VPS
|
||||
|
||||
`scp -i $env:USERPROFILE\.ssh\id_ed25519 .\local.txt <user>@46.202.173.47:~/local.txt`
|
||||
|
||||
## 5) Recuperer un fichier du VPS
|
||||
|
||||
`scp -i $env:USERPROFILE\.ssh\id_ed25519 <user>@46.202.173.47:~/remote.txt .\remote.txt`
|
||||
|
||||
## 6) Depannage sandbox (Codex)
|
||||
|
||||
- Symptome:
|
||||
- `Identity file ... not accessible: Permission denied`
|
||||
- Correctif:
|
||||
- relancer la commande SSH/SCP en mode escalade (`require_escalated`).
|
||||
@@ -93,6 +93,7 @@ class Model(
|
||||
cursor.execute(*ir_model.select(ir_model.id,
|
||||
where=ir_model.model == model.__name__))
|
||||
model_id = None
|
||||
logger.info("MODEL_NAME:%s",model.__name__)
|
||||
if cursor.rowcount == -1 or cursor.rowcount is None:
|
||||
data = cursor.fetchone()
|
||||
if data:
|
||||
|
||||
@@ -73,6 +73,7 @@ attlist.field &=
|
||||
| "text"
|
||||
| "time"
|
||||
| "timedelta"
|
||||
| "tolerance_gauge"
|
||||
| "url"
|
||||
}?
|
||||
attlist.field &= attribute fill { "0" | "1" }?
|
||||
@@ -122,6 +123,12 @@ attlist.field &= attribute symbol { text }?
|
||||
attlist.field &= [a:defaultValue = "1"] attribute grouping { "0" | "1" }?
|
||||
attlist.field &= [a:defaultValue = "square"] attribute border { "square" | "circle" | "rounded" }?
|
||||
attlist.field &= attribute loading { "lazy" | "eager" }?
|
||||
attlist.field &= attribute min { text }?
|
||||
attlist.field &= attribute max { text }?
|
||||
attlist.field &= attribute min_field { text }?
|
||||
attlist.field &= attribute max_field { text }?
|
||||
attlist.field &= attribute center { text }?
|
||||
attlist.field &= attribute digits { text }?
|
||||
image = element image { attlist.image, empty }
|
||||
attlist.image &= attribute name { text }
|
||||
attlist.image &= [a:defaultValue = "icon"] attribute type { "icon" | "url" }?
|
||||
|
||||
@@ -251,6 +251,7 @@
|
||||
<value>text</value>
|
||||
<value>time</value>
|
||||
<value>timedelta</value>
|
||||
<value>tolerance_gauge</value>
|
||||
<value>url</value>
|
||||
</choice>
|
||||
</attribute>
|
||||
@@ -577,6 +578,54 @@
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="attlist.field" combine="interleave">
|
||||
<optional>
|
||||
<attribute>
|
||||
<name ns="">min</name>
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="attlist.field" combine="interleave">
|
||||
<optional>
|
||||
<attribute>
|
||||
<name ns="">max</name>
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="attlist.field" combine="interleave">
|
||||
<optional>
|
||||
<attribute>
|
||||
<name ns="">min_field</name>
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="attlist.field" combine="interleave">
|
||||
<optional>
|
||||
<attribute>
|
||||
<name ns="">max_field</name>
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="attlist.field" combine="interleave">
|
||||
<optional>
|
||||
<attribute>
|
||||
<name ns="">center</name>
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="attlist.field" combine="interleave">
|
||||
<optional>
|
||||
<attribute>
|
||||
<name ns="">digits</name>
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="attlist.field" combine="interleave">
|
||||
<optional>
|
||||
<attribute>
|
||||
|
||||
@@ -42,6 +42,7 @@ attlist.field &=
|
||||
| "text"
|
||||
| "time"
|
||||
| "timedelta"
|
||||
| "tolerance_gauge"
|
||||
| "url"
|
||||
}?
|
||||
attlist.field &=
|
||||
@@ -75,6 +76,12 @@ attlist.field &= attribute help_field { text }?
|
||||
attlist.field &= attribute view_ids { text }?
|
||||
attlist.field &= attribute symbol { text }?
|
||||
attlist.field &= [a:defaultValue = "1"] attribute grouping { "0" | "1" }?
|
||||
attlist.field &= attribute min { text }?
|
||||
attlist.field &= attribute max { text }?
|
||||
attlist.field &= attribute min_field { text }?
|
||||
attlist.field &= attribute max_field { text }?
|
||||
attlist.field &= attribute center { text }?
|
||||
attlist.field &= attribute digits { text }?
|
||||
prefix = element prefix { attlist.affix, empty }
|
||||
suffix = element suffix { attlist.affix, empty }
|
||||
attlist.affix &= attribute string { text }?
|
||||
|
||||
@@ -139,6 +139,7 @@
|
||||
<value>text</value>
|
||||
<value>time</value>
|
||||
<value>timedelta</value>
|
||||
<value>tolerance_gauge</value>
|
||||
<value>url</value>
|
||||
</choice>
|
||||
</attribute>
|
||||
@@ -330,6 +331,54 @@
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="attlist.field" combine="interleave">
|
||||
<optional>
|
||||
<attribute>
|
||||
<name ns="">min</name>
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="attlist.field" combine="interleave">
|
||||
<optional>
|
||||
<attribute>
|
||||
<name ns="">max</name>
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="attlist.field" combine="interleave">
|
||||
<optional>
|
||||
<attribute>
|
||||
<name ns="">min_field</name>
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="attlist.field" combine="interleave">
|
||||
<optional>
|
||||
<attribute>
|
||||
<name ns="">max_field</name>
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="attlist.field" combine="interleave">
|
||||
<optional>
|
||||
<attribute>
|
||||
<name ns="">center</name>
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="attlist.field" combine="interleave">
|
||||
<optional>
|
||||
<attribute>
|
||||
<name ns="">digits</name>
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
<define name="prefix">
|
||||
<element>
|
||||
<name ns="">prefix</name>
|
||||
|
||||
@@ -30,8 +30,8 @@ class Binary(Field):
|
||||
on_change_with=None, depends=None, context=None, loading='lazy',
|
||||
filename=None, file_id=None, store_prefix=None):
|
||||
self.filename = filename
|
||||
self.file_id = file_id
|
||||
self.store_prefix = store_prefix
|
||||
self.file_id = None #file_id
|
||||
self.store_prefix = None #store_prefix
|
||||
super(Binary, self).__init__(string=string, help=help,
|
||||
required=required, readonly=readonly, domain=domain, states=states,
|
||||
on_change=on_change, on_change_with=on_change_with,
|
||||
|
||||
@@ -103,15 +103,26 @@ class Model(URLMixin, PoolBase, metaclass=ModelMeta):
|
||||
|
||||
@classmethod
|
||||
def _get_name(cls):
|
||||
'''
|
||||
Returns the first non-empty line of the model docstring.
|
||||
'''
|
||||
assert cls.__doc__, '%s has no docstring' % cls
|
||||
if cls.__doc__ is None:
|
||||
print("\n💥 MODELE SANS DOCSTRING :", cls.__name__, " (module:", cls.__module__, ")")
|
||||
raise Exception("MODELE SANS DOCSTRING")
|
||||
|
||||
lines = cls.__doc__.splitlines()
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if line:
|
||||
return line
|
||||
if lines:
|
||||
return lines[0]
|
||||
return cls.__name__
|
||||
|
||||
# @classmethod
|
||||
# def _get_name(cls):
|
||||
# '''
|
||||
# Returns the first non-empty line of the model docstring.
|
||||
# '''
|
||||
# assert cls.__doc__, '%s has no docstring' % cls
|
||||
# lines = cls.__doc__.splitlines()
|
||||
# for line in lines:
|
||||
# line = line.strip()
|
||||
# if line:
|
||||
# return line
|
||||
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
|
||||
10
modules/AGENTS.md
Normal file
10
modules/AGENTS.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# AGENTS.md - Regles globales des modules
|
||||
|
||||
Ce guide complete le `AGENTS.md` racine et s'applique a tous les modules sous `modules/`.
|
||||
|
||||
## Regles transversales
|
||||
|
||||
- Ne pas introduire de `fields.DateTime` dans les modules metier sauf demande explicite et code cible deja base sur ce type.
|
||||
- Pour afficher une date issue d'un champ technique comme `create_date`, preferer un champ fonctionnel en `fields.Date` plutot qu'un acces direct en vue.
|
||||
- Si une vue doit afficher une information technique non declaree explicitement sur le modele, ajouter un champ fonctionnel dedie cote Python avant de modifier le XML.
|
||||
|
||||
@@ -30,6 +30,8 @@ def register():
|
||||
account.GeneralLedgerAccountParty,
|
||||
account.GeneralLedgerLine,
|
||||
account.GeneralLedgerLineContext,
|
||||
account.GeneralLedgerDetail,
|
||||
account.GeneralLedgerDetailContext,
|
||||
account.BalanceSheetContext,
|
||||
account.BalanceSheetComparisionContext,
|
||||
account.IncomeStatementContext,
|
||||
@@ -118,6 +120,7 @@ def register():
|
||||
Pool.register(
|
||||
account.AccountTypeStatement,
|
||||
account.GeneralLedger,
|
||||
account.GeneralLedgerDetailReport,
|
||||
account.TrialBalance,
|
||||
account.AgedBalanceReport,
|
||||
move.GeneralJournal,
|
||||
|
||||
@@ -7,9 +7,10 @@ from decimal import Decimal
|
||||
from itertools import zip_longest
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from sql import Column, Literal, Null, Window
|
||||
from sql import Column, Literal, Null, Union, Window
|
||||
from sql.aggregate import Count, Max, Min, Sum
|
||||
from sql.conditionals import Case, Coalesce
|
||||
from sql.operators import Concat
|
||||
|
||||
from trytond import backend
|
||||
from trytond.i18n import gettext
|
||||
@@ -2583,6 +2584,544 @@ class GeneralLedgerLineContext(GeneralLedgerAccountContext):
|
||||
return super().get_context(fields_names=fields_names)
|
||||
|
||||
|
||||
class GeneralLedgerDetailContext(GeneralLedgerAccountContext):
|
||||
'General Ledger Detail Context'
|
||||
__name__ = 'account.general_ledger.detail.context'
|
||||
|
||||
account = fields.Many2One(
|
||||
'account.account', "Account",
|
||||
domain=[
|
||||
('company', '=', Eval('company')),
|
||||
('type', '!=', None),
|
||||
('closed', '!=', True),
|
||||
],
|
||||
depends=['company'])
|
||||
account_from = fields.Char("Account From")
|
||||
account_to = fields.Char("Account To")
|
||||
currency = fields.Many2One('currency.currency', "Transaction Currency")
|
||||
party = fields.Many2One(
|
||||
'party.party', "Party",
|
||||
context={
|
||||
'company': Eval('company', -1),
|
||||
},
|
||||
depends=['company'])
|
||||
document_number = fields.Char("Document Number")
|
||||
|
||||
@classmethod
|
||||
def default_account(cls):
|
||||
return Transaction().context.get('account')
|
||||
|
||||
@classmethod
|
||||
def default_account_from(cls):
|
||||
return Transaction().context.get('account_from')
|
||||
|
||||
@classmethod
|
||||
def default_account_to(cls):
|
||||
return Transaction().context.get('account_to')
|
||||
|
||||
@classmethod
|
||||
def default_currency(cls):
|
||||
return Transaction().context.get('currency')
|
||||
|
||||
@classmethod
|
||||
def default_party(cls):
|
||||
return Transaction().context.get('party')
|
||||
|
||||
@classmethod
|
||||
def default_document_number(cls):
|
||||
return Transaction().context.get('document_number')
|
||||
|
||||
@classmethod
|
||||
def get_context(cls, fields_names=None):
|
||||
fields_names = fields_names.copy() if fields_names is not None else []
|
||||
fields_names += [
|
||||
'account', 'account_from', 'account_to', 'currency', 'party',
|
||||
'document_number']
|
||||
return super().get_context(fields_names=fields_names)
|
||||
|
||||
|
||||
class GeneralLedgerDetail(DescriptionOriginMixin, ModelSQL, ModelView):
|
||||
'General Ledger Detail'
|
||||
__name__ = 'account.general_ledger.detail'
|
||||
|
||||
row_type = fields.Selection([
|
||||
('opening', "Opening"),
|
||||
('movement', "Movement"),
|
||||
('closing', "Closing"),
|
||||
], "Row Type", sort=False)
|
||||
row_sequence = fields.Integer("Row Sequence")
|
||||
|
||||
company = fields.Many2One('company.company', "Company")
|
||||
account = fields.Many2One('account.account', "Account")
|
||||
account_code = fields.Char("Account Code")
|
||||
account_name = fields.Char("Account Name")
|
||||
transaction_currency = fields.Many2One(
|
||||
'currency.currency', "Transaction Currency")
|
||||
base_currency = fields.Many2One('currency.currency', "Base Currency")
|
||||
|
||||
posting_date = fields.Date("Posting Date")
|
||||
journal = fields.Many2One('account.journal', "Journal")
|
||||
journal_entry_number = fields.Char("Journal Entry Number")
|
||||
document_number = fields.Function(
|
||||
fields.Char("Document Number"), 'get_document_field')
|
||||
voucher_number = fields.Char("Voucher Number")
|
||||
document_type = fields.Function(
|
||||
fields.Char("Document Type"), 'get_document_field')
|
||||
document_date = fields.Date("Document Date")
|
||||
posting_status = fields.Selection([
|
||||
('draft', "Draft"),
|
||||
('posted', "Posted"),
|
||||
], "Posting Status", sort=False)
|
||||
|
||||
party = fields.Many2One('party.party', "Party")
|
||||
move = fields.Many2One('account.move', "Move")
|
||||
move_line = fields.Many2One('account.move.line', "Move Line")
|
||||
origin = fields.Reference('Origin', selection='get_origin')
|
||||
description = fields.Char("Line Desc.")
|
||||
move_description = fields.Char("Description")
|
||||
reference = fields.Char("Reference")
|
||||
|
||||
debit_base_currency = fields.Numeric(
|
||||
"Debit Base Currency", digits=(16, 2))
|
||||
credit_base_currency = fields.Numeric(
|
||||
"Credit Base Currency", digits=(16, 2))
|
||||
balance_base_currency = fields.Numeric(
|
||||
"Balance Base Currency", digits=(16, 2))
|
||||
running_balance_base_currency = fields.Numeric(
|
||||
"Running Balance Base Currency", digits=(16, 2))
|
||||
|
||||
debit_transaction_currency = fields.Numeric(
|
||||
"Debit Transaction Currency", digits=(16, 2))
|
||||
credit_transaction_currency = fields.Numeric(
|
||||
"Credit Transaction Currency", digits=(16, 2))
|
||||
balance_transaction_currency = fields.Numeric(
|
||||
"Balance Transaction Currency", digits=(16, 2))
|
||||
running_balance_transaction_currency = fields.Numeric(
|
||||
"Running Balance Transaction Currency", digits=(16, 2))
|
||||
|
||||
payable_qty = fields.Numeric("Quantity", digits=(16, 5))
|
||||
uom = fields.Char("UoM")
|
||||
|
||||
entered_by = fields.Many2One('res.user', "Entered By")
|
||||
entered_date = fields.Date("Entered Date")
|
||||
modified_by = fields.Many2One('res.user', "Modified By")
|
||||
modified_date = fields.Date("Modified Date")
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls._order = [
|
||||
('account_code', 'ASC'),
|
||||
('transaction_currency', 'ASC'),
|
||||
('row_sequence', 'ASC'),
|
||||
('posting_date', 'ASC'),
|
||||
('journal_entry_number', 'ASC'),
|
||||
('id', 'ASC'),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def _get_period_ids(cls, name, context):
|
||||
pool = Pool()
|
||||
Period = pool.get('account.period')
|
||||
|
||||
period = None
|
||||
if name == 'opening':
|
||||
period_ids = []
|
||||
if context.get('start_period'):
|
||||
period = Period(context['start_period'])
|
||||
elif name == 'closing':
|
||||
period_ids = []
|
||||
if context.get('end_period'):
|
||||
period = Period(context['end_period'])
|
||||
else:
|
||||
periods = Period.search([
|
||||
('fiscalyear', '=', context.get('fiscalyear')),
|
||||
('type', '=', 'standard'),
|
||||
],
|
||||
order=[('start_date', 'DESC')], limit=1)
|
||||
if periods:
|
||||
period, = periods
|
||||
else:
|
||||
start_period_ids = set(cls._get_period_ids('opening', context))
|
||||
end_period_ids = set(cls._get_period_ids('closing', context))
|
||||
return list(end_period_ids.difference(start_period_ids))
|
||||
|
||||
if period:
|
||||
if name == 'opening':
|
||||
date_clause = ('end_date', '<=', period.start_date)
|
||||
else:
|
||||
date_clause = [
|
||||
('end_date', '<=', period.end_date),
|
||||
('start_date', '<', period.end_date),
|
||||
]
|
||||
periods = Period.search([
|
||||
('fiscalyear', '=', context.get('fiscalyear')),
|
||||
date_clause,
|
||||
])
|
||||
if period.start_date == period.end_date:
|
||||
periods.append(period)
|
||||
if periods:
|
||||
period_ids = [p.id for p in periods]
|
||||
if name == 'closing':
|
||||
period_ids.append(period.id)
|
||||
return period_ids
|
||||
|
||||
@classmethod
|
||||
def _get_dates(cls, name, context):
|
||||
if name == 'opening':
|
||||
to_date = context.get('from_date') or datetime.date.min
|
||||
if to_date:
|
||||
try:
|
||||
to_date -= datetime.timedelta(days=1)
|
||||
except OverflowError:
|
||||
pass
|
||||
return None, to_date
|
||||
elif name == 'closing':
|
||||
return None, context.get('to_date')
|
||||
return context.get('from_date'), context.get('to_date')
|
||||
|
||||
@classmethod
|
||||
def _query_context(cls, name, context):
|
||||
if context.get('start_period') or context.get('end_period'):
|
||||
return {
|
||||
'periods': cls._get_period_ids(name, context),
|
||||
'from_date': None,
|
||||
'to_date': None,
|
||||
}
|
||||
elif context.get('from_date') or context.get('to_date'):
|
||||
from_date, to_date = cls._get_dates(name, context)
|
||||
return {
|
||||
'periods': None,
|
||||
'from_date': from_date,
|
||||
'to_date': to_date,
|
||||
}
|
||||
else:
|
||||
if name == 'opening':
|
||||
return {
|
||||
'periods': [],
|
||||
'from_date': None,
|
||||
'to_date': None,
|
||||
}
|
||||
return {
|
||||
'periods': None,
|
||||
'from_date': None,
|
||||
'to_date': None,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def _extra_where(cls, line, move, account, tx_currency, context):
|
||||
where = (
|
||||
(move.company == context.get('company'))
|
||||
& (account.type != Null)
|
||||
& (account.closed != Literal(True)))
|
||||
if context.get('account'):
|
||||
where &= line.account == context['account']
|
||||
if context.get('account_from'):
|
||||
where &= account.code >= context['account_from']
|
||||
if context.get('account_to'):
|
||||
where &= account.code <= context['account_to']
|
||||
if context.get('currency'):
|
||||
where &= tx_currency == context['currency']
|
||||
if context.get('party'):
|
||||
where &= line.party == context['party']
|
||||
if context.get('document_number'):
|
||||
value = '%%%s%%' % context['document_number']
|
||||
where &= (
|
||||
move.number.ilike(value)
|
||||
| move.post_number.ilike(value)
|
||||
| move.ext_ref.ilike(value))
|
||||
return where
|
||||
|
||||
@classmethod
|
||||
def _tables(cls):
|
||||
pool = Pool()
|
||||
Line = pool.get('account.move.line')
|
||||
Move = pool.get('account.move')
|
||||
Account = pool.get('account.account')
|
||||
Company = pool.get('company.company')
|
||||
line = Line.__table__()
|
||||
move = Move.__table__()
|
||||
account = Account.__table__()
|
||||
company = Company.__table__()
|
||||
return Line, line, move, account, company
|
||||
|
||||
@classmethod
|
||||
def _lot_tables(cls, Line):
|
||||
if 'lot' not in Line._fields:
|
||||
return None, None
|
||||
pool = Pool()
|
||||
try:
|
||||
Lot = pool.get('lot.lot')
|
||||
Uom = pool.get('product.uom')
|
||||
except KeyError:
|
||||
return None, None
|
||||
return Lot.__table__(), Uom.__table__()
|
||||
|
||||
@classmethod
|
||||
def _account_label(cls, account):
|
||||
return Concat(
|
||||
Concat(Coalesce(account.code, ''), ' - '),
|
||||
Coalesce(account.name, ''))
|
||||
|
||||
@classmethod
|
||||
def _opening_balance_query(cls, context):
|
||||
Line, line, move, account, company = cls._tables()
|
||||
tx_currency = Coalesce(line.second_currency, company.currency)
|
||||
base_amount = Coalesce(line.debit, 0) - Coalesce(line.credit, 0)
|
||||
tx_amount = Case(
|
||||
(line.second_currency != Null,
|
||||
Coalesce(line.amount_second_currency, 0)),
|
||||
else_=base_amount)
|
||||
|
||||
with Transaction().set_context(
|
||||
context, **cls._query_context('opening', context)):
|
||||
line_query, _ = Line.query_get(line)
|
||||
where = line_query & cls._extra_where(
|
||||
line, move, account, tx_currency, context)
|
||||
|
||||
return (line.join(move, condition=line.move == move.id)
|
||||
.join(account, condition=line.account == account.id)
|
||||
.join(company, condition=move.company == company.id)
|
||||
.select(
|
||||
line.account.as_('account'),
|
||||
tx_currency.as_('transaction_currency'),
|
||||
Sum(base_amount).as_('base_amount'),
|
||||
Sum(tx_amount).as_('transaction_amount'),
|
||||
where=where,
|
||||
group_by=[line.account, tx_currency],
|
||||
having=Sum(base_amount) != 0))
|
||||
|
||||
@classmethod
|
||||
def _line_query(cls, name, context):
|
||||
Line, line, move, account, company = cls._tables()
|
||||
lot, uom = cls._lot_tables(Line)
|
||||
opening_balance = cls._opening_balance_query(context)
|
||||
account_label = cls._account_label(account)
|
||||
quantity = Literal(None)
|
||||
unit = Literal(None)
|
||||
tx_currency = Coalesce(line.second_currency, company.currency)
|
||||
from_ = (line.join(move, condition=line.move == move.id)
|
||||
.join(account, condition=line.account == account.id)
|
||||
.join(company, condition=move.company == company.id))
|
||||
from_ = from_.join(opening_balance, 'LEFT',
|
||||
condition=(
|
||||
(opening_balance.account == line.account)
|
||||
& (opening_balance.transaction_currency == tx_currency)))
|
||||
if lot is not None:
|
||||
from_ = (from_
|
||||
.join(lot, 'LEFT', condition=Column(line, 'lot') == lot.id)
|
||||
.join(uom, 'LEFT', condition=lot.lot_unit_line == uom.id))
|
||||
quantity = lot.lot_qt
|
||||
unit = uom.symbol
|
||||
base_amount = Coalesce(line.debit, 0) - Coalesce(line.credit, 0)
|
||||
second_amount = Coalesce(line.amount_second_currency, 0)
|
||||
tx_amount = Case(
|
||||
(line.second_currency != Null, second_amount),
|
||||
else_=base_amount)
|
||||
debit_tx = Case(
|
||||
(line.second_currency == Null, Coalesce(line.debit, 0)),
|
||||
(second_amount > 0, second_amount),
|
||||
else_=Literal(0))
|
||||
credit_tx = Case(
|
||||
(line.second_currency == Null, Coalesce(line.credit, 0)),
|
||||
(second_amount < 0, -second_amount),
|
||||
else_=Literal(0))
|
||||
|
||||
with Transaction().set_context(
|
||||
context, **cls._query_context(name, context)):
|
||||
line_query, _ = Line.query_get(line)
|
||||
where = line_query & cls._extra_where(
|
||||
line, move, account, tx_currency, context)
|
||||
|
||||
window = Window(
|
||||
[line.account, tx_currency],
|
||||
order_by=[move.date.asc, move.number.asc, line.id.asc])
|
||||
|
||||
return from_.select(
|
||||
(line.id * 3).as_('id'),
|
||||
line.create_uid.as_('create_uid'),
|
||||
line.create_date.as_('create_date'),
|
||||
line.write_uid.as_('write_uid'),
|
||||
line.write_date.as_('write_date'),
|
||||
Literal('movement').as_('row_type'),
|
||||
Literal(1).as_('row_sequence'),
|
||||
move.company.as_('company'),
|
||||
line.account.as_('account'),
|
||||
account_label.as_('account_code'),
|
||||
account.name.as_('account_name'),
|
||||
tx_currency.as_('transaction_currency'),
|
||||
company.currency.as_('base_currency'),
|
||||
move.date.as_('posting_date'),
|
||||
move.journal.as_('journal'),
|
||||
move.number.as_('journal_entry_number'),
|
||||
move.post_number.as_('voucher_number'),
|
||||
move.date.as_('document_date'),
|
||||
move.state.as_('posting_status'),
|
||||
line.party.as_('party'),
|
||||
line.move.as_('move'),
|
||||
line.id.as_('move_line'),
|
||||
move.origin.as_('origin'),
|
||||
line.description.as_('description'),
|
||||
move.description.as_('move_description'),
|
||||
Coalesce(move.ext_ref, move.description).as_('reference'),
|
||||
line.debit.as_('debit_base_currency'),
|
||||
line.credit.as_('credit_base_currency'),
|
||||
base_amount.as_('balance_base_currency'),
|
||||
(Coalesce(opening_balance.base_amount, 0)
|
||||
+ Sum(base_amount, window=window)).as_(
|
||||
'running_balance_base_currency'),
|
||||
debit_tx.as_('debit_transaction_currency'),
|
||||
credit_tx.as_('credit_transaction_currency'),
|
||||
tx_amount.as_('balance_transaction_currency'),
|
||||
(Coalesce(opening_balance.transaction_amount, 0)
|
||||
+ Sum(tx_amount, window=window)).as_(
|
||||
'running_balance_transaction_currency'),
|
||||
quantity.as_('payable_qty'),
|
||||
unit.as_('uom'),
|
||||
line.create_uid.as_('entered_by'),
|
||||
line.create_date.as_('entered_date'),
|
||||
line.write_uid.as_('modified_by'),
|
||||
line.write_date.as_('modified_date'),
|
||||
where=where)
|
||||
|
||||
@classmethod
|
||||
def _summary_query(cls, name, context):
|
||||
Line, line, move, account, company = cls._tables()
|
||||
account_label = cls._account_label(account)
|
||||
tx_currency = Coalesce(line.second_currency, company.currency)
|
||||
base_amount = Coalesce(line.debit, 0) - Coalesce(line.credit, 0)
|
||||
tx_amount = Case(
|
||||
(line.second_currency != Null,
|
||||
Coalesce(line.amount_second_currency, 0)),
|
||||
else_=base_amount)
|
||||
row_sequence = 0 if name == 'opening' else 2
|
||||
row_id = (
|
||||
(Min(line.id) * 3 - 1) if name == 'opening'
|
||||
else (Min(line.id) * 3 - 2))
|
||||
|
||||
with Transaction().set_context(
|
||||
context, **cls._query_context(name, context)):
|
||||
line_query, _ = Line.query_get(line)
|
||||
where = line_query & cls._extra_where(
|
||||
line, move, account, tx_currency, context)
|
||||
|
||||
return (line.join(move, condition=line.move == move.id)
|
||||
.join(account, condition=line.account == account.id)
|
||||
.join(company, condition=move.company == company.id)
|
||||
.select(
|
||||
row_id.as_('id'),
|
||||
Literal(0).as_('create_uid'),
|
||||
Max(line.create_date).as_('create_date'),
|
||||
Literal(0).as_('write_uid'),
|
||||
Max(line.write_date).as_('write_date'),
|
||||
Literal(name).as_('row_type'),
|
||||
Literal(row_sequence).as_('row_sequence'),
|
||||
move.company.as_('company'),
|
||||
line.account.as_('account'),
|
||||
account_label.as_('account_code'),
|
||||
account.name.as_('account_name'),
|
||||
tx_currency.as_('transaction_currency'),
|
||||
company.currency.as_('base_currency'),
|
||||
Literal(None).as_('posting_date'),
|
||||
Literal(None).as_('journal'),
|
||||
Literal(None).as_('journal_entry_number'),
|
||||
Literal(None).as_('voucher_number'),
|
||||
Literal(None).as_('document_date'),
|
||||
Literal(None).as_('posting_status'),
|
||||
Literal(None).as_('party'),
|
||||
Literal(None).as_('move'),
|
||||
Literal(None).as_('move_line'),
|
||||
Literal(None).as_('origin'),
|
||||
Literal(name.title()).as_('description'),
|
||||
Literal(None).as_('move_description'),
|
||||
Literal(None).as_('reference'),
|
||||
Literal(0).as_('debit_base_currency'),
|
||||
Literal(0).as_('credit_base_currency'),
|
||||
Sum(base_amount).as_('balance_base_currency'),
|
||||
Sum(base_amount).as_('running_balance_base_currency'),
|
||||
Literal(0).as_('debit_transaction_currency'),
|
||||
Literal(0).as_('credit_transaction_currency'),
|
||||
Sum(tx_amount).as_('balance_transaction_currency'),
|
||||
Sum(tx_amount).as_('running_balance_transaction_currency'),
|
||||
Literal(None).as_('payable_qty'),
|
||||
Literal(None).as_('uom'),
|
||||
Literal(None).as_('entered_by'),
|
||||
Literal(None).as_('entered_date'),
|
||||
Literal(None).as_('modified_by'),
|
||||
Literal(None).as_('modified_date'),
|
||||
where=where,
|
||||
group_by=[
|
||||
move.company, line.account, account.code, account.name,
|
||||
tx_currency, company.currency],
|
||||
having=Sum(base_amount) != 0))
|
||||
|
||||
@classmethod
|
||||
def table_query(cls):
|
||||
pool = Pool()
|
||||
DetailContext = pool.get('account.general_ledger.detail.context')
|
||||
context = DetailContext.get_context()
|
||||
return Union(
|
||||
cls._summary_query('opening', context),
|
||||
cls._line_query('movement', context),
|
||||
cls._summary_query('closing', context))
|
||||
|
||||
@classmethod
|
||||
def get_origin(cls):
|
||||
Move = Pool().get('account.move')
|
||||
return Move.get_origin()
|
||||
|
||||
def _get_related_payment(self):
|
||||
if not self.move_line:
|
||||
return None
|
||||
try:
|
||||
Payment = Pool().get('account.payment')
|
||||
except KeyError:
|
||||
return None
|
||||
payments = Payment.search([('line', '=', self.move_line.id)], limit=1)
|
||||
return payments[0] if payments else None
|
||||
|
||||
def get_document_field(self, name):
|
||||
origin = self.move.origin if self.move else None
|
||||
if origin:
|
||||
model_name = origin.__name__
|
||||
if model_name == 'account.invoice':
|
||||
if name == 'document_number':
|
||||
return origin.number or origin.reference
|
||||
side = {
|
||||
'out': 'Customer',
|
||||
'in': 'Supplier',
|
||||
}.get(origin.type, '')
|
||||
stage = ''
|
||||
reference = (origin.reference or '').lower()
|
||||
if 'provisional' in reference:
|
||||
stage = 'Provisional'
|
||||
elif 'final' in reference:
|
||||
stage = 'Final'
|
||||
return ' '.join(filter(None, ['Invoice', side, stage]))
|
||||
if model_name == 'account.payment':
|
||||
if name == 'document_number':
|
||||
return origin.number
|
||||
side = {
|
||||
'receivable': 'Customer',
|
||||
'payable': 'Supplier',
|
||||
}.get(origin.kind, '')
|
||||
return ' '.join(filter(None, ['Payment', side]))
|
||||
if name == 'document_number':
|
||||
return getattr(origin, 'number', None) or getattr(
|
||||
origin, 'reference', None)
|
||||
return getattr(origin, 'rec_name', None) or model_name
|
||||
payment = self._get_related_payment()
|
||||
if payment:
|
||||
if name == 'document_number':
|
||||
return payment.number
|
||||
side = {
|
||||
'receivable': 'Customer',
|
||||
'payable': 'Supplier',
|
||||
}.get(payment.kind, '')
|
||||
return ' '.join(filter(None, ['Payment', side]))
|
||||
return None
|
||||
|
||||
|
||||
class GeneralLedger(Report):
|
||||
__name__ = 'account.general_ledger'
|
||||
|
||||
@@ -2611,6 +3150,52 @@ class GeneralLedger(Report):
|
||||
return report_context
|
||||
|
||||
|
||||
class GeneralLedgerDetailReport(Report):
|
||||
__name__ = 'account.general_ledger.detail.report'
|
||||
|
||||
@classmethod
|
||||
def get_context(cls, records, header, data):
|
||||
pool = Pool()
|
||||
Company = pool.get('company.company')
|
||||
Fiscalyear = pool.get('account.fiscalyear')
|
||||
Period = pool.get('account.period')
|
||||
context = Transaction().context
|
||||
|
||||
report_context = super().get_context(records, header, data)
|
||||
report_context['company'] = Company(context['company'])
|
||||
report_context['fiscalyear'] = Fiscalyear(context['fiscalyear'])
|
||||
for period in ['start_period', 'end_period']:
|
||||
if context.get(period):
|
||||
report_context[period] = Period(context[period])
|
||||
else:
|
||||
report_context[period] = None
|
||||
report_context['from_date'] = context.get('from_date')
|
||||
report_context['to_date'] = context.get('to_date')
|
||||
report_context['groups'] = cls._groups(records)
|
||||
return report_context
|
||||
|
||||
@classmethod
|
||||
def _groups(cls, records):
|
||||
groups = []
|
||||
current_key = None
|
||||
current = None
|
||||
for line in records:
|
||||
currency = getattr(line, 'transaction_currency', None)
|
||||
key = (
|
||||
getattr(line, 'account_code', None),
|
||||
getattr(currency, 'id', None))
|
||||
if key != current_key:
|
||||
current = {
|
||||
'account': line.account_code or line.account_name or '',
|
||||
'currency': currency,
|
||||
'lines': [],
|
||||
}
|
||||
groups.append(current)
|
||||
current_key = key
|
||||
current['lines'].append(line)
|
||||
return groups
|
||||
|
||||
|
||||
class TrialBalance(Report):
|
||||
__name__ = 'account.trial_balance'
|
||||
|
||||
|
||||
@@ -806,6 +806,70 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="rule_group" ref="rule_group_general_ledger_line_companies"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="general_ledger_detail_view_list">
|
||||
<field name="model">account.general_ledger.detail</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">general_ledger_detail_list</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_general_ledger_detail_form">
|
||||
<field name="name">General Ledger Detail</field>
|
||||
<field name="res_model">account.general_ledger.detail</field>
|
||||
<field name="context_model">account.general_ledger.detail.context</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view"
|
||||
id="act_general_ledger_detail_form_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="general_ledger_detail_view_list"/>
|
||||
<field name="act_window" ref="act_general_ledger_detail_form"/>
|
||||
</record>
|
||||
<record model="ir.action.report" id="report_general_ledger_detail">
|
||||
<field name="name">General Ledger Detail</field>
|
||||
<field name="records">listed</field>
|
||||
<field name="model">account.general_ledger.detail</field>
|
||||
<field name="report_name">account.general_ledger.detail.report</field>
|
||||
<field name="report">account/general_ledger_detail.fodt</field>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="report_general_ledger_detail_keyword">
|
||||
<field name="keyword">form_print</field>
|
||||
<field name="model">account.general_ledger.detail,-1</field>
|
||||
<field name="action" ref="report_general_ledger_detail"/>
|
||||
</record>
|
||||
<menuitem
|
||||
name="General Ledger Detail"
|
||||
parent="menu_reporting"
|
||||
action="act_general_ledger_detail_form"
|
||||
sequence="11"
|
||||
id="menu_general_ledger_detail"/>
|
||||
|
||||
<record model="ir.model.access" id="access_general_ledger_detail">
|
||||
<field name="model">account.general_ledger.detail</field>
|
||||
<field name="perm_read" eval="False"/>
|
||||
<field name="perm_write" eval="False"/>
|
||||
<field name="perm_create" eval="False"/>
|
||||
<field name="perm_delete" eval="False"/>
|
||||
</record>
|
||||
<record model="ir.model.access" id="access_general_ledger_detail_account">
|
||||
<field name="model">account.general_ledger.detail</field>
|
||||
<field name="group" ref="group_account"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="False"/>
|
||||
<field name="perm_create" eval="False"/>
|
||||
<field name="perm_delete" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule.group" id="rule_group_general_ledger_detail_companies">
|
||||
<field name="name">User in companies</field>
|
||||
<field name="model">account.general_ledger.detail</field>
|
||||
<field name="global_p" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="rule_general_ledger_detail_companies">
|
||||
<field name="domain"
|
||||
eval="[('company', 'in', Eval('companies', []))]"
|
||||
pyson="1"/>
|
||||
<field name="rule_group" ref="rule_group_general_ledger_detail_companies"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="general_ledger_account_context_view_form">
|
||||
<field name="model">account.general_ledger.account.context</field>
|
||||
<field name="type">form</field>
|
||||
@@ -817,6 +881,11 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="inherit" ref="general_ledger_account_context_view_form"/>
|
||||
<field name="name">general_ledger_line_context_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="general_ledger_detail_context_view_form">
|
||||
<field name="model">account.general_ledger.detail.context</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">general_ledger_detail_context_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="account_balance_sheet_view_tree">
|
||||
<field name="model">account.account.type</field>
|
||||
|
||||
825
modules/account/general_ledger_detail.fodt
Normal file
825
modules/account/general_ledger_detail.fodt
Normal file
@@ -0,0 +1,825 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<office:document xmlns:officeooo="http://openoffice.org/2009/office" xmlns:css3t="http://www.w3.org/TR/css3-text/" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rpt="http://openoffice.org/2005/report" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:xforms="http://www.w3.org/2002/xforms" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
|
||||
<office:meta><meta:generator>LibreOffice/7.3.5.2$Linux_X86_64 LibreOffice_project/30$Build-2</meta:generator><meta:creation-date>2008-06-07T15:26:41</meta:creation-date><dc:date>2008-08-27T17:03:36</dc:date><meta:editing-cycles>1</meta:editing-cycles><meta:editing-duration>PT0S</meta:editing-duration><meta:document-statistic meta:table-count="4" meta:image-count="0" meta:object-count="0" meta:page-count="3" meta:paragraph-count="51" meta:word-count="145" meta:character-count="1808" meta:non-whitespace-character-count="1714"/><meta:user-defined meta:name="Info 1"/><meta:user-defined meta:name="Info 2"/><meta:user-defined meta:name="Info 3"/><meta:user-defined meta:name="Info 4"/></office:meta>
|
||||
<office:settings>
|
||||
<config:config-item-set config:name="ooo:view-settings">
|
||||
<config:config-item config:name="ViewAreaTop" config:type="long">24130</config:config-item>
|
||||
<config:config-item config:name="ViewAreaLeft" config:type="long">0</config:config-item>
|
||||
<config:config-item config:name="ViewAreaWidth" config:type="long">48129</config:config-item>
|
||||
<config:config-item config:name="ViewAreaHeight" config:type="long">22994</config:config-item>
|
||||
<config:config-item config:name="ShowRedlineChanges" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="InBrowseMode" config:type="boolean">false</config:config-item>
|
||||
<config:config-item-map-indexed config:name="Views">
|
||||
<config:config-item-map-entry>
|
||||
<config:config-item config:name="ViewId" config:type="string">view2</config:config-item>
|
||||
<config:config-item config:name="ViewLeft" config:type="long">19627</config:config-item>
|
||||
<config:config-item config:name="ViewTop" config:type="long">29242</config:config-item>
|
||||
<config:config-item config:name="VisibleLeft" config:type="long">0</config:config-item>
|
||||
<config:config-item config:name="VisibleTop" config:type="long">24130</config:config-item>
|
||||
<config:config-item config:name="VisibleRight" config:type="long">48128</config:config-item>
|
||||
<config:config-item config:name="VisibleBottom" config:type="long">47122</config:config-item>
|
||||
<config:config-item config:name="ZoomType" config:type="short">0</config:config-item>
|
||||
<config:config-item config:name="ViewLayoutColumns" config:type="short">0</config:config-item>
|
||||
<config:config-item config:name="ViewLayoutBookMode" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ZoomFactor" config:type="short">100</config:config-item>
|
||||
<config:config-item config:name="IsSelectedFrame" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="KeepRatio" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="AnchoredTextOverflowLegacy" config:type="boolean">false</config:config-item>
|
||||
</config:config-item-map-entry>
|
||||
</config:config-item-map-indexed>
|
||||
</config:config-item-set>
|
||||
<config:config-item-set config:name="ooo:configuration-settings">
|
||||
<config:config-item config:name="PrintProspect" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintReversed" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintSingleJobs" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintLeftPages" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrintTables" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrintControls" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrintPageBackground" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrintDrawings" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrintBlackFonts" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintAnnotationMode" config:type="short">0</config:config-item>
|
||||
<config:config-item config:name="PrintTextPlaceholder" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ProtectFields" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ProtectBookmarks" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="EmptyDbFieldHidesPara" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="DisableOffPagePositioning" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="SubtractFlysAnchoredAtFlys" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PropLineSpacingShrinksFirstLine" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ApplyParagraphMarkFormatToNumbering" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="GutterAtTop" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="TreatSingleColumnBreakAsPageBreak" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="EmbedSystemFonts" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="EmbedComplexScriptFonts" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="EmbedAsianScriptFonts" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="EmbedLatinScriptFonts" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="EmbedOnlyUsedFonts" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ContinuousEndnotes" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="EmbedFonts" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ClippedPictures" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="FloattableNomargins" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="UnbreakableNumberings" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="HeaderSpacingBelowLastPara" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="AllowPrintJobCancel" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="UseOldPrinterMetrics" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="TabOverMargin" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="TabsRelativeToIndent" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="UseOldNumbering" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="InvertBorderSpacing" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintPaperFromSetup" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="UpdateFromTemplate" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="CurrentDatabaseCommandType" config:type="int">0</config:config-item>
|
||||
<config:config-item config:name="LinkUpdateMode" config:type="short">1</config:config-item>
|
||||
<config:config-item config:name="AddParaSpacingToTableCells" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="FrameAutowidthWithMorePara" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="CurrentDatabaseCommand" config:type="string"/>
|
||||
<config:config-item config:name="PrinterIndependentLayout" config:type="string">high-resolution</config:config-item>
|
||||
<config:config-item config:name="ApplyUserData" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintFaxName" config:type="string"/>
|
||||
<config:config-item config:name="CurrentDatabaseDataSource" config:type="string"/>
|
||||
<config:config-item config:name="ClipAsCharacterAnchoredWriterFlyFrames" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="IsKernAsianPunctuation" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="SaveThumbnail" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="UseFormerTextWrapping" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="AddExternalLeading" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="AddParaTableSpacing" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="StylesNoDefault" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ChartAutoUpdate" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrinterSetup" config:type="base64Binary"/>
|
||||
<config:config-item config:name="AddParaTableSpacingAtStart" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="Rsid" config:type="int">4069242</config:config-item>
|
||||
<config:config-item config:name="EmbeddedDatabaseName" config:type="string"/>
|
||||
<config:config-item config:name="FieldAutoUpdate" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="OutlineLevelYieldsNumbering" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="FootnoteInColumnToPageEnd" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="AlignTabStopPosition" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="CharacterCompressionType" config:type="short">0</config:config-item>
|
||||
<config:config-item config:name="PrinterName" config:type="string"/>
|
||||
<config:config-item config:name="SaveGlobalDocumentLinks" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrinterPaperFromSetup" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="UseFormerLineSpacing" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="AddParaLineSpacingToTableCells" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="UseFormerObjectPositioning" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintGraphics" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="SurroundTextWrapSmall" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ConsiderTextWrapOnObjPos" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="MsWordCompTrailingBlanks" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="TabAtLeftIndentForParagraphsInList" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintRightPages" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="TabOverSpacing" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="IgnoreFirstLineIndentInNumbering" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="RedlineProtectionKey" config:type="base64Binary"/>
|
||||
<config:config-item config:name="DoNotJustifyLinesWithManualBreak" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintProspectRTL" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintEmptyPages" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="DoNotResetParaAttrsForNumFont" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="AddFrameOffsets" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="IgnoreTabsAndBlanksForLineCalculation" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="LoadReadonly" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="DoNotCaptureDrawObjsOnPage" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="AddVerticalFrameOffsets" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="UnxForceZeroExtLeading" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="IsLabelDocument" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="TableRowKeep" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="RsidRoot" config:type="int">2070049</config:config-item>
|
||||
<config:config-item config:name="PrintHiddenText" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ProtectForm" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="MsWordCompMinLineHeightByFly" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="BackgroundParaOverDrawings" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="SaveVersionOnClose" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="MathBaselineAlignment" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="SmallCapsPercentage66" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="CollapseEmptyCellPara" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="TabOverflow" config:type="boolean">false</config:config-item>
|
||||
</config:config-item-set>
|
||||
</office:settings>
|
||||
<office:scripts>
|
||||
<office:script script:language="ooo:Basic">
|
||||
<ooo:libraries xmlns:ooo="http://openoffice.org/2004/office" xmlns:xlink="http://www.w3.org/1999/xlink"/>
|
||||
</office:script>
|
||||
</office:scripts>
|
||||
<office:font-face-decls>
|
||||
<style:font-face style:name="Andale Sans UI" svg:font-family="'Andale Sans UI'" style:font-family-generic="system" style:font-pitch="variable"/>
|
||||
<style:font-face style:name="DejaVu Sans" svg:font-family="'DejaVu Sans'" style:font-family-generic="system" style:font-pitch="variable"/>
|
||||
<style:font-face style:name="Liberation Sans" svg:font-family="'Liberation Sans'" style:font-adornments="Regular" style:font-family-generic="swiss" style:font-pitch="variable"/>
|
||||
<style:font-face style:name="Liberation Serif" svg:font-family="'Liberation Serif'" style:font-family-generic="roman" style:font-pitch="variable"/>
|
||||
<style:font-face style:name="Liberation Serif1" svg:font-family="'Liberation Serif'" style:font-adornments="Bold" style:font-family-generic="roman" style:font-pitch="variable"/>
|
||||
<style:font-face style:name="Liberation Serif2" svg:font-family="'Liberation Serif'" style:font-adornments="Regular" style:font-family-generic="roman" style:font-pitch="variable"/>
|
||||
<style:font-face style:name="StarSymbol" svg:font-family="StarSymbol"/>
|
||||
<style:font-face style:name="Thorndale AMT" svg:font-family="'Thorndale AMT'" style:font-family-generic="roman" style:font-pitch="variable"/>
|
||||
</office:font-face-decls>
|
||||
<office:styles>
|
||||
<style:default-style style:family="graphic">
|
||||
<style:graphic-properties svg:stroke-color="#000000" draw:fill-color="#99ccff" fo:wrap-option="no-wrap" draw:shadow-offset-x="0.3cm" draw:shadow-offset-y="0.3cm" draw:start-line-spacing-horizontal="0.283cm" draw:start-line-spacing-vertical="0.283cm" draw:end-line-spacing-horizontal="0.283cm" draw:end-line-spacing-vertical="0.283cm" style:flow-with-text="false"/>
|
||||
<style:paragraph-properties style:text-autospace="ideograph-alpha" style:line-break="strict" style:writing-mode="lr-tb" style:font-independent-line-spacing="false">
|
||||
<style:tab-stops/>
|
||||
</style:paragraph-properties>
|
||||
<style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Thorndale AMT" fo:font-size="12pt" fo:language="en" fo:country="US" style:letter-kerning="true" style:font-name-asian="Andale Sans UI" style:font-size-asian="10.5pt" style:language-asian="zxx" style:country-asian="none" style:font-name-complex="Andale Sans UI" style:font-size-complex="12pt" style:language-complex="zxx" style:country-complex="none"/>
|
||||
</style:default-style>
|
||||
<style:default-style style:family="paragraph">
|
||||
<style:paragraph-properties fo:hyphenation-ladder-count="no-limit" style:text-autospace="ideograph-alpha" style:punctuation-wrap="hanging" style:line-break="strict" style:tab-stop-distance="1.251cm" style:writing-mode="lr-tb"/>
|
||||
<style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Thorndale AMT" fo:font-size="12pt" fo:language="en" fo:country="US" style:letter-kerning="true" style:font-name-asian="Andale Sans UI" style:font-size-asian="10.5pt" style:language-asian="zxx" style:country-asian="none" style:font-name-complex="Andale Sans UI" style:font-size-complex="12pt" style:language-complex="zxx" style:country-complex="none" fo:hyphenate="false" fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2" loext:hyphenation-no-caps="false"/>
|
||||
</style:default-style>
|
||||
<style:default-style style:family="table">
|
||||
<style:table-properties table:border-model="collapsing"/>
|
||||
</style:default-style>
|
||||
<style:default-style style:family="table-row">
|
||||
<style:table-row-properties fo:keep-together="auto"/>
|
||||
</style:default-style>
|
||||
<style:style style:name="Standard" style:family="paragraph" style:class="text">
|
||||
<style:text-properties style:font-name="Liberation Sans" fo:font-family="'Liberation Sans'" style:font-style-name="Regular" style:font-family-generic="swiss" style:font-pitch="variable" style:font-size-asian="10.5pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="Heading" style:family="paragraph" style:parent-style-name="Standard" style:next-style-name="Text_20_body" style:class="text">
|
||||
<style:paragraph-properties fo:margin-top="0.423cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" fo:keep-with-next="always"/>
|
||||
<style:text-properties style:font-name="Liberation Serif2" fo:font-family="'Liberation Serif'" style:font-style-name="Regular" style:font-family-generic="roman" style:font-pitch="variable" fo:font-size="16pt" style:font-name-asian="DejaVu Sans" style:font-family-asian="'DejaVu Sans'" style:font-family-generic-asian="system" style:font-pitch-asian="variable" style:font-size-asian="14pt" style:font-name-complex="DejaVu Sans" style:font-family-complex="'DejaVu Sans'" style:font-family-generic-complex="system" style:font-pitch-complex="variable" style:font-size-complex="14pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="Text_20_body" style:display-name="Text body" style:family="paragraph" style:parent-style-name="Standard" style:class="text">
|
||||
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false"/>
|
||||
<style:text-properties style:font-name="Liberation Sans" fo:font-family="'Liberation Sans'" style:font-style-name="Regular" style:font-family-generic="swiss" style:font-pitch="variable" style:font-size-asian="10.5pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="List" style:family="paragraph" style:parent-style-name="Text_20_body" style:class="list">
|
||||
<style:text-properties style:font-size-asian="12pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="Caption" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
|
||||
<style:paragraph-properties fo:margin-top="0.212cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" text:number-lines="false" text:line-number="0"/>
|
||||
<style:text-properties fo:font-size="12pt" fo:font-style="italic" style:font-size-asian="12pt" style:font-style-asian="italic" style:font-size-complex="12pt" style:font-style-complex="italic"/>
|
||||
</style:style>
|
||||
<style:style style:name="Index" style:family="paragraph" style:parent-style-name="Standard" style:class="index">
|
||||
<style:paragraph-properties text:number-lines="false" text:line-number="0"/>
|
||||
<style:text-properties style:font-size-asian="12pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table_20_Contents" style:display-name="Table Contents" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
|
||||
<style:paragraph-properties text:number-lines="false" text:line-number="0"/>
|
||||
<style:text-properties fo:font-size="11pt" style:font-size-asian="10.5pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="Heading_20_1" style:display-name="Heading 1" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="text">
|
||||
<style:text-properties fo:font-size="16pt" fo:font-weight="bold" style:font-size-asian="115%" style:font-weight-asian="bold" style:font-size-complex="115%" style:font-weight-complex="bold"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table_20_Heading" style:display-name="Table Heading" style:family="paragraph" style:parent-style-name="Table_20_Contents" style:class="extra">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" text:number-lines="false" text:line-number="0"/>
|
||||
<style:text-properties style:font-name="Liberation Serif1" fo:font-family="'Liberation Serif'" style:font-style-name="Bold" style:font-family-generic="roman" style:font-pitch="variable" fo:font-size="12pt" fo:font-weight="bold" style:font-size-asian="10.5pt" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
|
||||
</style:style>
|
||||
<style:style style:name="Horizontal_20_Line" style:display-name="Horizontal Line" style:family="paragraph" style:parent-style-name="Standard" style:next-style-name="Text_20_body" style:class="html">
|
||||
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0.499cm" style:contextual-spacing="false" style:border-line-width-bottom="0.002cm 0.035cm 0.002cm" fo:padding="0cm" fo:border-left="none" fo:border-right="none" fo:border-top="none" fo:border-bottom="1.11pt double #808080" text:number-lines="false" text:line-number="0" style:join-border="false"/>
|
||||
<style:text-properties fo:font-size="6pt" style:font-size-asian="6pt" style:font-size-complex="6pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="Header_20_and_20_Footer" style:display-name="Header and Footer" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
|
||||
<style:paragraph-properties text:number-lines="false" text:line-number="0">
|
||||
<style:tab-stops>
|
||||
<style:tab-stop style:position="8.5cm" style:type="center"/>
|
||||
<style:tab-stop style:position="17cm" style:type="right"/>
|
||||
</style:tab-stops>
|
||||
</style:paragraph-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Header" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
|
||||
<style:paragraph-properties text:number-lines="false" text:line-number="0">
|
||||
<style:tab-stops>
|
||||
<style:tab-stop style:position="8.795cm" style:type="center"/>
|
||||
<style:tab-stop style:position="17.59cm" style:type="right"/>
|
||||
</style:tab-stops>
|
||||
</style:paragraph-properties>
|
||||
<style:text-properties fo:font-size="9pt" style:font-size-asian="10.5pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="Heading_20_2" style:display-name="Heading 2" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="text">
|
||||
<style:text-properties fo:font-size="14pt" fo:font-style="italic" fo:font-weight="bold" style:font-size-asian="14pt" style:font-style-asian="italic" style:font-weight-asian="bold" style:font-size-complex="14pt" style:font-style-complex="italic" style:font-weight-complex="bold"/>
|
||||
</style:style>
|
||||
<style:style style:name="Footer" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
|
||||
<style:paragraph-properties text:number-lines="false" text:line-number="0">
|
||||
<style:tab-stops>
|
||||
<style:tab-stop style:position="8.795cm" style:type="center"/>
|
||||
<style:tab-stop style:position="17.59cm" style:type="right"/>
|
||||
</style:tab-stops>
|
||||
</style:paragraph-properties>
|
||||
<style:text-properties fo:font-size="9pt" style:font-size-asian="10.5pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="Heading_20_3" style:display-name="Heading 3" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="text">
|
||||
<style:text-properties fo:font-size="14pt" fo:font-weight="bold" style:font-size-asian="14pt" style:font-weight-asian="bold" style:font-size-complex="14pt" style:font-weight-complex="bold"/>
|
||||
</style:style>
|
||||
<style:style style:name="Text_20_body_20_indent" style:display-name="Text body indent" style:family="paragraph" style:parent-style-name="Text_20_body" style:class="text">
|
||||
<style:paragraph-properties fo:margin-left="0.499cm" fo:margin-right="0cm" fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" fo:text-indent="0cm" style:auto-text-indent="false"/>
|
||||
</style:style>
|
||||
<style:style style:name="Quotations" style:family="paragraph" style:parent-style-name="Standard" style:class="html">
|
||||
<style:paragraph-properties fo:margin-left="1cm" fo:margin-right="1cm" fo:margin-top="0cm" fo:margin-bottom="0.499cm" style:contextual-spacing="false" fo:text-indent="0cm" style:auto-text-indent="false"/>
|
||||
</style:style>
|
||||
<style:style style:name="Title" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="chapter">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
|
||||
<style:text-properties fo:font-size="28pt" fo:font-weight="bold" style:font-size-asian="28pt" style:font-weight-asian="bold" style:font-size-complex="28pt" style:font-weight-complex="bold"/>
|
||||
</style:style>
|
||||
<style:style style:name="Subtitle" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="chapter">
|
||||
<style:paragraph-properties fo:margin-top="0.106cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" fo:text-align="center" style:justify-single-word="false"/>
|
||||
<style:text-properties fo:font-size="18pt" style:font-size-asian="18pt" style:font-size-complex="18pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="Placeholder" style:family="text">
|
||||
<style:text-properties fo:font-variant="small-caps" fo:color="#008080" loext:opacity="100%" style:text-underline-style="dotted" style:text-underline-width="auto" style:text-underline-color="font-color"/>
|
||||
</style:style>
|
||||
<style:style style:name="Bullet_20_Symbols" style:display-name="Bullet Symbols" style:family="text">
|
||||
<style:text-properties style:font-name="StarSymbol" fo:font-family="StarSymbol" fo:font-size="9pt" style:font-name-asian="StarSymbol" style:font-family-asian="StarSymbol" style:font-size-asian="9pt" style:font-name-complex="StarSymbol" style:font-family-complex="StarSymbol" style:font-size-complex="9pt"/>
|
||||
</style:style>
|
||||
<text:outline-style style:name="Outline">
|
||||
<text:outline-level-style text:level="1" loext:num-list-format="%1%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="2" loext:num-list-format="%2%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="3" loext:num-list-format="%3%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="4" loext:num-list-format="%4%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="5" loext:num-list-format="%5%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="6" loext:num-list-format="%6%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="7" loext:num-list-format="%7%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="8" loext:num-list-format="%8%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="9" loext:num-list-format="%9%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="10" loext:num-list-format="%10%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
</text:outline-style>
|
||||
<text:notes-configuration text:note-class="footnote" style:num-format="1" text:start-value="0" text:footnotes-position="page" text:start-numbering-at="document"/>
|
||||
<text:notes-configuration text:note-class="endnote" style:num-format="i" text:start-value="0"/>
|
||||
<text:linenumbering-configuration text:number-lines="false" text:offset="0.499cm" style:num-format="1" text:number-position="left" text:increment="5"/>
|
||||
<style:style style:name="Default_20_Style.1" style:display-name="Default Style.1" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-top="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.2" style:display-name="Default Style.2" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.3" style:display-name="Default Style.3" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.4" style:display-name="Default Style.4" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-right="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.5" style:display-name="Default Style.5" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.6" style:display-name="Default Style.6" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.7" style:display-name="Default Style.7" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.8" style:display-name="Default Style.8" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.9" style:display-name="Default Style.9" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.10" style:display-name="Default Style.10" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-right="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.11" style:display-name="Default Style.11" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-top="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.12" style:display-name="Default Style.12" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-right="0.51pt solid #000000" fo:border-top="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.13" style:display-name="Default Style.13" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.14" style:display-name="Default Style.14" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-right="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.15" style:display-name="Default Style.15" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-top="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Default_20_Style.16" style:display-name="Default Style.16" style:family="table-cell">
|
||||
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
|
||||
</style:style>
|
||||
<table:table-template table:name="Default Style" table:first-row-end-column="row" table:first-row-start-column="row" table:last-row-end-column="row" table:last-row-start-column="row">
|
||||
<table:first-row table:style-name="Default_20_Style.1"/>
|
||||
<table:last-row table:style-name="Default_20_Style.2"/>
|
||||
<table:first-column table:style-name="Default_20_Style.3"/>
|
||||
<table:last-column table:style-name="Default_20_Style.4"/>
|
||||
<table:body table:style-name="Default_20_Style.9"/>
|
||||
<table:even-rows table:style-name="Default_20_Style.5"/>
|
||||
<table:odd-rows table:style-name="Default_20_Style.6"/>
|
||||
<table:even-columns table:style-name="Default_20_Style.7"/>
|
||||
<table:odd-columns table:style-name="Default_20_Style.8"/>
|
||||
<table:background table:style-name="Default_20_Style.10"/>
|
||||
<loext:first-row-even-column table:style-name="Default_20_Style.15"/>
|
||||
<loext:last-row-even-column table:style-name="Default_20_Style.16"/>
|
||||
<loext:first-row-end-column table:style-name="Default_20_Style.12"/>
|
||||
<loext:first-row-start-column table:style-name="Default_20_Style.11"/>
|
||||
<loext:last-row-end-column table:style-name="Default_20_Style.14"/>
|
||||
<loext:last-row-start-column table:style-name="Default_20_Style.13"/>
|
||||
</table:table-template>
|
||||
</office:styles>
|
||||
<office:automatic-styles>
|
||||
<style:style style:name="Table2" style:family="table">
|
||||
<style:table-properties style:width="23.94cm" table:align="margins"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table2.A" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="7.98cm" style:rel-column-width="21845*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table2.A1" style:family="table-cell">
|
||||
<style:table-cell-properties fo:background-color="transparent" fo:padding="0.097cm" fo:border="none">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Table1" style:family="table">
|
||||
<style:table-properties style:width="23.94cm" table:align="margins"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table1.A" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="11.97cm" style:rel-column-width="32768*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table1.B" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="11.97cm" style:rel-column-width="32767*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table1.A1" style:family="table-cell">
|
||||
<style:table-cell-properties fo:padding="0.097cm" fo:border="none"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table2" style:family="table">
|
||||
<style:table-properties style:width="23.94cm" table:align="margins"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table2.A" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="7.98cm" style:rel-column-width="21845*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table2.A1" style:family="table-cell">
|
||||
<style:table-cell-properties fo:background-color="transparent" fo:padding="0.097cm" fo:border="none">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Table1" style:family="table">
|
||||
<style:table-properties style:width="23.94cm" table:align="margins"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table1.A" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="11.97cm" style:rel-column-width="32768*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table1.B" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="11.97cm" style:rel-column-width="32767*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table1.A1" style:family="table-cell">
|
||||
<style:table-cell-properties fo:padding="0.097cm" fo:border="none"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines" style:family="table">
|
||||
<style:table-properties style:width="23.94cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:break-before="auto" fo:break-after="auto" table:align="margins" fo:background-color="transparent" fo:keep-with-next="always" style:may-break-between-rows="true" style:writing-mode="lr-tb">
|
||||
<style:background-image/>
|
||||
</style:table-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.A" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="2.499cm" style:rel-column-width="1417*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.B" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="3.358cm" style:rel-column-width="1904*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.C" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="3.36cm" style:rel-column-width="1905*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.F" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="4.002cm" style:rel-column-width="2269*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.1" style:family="table-row">
|
||||
<style:table-row-properties fo:keep-together="always"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.A1" style:family="table-cell">
|
||||
<style:table-cell-properties fo:background-color="#b3b3b3" fo:padding="0.097cm" fo:border="0.05pt solid #000000">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.A2" style:family="table-cell">
|
||||
<style:table-cell-properties fo:background-color="#b3b3b3" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.A3" style:family="table-cell">
|
||||
<style:table-cell-properties fo:background-color="#e6e6e6" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="0.05pt solid #000000" fo:border-bottom="0.05pt solid #000000">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.G3" style:family="table-cell">
|
||||
<style:table-cell-properties fo:background-color="#e6e6e6" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.A4" style:family="table-cell">
|
||||
<style:table-cell-properties fo:background-color="#cccccc" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="0.05pt solid #000000" fo:border-bottom="0.05pt solid #000000">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.C4" style:family="table-cell">
|
||||
<style:table-cell-properties fo:background-color="#cccccc" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.G4" style:family="table-cell">
|
||||
<style:table-cell-properties fo:background-color="#cccccc" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.A5" style:family="table-cell">
|
||||
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.A6" style:family="table-cell">
|
||||
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.B6" style:family="table-cell">
|
||||
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.C6" style:family="table-cell">
|
||||
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.D6" style:family="table-cell">
|
||||
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.E6" style:family="table-cell">
|
||||
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.F6" style:family="table-cell">
|
||||
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.G6" style:family="table-cell">
|
||||
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.A7" style:family="table-cell">
|
||||
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Lines.A8" style:family="table-cell">
|
||||
<style:table-cell-properties fo:background-color="#e6e6e6" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Table3" style:family="table" style:master-page-name="">
|
||||
<style:table-properties style:width="23.94cm" style:page-number="auto" fo:break-before="auto" fo:break-after="auto" table:align="margins" fo:background-color="transparent" style:shadow="none" fo:keep-with-next="always" style:may-break-between-rows="true" style:writing-mode="lr-tb">
|
||||
<style:background-image/>
|
||||
</style:table-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Table3.A" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="5.851cm" style:rel-column-width="3317*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table3.B" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="3.362cm" style:rel-column-width="1906*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table3.E" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="4.001cm" style:rel-column-width="2268*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table3.1" style:family="table-row">
|
||||
<style:table-row-properties fo:background-color="transparent" fo:keep-together="always">
|
||||
<style:background-image/>
|
||||
</style:table-row-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Table3.A1" style:family="table-cell">
|
||||
<style:table-cell-properties style:vertical-align="" fo:background-color="#b3b3b3" fo:padding="0.097cm" fo:border="0.05pt solid #000000" style:writing-mode="lr-tb">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Table3.A2" style:family="table-cell">
|
||||
<style:table-cell-properties style:vertical-align="" fo:background-color="#b3b3b3" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000" style:writing-mode="lr-tb">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Table3.A3" style:family="table-cell">
|
||||
<style:table-cell-properties style:vertical-align="" fo:background-color="#cccccc" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000" style:writing-mode="lr-tb">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Table3.F3" style:family="table-cell">
|
||||
<style:table-cell-properties style:vertical-align="" fo:background-color="#cccccc" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000" style:writing-mode="lr-tb">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="P1" style:family="paragraph" style:parent-style-name="Header">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
|
||||
</style:style>
|
||||
<style:style style:name="P2" style:family="paragraph" style:parent-style-name="Header">
|
||||
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false"/>
|
||||
</style:style>
|
||||
<style:style style:name="P3" style:family="paragraph" style:parent-style-name="Footer">
|
||||
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false"/>
|
||||
</style:style>
|
||||
<style:style style:name="P4" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
</style:style>
|
||||
<style:style style:name="P5" style:family="paragraph" style:parent-style-name="Table_20_Heading">
|
||||
<style:paragraph-properties fo:keep-together="always" style:shadow="none" fo:keep-with-next="always"/>
|
||||
</style:style>
|
||||
<style:style style:name="P6" style:family="paragraph" style:parent-style-name="Table_20_Heading">
|
||||
<style:paragraph-properties fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
</style:style>
|
||||
<style:style style:name="P7" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" style:shadow="none" fo:keep-with-next="always"/>
|
||||
<style:text-properties fo:font-size="12pt" fo:font-weight="bold" style:font-size-asian="12pt" style:font-weight-asian="bold" style:font-size-complex="12pt" style:font-weight-complex="bold"/>
|
||||
</style:style>
|
||||
<style:style style:name="P8" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
|
||||
<style:text-properties fo:font-size="12pt" style:text-underline-style="none" fo:font-weight="normal" style:font-size-asian="10.5pt" style:font-weight-asian="normal" style:font-size-complex="12pt" style:font-weight-complex="normal"/>
|
||||
</style:style>
|
||||
<style:style style:name="P9" style:family="paragraph" style:parent-style-name="Table_20_Heading">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
<style:text-properties style:font-size-asian="10.5pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P10" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
<style:text-properties style:font-size-asian="10.5pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P11" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
<style:text-properties officeooo:paragraph-rsid="002aca3c" style:font-size-asian="10.5pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P12" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
</style:style>
|
||||
<style:style style:name="P13" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
<style:text-properties fo:font-size="11pt" style:font-size-asian="11pt" style:font-size-complex="11pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P14" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
<style:text-properties fo:font-size="11pt" style:font-size-asian="11pt" style:font-size-complex="11pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P15" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
<style:text-properties fo:font-size="11pt" style:text-underline-style="none" style:font-size-asian="11pt" style:font-size-complex="11pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P16" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
<style:text-properties style:text-underline-style="none"/>
|
||||
</style:style>
|
||||
<style:style style:name="P17" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
<style:text-properties style:text-underline-style="none" officeooo:paragraph-rsid="00251ac8"/>
|
||||
</style:style>
|
||||
<style:style style:name="P18" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
|
||||
<style:text-properties style:text-underline-style="none"/>
|
||||
</style:style>
|
||||
<style:style style:name="P19" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
<style:text-properties style:text-underline-style="none" officeooo:paragraph-rsid="00251ac8" fo:background-color="transparent"/>
|
||||
</style:style>
|
||||
<style:style style:name="P20" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:text-properties style:text-underline-style="none"/>
|
||||
</style:style>
|
||||
<style:style style:name="P21" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:paragraph-properties fo:break-before="page"/>
|
||||
<style:text-properties style:text-underline-style="none"/>
|
||||
</style:style>
|
||||
<style:style style:name="P22" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:text-properties officeooo:paragraph-rsid="0037c304"/>
|
||||
</style:style>
|
||||
<style:style style:name="P23" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:text-properties officeooo:paragraph-rsid="003a045d"/>
|
||||
</style:style>
|
||||
<style:style style:name="P24" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:text-properties officeooo:rsid="003955ec" officeooo:paragraph-rsid="003a045d"/>
|
||||
</style:style>
|
||||
<style:style style:name="P25" style:family="paragraph" style:parent-style-name="Heading_20_1">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
|
||||
<style:text-properties style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color"/>
|
||||
</style:style>
|
||||
<style:style style:name="P26" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
|
||||
<style:text-properties style:font-name="Liberation Serif1" fo:font-weight="bold" style:font-size-asian="10.5pt" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
|
||||
</style:style>
|
||||
<style:style style:name="P27" style:family="paragraph" style:parent-style-name="Header">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
|
||||
<style:text-properties style:text-underline-style="none" officeooo:paragraph-rsid="003e177a"/>
|
||||
</style:style>
|
||||
<style:style style:name="P28" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
</style:style>
|
||||
<style:style style:name="P29" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
|
||||
<style:text-properties fo:color="#000000" loext:opacity="100%" style:text-outline="false" style:text-line-through-style="none" style:text-line-through-type="none" style:font-name="Liberation Serif" fo:font-size="12pt" fo:font-style="normal" fo:text-shadow="none" style:text-underline-style="none" fo:font-weight="normal" style:font-size-asian="12pt" style:font-style-asian="normal" style:font-weight-asian="normal" style:font-size-complex="12pt" style:font-style-complex="normal" style:font-weight-complex="normal" style:text-overline-style="none" style:text-overline-color="font-color"/>
|
||||
</style:style>
|
||||
<style:style style:name="P30" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
<style:text-properties officeooo:paragraph-rsid="002aca3c" style:font-size-asian="10.5pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P31" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
<style:text-properties style:font-size-asian="10.5pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P32" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false"/>
|
||||
</style:style>
|
||||
<style:style style:name="P33" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
|
||||
</style:style>
|
||||
<style:style style:name="P34" style:family="paragraph" style:parent-style-name="Table_20_Heading">
|
||||
<style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
|
||||
</style:style>
|
||||
<style:style style:name="P35" style:family="paragraph" style:parent-style-name="Table_20_Heading">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
|
||||
<style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
|
||||
</style:style>
|
||||
<style:style style:name="P36" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:break-before="page" fo:keep-with-next="always"/>
|
||||
<style:text-properties style:text-underline-style="none" officeooo:paragraph-rsid="003e177a"/>
|
||||
</style:style>
|
||||
<style:style style:name="P37" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
<style:text-properties style:text-underline-style="none" officeooo:paragraph-rsid="003e177a"/>
|
||||
</style:style>
|
||||
<style:style style:name="P38" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
|
||||
<style:text-properties fo:font-size="12pt" style:text-underline-style="none" fo:font-weight="normal" officeooo:paragraph-rsid="003e177a" fo:background-color="transparent" style:font-size-asian="10.5pt" style:font-weight-asian="normal" style:font-size-complex="12pt" style:font-weight-complex="normal"/>
|
||||
</style:style>
|
||||
<style:style style:name="T1" style:family="text">
|
||||
<style:text-properties officeooo:rsid="0037c304"/>
|
||||
</style:style>
|
||||
<style:style style:name="T2" style:family="text">
|
||||
<style:text-properties officeooo:rsid="0039b016"/>
|
||||
</style:style>
|
||||
<style:style style:name="T3" style:family="text">
|
||||
<style:text-properties officeooo:rsid="003e177a"/>
|
||||
</style:style>
|
||||
<style:style style:name="T4" style:family="text">
|
||||
<style:text-properties style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color"/>
|
||||
</style:style>
|
||||
<style:style style:name="T5" style:family="text">
|
||||
<style:text-properties style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color" officeooo:rsid="003e177a"/>
|
||||
</style:style>
|
||||
<style:style style:name="T6" style:family="text">
|
||||
<style:text-properties style:text-underline-style="none"/>
|
||||
</style:style>
|
||||
<style:style style:name="T7" style:family="text">
|
||||
<style:text-properties style:text-underline-style="none" officeooo:rsid="003e177a"/>
|
||||
</style:style>
|
||||
<style:page-layout style:name="pm1">
|
||||
<style:page-layout-properties fo:page-width="27.94cm" fo:page-height="21.59cm" style:num-format="1" style:print-orientation="landscape" fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" fo:margin-right="2cm" style:writing-mode="lr-tb" style:layout-grid-color="#c0c0c0" style:layout-grid-lines="44" style:layout-grid-base-height="0.55cm" style:layout-grid-ruby-height="0cm" style:layout-grid-mode="none" style:layout-grid-ruby-below="false" style:layout-grid-print="true" style:layout-grid-display="true" style:footnote-max-height="0cm" loext:margin-gutter="0cm">
|
||||
<style:footnote-sep style:width="0.018cm" style:distance-before-sep="0.101cm" style:distance-after-sep="0.101cm" style:line-style="none" style:adjustment="left" style:rel-width="25%" style:color="#000000"/>
|
||||
</style:page-layout-properties>
|
||||
<style:header-style>
|
||||
<style:header-footer-properties fo:min-height="0cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-bottom="0.499cm"/>
|
||||
</style:header-style>
|
||||
<style:footer-style>
|
||||
<style:header-footer-properties fo:min-height="0cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-top="0.499cm"/>
|
||||
</style:footer-style>
|
||||
</style:page-layout>
|
||||
<style:style style:name="dp1" style:family="drawing-page">
|
||||
<style:drawing-page-properties draw:background-size="full"/>
|
||||
</style:style>
|
||||
</office:automatic-styles>
|
||||
<office:master-styles>
|
||||
<style:master-page style:name="Standard" style:page-layout-name="pm1" draw:style-name="dp1">
|
||||
<style:header>
|
||||
<table:table table:name="Table2" table:style-name="Table2">
|
||||
<table:table-column table:style-name="Table2.A" table:number-columns-repeated="3"/>
|
||||
<table:table-row table:style-name="TableLine94368948362784">
|
||||
<table:table-cell table:style-name="Table2.A1" office:value-type="string">
|
||||
<text:p text:style-name="Header">Company: <text:placeholder text:placeholder-type="text"><company.rec_name></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Table2.A1" office:value-type="string">
|
||||
<text:p text:style-name="P27">General Ledger Detail<text:placeholder text:placeholder-type="text"><if test="not context.get('posted')"></text:placeholder> <text:span text:style-name="T3">Draft</text:span><text:span text:style-name="T3"><text:placeholder text:placeholder-type="text"></if></text:placeholder></text:span></text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Table2.A1" office:value-type="string">
|
||||
<text:p text:style-name="P2">Print Date: <text:placeholder text:placeholder-type="text"><format_date(datetime.date.today(), user.language)></text:placeholder><text:s/>at <text:placeholder text:placeholder-type="text"><datetime.datetime.now().strftime('%H:%M:%S')></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
</table:table-row>
|
||||
</table:table>
|
||||
</style:header>
|
||||
<style:footer>
|
||||
<table:table table:name="Table1" table:style-name="Table1">
|
||||
<table:table-column table:style-name="Table1.A"/>
|
||||
<table:table-column table:style-name="Table1.B"/>
|
||||
<table:table-row table:style-name="TableLine94368949141280">
|
||||
<table:table-cell table:style-name="Table1.A1" office:value-type="string">
|
||||
<text:p text:style-name="Footer">User: <text:placeholder text:placeholder-type="text"><user.rec_name></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Table1.A1" office:value-type="string">
|
||||
<text:p text:style-name="P3"><text:page-number text:select-page="current">1</text:page-number>/<text:page-count>3</text:page-count></text:p>
|
||||
</table:table-cell>
|
||||
</table:table-row>
|
||||
</table:table>
|
||||
</style:footer>
|
||||
</style:master-page>
|
||||
</office:master-styles>
|
||||
<office:body>
|
||||
<office:text text:use-soft-page-breaks="true">
|
||||
<office:forms form:automatic-focus="false" form:apply-design-mode="false"/>
|
||||
<text:sequence-decls>
|
||||
<text:sequence-decl text:display-outline-level="0" text:name="Illustration"/>
|
||||
<text:sequence-decl text:display-outline-level="0" text:name="Table"/>
|
||||
<text:sequence-decl text:display-outline-level="0" text:name="Text"/>
|
||||
<text:sequence-decl text:display-outline-level="0" text:name="Drawing"/>
|
||||
<text:sequence-decl text:display-outline-level="0" text:name="Figure"/>
|
||||
</text:sequence-decls>
|
||||
<text:p text:style-name="P25">General Ledger Detail<text:placeholder text:placeholder-type="text"><if test="not context.get('posted')"></text:placeholder> <text:span text:style-name="T3">Draft</text:span><text:span text:style-name="T3"><text:placeholder text:placeholder-type="text"></if></text:placeholder></text:span></text:p>
|
||||
<text:p text:style-name="P23"><text:span text:style-name="T1">Fiscal Year: </text:span><text:placeholder text:placeholder-type="text"><fiscalyear.rec_name></text:placeholder></text:p>
|
||||
<text:p text:style-name="P23"><text:placeholder text:placeholder-type="text"><if test="start_period or end_period"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P23"><text:span text:style-name="T1">From Period</text:span>: <text:placeholder text:placeholder-type="text"><start_period.name if start_period else ''></text:placeholder><text:s/><text:span text:style-name="T2">To</text:span> <text:placeholder text:placeholder-type="text"><end_period.name if end_period else ''></text:placeholder></text:p>
|
||||
<text:p text:style-name="P23"><text:placeholder text:placeholder-type="text"></if></text:placeholder></text:p>
|
||||
<text:p text:style-name="P23"><text:placeholder text:placeholder-type="text"><if test="from_date or to_date"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P24">From Date: <text:placeholder text:placeholder-type="text"><format_date(from_date, user.language) if from_date else ''></text:placeholder><text:s/>To <text:placeholder text:placeholder-type="text"><format_date(to_date, user.language) if to_date else ''></text:placeholder></text:p>
|
||||
<text:p text:style-name="P23"><text:placeholder text:placeholder-type="text"></if></text:placeholder></text:p>
|
||||
<text:p text:style-name="P20"><text:placeholder text:placeholder-type="text"><for each="group in groups"></text:placeholder></text:p>
|
||||
<table:table table:name="Lines" table:style-name="Lines">
|
||||
<table:table-column table:style-name="Lines.A"/>
|
||||
<table:table-column table:style-name="Lines.B"/>
|
||||
<table:table-column table:style-name="Lines.C"/>
|
||||
<table:table-column table:style-name="Lines.B" table:number-columns-repeated="2"/>
|
||||
<table:table-column table:style-name="Lines.F" table:number-columns-repeated="2"/>
|
||||
<table:table-header-rows>
|
||||
<table:table-row table:style-name="Lines.1">
|
||||
<table:table-cell table:style-name="Lines.A1" table:number-columns-spanned="7" office:value-type="string">
|
||||
<text:p text:style-name="P5">Account / Currency</text:p>
|
||||
</table:table-cell>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
</table:table-row>
|
||||
<table:table-row table:style-name="Lines.1">
|
||||
<table:table-cell table:style-name="Lines.A2" table:number-columns-spanned="7" office:value-type="string">
|
||||
<text:p text:style-name="P7"><text:placeholder text:placeholder-type="text"><group['account']></text:placeholder><text:s/>/<text:s/><text:placeholder text:placeholder-type="text"><group['currency'].rec_name if group['currency'] else ''></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
</table:table-row>
|
||||
<table:table-row table:style-name="Lines.1">
|
||||
<table:table-cell table:style-name="Lines.A3" office:value-type="string"><text:p text:style-name="P6">Date</text:p></table:table-cell>
|
||||
<table:table-cell table:style-name="Lines.A3" office:value-type="string"><text:p text:style-name="P6">Journal</text:p></table:table-cell>
|
||||
<table:table-cell table:style-name="Lines.A3" office:value-type="string"><text:p text:style-name="P6">Entry / Doc.</text:p></table:table-cell>
|
||||
<table:table-cell table:style-name="Lines.A3" office:value-type="string"><text:p text:style-name="Table_20_Heading">Debit</text:p></table:table-cell>
|
||||
<table:table-cell table:style-name="Lines.A3" office:value-type="string"><text:p text:style-name="P6">Credit</text:p></table:table-cell>
|
||||
<table:table-cell table:style-name="Lines.A3" office:value-type="string"><text:p text:style-name="P6">Running</text:p></table:table-cell>
|
||||
<table:table-cell table:style-name="Lines.G3" office:value-type="string"><text:p text:style-name="P6">Description / Ref.</text:p></table:table-cell>
|
||||
</table:table-row>
|
||||
</table:table-header-rows>
|
||||
<table:table-row table:style-name="Lines.1">
|
||||
<table:table-cell table:style-name="Lines.A5" table:number-columns-spanned="7" office:value-type="string">
|
||||
<text:p text:style-name="P16"><text:placeholder text:placeholder-type="text"><for each="line in group['lines']"></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:covered-table-cell/><table:covered-table-cell/><table:covered-table-cell/><table:covered-table-cell/><table:covered-table-cell/><table:covered-table-cell/>
|
||||
</table:table-row>
|
||||
<table:table-row table:style-name="Lines.1">
|
||||
<table:table-cell table:style-name="Lines.A6" office:value-type="string"><text:p text:style-name="P33"><text:placeholder text:placeholder-type="text"><format_date(line.posting_date, user.language) if line.posting_date else ''></text:placeholder></text:p></table:table-cell>
|
||||
<table:table-cell table:style-name="Lines.B6" office:value-type="string"><text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text"><line.journal.rec_name if line.journal else ''></text:placeholder></text:p></table:table-cell>
|
||||
<table:table-cell table:style-name="Lines.C6" office:value-type="string"><text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text"><line.journal_entry_number or ''></text:placeholder><text:line-break/><text:placeholder text:placeholder-type="text"><line.document_number or line.voucher_number or ''></text:placeholder></text:p></table:table-cell>
|
||||
<table:table-cell table:style-name="Lines.D6" office:value-type="string"><text:p text:style-name="P32"><text:placeholder text:placeholder-type="text"><format_currency(line.debit_base_currency or 0, user.language, line.base_currency or company.currency)></text:placeholder></text:p></table:table-cell>
|
||||
<table:table-cell table:style-name="Lines.E6" office:value-type="string"><text:p text:style-name="P32"><text:placeholder text:placeholder-type="text"><format_currency(line.credit_base_currency or 0, user.language, line.base_currency or company.currency)></text:placeholder></text:p></table:table-cell>
|
||||
<table:table-cell table:style-name="Lines.F6" office:value-type="string"><text:p text:style-name="P32"><text:placeholder text:placeholder-type="text"><format_currency(line.running_balance_base_currency or 0, user.language, line.base_currency or company.currency)></text:placeholder></text:p></table:table-cell>
|
||||
<table:table-cell table:style-name="Lines.G6" office:value-type="string"><text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text"><line.description or line.move_description or ''></text:placeholder><text:line-break/><text:placeholder text:placeholder-type="text"><line.party.rec_name if line.party else ''></text:placeholder><text:line-break/><text:placeholder text:placeholder-type="text"><line.reference or ''></text:placeholder></text:p></table:table-cell>
|
||||
</table:table-row>
|
||||
<text:soft-page-break/>
|
||||
<table:table-row table:style-name="Lines.1">
|
||||
<table:table-cell table:style-name="Lines.A7" table:number-columns-spanned="7" office:value-type="string">
|
||||
<text:p text:style-name="P18"><text:placeholder text:placeholder-type="text"></for></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:covered-table-cell/><table:covered-table-cell/><table:covered-table-cell/><table:covered-table-cell/><table:covered-table-cell/><table:covered-table-cell/>
|
||||
</table:table-row>
|
||||
</table:table>
|
||||
<text:p text:style-name="P8"><text:placeholder text:placeholder-type="text"></for></text:placeholder></text:p>
|
||||
</office:text>
|
||||
</office:body>
|
||||
</office:document>
|
||||
@@ -117,6 +117,7 @@ class Move(DescriptionOriginMixin, ModelSQL, ModelView):
|
||||
date = fields.Date('Effective Date', required=True, states=_MOVE_STATES)
|
||||
post_date = fields.Date('Post Date', readonly=True)
|
||||
description = fields.Char('Description', states=_MOVE_STATES)
|
||||
ext_ref = fields.Char('Ext. Ref')
|
||||
origin = fields.Reference('Origin', selection='get_origin',
|
||||
states=_MOVE_STATES)
|
||||
state = fields.Selection([
|
||||
@@ -921,6 +922,7 @@ class Line(DescriptionOriginMixin, MoveLineMixin, ModelSQL, ModelView):
|
||||
fields.Reference("Move Origin", selection='get_move_origin'),
|
||||
'get_move_field', searcher='search_move_field')
|
||||
description = fields.Char('Description', states=_states)
|
||||
ext_ref = fields.Char('Ext. Ref')
|
||||
move_description_used = fields.Function(
|
||||
fields.Char("Move Description", states=_states),
|
||||
'get_move_field',
|
||||
@@ -1011,7 +1013,9 @@ class Line(DescriptionOriginMixin, MoveLineMixin, ModelSQL, ModelView):
|
||||
|
||||
journal = fields.Function(fields.Many2One('account.journal',"Journal"),'get_journal')
|
||||
|
||||
rate = fields.Function(fields.Numeric("Rate",digits=(1,6)),'get_rate')
|
||||
rate = fields.Numeric("Rate", digits=(10, 6), states={
|
||||
'readonly': _states['readonly'],
|
||||
})
|
||||
|
||||
del _states
|
||||
|
||||
@@ -1115,46 +1119,84 @@ class Line(DescriptionOriginMixin, MoveLineMixin, ModelSQL, ModelView):
|
||||
return line.move.id
|
||||
|
||||
def get_rate(self,name=None):
|
||||
if self.amount_second_currency:
|
||||
return round((self.credit if self.credit else self.debit) / abs(self.amount_second_currency),6)
|
||||
|
||||
@fields.depends(
|
||||
'debit', 'credit',
|
||||
'date','second_currency','amount_second_currency')
|
||||
def on_change_amount_second_currency(self):
|
||||
Currency = Pool().get('currency.currency')
|
||||
Date = Pool().get('ir.date')
|
||||
if self.second_currency != None and self.amount_second_currency != None and (not self.credit and not self.debit):
|
||||
tdate = Date.today()
|
||||
if self.date:
|
||||
tdate = self.date
|
||||
rate = Currency._get_rate([self.second_currency],tdate)
|
||||
if rate:
|
||||
rate = rate[self.second_currency.id]
|
||||
if self.amount_second_currency > 0:
|
||||
self.debit = round(rate * self.amount_second_currency,2)
|
||||
elif self.amount_second_currency > 0:
|
||||
self.credit = round(rate * abs(self.amount_second_currency),2)
|
||||
self.rate = self.get_rate()
|
||||
amount = self.credit if self.credit else self.debit
|
||||
amount_second_currency = getattr(self, 'amount_second_currency', None)
|
||||
if amount_second_currency and amount:
|
||||
return round(abs(amount_second_currency) / amount, 6)
|
||||
|
||||
def _set_rate_from_amounts(self):
|
||||
if (getattr(self, 'amount_second_currency', None)
|
||||
and not getattr(self, 'rate', None)):
|
||||
self.rate = self.get_rate()
|
||||
|
||||
@fields.depends(
|
||||
'debit', 'credit',
|
||||
'date','second_currency','amount_second_currency')
|
||||
'date','second_currency','amount_second_currency', 'rate',
|
||||
'origin', 'move_origin', 'move', '_parent_move.origin',
|
||||
'company', 'account')
|
||||
def on_change_second_currency(self):
|
||||
if self._manual_rate_mode():
|
||||
self.rate = self._get_second_currency_rate()
|
||||
self._compute_amount_from_second_currency()
|
||||
|
||||
@fields.depends(
|
||||
'debit', 'credit',
|
||||
'date','second_currency','amount_second_currency', 'rate',
|
||||
'origin', 'move_origin', 'move', '_parent_move.origin',
|
||||
'company', 'account')
|
||||
def on_change_rate(self):
|
||||
if self._manual_rate_mode() and not self.debit and not self.credit:
|
||||
self._compute_amount_from_second_currency()
|
||||
|
||||
def _manual_rate_mode(self):
|
||||
if getattr(self, 'origin', None) or getattr(self, 'move_origin', None):
|
||||
return False
|
||||
move = getattr(self, 'move', None)
|
||||
if move and getattr(move, 'origin', None):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _get_second_currency_rate(self):
|
||||
Currency = Pool().get('currency.currency')
|
||||
Date = Pool().get('ir.date')
|
||||
if self.second_currency != None and self.amount_second_currency != None and (not self.credit and not self.debit):
|
||||
tdate = Date.today()
|
||||
if self.date:
|
||||
tdate = self.date
|
||||
rate = Currency._get_rate([self.second_currency],tdate)
|
||||
if rate:
|
||||
rate = rate[self.second_currency.id]
|
||||
if self.second_currency is None:
|
||||
return None
|
||||
tdate = Date.today()
|
||||
if self.date:
|
||||
tdate = self.date
|
||||
rates = Currency._get_rate([self.second_currency], tdate)
|
||||
return rates.get(self.second_currency.id) if rates else None
|
||||
|
||||
def _get_company_currency(self):
|
||||
Company = Pool().get('company.company')
|
||||
for record in (self, getattr(self, 'move', None),
|
||||
getattr(self, 'account', None)):
|
||||
company = getattr(record, 'company', None)
|
||||
if company:
|
||||
return company.currency
|
||||
company_id = Transaction().context.get('company')
|
||||
if company_id:
|
||||
return Company(company_id).currency
|
||||
|
||||
def _compute_amount_from_second_currency(self):
|
||||
if self.second_currency is not None and self.amount_second_currency is not None:
|
||||
if not self.rate:
|
||||
self.rate = self._get_second_currency_rate()
|
||||
if self.rate:
|
||||
company_currency = self._get_company_currency()
|
||||
if not company_currency:
|
||||
return
|
||||
amount = company_currency.round(
|
||||
abs(self.amount_second_currency) / self.rate)
|
||||
if self.amount_second_currency > 0:
|
||||
self.debit = round(rate * self.amount_second_currency,2)
|
||||
elif self.amount_second_currency > 0:
|
||||
self.credit = round(rate * abs(self.amount_second_currency),2)
|
||||
self.rate = self.get_rate()
|
||||
self.debit = amount
|
||||
self.credit = Decimal(0)
|
||||
elif self.amount_second_currency < 0:
|
||||
self.debit = Decimal(0)
|
||||
self.credit = amount
|
||||
else:
|
||||
self.debit = Decimal(0)
|
||||
self.credit = Decimal(0)
|
||||
|
||||
@fields.depends(
|
||||
'move', 'debit', 'credit',
|
||||
@@ -1221,15 +1263,26 @@ class Line(DescriptionOriginMixin, MoveLineMixin, ModelSQL, ModelView):
|
||||
if self.debit:
|
||||
self.credit = Decimal(0)
|
||||
self._amount_second_currency_sign()
|
||||
self.rate = self.get_rate()
|
||||
|
||||
@fields.depends('debit', 'credit', 'amount_second_currency')
|
||||
def on_change_credit(self):
|
||||
if self.credit:
|
||||
self.debit = Decimal(0)
|
||||
self._amount_second_currency_sign()
|
||||
self.rate = self.get_rate()
|
||||
|
||||
@fields.depends('amount_second_currency', 'debit', 'credit')
|
||||
@fields.depends(
|
||||
'amount_second_currency', 'debit', 'credit', 'date', 'second_currency',
|
||||
'rate', 'origin', 'move_origin', 'move', '_parent_move.origin',
|
||||
'company', 'account')
|
||||
def on_change_amount_second_currency(self):
|
||||
if self._manual_rate_mode():
|
||||
if not self.rate:
|
||||
self.rate = self._get_second_currency_rate()
|
||||
self._compute_amount_from_second_currency()
|
||||
elif not self.rate:
|
||||
self.rate = self.get_rate()
|
||||
self._amount_second_currency_sign()
|
||||
|
||||
def _amount_second_currency_sign(self):
|
||||
@@ -1239,6 +1292,7 @@ class Line(DescriptionOriginMixin, MoveLineMixin, ModelSQL, ModelView):
|
||||
self.amount_second_currency = \
|
||||
self.amount_second_currency.copy_sign(
|
||||
(self.debit or 0) - (self.credit or 0))
|
||||
self._set_rate_from_amounts()
|
||||
|
||||
@fields.depends('account')
|
||||
def on_change_account(self):
|
||||
@@ -1543,6 +1597,14 @@ class Line(DescriptionOriginMixin, MoveLineMixin, ModelSQL, ModelView):
|
||||
for fname in move_fields(move_name=False):
|
||||
vals.setdefault(fname, None)
|
||||
lines = super(Line, cls).create(vlist)
|
||||
to_save = []
|
||||
for line in lines:
|
||||
if (getattr(line, 'amount_second_currency', None)
|
||||
and not getattr(line, 'rate', None)):
|
||||
line._set_rate_from_amounts()
|
||||
to_save.append(line)
|
||||
if to_save:
|
||||
cls.save(to_save)
|
||||
period_and_journals = set((line.period, line.journal)
|
||||
for line in lines)
|
||||
for period, journal in period_and_journals:
|
||||
|
||||
@@ -12,7 +12,7 @@ from trytond.modules.account.exceptions import (
|
||||
from trytond.modules.account.tax import TaxableMixin
|
||||
from trytond.modules.company.tests import (
|
||||
CompanyTestMixin, PartyCompanyCheckEraseMixin, create_company, set_company)
|
||||
from trytond.modules.currency.tests import create_currency
|
||||
from trytond.modules.currency.tests import add_currency_rate, create_currency
|
||||
from trytond.pool import Pool
|
||||
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
|
||||
from trytond.transaction import Transaction, inactive_records
|
||||
@@ -576,6 +576,54 @@ class AccountTestCase(
|
||||
self.assertEqual(
|
||||
cash_cur.amount_second_currency, Decimal(50))
|
||||
|
||||
@with_transaction()
|
||||
def test_move_line_second_currency_amount_on_change(self):
|
||||
'Test account move line computes amount from second currency'
|
||||
pool = Pool()
|
||||
Account = pool.get('account.account')
|
||||
Line = pool.get('account.move.line')
|
||||
|
||||
company = create_company()
|
||||
with set_company(company):
|
||||
create_chart(company)
|
||||
second_currency = create_currency('sec')
|
||||
add_currency_rate(second_currency, Decimal('0.939506'))
|
||||
|
||||
expense, = Account.search([
|
||||
('type.expense', '=', True),
|
||||
])
|
||||
|
||||
line = Line(
|
||||
account=expense,
|
||||
second_currency=second_currency,
|
||||
amount_second_currency=Decimal('100.00'))
|
||||
line.on_change_amount_second_currency()
|
||||
|
||||
self.assertEqual(line.debit, Decimal('106.44'))
|
||||
self.assertEqual(line.credit, Decimal(0))
|
||||
self.assertEqual(line.rate, Decimal('0.939506'))
|
||||
self.assertEqual(line.get_rate(), Decimal('0.939496'))
|
||||
|
||||
line = Line(
|
||||
account=expense,
|
||||
credit=Decimal('7.61'),
|
||||
second_currency=second_currency,
|
||||
amount_second_currency=Decimal('-108.10'))
|
||||
line.on_change_amount_second_currency()
|
||||
|
||||
self.assertEqual(line.debit, Decimal(0))
|
||||
self.assertEqual(line.credit, Decimal('115.06'))
|
||||
self.assertEqual(line.rate, Decimal('0.939506'))
|
||||
self.assertEqual(line.get_rate(), Decimal('0.939510'))
|
||||
|
||||
line.debit = line.credit = Decimal(0)
|
||||
line.rate = Decimal('1.100000')
|
||||
line.on_change_rate()
|
||||
|
||||
self.assertEqual(line.debit, Decimal(0))
|
||||
self.assertEqual(line.credit, Decimal('98.27'))
|
||||
self.assertEqual(line.rate, Decimal('1.100000'))
|
||||
|
||||
@with_transaction()
|
||||
def test_account_type_amount(self):
|
||||
"Test account type amount"
|
||||
@@ -2049,5 +2097,104 @@ class AccountTestCase(
|
||||
|
||||
self.assertEqual(account.parent, new_account)
|
||||
|
||||
@with_transaction()
|
||||
def test_general_ledger_detail_models_are_registered(self):
|
||||
"Test General Ledger Detail models are registered with expected fields"
|
||||
pool = Pool()
|
||||
Detail = pool.get('account.general_ledger.detail')
|
||||
DetailContext = pool.get('account.general_ledger.detail.context')
|
||||
DetailReport = pool.get(
|
||||
'account.general_ledger.detail.report', type='report')
|
||||
|
||||
for name in [
|
||||
'row_type', 'account_code', 'transaction_currency',
|
||||
'journal_entry_number', 'document_number',
|
||||
'debit_base_currency', 'credit_base_currency',
|
||||
'running_balance_base_currency',
|
||||
'debit_transaction_currency',
|
||||
'credit_transaction_currency',
|
||||
'running_balance_transaction_currency', 'payable_qty']:
|
||||
self.assertIn(name, Detail._fields)
|
||||
for name in [
|
||||
'debit_base_currency', 'credit_base_currency',
|
||||
'balance_base_currency', 'running_balance_base_currency',
|
||||
'debit_transaction_currency',
|
||||
'credit_transaction_currency',
|
||||
'balance_transaction_currency',
|
||||
'running_balance_transaction_currency']:
|
||||
self.assertEqual(Detail._fields[name].digits, (16, 2))
|
||||
self.assertEqual(Detail._fields['payable_qty'].digits, (16, 5))
|
||||
|
||||
for name in [
|
||||
'account', 'account_from', 'account_to', 'currency', 'party',
|
||||
'document_number']:
|
||||
self.assertIn(name, DetailContext._fields)
|
||||
|
||||
account = pool.get('account.account').__table__()
|
||||
account_label = Detail._account_label(account)
|
||||
self.assertIn('||', str(account_label))
|
||||
self.assertNotIn(' + ', str(account_label))
|
||||
self.assertEqual(account_label.params, ('', ' - ', ''))
|
||||
self.assertEqual(
|
||||
DetailReport.__name__, 'account.general_ledger.detail.report')
|
||||
|
||||
@with_transaction()
|
||||
def test_general_ledger_detail_report_groups_by_account_currency(self):
|
||||
"Test General Ledger Detail report groups lines by account and currency"
|
||||
DetailReport = Pool().get(
|
||||
'account.general_ledger.detail.report', type='report')
|
||||
currency = type('Currency', (), {'id': 1})()
|
||||
other_currency = type('Currency', (), {'id': 2})()
|
||||
first = type('Line', (), {
|
||||
'account_code': '1000 - Cash',
|
||||
'account_name': 'Cash',
|
||||
'transaction_currency': currency,
|
||||
})()
|
||||
second = type('Line', (), {
|
||||
'account_code': '1000 - Cash',
|
||||
'account_name': 'Cash',
|
||||
'transaction_currency': currency,
|
||||
})()
|
||||
third = type('Line', (), {
|
||||
'account_code': '2000 - Revenue',
|
||||
'account_name': 'Revenue',
|
||||
'transaction_currency': other_currency,
|
||||
})()
|
||||
|
||||
groups = DetailReport._groups([first, second, third])
|
||||
|
||||
self.assertEqual(len(groups), 2)
|
||||
self.assertEqual(groups[0]['account'], '1000 - Cash')
|
||||
self.assertEqual(groups[0]['currency'], currency)
|
||||
self.assertEqual(groups[0]['lines'], [first, second])
|
||||
self.assertEqual(groups[1]['account'], '2000 - Revenue')
|
||||
self.assertEqual(groups[1]['currency'], other_currency)
|
||||
self.assertEqual(groups[1]['lines'], [third])
|
||||
|
||||
@with_transaction()
|
||||
def test_general_ledger_detail_date_contexts(self):
|
||||
"Test General Ledger Detail opening and movement date contexts"
|
||||
Detail = Pool().get('account.general_ledger.detail')
|
||||
context = {
|
||||
'from_date': datetime.date(2026, 1, 10),
|
||||
'to_date': datetime.date(2026, 1, 31),
|
||||
}
|
||||
|
||||
self.assertEqual(Detail._query_context('opening', context), {
|
||||
'periods': None,
|
||||
'from_date': None,
|
||||
'to_date': datetime.date(2026, 1, 9),
|
||||
})
|
||||
self.assertEqual(Detail._query_context('movement', context), {
|
||||
'periods': None,
|
||||
'from_date': datetime.date(2026, 1, 10),
|
||||
'to_date': datetime.date(2026, 1, 31),
|
||||
})
|
||||
self.assertEqual(Detail._query_context('closing', context), {
|
||||
'periods': None,
|
||||
'from_date': None,
|
||||
'to_date': datetime.date(2026, 1, 31),
|
||||
})
|
||||
|
||||
|
||||
del ModuleTestCase
|
||||
|
||||
41
modules/account/view/general_ledger_detail_context_form.xml
Normal file
41
modules/account/view/general_ledger_detail_context_form.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<form>
|
||||
<label name="fiscalyear"/>
|
||||
<field name="fiscalyear"/>
|
||||
<label name="company"/>
|
||||
<field name="company"/>
|
||||
|
||||
<label name="journal"/>
|
||||
<field name="journal" widget="selection"/>
|
||||
<group col="-1" colspan="2" id="checkboxes">
|
||||
<label name="posted"/>
|
||||
<field name="posted"/>
|
||||
</group>
|
||||
|
||||
<label name="start_period"/>
|
||||
<field name="start_period"/>
|
||||
<label name="end_period"/>
|
||||
<field name="end_period"/>
|
||||
|
||||
<label name="from_date"/>
|
||||
<field name="from_date"/>
|
||||
<label name="to_date"/>
|
||||
<field name="to_date"/>
|
||||
|
||||
<label name="account"/>
|
||||
<field name="account"/>
|
||||
<label name="currency"/>
|
||||
<field name="currency"/>
|
||||
|
||||
<label name="account_from"/>
|
||||
<field name="account_from"/>
|
||||
<label name="account_to"/>
|
||||
<field name="account_to"/>
|
||||
|
||||
<label name="party"/>
|
||||
<field name="party"/>
|
||||
<label name="document_number"/>
|
||||
<field name="document_number"/>
|
||||
</form>
|
||||
39
modules/account/view/general_ledger_detail_list.xml
Normal file
39
modules/account/view/general_ledger_detail_list.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<tree>
|
||||
<field name="row_type"/>
|
||||
<field name="account_code" expand="1"/>
|
||||
<field name="transaction_currency"/>
|
||||
<field name="base_currency" optional="1"/>
|
||||
<field name="posting_date"/>
|
||||
<field name="journal"/>
|
||||
<field name="journal_entry_number"/>
|
||||
<field name="document_number"/>
|
||||
<field name="voucher_number" optional="1"/>
|
||||
<field name="document_type" optional="1"/>
|
||||
<field name="document_date" optional="1"/>
|
||||
<field name="posting_status" optional="1"/>
|
||||
<field name="party" optional="1"/>
|
||||
<field name="description" expand="1" optional="1"/>
|
||||
<field name="move_description" expand="1" optional="1"/>
|
||||
<field name="reference" optional="1"/>
|
||||
<field name="origin" optional="1"/>
|
||||
<field name="debit_base_currency" sum="1"/>
|
||||
<field name="credit_base_currency" sum="1"/>
|
||||
<field name="balance_base_currency" sum="1"/>
|
||||
<field name="running_balance_base_currency" optional="1"/>
|
||||
<field name="debit_transaction_currency" sum="1" optional="1"/>
|
||||
<field name="credit_transaction_currency" sum="1" optional="1"/>
|
||||
<field name="balance_transaction_currency" sum="1" optional="1"/>
|
||||
<field name="running_balance_transaction_currency" optional="1"/>
|
||||
<field name="payable_qty" sum="1" optional="1"/>
|
||||
<field name="uom" optional="1"/>
|
||||
<field name="entered_by" optional="1"/>
|
||||
<field name="entered_date" optional="1"/>
|
||||
<field name="modified_by" optional="1"/>
|
||||
<field name="modified_date" optional="1"/>
|
||||
<field name="move" optional="1"/>
|
||||
<field name="move_line" optional="1"/>
|
||||
<field name="company" optional="1"/>
|
||||
</tree>
|
||||
@@ -21,6 +21,8 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="origin" colspan="3"/>
|
||||
<label name="description_used"/>
|
||||
<field name="description_used" colspan="3"/>
|
||||
<label name="ext_ref"/>
|
||||
<field name="ext_ref" colspan="3"/>
|
||||
<notebook>
|
||||
<page name="lines">
|
||||
<field name="lines" colspan="4"
|
||||
|
||||
@@ -26,6 +26,8 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="origin"/>
|
||||
<label name="description_used"/>
|
||||
<field name="description_used" colspan="3"/>
|
||||
<label name="ext_ref"/>
|
||||
<field name="ext_ref" colspan="3"/>
|
||||
<notebook colspan="4">
|
||||
<page string="Other Info" id="info">
|
||||
<label name="date"/>
|
||||
|
||||
@@ -26,6 +26,8 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="amount_second_currency" symbol=""/>
|
||||
<label name="second_currency"/>
|
||||
<field name="second_currency"/>
|
||||
<label name="rate"/>
|
||||
<field name="rate"/>
|
||||
</page>
|
||||
<page name="tax_lines">
|
||||
<field name="tax_lines" colspan="4"/>
|
||||
|
||||
@@ -4,6 +4,7 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<tree editable="1">
|
||||
<field name="move"/>
|
||||
<field name="account" expand="1"/>
|
||||
<field name="ext_ref" expand="1" optional="1"/>
|
||||
<field name="party" expand="1"/>
|
||||
<field name="debit" sum="1"/>
|
||||
<field name="credit" sum="1"/>
|
||||
|
||||
15
modules/account_ch_os/__init__.py
Normal file
15
modules/account_ch_os/__init__.py
Normal file
@@ -0,0 +1,15 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
from trytond.pool import Pool
|
||||
|
||||
from . import account
|
||||
|
||||
|
||||
def register():
|
||||
Pool.register(
|
||||
account.AccountTemplate,
|
||||
module='account_ch_os', type_='model')
|
||||
Pool.register(
|
||||
account.CreateChart,
|
||||
module='account_ch_os', type_='wizard')
|
||||
27
modules/account_ch_os/account.py
Normal file
27
modules/account_ch_os/account.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
|
||||
|
||||
class AccountTemplate(metaclass=PoolMeta):
|
||||
__name__ = 'account.account.template'
|
||||
|
||||
|
||||
class CreateChart(metaclass=PoolMeta):
|
||||
__name__ = 'account.create_chart'
|
||||
|
||||
def default_properties(self, fields):
|
||||
pool = Pool()
|
||||
ModelData = pool.get('ir.model.data')
|
||||
defaults = super().default_properties(fields)
|
||||
try:
|
||||
template_id = ModelData.get_id('account_ch_os', 'root')
|
||||
except KeyError:
|
||||
return defaults
|
||||
if self.account.account_template.id == template_id:
|
||||
defaults['account_receivable'] = self.get_account(
|
||||
'account_ch_os.acct_110000')
|
||||
defaults['account_payable'] = self.get_account(
|
||||
'account_ch_os.acct_200000')
|
||||
return defaults
|
||||
2395
modules/account_ch_os/account_ch_os.xml
Normal file
2395
modules/account_ch_os/account_ch_os.xml
Normal file
File diff suppressed because it is too large
Load Diff
2
modules/account_ch_os/tests/__init__.py
Normal file
2
modules/account_ch_os/tests/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
12
modules/account_ch_os/tests/test_module.py
Normal file
12
modules/account_ch_os/tests/test_module.py
Normal file
@@ -0,0 +1,12 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
from trytond.tests.test_tryton import ModuleTestCase
|
||||
|
||||
|
||||
class AccountCHOSTestCase(ModuleTestCase):
|
||||
'Test Account CH OS module'
|
||||
module = 'account_ch_os'
|
||||
|
||||
|
||||
del ModuleTestCase
|
||||
6
modules/account_ch_os/tryton.cfg
Normal file
6
modules/account_ch_os/tryton.cfg
Normal file
@@ -0,0 +1,6 @@
|
||||
[tryton]
|
||||
version=7.2.3
|
||||
depends:
|
||||
account
|
||||
xml:
|
||||
account_ch_os.xml
|
||||
3886
modules/account_invoice/commission__ict_final.fodt
Normal file
3886
modules/account_invoice/commission__ict_final.fodt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -485,7 +485,7 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
||||
})
|
||||
cls.__rpc__.update({
|
||||
'post': RPC(
|
||||
readonly=False, instantiate=0, fresh_session=True),
|
||||
readonly=False, instantiate=0, fresh_session=False),
|
||||
})
|
||||
|
||||
@classmethod
|
||||
@@ -515,7 +515,9 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
||||
~((table.state == 'cancelled') & (table.number == Null)),
|
||||
CharLength(table.number), table.number]
|
||||
|
||||
@fields.depends('selection_rate','rate_date')
|
||||
@fields.depends(
|
||||
'selection_rate', 'rate_date', 'currency', 'company', 'invoice_date',
|
||||
'lines')
|
||||
def on_change_with_rate(self, name=None):
|
||||
return self.get_selected_rate()
|
||||
|
||||
@@ -528,8 +530,8 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
||||
def get_selected_rate(self,name=None):
|
||||
Currency = Pool().get('currency.currency')
|
||||
Date = Pool().get('ir.date')
|
||||
company = self.company
|
||||
currency = self.currency
|
||||
company = getattr(self, 'company', None)
|
||||
currency = getattr(self, 'currency', None)
|
||||
|
||||
if not currency or not company:
|
||||
return None
|
||||
@@ -563,6 +565,16 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
||||
if rate:
|
||||
return round(1/rate,6) or 1
|
||||
|
||||
def _compute_company_currency_amount(self, amount, date=None):
|
||||
Currency = Pool().get('currency.currency')
|
||||
if self.currency == self.company.currency:
|
||||
return amount
|
||||
if self.rate:
|
||||
return self.company.currency.round(amount / self.rate)
|
||||
with Transaction().set_context(date=date or self.currency_date):
|
||||
return Currency.compute(
|
||||
self.currency, amount, self.company.currency)
|
||||
|
||||
@staticmethod
|
||||
def default_type():
|
||||
return Transaction().context.get('type', 'out')
|
||||
@@ -1218,6 +1230,7 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
||||
line.amount_second_currency = (
|
||||
line.amount_second_currency.copy_sign(
|
||||
line.debit - line.credit))
|
||||
line.rate = line.get_rate()
|
||||
line.account = self.account
|
||||
logger.info("_GET_MOVE_LINE_ACCOUNT:%s",line.account)
|
||||
if self.account.party_required:
|
||||
@@ -1286,9 +1299,16 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
||||
remainder = sum(l.debit - l.credit for l in move_lines)
|
||||
if self.payment_term:
|
||||
payment_date = self.payment_term_date or self.invoice_date or today
|
||||
purchase_line = int(str(self.lines[0].origin).split(",")[1]) if self.lines[0].origin else None
|
||||
origin = self.lines[0].origin if self.lines and self.lines[0].origin else None
|
||||
model = str(origin).split(",")[0] if origin else None
|
||||
logger.info("MODEL:%s",model)
|
||||
line = None
|
||||
if model:
|
||||
Line = Pool().get(model)
|
||||
line = Line(int(str(origin).split(",")[1]))
|
||||
logger.info("LINE:%s",line)
|
||||
term_lines = self.payment_term.compute(
|
||||
self.total_amount, self.currency, payment_date, purchase_line)
|
||||
self.total_amount, self.currency, payment_date, line)
|
||||
else:
|
||||
term_lines = [(self.payment_term_date or today, self.total_amount)]
|
||||
past_payment_term_dates = []
|
||||
@@ -1885,17 +1905,17 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
||||
cls._check_taxes(invoices)
|
||||
# cls._check_similar(invoices)
|
||||
|
||||
invoices_in = cls.browse([i for i in invoices if i.type == 'in'])
|
||||
cls.set_number(invoices_in)
|
||||
cls.set_number(invoices)
|
||||
cls._store_cache(invoices)
|
||||
|
||||
moves = []
|
||||
for invoice in invoices:
|
||||
if invoice.type == 'in':
|
||||
move = invoice.get_move()
|
||||
if move != invoice.move:
|
||||
invoice.move = move
|
||||
moves.append(move)
|
||||
if invoice.currency != invoice.company.currency and not invoice.rate:
|
||||
invoice.rate = invoice.get_selected_rate()
|
||||
move = invoice.get_move()
|
||||
if move != invoice.move:
|
||||
invoice.move = move
|
||||
moves.append(move)
|
||||
invoice.do_lot_invoicing()
|
||||
if moves:
|
||||
Move.save(moves)
|
||||
@@ -1958,21 +1978,35 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
||||
move_line_.amount_second_currency = -original_amount
|
||||
|
||||
if amount < 0:
|
||||
lot_has_sale_invoice = (
|
||||
move_line.lot
|
||||
and (move_line.lot.sale_invoice_line
|
||||
or move_line.lot.sale_invoice_line_prov))
|
||||
move_line.debit = Decimal(0)
|
||||
move_line.credit = -amount
|
||||
move_line.account = gl.product.account_stock_used
|
||||
move_line.account = gl.product.account_stock_used if not lot_has_sale_invoice else gl.product.account_stock_out_used
|
||||
move_line.account = gl.product.account_cogs_used if gl.fee else move_line.account
|
||||
move_line_.credit = Decimal(0)
|
||||
move_line_.debit = -amount
|
||||
move_line_.account = gl.product.account_stock_in_used
|
||||
else:
|
||||
lot_has_sale_invoice = (
|
||||
move_line.lot
|
||||
and (move_line.lot.sale_invoice_line
|
||||
or move_line.lot.sale_invoice_line_prov))
|
||||
move_line.debit = amount
|
||||
move_line.credit = Decimal(0)
|
||||
move_line.account = gl.product.account_stock_used
|
||||
move_line.account = gl.product.account_stock_used if not lot_has_sale_invoice else gl.product.account_stock_out_used
|
||||
move_line.account = gl.product.account_cogs_used if gl.fee else move_line.account
|
||||
move_line_.debit = Decimal(0)
|
||||
move_line_.credit = amount
|
||||
move_line_.account = gl.product.account_stock_in_used
|
||||
|
||||
move_lines = [move_line,move_line_]
|
||||
if getattr(move_line, 'amount_second_currency', None):
|
||||
move_line.rate = move_line.get_rate()
|
||||
if getattr(move_line_, 'amount_second_currency', None):
|
||||
move_line_.rate = move_line_.get_rate()
|
||||
|
||||
if drop:
|
||||
drop_line = AccountMoveLine()
|
||||
@@ -2010,6 +2044,10 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
||||
drop_line_.second_currency = self.currency
|
||||
drop_line_.amount_second_currency = -original_amount
|
||||
move_lines = [drop_line,drop_line_]
|
||||
if getattr(drop_line, 'amount_second_currency', None):
|
||||
drop_line.rate = drop_line.get_rate()
|
||||
if getattr(drop_line_, 'amount_second_currency', None):
|
||||
drop_line_.rate = drop_line_.get_rate()
|
||||
|
||||
return move_lines
|
||||
|
||||
@@ -2031,7 +2069,17 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
||||
var_qt = sum([i.quantity for i in gl])
|
||||
logger.info("LOT_TO_PROCESS:%s",lot)
|
||||
logger.info("FEE_TO_PROCESS:%s",gl[0].fee)
|
||||
if lot:
|
||||
fee_correction = (
|
||||
gl[0].fee
|
||||
and any(Decimal(str(i.quantity or 0)) < 0 for i in gl)
|
||||
and any(Decimal(str(i.quantity or 0)) > 0 for i in gl))
|
||||
if (gl[0].fee and not gl[0].product.landed_cost
|
||||
and not fee_correction):
|
||||
diff = gl[0].fee.amount - gl[0].fee.get_non_cog(lot)
|
||||
account_move = gl[0].fee._get_account_move_fee(lot,'in',diff)
|
||||
if account_move:
|
||||
Move.save([account_move])
|
||||
if (lot and not gl[0].fee) or (gl[0].fee and gl[0].product.landed_cost):
|
||||
adjust_move_lines = []
|
||||
mov = None
|
||||
if self.type == 'in':
|
||||
@@ -3163,7 +3211,6 @@ class InvoiceLine(sequence_ordered(), ModelSQL, ModelView, TaxableMixin):
|
||||
|
||||
def _compute_taxes(self):
|
||||
pool = Pool()
|
||||
Currency = pool.get('currency.currency')
|
||||
TaxLine = pool.get('account.tax.line')
|
||||
|
||||
tax_lines = []
|
||||
@@ -3172,11 +3219,7 @@ class InvoiceLine(sequence_ordered(), ModelSQL, ModelView, TaxableMixin):
|
||||
taxes = self._get_taxes().values()
|
||||
for tax in taxes:
|
||||
amount = tax['base']
|
||||
with Transaction().set_context(
|
||||
date=self.invoice.currency_date):
|
||||
amount = Currency.compute(
|
||||
self.invoice.currency, amount,
|
||||
self.invoice.company.currency)
|
||||
amount = self.invoice._compute_company_currency_amount(amount)
|
||||
tax_line = TaxLine()
|
||||
tax_line.amount = amount
|
||||
tax_line.type = 'base'
|
||||
@@ -3241,6 +3284,7 @@ class InvoiceLine(sequence_ordered(), ModelSQL, ModelView, TaxableMixin):
|
||||
line.amount_second_currency = (
|
||||
line.amount_second_currency.copy_sign(
|
||||
line.debit - line.credit))
|
||||
line.rate = line.get_rate()
|
||||
line.account = self.account
|
||||
logger.info("GET_MOVE_LINES_ACCOUNT:%s",line.account)
|
||||
if self.account.party_required:
|
||||
@@ -3500,9 +3544,7 @@ class InvoiceTax(sequence_ordered(), ModelSQL, ModelView):
|
||||
'''
|
||||
Return a list of move lines instances for invoice tax
|
||||
'''
|
||||
Currency = Pool().get('currency.currency')
|
||||
pool = Pool()
|
||||
Currency = pool.get('currency.currency')
|
||||
MoveLine = pool.get('account.move.line')
|
||||
TaxLine = pool.get('account.tax.line')
|
||||
line = MoveLine()
|
||||
@@ -3510,11 +3552,8 @@ class InvoiceTax(sequence_ordered(), ModelSQL, ModelView):
|
||||
return []
|
||||
line.description = self.description
|
||||
if self.invoice.currency != self.invoice.company.currency:
|
||||
with Transaction().set_context(date=self.invoice.currency_date):
|
||||
amount = Currency.compute(self.invoice.currency, self.amount,
|
||||
self.invoice.company.currency)
|
||||
base = Currency.compute(self.invoice.currency, self.base,
|
||||
self.invoice.company.currency)
|
||||
amount = self.invoice._compute_company_currency_amount(self.amount)
|
||||
base = self.invoice._compute_company_currency_amount(self.base)
|
||||
line.amount_second_currency = self.amount
|
||||
line.second_currency = self.invoice.currency
|
||||
else:
|
||||
@@ -3536,6 +3575,7 @@ class InvoiceTax(sequence_ordered(), ModelSQL, ModelView):
|
||||
line.amount_second_currency = (
|
||||
line.amount_second_currency.copy_sign(
|
||||
line.debit - line.credit))
|
||||
line.rate = line.get_rate()
|
||||
line.account = self.account
|
||||
if self.account.party_required:
|
||||
line.party = self.invoice.party
|
||||
@@ -3684,13 +3724,19 @@ class InvoiceReport(Report):
|
||||
Invoice = pool.get('account.invoice')
|
||||
# Re-instantiate because records are TranslateModel
|
||||
invoice, = Invoice.browse(records)
|
||||
if invoice.invoice_report_cache:
|
||||
report_path = cls._get_action_report_path(action)
|
||||
use_cache = (
|
||||
report_path in (None, 'account_invoice/invoice.fodt')
|
||||
and invoice.invoice_report_cache
|
||||
)
|
||||
if use_cache:
|
||||
return (
|
||||
invoice.invoice_report_format,
|
||||
invoice.invoice_report_cache)
|
||||
else:
|
||||
result = super()._execute(records, header, data, action)
|
||||
if invoice.invoice_report_versioned:
|
||||
if (invoice.invoice_report_versioned
|
||||
and report_path in (None, 'account_invoice/invoice.fodt')):
|
||||
format_, data = result
|
||||
if isinstance(data, str):
|
||||
data = bytes(data, 'utf-8')
|
||||
@@ -3707,6 +3753,12 @@ class InvoiceReport(Report):
|
||||
with Transaction().set_context(language=False):
|
||||
return super().render(*args, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def _get_action_report_path(action):
|
||||
if isinstance(action, dict):
|
||||
return action.get('report')
|
||||
return getattr(action, 'report', None)
|
||||
|
||||
@classmethod
|
||||
def execute(cls, ids, data):
|
||||
pool = Pool()
|
||||
|
||||
@@ -264,11 +264,6 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="wiz_name">account.invoice.refresh_invoice_report</field>
|
||||
<field name="model">account.invoice</field>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="refresh_invoice_report_keyword">
|
||||
<field name="keyword">form_print</field>
|
||||
<field name="model">account.invoice,-1</field>
|
||||
<field name="action" ref="refresh_invoice_report_wizard"/>
|
||||
</record>
|
||||
<record model="ir.action-res.group" id="refresh_invoice_report-group_account_admin">
|
||||
<field name="action" ref="refresh_invoice_report_wizard"/>
|
||||
<field name="group" ref="account.group_account_admin"/>
|
||||
@@ -318,6 +313,58 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="action" ref="report_prepayment"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.report" id="report_invoice_ict_final">
|
||||
<field name="name">CN/DN</field>
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="report_name">account.invoice</field>
|
||||
<field name="report">account_invoice/invoice_ict_final.fodt</field>
|
||||
<field name="single" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="report_invoice_ict_final_keyword">
|
||||
<field name="keyword">form_print</field>
|
||||
<field name="model">account.invoice,-1</field>
|
||||
<field name="action" ref="report_invoice_ict_final"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.report" id="report_invoice_ict_commission_final">
|
||||
<field name="name">CN/DN Commission</field>
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="report_name">account.invoice</field>
|
||||
<field name="report">account_invoice/commission__ict_final.fodt</field>
|
||||
<field name="single" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="report_invoice_ict_commission_final_keyword">
|
||||
<field name="keyword">form_print</field>
|
||||
<field name="model">account.invoice,-1</field>
|
||||
<field name="action" ref="report_invoice_ict_commission_final"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.report" id="report_sale_commission_ict">
|
||||
<field name="name">Commission invoice Sale</field>
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="report_name">account.invoice</field>
|
||||
<field name="report">account_invoice/sale_commission_ict.fodt</field>
|
||||
<field name="single" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="report_sale_commission_ict_keyword">
|
||||
<field name="keyword">form_print</field>
|
||||
<field name="model">account.invoice,-1</field>
|
||||
<field name="action" ref="report_sale_commission_ict"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.report" id="report_purchase_commission_ict">
|
||||
<field name="name">Commission invoice Purchase</field>
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="report_name">account.invoice</field>
|
||||
<field name="report">account_invoice/purchase_commission_ict.fodt</field>
|
||||
<field name="single" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="report_purchase_commission_ict_keyword">
|
||||
<field name="keyword">form_print</field>
|
||||
<field name="model">account.invoice,-1</field>
|
||||
<field name="action" ref="report_purchase_commission_ict"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.sequence.type" id="sequence_type_account_invoice">
|
||||
<field name="name">Invoice</field>
|
||||
</record>
|
||||
|
||||
4157
modules/account_invoice/invoice_ict.fodt
Normal file
4157
modules/account_invoice/invoice_ict.fodt
Normal file
File diff suppressed because it is too large
Load Diff
4127
modules/account_invoice/invoice_ict_final.fodt
Normal file
4127
modules/account_invoice/invoice_ict_final.fodt
Normal file
File diff suppressed because it is too large
Load Diff
4180
modules/account_invoice/invoice_ict_final.fodt.bak
Normal file
4180
modules/account_invoice/invoice_ict_final.fodt.bak
Normal file
File diff suppressed because it is too large
Load Diff
1775
modules/account_invoice/invoice_melya.fodt
Normal file
1775
modules/account_invoice/invoice_melya.fodt
Normal file
File diff suppressed because it is too large
Load Diff
1498
modules/account_invoice/packing_list.fodt
Normal file
1498
modules/account_invoice/packing_list.fodt
Normal file
File diff suppressed because it is too large
Load Diff
1872
modules/account_invoice/payment_order_melya.fodt
Normal file
1872
modules/account_invoice/payment_order_melya.fodt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -13,8 +13,10 @@ from trytond.pool import Pool
|
||||
from trytond.pyson import Eval
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.wizard import Button, StateView, Wizard
|
||||
|
||||
from .exceptions import PaymentTermComputeError, PaymentTermValidationError
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PaymentTerm(DeactivableMixin, ModelSQL, ModelView):
|
||||
@@ -46,7 +48,7 @@ class PaymentTerm(DeactivableMixin, ModelSQL, ModelView):
|
||||
'.msg_payment_term_missing_last_remainder',
|
||||
payment_term=term.rec_name))
|
||||
|
||||
def compute(self, amount, currency, date, purchase_line = None):
|
||||
def compute(self, amount, currency, date, line_ = None):
|
||||
"""Calculate payment terms and return a list of tuples
|
||||
with (date, amount) for each payment term line.
|
||||
|
||||
@@ -59,7 +61,7 @@ class PaymentTerm(DeactivableMixin, ModelSQL, ModelView):
|
||||
remainder = amount
|
||||
for line in self.lines:
|
||||
value = line.get_value(remainder, amount, currency)
|
||||
value_date = line.get_date(date, purchase_line)
|
||||
value_date = line.get_date(date, line_)
|
||||
if value is None or not value_date:
|
||||
continue
|
||||
if ((remainder - value) * sign) < Decimal(0):
|
||||
@@ -155,12 +157,11 @@ class PaymentTermLine(sequence_ordered(), ModelSQL, ModelView):
|
||||
self.ratio = self.round(1 / self.divisor,
|
||||
self.__class__.ratio.digits[1])
|
||||
|
||||
def get_date(self, date, purchase_line = None):
|
||||
def get_date(self, date, line = None):
|
||||
#find date based on trigger:
|
||||
if purchase_line and self.trigger_event:
|
||||
PurchaseLine = Pool().get('purchase.line')
|
||||
purchase_line = PurchaseLine(purchase_line)
|
||||
trigger_date = purchase_line.get_date(self.trigger_event)
|
||||
if line and self.trigger_event:
|
||||
trigger_date = line.get_date(self.trigger_event)
|
||||
logger.info("DATE_FROM_LINE:%s",trigger_date)
|
||||
if trigger_date:
|
||||
date = trigger_date
|
||||
|
||||
|
||||
3892
modules/account_invoice/purchase_commission_ict.fodt
Normal file
3892
modules/account_invoice/purchase_commission_ict.fodt
Normal file
File diff suppressed because it is too large
Load Diff
3730
modules/account_invoice/sale_commission_ict.fodt
Normal file
3730
modules/account_invoice/sale_commission_ict.fodt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,7 @@
|
||||
|
||||
import datetime
|
||||
from decimal import Decimal
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from trytond.modules.account_invoice.exceptions import (
|
||||
PaymentTermValidationError)
|
||||
@@ -251,5 +252,70 @@ class AccountInvoiceTestCase(
|
||||
(datetime.date(2012, 1, 14), Decimal('-1.0')),
|
||||
])
|
||||
|
||||
def test_post_rpc_does_not_require_fresh_session(self):
|
||||
'posting invoices does not force a fresh session'
|
||||
Invoice = Pool().get('account.invoice')
|
||||
|
||||
self.assertFalse(Invoice.__rpc__['post'].fresh_session)
|
||||
|
||||
@with_transaction()
|
||||
def test_validate_invoice_creates_move_for_customer_invoice(self):
|
||||
'validating customer invoices now creates the account move'
|
||||
Invoice = Pool().get('account.invoice')
|
||||
|
||||
move = Mock()
|
||||
invoice = Invoice()
|
||||
invoice.type = 'out'
|
||||
invoice.move = None
|
||||
invoice.get_move = Mock(return_value=move)
|
||||
invoice.do_lot_invoicing = Mock()
|
||||
|
||||
move_model = Mock()
|
||||
|
||||
with patch.object(Invoice, '_check_taxes'), patch.object(
|
||||
Invoice, '_store_cache'), patch.object(
|
||||
Invoice, 'browse', return_value=[]), patch.object(
|
||||
Invoice, 'cleanMoves') as clean_moves, patch.object(
|
||||
Invoice, 'save') as save_invoices, patch(
|
||||
'trytond.modules.account_invoice.invoice.Pool'
|
||||
) as PoolMock:
|
||||
PoolMock.return_value.get.return_value = move_model
|
||||
|
||||
Invoice.validate_invoice([invoice])
|
||||
|
||||
self.assertIs(invoice.move, move)
|
||||
invoice.get_move.assert_called_once_with()
|
||||
invoice.do_lot_invoicing.assert_called_once_with()
|
||||
move_model.save.assert_called_once_with([move])
|
||||
clean_moves.assert_called_once_with([move])
|
||||
save_invoices.assert_called()
|
||||
|
||||
@with_transaction()
|
||||
def test_validate_invoice_sets_number_for_customer_invoice(self):
|
||||
'validating customer invoices now assigns the invoice number'
|
||||
Invoice = Pool().get('account.invoice')
|
||||
|
||||
move = Mock()
|
||||
invoice = Invoice()
|
||||
invoice.type = 'out'
|
||||
invoice.move = None
|
||||
invoice.get_move = Mock(return_value=move)
|
||||
invoice.do_lot_invoicing = Mock()
|
||||
|
||||
move_model = Mock()
|
||||
|
||||
with patch.object(Invoice, '_check_taxes'), patch.object(
|
||||
Invoice, '_store_cache'), patch.object(
|
||||
Invoice, 'set_number') as set_number, patch.object(
|
||||
Invoice, 'cleanMoves'), patch.object(
|
||||
Invoice, 'save'), patch(
|
||||
'trytond.modules.account_invoice.invoice.Pool'
|
||||
) as PoolMock:
|
||||
PoolMock.return_value.get.return_value = move_model
|
||||
|
||||
Invoice.validate_invoice([invoice])
|
||||
|
||||
set_number.assert_called_once_with([invoice])
|
||||
|
||||
|
||||
del ModuleTestCase
|
||||
|
||||
@@ -92,8 +92,8 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="invoice_report_revisions" colspan="4"/>
|
||||
</page>
|
||||
<page string="Rate management" id="rate">
|
||||
<label name="warning"/>
|
||||
<field name="warning"/>
|
||||
<!-- <label name="warning"/>
|
||||
<field name="warning"/> -->
|
||||
<newline/>
|
||||
<label name="rate"/>
|
||||
<field name="rate"/>
|
||||
|
||||
14
modules/account_itsa/__init__.py
Normal file
14
modules/account_itsa/__init__.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# 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_itsa', type_='model')
|
||||
Pool.register(
|
||||
account.CreateChart,
|
||||
module='account_itsa', type_='wizard')
|
||||
40
modules/account_itsa/account.py
Normal file
40
modules/account_itsa/account.py
Normal file
@@ -0,0 +1,40 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
import csv
|
||||
from io import BytesIO, TextIOWrapper
|
||||
|
||||
from sql import Table
|
||||
from sql.aggregate import Sum
|
||||
from sql.conditionals import Coalesce
|
||||
|
||||
from trytond.config import config
|
||||
from trytond.model import ModelStorage, ModelView, fields
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pyson import Eval
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.wizard import Button, StateTransition, StateView, Wizard
|
||||
|
||||
class AccountTemplate(metaclass=PoolMeta):
|
||||
__name__ = 'account.account.template'
|
||||
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
cursor = Transaction().connection.cursor()
|
||||
model_data = Table('ir_model_data')
|
||||
super().__register__(module_name)
|
||||
|
||||
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)
|
||||
# template_id = ModelData.get_id('account_ch.root')
|
||||
# if self.account.account_template.id == template_id:
|
||||
# defaults['account_receivable'] = self.get_account(
|
||||
# 'account_ch.3400')
|
||||
# defaults['account_payable'] = self.get_account(
|
||||
# 'account_ch.6040')
|
||||
return defaults
|
||||
|
||||
3336
modules/account_itsa/account_itsa.xml
Normal file
3336
modules/account_itsa/account_itsa.xml
Normal file
File diff suppressed because it is too large
Load Diff
10
modules/account_itsa/tryton.cfg
Normal file
10
modules/account_itsa/tryton.cfg
Normal file
@@ -0,0 +1,10 @@
|
||||
[tryton]
|
||||
version=7.2.3
|
||||
depends:
|
||||
account
|
||||
extras_depend:
|
||||
account_invoice
|
||||
xml:
|
||||
account_itsa.xml
|
||||
#tax_ict.xml
|
||||
|
||||
@@ -6,7 +6,7 @@ from decimal import Decimal
|
||||
from trytond.i18n import gettext
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
from trytond.exceptions import UserWarning, UserError
|
||||
from .exceptions import COGSWarning
|
||||
import logging
|
||||
|
||||
@@ -74,8 +74,8 @@ class InvoiceLine(metaclass=PoolMeta):
|
||||
if move_line.second_currency:
|
||||
move_line.amount_second_currency = amount
|
||||
else:
|
||||
move_line.debit = Decimal(0)
|
||||
move_line.credit = -amount_converted
|
||||
move_line.debit = -amount_converted
|
||||
move_line.credit = Decimal(0)
|
||||
move_line.account = self.product.account_stock_out_used
|
||||
if move_line.second_currency:
|
||||
move_line.amount_second_currency = amount
|
||||
@@ -171,10 +171,32 @@ class InvoiceLine(metaclass=PoolMeta):
|
||||
cost = self.amount
|
||||
else:
|
||||
cost = self.lot.get_cog()
|
||||
if not cost or cost == 0:
|
||||
raise UserError('No COG for this invoice, please generate the reception of the goods')
|
||||
if self.amount < 0 :
|
||||
cost *= -1
|
||||
logger.info("GETMOVELINES_COST:%s",cost)
|
||||
anglo_saxon_move_lines_ = []
|
||||
with Transaction().set_context(
|
||||
company=self.invoice.company.id, date=accounting_date):
|
||||
anglo_saxon_move_lines = self._get_anglo_saxon_move_lines(
|
||||
cost, type_)
|
||||
lot_has_sale_invoice = (
|
||||
self.lot
|
||||
and (self.lot.sale_invoice_line_prov
|
||||
or self.lot.sale_invoice_line))
|
||||
if type_ == 'in_supplier' and lot_has_sale_invoice and not self.fee:
|
||||
anglo_saxon_move_lines_ = self._get_anglo_saxon_move_lines(cost, 'out_customer')
|
||||
result.extend(anglo_saxon_move_lines)
|
||||
result.extend(anglo_saxon_move_lines_)
|
||||
#Fee inventoried delivery management
|
||||
if self.lot and type_ != 'in_supplier':
|
||||
FeeLots = Pool().get('fee.lots')
|
||||
fees = FeeLots.search(['lot','=',self.lot.id])
|
||||
for fl in fees:
|
||||
if fl.fee.type == 'ordered' and fl.fee.product.template.landed_cost:
|
||||
AccountMove = Pool().get('account.move')
|
||||
account_move = fl.fee._get_account_move_fee(fl.lot,'out')
|
||||
AccountMove.save([account_move])
|
||||
|
||||
return result
|
||||
|
||||
@@ -16,11 +16,11 @@ account_names = [
|
||||
class Category(metaclass=PoolMeta):
|
||||
__name__ = 'product.category'
|
||||
account_stock = fields.MultiValue(fields.Many2One(
|
||||
'account.account', "Account Stock",
|
||||
'account.account', "Account Stock/Cost Income",
|
||||
domain=[
|
||||
('closed', '!=', True),
|
||||
('type.stock', '=', True),
|
||||
('type.statement', '=', 'balance'),
|
||||
# ('type.stock', '=', True),
|
||||
# ('type.statement', '=', 'balance'),
|
||||
('company', '=', Eval('context', {}).get('company', -1)),
|
||||
],
|
||||
states={
|
||||
@@ -29,7 +29,7 @@ class Category(metaclass=PoolMeta):
|
||||
| ~Eval('accounting', False)),
|
||||
}))
|
||||
account_stock_in = fields.MultiValue(fields.Many2One(
|
||||
'account.account', "Account Stock IN",
|
||||
'account.account', "Account Stock IN/Cost liability",
|
||||
domain=[
|
||||
('closed', '!=', True),
|
||||
('type.stock', '=', True),
|
||||
@@ -41,7 +41,7 @@ class Category(metaclass=PoolMeta):
|
||||
| ~Eval('accounting', False)),
|
||||
}))
|
||||
account_stock_out = fields.MultiValue(fields.Many2One(
|
||||
'account.account', "Account Stock OUT",
|
||||
'account.account', "Account Stock OUT/Cost liability",
|
||||
domain=[
|
||||
('closed', '!=', True),
|
||||
('type.stock', '=', True),
|
||||
@@ -103,8 +103,8 @@ class CategoryAccount(metaclass=PoolMeta):
|
||||
'account.account', "Account Stock",
|
||||
domain=[
|
||||
('closed', '!=', True),
|
||||
('type.stock', '=', True),
|
||||
('type.statement', '=', 'balance'),
|
||||
# ('type.stock', '=', True),
|
||||
# ('type.statement', '=', 'balance'),
|
||||
('company', '=', Eval('company', -1)),
|
||||
])
|
||||
account_stock_in = fields.Many2One(
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<data>
|
||||
<xpath expr="/form/notebook" position="inside">
|
||||
<page string="Analytic" id="analytic_accounts">
|
||||
<xpath expr="/form/notebook/page[@id='invoice']" position="before">
|
||||
<page string="Analytics" id="analytic_accounts">
|
||||
<field name="analytic_accounts" colspan="4"/>
|
||||
</page>
|
||||
</xpath>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
from trytond.pool import Pool
|
||||
from . import automation,rules #, document
|
||||
from . import automation,rules,freight_booking,cron #, document
|
||||
|
||||
def register():
|
||||
Pool.register(
|
||||
automation.AutomationDocument,
|
||||
rules.AutomationRuleSet,
|
||||
freight_booking.FreightBookingInfo,
|
||||
cron.Cron,
|
||||
cron.AutomationCron,
|
||||
module='automation', type_='model')
|
||||
@@ -1,10 +1,15 @@
|
||||
from trytond.model import ModelSQL, ModelView, fields, Workflow
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pyson import Eval
|
||||
from trytond.wizard import Button
|
||||
from trytond.transaction import Transaction
|
||||
from sql import Table
|
||||
from decimal import getcontext, Decimal, ROUND_HALF_UP
|
||||
import requests
|
||||
import io
|
||||
import logging
|
||||
import json
|
||||
import re
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -17,6 +22,7 @@ class AutomationDocument(ModelSQL, ModelView, Workflow):
|
||||
('invoice', 'Invoice'),
|
||||
('statement_of_facts', 'Statement of Facts'),
|
||||
('weight_report', 'Weight Report'),
|
||||
('controller', 'Controller'),
|
||||
('bol', 'Bill of Lading'),
|
||||
('controller_invoice', 'Controller Invoice'),
|
||||
], 'Type')
|
||||
@@ -57,25 +63,53 @@ class AutomationDocument(ModelSQL, ModelView, Workflow):
|
||||
def run_ocr(cls, docs):
|
||||
for doc in docs:
|
||||
try:
|
||||
# Décoder le fichier depuis le champ Binary
|
||||
file_data = doc.document.data or b""
|
||||
logger.info(f"File size: {len(file_data)} bytes")
|
||||
logger.info(f"First 20 bytes: {file_data[:20]}")
|
||||
logger.info(f"Last 20 bytes: {file_data[-20:]}")
|
||||
if doc.type == 'weight_report':
|
||||
# Décoder le fichier depuis le champ Binary
|
||||
file_data = doc.document.data or b""
|
||||
logger.info(f"File size: {len(file_data)} bytes")
|
||||
logger.info(f"First 20 bytes: {file_data[:20]}")
|
||||
logger.info(f"Last 20 bytes: {file_data[-20:]}")
|
||||
|
||||
file_name = doc.document.name or "document"
|
||||
file_name = doc.document.name or "document"
|
||||
|
||||
# Envoyer le fichier au service OCR
|
||||
response = requests.post(
|
||||
"http://automation-service:8006/ocr",
|
||||
files={"file": (file_name, io.BytesIO(file_data))}
|
||||
)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
logger.info("RUN_OCR_RESPONSE:%s",data)
|
||||
doc.ocr_text = data.get("ocr_text", "")
|
||||
doc.state = "ocr_done"
|
||||
doc.notes = (doc.notes or "") + "OCR done\n"
|
||||
# Envoyer le fichier au service OCR
|
||||
response = requests.post(
|
||||
"http://automation-service:8006/ocr",
|
||||
files={"file": (file_name, io.BytesIO(file_data))}
|
||||
)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
logger.info("RUN_OCR_RESPONSE:%s",data)
|
||||
doc.ocr_text = data.get("ocr_text", "")
|
||||
doc.state = "ocr_done"
|
||||
doc.notes = (doc.notes or "") + "OCR done\n"
|
||||
else:
|
||||
doc.ocr_text = (doc.document.data or b"").decode('utf-8', errors='replace')
|
||||
match = re.search(r"\bID\s*:\s*(\d+)", doc.ocr_text)
|
||||
if match:
|
||||
request_id = match.group(1)
|
||||
match = re.search(r"\bBL\s*number\s*:\s*([A-Za-z0-9_-]+)", doc.ocr_text, re.IGNORECASE)
|
||||
if match:
|
||||
bl_number = match.group(1)
|
||||
ShipmentIn = Pool().get('stock.shipment.in')
|
||||
sh = ShipmentIn.search(['bl_number','=',bl_number])
|
||||
if sh:
|
||||
sh[0].returned_id = request_id
|
||||
ShipmentIn.save(sh)
|
||||
doc.notes = (doc.notes or "") + "Id returned: " + request_id
|
||||
|
||||
so_payload = {
|
||||
"ServiceOrderKey": sh[0].service_order_key,
|
||||
"ID_Number": request_id
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
"http://automation-service:8006/service-order-update",
|
||||
json=so_payload,
|
||||
timeout=10
|
||||
)
|
||||
response.raise_for_status()
|
||||
doc.notes = (doc.notes or "") + " SO updated"
|
||||
|
||||
except Exception as e:
|
||||
doc.state = "error"
|
||||
@@ -154,7 +188,8 @@ class AutomationDocument(ModelSQL, ModelView, Workflow):
|
||||
logger.info("Sending OCR text to metadata API: %s", doc.ocr_text)
|
||||
|
||||
response = requests.post(
|
||||
"http://automation-service:8006/metadata",
|
||||
#"http://automation-service:8006/metadata",
|
||||
"http://automation-service:8006/parse",
|
||||
json={"text": doc.ocr_text or ""}
|
||||
)
|
||||
response.raise_for_status()
|
||||
@@ -176,6 +211,18 @@ class AutomationDocument(ModelSQL, ModelView, Workflow):
|
||||
logger.error("Metadata processing error: %s", e)
|
||||
|
||||
doc.save()
|
||||
|
||||
def create_weight_report(self,wr_payload):
|
||||
|
||||
response = requests.post(
|
||||
"http://automation-service:8006/weight-report",
|
||||
json=wr_payload, # 👈 ICI la correction
|
||||
timeout=10
|
||||
)
|
||||
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
# -------------------------------------------------------
|
||||
# FULL PIPELINE
|
||||
# -------------------------------------------------------
|
||||
@@ -184,18 +231,47 @@ class AutomationDocument(ModelSQL, ModelView, Workflow):
|
||||
def run_pipeline(cls, docs):
|
||||
for doc in docs:
|
||||
try:
|
||||
if cls.rule_set.ocr_required:
|
||||
cls.run_ocr([doc])
|
||||
if cls.rule_set.structure_required and doc.state != "error":
|
||||
cls.run_structure([doc])
|
||||
if cls.rule_set.table_required and doc.state != "error":
|
||||
cls.run_tables([doc])
|
||||
if cls.rule_set.metadata_required and doc.state != "error":
|
||||
cls.run_metadata([doc])
|
||||
if doc.state != "error":
|
||||
doc.state = "validated"
|
||||
doc.notes = (doc.notes or "") + "Pipeline completed\n"
|
||||
logger.info("DATA_TYPE:%s",type(doc.metadata_json))
|
||||
metadata = json.loads(str(doc.metadata_json))
|
||||
logger.info("JSON STRUCTURE:%s",metadata)
|
||||
|
||||
WeightReport = Pool().get('weight.report')
|
||||
wr = WeightReport.create_from_json(metadata)
|
||||
|
||||
ShipmentIn = Pool().get('stock.shipment.in')
|
||||
ShipmentWR = Pool().get('shipment.wr')
|
||||
sh = ShipmentIn.search([('bl_number','ilike',wr.bl_no)])
|
||||
if sh:
|
||||
swr = ShipmentWR()
|
||||
swr.shipment_in = sh[0]
|
||||
swr.wr = wr
|
||||
ShipmentWR.save([swr])
|
||||
doc.notes = (doc.notes or "") + f"Shipment found: {sh[0].number}\n"
|
||||
logger.info("BL_NUMBER:%s",sh[0].bl_number)
|
||||
doc.notes = (
|
||||
(doc.notes or "")
|
||||
+ "Global WR linked to shipment. "
|
||||
+ "Create remote lot WRs from the weight report form.\n")
|
||||
|
||||
# if cls.rule_set.ocr_required:[]
|
||||
# cls.run_ocr([doc])
|
||||
# if cls.rule_set.structure_required and doc.state != "error":
|
||||
# cls.run_structure([doc])
|
||||
# if cls.rule_set.table_required and doc.state != "error":
|
||||
# cls.run_tables([doc])
|
||||
# if cls.rule_set.metadata_required and doc.state != "error":
|
||||
# cls.run_metadata([doc])
|
||||
# if doc.state != "error":
|
||||
# doc.state = "validated"
|
||||
# doc.notes = (doc.notes or "") + "Pipeline completed\n"
|
||||
except Exception as e:
|
||||
logger.exception("PIPELINE FAILED") # 👈 TRACE COMPLETE
|
||||
doc.state = "error"
|
||||
doc.notes = (doc.notes or "") + f"Pipeline error: {e}\n"
|
||||
doc.save()
|
||||
doc.save()
|
||||
raise
|
||||
|
||||
# except Exception as e:
|
||||
# doc.state = "error"
|
||||
# doc.notes = (doc.notes or "") + f"Pipeline error: {e}\n"
|
||||
doc.save()
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
<record model="ir.model.button" id="auto_button1">
|
||||
<field name="model">automation.document</field>
|
||||
<field name="name">run_pipeline</field>
|
||||
<field name="string">Run Full Pipeline</field>
|
||||
<field name="string">Create Weight Report</field>
|
||||
</record>
|
||||
<record model="ir.model.button" id="auto_button2">
|
||||
<field name="model">automation.document</field>
|
||||
|
||||
408
modules/automation/cron.py
Normal file
408
modules/automation/cron.py
Normal file
@@ -0,0 +1,408 @@
|
||||
import requests
|
||||
from decimal import getcontext, Decimal, ROUND_HALF_UP
|
||||
from datetime import datetime, timedelta
|
||||
from trytond.model import fields
|
||||
from trytond.model import ModelSQL, ModelView
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.transaction import Transaction
|
||||
import logging
|
||||
from sql import Table
|
||||
import traceback
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Cron(metaclass=PoolMeta):
|
||||
__name__ = 'ir.cron'
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls.method.selection.append(
|
||||
('automation.cron|update_shipment',
|
||||
"Update Shipment from freight booking info")
|
||||
)
|
||||
|
||||
|
||||
class AutomationCron(ModelSQL, ModelView):
|
||||
"Automation Cron"
|
||||
__name__ = 'automation.cron'
|
||||
|
||||
frequency = fields.Selection([
|
||||
('daily', "Daily"),
|
||||
('weekly', "Weekly"),
|
||||
('monthly', "Monthly"),
|
||||
], "Frequency", required=True,
|
||||
help="How frequently rates must be updated.")
|
||||
|
||||
last_update = fields.Date("Last Update", required=True)
|
||||
|
||||
@classmethod
|
||||
def run(cls, crons):
|
||||
cls.update_shipment()
|
||||
|
||||
@classmethod
|
||||
def update_shipment(cls):
|
||||
PoolObj = Pool()
|
||||
ShipmentIn = PoolObj.get('stock.shipment.in')
|
||||
Party = PoolObj.get('party.party')
|
||||
Vessel = PoolObj.get('trade.vessel')
|
||||
Location = PoolObj.get('stock.location')
|
||||
|
||||
# Table externe
|
||||
t = Table('freight_booking_info')
|
||||
cursor = Transaction().connection.cursor()
|
||||
cursor.execute(*t.select(
|
||||
t.ShippingInstructionNumber,
|
||||
t.ShippingInstructionDate,
|
||||
t.ShippingInstructionQuantity,
|
||||
t.ShippingInstructionQuantityUnit,
|
||||
t.NumberOfContainers,
|
||||
t.ContainerType,
|
||||
t.Loading,
|
||||
t.Destination,
|
||||
t.BookingAgent,
|
||||
t.Carrier,
|
||||
t.Vessel,
|
||||
t.BL_Number,
|
||||
t.ETD_Date,
|
||||
t.BL_Date,
|
||||
t.ExpectedController,
|
||||
t.Comments,
|
||||
t.FintradeBookingKey,
|
||||
))
|
||||
|
||||
rows = cursor.fetchall()
|
||||
logger.info(f"Nombre total de lignes a traiter : {len(rows)}")
|
||||
|
||||
# Premiere transaction : creation des objets de reference
|
||||
with Transaction().new_transaction() as trans1:
|
||||
try:
|
||||
logger.info(
|
||||
"Debut de la creation des objets de reference...")
|
||||
|
||||
parties_to_save = []
|
||||
vessels_to_save = []
|
||||
locations_to_save = []
|
||||
|
||||
parties_cache = {}
|
||||
vessels_cache = {}
|
||||
locations_cache = {}
|
||||
|
||||
# Collecter les donnees des objets de reference
|
||||
for row in rows:
|
||||
(
|
||||
si_number, si_date, si_quantity, si_unit,
|
||||
container_number, container_type,
|
||||
loading_name, destination_name,
|
||||
agent_name, carrier_name,
|
||||
vessel_name, bl_number,
|
||||
etd_date, bl_date, controller,
|
||||
comments, fintrade_booking_key
|
||||
) = row
|
||||
|
||||
def get_or_create_party(name):
|
||||
if not name:
|
||||
return None
|
||||
name_upper = str(name).strip().upper()
|
||||
if name_upper in parties_cache:
|
||||
return parties_cache[name_upper]
|
||||
|
||||
existing = Party.search(
|
||||
[('name', '=', name_upper)], limit=1)
|
||||
if existing:
|
||||
parties_cache[name_upper] = existing[0]
|
||||
return existing[0]
|
||||
|
||||
new_p = Party()
|
||||
new_p.name = name_upper
|
||||
parties_cache[name_upper] = new_p
|
||||
parties_to_save.append(new_p)
|
||||
return new_p
|
||||
|
||||
def get_or_create_vessel(name):
|
||||
if not name:
|
||||
return None
|
||||
name_upper = str(name).strip().upper()
|
||||
if name_upper in vessels_cache:
|
||||
return vessels_cache[name_upper]
|
||||
|
||||
existing = Vessel.search(
|
||||
[('vessel_name', '=', name_upper)], limit=1)
|
||||
if existing:
|
||||
vessels_cache[name_upper] = existing[0]
|
||||
return existing[0]
|
||||
|
||||
new_v = Vessel()
|
||||
new_v.vessel_name = name_upper
|
||||
vessels_cache[name_upper] = new_v
|
||||
vessels_to_save.append(new_v)
|
||||
return new_v
|
||||
|
||||
def get_or_create_location(name, type_):
|
||||
if not name:
|
||||
return None
|
||||
name_upper = str(name).strip().upper()
|
||||
key = f"{name_upper}_{type_}"
|
||||
if key in locations_cache:
|
||||
return locations_cache[key]
|
||||
|
||||
existing = Location.search([
|
||||
('name', '=', name_upper),
|
||||
('type', '=', type_),
|
||||
], limit=1)
|
||||
if existing:
|
||||
locations_cache[key] = existing[0]
|
||||
return existing[0]
|
||||
|
||||
new_loc = Location()
|
||||
new_loc.name = name_upper
|
||||
new_loc.type = type_
|
||||
locations_cache[key] = new_loc
|
||||
locations_to_save.append(new_loc)
|
||||
return new_loc
|
||||
|
||||
_ = get_or_create_party(carrier_name)
|
||||
_ = get_or_create_party(agent_name)
|
||||
_ = get_or_create_vessel(vessel_name)
|
||||
_ = get_or_create_location(loading_name, 'supplier')
|
||||
_ = get_or_create_location(destination_name, 'customer')
|
||||
|
||||
if parties_to_save:
|
||||
logger.info(f"Creation de {len(parties_to_save)} parties...")
|
||||
Party.save(parties_to_save)
|
||||
|
||||
if vessels_to_save:
|
||||
logger.info(f"Creation de {len(vessels_to_save)} vessels...")
|
||||
Vessel.save(vessels_to_save)
|
||||
|
||||
if locations_to_save:
|
||||
logger.info(
|
||||
f"Creation de {len(locations_to_save)} locations...")
|
||||
Location.save(locations_to_save)
|
||||
|
||||
trans1.commit()
|
||||
logger.info(
|
||||
"Premiere transaction commitee : objets de reference crees")
|
||||
|
||||
except Exception as e:
|
||||
trans1.rollback()
|
||||
logger.error(
|
||||
f"Erreur dans la creation des objets de reference : {e}")
|
||||
logger.error(traceback.format_exc())
|
||||
raise
|
||||
|
||||
# Transactions individuelles pour chaque shipment
|
||||
successful_shipments = 0
|
||||
failed_shipments = []
|
||||
|
||||
cursor2 = Transaction().connection.cursor()
|
||||
cursor2.execute(*t.select(
|
||||
t.ShippingInstructionNumber,
|
||||
t.ShippingInstructionDate,
|
||||
t.ShippingInstructionQuantity,
|
||||
t.ShippingInstructionQuantityUnit,
|
||||
t.NumberOfContainers,
|
||||
t.ContainerType,
|
||||
t.Loading,
|
||||
t.Destination,
|
||||
t.BookingAgent,
|
||||
t.Carrier,
|
||||
t.Vessel,
|
||||
t.BL_Number,
|
||||
t.ETD_Date,
|
||||
t.BL_Date,
|
||||
t.ExpectedController,
|
||||
t.Comments,
|
||||
t.FintradeBookingKey,
|
||||
))
|
||||
|
||||
rows2 = cursor2.fetchall()
|
||||
|
||||
for i, row in enumerate(rows2, 1):
|
||||
(
|
||||
si_number, si_date, si_quantity, si_unit,
|
||||
container_number, container_type,
|
||||
loading_name, destination_name,
|
||||
agent_name, carrier_name,
|
||||
vessel_name, bl_number,
|
||||
etd_date, bl_date, controller,
|
||||
comments, fintrade_booking_key
|
||||
) = row
|
||||
|
||||
logger.info(f"Traitement shipment {i}/{len(rows2)} : SI {si_number}")
|
||||
|
||||
try:
|
||||
with Transaction().new_transaction() as trans_shipment:
|
||||
logger.info(f"Debut transaction pour SI {si_number}")
|
||||
|
||||
existing_shipment = ShipmentIn.search([
|
||||
('reference', '=', si_number)
|
||||
], limit=1)
|
||||
|
||||
if existing_shipment:
|
||||
shipment = existing_shipment[0]
|
||||
if shipment.incoming_moves:
|
||||
logger.info(
|
||||
"Shipment %s existe deja avec lots, ignore",
|
||||
si_number)
|
||||
trans_shipment.commit()
|
||||
continue
|
||||
|
||||
logger.info(
|
||||
"Shipment %s existe deja sans lots, verification freight_booking_lots",
|
||||
si_number)
|
||||
inv_date, inv_nb = shipment._create_lots_from_fintrade()
|
||||
shipment = ShipmentIn(shipment.id)
|
||||
if shipment.incoming_moves:
|
||||
shipment.controller = shipment.get_controller()
|
||||
shipment.controller_target = controller
|
||||
if not shipment.fees:
|
||||
shipment.create_fee(shipment.controller)
|
||||
shipment.instructions = shipment.get_instructions_html(
|
||||
inv_date, inv_nb)
|
||||
ShipmentIn.save([shipment])
|
||||
logger.info(
|
||||
"Shipment %s mis a jour avec %s incoming move(s)",
|
||||
si_number, len(shipment.incoming_moves))
|
||||
else:
|
||||
logger.info(
|
||||
"Shipment %s existe sans lots et aucun lot disponible pour l'instant",
|
||||
si_number)
|
||||
trans_shipment.commit()
|
||||
continue
|
||||
|
||||
carrier = None
|
||||
if carrier_name:
|
||||
carrier_list = Party.search([
|
||||
('name', '=',
|
||||
str(carrier_name).strip().upper())
|
||||
], limit=1)
|
||||
if carrier_list:
|
||||
carrier = carrier_list[0]
|
||||
logger.info(
|
||||
f"Carrier trouve pour {si_number}: {carrier.name}")
|
||||
else:
|
||||
logger.warning(
|
||||
f"Carrier NON TROUVE pour {si_number}: '{carrier_name}'")
|
||||
|
||||
agent = None
|
||||
agent_list = Party.search([
|
||||
('name', '=', str(agent_name or 'TBN').strip().upper())
|
||||
], limit=1)
|
||||
if agent_list:
|
||||
agent = agent_list[0]
|
||||
|
||||
vessel = None
|
||||
if vessel_name:
|
||||
vessel_list = Vessel.search([
|
||||
('vessel_name', '=',
|
||||
str(vessel_name).strip().upper())
|
||||
], limit=1)
|
||||
if vessel_list:
|
||||
vessel = vessel_list[0]
|
||||
|
||||
loc_from = None
|
||||
if loading_name:
|
||||
loc_from_list = Location.search([
|
||||
('name', '=', str(loading_name).strip().upper()),
|
||||
('type', '=', 'supplier')
|
||||
], limit=1)
|
||||
if loc_from_list:
|
||||
loc_from = loc_from_list[0]
|
||||
|
||||
loc_to = None
|
||||
if destination_name:
|
||||
loc_to_list = Location.search([
|
||||
('name', '=', str(destination_name).strip().upper()),
|
||||
('type', '=', 'customer')
|
||||
], limit=1)
|
||||
if loc_to_list:
|
||||
loc_to = loc_to_list[0]
|
||||
|
||||
if not carrier:
|
||||
error_msg = (
|
||||
f"ERREUR CRITIQUE: Carrier manquant pour SI {si_number} "
|
||||
f"(valeur: '{carrier_name}')")
|
||||
logger.error(error_msg)
|
||||
raise ValueError(error_msg)
|
||||
|
||||
shipment = ShipmentIn()
|
||||
shipment.reference = si_number
|
||||
shipment.from_location = loc_from
|
||||
shipment.to_location = loc_to
|
||||
shipment.carrier = None # carrier
|
||||
shipment.supplier = agent
|
||||
shipment.agent = agent
|
||||
shipment.vessel = vessel
|
||||
shipment.cargo_mode = 'bulk'
|
||||
shipment.bl_number = bl_number
|
||||
shipment.bl_date = bl_date
|
||||
shipment.etd = etd_date
|
||||
shipment.etad = shipment.bl_date + timedelta(days=20)
|
||||
|
||||
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])
|
||||
trans_shipment.commit()
|
||||
successful_shipments += 1
|
||||
logger.info(f"Shipment {si_number} cree avec succes")
|
||||
|
||||
except Exception as e:
|
||||
error_details = {
|
||||
'si_number': si_number,
|
||||
'carrier_name': carrier_name,
|
||||
'error': str(e),
|
||||
'traceback': traceback.format_exc()
|
||||
}
|
||||
failed_shipments.append(error_details)
|
||||
|
||||
logger.error(f"ERREUR pour shipment {si_number}: {e}")
|
||||
logger.error(f" Carrier: '{carrier_name}'")
|
||||
logger.error(f" Agent: '{agent_name}'")
|
||||
logger.error(f" Vessel: '{vessel_name}'")
|
||||
logger.error(" Traceback complet:")
|
||||
for line in traceback.format_exc().split('\n'):
|
||||
if line.strip():
|
||||
logger.error(f" {line}")
|
||||
|
||||
logger.info("=" * 60)
|
||||
logger.info("RESUME DE L'EXECUTION")
|
||||
logger.info("=" * 60)
|
||||
logger.info(f"Total de shipments a traiter : {len(rows2)}")
|
||||
logger.info(f"Shipments crees avec succes : {successful_shipments}")
|
||||
logger.info(f"Shipments en echec : {len(failed_shipments)}")
|
||||
|
||||
if failed_shipments:
|
||||
logger.info("\nDetail des echecs :")
|
||||
for i, error in enumerate(failed_shipments, 1):
|
||||
logger.info(f" {i}. SI {error['si_number']}:")
|
||||
logger.info(f" Carrier: '{error['carrier_name']}'")
|
||||
logger.info(f" Erreur: {error['error']}")
|
||||
|
||||
logger.info("\nAnalyse des carriers problematiques :")
|
||||
problematic_carriers = {}
|
||||
for error in failed_shipments:
|
||||
carrier = error['carrier_name']
|
||||
if carrier in problematic_carriers:
|
||||
problematic_carriers[carrier] += 1
|
||||
else:
|
||||
problematic_carriers[carrier] = 1
|
||||
|
||||
for carrier, count in problematic_carriers.items():
|
||||
logger.info(f" Carrier '{carrier}' : {count} echec(s)")
|
||||
existing = Party.search([
|
||||
('name', '=', str(carrier).strip().upper())
|
||||
], limit=1)
|
||||
if existing:
|
||||
logger.info(
|
||||
f" -> EXISTE DANS LA BASE (ID: {existing[0].id})")
|
||||
else:
|
||||
logger.info(" -> N'EXISTE PAS DANS LA BASE")
|
||||
|
||||
logger.info("=" * 60)
|
||||
37
modules/automation/cron.xml
Normal file
37
modules/automation/cron.xml
Normal file
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0"?>
|
||||
<tryton>
|
||||
<data>
|
||||
<record model="ir.ui.view" id="cron_view_list">
|
||||
<field name="model">automation.cron</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">cron_list</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="cron_view_form">
|
||||
<field name="model">automation.cron</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">cron_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_cron_form">
|
||||
<field name="name">Update shipment from freight booking</field>
|
||||
<field name="res_model">automation.cron</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_cron_form_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="cron_view_list"/>
|
||||
<field name="act_window" ref="act_cron_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_cron_form_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="cron_view_form"/>
|
||||
<field name="act_window" ref="act_cron_form"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.cron" id="cron_cron">
|
||||
<field name="method">automation.cron|update_shipment</field>
|
||||
<field name="interval_number" eval="1"/>
|
||||
<field name="interval_type">days</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
72
modules/automation/execution_automation.py
Normal file
72
modules/automation/execution_automation.py
Normal file
@@ -0,0 +1,72 @@
|
||||
from trytond.model import ModelSQL, fields
|
||||
|
||||
class ExecutionFollowUp(ModelSQL):
|
||||
"Execution Follow Up"
|
||||
__name__ = 'execution.automation'
|
||||
|
||||
port_of_loading = fields.Char("Port of Loading")
|
||||
fb_loading = fields.Char("FB Loading")
|
||||
warehouse = fields.Char("Warehouse")
|
||||
origin = fields.Char("Origin")
|
||||
agent = fields.Char("Agent")
|
||||
operator = fields.Char("Operator")
|
||||
|
||||
fintrade_lc_nb = fields.Char("Fintrade LC Nb")
|
||||
lc_number = fields.Char("LC Number")
|
||||
si = fields.Char("SI")
|
||||
|
||||
port_of_destination = fields.Char("Port of Destination")
|
||||
fb_destination = fields.Char("FB Destination")
|
||||
status = fields.Char("Status")
|
||||
|
||||
etd_date = fields.Date("ETD Date")
|
||||
bl_date = fields.Date("BL Date")
|
||||
etd_sob = fields.Date("ETD SOB")
|
||||
|
||||
sale_contract_no = fields.Char("Sale Contract No")
|
||||
customer = fields.Char("Customer")
|
||||
elt_quantity = fields.Float("Elt Quantity")
|
||||
|
||||
number_of_container = fields.Integer("Containers")
|
||||
vessel = fields.Char("Vessel")
|
||||
shipping_company = fields.Char("Shipping Company")
|
||||
booking_ref = fields.Char("Booking Ref")
|
||||
freight_forwarder = fields.Char("Freight Forwarder")
|
||||
|
||||
instrument_status = fields.Char("Instrument Status")
|
||||
ip_date = fields.Date("IP Date")
|
||||
ip_status = fields.Char("IP Status")
|
||||
latest_shipment_date = fields.Date("Latest Shipment Date")
|
||||
|
||||
countersigned = fields.Boolean("Countersigned")
|
||||
comments = fields.Text("Comments")
|
||||
docs_internal_comments = fields.Text("Docs Internal Comments")
|
||||
|
||||
alloc_quantity = fields.Float("Allocated Quantity")
|
||||
si_comments = fields.Text("SI Comments")
|
||||
is_archived = fields.Boolean("Archived")
|
||||
|
||||
price_cont = fields.Numeric("Price / Cont")
|
||||
price_cont_curr = fields.Char("Currency")
|
||||
|
||||
ct_period_start = fields.Date("CT Start")
|
||||
ct_period_end = fields.Date("CT End")
|
||||
|
||||
lsd_check = fields.Char("LSD Check")
|
||||
bl2lsd_delta = fields.Integer("BL → LSD Delta")
|
||||
|
||||
fintrade_booking = fields.Char("Fintrade Booking")
|
||||
alloc_unit_price = fields.Numeric("Alloc Unit Price")
|
||||
alloc_price_curr = fields.Char("Alloc Price Curr")
|
||||
alloc_price_unit = fields.Char("Alloc Price Unit")
|
||||
|
||||
left_time = fields.Integer("Days Left")
|
||||
|
||||
@classmethod
|
||||
def table_query(cls):
|
||||
return (
|
||||
"SELECT "
|
||||
"row_number() OVER () AS id, "
|
||||
"* "
|
||||
"FROM singa_execution_follow_up"
|
||||
)
|
||||
9
modules/automation/execution_automation.xml
Normal file
9
modules/automation/execution_automation.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<tryton>
|
||||
<data>
|
||||
<record model="ir.ui.view" id="execution_followup_tree">
|
||||
<field name="model">execution.automation</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">execution_automation_tree</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
56
modules/automation/freight_booking.py
Normal file
56
modules/automation/freight_booking.py
Normal file
@@ -0,0 +1,56 @@
|
||||
from trytond.model import ModelSQL, ModelView, fields
|
||||
from sql import Table
|
||||
from sql.functions import CurrentTimestamp
|
||||
from sql import Column, Literal
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class FreightBookingInfo(ModelSQL, ModelView):
|
||||
"Freight Booking"
|
||||
__name__ = 'freight.booking.info'
|
||||
|
||||
booking_number = fields.Char("Booking Number")
|
||||
agent = fields.Char("Agent")
|
||||
controller = fields.Char("Customer")
|
||||
origin = fields.Char("Origin")
|
||||
destination = fields.Char("Destination")
|
||||
etd = fields.Date("ETD")
|
||||
bl_date = fields.Date("BL date")
|
||||
bl_number = fields.Char("BL Nb")
|
||||
carrier = fields.Char("Carrier")
|
||||
vessel = fields.Char("Vessel")
|
||||
container_count = fields.Float("Containers")
|
||||
quantity = fields.Float("Gross Weight")
|
||||
|
||||
@classmethod
|
||||
def table_query(cls):
|
||||
t = Table('freight_booking_info')
|
||||
|
||||
query = t.select(
|
||||
Literal(None).as_('create_uid'),
|
||||
CurrentTimestamp().as_('create_date'),
|
||||
Literal(None).as_('write_uid'),
|
||||
Literal(None).as_('write_date'),
|
||||
Column(t, 'FintradeBookingKey').as_('id'),
|
||||
Column(t, 'ShippingInstructionNumber').as_('booking_number'),
|
||||
Column(t, 'BookingAgent').as_('agent'),
|
||||
Column(t, 'ExpectedController').as_('controller'),
|
||||
Column(t, 'Loading').as_('origin'),
|
||||
Column(t, 'Destination').as_('destination'),
|
||||
Column(t, 'ETD_Date').as_('etd'),
|
||||
Column(t, 'BL_Date').as_('bl_date'),
|
||||
Column(t, 'BL_Number').as_('bl_number'),
|
||||
Column(t, 'Carrier').as_('carrier'),
|
||||
Column(t, 'Vessel').as_('vessel'),
|
||||
Column(t, 'NumberOfContainers').as_('container_count'),
|
||||
Column(t, 'ShippingInstructionQuantity').as_('quantity'),
|
||||
)
|
||||
#logger.info("*****QUERY*****:%s",query)
|
||||
return query
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls._order = [
|
||||
('etd', 'DESC'),
|
||||
]
|
||||
25
modules/automation/freight_booking.xml
Normal file
25
modules/automation/freight_booking.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<tryton>
|
||||
<data>
|
||||
<record model="ir.ui.view" id="freight_booking_info_tree">
|
||||
<field name="model">freight.booking.info</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">freight_booking_info_tree</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window" id="act_freight_booking_info">
|
||||
<field name="name">Freight Bookings</field>
|
||||
<field name="res_model">freight.booking.info</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_freight_booking_info_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="freight_booking_info_tree"/>
|
||||
<field name="act_window" ref="act_freight_booking_info"/>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
name="Freight Booking"
|
||||
action="act_freight_booking_info"
|
||||
parent="menu_automation"
|
||||
sequence="10"
|
||||
id="menu_freight_booking" />
|
||||
</data>
|
||||
</tryton>
|
||||
@@ -5,4 +5,6 @@ depends:
|
||||
res
|
||||
document_incoming
|
||||
xml:
|
||||
automation.xml
|
||||
automation.xml
|
||||
freight_booking.xml
|
||||
cron.xml
|
||||
60
modules/automation/view/execution_automation_tree.xml
Normal file
60
modules/automation/view/execution_automation_tree.xml
Normal file
@@ -0,0 +1,60 @@
|
||||
<tree>
|
||||
<field name="port_of_loading"/>
|
||||
<field name="fb_loading"/>
|
||||
<field name="warehouse"/>
|
||||
<field name="origin"/>
|
||||
<field name="agent"/>
|
||||
<field name="operator"/>
|
||||
|
||||
<field name="fintrade_lc_nb"/>
|
||||
<field name="lc_number"/>
|
||||
<field name="si"/>
|
||||
|
||||
<field name="port_of_destination"/>
|
||||
<field name="fb_destination"/>
|
||||
<field name="status"/>
|
||||
|
||||
<field name="etd_date"/>
|
||||
<field name="bl_date"/>
|
||||
<field name="etd_sob"/>
|
||||
|
||||
<field name="sale_contract_no"/>
|
||||
<field name="customer"/>
|
||||
<field name="elt_quantity"/>
|
||||
|
||||
<field name="number_of_container"/>
|
||||
<field name="vessel"/>
|
||||
<field name="shipping_company"/>
|
||||
<field name="booking_ref"/>
|
||||
<field name="freight_forwarder"/>
|
||||
|
||||
<field name="instrument_status"/>
|
||||
<field name="ip_date"/>
|
||||
<field name="ip_status"/>
|
||||
<field name="latest_shipment_date"/>
|
||||
|
||||
<field name="countersigned"/>
|
||||
<field name="comments"/>
|
||||
<field name="docs_internal_comments"/>
|
||||
|
||||
<field name="alloc_quantity"/>
|
||||
<field name="si_comments"/>
|
||||
<field name="is_archived"/>
|
||||
|
||||
<field name="price_cont"/>
|
||||
<field name="price_cont_curr"/>
|
||||
|
||||
<field name="ct_period_start"/>
|
||||
<field name="ct_period_end"/>
|
||||
|
||||
<field name="lsd_check"/>
|
||||
<field name="bl2lsd_delta"/>
|
||||
|
||||
<field name="fintrade_booking"/>
|
||||
<field name="alloc_unit_price"/>
|
||||
<field name="alloc_price_curr"/>
|
||||
<field name="alloc_price_unit"/>
|
||||
|
||||
<field name="left_time"/>
|
||||
|
||||
</tree>
|
||||
13
modules/automation/view/freight_booking_info_tree.xml
Normal file
13
modules/automation/view/freight_booking_info_tree.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<tree>
|
||||
<field name="booking_number"/>
|
||||
<field name="agent"/>
|
||||
<field name="controller"/>
|
||||
<field name="origin"/>
|
||||
<field name="destination"/>
|
||||
<field name="etd"/>
|
||||
<field name="bl_date"/>
|
||||
<field name="bl_number"/>
|
||||
<field name="carrier"/>
|
||||
<field name="vessel"/>
|
||||
<field name="container_count"/>
|
||||
</tree>
|
||||
@@ -2,9 +2,4 @@
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<data>
|
||||
<xpath expr="//page[@id='costs']" position="inside">
|
||||
<field name="before_carriages" colspan="4"/>
|
||||
|
||||
<field name="after_carriages" colspan="4"/>
|
||||
</xpath>
|
||||
</data>
|
||||
|
||||
@@ -27,6 +27,14 @@ class Sale(metaclass=PoolMeta):
|
||||
invoice.save()
|
||||
return invoice
|
||||
|
||||
@property
|
||||
def report_agent(self):
|
||||
if self.agent:
|
||||
return (self.agent.party.address_get(
|
||||
type='delivery')).full_address
|
||||
else:
|
||||
return ''
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('quotation')
|
||||
|
||||
@@ -47,6 +47,8 @@ class Company(ModelSQL, ModelView):
|
||||
help="Used to compute the today date.")
|
||||
employees = fields.One2Many('company.employee', 'company', 'Employees',
|
||||
help="Add employees to the company.")
|
||||
|
||||
logo = fields.Binary("Logo")
|
||||
|
||||
@property
|
||||
def header_used(self):
|
||||
|
||||
@@ -17,6 +17,8 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="header"/>
|
||||
<separator name="footer"/>
|
||||
<field name="footer"/>
|
||||
<separator name="logo"/>
|
||||
<field name="logo" widget="image" stretch="true"/>
|
||||
</page>
|
||||
</notebook>
|
||||
</form>
|
||||
|
||||
@@ -137,6 +137,13 @@ class Currency(
|
||||
closer = date
|
||||
return res
|
||||
|
||||
@classmethod
|
||||
def get_by_name(cls, name):
|
||||
currencies = cls.search([('symbol', '=', name)], limit=1)
|
||||
if not currencies:
|
||||
return None
|
||||
return currencies[0]
|
||||
|
||||
@staticmethod
|
||||
def _get_rate(currencies, tdate=None):
|
||||
'''
|
||||
|
||||
@@ -17,6 +17,8 @@ from trytond.transaction import Transaction
|
||||
from trytond.wizard import Button, StateTransition, StateView, Wizard
|
||||
|
||||
from .exceptions import DocumentIncomingSplitError
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
if config.getboolean('document_incoming', 'filestore', default=True):
|
||||
file_id = 'file_id'
|
||||
@@ -179,30 +181,112 @@ class Incoming(DeactivableMixin, Workflow, ModelSQL, ModelView):
|
||||
def _split_mime_types(cls):
|
||||
return ['application/pdf']
|
||||
|
||||
# @classmethod
|
||||
# def from_inbound_email(cls, email_, rule):
|
||||
# message = email_.as_dict()
|
||||
# attachments = message.get('attachments')
|
||||
# active = False
|
||||
# data = message.get('text', message.get('html'))
|
||||
# logger.info("DATA_FROM_INBOUND_MAIL:%s",data)
|
||||
# if isinstance(data, str):
|
||||
# data = data.encode()
|
||||
# body = message.get('text') or message.get('html') or ''
|
||||
# if isinstance(body, str):
|
||||
# body_bytes = body.encode('utf-8')
|
||||
# else:
|
||||
# body_bytes = body
|
||||
# document = cls(
|
||||
# active=active,
|
||||
# name=message.get('subject', 'No Subject'),
|
||||
# company=rule.document_incoming_company,
|
||||
# data=data,
|
||||
# type=rule.document_incoming_type if active else None,
|
||||
# source='inbound_email',
|
||||
# )
|
||||
# children = []
|
||||
# if attachments:
|
||||
# for attachment in attachments:
|
||||
# child = cls(
|
||||
# name=attachment['filename'] or 'data.bin',
|
||||
# company=rule.document_incoming_company,
|
||||
# data=attachment['data'],
|
||||
# type=rule.document_incoming_type,
|
||||
# source='inbound_email')
|
||||
# children.append(child)
|
||||
# else:
|
||||
# child = cls(
|
||||
# name='mail_' + message.get('subject', 'No Subject') + '.txt',
|
||||
# company=rule.document_incoming_company,
|
||||
# data=body_bytes,
|
||||
# type=rule.document_incoming_type,
|
||||
# source='inbound_email',
|
||||
# )
|
||||
# children.append(child)
|
||||
# document.children = children
|
||||
# document.save()
|
||||
# return document
|
||||
@classmethod
|
||||
def from_inbound_email(cls, email_, rule):
|
||||
message = email_.as_dict()
|
||||
active = not message.get('attachments')
|
||||
|
||||
def clean(value):
|
||||
if not value:
|
||||
return value
|
||||
return (
|
||||
value
|
||||
.replace('\n', ' ')
|
||||
.replace('\r', ' ')
|
||||
.replace("'", '')
|
||||
.replace('"', '')
|
||||
.strip()
|
||||
)
|
||||
|
||||
subject = clean(message.get('subject', 'No Subject'))
|
||||
|
||||
attachments = message.get('attachments')
|
||||
active = False
|
||||
data = message.get('text', message.get('html'))
|
||||
logger.info("DATA_FROM_INBOUND_MAIL:%s", data)
|
||||
|
||||
if isinstance(data, str):
|
||||
data = data.encode()
|
||||
|
||||
body = message.get('text') or message.get('html') or ''
|
||||
if isinstance(body, str):
|
||||
body_bytes = body.encode('utf-8')
|
||||
else:
|
||||
body_bytes = body
|
||||
|
||||
document = cls(
|
||||
active=active,
|
||||
name=message.get('subject', 'No Subject'),
|
||||
name=subject,
|
||||
company=rule.document_incoming_company,
|
||||
data=data,
|
||||
type=rule.document_incoming_type if active else None,
|
||||
source='inbound_email',
|
||||
)
|
||||
)
|
||||
|
||||
children = []
|
||||
for attachment in message.get('attachments', []):
|
||||
if attachments:
|
||||
for attachment in attachments:
|
||||
filename = clean(attachment['filename'] or 'data.bin')
|
||||
child = cls(
|
||||
name=filename,
|
||||
company=rule.document_incoming_company,
|
||||
data=attachment['data'],
|
||||
type=rule.document_incoming_type,
|
||||
source='inbound_email')
|
||||
children.append(child)
|
||||
else:
|
||||
child = cls(
|
||||
name=attachment['filename'] or 'data.bin',
|
||||
name='mail_' + subject + '.txt',
|
||||
company=rule.document_incoming_company,
|
||||
data=attachment['data'],
|
||||
data=body_bytes,
|
||||
type=rule.document_incoming_type,
|
||||
source='inbound_email')
|
||||
source='inbound_email',
|
||||
)
|
||||
children.append(child)
|
||||
|
||||
document.children = children
|
||||
document.save()
|
||||
return document
|
||||
@@ -265,7 +349,6 @@ class Incoming(DeactivableMixin, Workflow, ModelSQL, ModelView):
|
||||
default.setdefault('children')
|
||||
return super().copy(documents, default=default)
|
||||
|
||||
|
||||
def iter_pages(expression, size):
|
||||
ranges = set()
|
||||
for pages in expression.split(','):
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
from trytond.model import fields
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pyson import Eval
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class Rule(metaclass=PoolMeta):
|
||||
__name__ = 'inbound.email.rule'
|
||||
@@ -53,4 +54,4 @@ class Rule(metaclass=PoolMeta):
|
||||
if (self.action == 'document.incoming|from_inbound_email'
|
||||
and self.document_incoming_process):
|
||||
document = email_.result
|
||||
DocumentIncoming.process([document], with_children=True)
|
||||
DocumentIncoming.process([document], with_children=True)
|
||||
15
modules/document_incoming_wr/__init__.py
Normal file
15
modules/document_incoming_wr/__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 document
|
||||
|
||||
__all__ = ['register']
|
||||
|
||||
|
||||
def register():
|
||||
Pool.register(
|
||||
document.IncomingConfiguration,
|
||||
document.Incoming,
|
||||
module='document_incoming_wr', type_='model')
|
||||
90
modules/document_incoming_wr/document.py
Normal file
90
modules/document_incoming_wr/document.py
Normal file
@@ -0,0 +1,90 @@
|
||||
# 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.i18n import gettext
|
||||
from trytond.model import fields
|
||||
from trytond.modules.document_incoming.exceptions import (
|
||||
DocumentIncomingProcessError)
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
import json
|
||||
|
||||
|
||||
class IncomingConfiguration(metaclass=PoolMeta):
|
||||
__name__ = 'document.incoming.configuration'
|
||||
|
||||
default_controller = fields.Many2One('party.party', "Default Controller")
|
||||
|
||||
|
||||
class Incoming(metaclass=PoolMeta):
|
||||
__name__ = 'document.incoming'
|
||||
|
||||
created_at = fields.Function(
|
||||
fields.Date("Created At"),
|
||||
'get_created_at')
|
||||
result_notes = fields.Function(
|
||||
fields.Text("Result Notes"),
|
||||
'get_result_notes')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls.type.selection.append(
|
||||
('weight_report', "Weight Report"))
|
||||
cls.type.selection.append(
|
||||
('controller', "Controller"))
|
||||
cls._order = [('create_date', 'DESC')]
|
||||
|
||||
@classmethod
|
||||
def _get_results(cls):
|
||||
return super()._get_results() | {'automation.document'}
|
||||
|
||||
def _process_weight_report(self):
|
||||
WR = Pool().get('automation.document')
|
||||
wr = WR()
|
||||
wr.document = self.id
|
||||
wr.type = 'weight_report'
|
||||
wr.state = 'draft'
|
||||
WR.save([wr])
|
||||
WR.run_ocr([wr])
|
||||
WR.run_metadata([wr])
|
||||
WR.run_pipeline([wr])
|
||||
return wr
|
||||
|
||||
def _process_controller(self):
|
||||
WR = Pool().get('automation.document')
|
||||
wr = WR()
|
||||
wr.document = self.id
|
||||
wr.type = 'controller'
|
||||
wr.state = 'draft'
|
||||
WR.save([wr])
|
||||
WR.run_ocr([wr])
|
||||
# WR.run_metadata([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
|
||||
# def supplier_invoice_company(self):
|
||||
# pass
|
||||
|
||||
# @property
|
||||
# def supplier_invoice_party(self):
|
||||
# pass
|
||||
17
modules/document_incoming_wr/document.xml
Normal file
17
modules/document_incoming_wr/document.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?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="ddocument_incoming_configuration_view_form">
|
||||
<field name="model">document.incoming.configuration</field>
|
||||
<field name="inherit" ref="document_incoming.document_incoming_configuration_view_form"/>
|
||||
<field name="name">document_incoming_configuration_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="document_incoming_view_list_wr">
|
||||
<field name="model">document.incoming</field>
|
||||
<field name="inherit" ref="document_incoming.document_incoming_view_list"/>
|
||||
<field name="name">document_incoming_list</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
10
modules/document_incoming_wr/tryton.cfg
Normal file
10
modules/document_incoming_wr/tryton.cfg
Normal file
@@ -0,0 +1,10 @@
|
||||
[tryton]
|
||||
version=7.2.0
|
||||
depends:
|
||||
document_incoming
|
||||
automation
|
||||
ir
|
||||
party
|
||||
xml:
|
||||
document.xml
|
||||
view/document_incoming_list.xml
|
||||
@@ -0,0 +1,10 @@
|
||||
<?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="/form" position="inside">
|
||||
<separator string="Weight Report" id="weight_report" colspan="4"/>
|
||||
<label name="default_controller"/>
|
||||
<field name="default_controller"/>
|
||||
</xpath>
|
||||
</data>
|
||||
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>
|
||||
@@ -17,6 +17,9 @@ from trytond.pyson import Eval
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.url import http_host
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
if config.getboolean('inbound_email', 'filestore', default=True):
|
||||
file_id = 'data_id'
|
||||
store_prefix = config.get('inbound_email', 'store_prefix', default=None)
|
||||
@@ -74,6 +77,7 @@ class Inbox(ModelSQL, ModelView):
|
||||
assert email_.inbox == self
|
||||
for rule in self.rules:
|
||||
if rule.match(email_.as_dict()):
|
||||
logger.info("RULE_MATCHED:%s",rule)
|
||||
email_.rule = rule
|
||||
rule.run(email_)
|
||||
return
|
||||
|
||||
@@ -12,7 +12,7 @@ __all__ = ['IncotermMixin', 'IncotermAvailableMixin']
|
||||
class IncotermMixin(Model):
|
||||
|
||||
incoterm = fields.Many2One(
|
||||
'incoterm.incoterm', lazy_gettext('incoterm.msg_incoterm'),
|
||||
'incoterm.incoterm', lazy_gettext('incoterm.msg_incoterm'), required=False,
|
||||
ondelete='RESTRICT')
|
||||
incoterm_location = fields.Many2One(
|
||||
'party.address', lazy_gettext('incoterm.msg_incoterm_location'),
|
||||
|
||||
@@ -24,8 +24,14 @@ class Lot(ModelSQL, ModelView):
|
||||
|
||||
lot_name = fields.Char("Lot")
|
||||
number = fields.Char("Number", readonly=True)
|
||||
lot_qt = fields.Float("Quantity",required=False)
|
||||
lot_unit = fields.Many2One('product.uom', "Unit",required=False)
|
||||
lot_qt = fields.Float("Quantity", required=False,
|
||||
states={
|
||||
'readonly': Eval('lot_type') == 'virtual',
|
||||
})
|
||||
lot_unit = fields.Many2One('product.uom', "Unit", required=False,
|
||||
states={
|
||||
'readonly': Eval('lot_type') == 'virtual',
|
||||
})
|
||||
lot_product = fields.Many2One('product.product', "Product")
|
||||
lot_type = fields.Selection([
|
||||
('virtual', 'Open'),
|
||||
@@ -477,15 +483,16 @@ class Lot(ModelSQL, ModelView):
|
||||
else:
|
||||
return str(self.line.currency.symbol) + "/" + str(self.line.unit.symbol)
|
||||
|
||||
def get_hist_quantity(self,seq):
|
||||
def get_hist_quantity(self,state_id=0):
|
||||
qt = Decimal(0)
|
||||
gross_qt = Decimal(0)
|
||||
if self.lot_state:
|
||||
if self.lot_hist:
|
||||
if seq != 0:
|
||||
st = seq
|
||||
if state_id != 0:
|
||||
st = state_id
|
||||
else:
|
||||
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]
|
||||
qt = round(lot.quantity,5)
|
||||
gross_qt = round(lot.gross_quantity,5)
|
||||
@@ -499,24 +506,48 @@ class Lot(ModelSQL, ModelView):
|
||||
physic_sum = Decimal(0)
|
||||
for l in line.lots:
|
||||
if l.lot_type == 'physic' :
|
||||
physic_sum += round(Decimal(Uom.compute_qty(Uom(l.lot_unit_line),float(l.get_current_quantity()),l.line.unit)),5)
|
||||
factor = None
|
||||
rate = None
|
||||
if l.lot_unit_line.category.id != l.line.unit.category.id:
|
||||
factor = 1
|
||||
rate = 1
|
||||
physic_sum += round(Decimal(Uom.compute_qty(Uom(l.lot_unit_line),float(l.get_current_quantity()),l.line.unit, True, factor, rate)),5)
|
||||
return line.quantity_theorical - physic_sum
|
||||
|
||||
def get_current_quantity(self,name=None):
|
||||
# if self.lot_type == 'physic':
|
||||
qt, gross_qt = self.get_hist_quantity(0)
|
||||
qt, gross_qt = self.get_hist_quantity()
|
||||
return qt
|
||||
# else:
|
||||
# return self.get_virtual_diff()
|
||||
|
||||
def get_current_quantity_converted(self,name=None):
|
||||
def get_current_quantity_converted(self,state_id=0,unit=None):
|
||||
Uom = Pool().get('product.uom')
|
||||
unit = self.line.unit if self.line else self.sale_line.unit
|
||||
return round(Decimal(Uom.compute_qty(self.lot_unit_line, float(self.get_current_quantity()), unit)),5)
|
||||
if not unit:
|
||||
unit = self.line.unit if self.line else self.sale_line.unit
|
||||
qt, gross_qt = self.get_hist_quantity(state_id)
|
||||
factor = None
|
||||
rate = None
|
||||
if self.lot_unit_line.category.id != unit.category.id:
|
||||
factor = 1
|
||||
rate = 1
|
||||
return round(Decimal(Uom.compute_qty(self.lot_unit_line, float(qt), unit, True, factor, rate)),5)
|
||||
|
||||
def get_current_gross_quantity_converted(self,state_id=0,unit=None):
|
||||
Uom = Pool().get('product.uom')
|
||||
if not unit:
|
||||
unit = self.line.unit if self.line else self.sale_line.unit
|
||||
qt, gross_qt = self.get_hist_quantity(state_id)
|
||||
factor = None
|
||||
rate = None
|
||||
if self.lot_unit_line.category.id != unit.category.id:
|
||||
factor = 1
|
||||
rate = 1
|
||||
return round(Decimal(Uom.compute_qty(self.lot_unit_line, float(gross_qt), unit, True, factor, rate)),5)
|
||||
|
||||
def get_current_gross_quantity(self,name=None):
|
||||
if self.lot_type == 'physic':
|
||||
qt, gross_qt = self.get_hist_quantity(0)
|
||||
qt, gross_qt = self.get_hist_quantity()
|
||||
return gross_qt
|
||||
else:
|
||||
return None
|
||||
@@ -526,6 +557,7 @@ class Lot(ModelSQL, ModelView):
|
||||
lqh = LotQtHist()
|
||||
lqh.quantity_type = qt_type
|
||||
lqh.quantity = net
|
||||
logger.info("ADD_QUANTITY_TO_HIST:%s",gross)
|
||||
lqh.gross_quantity = gross
|
||||
lqh.lot = self
|
||||
return lqh
|
||||
@@ -542,6 +574,7 @@ class Lot(ModelSQL, ModelView):
|
||||
if existing:
|
||||
hist = existing[0]
|
||||
hist.quantity = net
|
||||
logger.info("SET_CURRENT_HIST:%s",gross)
|
||||
hist.gross_quantity = gross
|
||||
else:
|
||||
lot_hist.append(self.add_quantity_to_hist(net, gross, lqtt[0]))
|
||||
@@ -633,6 +666,7 @@ class SplitLine(ModelView):
|
||||
weight = fields.Numeric('Weight', digits=(16,5))
|
||||
|
||||
class SplitWizardStart(ModelView):
|
||||
"Split Line Start"
|
||||
__name__ = 'lot.split.wizard.start'
|
||||
|
||||
mode = fields.Selection([
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<notebook colspan="4">
|
||||
<page string="General" id="general">
|
||||
<field name="lot_childs" colspan="4" mode="tree,form" view_ids="lot.lot_view_tree_sequence2,lot.lot_view_form"/>
|
||||
<field name="lot_hist" colspan="4"/>
|
||||
<field name="lot_hist" colspan="4" readonly="1"/>
|
||||
</page>
|
||||
<page string="Accounting" id="accounting">
|
||||
<field name="pivot" widget="html_viewer" height="600" colspan="4"/>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0"?>
|
||||
<form col="4">
|
||||
<label name="lot"/>
|
||||
<field name="lot"/>
|
||||
<field name="lot" readonly="1"/>
|
||||
<label name="quantity_type"/>
|
||||
<field name="quantity_type"/>
|
||||
<field name="quantity_type" readonly="1"/>
|
||||
<label name="quantity"/>
|
||||
<field name="quantity"/>
|
||||
<field name="quantity" readonly="1"/>
|
||||
<label name="gross_quantity"/>
|
||||
<field name="gross_quantity"/>
|
||||
<field name="gross_quantity" readonly="1"/>
|
||||
</form>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<tree editable="1">
|
||||
<tree>
|
||||
<field name="lot"/>
|
||||
<field name="quantity_type"/>
|
||||
<field name="quantity"/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<tree editable="1">
|
||||
<tree>
|
||||
<field name="lot"/>
|
||||
<field name="quantity_type"/>
|
||||
<field name="quantity"/>
|
||||
|
||||
@@ -44,7 +44,7 @@ class Price(
|
||||
price_composite = fields.One2Many('price.composite','price',"Composites")
|
||||
price_product = fields.One2Many('price.product', 'price', "Product")
|
||||
price_ct_size = fields.Numeric("Ct size")
|
||||
|
||||
|
||||
def get_qt(self,nb_ct,unit):
|
||||
Uom = Pool().get('product.uom')
|
||||
return round(Decimal(Uom.compute_qty(self.price_unit, float(self.price_ct_size * nb_ct), unit)),4)
|
||||
@@ -68,18 +68,22 @@ class Price(
|
||||
amount = Decimal(self.get_price_per_qt(price,unit,currency)) * Decimal(Uom.compute_qty(self.price_unit, float(self.price_ct_size * nb_ct), unit))
|
||||
return round(amount,4)
|
||||
|
||||
def get_price(self,dt,unit,currency,last=False):
|
||||
price = float(0)
|
||||
PV = Pool().get('price.price_value')
|
||||
logger.info("ASKED_PRICE_FOR:%s",dt)
|
||||
if self.price_values:
|
||||
dt = dt.strftime("%Y-%m-%d")
|
||||
pv = PV.search([('price','=',self.id),('price_date','=',dt)])
|
||||
if not pv and last:
|
||||
pv = PV.search([('price','=',self.id)],order=[('price_date', 'DESC')])
|
||||
if pv:
|
||||
price = self.get_price_per_qt(pv[0].price_value,unit,currency)
|
||||
return round(price,4)
|
||||
def get_price(self,dt,unit,currency,last=False, relative_last=False):
|
||||
price = float(0)
|
||||
PV = Pool().get('price.price_value')
|
||||
if self.price_values:
|
||||
dt = dt.strftime("%Y-%m-%d")
|
||||
pv = PV.search([('price','=',self.id),('price_date','=',dt)])
|
||||
if not pv and last:
|
||||
pv = PV.search([('price','=',self.id)],order=[('price_date', 'DESC')])
|
||||
elif not pv and relative_last:
|
||||
pv = PV.search([
|
||||
('price','=',self.id),
|
||||
('price_date','<=',dt),
|
||||
], order=[('price_date', 'DESC')])
|
||||
if pv:
|
||||
price = self.get_price_per_qt(pv[0].price_value,unit,currency)
|
||||
return round(price,4)
|
||||
|
||||
class FixType(ModelSQL,ModelView):
|
||||
"Fixation type"
|
||||
@@ -115,7 +119,6 @@ class Calendar(DeactivableMixin,ModelSQL,ModelView,MultiValueMixin):
|
||||
dt = dt.strftime("%Y-%m-%d")
|
||||
cl = CL.search([('calendar','=',self.id),('price_date','=',dt)])
|
||||
if cl:
|
||||
#logger.info("ISQUOTE:%s",cl)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
@@ -136,3 +139,20 @@ class Product(ModelSQL,ModelView):
|
||||
__name__ = 'price.product'
|
||||
price = fields.Many2One('price.price',"Price index")
|
||||
product = fields.Many2One('product.product',"Product")
|
||||
attributes = fields.Many2One('product.attribute',"Attribute",domain=[
|
||||
('sets', '=', Eval('attribute_set')),
|
||||
],
|
||||
states={
|
||||
'readonly': ~Eval('attribute_set'),
|
||||
},
|
||||
depends=['product', 'attribute_set'])
|
||||
attribute_set = fields.Function(
|
||||
fields.Many2One('product.attribute.set', "Attribute Set"),
|
||||
'on_change_with_attribute_set'
|
||||
)
|
||||
|
||||
@fields.depends('product')
|
||||
def on_change_with_attribute_set(self, name=None):
|
||||
if self.product and self.product.template and self.product.template.attribute_set:
|
||||
return self.product.template.attribute_set.id
|
||||
|
||||
|
||||
@@ -35,10 +35,11 @@ class PriceValue(
|
||||
}), 'get_price_index')
|
||||
|
||||
price_date = fields.Date("Price date")
|
||||
price_value = fields.Float("Price value")
|
||||
open_price = fields.Float("Open price")
|
||||
low_price = fields.Float("Low price")
|
||||
high_price = fields.Float("High price")
|
||||
price_value = fields.Float("Price value")
|
||||
open_price = fields.Float("Open price")
|
||||
low_price = fields.Float("Low price")
|
||||
mid_price = fields.Float("Mid price")
|
||||
high_price = fields.Float("High price")
|
||||
|
||||
def get_price_index(self, name):
|
||||
if self.price:
|
||||
@@ -55,9 +56,10 @@ class PriceValueReport(
|
||||
r_price_desc = fields.Char("Description")
|
||||
r_price_date = fields.Date("Price date")
|
||||
r_price_value = fields.Float("Price")
|
||||
r_open_price = fields.Float("Open price")
|
||||
r_low_price = fields.Float("Low price")
|
||||
r_high_price = fields.Float("High price")
|
||||
r_open_price = fields.Float("Open price")
|
||||
r_low_price = fields.Float("Low price")
|
||||
r_mid_price = fields.Float("Mid price")
|
||||
r_high_price = fields.Float("High price")
|
||||
|
||||
@classmethod
|
||||
def table_query(cls):
|
||||
@@ -86,10 +88,11 @@ class PriceValueReport(
|
||||
pp.price_desc.as_('r_price_desc'),
|
||||
pv.price_date.as_('r_price_date'),
|
||||
pv.price_value.as_('r_price_value'),
|
||||
pv.open_price.as_('r_open_price'),
|
||||
pv.low_price.as_('r_low_price'),
|
||||
pv.high_price.as_('r_high_price'),
|
||||
where=wh)
|
||||
pv.open_price.as_('r_open_price'),
|
||||
pv.low_price.as_('r_low_price'),
|
||||
pv.mid_price.as_('r_mid_price'),
|
||||
pv.high_price.as_('r_high_price'),
|
||||
where=wh)
|
||||
|
||||
return query
|
||||
|
||||
@@ -116,9 +119,10 @@ class PriceValueReport2(
|
||||
r_price_desc = fields.Char("Description")
|
||||
r_price_date = fields.Date("Price date")
|
||||
r_price_value = fields.Float("Price")
|
||||
r_open_price = fields.Float("Open price")
|
||||
r_low_price = fields.Float("Low price")
|
||||
r_high_price = fields.Float("High price")
|
||||
r_open_price = fields.Float("Open price")
|
||||
r_low_price = fields.Float("Low price")
|
||||
r_mid_price = fields.Float("Mid price")
|
||||
r_high_price = fields.Float("High price")
|
||||
r_naphta_fob_low = fields.Float("Naph F Med l", digits=(1,2), help="Naphta Fob PAAAI00 low")
|
||||
r_naphta_fob_high = fields.Float("Naph F Med h", digits=(1,2), help="Naphta Fob PAAAI00 high")
|
||||
r_naphta_fob_mid = fields.Float("Naph F Med m", digits=(1,2), help="Naphta Fob PAAAI00 mid")
|
||||
@@ -171,9 +175,10 @@ class PriceValueReport2(
|
||||
Max(pp.price_desc).as_('r_price_desc'),
|
||||
pv.price_date.as_('r_price_date'),
|
||||
Max(pv.price_value).as_('r_price_value'),
|
||||
Max(pv.open_price).as_('r_open_price'),
|
||||
Max(pv.low_price).as_('r_low_price'),
|
||||
Max(pv.high_price).as_('r_high_price'),
|
||||
Max(pv.open_price).as_('r_open_price'),
|
||||
Max(pv.low_price).as_('r_low_price'),
|
||||
Max(pv.mid_price).as_('r_mid_price'),
|
||||
Max(pv.high_price).as_('r_high_price'),
|
||||
Max(Case((pv.price==15, pv.low_price),else_=0)).as_('r_naphta_fob_low'),
|
||||
Max(Case((pv.price==15, pv.high_price),else_=0)).as_('r_naphta_fob_high'),
|
||||
Max(Case((pv.price==15, (pv.high_price-pv.low_price)/2 + pv.low_price),else_=0)).as_('r_naphta_fob_mid'),
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
<tree>
|
||||
<field name="r_price_index"/>
|
||||
<field name="r_price_desc"/>
|
||||
<field name="r_price_date"/>
|
||||
<field name="r_price_value"/>
|
||||
<field name="r_low_price"/>
|
||||
<field name="r_high_price"/>
|
||||
</tree>
|
||||
<field name="r_price_date"/>
|
||||
<field name="r_price_value"/>
|
||||
<field name="r_low_price"/>
|
||||
<field name="r_mid_price"/>
|
||||
<field name="r_high_price"/>
|
||||
</tree>
|
||||
|
||||
@@ -8,10 +8,12 @@
|
||||
<label name="price_value"/>
|
||||
<field name="price_value"/>
|
||||
<label name="open_price"/>
|
||||
<field name="open_price"/>
|
||||
<label name="low_price"/>
|
||||
<field name="low_price"/>
|
||||
<label name="high_price"/>
|
||||
<field name="high_price"/>
|
||||
<field name="open_price"/>
|
||||
<label name="low_price"/>
|
||||
<field name="low_price"/>
|
||||
<label name="mid_price"/>
|
||||
<field name="mid_price"/>
|
||||
<label name="high_price"/>
|
||||
<field name="high_price"/>
|
||||
</group>
|
||||
</form>
|
||||
|
||||
@@ -9,6 +9,7 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="price_value" fill="1" empty="0"/>
|
||||
<field name="open_price" fill="1" empty="0"/>
|
||||
<field name="low_price" fill="1" empty="0"/>
|
||||
<field name="mid_price" fill="1" empty="0"/>
|
||||
<field name="high_price" fill="1" empty="0"/>
|
||||
</y>
|
||||
</graph>
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
<tree>
|
||||
<field name="price_index"/>
|
||||
<field name="price_date"/>
|
||||
<field name="price_value"/>
|
||||
<field name="open_price"/>
|
||||
<field name="low_price"/>
|
||||
<field name="high_price"/>
|
||||
</tree>
|
||||
<field name="price_value"/>
|
||||
<field name="open_price"/>
|
||||
<field name="low_price"/>
|
||||
<field name="mid_price"/>
|
||||
<field name="high_price"/>
|
||||
</tree>
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
<tree editable="1">
|
||||
<field name="price_index"/>
|
||||
<field name="price_date"/>
|
||||
<field name="price_value"/>
|
||||
<field name="open_price"/>
|
||||
<field name="low_price"/>
|
||||
<field name="high_price"/>
|
||||
</tree>
|
||||
<field name="price_value"/>
|
||||
<field name="open_price"/>
|
||||
<field name="low_price"/>
|
||||
<field name="mid_price"/>
|
||||
<field name="high_price"/>
|
||||
</tree>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<form>
|
||||
<label name="price"/>
|
||||
<field name="price"/>
|
||||
<label name="product"/>
|
||||
<field name="product"/>
|
||||
<label name="attributes"/>
|
||||
<field name="attributes"/>
|
||||
</form>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<tree>
|
||||
<field name="price"/>
|
||||
<field name="product"/>
|
||||
<field name="attributes"/>
|
||||
</tree>
|
||||
|
||||
@@ -609,6 +609,26 @@ class Product(
|
||||
('template.code', operator, code_value, *extra),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def get_by_name(cls, name, type_='goods'):
|
||||
pool = Pool()
|
||||
Template = pool.get('product.template')
|
||||
Uom = pool.get('product.uom')
|
||||
|
||||
templates = Template.search([('name', '=', name)], limit=1)
|
||||
if templates:
|
||||
return templates[0].products[0]
|
||||
|
||||
unit_uom, = Uom.search([('name', '=', 'Mt')], limit=1)
|
||||
|
||||
template, = Template.create([{
|
||||
'name': name,
|
||||
'type': type_,
|
||||
'default_uom': unit_uom.id,
|
||||
'cost_price_method': 'fixed',
|
||||
}])
|
||||
return template.products[0]
|
||||
|
||||
@staticmethod
|
||||
def get_price_uom(products, name):
|
||||
Uom = Pool().get('product.uom')
|
||||
|
||||
@@ -92,6 +92,13 @@ class Uom(SymbolMixin, DigitsMixin, DeactivableMixin, ModelSQL, ModelView):
|
||||
def default_digits():
|
||||
return 2
|
||||
|
||||
@classmethod
|
||||
def get_by_name(cls, name):
|
||||
uom = cls.search([('symbol', '=', name)], limit=1)
|
||||
if not uom:
|
||||
return None
|
||||
return uom[0]
|
||||
|
||||
@fields.depends('factor')
|
||||
def on_change_factor(self):
|
||||
if (self.factor or 0.0) == 0.0:
|
||||
|
||||
@@ -22,6 +22,7 @@ class Month(ModelView, ModelSQL):
|
||||
is_cotation = fields.Boolean("Cotation month")
|
||||
beg_date = fields.Date("Date from")
|
||||
end_date = fields.Date("Date end")
|
||||
description = fields.Char("Description")
|
||||
|
||||
class ProductMonth(ModelView, ModelSQL):
|
||||
"Product month"
|
||||
|
||||
105
modules/purchase/AGENTS.md
Normal file
105
modules/purchase/AGENTS.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# AGENTS.md - Module `purchase`
|
||||
|
||||
Ce guide complete le `AGENTS.md` racine.
|
||||
Pour ce module, les regles locales ci-dessous priment.
|
||||
|
||||
## 1) Perimetre metier
|
||||
|
||||
Le module `purchase` gere le cycle d'achat fournisseur:
|
||||
|
||||
- commande d'achat (`purchase.purchase`, `purchase.line`)
|
||||
- facturation fournisseur (`account.invoice` liee a l'achat)
|
||||
- reception/retour de stock (`stock.move`, `stock.shipment.in`, `stock.shipment.in.return`)
|
||||
- reporting achats (axes temporels, fournisseur, produit)
|
||||
|
||||
## 2) Fichiers pivots
|
||||
|
||||
- Logique coeur:
|
||||
- `modules/purchase/purchase.py`
|
||||
- Extensions metier connexes:
|
||||
- `modules/purchase/product.py`
|
||||
- `modules/purchase/stock.py`
|
||||
- `modules/purchase/invoice.py`
|
||||
- `modules/purchase/party.py`
|
||||
- `modules/purchase/configuration.py`
|
||||
- `modules/purchase/purchase_reporting.py`
|
||||
- Vues et actions:
|
||||
- `modules/purchase/purchase.xml`
|
||||
- `modules/purchase/stock.xml`
|
||||
- `modules/purchase/invoice.xml`
|
||||
- `modules/purchase/purchase_reporting.xml`
|
||||
- Manifest et dependances:
|
||||
- `modules/purchase/tryton.cfg`
|
||||
- Documentation metier:
|
||||
- `modules/purchase/docs/business-rules.template.md` (template)
|
||||
- `modules/purchase/docs/business-rules.md` (instance a remplir)
|
||||
|
||||
## 3) Etats et flux critiques a preserver
|
||||
|
||||
Workflow de commande (dans `purchase.py`):
|
||||
|
||||
- `draft -> quotation -> confirmed -> processing -> done`
|
||||
- transitions de retour existent aussi (`cancelled`, retour a `draft`, etc.)
|
||||
|
||||
Invariants importants:
|
||||
|
||||
- `invoice_state` et `shipment_state` doivent rester coherents apres `process()`.
|
||||
- `process()` orchestre facture + stock + recalcul d'etats, ne pas contourner sans raison.
|
||||
- `delete()` exige une commande annulee.
|
||||
- Les methodes `create_invoice()` et `create_move()` sont sensibles (gestion `lots` et `action`).
|
||||
|
||||
## 4) Couplages a surveiller
|
||||
|
||||
- Facture:
|
||||
- `purchase.py` <-> `invoice.py`
|
||||
- gestion des exceptions facture (`purchase_exception_state`)
|
||||
- Stock:
|
||||
- `purchase.py` <-> `stock.py`
|
||||
- liens `moves`, expeditions entrantes, retours
|
||||
- Produit/fournisseur/prix:
|
||||
- `product.py` impacte prix d'achat, UoM, fournisseurs
|
||||
- Tiers:
|
||||
- `party.py` impacte adresses/parametres fournisseur et contraintes d'effacement
|
||||
|
||||
## 5) Convention de modification pour ce module
|
||||
|
||||
1. Modifier d'abord le coeur minimal dans `purchase.py` ou le fichier specialise adequat.
|
||||
2. Mettre a jour XML uniquement si comportement UI/action change.
|
||||
3. Si regle metier impactee, mettre a jour `docs/business-rules.md`.
|
||||
4. Ajouter un test proche du flux reel (scenario `.rst` prioritaire si possible).
|
||||
5. Verifier les impacts transverses facture/stock avant rendu.
|
||||
|
||||
## 6) Strategie de test recommandee
|
||||
|
||||
Priorite 1 (rapide):
|
||||
|
||||
- `modules/purchase/tests/test_module.py`
|
||||
|
||||
Priorite 2 (comportement metier):
|
||||
|
||||
- `modules/purchase/tests/test_scenario.py`
|
||||
- Scenarios cibles selon la modif:
|
||||
- `scenario_purchase.rst`
|
||||
- `scenario_purchase_manual_invoice.rst`
|
||||
- `scenario_purchase_line_cancelled.rst`
|
||||
- `scenario_purchase_line_cancelled_on_shipment.rst`
|
||||
- `scenario_purchase_return_wizard.rst`
|
||||
- `scenario_purchase_reporting.rst`
|
||||
|
||||
Si la modif touche prix/UoM/fournisseur:
|
||||
|
||||
- ajouter un cas dans `test_module.py` ou un scenario dedie.
|
||||
|
||||
## 7) Cas qui exigent validation humaine
|
||||
|
||||
- Changement du workflow d'etats
|
||||
- Changement des regles de creation facture/mouvement
|
||||
- Changement de logique sur retours fournisseur
|
||||
- Changement qui altere les ecritures comptables ou le statut de paiement
|
||||
|
||||
## 8) Definition of done (module `purchase`)
|
||||
|
||||
- Le flux metier cible fonctionne de bout en bout.
|
||||
- Les etats `state`, `invoice_state`, `shipment_state` restent coherents.
|
||||
- Les tests du module pertinents passent.
|
||||
- Le patch est limite aux fichiers necessaires.
|
||||
122
modules/purchase/docs/business-rules.template.md
Normal file
122
modules/purchase/docs/business-rules.template.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# Business Rules Template - Purchase
|
||||
|
||||
Statut: `draft` | `reviewed` | `approved`
|
||||
Version: `v0.1`
|
||||
Derniere mise a jour: `YYYY-MM-DD`
|
||||
Owner metier: `Nom / Equipe`
|
||||
Owner technique: `Nom / Equipe`
|
||||
|
||||
## 1) Scope
|
||||
|
||||
- Domaine: `ex: achats fournisseur`
|
||||
- Hors scope: `ex: achats intercompany`
|
||||
- Modules impactes:
|
||||
- `purchase`
|
||||
- `stock` (si applicable)
|
||||
- `account_invoice` (si applicable)
|
||||
|
||||
## 2) Glossaire
|
||||
|
||||
- `Purchase`: commande d'achat fournisseur.
|
||||
- `Line`: ligne de commande.
|
||||
- `Invoice State`: etat facture calcule.
|
||||
- `Shipment State`: etat reception calcule.
|
||||
- Ajouter ici les termes metier propres a ton contexte.
|
||||
|
||||
## 3) Regles metier (source de verite)
|
||||
|
||||
### BR-001 - [Titre court]
|
||||
|
||||
- Intent: `Pourquoi cette regle existe`
|
||||
- Description:
|
||||
- `Enonce clair et testable`
|
||||
- Conditions d'entree:
|
||||
- `Etat`
|
||||
- `Type de ligne (goods/service)`
|
||||
- `Contexte (societe, devise, fournisseur, lot, etc.)`
|
||||
- Resultat attendu:
|
||||
- `Etat/valeur/action attendue`
|
||||
- Exceptions:
|
||||
- `Cas ou la regle ne s'applique pas`
|
||||
- Priorite:
|
||||
- `bloquante | importante | informative`
|
||||
- Source:
|
||||
- `Ticket / spec / decision metier`
|
||||
|
||||
### BR-002 - [Titre court]
|
||||
|
||||
- Intent:
|
||||
- Description:
|
||||
- Conditions d'entree:
|
||||
- Resultat attendu:
|
||||
- Exceptions:
|
||||
- Priorite:
|
||||
- Source:
|
||||
|
||||
## 4) Matrice d'etats (optionnel mais recommande)
|
||||
|
||||
| Regle | Etat initial | Evenement | Etat attendu | Notes |
|
||||
|---|---|---|---|---|
|
||||
| BR-001 | `draft` | `quote` | `quotation` | |
|
||||
| BR-002 | `quotation` | `confirm` | `confirmed/processing` | |
|
||||
|
||||
## 5) Exemples concrets
|
||||
|
||||
### Exemple E1 - Cas nominal
|
||||
|
||||
- Donnees:
|
||||
- `fournisseur = X`
|
||||
- `produit = Y`
|
||||
- `quantite = 10`
|
||||
- Attendu:
|
||||
- `invoice_state = pending`
|
||||
- `shipment_state = waiting`
|
||||
|
||||
### Exemple E2 - Cas limite
|
||||
|
||||
- Donnees:
|
||||
- Attendu:
|
||||
|
||||
## 6) Impact code attendu
|
||||
|
||||
- Fichiers Python potentiellement concernes:
|
||||
- `modules/purchase/purchase.py`
|
||||
- `modules/purchase/stock.py`
|
||||
- `modules/purchase/invoice.py`
|
||||
- `modules/purchase/product.py`
|
||||
- Fichiers XML potentiellement concernes:
|
||||
- `modules/purchase/purchase.xml`
|
||||
- `modules/purchase/stock.xml`
|
||||
- `modules/purchase/invoice.xml`
|
||||
|
||||
## 7) Strategie de tests
|
||||
|
||||
- Unitaires:
|
||||
- `modules/purchase/tests/test_module.py`
|
||||
- Scenarios:
|
||||
- `modules/purchase/tests/scenario_purchase.rst`
|
||||
- `modules/purchase/tests/scenario_purchase_manual_invoice.rst`
|
||||
- `modules/purchase/tests/scenario_purchase_return_wizard.rst`
|
||||
|
||||
Pour chaque regle BR-xxx, lister le test associe:
|
||||
|
||||
| Regle | Test existant | Nouveau test a ajouter | Statut |
|
||||
|---|---|---|---|
|
||||
| BR-001 | `...` | `...` | `todo` |
|
||||
|
||||
## 8) Compatibilite et migration
|
||||
|
||||
- Effet retroactif sur commandes existantes: `oui/non`
|
||||
- Migration necessaire: `oui/non`
|
||||
- Plan de rollback:
|
||||
- `comment revenir en arriere sans corruption metier`
|
||||
|
||||
## 9) Validation
|
||||
|
||||
- Valide par metier:
|
||||
- `Nom` - `date`
|
||||
- Valide par technique:
|
||||
- `Nom` - `date`
|
||||
- Decision finale:
|
||||
- `approved / rejected / needs update`
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user