Compare commits
899 Commits
om_account
...
19.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 1ed351afcb | |||
| 37d902bbf1 | |||
| 4620f1f15d | |||
| 0919e928a2 | |||
| 6ccd6f551d | |||
| d9dbe67b8c | |||
| 00ecdb1eee | |||
| a32918ffab | |||
| 599cc43b87 | |||
| 3e951f4586 | |||
| 179eb40651 | |||
| 1ea0f65bad | |||
| 37a5973caa | |||
| 3bfd304755 | |||
| 422687b588 | |||
| e8760f7d0d | |||
| d7f5f98734 | |||
| e224f85ce5 | |||
| 9793d30a0b | |||
| 7ee8c79c11 | |||
| c5b1f87197 | |||
| 6d4b838700 | |||
| 32ebb495b2 | |||
| 1b0b3816fd | |||
| 461a147883 | |||
| 96a99e98a6 | |||
| 3f1e67983a | |||
| 480a145045 | |||
| 4d6d6e3a0b | |||
| 561595705c | |||
| 6fe848fe50 | |||
| 340836e114 | |||
| 8550a2c003 | |||
| b9cb1e1d6c | |||
| 058d098354 | |||
| 05e11743bc | |||
| 7bd3c34d30 | |||
| 365e38e9bd | |||
| da1339981b | |||
| 21e3832c7a | |||
| cdf57466e0 | |||
| 1dd7159ebf | |||
| a7aac0a6fc | |||
| 46ddc4e83d | |||
| f598856a85 | |||
| e4fab2bcd0 | |||
| 2b9c28b17d | |||
| aa240a5294 | |||
| 3a3f69f769 | |||
| e1550843f2 | |||
| 6e9bd26cc2 | |||
| 0274e8118a | |||
| fbee040fbe | |||
| cc39e82b47 | |||
| cc173aa77f | |||
| 49fd715c87 | |||
| be38f82271 | |||
| 3d6d13e7f6 | |||
| 057fe4b3d5 | |||
| e4b13e539e | |||
| 995b9e65ac | |||
| bc01210496 | |||
| cab4aebd03 | |||
| 7ecc317e64 | |||
| 30d46fbca0 | |||
| 9b2180deef | |||
| 9ad33e290f | |||
| 787efca2b6 | |||
| 16ced6a395 | |||
| 773c3cdd6e | |||
| 4629806e3e | |||
| 2129a6d505 | |||
| bf6096bf45 | |||
| e8bf47a6a9 | |||
| 7eb70e2623 | |||
| 89253bd00a | |||
| b55ae4fe9d | |||
| bb68ff0cc5 | |||
| 66add92264 | |||
| 2588c66508 | |||
| 5b4a3aab11 | |||
| 88118d9448 | |||
| d389ec4902 | |||
| 81bc19c9ca | |||
| 5859333ac1 | |||
| 403db72dc7 | |||
| 1e058fdb9a | |||
| 8c015d75be | |||
| 1ffc9fe53f | |||
| ea982c7dc5 | |||
| d6922f99a3 | |||
| a04bf4be8b | |||
| af2de3d58f | |||
| 485d66cb19 | |||
| 18e3ab1723 | |||
| 9abef0c065 | |||
| 8aff2d5df6 | |||
| 926071995c | |||
| 69fc239de1 | |||
| 08bfaa13cf | |||
| 43d06b9ead | |||
| 100f596f31 | |||
| 6b1e846f86 | |||
| 2a3bd8b770 | |||
| cad4fdef9e | |||
| f25a7de844 | |||
| da3727481e | |||
| 44c80d21ea | |||
| 0f815cd793 | |||
| 962f5792a3 | |||
| 9527852970 | |||
| b5aef11d8e | |||
| 90e3b774fe | |||
| d12ac3d874 | |||
| f5593b775d | |||
| 64268fad89 | |||
| 9e9b83e383 | |||
| b0959026a5 | |||
| c94d4b2692 | |||
| feb7eac8d0 | |||
| deb912fe74 | |||
| f48188cdc2 | |||
| ef2a582cbb | |||
| efa56c6aee | |||
| 3b45b08cd1 | |||
| 8ad2d11c45 | |||
| 35b29fa27c | |||
| 8f09fa4305 | |||
| b5b245876c | |||
| b16b052934 | |||
| b069c94742 | |||
| 03e8bb9e34 | |||
| bf629028b6 | |||
| 3395d3b27d | |||
| e14357c691 | |||
| 5b071517d5 | |||
| 83906f95f7 | |||
| 164d5e4e27 | |||
| 1575a75869 | |||
| 963b0d0dfa | |||
| f65e31fabc | |||
| d0bc1094b5 | |||
| e927eea9d2 | |||
| db23098f86 | |||
| 0acea56f70 | |||
| 3b1e6031ed | |||
| e600110516 | |||
| 52330d8399 | |||
| b0cc42047c | |||
| e94654c113 | |||
| 5ed65f685d | |||
| a997f2fa4b | |||
| 7ff298551b | |||
| 83dc76f9da | |||
| 603a8f1c72 | |||
| 2618f5bc15 | |||
| 53ed74871d | |||
| 1c2bbaf08e | |||
| e1001bc697 | |||
| 5e517adbef | |||
| 823f4a4078 | |||
| 40caeba39e | |||
| ee4a4afa94 | |||
| c7fc09061d | |||
| 3292953c6e | |||
| 0c6035f883 | |||
| d5dec2ce97 | |||
| db7810ce15 | |||
| b2657e9c92 | |||
| 48483a2e25 | |||
| cc2215e75f | |||
| baf34a96a9 | |||
| c412cb62b4 | |||
| 58abc5c6df | |||
| 0b69dbf4cc | |||
| 0c44c5f90d | |||
| c1886be4a6 | |||
| 832c6c58e0 | |||
| b918b2d54b | |||
| 731f8ef60c | |||
| 23c946dee2 | |||
| 4b70c0b1b8 | |||
| a80a309f6b | |||
| 76b546e0d2 | |||
| e12cbe8a1d | |||
| 53f96e1c12 | |||
| c6fcc52911 | |||
| 001f689a99 | |||
| dd63749c5f | |||
| d0a6a9ba6f | |||
| 3d9224af3c | |||
| 2547699573 | |||
| 12d9818809 | |||
| 019c26cbd2 | |||
| a2abda6b07 | |||
| 649d3a13dd | |||
| 96e8abf5de | |||
| 2989ad5a81 | |||
| dc5a6616a4 | |||
| b1e930eafb | |||
| c1521a6372 | |||
| 269beaf624 | |||
| 13e5e7cde8 | |||
| 4bbcae399e | |||
| 14899e48d3 | |||
| 0e5a2af28e | |||
| 83277217f9 | |||
| 4d226ec086 | |||
| 2d41262428 | |||
| 8dfd64f53a | |||
| ea73d93e8a | |||
| 1e4e023c1c | |||
| 41ecea74c6 | |||
| ac2ff0476b | |||
| 147c1e5050 | |||
| 2a6c7e3a87 | |||
| 5c2ae7b2c4 | |||
| 0fed636461 | |||
| d1772985ab | |||
| 697315c47f | |||
| ae802507a3 | |||
| cb69646f9f | |||
| 91a454851c | |||
| 7eb66dd5a4 | |||
| 67db32090b | |||
| 45091483a3 | |||
| b4f0e672b3 | |||
| 0603a3d6af | |||
| 0e403e40f7 | |||
| 8e857c5d16 | |||
| 27f1d35c41 | |||
| 25ab8eb3a5 | |||
| 5e11c33d38 | |||
| 8c450b3275 | |||
| fc8f57dc1e | |||
| 3eba6d66aa | |||
| 848aae224c | |||
| 5133ee78c9 | |||
| 083852cb99 | |||
| 53a7b6a318 | |||
| ae3a204c7e | |||
| 431b209881 | |||
| 2e2bbff9b0 | |||
| c666b76c2a | |||
| 92f51cc251 | |||
| a91d88866f | |||
| f55d2cb472 | |||
| 720276e095 | |||
| 5e3ad11d2a | |||
| 219f706143 | |||
| 9ca4e55cc9 | |||
| 254fb1d885 | |||
| c78fc717af | |||
| 2512b32b28 | |||
| b11ad8e295 | |||
| e9d4d08962 | |||
| 3f9f298838 | |||
| fe351faefe | |||
| e3aa3d8990 | |||
| 5253193596 | |||
| 3001e7c0b2 | |||
| ec436f6e9c | |||
| 7872aa608b | |||
| da801c6eaf | |||
| 2ebda56c3f | |||
| a4b2164f7c | |||
| 58bf5a2def | |||
| 7c33f0b44f | |||
| bdde4082d7 | |||
| 1b90a3e867 | |||
| d524c5e1d4 | |||
| f01f577792 | |||
| 27ce1c54a5 | |||
| 128525d282 | |||
| 32003002ac | |||
| 94e116c52d | |||
| c1ba536d03 | |||
| 9b7541ccc3 | |||
| 5f3f5baf37 | |||
| 8d12539329 | |||
| f5719810a4 | |||
| db0cb50deb | |||
| 2b65de6758 | |||
| 030be683b1 | |||
| 14d5ad7f29 | |||
| 531ad91474 | |||
| d4a490c757 | |||
| 9f15d65490 | |||
| a2a74e8744 | |||
| c21e46ddeb | |||
| da07d2e445 | |||
| 6dc553fde9 | |||
| f99799a954 | |||
| 9e23e1317c | |||
| 32d774fcd5 | |||
| e207f08083 | |||
| 47a23fed67 | |||
| c1a763bddc | |||
| 7d30801033 | |||
| 84e8400cb4 | |||
| 3e481ba6ab | |||
| e33e0077a1 | |||
| 75ba8a98cb | |||
| 0366582790 | |||
| f689083840 | |||
| 48703f32da | |||
| 2de2a5d904 | |||
| a785bff7bc | |||
| 545a23229d | |||
| 1a354517a3 | |||
| d99608dfe0 | |||
| 7fd8ff1230 | |||
| e4f3261e8a | |||
| f76ece3c19 | |||
| 61be87a6a0 | |||
| 32ef4a1135 | |||
| 105f6a4b1d | |||
| 76487f4782 | |||
| d854dde695 | |||
| 0d2ab5ef0f | |||
| eef53a3aaf | |||
| 5cd06f5379 | |||
| ba653ff7da | |||
| 8910346aa4 | |||
| d004eb3298 | |||
| 953d506294 | |||
| 2d1a474823 | |||
| 4aaf162b07 | |||
| 55919440aa | |||
| def3919e36 | |||
| 23729f436a | |||
| a498382b27 | |||
| ef2464809f | |||
| c5bc923362 | |||
| b921e79306 | |||
| 7d31a8c951 | |||
| cbd4217d7d | |||
| a9a25dbf23 | |||
| e9d49c9a58 | |||
| 0682aecf37 | |||
| 2637250db2 | |||
| d33628aebd | |||
| a80add2939 | |||
| 41de310ee1 | |||
| a6bebfd99c | |||
| 76e4d29876 | |||
| 8ade4a6e05 | |||
| a6f421a104 | |||
| 8aa7c35368 | |||
| 6c8caf18a8 | |||
| 66986764f8 | |||
| fa4f2d6714 | |||
| 8f445713f2 | |||
| 40fb8f2fa6 | |||
| 33d3610337 | |||
| 569ed5fa45 | |||
| ffb2695771 | |||
| 0b2dad4a11 | |||
| f2c0f1dbf1 | |||
| 1f4d827be0 | |||
| 77237f7463 | |||
| 275e79a729 | |||
| 589c24b30c | |||
| 9f7dc4f18b | |||
| 7243cebeb7 | |||
| 968080a32f | |||
| 711ae46103 | |||
| a2bbff1742 | |||
| 2cfc0fbb9e | |||
| 0f5db71fcc | |||
| ab3cdca6ac | |||
| b6b317cf39 | |||
| a572280a1b | |||
| 0bba7d8660 | |||
| b83c259bc9 | |||
| 275005173e | |||
| e894d52fb3 | |||
| d93f8decd8 | |||
| d3b0c242da | |||
| b198163958 | |||
| a947ad7169 | |||
| fb945d1f13 | |||
| b70d7ffd84 | |||
| 799550985c | |||
| 0507e609d0 | |||
| 6e6c57734f | |||
| 3a8001bbf5 | |||
| c5531c9622 | |||
| 5fc1508fbb | |||
| 5b2176f740 | |||
| 8e0f4bedc1 | |||
| d0a6262774 | |||
| 50c3b59309 | |||
| 382b052b20 | |||
| a942abb5d8 | |||
| 8d8980a351 | |||
| 9147825a27 | |||
| a5bfe360bc | |||
| 9b0de5b906 | |||
| 0a961e60f2 | |||
| b56eef73af | |||
| c01bf9817e | |||
| aad8160ddb | |||
| 01ad9545ef | |||
| 891226fb3a | |||
| 51df143937 | |||
| 4bd584cc1c | |||
| 2983906b42 | |||
| 9b503f6da4 | |||
| 3be92de4da | |||
| f2b145fbe3 | |||
| b441195cbf | |||
| c680ca48b9 | |||
| 3f3f72f1b2 | |||
| d24265e1f7 | |||
| a1debc4b51 | |||
| 3f87848fb0 | |||
| 6a5d7bd5c9 | |||
| 3e5e94610b | |||
| 8fef2569a6 | |||
| b67e36219b | |||
| 8b9bc53b4b | |||
| 7f0c5bb6d6 | |||
| a624a78e89 | |||
| 2d007bddd6 | |||
| 3b0fd1a653 | |||
| 6dfec1ece8 | |||
| f2f9f1356e | |||
| 8013a8466b | |||
| 1ea2132ae1 | |||
| f625656478 | |||
| 97c12ce152 | |||
| 8f4d684054 | |||
| 7345062f82 | |||
| 745334a16a | |||
| b2ede24091 | |||
| 1c8f09b64c | |||
| c1c520274f | |||
| d000fead4c | |||
| 0de0a6d5aa | |||
| 0e872ab117 | |||
| 528f191d56 | |||
| ce4ee639d3 | |||
| f810014cb2 | |||
| 9d51ac3252 | |||
| a2028e5a97 | |||
| e9cadbe823 | |||
| 3f799e5d6f | |||
| 8722d4b1b2 | |||
| e653e447c6 | |||
| 88c2a9003a | |||
| 739c87dd5e | |||
| 046eb43f56 | |||
| 8c17569d52 | |||
| 0f995e80a9 | |||
| 09696b9c10 | |||
| b6362d895d | |||
| fc6d7d9b29 | |||
| 4bc1d5939e | |||
| fb1ec4b879 | |||
| d3a3fa3c77 | |||
| 72e36fbf76 | |||
| 558ed2306c | |||
| 0e8fbce3ea | |||
| b0cecf4b66 | |||
| 314f12f0d4 | |||
| df0d005724 | |||
| a487adf187 | |||
| 5e40d1c63a | |||
| 304c5172e6 | |||
| 23b7b1e80b | |||
| 5bbab6fab5 | |||
| 1585c66a2d | |||
| f57d262d62 | |||
| 69c4b974ef | |||
| 214433437e | |||
| de13dc5909 | |||
| ad34c1d6eb | |||
| ac1f5c0c8f | |||
| 475273a71b | |||
| 2f525cb95e | |||
| 9874bd4e21 | |||
| 0212b26047 | |||
| 20da29605b | |||
| 034eab9244 | |||
| 60fc5bcdda | |||
| 6a80f28c52 | |||
| 13bb0df929 | |||
| 04b4210da2 | |||
| 7d03e32f51 | |||
| 4fdbdaedc9 | |||
| 1cc392bfb2 | |||
| c856fed428 | |||
| c4a447738e | |||
| d3b296b587 | |||
| c678877b59 | |||
| 104f73b7d2 | |||
| 135ae7fcfe | |||
| 9d502807fb | |||
| c3581e1b69 | |||
| 3ce9111fa7 | |||
| 516e985bd0 | |||
| 46c0d054a6 | |||
| 392984c5e8 | |||
| 2bc536263e | |||
| 957fb6f312 | |||
| 4dbea36b0f | |||
| 3c67472a8c | |||
| f6e22cc063 | |||
| 9b4f874307 | |||
| a054dc878a | |||
| 48e48ecf03 | |||
| 471c451c21 | |||
| a88793a6ad | |||
| 3e50cea188 | |||
| 4da0dfbd51 | |||
| da5eab2f1f | |||
| 78e1187722 | |||
| bfda8e2e56 | |||
| 5014234817 | |||
| a185c34d65 | |||
| 8fc4975a9e | |||
| 7414a836e7 | |||
| 122f31f4b6 | |||
| b59d8cee90 | |||
| 1db27642af | |||
| 6f5be6eae9 | |||
| 2a7103cdd3 | |||
| af722d6184 | |||
| 2bf0695e9d | |||
| faf1b4fff1 | |||
| bd59526535 | |||
| f404e0ef31 | |||
| a11b547cdc | |||
| 1507d10fe5 | |||
| 84cc63a038 | |||
| cbb5244320 | |||
| 809e33bd93 | |||
| b8f6c82010 | |||
| 4be3583e7b | |||
| de10442135 | |||
| 44ec4115ea | |||
| eb27193b6e | |||
| cead209507 | |||
| f2122a7706 | |||
| 88f970547f | |||
| fc7bfaa089 | |||
| 234aee88e2 | |||
| 33a761413c | |||
| db76be28e3 | |||
| 62a64bcadc | |||
| 3d69d5b814 | |||
| 850d9bb6c5 | |||
| 89f02eeda5 | |||
| cba2ad4052 | |||
| f566b64a4e | |||
| 9e5070e4c7 | |||
| d95c52e27a | |||
| 051e01b1d5 | |||
| 4261ec5ed0 | |||
| 98bc5f8027 | |||
| ccdc19f576 | |||
| 84e6416ca1 | |||
| 368561c08a | |||
| 3de397f595 | |||
| 700d367666 | |||
| a384996942 | |||
| 81f0c9411c | |||
| cbdea4a66a | |||
| 752c1ebe59 | |||
| f77ab77c0b | |||
| 71d223259b | |||
| a69b413c5b | |||
| daafc7c705 | |||
| 52b130631c | |||
| 01594ba32b | |||
| 9015195fb9 | |||
| 6d07bc4c06 | |||
| acf12b508b | |||
| b7a2c4ea2f | |||
| 4399a06c37 | |||
| 5ee5d4f5cd | |||
| 256c5679c7 | |||
| 45bfc361f7 | |||
| 0ecbc9213c | |||
| 63dc434824 | |||
| 3e1a94eaed | |||
| 68ce996d7b | |||
| 89d5846a15 | |||
| 7f76c2d7e7 | |||
| 1326d21dfa | |||
| b37e8a0f7a | |||
| 8f629981ce | |||
| beace16323 | |||
| e19464e2a8 | |||
| 008ca2b8a7 | |||
| 6e56dc2437 | |||
| a09d856ddb | |||
| 9a69aa7709 | |||
| 0254cac7fc | |||
| bc629b414c | |||
| 8f69ab2665 | |||
| 01514c86c9 | |||
| ca1f963db1 | |||
| 5fd44a1ae0 | |||
| 284e2939f5 | |||
| 70d43d34ca | |||
| 8425e51ca4 | |||
| 4994547440 | |||
| 2fbf03f179 | |||
| 2e1a1fbe03 | |||
| dd5c59c645 | |||
| 3e88c5783b | |||
| 0a63809482 | |||
| 743b002387 | |||
| 79fe584847 | |||
| 63d6a65f65 | |||
| d20f9916f3 | |||
| 285397f44b | |||
| 5d7a1983f9 | |||
| 8cb7ce7d65 | |||
| 2b78518c30 | |||
| b6be57e58f | |||
| e225ff2780 | |||
| b98f7b4769 | |||
| 795d6e7d9d | |||
| 100198bd1f | |||
| 23a254a556 | |||
| 12edb222fe | |||
| e8ba8fa737 | |||
| b4322a0037 | |||
| 4ad5be42ac | |||
| 36edebf085 | |||
| fed7367831 | |||
| f2733d5329 | |||
| bb090554de | |||
| 42a7238060 | |||
| f9df23afbd | |||
| 81e9e5a672 | |||
| c7d69b60ae | |||
| a2f9f9b7c5 | |||
| 32431d413c | |||
| cfa3b92440 | |||
| d84b02bbd3 | |||
| 55c7f73cf0 | |||
| 0ca0df7b4d | |||
| 57d989b675 | |||
| de9e8736a2 | |||
| c404a4666b | |||
| 25707d453f | |||
| 39099edaf2 | |||
| 284678d009 | |||
| 30c49600c4 | |||
| a3dbb7e17b | |||
| 6008b2dc05 | |||
| 829ad64e09 | |||
| 25043fe102 | |||
| 88686ff2c3 | |||
| 12abc05f7d | |||
| aefa4babca | |||
| c62fd5ecfa | |||
| 7eb7f7ea8e | |||
| d9fe73f32b | |||
| e7824591a7 | |||
| b05fc21d36 | |||
| d93328bf86 | |||
| f4ccfb22ac | |||
| 0113a319f4 | |||
| 0ee6d65498 | |||
| 86de2d1eb9 | |||
| db3ab398e9 | |||
| cc3bcd9819 | |||
| 7230f7788f | |||
| bb433c6f46 | |||
| b3883d85d5 | |||
| 65e8752498 | |||
| 6327aefce2 | |||
| be499f838f | |||
| 5a4e74189a | |||
| 071b72038c | |||
| 43e08cbef1 | |||
| 3acbb098bc | |||
| 01ea86d870 | |||
| 022900429a | |||
| f468d2f4fd | |||
| 7e8d4e2128 | |||
| c5bd3349f8 | |||
| 1185f5ea8b | |||
| eeb9acdef1 | |||
| 71924556d6 | |||
| 437bf1914f | |||
| 2247a2b2aa | |||
| 142ef0ddd2 | |||
| a6ef031a85 | |||
| 1201b65d40 | |||
| 0e58a32f10 | |||
| 1ac96b414e | |||
| 1f2b9fbed1 | |||
| d491d1fc0b | |||
| 2475691c4e | |||
| 87d4426b3d | |||
| 0ecc6a5a18 | |||
| 844bc6c148 | |||
| 66dfe1c5e4 | |||
| 150ecdf120 | |||
| aad3d4bd50 | |||
| 6cc75e639c | |||
| a628c83a90 | |||
| 40dfe0fccc | |||
| 215e5b1677 | |||
| cd5eda9df9 | |||
| cad8f0a19d | |||
| 8563621d65 | |||
| 86fce55c97 | |||
| 86d61ed1a0 | |||
| fbacd499dc | |||
| ccb8b3d268 | |||
| 1773ce4444 | |||
| c2517abff9 | |||
| e87775affa | |||
| cc5c04d5da | |||
| a18cbbf49a | |||
| ee3e8edfa8 | |||
| 79b3210bda | |||
| 9580b1f29e | |||
| 04a7bbbda8 | |||
| c6857b850e | |||
| 48a025302f | |||
| 38246e90ee | |||
| 7e1e94d165 | |||
| aa83112e71 | |||
| 044fec0d50 | |||
| e32318b2ad | |||
| e742c77d12 | |||
| 20ed701fec | |||
| e1e2481bbb | |||
| 015696e7c4 | |||
| d318a5a9ec | |||
| d802d63444 | |||
| 7decda722d | |||
| 4a8b911529 | |||
| 435650436c | |||
| 7eb8ce5eba | |||
| 45ab562a84 | |||
| 1cd28a15b5 | |||
| be15835a67 | |||
| 0043ea605b | |||
| a9d9132707 | |||
| 968f881838 | |||
| 6e5863b6f0 | |||
| f096aad827 | |||
| 75afd665b8 | |||
| 2f8a31ca87 | |||
| 02c94565a6 | |||
| 2eaabfca59 | |||
| c300658c45 | |||
| ffd3540da7 | |||
| af57c49e0c | |||
| 3cb908e43a | |||
| b9763d1cab | |||
| 3081b1727e | |||
| 795a79e2de | |||
| 622d469902 | |||
| 0d3d9fd659 | |||
| 0624e3329d | |||
| d37effb75e | |||
| 5d6cbe8712 | |||
| 7da2508ad5 | |||
| e84b5fc21c | |||
| 302b4e2086 | |||
| 8979652c30 | |||
| cd5e49cad6 | |||
| aceab86bd0 | |||
| ac0562565d | |||
| dea9410bff | |||
| 321300cb96 | |||
| fe85d4a831 | |||
| d96900edcd | |||
| 506f0f11c4 | |||
| ae32f463b7 | |||
| 4df8ed7aca | |||
| 4319b86036 | |||
| af9f7335fe | |||
| 6dd9422d13 | |||
| c161cf6911 | |||
| 55a84c282b | |||
| 5ef1cc0aed | |||
| c774c5b9ed | |||
| 03914adbd3 | |||
| f944dc402e | |||
| 41a2fb8e87 | |||
| d98c59977c | |||
| 5635da77ea | |||
| f566e6ebc0 | |||
| 7ac4617819 | |||
| e5a427556a | |||
| eadaacc266 | |||
| 94c1637d10 | |||
| 3f9d72c4fe | |||
| 00bdf183bb | |||
| 0b5452b9ef | |||
| 603e4a5a7a | |||
| 2682634640 | |||
| 5bac29e608 | |||
| 0b34ccfe0b | |||
| 4d9163364b | |||
| 64aa4f4cd8 | |||
| 10755bf441 | |||
| 95cb5d6eb5 | |||
| bd05b6f737 | |||
| 90bfd7cd31 | |||
| 6aebd3a480 | |||
| fa5a12e8b7 | |||
| a4f1c765d0 | |||
| 6809c236c3 | |||
| 53dd954217 | |||
| 525f4160a7 | |||
| f9f462b129 | |||
| 1f55211ecd | |||
| db8ff1ab76 | |||
| 98fb9f325e | |||
| 0811265bc1 | |||
| 4357f1842a | |||
| 3a54c60b51 | |||
| c79cf90797 | |||
| 11573d49d4 | |||
| 15fc7bb78b | |||
| e92df10e2b | |||
| b49af826f3 | |||
| a95c2da928 | |||
| c04db924cc | |||
| c2ff38fd10 | |||
| 3b51b7e059 | |||
| 473f603fee | |||
| 03136df83e | |||
| 7c3e66de7c | |||
| 50b4c9349a | |||
| dab6e99136 | |||
| 2a74c6981c | |||
| bbf0e0ede6 | |||
| 107b28dd6b | |||
| 45d4562015 | |||
| 0305486fc0 | |||
| 92ce1ad080 | |||
| 465f081a01 | |||
| a7bfa330d5 | |||
| c4116b0001 | |||
| d724678c31 | |||
| 7b6bc7c32c | |||
| 14006bf8b6 | |||
| 16ffcf64c9 | |||
| 16c7dfa4c7 | |||
| a5bc3df00b | |||
| c3697aa775 | |||
| 0545a5e27f | |||
| d4140f6046 | |||
| 4e83a19734 | |||
| 90d255d5b7 | |||
| fde08f1a51 | |||
| 367c39cd19 | |||
| 1fe1b890f7 | |||
| 039f0b9679 | |||
| b59bf760fc | |||
| 254a5edeb1 | |||
| e1cd635b2b | |||
| fd488f3611 | |||
| 936141a47c | |||
| 00ad28e7e5 | |||
| 133d01f1bc | |||
| 2bc961a7f6 | |||
| 2763df5b85 | |||
| c70fc08d8a | |||
| fb315401ed | |||
| 7426af0136 | |||
| 7d4ce79754 | |||
| 76c842ad49 | |||
| c5b643b175 | |||
| 7be3e6fc46 | |||
| 3768d57ef1 | |||
| 79a1fc6302 | |||
| cfae5c1e6e | |||
| ee86816a05 | |||
| 3e5625c49a | |||
| aad046ad01 | |||
| 8b0ce6857d | |||
| 0a00df7d8f | |||
| 0b04773828 | |||
| 0bfcfc8e5e | |||
| 6569b22484 | |||
| 6c99447b98 | |||
| 6c2975aa2c | |||
| 8a323969dd | |||
| 6adfee8522 | |||
| 94e5c2bc26 | |||
| a730c30313 | |||
| 0f9842a4e4 | |||
| d48cd0eb4f | |||
| d051b7eb77 | |||
| 229fb1c9d1 |
46
addons/base_accounting_kit/README.rst
Normal file
@@ -0,0 +1,46 @@
|
||||
.. image:: https://img.shields.io/badge/license-LGPL--3-blue.svg
|
||||
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
|
||||
:alt: License: LGPL-3
|
||||
|
||||
Odoo 19 Full Accounting Kit for Community
|
||||
=========================================
|
||||
Full accounting kit for Odoo 19 community editions
|
||||
|
||||
Configuration
|
||||
=============
|
||||
No configuration
|
||||
|
||||
Company
|
||||
-------
|
||||
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__
|
||||
|
||||
License
|
||||
-------
|
||||
General Public License, Version 3 (LGPL v3).
|
||||
(http://www.gnu.org/licenses/lgpl-3.0-standalone.html)
|
||||
|
||||
Credits
|
||||
-------
|
||||
Developer: (V19) MohammedIrfan T, Ashik MA, Contact: odoo@cybrosys.com
|
||||
|
||||
Contacts
|
||||
--------
|
||||
* Mail Contact : odoo@cybrosys.com
|
||||
* Website : https://cybrosys.com
|
||||
|
||||
Bug Tracker
|
||||
-----------
|
||||
Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported.
|
||||
|
||||
Maintainer
|
||||
==========
|
||||
.. image:: https://cybrosys.com/images/logo.png
|
||||
:target: https://cybrosys.com
|
||||
|
||||
This module is maintained by Cybrosys Technologies.
|
||||
|
||||
For support and more information, please visit `Our Website <https://cybrosys.com/>`__
|
||||
|
||||
Further information
|
||||
===================
|
||||
HTML Description: `<static/description/index.html>`__
|
||||
25
addons/base_accounting_kit/__init__.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# -*- # -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from . import models
|
||||
from . import report
|
||||
from . import wizard
|
||||
from . import controllers
|
||||
124
addons/base_accounting_kit/__manifest__.py
Normal file
@@ -0,0 +1,124 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
{
|
||||
'name': 'Odoo 19 Full Accounting Kit for Community',
|
||||
'version': '19.0.2.3.1',
|
||||
'category': 'Accounting',
|
||||
'live_test_url': 'https://kit.easyinstance.com/web/login?redirect=/odoo/accounting',
|
||||
'summary': """Odoo 19 Accounting, Odoo 19 Accounting Reports, Odoo18 Accounting, Odoo Accounting, Odoo19 Financial Reports, Odoo19 Asset, Odoo19 Profit and Loss, PDC, Followups, Odoo19, Accounting, Odoo Apps, Reports""",
|
||||
'description': """ Odoo 19 Accounting, The module used to manage the Full
|
||||
Account Features that can manage the Account Reports,Journals Asset and
|
||||
Budget Management, Accounting Reports, PDC, Credit Limit,
|
||||
Follow Ups, Day-Bank-Cash book report, Odoo 18 Accounting, odoo apps""",
|
||||
'author': 'Cybrosys Techno Solutions',
|
||||
'company': 'Cybrosys Techno Solutions',
|
||||
'maintainer': 'Cybrosys Techno Solutions',
|
||||
'website': "https://www.cybrosys.com",
|
||||
'depends': ['account', 'sale', 'account_check_printing', 'analytic',
|
||||
'base_account_budget', 'contacts'],
|
||||
'data': [
|
||||
'security/security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'data/account_financial_report_data.xml',
|
||||
'data/cash_flow_data.xml',
|
||||
'data/followup_levels.xml',
|
||||
'data/multiple_invoice_data.xml',
|
||||
'data/recurring_entry_cron.xml',
|
||||
'data/account_pdc_data.xml',
|
||||
'views/reports_config_view.xml',
|
||||
'views/accounting_menu.xml',
|
||||
'views/account_group.xml',
|
||||
'views/credit_limit_view.xml',
|
||||
'views/account_configuration.xml',
|
||||
'views/res_config_settings_views.xml',
|
||||
'views/account_followup.xml',
|
||||
'views/followup_line_views.xml',
|
||||
'views/followup_report.xml',
|
||||
'wizard/asset_depreciation_confirmation_views.xml',
|
||||
'wizard/asset_modify_views.xml',
|
||||
'views/account_asset_asset_views.xml',
|
||||
'views/account_asset_category_views.xml',
|
||||
'views/account_move_views.xml',
|
||||
'views/product_template_views.xml',
|
||||
'views/multiple_invoice_layout_view.xml',
|
||||
'views/multiple_invoice_form.xml',
|
||||
'views/account_journal_views.xml',
|
||||
'views/res_partner_views.xml',
|
||||
'wizard/financial_report_views.xml',
|
||||
'wizard/account_report_general_ledger_views.xml',
|
||||
'wizard/account_report_partner_ledger_views.xml',
|
||||
'wizard/kit_account_tax_report_views.xml',
|
||||
'wizard/account_balance_report_views.xml',
|
||||
'wizard/account_aged_trial_balance_views.xml',
|
||||
'wizard/account_print_journal_views.xml',
|
||||
'wizard/cash_flow_report_views.xml',
|
||||
'wizard/account_bank_book_report_views.xml',
|
||||
'wizard/account_cash_book_report_views.xml',
|
||||
'wizard/account_day_book_report_views.xml',
|
||||
'report/report_financial_template.xml',
|
||||
'report/general_ledger_report_template.xml',
|
||||
'report/report_journal_audit_template.xml',
|
||||
'report/report_aged_partner_template.xml',
|
||||
'report/report_trial_balance_template.xml',
|
||||
'report/report_tax_template.xml',
|
||||
'report/report_partner_ledger_template.xml',
|
||||
'report/cash_flow_report_template.xml',
|
||||
'report/account_bank_book_template.xml',
|
||||
'report/account_cash_book_template.xml',
|
||||
'report/account_day_book_template.xml',
|
||||
'report/account_asset_report_views.xml',
|
||||
'report/report.xml',
|
||||
'report/multiple_invoice_layouts.xml',
|
||||
'report/multiple_invoice_report_template.xml',
|
||||
'report/res_partner_reports.xml',
|
||||
'report/res_partner_templates.xml',
|
||||
'views/account_recurring_payments_view.xml',
|
||||
'views/account_move_line_views.xml',
|
||||
'views/account_bank_statement_views.xml',
|
||||
'views/account_bank_statement_line_views.xml',
|
||||
'views/account_payment_view.xml',
|
||||
'wizard/account_lock_date_views.xml',
|
||||
'wizard/import_bank_statement_views.xml',
|
||||
],
|
||||
'external_dependencies': {
|
||||
'python': ['openpyxl', 'ofxparse', 'qifparse']
|
||||
},
|
||||
'assets': {
|
||||
'web.assets_backend': [
|
||||
'base_accounting_kit/static/src/scss/style.scss',
|
||||
'base_accounting_kit/static/src/scss/bank_rec_widget.css',
|
||||
'base_accounting_kit/static/src/js/bank_reconcile_form_list_widget.js',
|
||||
'base_accounting_kit/static/src/js/KanbanController.js',
|
||||
'base_accounting_kit/static/src/js/ListController.js',
|
||||
'base_accounting_kit/static/src/js/bank_reconcile_form_lines_widget.js',
|
||||
'base_accounting_kit/static/src/js/action_manager.js',
|
||||
'base_accounting_kit/static/src/xml/bank_rec_widget.xml',
|
||||
'base_accounting_kit/static/src/xml/bank_reconcile_widget.xml',
|
||||
]
|
||||
},
|
||||
'license': 'LGPL-3',
|
||||
'images': ['static/description/banner.gif'],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
'application': True,
|
||||
}
|
||||
|
||||
22
addons/base_accounting_kit/controllers/__init__.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from . import statement_report
|
||||
53
addons/base_accounting_kit/controllers/statement_report.py
Normal file
@@ -0,0 +1,53 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
import json
|
||||
from odoo import http
|
||||
from odoo.http import content_disposition, request
|
||||
from odoo.tools import html_escape
|
||||
|
||||
|
||||
class XLSXReportController(http.Controller):
|
||||
""" Controller for xlsx report """
|
||||
@http.route('/xlsx_report', type='http', auth='user', methods=['POST'],
|
||||
csrf=False)
|
||||
def get_report_xlsx(self, model, data, output_format, report_name,
|
||||
report_action=None, options=None, **kwargs):
|
||||
""" Get xlsx report data """
|
||||
report_obj = request.env[model].sudo()
|
||||
try:
|
||||
if output_format == 'xlsx':
|
||||
response = request.make_response(
|
||||
None, headers=[
|
||||
('Content-Type', 'application/vnd.ms-excel'),
|
||||
('Content-Disposition', content_disposition(
|
||||
report_name + '.xlsx'))])
|
||||
report_obj.get_xlsx_report(data, response, report_name, report_action)
|
||||
response.set_cookie('fileToken', 'dummy token')
|
||||
return response
|
||||
except Exception as event:
|
||||
serialize = http.serialize_exception(event)
|
||||
error = {
|
||||
'code': 200,
|
||||
'message': 'Odoo Server Error',
|
||||
'data': serialize
|
||||
}
|
||||
return request.make_response(html_escape(json.dumps(error)))
|
||||
@@ -0,0 +1,152 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<!-- Financial Reports data records in the model account.financial.report-->
|
||||
<record id="account_financial_report_profitandloss0"
|
||||
model="account.financial.report">
|
||||
<field name="name">Profit and Loss</field>
|
||||
<field name="sign" eval="'-1'"/>
|
||||
<field name="type">sum</field>
|
||||
</record>
|
||||
<!-- Income report -->
|
||||
<record id="account_financial_report_income0"
|
||||
model="account.financial.report">
|
||||
<field name="name">Income</field>
|
||||
<field name="sign" eval="'-1'"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="parent_id"
|
||||
ref="account_financial_report_profitandloss0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">sum</field>
|
||||
</record>
|
||||
<!-- Other Income -->
|
||||
<record id="account_financial_report_other_income0"
|
||||
model="account.financial.report">
|
||||
<field name="name">Other Income</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="parent_id"
|
||||
ref="account_financial_report_income0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">account_type</field>
|
||||
<field name="account_type_ids">income_other</field>
|
||||
</record>
|
||||
<!-- Gross profit -->
|
||||
<record id="financial_report_gross_profit"
|
||||
model="account.financial.report">
|
||||
<field name="name">Gross Profit</field>
|
||||
<field name="parent_id"
|
||||
ref="account_financial_report_income0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">sum</field>
|
||||
<field name="sequence">3</field>
|
||||
</record>
|
||||
<!-- Cost of revenue -->
|
||||
<record id="financial_report_cost_of_revenue"
|
||||
model="account.financial.report">
|
||||
<field name="name">Cost of Revenue</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="parent_id"
|
||||
ref="financial_report_gross_profit"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">account_type</field>
|
||||
<field name="account_type_ids">expense_direct_cost</field>
|
||||
</record>
|
||||
<!-- Operating Income -->
|
||||
<record id="account_financial_report_operating_income0"
|
||||
model="account.financial.report">
|
||||
<field name="name">Operating Income</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="parent_id"
|
||||
ref="financial_report_gross_profit"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">account_type</field>
|
||||
<field name="account_type_ids">income</field>
|
||||
</record>
|
||||
<!-- Expense -->
|
||||
<record id="account_financial_report_expense0"
|
||||
model="account.financial.report">
|
||||
<field name="name">Expense</field>
|
||||
<field name="sign" eval="'-1'"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="parent_id"
|
||||
ref="account_financial_report_profitandloss0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">account_type</field>
|
||||
<field name="account_type_ids">expense</field>
|
||||
</record>
|
||||
<!-- Balance Sheet -->
|
||||
<record id="account_financial_report_balancesheet0"
|
||||
model="account.financial.report">
|
||||
<field name="name">Balance Sheet</field>
|
||||
<field name="type">sum</field>
|
||||
</record>
|
||||
<!-- Assets -->
|
||||
<record id="account_financial_report_assets0"
|
||||
model="account.financial.report">
|
||||
<field name="name">Assets</field>
|
||||
<field name="parent_id"
|
||||
ref="account_financial_report_balancesheet0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">account_type</field>
|
||||
<field name="account_type_ids">income_other</field>
|
||||
</record>
|
||||
<!-- Liability -->
|
||||
<record id="account_financial_report_liabilitysum0"
|
||||
model="account.financial.report">
|
||||
<field name="name">Liability</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="parent_id"
|
||||
ref="account_financial_report_balancesheet0"/>
|
||||
<field name="display_detail">no_detail</field>
|
||||
<field name="type">sum</field>
|
||||
</record>
|
||||
<!-- Liability -->
|
||||
<record id="account_financial_report_liability0"
|
||||
model="account.financial.report">
|
||||
<field name="name">Liability</field>
|
||||
<field name="parent_id"
|
||||
ref="account_financial_report_liabilitysum0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">account_type</field>
|
||||
<field name="account_type_ids">income_other</field>
|
||||
</record>
|
||||
<!-- Profit (Loss) to report -->
|
||||
<record id="account_financial_report_profitloss_toreport0"
|
||||
model="account.financial.report">
|
||||
<field name="name">Profit (Loss) to report</field>
|
||||
<field name="parent_id"
|
||||
ref="account_financial_report_liabilitysum0"/>
|
||||
<field name="display_detail">no_detail</field>
|
||||
<field name="type">account_report</field>
|
||||
<field name="account_report_id"
|
||||
ref="account_financial_report_profitandloss0"/>
|
||||
</record>
|
||||
<!-- Common Report -->
|
||||
<record id="account_report_view_form" model="ir.ui.view">
|
||||
<field name="name">account.report.view.form</field>
|
||||
<field name="model">account.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Report Options">
|
||||
<group col="4">
|
||||
<field name="target_move" widget="radio"/>
|
||||
<field name="date_from"/>
|
||||
<field name="date_to"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="journal_ids" widget="many2many_tags"
|
||||
options="{'no_create': True}"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="check_report" string="Print"
|
||||
type="object" default_focus="1"
|
||||
class="oe_highlight"
|
||||
data-hotkey="q"/>
|
||||
<button string="Cancel" class="btn btn-secondary"
|
||||
special="cancel" data-hotkey="z"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
25
addons/base_accounting_kit/data/account_pdc_data.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<!-- Account payment method Inbound pdc-->
|
||||
<record id="account_payment_method_pdc_in"
|
||||
model="account.payment.method">
|
||||
<field name="name">PDC</field>
|
||||
<field name="code">pdc</field>
|
||||
<field name="payment_type">inbound</field>
|
||||
</record>
|
||||
<!-- Outbound pdc -->
|
||||
<record id="account_payment_method_pdc_out"
|
||||
model="account.payment.method">
|
||||
<field name="name">PDC</field>
|
||||
<field name="code">pdc</field>
|
||||
<field name="payment_type">outbound</field>
|
||||
</record>
|
||||
<!-- Decimal precision for account -->
|
||||
<record forcecreate="True" id="decimal_account"
|
||||
model="decimal.precision">
|
||||
<field name="name">Account</field>
|
||||
<field name="digits" eval="3"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
79
addons/base_accounting_kit/data/cash_flow_data.xml
Normal file
@@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<!-- Records for the account.financial.report model-->
|
||||
<record id="account_financial_report_cash_flow0" model="account.financial.report">
|
||||
<field name="name">Cash Flow Statement</field>
|
||||
<field name="type">sum</field>
|
||||
</record>
|
||||
<!-- Defines a financial report record for operations -->
|
||||
<record id="account_financial_report_operation0"
|
||||
model="account.financial.report">
|
||||
<field name="name">Operations</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="parent_id" ref="account_financial_report_cash_flow0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">sum</field>
|
||||
</record>
|
||||
<!-- Cash in operation -->
|
||||
<record id="cash_in_from_operation0" model="account.financial.report">
|
||||
<field name="name">Cash In</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="parent_id" ref="account_financial_report_operation0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">accounts</field>
|
||||
</record>
|
||||
<!-- Cash out operation -->
|
||||
<record id="cash_out_operation1" model="account.financial.report">
|
||||
<field name="name">Cash Out</field>
|
||||
<field name="sequence">2</field>
|
||||
<field name="parent_id" ref="account_financial_report_operation0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">accounts</field>
|
||||
</record>
|
||||
<!-- Defines a financial report record for Investing Activities -->
|
||||
<record id="account_financial_report_investing_activity0" model="account.financial.report">
|
||||
<field name="name">Investing Activities</field>
|
||||
<field name="sequence">2</field>
|
||||
<field name="parent_id" ref="account_financial_report_cash_flow0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">sum</field>
|
||||
</record>
|
||||
<!-- Cash in Investing Activities -->
|
||||
<record id="cash_in_investing0" model="account.financial.report">
|
||||
<field name="name">Cash In</field>
|
||||
<field name="parent_id" ref="account_financial_report_investing_activity0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">accounts</field>
|
||||
</record>
|
||||
<!-- Cash out Investing Activities -->
|
||||
<record id="cash_out_investing1" model="account.financial.report">
|
||||
<field name="name">Cash Out</field>
|
||||
<field name="parent_id" ref="account_financial_report_investing_activity0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">accounts</field>
|
||||
</record>
|
||||
<!-- Defines a financial report record for Financing Activities -->
|
||||
<record id="account_financial_report_financing_activity1" model="account.financial.report">
|
||||
<field name="name">Financing Activities</field>
|
||||
<field name="sequence">3</field>
|
||||
<field name="parent_id" ref="account_financial_report_cash_flow0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">sum</field>
|
||||
</record>
|
||||
<!-- Cash in Financing Activities -->
|
||||
<record id="cash_in_financial0" model="account.financial.report">
|
||||
<field name="name">Cash In</field>
|
||||
<field name="parent_id" ref="account_financial_report_financing_activity1"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">accounts</field>
|
||||
</record>
|
||||
<!-- Cash out Financing Activities -->
|
||||
<record id="cash_out_financial1" model="account.financial.report">
|
||||
<field name="name">Cash Out</field>
|
||||
<field name="parent_id" ref="account_financial_report_financing_activity1"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">accounts</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
14
addons/base_accounting_kit/data/followup_levels.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<!-- Data file for the follow-up lines -->
|
||||
<record model="followup.line" id="followup_line_id" >
|
||||
<field name="name">Reminder</field>
|
||||
<field name="delay">5</field>
|
||||
</record>
|
||||
|
||||
<record model="account.followup" id="followup">
|
||||
<field name="followup_line_ids" eval="[(6,0,[ref('followup_line_id')])]"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<!-- Data file for the Multiple Invoice-->
|
||||
<record id="multiple_invoice_sample_name" model="multiple.invoice">
|
||||
<field name="copy_name">Sample Name</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
14
addons/base_accounting_kit/data/recurring_entry_cron.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding='UTF-8'?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<!-- The schedular action for Recurring Entries -->
|
||||
<record id="recurring_template_cron" model="ir.cron">
|
||||
<field name="name">Generate Recurring Entries</field>
|
||||
<field name="model_id" ref="model_account_recurring_payments"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model._cron_generate_entries()</field>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">days</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
26
addons/base_accounting_kit/doc/RELEASE_NOTES.md
Normal file
@@ -0,0 +1,26 @@
|
||||
## Module <base_accounting_kit>
|
||||
|
||||
#### 17.09.2025
|
||||
#### Version 19.0.1.0.0
|
||||
#### ADD
|
||||
- Initial commit for Odoo 19 Full Accounting Kit for Community
|
||||
|
||||
#### 21.10.2025
|
||||
#### Version 19.0.2.0.0
|
||||
#### ADD
|
||||
- Added Customer Statement feature.
|
||||
|
||||
#### 09.01.2026
|
||||
#### Version 19.0.2.1.0
|
||||
#### ADD
|
||||
- Fixed the issue in the bank statement import.
|
||||
|
||||
#### 09.02.2026
|
||||
#### Version 19.0.2.2.0
|
||||
#### UPDT
|
||||
- Fixed the issues in the bank statement import of csv and ofx files.
|
||||
-
|
||||
#### 25.03.2026
|
||||
#### Version 19.0.2.3.0
|
||||
#### UPDT
|
||||
- Fixed the issue in fiscal year dates.
|
||||
4132
addons/base_accounting_kit/i18n/ar_001.po
Normal file
4132
addons/base_accounting_kit/i18n/de_CH.po
Normal file
4132
addons/base_accounting_kit/i18n/es_AR.po
Normal file
4132
addons/base_accounting_kit/i18n/fr_BE.po
Normal file
4132
addons/base_accounting_kit/i18n/uk_UA.po
Normal file
4171
addons/base_accounting_kit/i18n/zh_CN.po
Normal file
4130
addons/base_accounting_kit/i18n/zh_HK.po
Normal file
43
addons/base_accounting_kit/models/__init__.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from . import account_account
|
||||
from . import account_asset_asset
|
||||
from . import account_asset_category
|
||||
from . import account_asset_depreciation_line
|
||||
from . import account_bank_statement_line
|
||||
from . import account_followup
|
||||
from . import account_journal
|
||||
from . import account_move
|
||||
from . import account_move_line
|
||||
from . import account_payment
|
||||
from . import account_payment_method
|
||||
from . import account_recurring_entries_line
|
||||
from . import account_report
|
||||
from . import followup_line
|
||||
from . import multiple_invoice
|
||||
from . import multiple_invoice_layout
|
||||
from . import product_template
|
||||
from . import recurring_payments
|
||||
from . import res_company
|
||||
from . import res_config_settings
|
||||
from . import res_partner
|
||||
from . import sale_order
|
||||
112
addons/base_accounting_kit/models/account_account.py
Normal file
@@ -0,0 +1,112 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from odoo import api, fields, models
|
||||
from odoo.tools.misc import get_lang
|
||||
|
||||
|
||||
class CashFlow(models.Model):
|
||||
"""Inherits the account.account model to add additional functionality and
|
||||
fields to the account"""
|
||||
_inherit = 'account.account'
|
||||
|
||||
def get_cash_flow_ids(self):
|
||||
"""Returns a list of cashflows for the account"""
|
||||
cash_flow_id = self.env.ref('base_accounting_kit.account_financial_report_cash_flow0')
|
||||
if cash_flow_id:
|
||||
return [('parent_id.id', '=', cash_flow_id.id)]
|
||||
|
||||
cash_flow_type = fields.Many2one('account.financial.report',
|
||||
string="Cash Flow type",
|
||||
domain=get_cash_flow_ids)
|
||||
|
||||
@api.onchange('cash_flow_type')
|
||||
def onchange_cash_flow_type(self):
|
||||
"""Onchange the cash flow type of the account that will be updating
|
||||
the account_ids values"""
|
||||
for rec in self.cash_flow_type:
|
||||
# update new record
|
||||
rec.write({
|
||||
'account_ids': [(4, self._origin.id)]
|
||||
})
|
||||
if self._origin.cash_flow_type.ids:
|
||||
for rec in self._origin.cash_flow_type:
|
||||
# remove old record
|
||||
rec.write({'account_ids': [(3, self._origin.id)]})
|
||||
|
||||
|
||||
class AccountCommonJournalReport(models.TransientModel):
|
||||
"""Model used for creating the common journal report"""
|
||||
_name = 'account.common.journal.report'
|
||||
_description = 'Common Journal Report'
|
||||
_inherit = "account.report"
|
||||
|
||||
section_main_report_ids = fields.Many2many(string="Section Of",
|
||||
comodel_name='account.report',
|
||||
relation="account_common_journal_report_section_rel",
|
||||
column1="sub_report_id",
|
||||
column2="main_report_id")
|
||||
section_report_ids = fields.Many2many(string="Sections",
|
||||
comodel_name='account.report',
|
||||
relation="account_common_journal_report_section_rel",
|
||||
column1="main_report_id",
|
||||
column2="sub_report_id")
|
||||
amount_currency = fields.Boolean(
|
||||
'With Currency',
|
||||
help="Print Report with the currency column if the currency differs "
|
||||
"from the company currency.")
|
||||
company_id = fields.Many2one('res.company', string='Company',
|
||||
required=True, readonly=True,
|
||||
default=lambda self: self.env.company)
|
||||
date_from = fields.Date(string='Start Date')
|
||||
date_to = fields.Date(string='End Date')
|
||||
target_move = fields.Selection([('posted', 'All Posted Entries'),
|
||||
('all', 'All Entries'),
|
||||
], string='Target Moves',
|
||||
required=True, default='posted')
|
||||
|
||||
def pre_print_report(self, data):
|
||||
"""Pre-print the given data and that updates the amount
|
||||
amount_currency value"""
|
||||
data['form'].update({'amount_currency': self.amount_currency})
|
||||
return data
|
||||
|
||||
def check_report(self):
|
||||
"""Function to check if the report comes active models and related values"""
|
||||
self.ensure_one()
|
||||
data = {}
|
||||
data['ids'] = self.env.context.get('active_ids', [])
|
||||
data['model'] = self.env.context.get('active_model', 'ir.ui.menu')
|
||||
data['form'] = self.read(['date_from', 'date_to', 'journal_ids', 'target_move', 'company_id'])[0]
|
||||
used_context = self._build_contexts(data)
|
||||
data['form']['used_context'] = dict(used_context, lang=get_lang(self.env).code)
|
||||
return self.with_context(discard_logo_check=True)._print_report(data)
|
||||
|
||||
def _build_contexts(self, data):
|
||||
"""Builds the context information for the given data"""
|
||||
result = {}
|
||||
result['journal_ids'] = 'journal_ids' in data['form'] and data['form']['journal_ids'] or False
|
||||
result['state'] = 'target_move' in data['form'] and data['form']['target_move'] or ''
|
||||
result['date_from'] = data['form']['date_from'] or False
|
||||
result['date_to'] = data['form']['date_to'] or False
|
||||
result['strict_range'] = True if result['date_from'] else False
|
||||
result['company_id'] = data['form']['company_id'][0] or False
|
||||
return result
|
||||
623
addons/base_accounting_kit/models/account_asset_asset.py
Normal file
@@ -0,0 +1,623 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
import calendar
|
||||
from datetime import date, datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.fields import Date
|
||||
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT as DF, float_is_zero
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
|
||||
|
||||
class AccountAssetAsset(models.Model):
|
||||
"""
|
||||
Model for managing assets with depreciation functionality
|
||||
"""
|
||||
_name = 'account.asset.asset'
|
||||
_description = 'Asset/Revenue Recognition'
|
||||
_inherit = ['mail.thread']
|
||||
|
||||
entry_count = fields.Integer(compute='_entry_count',
|
||||
string='# Asset Entries')
|
||||
name = fields.Char(string='Asset Name', required=True)
|
||||
code = fields.Char(string='Reference', size=32)
|
||||
value = fields.Float(string='Gross Value', required=True,
|
||||
digits=0)
|
||||
currency_id = fields.Many2one('res.currency', string='Currency',
|
||||
required=True,
|
||||
default=lambda self: self.env.company.currency_id.id)
|
||||
company_id = fields.Many2one('res.company', string='Company',
|
||||
required=True,
|
||||
default=lambda self: self.env.company)
|
||||
note = fields.Text()
|
||||
category_id = fields.Many2one('account.asset.category', string='Asset Model',
|
||||
required=False, change_default=True
|
||||
)
|
||||
date = fields.Date(string='Date', required=True,
|
||||
default=fields.Date.context_today)
|
||||
state = fields.Selection(
|
||||
[('draft', 'Draft'), ('open', 'Running'), ('close', 'Close'),('cancelled','Cancelled')],
|
||||
'Status', required=True, copy=False, default='draft',
|
||||
help="When an asset is created, the status is 'Draft'.\n"
|
||||
"If the asset is confirmed, the status goes in 'Running' and the depreciation lines can be posted in the accounting.\n"
|
||||
"You can manually close an asset when the depreciation is over. If the last line of depreciation is posted, the asset automatically goes in that status.")
|
||||
active = fields.Boolean(default=True)
|
||||
partner_id = fields.Many2one('res.partner', string='Partner')
|
||||
method = fields.Selection(
|
||||
[('linear', 'Straight Line'), ('degressive', 'Declining')],
|
||||
string='Computation Method', required=True,default='linear',
|
||||
help="Choose the method to use to compute the amount of depreciation lines.\n * Linear: Calculated on basis of: Gross Value / Number of Depreciations\n"
|
||||
" * Degressive: Calculated on basis of: Residual Value * Degressive Factor")
|
||||
method_number = fields.Integer(string='Number of Depreciations',
|
||||
default=5,
|
||||
help="The number of depreciation's needed to depreciate your asset")
|
||||
method_period = fields.Integer(string='Number of Months in a Period',
|
||||
required=True, default=12,
|
||||
help="The amount of time between two depreciation's, in months")
|
||||
method_end = fields.Date(string='Ending Date')
|
||||
method_progress_factor = fields.Float(string='Degressive Factor',
|
||||
default=0.3,)
|
||||
value_residual = fields.Float(compute='_amount_residual',
|
||||
digits=0, string='Residual Value')
|
||||
method_time = fields.Selection(
|
||||
[('number', 'Number of Entries'), ('end', 'Ending Date')],
|
||||
string='Time Method', required=True, default='number',
|
||||
help="Choose the method to use to compute the dates and number of entries.\n"
|
||||
" * Number of Entries: Fix the number of entries and the time between 2 depreciations.\n"
|
||||
" * Ending Date: Choose the time between 2 depreciations and the date the depreciations won't go beyond.")
|
||||
prorata = fields.Boolean(string='Prorata Temporis',
|
||||
help='Indicates that the first depreciation entry for this asset have to be done from the purchase date instead of the first January / Start date of fiscal year')
|
||||
depreciation_line_ids = fields.One2many('account.asset.depreciation.line',
|
||||
'asset_id',
|
||||
string='Depreciation Lines',
|
||||
)
|
||||
salvage_value = fields.Float(string='Salvage Value', digits=0,
|
||||
|
||||
help="It is the amount you plan to have that you cannot depreciate.")
|
||||
invoice_id = fields.Many2one('account.move', string='Invoice',
|
||||
copy=False)
|
||||
type = fields.Selection([('sale', 'Sale: Revenue Recognition'),
|
||||
('purchase', 'Purchase: Asset')], required=True, index=True, default='purchase')
|
||||
|
||||
|
||||
#asset category
|
||||
account_analytic_id = fields.Many2one('account.analytic.account',
|
||||
string='Analytic Account',
|
||||
domain="[('company_id', '=', company_id)]")
|
||||
account_asset_id = fields.Many2one('account.account',
|
||||
string='Asset Account', required=True,
|
||||
domain="[('account_type', '!=', 'asset_receivable'),('account_type', '!=', 'liability_payable'),('account_type', '!=', 'asset_cash'),('account_type', '!=', 'liability_credit_card'),('active', '=', True)]",
|
||||
help="Account used to record the purchase of the asset at its original price.")
|
||||
account_depreciation_id = fields.Many2one(
|
||||
'account.account', string='Depreciation Account',
|
||||
required=True,
|
||||
domain="[('account_type', '!=', 'asset_receivable'),('account_type', '!=', 'liability_payable'),('account_type', '!=', 'asset_cash'),('account_type', '!=', 'liability_credit_card'),('active', '=', True)]",
|
||||
help="Account used in the depreciation entries, to decrease the asset value.")
|
||||
account_depreciation_expense_id = fields.Many2one(
|
||||
'account.account', string='Expense Account',
|
||||
required=True,
|
||||
domain="[('account_type', '!=', 'asset_receivable'),('account_type', '!=','liability_payable'),('account_type', '!=', 'asset_cash'),('account_type', '!=','liability_credit_card'),('active', '=', True)]",
|
||||
help="Account used in the periodical entries, to record a part of the asset as expense.")
|
||||
journal_id = fields.Many2one('account.journal', string='Journal',
|
||||
required=True)
|
||||
open_asset = fields.Boolean(string='Auto-confirm Assets',
|
||||
help="Check this if you want to automatically confirm the assets of this category when created by invoices.")
|
||||
group_entries = fields.Boolean(string='Group Journal Entries',
|
||||
help="Check this if you want to group the generated entries by categories.")
|
||||
|
||||
def unlink(self):
|
||||
""" Prevents deletion of assets in 'open' or 'close' state or with posted depreciation entries."""
|
||||
for asset in self:
|
||||
if asset.state in ['open', 'close']:
|
||||
raise UserError(
|
||||
_('You cannot delete a document is in %s state.') % (
|
||||
asset.state,))
|
||||
for depreciation_line in asset.depreciation_line_ids:
|
||||
if depreciation_line.move_id:
|
||||
raise UserError(_(
|
||||
'You cannot delete a document that contains posted entries.'))
|
||||
return super(AccountAssetAsset, self).unlink()
|
||||
|
||||
def _get_last_depreciation_date(self):
|
||||
"""
|
||||
@param id: ids of a account.asset.asset objects
|
||||
@return: Returns a dictionary of the effective dates of the last depreciation entry made for given asset ids. If there isn't any, return the purchase date of this asset
|
||||
"""
|
||||
self.env.cr.execute("""
|
||||
SELECT a.id as id, COALESCE(MAX(m.date),a.date) AS date
|
||||
FROM account_asset_asset a
|
||||
LEFT JOIN account_asset_depreciation_line rel ON (rel.asset_id = a.id)
|
||||
LEFT JOIN account_move m ON (rel.move_id = m.id)
|
||||
WHERE a.id IN %s
|
||||
GROUP BY a.id, m.date """, (tuple(self.ids),))
|
||||
result = dict(self.env.cr.fetchall())
|
||||
return result
|
||||
|
||||
@api.onchange('category_id')
|
||||
def gross_value(self):
|
||||
"""Update the 'value' field based on the 'price' of the selected 'category_id'."""
|
||||
self.value = self.category_id.price
|
||||
@api.onchange('method')
|
||||
def onchange_method(self):
|
||||
if self.depreciation_line_ids:
|
||||
self.depreciation_line_ids = [(fields.Command.clear())]
|
||||
|
||||
|
||||
@api.model
|
||||
def compute_generated_entries(self, date, asset_type=None):
|
||||
"""Compute generated entries for assets based on the provided date and asset type."""
|
||||
# Entries generated : one by grouped category and one by asset from ungrouped category
|
||||
created_move_ids = []
|
||||
type_domain = []
|
||||
if asset_type:
|
||||
type_domain = [('type', '=', asset_type)]
|
||||
|
||||
ungrouped_assets = self.env['account.asset.asset'].search(
|
||||
type_domain + [('state', '=', 'open'),
|
||||
('category_id.group_entries', '=', False)])
|
||||
created_move_ids += ungrouped_assets._compute_entries(date,
|
||||
group_entries=False)
|
||||
|
||||
for grouped_category in self.env['account.asset.category'].search(
|
||||
type_domain + [('group_entries', '=', True)]):
|
||||
assets = self.env['account.asset.asset'].search(
|
||||
[('state', '=', 'open'),
|
||||
('category_id', '=', grouped_category.id)])
|
||||
created_move_ids += assets._compute_entries(date,
|
||||
group_entries=True)
|
||||
return created_move_ids
|
||||
|
||||
def _compute_board_amount(self, sequence, residual_amount, amount_to_depr,
|
||||
undone_dotation_number,
|
||||
posted_depreciation_line_ids, total_days,
|
||||
depreciation_date):
|
||||
"""Compute the depreciation amount for a specific sequence in the asset's depreciation schedule."""
|
||||
amount = 0
|
||||
if sequence == undone_dotation_number:
|
||||
amount = residual_amount
|
||||
else:
|
||||
if self.method == 'linear':
|
||||
amount = amount_to_depr / (undone_dotation_number - len(
|
||||
posted_depreciation_line_ids))
|
||||
if self.prorata:
|
||||
amount = amount_to_depr / self.method_number
|
||||
if sequence == 1:
|
||||
if self.method_period % 12 != 0:
|
||||
date = datetime.strptime(str(self.date),
|
||||
'%Y-%m-%d')
|
||||
month_days = \
|
||||
calendar.monthrange(date.year, date.month)[1]
|
||||
days = month_days - date.day + 1
|
||||
amount = (
|
||||
amount_to_depr / self.method_number) / month_days * days
|
||||
else:
|
||||
days = (self.company_id.compute_fiscalyear_dates(
|
||||
depreciation_date)[
|
||||
'date_to'] - depreciation_date).days + 1
|
||||
amount = (
|
||||
amount_to_depr / self.method_number) / total_days * days
|
||||
elif self.method == 'degressive':
|
||||
amount = residual_amount * self.method_progress_factor
|
||||
if self.prorata:
|
||||
if sequence == 1:
|
||||
if self.method_period % 12 != 0:
|
||||
date = datetime.strptime(str(self.date),
|
||||
'%Y-%m-%d')
|
||||
month_days = \
|
||||
calendar.monthrange(date.year, date.month)[1]
|
||||
days = month_days - date.day + 1
|
||||
amount = (
|
||||
residual_amount * self.method_progress_factor) / month_days * days
|
||||
else:
|
||||
days = (self.company_id.compute_fiscalyear_dates(
|
||||
depreciation_date)[
|
||||
'date_to'] - depreciation_date).days + 1
|
||||
amount = (
|
||||
residual_amount * self.method_progress_factor) / total_days * days
|
||||
return amount
|
||||
|
||||
def _compute_board_undone_dotation_nb(self, depreciation_date, total_days):
|
||||
"""Compute the number of remaining depreciations for an asset based on the depreciation date and total days."""
|
||||
undone_dotation_number = self.method_number
|
||||
if self.method_time == 'end':
|
||||
end_date = datetime.strptime(str(self.method_end), DF).date()
|
||||
undone_dotation_number = 0
|
||||
while depreciation_date <= end_date:
|
||||
depreciation_date = date(depreciation_date.year,
|
||||
depreciation_date.month,
|
||||
depreciation_date.day) + relativedelta(
|
||||
months=+self.method_period)
|
||||
undone_dotation_number += 1
|
||||
if self.prorata:
|
||||
undone_dotation_number += 1
|
||||
return undone_dotation_number
|
||||
|
||||
def compute_depreciation_board(self):
|
||||
"""
|
||||
Compute the depreciation schedule for the asset based on its current state and parameters.
|
||||
This method calculates the depreciation amount for each period and generates depreciation entries accordingly.
|
||||
"""
|
||||
self.ensure_one()
|
||||
posted_depreciation_line_ids = self.depreciation_line_ids.filtered(
|
||||
lambda x: x.move_check).sorted(key=lambda l: l.depreciation_date)
|
||||
unposted_depreciation_line_ids = self.depreciation_line_ids.filtered(
|
||||
lambda x: not x.move_check)
|
||||
|
||||
# Remove old unposted depreciation lines. We cannot use unlink() with One2many field
|
||||
commands = [(2, line_id.id, False) for line_id in
|
||||
unposted_depreciation_line_ids]
|
||||
|
||||
if self.value_residual != 0.0:
|
||||
amount_to_depr = residual_amount = self.value_residual
|
||||
if self.prorata:
|
||||
# if we already have some previous validated entries, starting date is last entry + method perio
|
||||
if posted_depreciation_line_ids and \
|
||||
posted_depreciation_line_ids[-1].depreciation_date:
|
||||
last_depreciation_date = datetime.strptime(
|
||||
posted_depreciation_line_ids[-1].depreciation_date,
|
||||
DF).date()
|
||||
depreciation_date = last_depreciation_date + relativedelta(
|
||||
months=+self.method_period)
|
||||
else:
|
||||
depreciation_date = datetime.strptime(
|
||||
str(self._get_last_depreciation_date()[self.id]),
|
||||
DF).date()
|
||||
else:
|
||||
# depreciation_date = 1st of January of purchase year if annual valuation, 1st of
|
||||
# purchase month in other cases
|
||||
if self.method_period >= 12:
|
||||
if self.company_id.fiscalyear_last_month:
|
||||
asset_date = date(year=int(self.date.year),
|
||||
month=int(
|
||||
self.company_id.fiscalyear_last_month),
|
||||
day=int(
|
||||
self.company_id.fiscalyear_last_day)) + relativedelta(
|
||||
days=1) + \
|
||||
relativedelta(year=int(
|
||||
self.date.year)) # e.g. 2018-12-31 +1 -> 2019
|
||||
else:
|
||||
asset_date = datetime.strptime(
|
||||
str(self.date)[:4] + '-01-01', DF).date()
|
||||
else:
|
||||
asset_date = datetime.strptime(str(self.date)[:7] + '-01',
|
||||
DF).date()
|
||||
# if we already have some previous validated entries, starting date isn't 1st January but last entry + method period
|
||||
if posted_depreciation_line_ids and \
|
||||
posted_depreciation_line_ids[-1].depreciation_date:
|
||||
last_depreciation_date = datetime.strptime(str(
|
||||
posted_depreciation_line_ids[-1].depreciation_date),
|
||||
DF).date()
|
||||
depreciation_date = last_depreciation_date + relativedelta(
|
||||
months=+self.method_period)
|
||||
else:
|
||||
depreciation_date = asset_date
|
||||
day = depreciation_date.day
|
||||
month = depreciation_date.month
|
||||
year = depreciation_date.year
|
||||
total_days = (year % 4) and 365 or 366
|
||||
|
||||
undone_dotation_number = self._compute_board_undone_dotation_nb(
|
||||
depreciation_date, total_days)
|
||||
|
||||
for x in range(len(posted_depreciation_line_ids),
|
||||
undone_dotation_number):
|
||||
sequence = x + 1
|
||||
amount = self._compute_board_amount(sequence, residual_amount,
|
||||
amount_to_depr,
|
||||
undone_dotation_number,
|
||||
posted_depreciation_line_ids,
|
||||
total_days,
|
||||
depreciation_date)
|
||||
|
||||
amount = self.currency_id.round(amount)
|
||||
if float_is_zero(amount,
|
||||
precision_rounding=self.currency_id.rounding):
|
||||
continue
|
||||
residual_amount -= amount
|
||||
vals = {
|
||||
'amount': amount,
|
||||
'asset_id': self.id,
|
||||
'sequence': sequence,
|
||||
'name': (self.code or '') + '/' + str(sequence),
|
||||
'remaining_value': residual_amount if residual_amount >= 0 else 0.0,
|
||||
'depreciated_value': self.value - (
|
||||
self.salvage_value + residual_amount),
|
||||
'depreciation_date': depreciation_date.strftime(DF),
|
||||
}
|
||||
commands.append((0, False, vals))
|
||||
# Considering Depr. Period as months
|
||||
depreciation_date = date(year, month, day) + relativedelta(
|
||||
months=+self.method_period)
|
||||
day = depreciation_date.day
|
||||
month = depreciation_date.month
|
||||
year = depreciation_date.year
|
||||
|
||||
self.write({'depreciation_line_ids': commands})
|
||||
last_depr_date = None
|
||||
if self.depreciation_line_ids:
|
||||
last_depr_date = max(self.depreciation_line_ids.mapped('depreciation_date'))
|
||||
if last_depr_date:
|
||||
self._compute_entries(date=last_depr_date)
|
||||
return True
|
||||
|
||||
def validate(self):
|
||||
"""Update the state to 'open' and track specific fields based on the asset's method."""
|
||||
self.write({'state': 'open'})
|
||||
field = [
|
||||
'method',
|
||||
'method_number',
|
||||
'method_period',
|
||||
'method_end',
|
||||
'method_progress_factor',
|
||||
'method_time',
|
||||
'salvage_value',
|
||||
'invoice_id',
|
||||
]
|
||||
ref_tracked_fields = self.env['account.asset.asset'].fields_get(field)
|
||||
if not self.depreciation_line_ids:
|
||||
self.compute_depreciation_board()
|
||||
for asset in self:
|
||||
tracked_fields = ref_tracked_fields.copy()
|
||||
if asset.method == 'linear':
|
||||
del (tracked_fields['method_progress_factor'])
|
||||
if asset.method_time != 'end':
|
||||
del (tracked_fields['method_end'])
|
||||
else:
|
||||
del (tracked_fields['method_number'])
|
||||
dummy, tracking_value_ids = asset._mail_track(tracked_fields,
|
||||
dict.fromkeys(
|
||||
field))
|
||||
asset.message_post(subject=_('Asset created'),
|
||||
tracking_value_ids=tracking_value_ids)
|
||||
|
||||
today_date = fields.Date.context_today(self)
|
||||
|
||||
# Split lines based on depreciation_date
|
||||
draft_lines = asset.depreciation_line_ids.filtered(lambda l: l.move_id and l.move_id.state == 'draft')
|
||||
|
||||
#Post only entries before today
|
||||
lines_to_post_now = draft_lines.filtered(lambda l: l.depreciation_date < today_date)
|
||||
moves_to_post_now = lines_to_post_now.mapped('move_id')
|
||||
if moves_to_post_now:
|
||||
moves_to_post_now._post()
|
||||
|
||||
#Set auto_post='at_date' for entries today or later
|
||||
future_lines = draft_lines.filtered(lambda l: l.depreciation_date >= today_date)
|
||||
future_moves = future_lines.mapped('move_id')
|
||||
if future_moves:
|
||||
future_moves.write({'auto_post': 'at_date'})
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def _get_disposal_moves(self):
|
||||
"""Get the disposal moves for the asset."""
|
||||
move_ids = []
|
||||
for asset in self:
|
||||
unposted_depreciation_line_ids = asset.depreciation_line_ids.filtered(
|
||||
lambda x: not x.move_check)
|
||||
if unposted_depreciation_line_ids:
|
||||
old_values = {
|
||||
'method_end': asset.method_end,
|
||||
'method_number': asset.method_number,
|
||||
}
|
||||
|
||||
# Remove all unposted depr. lines
|
||||
commands = [(2, line_id.id, False) for line_id in
|
||||
unposted_depreciation_line_ids]
|
||||
|
||||
# Create a new depr. line with the residual amount and post it
|
||||
sequence = len(asset.depreciation_line_ids) - len(
|
||||
unposted_depreciation_line_ids) + 1
|
||||
today = datetime.today().strftime(DF)
|
||||
vals = {
|
||||
'amount': asset.value_residual,
|
||||
'asset_id': asset.id,
|
||||
'sequence': sequence,
|
||||
'name': (asset.code or '') + '/' + str(sequence),
|
||||
'remaining_value': 0,
|
||||
'depreciated_value': asset.value - asset.salvage_value,
|
||||
# the asset is completely depreciated
|
||||
'depreciation_date': today,
|
||||
}
|
||||
commands.append((0, False, vals))
|
||||
asset.write(
|
||||
{'depreciation_line_ids': commands, 'method_end': today,
|
||||
'method_number': sequence})
|
||||
tracked_fields = self.env['account.asset.asset'].fields_get(
|
||||
['method_number', 'method_end'])
|
||||
changes, tracking_value_ids = asset._mail_track(
|
||||
tracked_fields, old_values)
|
||||
|
||||
if changes:
|
||||
asset.message_post(subject=_(
|
||||
'Asset sold or disposed. Accounting entry awaiting for validation.'),
|
||||
tracking_value_ids=tracking_value_ids)
|
||||
move_ids += asset.depreciation_line_ids[-1].create_move(
|
||||
post_move=False)
|
||||
|
||||
return move_ids
|
||||
|
||||
def set_to_close(self):
|
||||
"""Set the asset to close state by creating disposal moves and returning an action window to view the move(s)."""
|
||||
move_ids = self._get_disposal_moves()
|
||||
if move_ids:
|
||||
name = _('Disposal Move')
|
||||
view_mode = 'form'
|
||||
if len(move_ids) > 1:
|
||||
name = _('Disposal Moves')
|
||||
view_mode = 'list,form'
|
||||
return {
|
||||
'name': name,
|
||||
'view_mode': view_mode,
|
||||
'res_model': 'account.move',
|
||||
'type': 'ir.actions.act_window',
|
||||
'target': 'current',
|
||||
'res_id': move_ids[0],
|
||||
}
|
||||
# Fallback, as if we just clicked on the smartbutton
|
||||
return self.open_entries()
|
||||
|
||||
def set_to_draft(self):
|
||||
"""Set the asset's state to 'draft'."""
|
||||
self.write({'state': 'draft'})
|
||||
|
||||
@api.depends('value', 'salvage_value', 'depreciation_line_ids.move_check',
|
||||
'depreciation_line_ids.amount')
|
||||
def _amount_residual(self):
|
||||
"""Compute the residual value of the asset based on the total depreciation amount."""
|
||||
for record in self:
|
||||
total_amount = 0.0
|
||||
for line in record.depreciation_line_ids:
|
||||
if line.move_check:
|
||||
total_amount += line.amount
|
||||
record.value_residual = record.value - total_amount - record.salvage_value
|
||||
|
||||
@api.onchange('company_id')
|
||||
def onchange_company_id(self):
|
||||
"""Update the 'currency_id' field based on the selected 'company_id'."""
|
||||
self.currency_id = self.company_id.currency_id.id
|
||||
|
||||
@api.depends('depreciation_line_ids.move_id')
|
||||
def _entry_count(self):
|
||||
"""Compute the number of entries related to the asset based on the depreciation lines."""
|
||||
for asset in self:
|
||||
res = self.env['account.asset.depreciation.line'].search_count(
|
||||
[('asset_id', '=', asset.id), ('move_id', '!=', False)])
|
||||
asset.entry_count = res or 0
|
||||
|
||||
@api.constrains('prorata', 'method_time')
|
||||
def _check_prorata(self):
|
||||
"""Check if prorata temporis can be applied for the given asset based on the 'prorata' and 'method_time' fields."""
|
||||
if self.prorata and self.method_time != 'number':
|
||||
raise ValidationError(_(
|
||||
'Prorata temporis can be applied only for time method "number of depreciations".'))
|
||||
|
||||
@api.onchange('category_id')
|
||||
def onchange_category_id(self):
|
||||
"""Update the fields of the asset based on the selected 'category_id'."""
|
||||
vals = self.onchange_category_id_values(self.category_id.id)
|
||||
# We cannot use 'write' on an object that doesn't exist yet
|
||||
if vals:
|
||||
for k, v in vals['value'].items():
|
||||
setattr(self, k, v)
|
||||
|
||||
def onchange_category_id_values(self, category_id):
|
||||
"""Update the fields of the asset based on the selected 'category_id'."""
|
||||
if category_id:
|
||||
category = self.env['account.asset.category'].browse(category_id)
|
||||
return {
|
||||
'value': {
|
||||
'method': category.method,
|
||||
'method_number': category.method_number,
|
||||
'method_time': category.method_time,
|
||||
'method_period': category.method_period,
|
||||
'method_progress_factor': category.method_progress_factor,
|
||||
'method_end': category.method_end,
|
||||
'prorata': category.prorata,
|
||||
'journal_id':category.journal_id.id,
|
||||
'account_asset_id':category.account_asset_id.id,
|
||||
'account_depreciation_id':category.account_depreciation_id.id,
|
||||
'account_depreciation_expense_id':category.account_depreciation_expense_id.id,
|
||||
'account_analytic_id':category.account_analytic_id.id
|
||||
}
|
||||
}
|
||||
|
||||
@api.onchange('method_time')
|
||||
def onchange_method_time(self):
|
||||
"""Update the 'prorata' field based on the selected 'method_time' value."""
|
||||
if self.method_time != 'number':
|
||||
self.prorata = False
|
||||
|
||||
def copy_data(self, default=None):
|
||||
"""Copies the data of the current record with the option to override default values."""
|
||||
if default is None:
|
||||
default = {}
|
||||
default['name'] = self.name + _(' (copy)')
|
||||
return super(AccountAssetAsset, self).copy_data(default)
|
||||
|
||||
def _compute_entries(self, date, group_entries=False):
|
||||
"""Compute depreciation entries for the given date."""
|
||||
depreciation_ids = self.env['account.asset.depreciation.line'].search([
|
||||
('asset_id', 'in', self.ids), ('depreciation_date', '<=', date),
|
||||
('move_check', '=', False)])
|
||||
if group_entries:
|
||||
return depreciation_ids.create_grouped_move()
|
||||
return depreciation_ids.create_move()
|
||||
|
||||
def open_entries(self):
|
||||
"""Return a dictionary to open journal entries related to the asset."""
|
||||
move_ids = []
|
||||
for asset in self:
|
||||
for depreciation_line in asset.depreciation_line_ids:
|
||||
if depreciation_line.move_id:
|
||||
move_ids.append(depreciation_line.move_id.id)
|
||||
return {
|
||||
'name': _('Journal Entries'),
|
||||
'view_mode': 'list,form',
|
||||
'res_model': 'account.move',
|
||||
'views': [(self.env.ref('account.view_move_tree').id, 'list'), (False, 'form')],
|
||||
'view_id': False,
|
||||
'type': 'ir.actions.act_window',
|
||||
'domain': [('id', 'in', move_ids)],
|
||||
}
|
||||
|
||||
def action_save_model(self):
|
||||
return{
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Asset Model'),
|
||||
'res_model': 'account.asset.category',
|
||||
'view_mode': 'form',
|
||||
'target': 'current',
|
||||
'context': {'default_price': self.value,
|
||||
'default_method_time':self.method_time,
|
||||
'default_method_end':self.method_end,
|
||||
'default_method_number':self.method_number,
|
||||
'default_method_period':self.method_period,
|
||||
'default_method':self.method,
|
||||
'default_company_id':self.company_id.id,
|
||||
'default_method_progress_factor':self.method_progress_factor,
|
||||
'default_prorata':self.prorata,
|
||||
'default_group_entries':self.group_entries,
|
||||
'default_open_asset':self.open_asset,
|
||||
'default_account_analytic_id':self.account_analytic_id.id,
|
||||
'default_account_depreciation_expense_id':self.account_depreciation_expense_id.id,
|
||||
'default_account_depreciation_id':self.account_depreciation_id.id,
|
||||
'default_account_asset_id':self.account_asset_id.id,
|
||||
'default_journal_id':self.journal_id.id,
|
||||
'default_asset_id': self.id,
|
||||
}
|
||||
}
|
||||
|
||||
def action_cancel_assets(self):
|
||||
for asset in self:
|
||||
for move in asset.depreciation_line_ids.mapped('move_id'):
|
||||
if move.state == 'posted':
|
||||
# Force to draft
|
||||
move.button_draft() # or move.state = 'draft' if button_draft is restricted
|
||||
move.unlink()
|
||||
|
||||
# Delete all depreciation lines
|
||||
asset.depreciation_line_ids.unlink()
|
||||
|
||||
# Reset state
|
||||
asset.state = 'cancelled'
|
||||
120
addons/base_accounting_kit/models/account_asset_category.py
Normal file
@@ -0,0 +1,120 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class AccountAssetCategory(models.Model):
|
||||
_name = 'account.asset.category'
|
||||
_description = 'Asset category'
|
||||
|
||||
active = fields.Boolean(default=True)
|
||||
name = fields.Char(required=True, index=True, string="Asset Type")
|
||||
company_id = fields.Many2one('res.company', string='Company',
|
||||
required=True,
|
||||
default=lambda self: self.env.company)
|
||||
price = fields.Monetary(string='Price', required=True)
|
||||
currency_id = fields.Many2one("res.currency",
|
||||
default=lambda self: self.env[
|
||||
'res.currency'].search(
|
||||
[('name', '=', 'USD')]).id,
|
||||
readonly=True, hide=True)
|
||||
account_analytic_id = fields.Many2one('account.analytic.account',
|
||||
string='Analytic Account',
|
||||
domain="[('company_id', '=', company_id)]")
|
||||
account_asset_id = fields.Many2one('account.account',
|
||||
string='Asset Account', required=True,
|
||||
domain="[('account_type', '!=', 'asset_receivable'),('account_type', '!=', 'liability_payable'),('account_type', '!=', 'asset_cash'),('account_type', '!=', 'liability_credit_card'),('active', '=', True)]",
|
||||
help="Account used to record the purchase of the asset at its original price.")
|
||||
account_depreciation_id = fields.Many2one(
|
||||
'account.account', string='Depreciation Account',
|
||||
required=True,
|
||||
domain="[('account_type', '!=', 'asset_receivable'),('account_type', '!=', 'liability_payable'),('account_type', '!=', 'asset_cash'),('account_type', '!=', 'liability_credit_card'),('active', '=', True)]",
|
||||
help="Account used in the depreciation entries, to decrease the asset value.")
|
||||
account_depreciation_expense_id = fields.Many2one(
|
||||
'account.account', string='Expense Account',
|
||||
required=True,
|
||||
domain="[('account_type', '!=', 'asset_receivable'),('account_type', '!=','liability_payable'),('account_type', '!=', 'asset_cash'),('account_type', '!=','liability_credit_card'),('active', '=', True)]",
|
||||
help="Account used in the periodical entries, to record a part of the asset as expense.")
|
||||
journal_id = fields.Many2one('account.journal', string='Journal',
|
||||
required=True)
|
||||
method = fields.Selection(
|
||||
[('linear', 'Straight Line'), ('degressive', 'Declining')],
|
||||
string='Computation Method', required=True, default='linear',
|
||||
help="Choose the method to use to compute the amount of depreciation lines.\n"
|
||||
" * Linear: Calculated on basis of: Gross Value / Number of Depreciations\n"
|
||||
" * Degressive: Calculated on basis of: Residual Value * Degressive Factor")
|
||||
method_number = fields.Integer(string='Number of Depreciations', default=5,
|
||||
help="The number of depreciations needed to depreciate your asset")
|
||||
method_period = fields.Integer(string='Period Length', default=1,
|
||||
help="State here the time between 2 depreciations, in months",
|
||||
required=True)
|
||||
method_progress_factor = fields.Float('Degressive Factor', default=0.3)
|
||||
method_time = fields.Selection(
|
||||
[('number', 'Number of Entries'), ('end', 'Ending Date')],
|
||||
string='Time Method', required=True, default='number',
|
||||
help="Choose the method to use to compute the dates and number of entries.\n"
|
||||
" * Number of Entries: Fix the number of entries and the time between 2 depreciations.\n"
|
||||
" * Ending Date: Choose the time between 2 depreciations and the date the depreciations won't go beyond.")
|
||||
method_end = fields.Date('Ending date')
|
||||
prorata = fields.Boolean(string='Prorata Temporis',
|
||||
help='Indicates that the first depreciation entry for this asset have to be done from the purchase date instead of the first of January')
|
||||
open_asset = fields.Boolean(string='Auto-confirm Assets',
|
||||
help="Check this if you want to automatically confirm the assets of this category when created by invoices.")
|
||||
group_entries = fields.Boolean(string='Group Journal Entries',
|
||||
help="Check this if you want to group the generated entries by categories.")
|
||||
type = fields.Selection([('sale', 'Sale: Revenue Recognition'),
|
||||
('purchase', 'Purchase: Asset')], required=True,
|
||||
index=True, default='purchase')
|
||||
|
||||
@api.onchange('account_asset_id')
|
||||
def onchange_account_asset(self):
|
||||
"""Onchange method triggered when the 'account_asset_id' field is modified.
|
||||
Updates 'account_depreciation_id' or 'account_depreciation_expense_id' based on the 'type' field value."""
|
||||
if self.type == "purchase":
|
||||
self.account_depreciation_id = self.account_asset_id
|
||||
elif self.type == "sale":
|
||||
self.account_depreciation_expense_id = self.account_asset_id
|
||||
|
||||
@api.onchange('type')
|
||||
def onchange_type(self):
|
||||
"""Update the 'prorata' and 'method_period' fields based on the value of the 'type' field."""
|
||||
if self.type == 'sale':
|
||||
self.prorata = True
|
||||
self.method_period = 1
|
||||
else:
|
||||
self.method_period = 12
|
||||
|
||||
@api.onchange('method_time')
|
||||
def _onchange_method_time(self):
|
||||
"""Update the 'prorata' field based on the value of the 'method_time' field.
|
||||
Set 'prorata' to False if 'method_time' is not equal to 'number'."""
|
||||
if self.method_time != 'number':
|
||||
self.prorata = False
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
record = super().create(vals)
|
||||
asset_id = self.env.context.get('default_asset_id')
|
||||
if asset_id:
|
||||
asset = self.env['account.asset.asset'].browse(asset_id)
|
||||
asset.category_id = record.id
|
||||
return record
|
||||
@@ -0,0 +1,247 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools import float_compare
|
||||
|
||||
|
||||
class AccountAssetDepreciationLine(models.Model):
|
||||
"""Model for managing asset depreciation lines in the accounting system."""
|
||||
_name = 'account.asset.depreciation.line'
|
||||
_description = 'Asset depreciation line'
|
||||
|
||||
name = fields.Char(string='Depreciation Name', required=True, index=True)
|
||||
sequence = fields.Integer(required=True)
|
||||
asset_id = fields.Many2one('account.asset.asset', string='Asset',
|
||||
required=True, ondelete='cascade')
|
||||
parent_state = fields.Selection(related='asset_id.state',
|
||||
string='State of Asset')
|
||||
amount = fields.Float(string='Current Depreciation',
|
||||
required=True)
|
||||
remaining_value = fields.Float(string='Next Period Depreciation',
|
||||
required=True)
|
||||
depreciated_value = fields.Float(string='Cumulative Depreciation',
|
||||
required=True)
|
||||
depreciation_date = fields.Date('Depreciation Date', index=True)
|
||||
move_id = fields.Many2one('account.move', string='Depreciation Entry')
|
||||
move_check = fields.Boolean(compute='_get_move_check', string='Linked',
|
||||
store=True)
|
||||
move_posted_check = fields.Boolean(compute='_get_move_posted_check',
|
||||
string='Posted', store=True)
|
||||
|
||||
@api.depends('move_id')
|
||||
def _get_move_check(self):
|
||||
"""Compute the 'move_check' field based on the presence of 'move_id'
|
||||
for each record in the 'AccountAssetDepreciationLine' class."""
|
||||
for line in self:
|
||||
line.move_check = bool(line.move_id)
|
||||
|
||||
@api.depends('move_id.state')
|
||||
def _get_move_posted_check(self):
|
||||
"""Compute the 'move_posted_check' field based on the state of 'move_id'
|
||||
for each record in the 'AccountAssetDepreciationLine' class."""
|
||||
for line in self:
|
||||
line.move_posted_check = True if line.move_id and line.move_id.state == 'posted' else False
|
||||
|
||||
def create_move(self, post_move=True):
|
||||
"""Create accounting moves for asset depreciation lines."""
|
||||
created_moves = self.env['account.move']
|
||||
prec = self.env['decimal.precision'].precision_get('Account')
|
||||
if self.mapped('move_id'):
|
||||
raise UserError(_(
|
||||
'This depreciation is already linked to a journal entry! Please post or delete it.'))
|
||||
for line in self:
|
||||
asset_id = line.asset_id
|
||||
depreciation_date = self.env.context.get(
|
||||
'depreciation_date') or line.depreciation_date or fields.Date.context_today(
|
||||
self)
|
||||
company_currency = asset_id.company_id.currency_id
|
||||
current_currency = asset_id.currency_id
|
||||
amount = current_currency._convert(line.amount, company_currency,
|
||||
line.asset_id.company_id,
|
||||
depreciation_date)
|
||||
asset_name = line.asset_id.name + ' (%s/%s)' % (line.sequence, len(line.asset_id.depreciation_line_ids))
|
||||
partner = self.env['res.partner']._find_accounting_partner(line.asset_id.partner_id)
|
||||
move_line_1 = {
|
||||
'name': asset_name,
|
||||
'account_id': asset_id.account_depreciation_id.id,
|
||||
'debit': 0.0 if float_compare(amount, 0.0,
|
||||
precision_digits=prec) > 0 else -amount,
|
||||
'credit': amount if float_compare(amount, 0.0,
|
||||
precision_digits=prec) > 0 else 0.0,
|
||||
'journal_id': asset_id.journal_id.id,
|
||||
'partner_id': partner.id,
|
||||
'currency_id': company_currency != current_currency and current_currency.id or company_currency.id,
|
||||
'amount_currency': company_currency != current_currency and - 1.0 * line.amount or 0.0,
|
||||
}
|
||||
move_line_2 = {
|
||||
'name': asset_name,
|
||||
'account_id': asset_id.account_depreciation_expense_id.id,
|
||||
'credit': 0.0 if float_compare(amount, 0.0,
|
||||
precision_digits=prec) > 0 else -amount,
|
||||
'debit': amount if float_compare(amount, 0.0,
|
||||
precision_digits=prec) > 0 else 0.0,
|
||||
'journal_id': asset_id.journal_id.id,
|
||||
'partner_id': partner.id,
|
||||
'currency_id': company_currency != current_currency and current_currency.id or company_currency.id,
|
||||
'amount_currency': company_currency != current_currency and line.amount or 0.0,
|
||||
}
|
||||
line_ids = [(0, 0, {
|
||||
'account_id': asset_id.account_depreciation_id.id,
|
||||
'partner_id': partner.id,
|
||||
'credit': amount if float_compare(amount, 0.0,
|
||||
precision_digits=prec) > 0 else 0.0,
|
||||
}), (0, 0, {
|
||||
'account_id': asset_id.account_depreciation_expense_id.id,
|
||||
'partner_id': partner.id,
|
||||
'debit': amount if float_compare(amount, 0.0,
|
||||
precision_digits=prec) > 0 else 0.0,
|
||||
})]
|
||||
move = self.env['account.move'].create({
|
||||
'ref': line.asset_id.code,
|
||||
'date': depreciation_date or False,
|
||||
'journal_id': asset_id.journal_id.id,
|
||||
'line_ids': line_ids,
|
||||
})
|
||||
for move_line in move.line_ids:
|
||||
if move_line.account_id.id == move_line_1['account_id']:
|
||||
move_line.write({'credit': move_line_1['credit'],
|
||||
'debit': move_line_1['debit']})
|
||||
elif move_line.account_id.id == move_line_2['account_id']:
|
||||
move_line.write({'debit': move_line_2['debit'],
|
||||
'credit': move_line_2['credit']})
|
||||
if move.line_ids.filtered(
|
||||
lambda x: x.name == 'Automatic Balancing Line'):
|
||||
move.line_ids.filtered(
|
||||
lambda x: x.name == 'Automatic Balancing Line').unlink()
|
||||
line.write({'move_id': move.id, 'move_check': True})
|
||||
created_moves |= move
|
||||
|
||||
if post_move and created_moves:
|
||||
created_moves.filtered(lambda m: any(
|
||||
m.asset_depreciation_ids.mapped(
|
||||
'asset_id.open_asset'))).post()
|
||||
return [x.id for x in created_moves]
|
||||
|
||||
def create_grouped_move(self, post_move=True):
|
||||
"""Create a grouped accounting move for asset depreciation lines."""
|
||||
if not self.exists():
|
||||
return []
|
||||
created_moves = self.env['account.move']
|
||||
category_id = self[
|
||||
0].asset_id.category_id # we can suppose that all lines have the same category
|
||||
depreciation_date = self.env.context.get(
|
||||
'depreciation_date') or fields.Date.context_today(self)
|
||||
amount = 0.0
|
||||
for line in self:
|
||||
# Sum amount of all depreciation lines
|
||||
company_currency = line.asset_id.company_id.currency_id
|
||||
current_currency = line.asset_id.currency_id
|
||||
amount += current_currency.compute(line.amount, company_currency)
|
||||
|
||||
name = category_id.name + _(' (grouped)')
|
||||
move_line_1 = {
|
||||
'name': name,
|
||||
'account_id': category_id.account_depreciation_id.id,
|
||||
'debit': 0.0,
|
||||
'credit': amount,
|
||||
'journal_id': category_id.journal_id.id,
|
||||
'analytic_account_id': category_id.account_analytic_id.id if category_id.type == 'sale' else False,
|
||||
}
|
||||
move_line_2 = {
|
||||
'name': name,
|
||||
'account_id': category_id.account_depreciation_expense_id.id,
|
||||
'credit': 0.0,
|
||||
'debit': amount,
|
||||
'journal_id': category_id.journal_id.id,
|
||||
'analytic_account_id': category_id.account_analytic_id.id if category_id.type == 'purchase' else False,
|
||||
}
|
||||
move_vals = {
|
||||
'ref': category_id.name,
|
||||
'date': depreciation_date or False,
|
||||
'journal_id': category_id.journal_id.id,
|
||||
'line_ids': [(0, 0, move_line_1), (0, 0, move_line_2)],
|
||||
}
|
||||
move = self.env['account.move'].create(move_vals)
|
||||
self.write({'move_id': move.id, 'move_check': True})
|
||||
created_moves |= move
|
||||
|
||||
if post_move and created_moves:
|
||||
self.post_lines_and_close_asset()
|
||||
created_moves.post()
|
||||
return [x.id for x in created_moves]
|
||||
|
||||
def post_lines_and_close_asset(self):
|
||||
# we re-evaluate the assets to determine whether we can close them
|
||||
# `message_post` invalidates the (whole) cache
|
||||
# preprocess the assets and lines in which a message should be posted,
|
||||
# and then post in batch will prevent the re-fetch of the same data over and over.
|
||||
assets_to_close = self.env['account.asset.asset']
|
||||
for line in self:
|
||||
asset = line.asset_id
|
||||
if asset.currency_id.is_zero(asset.value_residual):
|
||||
assets_to_close |= asset
|
||||
self.log_message_when_posted()
|
||||
assets_to_close.write({'state': 'close'})
|
||||
for asset in assets_to_close:
|
||||
asset.message_post(body=_("Document closed."))
|
||||
|
||||
def log_message_when_posted(self):
|
||||
"""Format and post messages for asset depreciation lines that are posted."""
|
||||
def _format_message(message_description, tracked_values):
|
||||
message = ''
|
||||
if message_description:
|
||||
message = '<span>%s</span>' % message_description
|
||||
for name, values in tracked_values.items():
|
||||
message += '<div> • <b>%s</b>: ' % name
|
||||
message += '%s</div>' % values
|
||||
return message
|
||||
|
||||
# `message_post` invalidates the (whole) cache
|
||||
# preprocess the assets in which messages should be posted,
|
||||
# and then post in batch will prevent the re-fetch of the same data over and over.
|
||||
assets_to_post = {}
|
||||
for line in self:
|
||||
if line.move_id and line.move_id.state == 'draft':
|
||||
partner_name = line.asset_id.partner_id.name
|
||||
currency_name = line.asset_id.currency_id.name
|
||||
msg_values = {_('Currency'): currency_name,
|
||||
_('Amount'): line.amount}
|
||||
if partner_name:
|
||||
msg_values[_('Partner')] = partner_name
|
||||
msg = _format_message(_('Depreciation line posted.'),
|
||||
msg_values)
|
||||
assets_to_post.setdefault(line.asset_id, []).append(msg)
|
||||
for asset, messages in assets_to_post.items():
|
||||
for msg in messages:
|
||||
asset.message_post(body=msg)
|
||||
|
||||
# def unlink(self):
|
||||
# """Check if the depreciation line is linked to a posted move before deletion."""
|
||||
# for record in self:
|
||||
# if record.move_check:
|
||||
# if record.asset_id.category_id.type == 'purchase':
|
||||
# msg = _("You cannot delete posted depreciation lines.")
|
||||
# else:
|
||||
# msg = _("You cannot delete posted installment lines.")
|
||||
# raise UserError(msg)
|
||||
# return super(AccountAssetDepreciationLine, self).unlink()
|
||||
170
addons/base_accounting_kit/models/account_bank_statement_line.py
Normal file
@@ -0,0 +1,170 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from odoo import api, fields, models
|
||||
from odoo.http import request
|
||||
|
||||
|
||||
class AccountBankStatementLine(models.Model):
|
||||
"""Update the 'rowdata' field for the specified record."""
|
||||
_name = 'account.bank.statement.line'
|
||||
_inherit = ['account.bank.statement.line', 'mail.thread',
|
||||
'mail.activity.mixin', 'analytic.mixin']
|
||||
|
||||
lines_widget = fields.Char(string="Lines Widget")
|
||||
account_id = fields.Many2one('account.account', string='Account')
|
||||
tax_ids = fields.Many2many('account.tax')
|
||||
form_name = fields.Char()
|
||||
form_balance = fields.Monetary(currency_field='currency_id')
|
||||
rowdata = fields.Json(string="RowData")
|
||||
matchRowdata = fields.Json(string="MatchRowData")
|
||||
record_id = fields.Integer()
|
||||
company_currency_id = fields.Many2one(
|
||||
related='company_id.currency_id', readonly=True,
|
||||
)
|
||||
bank_state = fields.Selection(selection=[('invalid', 'Invalid'),
|
||||
('valid', 'Valid'),
|
||||
('reconciled', 'Reconciled')],
|
||||
compute='_compute_state', store=True)
|
||||
reconcile_models_widget = fields.Char()
|
||||
lines_widget_json = fields.Json(store=True)
|
||||
|
||||
@api.model
|
||||
def update_rowdata(self, record_id):
|
||||
"""Update the 'rowdata' field for the specified record."""
|
||||
request.session['record_id'] = record_id
|
||||
|
||||
@api.model
|
||||
def update_match_row_data(self, resId):
|
||||
"""Update the match row data for a specific record identified by the given resId."""
|
||||
request.session['resId'] = resId
|
||||
move_record = self.env['account.move.line'].browse(resId)
|
||||
move_record_values = {
|
||||
'id': move_record.id,
|
||||
'account_id': move_record.account_id.id,
|
||||
'account_name': move_record.account_id.name,
|
||||
'account_code': move_record.account_id.code,
|
||||
'partner_id': move_record.partner_id,
|
||||
'partner_name': move_record.partner_id.name,
|
||||
'date': move_record.date,
|
||||
'move_id': move_record.move_id,
|
||||
'move_name': move_record.move_id.name,
|
||||
'name': move_record.name,
|
||||
'amount_residual_currency': move_record.amount_residual_currency,
|
||||
'amount_residual': move_record.amount_residual,
|
||||
'currency_id': move_record.currency_id.id,
|
||||
'currency_symbol': move_record.currency_id.symbol
|
||||
}
|
||||
return move_record_values
|
||||
|
||||
def button_validation(self, async_action=False):
|
||||
"""Ensure the current recordset holds a single record and mark it as reconciled."""
|
||||
self.ensure_one()
|
||||
self.is_reconciled = True
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'reload',
|
||||
}
|
||||
|
||||
def button_reset(self):
|
||||
"""Reset the current bank statement line if it is in a 'reconciled' state."""
|
||||
self.ensure_one()
|
||||
if self.bank_state == 'reconciled':
|
||||
self.action_undo_reconciliation()
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'reload',
|
||||
}
|
||||
|
||||
def button_to_check(self, async_action=True):
|
||||
"""Ensure the current recordset holds a single record, validate the bank
|
||||
state, and mark the move as 'to check'."""
|
||||
self.ensure_one()
|
||||
if self.bank_state == 'valid':
|
||||
self.button_validation(async_action=async_action)
|
||||
self.move_id.to_check = True
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'reload',
|
||||
}
|
||||
|
||||
def button_set_as_checked(self):
|
||||
"""Mark the associated move as 'not to check' by setting 'to_check' to False."""
|
||||
self.ensure_one()
|
||||
self.move_id.to_check = False
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'reload',
|
||||
}
|
||||
|
||||
@api.model
|
||||
def get_statement_line(self, record_id):
|
||||
"""Retrieve and format bank statement line details based on the provided record ID."""
|
||||
statement_line_records = self.env[
|
||||
'account.bank.statement.line'].search_read([('id', '=', record_id)])
|
||||
result_list = []
|
||||
for record in statement_line_records:
|
||||
move_id = record.get('move_id', False)
|
||||
partner_id = record.get('partner_id', False)
|
||||
date = record.get('date', False)
|
||||
amount = record.get('amount', False)
|
||||
currency_id = record.get('currency_id', False)
|
||||
payment_ref = record.get("payment_ref", False)
|
||||
bank_state = record.get("bank_state", False)
|
||||
id = record.get("id", False)
|
||||
if move_id:
|
||||
move_record = self.env['account.move.line'].search(
|
||||
[('move_id', '=', move_id[0])], limit=1)
|
||||
currency_symbol = self.env['res.currency'].browse(
|
||||
currency_id[0])
|
||||
account_id = move_record.account_id
|
||||
date_str = date.strftime('%Y-%m-%d') if date else None
|
||||
result_list.append({
|
||||
'id': id,
|
||||
'move_id': move_id,
|
||||
'partner_id': partner_id,
|
||||
'account_id': account_id.id,
|
||||
'account_name': account_id.name,
|
||||
'account_code': account_id.code,
|
||||
'date': date_str,
|
||||
'amount': amount,
|
||||
'currency_symbol': currency_symbol.symbol,
|
||||
'payment_ref': payment_ref,
|
||||
'bank_state': bank_state,
|
||||
})
|
||||
# Update the account_id for the current record
|
||||
self.env['account.bank.statement.line'].browse(
|
||||
record['id']).write({'account_id': account_id.id})
|
||||
return result_list
|
||||
|
||||
@api.depends('account_id')
|
||||
def _compute_state(self):
|
||||
"""Compute the state of bank transactions based on the account's
|
||||
reconciliation status and journal settings."""
|
||||
for record in self:
|
||||
if record.is_reconciled:
|
||||
record.bank_state = 'reconciled'
|
||||
else:
|
||||
suspense_account = record.journal_id.suspense_account_id
|
||||
if suspense_account in record.account_id:
|
||||
record.bank_state = 'invalid'
|
||||
else:
|
||||
record.bank_state = 'valid'
|
||||
35
addons/base_accounting_kit/models/account_followup.py
Normal file
@@ -0,0 +1,35 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class Followup(models.Model):
|
||||
"""Model for managing account follow-ups."""
|
||||
_name = 'account.followup'
|
||||
_description = 'Account Follow-up'
|
||||
_rec_name = 'name'
|
||||
|
||||
followup_line_ids = fields.One2many('followup.line', 'followup_id',
|
||||
'Follow-up', copy=True)
|
||||
company_id = fields.Many2one('res.company', 'Company',
|
||||
default=lambda self: self.env.company)
|
||||
name = fields.Char(related='company_id.name', readonly=True)
|
||||
119
addons/base_accounting_kit/models/account_journal.py
Normal file
@@ -0,0 +1,119 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from odoo import fields, models, _
|
||||
|
||||
|
||||
class AccountJournal(models.Model):
|
||||
"""Module inherited for adding the reconcile method in the account
|
||||
journal"""
|
||||
_inherit = "account.journal"
|
||||
|
||||
multiple_invoice_ids = fields.One2many('multiple.invoice',
|
||||
'journal_id',
|
||||
string='Multiple Invoice')
|
||||
multiple_invoice_type = fields.Selection(
|
||||
[('text', 'Text'), ('watermark', 'Watermark')], required=True,
|
||||
default='text', string="Display Type")
|
||||
text_position = fields.Selection([
|
||||
('header', 'Header'),
|
||||
('footer', 'Footer'),
|
||||
('body', 'Document Body')
|
||||
], required=True, default='header', string='Text Position')
|
||||
body_text_position = fields.Selection([
|
||||
('tl', 'Top Left'),
|
||||
('tr', 'Top Right'),
|
||||
('bl', 'Bottom Left'),
|
||||
('br', 'Bottom Right'),
|
||||
], default='tl', string='Body Text Position')
|
||||
text_align = fields.Selection([
|
||||
('right', 'Right'),
|
||||
('left', 'Left'),
|
||||
('center', 'Center'),
|
||||
], default='right', string='Center Align Text Position')
|
||||
layout = fields.Char(string="Layout",
|
||||
related="company_id.external_report_layout_id.key")
|
||||
|
||||
def action_open_reconcile(self):
|
||||
"""Open the reconciliation view based on the type of the account journal."""
|
||||
self.ensure_one()
|
||||
if self.type in ('bank', 'cash'):
|
||||
views = [
|
||||
(self.env.ref(
|
||||
'base_accounting_kit.account_bank_statement_line_view_kanban').id,
|
||||
'kanban'),
|
||||
(self.env.ref(
|
||||
'base_accounting_kit.account_bank_statement_line_view_tree').id,
|
||||
'list'), # Include tree view
|
||||
]
|
||||
context = {
|
||||
'default_journal_id': self.id,
|
||||
'search_default_journal_id': self.id,
|
||||
}
|
||||
kanban_first = True
|
||||
name = None
|
||||
extra_domain = None
|
||||
return {
|
||||
'name': name or _("Bank Reconciliation"),
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'account.bank.statement.line',
|
||||
'context': context,
|
||||
'search_view_id': [
|
||||
self.env.ref(
|
||||
'base_accounting_kit.account_bank_statement_line_view_search').id,
|
||||
'search'],
|
||||
'view_mode': 'kanban,list' if kanban_first else 'list,kanban',
|
||||
'views': views if kanban_first else views[::-1],
|
||||
'domain': [('state', '!=', 'cancel')] + (extra_domain or []),
|
||||
'help': _("""
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Nothing to do here!
|
||||
</p>
|
||||
<p>
|
||||
No transactions matching your filters were found.
|
||||
</p>
|
||||
"""),
|
||||
}
|
||||
else:
|
||||
# Open reconciliation view for customers/suppliers
|
||||
action_context = {'show_mode_selector': False,
|
||||
'company_ids': self.mapped('company_id').ids}
|
||||
if self.type == 'sale':
|
||||
action_context.update({'mode': 'customers'})
|
||||
elif self.type == 'purchase':
|
||||
action_context.update({'mode': 'suppliers'})
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'manual_reconciliation_view',
|
||||
'context': action_context,
|
||||
}
|
||||
|
||||
def action_import_wizard(self):
|
||||
"""Function to open wizard"""
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'view_mode': 'form',
|
||||
'res_model': 'import.bank.statement',
|
||||
'target': 'new',
|
||||
'context': {
|
||||
'default_journal_id': self.id,
|
||||
}
|
||||
}
|
||||
115
addons/base_accounting_kit/models/account_move.py
Normal file
@@ -0,0 +1,115 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class AccountMove(models.Model):
|
||||
"""Inherits from the account.move model for adding the depreciation
|
||||
field to the account"""
|
||||
_inherit = 'account.move'
|
||||
|
||||
has_due = fields.Boolean(string='Has due')
|
||||
is_warning = fields.Boolean(string='Is warning')
|
||||
due_amount = fields.Float(string="Due Amount",
|
||||
related='partner_id.due_amount')
|
||||
recurring_ref = fields.Char(string='Recurring Ref')
|
||||
asset_depreciation_ids = fields.One2many('account.asset.depreciation.line',
|
||||
'move_id',
|
||||
string='Assets Depreciation Lines')
|
||||
to_check = fields.Boolean(string='To Check', tracking=True,
|
||||
help="If this checkbox is ticked, it means that the user was not sure of all the related "
|
||||
"information at the time of the creation of the move and that the move needs to be "
|
||||
"checked again.",
|
||||
)
|
||||
|
||||
def button_cancel(self):
|
||||
"""Button action to cancel the transfer"""
|
||||
for move in self:
|
||||
for line in move.asset_depreciation_ids:
|
||||
line.move_posted_check = False
|
||||
return super(AccountMove, self).button_cancel()
|
||||
|
||||
def post(self):
|
||||
"""Supering the post method to mapped the asset depreciation records"""
|
||||
self.mapped('asset_depreciation_ids').post_lines_and_close_asset()
|
||||
return super(AccountMove, self).action_post()
|
||||
|
||||
@api.model
|
||||
def _refund_cleanup_lines(self, lines):
|
||||
"""Supering the refund cleanup lines to check the asset category """
|
||||
result = super(AccountMove, self)._refund_cleanup_lines(lines)
|
||||
for i, line in enumerate(lines):
|
||||
for name, field in line._fields.items():
|
||||
if name == 'asset_category_id':
|
||||
result[i][2][name] = False
|
||||
break
|
||||
return result
|
||||
|
||||
def action_cancel(self):
|
||||
"""Action perform to cancel the asset record"""
|
||||
res = super(AccountMove, self).action_cancel()
|
||||
self.env['account.asset.asset'].sudo().search(
|
||||
[('invoice_id', 'in', self.ids)]).write({'active': False})
|
||||
return res
|
||||
|
||||
def action_post(self):
|
||||
"""To check the selected customers due amount is exceed than blocking stage"""
|
||||
pay_type = ['out_invoice', 'out_refund', 'out_receipt']
|
||||
for rec in self:
|
||||
if rec.partner_id.active_limit and rec.move_type in pay_type \
|
||||
and rec.partner_id.enable_credit_limit:
|
||||
if rec.due_amount >= rec.partner_id.blocking_stage and rec.partner_id.blocking_stage != 0:
|
||||
raise UserError(_(
|
||||
"%s is in Blocking Stage and "
|
||||
"has a due amount of %s %s to pay") % (
|
||||
rec.partner_id.name, rec.due_amount,
|
||||
rec.currency_id.symbol))
|
||||
|
||||
result = super(AccountMove, self).action_post()
|
||||
for inv in self:
|
||||
context = dict(self.env.context)
|
||||
# Within the context of an invoice,
|
||||
# this default value is for the type of the invoice, not the type
|
||||
# of the asset. This has to be cleaned from the context before
|
||||
# creating the asset,otherwise it tries to create the asset with
|
||||
# the type of the invoice.
|
||||
context.pop('default_type', None)
|
||||
inv.invoice_line_ids.with_context(context).asset_create()
|
||||
return result
|
||||
|
||||
@api.onchange('partner_id')
|
||||
def check_due(self):
|
||||
"""To show the due amount and warning stage"""
|
||||
if self.partner_id and self.partner_id.due_amount > 0 \
|
||||
and self.partner_id.active_limit \
|
||||
and self.partner_id.enable_credit_limit:
|
||||
self.has_due = True
|
||||
else:
|
||||
self.has_due = False
|
||||
if self.partner_id and self.partner_id.active_limit \
|
||||
and self.partner_id.enable_credit_limit:
|
||||
if self.due_amount >= self.partner_id.warning_stage:
|
||||
if self.partner_id.warning_stage != 0:
|
||||
self.is_warning = True
|
||||
else:
|
||||
self.is_warning = False
|
||||
211
addons/base_accounting_kit/models/account_move_line.py
Normal file
@@ -0,0 +1,211 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
import ast
|
||||
from datetime import datetime
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT as DF
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
|
||||
class AccountInvoiceLine(models.Model):
|
||||
"""Define a model for account invoice lines with fields related to assets and their management."""
|
||||
_inherit = 'account.move.line'
|
||||
|
||||
asset_category_id = fields.Many2one('account.asset.category',
|
||||
string='Asset Category')
|
||||
asset_start_date = fields.Date(string='Asset Start Date',
|
||||
compute='_get_asset_date', readonly=True,
|
||||
store=True)
|
||||
asset_end_date = fields.Date(string='Asset End Date',
|
||||
compute='_get_asset_date', readonly=True,
|
||||
store=True)
|
||||
asset_mrr = fields.Float(string='Monthly Recurring Revenue',
|
||||
compute='_get_asset_date',
|
||||
readonly=True, digits='Account',
|
||||
store=True)
|
||||
|
||||
@api.depends('asset_category_id', 'move_id.invoice_date')
|
||||
def _get_asset_date(self):
|
||||
"""Returns the asset_start_date and the asset_end_date of the Asset"""
|
||||
for record in self:
|
||||
record.asset_mrr = 0
|
||||
record.asset_start_date = False
|
||||
record.asset_end_date = False
|
||||
cat = record.asset_category_id
|
||||
if cat:
|
||||
if cat.method_number == 0 or cat.method_period == 0:
|
||||
raise UserError(_(
|
||||
'The number of depreciations or the period length of '
|
||||
'your asset category cannot be null.'))
|
||||
months = cat.method_number * cat.method_period
|
||||
if record.move_id in ['out_invoice', 'out_refund']:
|
||||
record.asset_mrr = record.price_subtotal_signed / months
|
||||
if record.move_id.invoice_date:
|
||||
start_date = datetime.strptime(
|
||||
str(record.move_id.invoice_date), DF).replace(day=1)
|
||||
end_date = (start_date + relativedelta(months=months,
|
||||
days=-1))
|
||||
record.asset_start_date = start_date.strftime(DF)
|
||||
record.asset_end_date = end_date.strftime(DF)
|
||||
|
||||
def asset_create(self):
|
||||
"""Create function for the asset and its associated properties"""
|
||||
for record in self:
|
||||
if record.asset_category_id:
|
||||
vals = {
|
||||
'name': record.name,
|
||||
'code': record.move_id.name or False,
|
||||
'category_id': record.asset_category_id.id,
|
||||
'value': record.price_subtotal,
|
||||
'partner_id': record.partner_id.id,
|
||||
'company_id': record.move_id.company_id.id,
|
||||
'currency_id': record.move_id.company_currency_id.id,
|
||||
'date': record.move_id.invoice_date,
|
||||
'invoice_id': record.move_id.id,
|
||||
}
|
||||
changed_vals = record.env[
|
||||
'account.asset.asset'].onchange_category_id_values(
|
||||
vals['category_id'])
|
||||
vals.update(changed_vals['value'])
|
||||
asset = record.env['account.asset.asset'].create(vals)
|
||||
if record.asset_category_id.open_asset:
|
||||
asset.validate()
|
||||
return True
|
||||
|
||||
@api.depends('asset_category_id')
|
||||
def onchange_asset_category_id(self):
|
||||
"""On change function based on the category and its updates the
|
||||
account status"""
|
||||
if self.move_id.move_type == 'out_invoice' and self.asset_category_id:
|
||||
self.account_id = self.asset_category_id.account_asset_id.id
|
||||
elif self.move_id.move_type == 'in_invoice' and self.asset_category_id:
|
||||
self.account_id = self.asset_category_id.account_asset_id.id
|
||||
|
||||
@api.onchange('product_id')
|
||||
def _onchange_uom_id(self):
|
||||
"""Onchange function for product that's call the UOM compute function
|
||||
and the asset category function"""
|
||||
result = super(AccountInvoiceLine, self)._compute_product_uom_id()
|
||||
self.onchange_asset_category_id()
|
||||
return result
|
||||
|
||||
@api.depends('product_id')
|
||||
def _onchange_product_id(self):
|
||||
"""Onchange product values and it's associated with the move types"""
|
||||
vals = super(AccountInvoiceLine, self)._compute_price_unit()
|
||||
if self.product_id:
|
||||
if self.move_id.move_type == 'out_invoice':
|
||||
self.asset_category_id = self.product_id.product_tmpl_id.deferred_revenue_category_id
|
||||
elif self.move_id.move_type == 'in_invoice':
|
||||
self.asset_category_id = self.product_id.product_tmpl_id.asset_category_id
|
||||
return vals
|
||||
|
||||
def _set_additional_fields(self, invoice):
|
||||
"""The function adds additional fields that based on the invoice
|
||||
move types"""
|
||||
if not self.asset_category_id:
|
||||
if invoice.type == 'out_invoice':
|
||||
self.asset_category_id = self.product_id.product_tmpl_id.deferred_revenue_category_id.id
|
||||
elif invoice.type == 'in_invoice':
|
||||
self.asset_category_id = self.product_id.product_tmpl_id.asset_category_id.id
|
||||
self.onchange_asset_category_id()
|
||||
super(AccountInvoiceLine, self)._set_additional_fields(invoice)
|
||||
|
||||
def get_invoice_line_account(self, type, product, fpos, company):
|
||||
""""It returns the invoice line and callback"""
|
||||
return product.asset_category_id.account_asset_id or super(
|
||||
AccountInvoiceLine, self).get_invoice_line_account(type, product,
|
||||
fpos, company)
|
||||
|
||||
@api.model
|
||||
def _query_get(self, domain=None):
|
||||
"""Used to add domain constraints to the query"""
|
||||
self.check_access_rights('read')
|
||||
|
||||
context = dict(self._context or {})
|
||||
domain = domain or []
|
||||
if not isinstance(domain, (list, tuple)):
|
||||
domain = ast.literal_eval(domain)
|
||||
|
||||
date_field = 'date'
|
||||
if context.get('aged_balance'):
|
||||
date_field = 'date_maturity'
|
||||
if context.get('date_to'):
|
||||
domain += [(date_field, '<=', context['date_to'])]
|
||||
if context.get('date_from'):
|
||||
if not context.get('strict_range'):
|
||||
domain += ['|', (date_field, '>=', context['date_from']),
|
||||
('account_id.include_initial_balance', '=', True)]
|
||||
elif context.get('initial_bal'):
|
||||
domain += [(date_field, '<', context['date_from'])]
|
||||
else:
|
||||
domain += [(date_field, '>=', context['date_from'])]
|
||||
|
||||
if context.get('journal_ids'):
|
||||
domain += [('journal_id', 'in', context['journal_ids'])]
|
||||
|
||||
state = context.get('state')
|
||||
if state and state.lower() != 'all':
|
||||
domain += [('parent_state', '=', state)]
|
||||
|
||||
if context.get('company_id'):
|
||||
domain += [('company_id', '=', context['company_id'])]
|
||||
elif context.get('allowed_company_ids'):
|
||||
domain += [('company_id', 'in', self.env.companies.ids)]
|
||||
else:
|
||||
domain += [('company_id', '=', self.env.company.id)]
|
||||
|
||||
if context.get('reconcile_date'):
|
||||
domain += ['|', ('reconciled', '=', False), '|',
|
||||
('matched_debit_ids.max_date', '>', context['reconcile_date']),
|
||||
('matched_credit_ids.max_date', '>', context['reconcile_date'])]
|
||||
|
||||
if context.get('account_tag_ids'):
|
||||
domain += [('account_id.tag_ids', 'in', context['account_tag_ids'].ids)]
|
||||
|
||||
if context.get('account_ids'):
|
||||
domain += [('account_id', 'in', context['account_ids'].ids)]
|
||||
|
||||
if context.get('analytic_tag_ids'):
|
||||
domain += [('analytic_tag_ids', 'in', context['analytic_tag_ids'].ids)]
|
||||
|
||||
if context.get('analytic_account_ids'):
|
||||
domain += [('analytic_account_id', 'in', context['analytic_account_ids'].ids)]
|
||||
|
||||
if context.get('partner_ids'):
|
||||
domain += [('partner_id', 'in', context['partner_ids'].ids)]
|
||||
|
||||
if context.get('partner_categories'):
|
||||
domain += [('partner_id.category_id', 'in', context['partner_categories'].ids)]
|
||||
|
||||
where_clause = ""
|
||||
where_clause_params = []
|
||||
tables = ''
|
||||
if domain:
|
||||
domain.append(('display_type', 'not in', ('line_section', 'line_note')))
|
||||
domain.append(('parent_state', '!=', 'cancel'))
|
||||
query = self._search(domain, bypass_access=True)
|
||||
tables, from_params = query.from_clause
|
||||
where_clause, where_params = query.where_clause
|
||||
where_clause_params = from_params + where_params
|
||||
return tables, where_clause, where_clause_params
|
||||
200
addons/base_accounting_kit/models/account_payment.py
Normal file
@@ -0,0 +1,200 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from odoo import fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class AccountRegisterPayments(models.TransientModel):
|
||||
"""Inherits the account.payment.register model to add the new
|
||||
fields and functions"""
|
||||
_inherit = "account.payment.register"
|
||||
|
||||
bank_reference = fields.Char(string="Bank Reference", copy=False)
|
||||
cheque_reference = fields.Char(string="Cheque Reference", copy=False)
|
||||
effective_date = fields.Date('Effective Date',
|
||||
help='Effective date of PDC', copy=False,
|
||||
default=False)
|
||||
|
||||
def _prepare_payment_vals(self, invoices):
|
||||
"""Its prepare the payment values for the invoice and update
|
||||
the MultiPayment"""
|
||||
res = super(AccountRegisterPayments, self)._prepare_payment_vals(
|
||||
invoices)
|
||||
# Check payment method is Check or PDC
|
||||
check_pdc_ids = self.env['account.payment.method'].search(
|
||||
[('code', 'in', ['pdc', 'check_printing'])])
|
||||
if self.payment_method_id.id in check_pdc_ids.ids:
|
||||
currency_id = self.env['res.currency'].browse(res['currency_id'])
|
||||
journal_id = self.env['account.journal'].browse(res['journal_id'])
|
||||
# Updating values in case of Multi payments
|
||||
res.update({
|
||||
'bank_reference': self.bank_reference,
|
||||
'cheque_reference': self.cheque_reference,
|
||||
'check_manual_sequencing': journal_id.check_manual_sequencing,
|
||||
'effective_date': self.effective_date,
|
||||
'check_amount_in_words': currency_id.amount_to_text(
|
||||
res['amount']),
|
||||
})
|
||||
return res
|
||||
|
||||
def _create_payment_vals_from_wizard(self, batch_result):
|
||||
"""It super the wizard action of the create payment values and update
|
||||
the bank and cheque values"""
|
||||
res = super(AccountRegisterPayments,
|
||||
self)._create_payment_vals_from_wizard(
|
||||
batch_result)
|
||||
if self.effective_date:
|
||||
res.update({
|
||||
'bank_reference': self.bank_reference,
|
||||
'cheque_reference': self.cheque_reference,
|
||||
'effective_date': self.effective_date,
|
||||
})
|
||||
return res
|
||||
|
||||
def _create_payment_vals_from_batch(self, batch_result):
|
||||
"""It super the batch action of the create payment values and update
|
||||
the bank and cheque values"""
|
||||
res = super(AccountRegisterPayments,
|
||||
self)._create_payment_vals_from_batch(
|
||||
batch_result)
|
||||
if self.effective_date:
|
||||
res.update({
|
||||
'bank_reference': self.bank_reference,
|
||||
'cheque_reference': self.cheque_reference,
|
||||
'effective_date': self.effective_date,
|
||||
})
|
||||
return res
|
||||
|
||||
def _create_payments(self):
|
||||
"""USed to create a list of payments and update the bank and
|
||||
cheque reference"""
|
||||
payments = super(AccountRegisterPayments, self)._create_payments()
|
||||
|
||||
for payment in payments:
|
||||
payment.write({
|
||||
'bank_reference': self.bank_reference,
|
||||
'cheque_reference': self.cheque_reference
|
||||
})
|
||||
return payments
|
||||
|
||||
|
||||
class AccountPayment(models.Model):
|
||||
"""It inherits the account.payment model for adding new fields
|
||||
and functions"""
|
||||
_inherit = "account.payment"
|
||||
|
||||
bank_reference = fields.Char(string="Bank Reference", copy=False)
|
||||
cheque_reference = fields.Char(string="Cheque Reference",copy=False)
|
||||
effective_date = fields.Date('Effective Date',
|
||||
help='Effective date of PDC', copy=False,
|
||||
default=False)
|
||||
|
||||
def open_payment_matching_screen(self):
|
||||
"""Open reconciliation view for customers/suppliers"""
|
||||
move_line_id = False
|
||||
for move_line in self.line_ids:
|
||||
if move_line.account_id.reconcile:
|
||||
move_line_id = move_line.id
|
||||
break
|
||||
if not self.partner_id:
|
||||
raise UserError(_("Payments without a customer can't be matched"))
|
||||
action_context = {'company_ids': [self.company_id.id], 'partner_ids': [
|
||||
self.partner_id.commercial_partner_id.id]}
|
||||
if self.partner_type == 'customer':
|
||||
action_context.update({'mode': 'customers'})
|
||||
elif self.partner_type == 'supplier':
|
||||
action_context.update({'mode': 'suppliers'})
|
||||
if move_line_id:
|
||||
action_context.update({'move_line_id': move_line_id})
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'manual_reconciliation_view',
|
||||
'context': action_context,
|
||||
}
|
||||
|
||||
def print_checks(self):
|
||||
""" Check that the recordset is valid, set the payments state to
|
||||
sent and call print_checks() """
|
||||
# Since this method can be called via a client_action_multi, we
|
||||
# need to make sure the received records are what we expect
|
||||
selfs = self.filtered(lambda r:
|
||||
r.payment_method_id.code
|
||||
in ['check_printing', 'pdc']
|
||||
and r.state != 'reconciled')
|
||||
if len(selfs) == 0:
|
||||
raise UserError(_(
|
||||
"Payments to print as a checks must have 'Check' "
|
||||
"or 'PDC' selected as payment method and "
|
||||
"not have already been reconciled"))
|
||||
if any(payment.journal_id != selfs[0].journal_id for payment in selfs):
|
||||
raise UserError(_(
|
||||
"In order to print multiple checks at once, they "
|
||||
"must belong to the same bank journal."))
|
||||
|
||||
if not selfs[0].journal_id.check_manual_sequencing:
|
||||
# The wizard asks for the number printed on the first
|
||||
# pre-printed check so payments are attributed the
|
||||
# number of the check the'll be printed on.
|
||||
last_printed_check = selfs.search([
|
||||
('journal_id', '=', selfs[0].journal_id.id),
|
||||
('check_number', '!=', "0")], order="check_number desc",
|
||||
limit=1)
|
||||
next_check_number = last_printed_check and int(
|
||||
last_printed_check.check_number) + 1 or 1
|
||||
return {
|
||||
'name': _('Print Pre-numbered Checks'),
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'print.prenumbered.checks',
|
||||
'view_mode': 'form',
|
||||
'target': 'new',
|
||||
'context': {
|
||||
'payment_ids': self.ids,
|
||||
'default_next_check_number': next_check_number,
|
||||
}
|
||||
}
|
||||
else:
|
||||
self.filtered(lambda r: r.state == 'draft').post()
|
||||
self.write({'state': 'sent'})
|
||||
return self.do_print_checks()
|
||||
|
||||
def _prepare_payment_moves(self):
|
||||
""" supered function to set effective date """
|
||||
res = super(AccountPayment, self)._prepare_payment_moves()
|
||||
inbound_pdc_id = self.env.ref(
|
||||
'base_accounting_kit.account_payment_method_pdc_in').id
|
||||
outbound_pdc_id = self.env.ref(
|
||||
'base_accounting_kit.account_payment_method_pdc_out').id
|
||||
if self.payment_method_id.id == inbound_pdc_id or \
|
||||
self.payment_method_id.id == outbound_pdc_id \
|
||||
and self.effective_date:
|
||||
res[0]['date'] = self.effective_date
|
||||
for line in res[0]['line_ids']:
|
||||
line[2]['date_maturity'] = self.effective_date
|
||||
return res
|
||||
|
||||
def mark_as_sent(self):
|
||||
"""Updates the is_move_sent value of the payment model"""
|
||||
self.write({'is_sent': True})
|
||||
|
||||
def unmark_as_sent(self):
|
||||
"""Updates the is_move_sent value of the payment model"""
|
||||
self.write({'is_sent': False})
|
||||
35
addons/base_accounting_kit/models/account_payment_method.py
Normal file
@@ -0,0 +1,35 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class AccountPaymentMethod(models.Model):
|
||||
"""The class inherits the account payment method for supering the
|
||||
_get_payment_method_information function"""
|
||||
_inherit = "account.payment.method"
|
||||
|
||||
@api.model
|
||||
def _get_payment_method_information(self):
|
||||
"""Super the function to update the pdc values"""
|
||||
res = super()._get_payment_method_information()
|
||||
res['pdc'] = {'mode': 'multi', 'domain': [('type', '=', 'bank')]}
|
||||
return res
|
||||
@@ -0,0 +1,33 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class GetAllRecurringEntries(models.TransientModel):
|
||||
"""Model for managing account recurring entries lines."""
|
||||
_name = 'account.recurring.entries.line'
|
||||
_description = 'Account Recurring Entries Line'
|
||||
|
||||
date = fields.Date('Date')
|
||||
template_name = fields.Char('Name')
|
||||
amount = fields.Float('Amount')
|
||||
tmpl_id = fields.Many2one('account.recurring.payments', string='id')
|
||||
81
addons/base_accounting_kit/models/account_report.py
Normal file
@@ -0,0 +1,81 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from odoo import api, fields, models
|
||||
from odoo.tools import get_lang
|
||||
|
||||
|
||||
class AccountCommonReport(models.Model):
|
||||
"""Inherits the Account report model to add special fields and functions"""
|
||||
_inherit = "account.report"
|
||||
_description = "Account Common Report"
|
||||
|
||||
company_id = fields.Many2one('res.company', string='Company',
|
||||
required=True, readonly=True,
|
||||
default=lambda self: self.env.company)
|
||||
journal_ids = fields.Many2many(
|
||||
comodel_name='account.journal',
|
||||
string='Journals',
|
||||
required=True,
|
||||
default=lambda self: self.env['account.journal'].search([('company_id', '=', self.company_id.id)]),
|
||||
domain="[('company_id', '=', company_id)]")
|
||||
date_from = fields.Date(string='Start Date')
|
||||
date_to = fields.Date(string='End Date')
|
||||
target_move = fields.Selection([('posted', 'All Posted Entries'),
|
||||
('all', 'All Entries'),
|
||||
], string='Target Moves',
|
||||
required=True, default='posted')
|
||||
|
||||
@api.onchange('company_id')
|
||||
def _onchange_company_id(self):
|
||||
"""Onchange function based on the company and updated the journals"""
|
||||
if self.company_id:
|
||||
self.journal_ids = self.env['account.journal'].search(
|
||||
[('company_id', '=', self.company_id.id)])
|
||||
else:
|
||||
self.journal_ids = self.env['account.journal'].search([])
|
||||
|
||||
def _build_contexts(self, data):
|
||||
"""Builds the context information for the given data"""
|
||||
result = {}
|
||||
result['journal_ids'] = 'journal_ids' in data['form'] and data['form']['journal_ids'] or False
|
||||
result['state'] = 'target_move' in data['form'] and data['form']['target_move'] or ''
|
||||
result['date_from'] = data['form']['date_from'] or False
|
||||
result['date_to'] = data['form']['date_to'] or False
|
||||
result['strict_range'] = True if result['date_from'] else False
|
||||
result['company_id'] = data['form']['company_id'][0] or False
|
||||
return result
|
||||
|
||||
def _print_report(self, data):
|
||||
"""Raise an error if the report comes checked """
|
||||
raise NotImplementedError()
|
||||
|
||||
def check_report(self):
|
||||
"""Function to check if the report comes active models and related
|
||||
values"""
|
||||
self.ensure_one()
|
||||
data = {}
|
||||
data['ids'] = self.env.context.get('active_ids', [])
|
||||
data['model'] = self.env.context.get('active_model', 'ir.ui.menu')
|
||||
data['form'] = self.read(['date_from', 'date_to', 'journal_ids', 'target_move', 'company_id'])[0]
|
||||
used_context = self._build_contexts(data)
|
||||
data['form']['used_context'] = dict(used_context, lang=get_lang(self.env).code)
|
||||
return self.with_context(discard_logo_check=True)._print_report(data)
|
||||
39
addons/base_accounting_kit/models/followup_line.py
Normal file
@@ -0,0 +1,39 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class FollowupLine(models.Model):
|
||||
"""Model for defining follow-up criteria including the action name, sequence order, due days, and related follow-ups."""
|
||||
_name = 'followup.line'
|
||||
_description = 'Follow-up Criteria'
|
||||
_order = 'delay'
|
||||
|
||||
name = fields.Char('Follow-Up Action', required=True, translate=True)
|
||||
sequence = fields.Integer(
|
||||
help="Gives the sequence order when displaying a list of follow-up lines.")
|
||||
delay = fields.Integer('Due Days', required=True,
|
||||
help="The number of days after the due date of the invoice"
|
||||
" to wait before sending the reminder."
|
||||
" Could be negative if you want to send a polite alert beforehand.")
|
||||
followup_id = fields.Many2one('account.followup', 'Follow Ups',
|
||||
ondelete="cascade")
|
||||
33
addons/base_accounting_kit/models/multiple_invoice.py
Normal file
@@ -0,0 +1,33 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class MultipleInvoice(models.Model):
|
||||
"""Multiple Invoice Model"""
|
||||
_name = "multiple.invoice"
|
||||
_description = 'Multiple Invoice'
|
||||
_order = "sequence"
|
||||
|
||||
sequence = fields.Integer(string='Sequence No')
|
||||
copy_name = fields.Char(string='Invoice Copy Name')
|
||||
journal_id = fields.Many2one('account.journal', string="Journal")
|
||||
158
addons/base_accounting_kit/models/multiple_invoice_layout.py
Normal file
@@ -0,0 +1,158 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from odoo import api, fields, models
|
||||
from odoo.tools.misc import file_path
|
||||
|
||||
try:
|
||||
import sass as libsass
|
||||
except ImportError:
|
||||
libsass = None
|
||||
|
||||
|
||||
class MultipleInvoiceLayout(models.TransientModel):
|
||||
"""
|
||||
Customise the invoice copy document layout and display a live preview
|
||||
"""
|
||||
_name = 'multiple.invoice.layout'
|
||||
_description = 'Multiple Invoice Document Layout'
|
||||
|
||||
def _get_default_journal(self):
|
||||
"""The default function to return the journal for the invoice"""
|
||||
return self.env['account.journal'].search(
|
||||
[('id', '=', self.env.context.get('active_id'))]).id
|
||||
|
||||
company_id = fields.Many2one(
|
||||
'res.company', default=lambda self: self.env.company, required=True)
|
||||
layout = fields.Char(related="company_id.external_report_layout_id.key")
|
||||
journal_id = fields.Many2one('account.journal', string='Journal',
|
||||
required=True, default=_get_default_journal)
|
||||
multiple_invoice_type = fields.Selection(
|
||||
related='journal_id.multiple_invoice_type', readonly=False,
|
||||
required=True)
|
||||
text_position = fields.Selection(related='journal_id.text_position',
|
||||
readonly=False, required=True,
|
||||
default='header')
|
||||
body_text_position = fields.Selection(
|
||||
related='journal_id.body_text_position',
|
||||
readonly=False)
|
||||
text_align = fields.Selection(
|
||||
related='journal_id.text_align',
|
||||
readonly=False)
|
||||
preview = fields.Html(compute='_compute_preview',
|
||||
sanitize=False,
|
||||
sanitize_tags=False,
|
||||
sanitize_attributes=False,
|
||||
sanitize_style=False,
|
||||
sanitize_form=False,
|
||||
strip_style=False,
|
||||
strip_classes=False)
|
||||
|
||||
@api.depends('multiple_invoice_type', 'text_position', 'body_text_position',
|
||||
'text_align')
|
||||
def _compute_preview(self):
|
||||
""" compute a qweb based preview to display on the wizard """
|
||||
|
||||
styles = self._get_asset_style()
|
||||
|
||||
for wizard in self:
|
||||
if wizard.company_id:
|
||||
preview_css = self._get_css_for_preview(styles, wizard.id)
|
||||
layout = self._get_layout_for_preview()
|
||||
ir_ui_view = wizard.env['ir.ui.view']
|
||||
wizard.preview = ir_ui_view._render_template(
|
||||
'base_accounting_kit.multiple_invoice_wizard_preview',
|
||||
{'company': wizard.company_id, 'preview_css': preview_css,
|
||||
'layout': layout,
|
||||
'mi_type': self.multiple_invoice_type,
|
||||
'txt_position': self.text_position,
|
||||
'body_txt_position': self.body_text_position,
|
||||
'txt_align': self.text_align,
|
||||
'mi': self.env.ref(
|
||||
'base_accounting_kit.multiple_invoice_sample_name')
|
||||
})
|
||||
else:
|
||||
wizard.preview = False
|
||||
|
||||
def _get_asset_style(self):
|
||||
"""Used to set the asset style"""
|
||||
company_styles = self.env['ir.qweb']._render(
|
||||
'web.styles_company_report', {
|
||||
'company_ids': self.company_id,
|
||||
}, raise_if_not_found=False)
|
||||
return company_styles
|
||||
|
||||
@api.model
|
||||
def _get_css_for_preview(self, scss, new_id):
|
||||
"""
|
||||
Compile the scss into css.
|
||||
"""
|
||||
css_code = self._compile_scss(scss)
|
||||
return css_code
|
||||
|
||||
@api.model
|
||||
def _compile_scss(self, scss_source):
|
||||
"""
|
||||
This code will compile valid scss into css.
|
||||
Parameters are the same from odoo/addons/base/models/assetsbundle.py
|
||||
Simply copied and adapted slightly
|
||||
"""
|
||||
|
||||
# No scss ? still valid, returns empty css
|
||||
if not scss_source.strip():
|
||||
return ""
|
||||
|
||||
precision = 8
|
||||
output_style = 'expanded'
|
||||
bootstrap_path = file_path('web', 'static', 'lib', 'bootstrap',
|
||||
'scss')
|
||||
try:
|
||||
return libsass.compile(
|
||||
string=scss_source,
|
||||
include_paths=[
|
||||
bootstrap_path,
|
||||
],
|
||||
output_style=output_style,
|
||||
precision=precision,
|
||||
)
|
||||
except libsass.CompileError as e:
|
||||
raise libsass.CompileError(e.args[0])
|
||||
|
||||
def _get_layout_for_preview(self):
|
||||
"""Returns the layout Preview for the accounting module"""
|
||||
if self.layout == 'web.external_layout_boxed':
|
||||
new_layout = 'base_accounting_kit.boxed'
|
||||
|
||||
elif self.layout == 'web.external_layout_bold':
|
||||
new_layout = 'base_accounting_kit.bold'
|
||||
|
||||
elif self.layout == 'web.external_layout_striped':
|
||||
new_layout = 'base_accounting_kit.striped'
|
||||
|
||||
else:
|
||||
new_layout = 'base_accounting_kit.standard'
|
||||
|
||||
return new_layout
|
||||
|
||||
def document_layout_save(self):
|
||||
"""meant to be overridden document_layout_save"""
|
||||
return self.env.context.get('report_action') or {
|
||||
'type': 'ir.actions.act_window_close'}
|
||||
45
addons/base_accounting_kit/models/product_template.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ProductTemplate(models.Model):
|
||||
"""Inherited the model for adding new fields and functions"""
|
||||
_inherit = 'product.template'
|
||||
|
||||
asset_category_id = fields.Many2one('account.asset.category',
|
||||
string='Asset Type',
|
||||
company_dependent=True,
|
||||
ondelete="restrict")
|
||||
deferred_revenue_category_id = fields.Many2one('account.asset.category',
|
||||
string='Deferred Revenue Type',
|
||||
company_dependent=True,
|
||||
ondelete="restrict")
|
||||
|
||||
def _get_asset_accounts(self):
|
||||
"""Override method to customize asset accounts based on asset and deferred revenue categories."""
|
||||
res = super(ProductTemplate, self)._get_asset_accounts()
|
||||
if self.asset_category_id:
|
||||
res['stock_input'] = self.property_account_expense_id
|
||||
if self.deferred_revenue_category_id:
|
||||
res['stock_output'] = self.property_account_income_id
|
||||
return res
|
||||
159
addons/base_accounting_kit/models/recurring_payments.py
Normal file
@@ -0,0 +1,159 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from datetime import datetime, date
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from odoo import api, models, fields
|
||||
|
||||
|
||||
class RecurringPayments(models.Model):
|
||||
"""Created the module for recurring payments"""
|
||||
_name = 'account.recurring.payments'
|
||||
_description = 'Accounting Recurring Payment'
|
||||
|
||||
def _get_next_schedule(self):
|
||||
"""Function for adding the schedule process"""
|
||||
if self.date:
|
||||
recurr_dates = []
|
||||
today = datetime.today()
|
||||
start_date = datetime.strptime(str(self.date), '%Y-%m-%d')
|
||||
while start_date <= today:
|
||||
recurr_dates.append(str(start_date.date()))
|
||||
if self.recurring_period == 'days':
|
||||
start_date += relativedelta(days=self.recurring_interval)
|
||||
elif self.recurring_period == 'weeks':
|
||||
start_date += relativedelta(weeks=self.recurring_interval)
|
||||
elif self.recurring_period == 'months':
|
||||
start_date += relativedelta(months=self.recurring_interval)
|
||||
else:
|
||||
start_date += relativedelta(years=self.recurring_interval)
|
||||
self.next_date = start_date.date()
|
||||
|
||||
name = fields.Char(string='Name')
|
||||
debit_account = fields.Many2one('account.account', 'Debit Account',
|
||||
required=True)
|
||||
credit_account = fields.Many2one('account.account', 'Credit Account',
|
||||
required=True)
|
||||
journal_id = fields.Many2one('account.journal', 'Journal', required=True)
|
||||
analytic_account_id = fields.Many2one('account.analytic.account',
|
||||
'Analytic Account')
|
||||
date = fields.Date('Starting Date', required=True, default=date.today())
|
||||
next_date = fields.Date('Next Schedule', compute=_get_next_schedule,
|
||||
readonly=True, copy=False)
|
||||
recurring_period = fields.Selection(selection=[('days', 'Days'),
|
||||
('weeks', 'Weeks'),
|
||||
('months', 'Months'),
|
||||
('years', 'Years')],
|
||||
store=True, required=True)
|
||||
amount = fields.Float('Amount')
|
||||
description = fields.Text('Description')
|
||||
state = fields.Selection(selection=[('draft', 'Draft'),
|
||||
('running', 'Running')],
|
||||
default='draft', string='Status')
|
||||
journal_state = fields.Selection(selection=[('draft', 'Unposted'),
|
||||
('posted', 'Posted')],
|
||||
required=True, default='draft',
|
||||
string='Generate Journal As')
|
||||
recurring_interval = fields.Integer('Recurring Interval', default=1)
|
||||
partner_id = fields.Many2one('res.partner', 'Partner')
|
||||
pay_time = fields.Selection(selection=[('pay_now', 'Pay Directly'),
|
||||
('pay_later', 'Pay Later')],
|
||||
store=True, required=True)
|
||||
company_id = fields.Many2one('res.company',
|
||||
default=lambda l: l.env.company.id)
|
||||
recurring_lines = fields.One2many('account.recurring.entries.line', 'tmpl_id')
|
||||
|
||||
@api.onchange('partner_id')
|
||||
def onchange_partner_id(self):
|
||||
"""Onchange partner field for updating the credit account value"""
|
||||
if self.partner_id.property_account_receivable_id:
|
||||
self.credit_account = self.partner_id.property_account_payable_id
|
||||
|
||||
@api.model
|
||||
def _cron_generate_entries(self):
|
||||
"""Generate recurring entries based on the defined schedule
|
||||
and create corresponding accounting moves."""
|
||||
data = self.env['account.recurring.payments'].search(
|
||||
[('state', '=', 'running')])
|
||||
entries = self.env['account.move'].search(
|
||||
[('recurring_ref', '!=', False)])
|
||||
journal_dates = []
|
||||
journal_codes = []
|
||||
remaining_dates = []
|
||||
for entry in entries:
|
||||
journal_dates.append(str(entry.date))
|
||||
if entry.recurring_ref:
|
||||
journal_codes.append(str(entry.recurring_ref))
|
||||
today = datetime.today()
|
||||
for line in data:
|
||||
if line.date:
|
||||
recurr_dates = []
|
||||
start_date = datetime.strptime(str(line.date), '%Y-%m-%d')
|
||||
while start_date <= today:
|
||||
recurr_dates.append(str(start_date.date()))
|
||||
if line.recurring_period == 'days':
|
||||
start_date += relativedelta(
|
||||
days=line.recurring_interval)
|
||||
elif line.recurring_period == 'weeks':
|
||||
start_date += relativedelta(
|
||||
weeks=line.recurring_interval)
|
||||
elif line.recurring_period == 'months':
|
||||
start_date += relativedelta(
|
||||
months=line.recurring_interval)
|
||||
else:
|
||||
start_date += relativedelta(
|
||||
years=line.recurring_interval)
|
||||
for rec in recurr_dates:
|
||||
recurr_code = str(line.id) + '/' + str(rec)
|
||||
if recurr_code not in journal_codes:
|
||||
remaining_dates.append({
|
||||
'date': rec,
|
||||
'template_name': line.name,
|
||||
'amount': line.amount,
|
||||
'tmpl_id': line.id,
|
||||
})
|
||||
child_ids = self.recurring_lines.create(remaining_dates)
|
||||
for line in child_ids:
|
||||
tmpl_id = line.tmpl_id
|
||||
recurr_code = str(tmpl_id.id) + '/' + str(line.date)
|
||||
line_ids = [(0, 0, {
|
||||
'account_id': tmpl_id.credit_account.id,
|
||||
'partner_id': tmpl_id.partner_id.id,
|
||||
'credit': line.amount,
|
||||
# 'analytic_account_id': tmpl_id.analytic_account_id.id,
|
||||
}), (0, 0, {
|
||||
'account_id': tmpl_id.debit_account.id,
|
||||
'partner_id': tmpl_id.partner_id.id,
|
||||
'debit': line.amount,
|
||||
# 'analytic_account_id': tmpl_id.analytic_account_id.id,
|
||||
})]
|
||||
vals = {
|
||||
'date': line.date,
|
||||
'recurring_ref': recurr_code,
|
||||
'company_id': self.env.company.id,
|
||||
'journal_id': tmpl_id.journal_id.id,
|
||||
'ref': line.template_name,
|
||||
'narration': 'Recurring entry',
|
||||
'line_ids': line_ids
|
||||
}
|
||||
move_id = self.env['account.move'].create(vals)
|
||||
if tmpl_id.journal_state == 'posted':
|
||||
move_id.post()
|
||||
109
addons/base_accounting_kit/models/res_company.py
Normal file
@@ -0,0 +1,109 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from datetime import datetime
|
||||
import calendar
|
||||
from odoo import models, api, _
|
||||
from odoo.exceptions import RedirectWarning
|
||||
|
||||
|
||||
class ResCompany(models.Model):
|
||||
"""Model for inheriting res_company."""
|
||||
_inherit = "res.company"
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
"""Ensure fiscal year day does not exceed the maximum valid day for the selected month during record creation."""
|
||||
for vals in vals_list:
|
||||
if 'fiscalyear_last_month' in vals and 'fiscalyear_last_day' in vals:
|
||||
month = vals.get('fiscalyear_last_month')
|
||||
day = vals.get('fiscalyear_last_day')
|
||||
if month and day:
|
||||
if vals.account_opening_date:
|
||||
year = vals.account_opening_date.year
|
||||
else:
|
||||
year = datetime.now().year
|
||||
max_day = calendar.monthrange(year, int(month))[1]
|
||||
if int(day) > max_day:
|
||||
vals['fiscalyear_last_day'] = max_day
|
||||
return super(ResCompany, self).create(vals_list)
|
||||
|
||||
def write(self, vals):
|
||||
"""Auto-correct fiscal year day to a valid value when month or day is updated to prevent invalid calendar dates."""
|
||||
if 'fiscalyear_last_month' in vals or 'fiscalyear_last_day' in vals:
|
||||
month = vals.get('fiscalyear_last_month')
|
||||
day = vals.get('fiscalyear_last_day')
|
||||
if month:
|
||||
if self.account_opening_date:
|
||||
year = self.account_opening_date.year
|
||||
else:
|
||||
year = datetime.now().year
|
||||
max_day = calendar.monthrange(year, int(month))[1]
|
||||
if not day:
|
||||
if any(company.fiscalyear_last_day > max_day for company in self):
|
||||
vals['fiscalyear_last_day'] = max_day
|
||||
elif int(day) > max_day:
|
||||
vals['fiscalyear_last_day'] = max_day
|
||||
|
||||
return super(ResCompany, self).write(vals)
|
||||
|
||||
def _validate_locks(self, values):
|
||||
"""Validate the hard lock date by checking for unposted entries and unreconciled bank statement lines."""
|
||||
if values.get('hard_lock_date'):
|
||||
draft_entries = self.env['account.move'].search([
|
||||
('company_id', 'in', self.ids),
|
||||
('state', '=', 'draft'),
|
||||
('date', '<=', values['hard_lock_date'])])
|
||||
if draft_entries:
|
||||
error_msg = _('There are still unposted entries in the '
|
||||
'period you want to lock. You should either post '
|
||||
'or delete them.')
|
||||
action_error = {
|
||||
'view_mode': 'list',
|
||||
'name': 'Unposted Entries',
|
||||
'res_model': 'account.move',
|
||||
'type': 'ir.actions.act_window',
|
||||
'domain': [('id', 'in', draft_entries.ids)],
|
||||
'search_view_id': [self.env.ref('account.view_account_move_filter').id, 'search'],
|
||||
'views': [[self.env.ref('account.view_move_tree').id, 'list'], [self.env.ref('account.view_move_form').id, 'form']],
|
||||
}
|
||||
raise RedirectWarning(error_msg, action_error, _('Show unposted entries'))
|
||||
|
||||
unreconciled_statement_lines = self.env['account.bank.statement.line'].search([
|
||||
('company_id', 'in', self.ids),
|
||||
('is_reconciled', '=', False),
|
||||
('date', '<=', values['hard_lock_date']),
|
||||
('move_id.state', 'in', ('draft', 'posted')),
|
||||
])
|
||||
if unreconciled_statement_lines:
|
||||
error_msg = _("There are still unreconciled bank statement lines in the period you want to lock."
|
||||
"You should either reconcile or delete them.")
|
||||
action_error = {
|
||||
'view_mode': 'kanban',
|
||||
'name': 'Unreconciled Transactions',
|
||||
'res_model': 'account.bank.statement.line',
|
||||
'type': 'ir.actions.act_window',
|
||||
'domain': [('id', 'in', unreconciled_statement_lines.ids)],
|
||||
'views': [[self.env.ref(
|
||||
'base_accounting_kit.account_bank_statement_line_view_kanban').id,
|
||||
'kanban']]
|
||||
}
|
||||
raise RedirectWarning(error_msg, action_error, _('Show Unreconciled Bank Statement Lines'))
|
||||
64
addons/base_accounting_kit/models/res_config_settings.py
Normal file
@@ -0,0 +1,64 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
"""Defines a model for configuration settings with additional fields for
|
||||
managing customer credit limit and Anglo-Saxon accounting settings."""
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
customer_credit_limit = fields.Boolean(string="Customer Credit Limit")
|
||||
|
||||
use_anglo_saxon_accounting = fields.Boolean(string="Use Anglo-Saxon accounting", readonly=False,
|
||||
related='company_id.anglo_saxon_accounting')
|
||||
fiscalyear_last_day = fields.Integer(
|
||||
related='company_id.fiscalyear_last_day', readonly=False
|
||||
)
|
||||
fiscalyear_last_month = fields.Selection(
|
||||
related='company_id.fiscalyear_last_month', readonly=False
|
||||
)
|
||||
|
||||
@api.model
|
||||
def get_values(self):
|
||||
"""Retrieve the values for configuration settings including the
|
||||
customer credit limit from the database parameters. """
|
||||
res = super(ResConfigSettings, self).get_values()
|
||||
params = self.env['ir.config_parameter'].sudo()
|
||||
customer_credit_limit = params.get_param('customer_credit_limit',
|
||||
default=False)
|
||||
res.update(customer_credit_limit=customer_credit_limit)
|
||||
return res
|
||||
|
||||
def set_values(self):
|
||||
"""Set the customer credit limit value in the database parameters using superuser access."""
|
||||
super(ResConfigSettings, self).set_values()
|
||||
self.env['ir.config_parameter'].sudo().set_param(
|
||||
"customer_credit_limit",
|
||||
self.customer_credit_limit)
|
||||
|
||||
@api.model
|
||||
def get_view_id(self):
|
||||
"""Retrieve the ID of the view for bank reconciliation widget form."""
|
||||
view_id = self.env['ir.model.data']._xmlid_to_res_id(
|
||||
'base_accounting_kit.view_bank_reconcile_widget_form')
|
||||
return view_id
|
||||
528
addons/base_accounting_kit/models/res_partner.py
Normal file
@@ -0,0 +1,528 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from datetime import date, timedelta
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
import base64
|
||||
import io
|
||||
import json
|
||||
import xlsxwriter
|
||||
from odoo.exceptions import ValidationError, UserError
|
||||
from odoo.tools.json import json_default
|
||||
|
||||
|
||||
class ResPartner(models.Model):
|
||||
"""Inheriting res.partner"""
|
||||
_inherit = "res.partner"
|
||||
|
||||
invoice_list = fields.One2many('account.move', 'partner_id',
|
||||
string="Invoice Details",
|
||||
readonly=True,
|
||||
domain=(
|
||||
[('payment_state', '=', 'not_paid'),
|
||||
('move_type', '=', 'out_invoice')]))
|
||||
total_due = fields.Monetary(compute='_compute_for_followup', store=False,
|
||||
readonly=True)
|
||||
next_reminder_date = fields.Date(compute='_compute_for_followup',
|
||||
store=False, readonly=True)
|
||||
total_overdue = fields.Monetary(compute='_compute_for_followup',
|
||||
store=False, readonly=True)
|
||||
followup_status = fields.Selection(
|
||||
[('in_need_of_action', 'In need of action'),
|
||||
('with_overdue_invoices', 'With overdue invoices'),
|
||||
('no_action_needed', 'No action needed')],
|
||||
string='Followup status',
|
||||
)
|
||||
|
||||
warning_stage = fields.Float(string='Warning Amount',
|
||||
help="A warning message will appear once the "
|
||||
"selected customer is crossed warning "
|
||||
"amount. Set its value to 0.00 to"
|
||||
" disable this feature")
|
||||
blocking_stage = fields.Float(string='Blocking Amount',
|
||||
help="Cannot make sales once the selected "
|
||||
"customer is crossed blocking amount."
|
||||
"Set its value to 0.00 to disable "
|
||||
"this feature")
|
||||
due_amount = fields.Float(string="Total Sale",
|
||||
compute="compute_due_amount")
|
||||
active_limit = fields.Boolean("Active Credit Limit", default=False)
|
||||
|
||||
enable_credit_limit = fields.Boolean(string="Credit Limit Enabled",
|
||||
compute="_compute_enable_credit_limit")
|
||||
|
||||
def _compute_for_followup(self):
|
||||
"""
|
||||
Compute the fields 'total_due', 'total_overdue' , 'next_reminder_date' and 'followup_status'
|
||||
"""
|
||||
for record in self:
|
||||
total_due = 0
|
||||
total_overdue = 0
|
||||
today = fields.Date.today()
|
||||
for am in record.invoice_list:
|
||||
if am.company_id == self.env.company:
|
||||
amount = am.amount_residual
|
||||
total_due += amount
|
||||
|
||||
is_overdue = today > am.invoice_date_due if am.invoice_date_due else today > am.date
|
||||
if is_overdue:
|
||||
total_overdue += amount or 0
|
||||
min_date = record.get_min_date()
|
||||
action = record.action_after()
|
||||
if min_date:
|
||||
date_reminder = min_date + timedelta(days=action)
|
||||
if date_reminder:
|
||||
record.next_reminder_date = date_reminder
|
||||
else:
|
||||
date_reminder = today
|
||||
record.next_reminder_date = date_reminder
|
||||
if total_overdue > 0 and date_reminder > today:
|
||||
followup_status = "with_overdue_invoices"
|
||||
elif total_due > 0 and date_reminder <= today:
|
||||
followup_status = "in_need_of_action"
|
||||
else:
|
||||
followup_status = "no_action_needed"
|
||||
record.total_due = total_due
|
||||
record.total_overdue = total_overdue
|
||||
record.followup_status = followup_status
|
||||
|
||||
def get_min_date(self):
|
||||
"""Get the minimum invoice due date from the partner's invoice list."""
|
||||
today = date.today()
|
||||
for this in self:
|
||||
if this.invoice_list:
|
||||
min_list = this.invoice_list.mapped('invoice_date_due')
|
||||
while False in min_list:
|
||||
min_list.remove(False)
|
||||
return min(min_list)
|
||||
else:
|
||||
return today
|
||||
|
||||
def get_delay(self):
|
||||
"""Retrieve the delay information for follow-up lines associated with the company."""
|
||||
delay = """SELECT fl.id, fl.delay
|
||||
FROM followup_line fl
|
||||
JOIN account_followup af ON fl.followup_id = af.id
|
||||
WHERE af.company_id = %s
|
||||
ORDER BY fl.delay;
|
||||
|
||||
"""
|
||||
self._cr.execute(delay, [self.env.company.id])
|
||||
record = self._cr.dictfetchall()
|
||||
|
||||
return record
|
||||
|
||||
def action_after(self):
|
||||
"""Retrieve the delay information for follow-up lines associated with the company and return the delay value if found."""
|
||||
lines = self.env['followup.line'].search([(
|
||||
'followup_id.company_id', '=', self.env.company.id)])
|
||||
if lines:
|
||||
record = self.get_delay()
|
||||
for i in record:
|
||||
return i['delay']
|
||||
|
||||
def compute_due_amount(self):
|
||||
"""Compute function to compute the due amount with the
|
||||
credit and debit amount"""
|
||||
for rec in self:
|
||||
if not rec.id:
|
||||
continue
|
||||
rec.due_amount = rec.credit - rec.debit
|
||||
|
||||
def _compute_enable_credit_limit(self):
|
||||
""" Check credit limit is enabled in account settings """
|
||||
params = self.env['ir.config_parameter'].sudo()
|
||||
customer_credit_limit = params.get_param('customer_credit_limit',
|
||||
default=False)
|
||||
for rec in self:
|
||||
rec.enable_credit_limit = True if customer_credit_limit else False
|
||||
|
||||
@api.constrains('warning_stage', 'blocking_stage')
|
||||
def constrains_warning_stage(self):
|
||||
"""Constrains functionality used to indicate or raise an
|
||||
UserError"""
|
||||
if self.active_limit and self.enable_credit_limit:
|
||||
if self.warning_stage >= self.blocking_stage:
|
||||
if self.blocking_stage > 0:
|
||||
raise UserError(_(
|
||||
"Warning amount should be less than Blocking amount"))
|
||||
|
||||
# customer statement
|
||||
|
||||
customer_report_ids = fields.Many2many(
|
||||
'account.move',
|
||||
compute='_compute_customer_report_ids',
|
||||
help='Partner Invoices related to Customer')
|
||||
vendor_statement_ids = fields.Many2many(
|
||||
'account.move',
|
||||
compute='_compute_vendor_statement_ids',
|
||||
help='Partner Bills related to Vendor')
|
||||
currency_id = fields.Many2one(
|
||||
'res.currency',
|
||||
default=lambda self: self.env.company.currency_id.id,
|
||||
help="currency related to Customer or Vendor")
|
||||
|
||||
def _compute_customer_report_ids(self):
|
||||
""" For computing 'invoices' of partner """
|
||||
for rec in self:
|
||||
inv_ids = self.env['account.move'].search(
|
||||
[('partner_id', '=', rec.id),
|
||||
('move_type', '=', 'out_invoice'),
|
||||
('payment_state', '!=', 'paid'),
|
||||
('state', '=', 'posted')])
|
||||
rec.customer_report_ids = inv_ids
|
||||
|
||||
def _compute_vendor_statement_ids(self):
|
||||
""" For computing 'bills' of partner """
|
||||
for rec in self:
|
||||
bills = self.env['account.move'].search(
|
||||
[('partner_id', '=', rec.id),
|
||||
('move_type', '=', 'in_invoice'),
|
||||
('payment_state', '!=', 'paid'),
|
||||
('state', '=', 'posted')])
|
||||
rec.vendor_statement_ids = bills
|
||||
|
||||
def main_query(self):
|
||||
""" Return select query """
|
||||
query = """SELECT name , invoice_date, invoice_date_due,
|
||||
amount_total_signed AS sub_total,
|
||||
amount_residual_signed AS amount_due ,
|
||||
amount_residual AS balance
|
||||
FROM account_move WHERE payment_state != 'paid'
|
||||
AND state ='posted' AND partner_id= '%s'
|
||||
AND company_id = '%s' """ % (self.id, self.env.company.id)
|
||||
return query
|
||||
|
||||
def amount_query(self):
|
||||
""" Return query for calculating total amount """
|
||||
amount_query = """ SELECT SUM(amount_total_signed) AS total,
|
||||
SUM(amount_residual) AS balance
|
||||
FROM account_move WHERE payment_state != 'paid'
|
||||
AND state ='posted' AND partner_id= '%s'
|
||||
AND company_id = '%s' """ % (self.id, self.env.company.id)
|
||||
return amount_query
|
||||
|
||||
def action_share_pdf(self):
|
||||
""" Action for sharing customer pdf report """
|
||||
if self.customer_report_ids:
|
||||
main_query = self.main_query()
|
||||
main_query += """ AND move_type IN ('out_invoice')"""
|
||||
amount = self.amount_query()
|
||||
amount += """ AND move_type IN ('out_invoice')"""
|
||||
self.env.cr.execute(main_query)
|
||||
main = self.env.cr.dictfetchall()
|
||||
self.env.cr.execute(amount)
|
||||
amount = self.env.cr.dictfetchall()
|
||||
data = {
|
||||
'customer': self.display_name,
|
||||
'street': self.street,
|
||||
'street2': self.street2,
|
||||
'city': self.city,
|
||||
'state': self.state_id.name,
|
||||
'zip': self.zip,
|
||||
'my_data': main,
|
||||
'total': amount[0]['total'],
|
||||
'balance': amount[0]['balance'],
|
||||
'currency': self.currency_id.symbol,
|
||||
}
|
||||
report = self.env['ir.actions.report'].sudo()._render_qweb_pdf(
|
||||
'base_accounting_kit.res_partner_action', self, data=data)
|
||||
data_record = base64.b64encode(report[0])
|
||||
ir_values = {
|
||||
'name': 'Statement Report',
|
||||
'type': 'binary',
|
||||
'datas': data_record,
|
||||
'mimetype': 'application/pdf',
|
||||
'res_model': 'res.partner'
|
||||
}
|
||||
attachment = self.env['ir.attachment'].sudo().create(ir_values)
|
||||
email_values = {
|
||||
'email_to': self.email,
|
||||
'subject': 'Payment Statement Report',
|
||||
'body_html': '<p>Dear <strong> Mr/Miss. ' + self.name +
|
||||
'</strong> </p> <p> We have attached your '
|
||||
'payment statement. Please check </p> '
|
||||
'<p>Best regards, </p> <p> ' + self.env.user.name,
|
||||
'attachment_ids': [attachment.id],
|
||||
}
|
||||
mail = self.env['mail.mail'].sudo().create(email_values)
|
||||
mail.send()
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'message': 'Email Sent Successfully',
|
||||
'type': 'success',
|
||||
'sticky': False
|
||||
}
|
||||
}
|
||||
else:
|
||||
raise ValidationError('There is no statement to send')
|
||||
|
||||
def action_print_pdf(self):
|
||||
""" Action for printing pdf report """
|
||||
if self.customer_report_ids:
|
||||
main_query = self.main_query()
|
||||
main_query += """ AND move_type IN ('out_invoice')"""
|
||||
amount = self.amount_query()
|
||||
amount += """ AND move_type IN ('out_invoice')"""
|
||||
self.env.cr.execute(main_query)
|
||||
main = self.env.cr.dictfetchall()
|
||||
self.env.cr.execute(amount)
|
||||
amount = self.env.cr.dictfetchall()
|
||||
data = {
|
||||
'customer': self.display_name,
|
||||
'street': self.street,
|
||||
'street2': self.street2,
|
||||
'city': self.city,
|
||||
'state': self.state_id.name,
|
||||
'zip': self.zip,
|
||||
'my_data': main,
|
||||
'total': amount[0]['total'],
|
||||
'balance': amount[0]['balance'],
|
||||
'currency': self.currency_id.symbol,
|
||||
}
|
||||
return self.env.ref('base_accounting_kit.res_partner_action'
|
||||
).report_action(self, data=data)
|
||||
else:
|
||||
raise ValidationError('There is no statement to print')
|
||||
|
||||
def action_print_xlsx(self):
|
||||
""" Action for printing xlsx report of customers """
|
||||
if self.customer_report_ids:
|
||||
main_query = self.main_query()
|
||||
main_query += """ AND move_type IN ('out_invoice')"""
|
||||
amount = self.amount_query()
|
||||
amount += """ AND move_type IN ('out_invoice')"""
|
||||
self.env.cr.execute(main_query)
|
||||
main = self.env.cr.dictfetchall()
|
||||
self.env.cr.execute(amount)
|
||||
amount = self.env.cr.dictfetchall()
|
||||
data = {
|
||||
'customer': self.display_name,
|
||||
'street': self.street,
|
||||
'street2': self.street2,
|
||||
'city': self.city,
|
||||
'state': self.state_id.name,
|
||||
'zip': self.zip,
|
||||
'my_data': main,
|
||||
'total': amount[0]['total'],
|
||||
'balance': amount[0]['balance'],
|
||||
'currency': self.currency_id.symbol,
|
||||
}
|
||||
return {
|
||||
'type': 'ir.actions.report',
|
||||
'data': {
|
||||
'model': 'res.partner',
|
||||
'options': json.dumps(data,
|
||||
default=json_default),
|
||||
'output_format': 'xlsx',
|
||||
'report_name': 'Payment Statement Report'
|
||||
},
|
||||
'report_type': 'xlsx',
|
||||
}
|
||||
else:
|
||||
raise ValidationError('There is no statement to print')
|
||||
|
||||
def get_xlsx_report(self, data, response):
|
||||
""" Get xlsx report data """
|
||||
output = io.BytesIO()
|
||||
workbook = xlsxwriter.Workbook(output, {'in_memory': True})
|
||||
sheet = workbook.add_worksheet()
|
||||
cell_format_with_color = workbook.add_format({
|
||||
'font_size': '14px', 'bold': True,
|
||||
'bg_color': 'yellow', 'border': 1})
|
||||
cell_format = workbook.add_format({'font_size': '14px', 'bold': True})
|
||||
txt = workbook.add_format({'font_size': '13px'})
|
||||
txt_border = workbook.add_format({'font_size': '13px', 'border': 1})
|
||||
head = workbook.add_format({'align': 'center', 'bold': True,
|
||||
'font_size': '22px'})
|
||||
sheet.merge_range('B2:Q4', 'Payment Statement Report', head)
|
||||
if data['customer']:
|
||||
sheet.merge_range('B7:D7', 'Customer/Supplier : ', cell_format)
|
||||
sheet.merge_range('E7:H7', data['customer'], txt)
|
||||
sheet.merge_range('B9:C9', 'Address : ', cell_format)
|
||||
if data['street']:
|
||||
sheet.merge_range('D9:F9', data['street'], txt)
|
||||
if data['street2']:
|
||||
sheet.merge_range('D10:F10', data['street2'], txt)
|
||||
if data['city']:
|
||||
sheet.merge_range('D11:F11', data['city'], txt)
|
||||
if data['state']:
|
||||
sheet.merge_range('D12:F12', data['state'], )
|
||||
if data['zip']:
|
||||
sheet.merge_range('D13:F13', data['zip'], txt)
|
||||
sheet.merge_range('B15:C15', 'Date', cell_format_with_color)
|
||||
sheet.merge_range('D15:G15', 'Invoice/Bill Number',
|
||||
cell_format_with_color)
|
||||
sheet.merge_range('H15:I15', 'Due Date', cell_format_with_color)
|
||||
sheet.merge_range('J15:L15', 'Invoices/Debit', cell_format_with_color)
|
||||
sheet.merge_range('M15:O15', 'Amount Due', cell_format_with_color)
|
||||
sheet.merge_range('P15:R15', 'Balance Due', cell_format_with_color)
|
||||
row = 15
|
||||
column = 0
|
||||
for record in data['my_data']:
|
||||
sub_total = data['currency'] + str(record['sub_total'])
|
||||
amount_due = data['currency'] + str(record['amount_due'])
|
||||
balance = data['currency'] + str(record['balance'])
|
||||
total = data['currency'] + str(data['total'])
|
||||
remain_balance = data['currency'] + str(data['balance'])
|
||||
sheet.merge_range(row, column + 1, row, column + 2,
|
||||
record['invoice_date'], txt_border)
|
||||
sheet.merge_range(row, column + 3, row, column + 6,
|
||||
record['name'], txt_border)
|
||||
sheet.merge_range(row, column + 7, row, column + 8,
|
||||
record['invoice_date_due'], txt_border)
|
||||
sheet.merge_range(row, column + 9, row, column + 11,
|
||||
sub_total, txt_border)
|
||||
sheet.merge_range(row, column + 12, row, column + 14,
|
||||
amount_due, txt_border)
|
||||
sheet.merge_range(row, column + 15, row, column + 17,
|
||||
balance, txt_border)
|
||||
row = row + 1
|
||||
sheet.write(row + 2, column + 1, 'Total Amount: ', cell_format)
|
||||
sheet.merge_range(row + 2, column + 3, row + 2, column + 4,
|
||||
total, txt)
|
||||
sheet.write(row + 4, column + 1, 'Balance Due: ', cell_format)
|
||||
sheet.merge_range(row + 4, column + 3, row + 4, column + 4,
|
||||
remain_balance, txt)
|
||||
workbook.close()
|
||||
output.seek(0)
|
||||
response.stream.write(output.read())
|
||||
output.close()
|
||||
|
||||
def action_share_xlsx(self):
|
||||
""" Action for sharing xlsx report via email """
|
||||
if self.customer_report_ids:
|
||||
main_query = self.main_query()
|
||||
main_query += """ AND move_type IN ('out_invoice')"""
|
||||
amount = self.amount_query()
|
||||
amount += """ AND move_type IN ('out_invoice')"""
|
||||
self.env.cr.execute(main_query)
|
||||
main = self.env.cr.dictfetchall()
|
||||
self.env.cr.execute(amount)
|
||||
amount = self.env.cr.dictfetchall()
|
||||
data = {
|
||||
'customer': self.display_name,
|
||||
'street': self.street,
|
||||
'street2': self.street2,
|
||||
'city': self.city,
|
||||
'state': self.state_id.name,
|
||||
'zip': self.zip,
|
||||
'my_data': main,
|
||||
'total': amount[0]['total'],
|
||||
'balance': amount[0]['balance'],
|
||||
'currency': self.currency_id.symbol,
|
||||
}
|
||||
output = io.BytesIO()
|
||||
workbook = xlsxwriter.Workbook(output, {'in_memory': True})
|
||||
sheet = workbook.add_worksheet()
|
||||
cell_format = workbook.add_format({
|
||||
'font_size': '14px', 'bold': True})
|
||||
txt = workbook.add_format({'font_size': '13px'})
|
||||
head = workbook.add_format(
|
||||
{'align': 'center', 'bold': True, 'font_size': '22px'})
|
||||
sheet.merge_range('B2:P4', 'Payment Statement Report', head)
|
||||
date_style = workbook.add_format(
|
||||
{'text_wrap': True, 'align': 'center',
|
||||
'num_format': 'yyyy-mm-dd'})
|
||||
if data['customer']:
|
||||
sheet.write('B7:C7', 'Customer : ', cell_format)
|
||||
sheet.merge_range('D7:G7', data['customer'], txt)
|
||||
sheet.write('B9:C7', 'Address : ', cell_format)
|
||||
if data['street']:
|
||||
sheet.merge_range('D9:F9', data['street'], txt)
|
||||
if data['street2']:
|
||||
sheet.merge_range('D10:F10', data['street2'], txt)
|
||||
if data['city']:
|
||||
sheet.merge_range('D11:F11', data['city'], txt)
|
||||
if data['state']:
|
||||
sheet.merge_range('D12:F12', data['state'], txt)
|
||||
if data['zip']:
|
||||
sheet.merge_range('D13:F13', data['zip'], txt)
|
||||
sheet.write('B15', 'Date', cell_format)
|
||||
sheet.write('D15', 'Invoice/Bill Number', cell_format)
|
||||
sheet.write('H15', 'Due Date', cell_format)
|
||||
sheet.write('J15', 'Invoices/Debit', cell_format)
|
||||
sheet.write('M15', 'Amount Due', cell_format)
|
||||
sheet.write('P15', 'Balance Due', cell_format)
|
||||
row = 16
|
||||
column = 0
|
||||
for record in data['my_data']:
|
||||
sub_total = data['currency'] + str(record['sub_total'])
|
||||
amount_due = data['currency'] + str(record['amount_due'])
|
||||
balance = data['currency'] + str(record['balance'])
|
||||
total = data['currency'] + str(data['total'])
|
||||
remain_balance = data['currency'] + str(data['balance'])
|
||||
sheet.merge_range(row, column + 1, row, column + 2,
|
||||
record['invoice_date'], date_style)
|
||||
sheet.merge_range(row, column + 3, row, column + 5,
|
||||
record['name'], txt)
|
||||
sheet.merge_range(row, column + 7, row, column + 8,
|
||||
record['invoice_date_due'], date_style)
|
||||
sheet.merge_range(row, column + 9, row, column + 10,
|
||||
sub_total, txt)
|
||||
sheet.merge_range(row, column + 12, row, column + 13,
|
||||
amount_due, txt)
|
||||
sheet.merge_range(row, column + 15, row, column + 16,
|
||||
balance, txt)
|
||||
row = row + 1
|
||||
sheet.write(row + 2, column + 1, 'Total Amount : ', cell_format)
|
||||
sheet.merge_range(row + 2, column + 4, row + 2, column + 5,
|
||||
total, txt)
|
||||
sheet.write(row + 4, column + 1, 'Balance Due : ', cell_format)
|
||||
sheet.merge_range(row + 4, column + 4, row + 4, column + 5,
|
||||
remain_balance, txt)
|
||||
workbook.close()
|
||||
output.seek(0)
|
||||
xlsx = base64.b64encode(output.read())
|
||||
output.close()
|
||||
ir_values = {
|
||||
'name': "Statement Report.xlsx",
|
||||
'type': 'binary',
|
||||
'datas': xlsx,
|
||||
'store_fname': xlsx,
|
||||
}
|
||||
attachment = self.env['ir.attachment'].sudo().create(ir_values)
|
||||
email_values = {
|
||||
'email_to': self.email,
|
||||
'subject': 'Payment Statement Report',
|
||||
'body_html': '<p>Dear <strong> Mr/Miss. ' + self.name +
|
||||
'</strong> </p> <p> We have attached your'
|
||||
' payment statement. Please check </p> '
|
||||
'<p>Best regards, </p> <p> ' + self.env.user.name,
|
||||
'attachment_ids': [attachment.id],
|
||||
}
|
||||
mail = self.env['mail.mail'].sudo().create(email_values)
|
||||
mail.send()
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'message': 'Email Sent Successfully',
|
||||
'type': 'success',
|
||||
'sticky': False
|
||||
}
|
||||
}
|
||||
else:
|
||||
raise ValidationError('There is no statement to send')
|
||||
|
||||
66
addons/base_accounting_kit/models/sale_order.py
Normal file
@@ -0,0 +1,66 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from odoo import api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools.translate import _
|
||||
|
||||
|
||||
class SaleOrder(models.Model):
|
||||
"""The Class inherits the sale.order model for adding the new
|
||||
fields and functions"""
|
||||
_inherit = 'sale.order'
|
||||
|
||||
has_due = fields.Boolean(string='Has due')
|
||||
is_warning = fields.Boolean(string='Is warning')
|
||||
due_amount = fields.Float(string='Due Amount',
|
||||
related='partner_id.due_amount')
|
||||
|
||||
def _action_confirm(self):
|
||||
"""To check the selected customers due amount is exceed than
|
||||
blocking stage"""
|
||||
if self.partner_id.active_limit \
|
||||
and self.partner_id.enable_credit_limit:
|
||||
if self.due_amount >= self.partner_id.blocking_stage:
|
||||
if self.partner_id.blocking_stage != 0:
|
||||
raise UserError(_(
|
||||
"%s is in Blocking Stage and "
|
||||
"has a due amount of %s %s to pay") % (
|
||||
self.partner_id.name, self.due_amount,
|
||||
self.currency_id.symbol))
|
||||
return super(SaleOrder, self)._action_confirm()
|
||||
|
||||
@api.onchange('partner_id')
|
||||
def check_due(self):
|
||||
"""To show the due amount and warning stage"""
|
||||
if self.partner_id and self.partner_id.due_amount > 0 \
|
||||
and self.partner_id.active_limit \
|
||||
and self.partner_id.enable_credit_limit:
|
||||
self.has_due = True
|
||||
else:
|
||||
self.has_due = False
|
||||
if self.partner_id and self.partner_id.active_limit\
|
||||
and self.partner_id.enable_credit_limit:
|
||||
if self.due_amount >= self.partner_id.warning_stage:
|
||||
if self.partner_id.warning_stage != 0:
|
||||
self.is_warning = True
|
||||
else:
|
||||
self.is_warning = False
|
||||
35
addons/base_accounting_kit/report/__init__.py
Normal file
@@ -0,0 +1,35 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from . import account_asset_report
|
||||
from . import account_bank_book
|
||||
from . import account_cash_book
|
||||
from . import account_day_book
|
||||
from . import account_report_common_account
|
||||
from . import cash_flow_report
|
||||
from . import general_ledger_report
|
||||
from . import multiple_invoice_report
|
||||
from . import report_aged_partner
|
||||
from . import report_financial
|
||||
from . import report_journal_audit
|
||||
from . import report_partner_ledger
|
||||
from . import report_tax
|
||||
from . import report_trial_balance
|
||||
90
addons/base_accounting_kit/report/account_asset_report.py
Normal file
@@ -0,0 +1,90 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from odoo import fields, models, tools
|
||||
|
||||
|
||||
class AssetAssetReport(models.Model):
|
||||
_name = "asset.asset.report"
|
||||
_description = "Assets Analysis"
|
||||
_auto = False
|
||||
|
||||
name = fields.Char(string='Year', required=False, readonly=True)
|
||||
date = fields.Date(readonly=True)
|
||||
depreciation_date = fields.Date(string='Depreciation Date', readonly=True)
|
||||
asset_id = fields.Many2one('account.asset.asset', string='Asset', readonly=True)
|
||||
asset_category_id = fields.Many2one('account.asset.category', string='Asset category', readonly=True)
|
||||
partner_id = fields.Many2one('res.partner', string='Partner', readonly=True)
|
||||
state = fields.Selection([('draft', 'Draft'), ('open', 'Running'), ('close', 'Close')], string='Status', readonly=True)
|
||||
depreciation_value = fields.Float(string='Amount of Depreciation Lines', readonly=True)
|
||||
installment_value = fields.Float(string='Amount of Installment Lines', readonly=True)
|
||||
move_check = fields.Boolean(string='Posted', readonly=True)
|
||||
installment_nbr = fields.Integer(string='# of Installment Lines', readonly=True)
|
||||
depreciation_nbr = fields.Integer(string='# of Depreciation Lines', readonly=True)
|
||||
gross_value = fields.Float(string='Gross Amount', readonly=True)
|
||||
posted_value = fields.Float(string='Posted Amount', readonly=True)
|
||||
unposted_value = fields.Float(string='Unposted Amount', readonly=True)
|
||||
company_id = fields.Many2one('res.company', string='Company', readonly=True)
|
||||
|
||||
def init(self):
|
||||
tools.drop_view_if_exists(self._cr, 'asset_asset_report')
|
||||
self._cr.execute("""
|
||||
create or replace view asset_asset_report as (
|
||||
select
|
||||
min(dl.id) as id,
|
||||
dl.name as name,
|
||||
dl.depreciation_date as depreciation_date,
|
||||
a.date as date,
|
||||
(CASE WHEN dlmin.id = min(dl.id)
|
||||
THEN a.value
|
||||
ELSE 0
|
||||
END) as gross_value,
|
||||
dl.amount as depreciation_value,
|
||||
dl.amount as installment_value,
|
||||
(CASE WHEN dl.move_check
|
||||
THEN dl.amount
|
||||
ELSE 0
|
||||
END) as posted_value,
|
||||
(CASE WHEN NOT dl.move_check
|
||||
THEN dl.amount
|
||||
ELSE 0
|
||||
END) as unposted_value,
|
||||
dl.asset_id as asset_id,
|
||||
dl.move_check as move_check,
|
||||
a.category_id as asset_category_id,
|
||||
a.partner_id as partner_id,
|
||||
a.state as state,
|
||||
count(dl.*) as installment_nbr,
|
||||
count(dl.*) as depreciation_nbr,
|
||||
a.company_id as company_id
|
||||
from account_asset_depreciation_line dl
|
||||
left join account_asset_asset a on (dl.asset_id=a.id)
|
||||
left join (select min(d.id) as id,ac.id as ac_id from
|
||||
account_asset_depreciation_line as d inner join
|
||||
account_asset_asset as ac ON (ac.id=d.asset_id) group by
|
||||
ac_id) as dlmin on dlmin.ac_id=a.id
|
||||
where a.active is true
|
||||
group by
|
||||
dl.amount,dl.asset_id,dl.depreciation_date,dl.name,
|
||||
a.date, dl.move_check, a.state, a.category_id,
|
||||
a.partner_id, a.company_id,
|
||||
a.value, a.id, a.salvage_value, dlmin.id
|
||||
)""")
|
||||
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<!-- Asset Report Pivot View -->
|
||||
<record id="asset_asset_report_view_pivot" model="ir.ui.view">
|
||||
<field name="name">asset.asset.report.view.pivot</field>
|
||||
<field name="model">asset.asset.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<pivot string="Assets Analysis" disable_linking="True">
|
||||
<field name="asset_category_id" type="row"/>
|
||||
<field name="gross_value" type="measure"/>
|
||||
<field name="unposted_value" type="measure"/>
|
||||
</pivot>
|
||||
</field>
|
||||
</record>
|
||||
<!-- Asset Report Graph View -->
|
||||
<record id="asset_asset_report_view_graph" model="ir.ui.view">
|
||||
<field name="name">asset.asset.report.view.graph</field>
|
||||
<field name="model">asset.asset.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<graph string="Assets Analysis">
|
||||
<field name="asset_category_id" type="row"/>
|
||||
<field name="gross_value" type="measure"/>
|
||||
<field name="unposted_value" type="measure"/>
|
||||
</graph>
|
||||
</field>
|
||||
</record>
|
||||
<!-- Asset Report Search View -->
|
||||
<record id="asset_asset_report_view_search" model="ir.ui.view">
|
||||
<field name="name">asset.asset.report.view.search</field>
|
||||
<field name="model">asset.asset.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Assets Analysis">
|
||||
<field name="date"/>
|
||||
<field name="depreciation_date"/>
|
||||
<filter string="Draft" name="draft" domain="[('state','=','draft')]" help="Assets in draft state"/>
|
||||
<filter string="Running" name="running" domain="[('state','=','open')]" help="Assets in running state"/>
|
||||
<filter string="Not archived" name="only_active" domain="[('asset_id.active','=', True)]"/>
|
||||
<separator/>
|
||||
<filter string="Posted" name="posted" domain="[('move_check','=',True)]" help="Posted depreciation lines" context="{'unposted_value_visible': 0}"/>
|
||||
<field name="asset_id"/>
|
||||
<field name="asset_category_id"/>
|
||||
<group>
|
||||
<field name="partner_id" filter_domain="[('partner_id','child_of',self)]"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</group>
|
||||
<group>
|
||||
<filter string="Asset" name="asset" context="{'group_by':'asset_id'}"/>
|
||||
<filter string="Asset Category" name="asset_category" context="{'group_by':'asset_category_id'}"/>
|
||||
<filter string="Company" name="company" context="{'group_by':'company_id'}" groups="base.group_multi_company"/>
|
||||
<separator/>
|
||||
<filter string="Purchase Month" name="purchase_month" help="Date of asset purchase"
|
||||
context="{'group_by':'date:month'}"/>
|
||||
<filter string="Depreciation Month" name="deprecation_month" help="Date of depreciation"
|
||||
context="{'group_by':'depreciation_date:month'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<!--Asset Report Action-->
|
||||
<record id="action_asset_asset_report" model="ir.actions.act_window">
|
||||
<field name="name">Assets Analysis</field>
|
||||
<field name="res_model">asset.asset.report</field>
|
||||
<field name="view_mode">graph,pivot</field>
|
||||
<field name="search_view_id" ref="asset_asset_report_view_search"/>
|
||||
<field name="domain">[('asset_category_id.type', '=', 'purchase')]</field>
|
||||
<field name="context">{'search_default_only_active': 1}</field>
|
||||
<field name="help" type="html">
|
||||
<p>
|
||||
From this report, you can have an overview on all depreciation's. The
|
||||
search bar can also be used to personalize your assets depreciation reporting.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
<!--Assets Menuitem-->
|
||||
<menuitem name="Assets" action="action_asset_asset_report"
|
||||
id="menu_action_asset_asset_report"
|
||||
parent="account.account_reports_management_menu" sequence="21"/>
|
||||
</odoo>
|
||||
175
addons/base_accounting_kit/report/account_bank_book.py
Normal file
@@ -0,0 +1,175 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from datetime import time
|
||||
from odoo import api, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class ReportBankBook(models.AbstractModel):
|
||||
_name = 'report.base_accounting_kit.report_bank_book'
|
||||
_description = 'Bank Book Report'
|
||||
|
||||
def _get_account_move_entry(self, accounts, init_balance, sortby,
|
||||
display_account):
|
||||
cr = self.env.cr
|
||||
move_line = self.env['account.move.line']
|
||||
move_lines = {x: [] for x in accounts.ids}
|
||||
|
||||
# Prepare initial sql query and Get the initial move lines
|
||||
if init_balance:
|
||||
init_tables, init_where_clause, init_where_params = move_line.with_context(
|
||||
date_from=self.env.context.get('date_from'), date_to=False,
|
||||
initial_bal=True)._query_get()
|
||||
init_wheres = [""]
|
||||
if init_where_clause.strip():
|
||||
init_wheres.append(init_where_clause.strip())
|
||||
init_filters = " AND ".join(init_wheres)
|
||||
filters = init_filters.replace('account_move_line__move_id',
|
||||
'm').replace('account_move_line',
|
||||
'l')
|
||||
sql = ("""SELECT 0 AS lid, l.account_id AS account_id, \
|
||||
'' AS ldate, '' AS lcode, 0.0 AS amount_currency, \
|
||||
'' AS lref, 'Initial Balance' AS lname, \
|
||||
COALESCE(SUM(l.debit),0.0) AS debit, \
|
||||
COALESCE(SUM(l.credit),0.0) AS credit, \
|
||||
COALESCE(SUM(l.debit),0) - COALESCE(SUM(l.credit), 0) as balance, \
|
||||
'' AS lpartner_id,\
|
||||
'' AS move_name, '' AS mmove_id, '' AS currency_code,\
|
||||
NULL AS currency_id,\
|
||||
'' AS invoice_id, '' AS invoice_type, '' AS invoice_number,\
|
||||
'' AS partner_name\
|
||||
FROM account_move_line l\
|
||||
LEFT JOIN account_move m ON (l.move_id=m.id)\
|
||||
LEFT JOIN res_currency c ON (l.currency_id=c.id)\
|
||||
LEFT JOIN res_partner p ON (l.partner_id=p.id)\
|
||||
JOIN account_journal j ON (l.journal_id=j.id)\
|
||||
WHERE l.account_id IN %s""" + filters + ' GROUP BY l.account_id')
|
||||
params = (tuple(accounts.ids),) + tuple(init_where_params)
|
||||
cr.execute(sql, params)
|
||||
for row in cr.dictfetchall():
|
||||
move_lines[row.pop('account_id')].append(row)
|
||||
sql_sort = 'l.date, l.move_id'
|
||||
if sortby == 'sort_journal_partner':
|
||||
sql_sort = 'j.code, p.name, l.move_id'
|
||||
|
||||
# Prepare sql query base on selected parameters from wizard
|
||||
tables, where_clause, where_params = move_line._query_get()
|
||||
wheres = [""]
|
||||
if where_clause.strip():
|
||||
wheres.append(where_clause.strip())
|
||||
filters = " AND ".join(wheres)
|
||||
filters = filters.replace('account_move_line__move_id', 'm').replace(
|
||||
'account_move_line', 'l')
|
||||
|
||||
# Get move lines base on sql query and Calculate the total
|
||||
# balance of move lines
|
||||
sql = ('''SELECT l.id AS lid, l.account_id \
|
||||
AS account_id, l.date AS ldate, j.code AS lcode,\
|
||||
l.currency_id, l.amount_currency, l.ref AS lref, l.name AS lname,\
|
||||
COALESCE(l.debit,0) AS debit, \
|
||||
COALESCE(l.credit,0) AS credit, \
|
||||
COALESCE(SUM(l.debit),0) - COALESCE(SUM(l.credit), 0) AS balance,\
|
||||
m.name AS move_name, c.symbol AS \
|
||||
currency_code, p.name AS partner_name\
|
||||
FROM account_move_line l\
|
||||
JOIN account_move m ON (l.move_id=m.id)\
|
||||
LEFT JOIN res_currency c ON (l.currency_id=c.id)\
|
||||
LEFT JOIN res_partner p ON (l.partner_id=p.id)\
|
||||
JOIN account_journal j ON (l.journal_id=j.id)\
|
||||
JOIN account_account acc ON (l.account_id = acc.id) \
|
||||
WHERE l.account_id IN %s ''' + filters + ''' GROUP BY \
|
||||
l.id, l.account_id, l.date, j.code, l.currency_id, \
|
||||
l.amount_currency, l.ref, l.name, m.name, \
|
||||
c.symbol, p.name ORDER BY ''' + sql_sort)
|
||||
params = (tuple(accounts.ids),) + tuple(where_params)
|
||||
cr.execute(sql, params)
|
||||
|
||||
for row in cr.dictfetchall():
|
||||
balance = 0
|
||||
for line in move_lines.get(row['account_id']):
|
||||
balance += line['debit'] - line['credit']
|
||||
row['balance'] += balance
|
||||
move_lines[row.pop('account_id')].append(row)
|
||||
|
||||
# Calculate the debit, credit and balance for Accounts
|
||||
account_res = []
|
||||
for account in accounts:
|
||||
account_company = self.env.company
|
||||
currency = account.currency_id and \
|
||||
account.currency_id or account_company.currency_id
|
||||
res = dict((fn, 0.0) for fn in ['credit', 'debit', 'balance'])
|
||||
res['code'] = account.code
|
||||
res['name'] = account.name
|
||||
res['move_lines'] = move_lines[account.id]
|
||||
for line in res.get('move_lines'):
|
||||
res['debit'] += line['debit']
|
||||
res['credit'] += line['credit']
|
||||
res['balance'] = line['balance']
|
||||
if display_account == 'all':
|
||||
account_res.append(res)
|
||||
if display_account == 'movement' and res.get('move_lines'):
|
||||
account_res.append(res)
|
||||
if display_account == 'not_zero' and not currency.is_zero(
|
||||
res['balance']):
|
||||
account_res.append(res)
|
||||
return account_res
|
||||
|
||||
@api.model
|
||||
def _get_report_values(self, docids, data=None):
|
||||
if not data.get('form') or not self.env.context.get('active_model'):
|
||||
raise UserError(
|
||||
_("Form content is missing, this report cannot be printed."))
|
||||
|
||||
model = self.env.context.get('active_model')
|
||||
docs = self.env[model].browse(self.env.context.get('active_ids', []))
|
||||
init_balance = data['form'].get('initial_balance', True)
|
||||
sortby = data['form'].get('sortby', 'sort_date')
|
||||
display_account = 'movement'
|
||||
codes = []
|
||||
if data['form'].get('journal_ids', False):
|
||||
codes = [journal.code for journal in
|
||||
self.env['account.journal'].search(
|
||||
[('id', 'in', data['form']['journal_ids'])])]
|
||||
account_ids = data['form']['account_ids']
|
||||
accounts = self.env['account.account'].search(
|
||||
[('id', 'in', account_ids)])
|
||||
if not accounts:
|
||||
journals = self.env['account.journal'].search([('type', '=', 'bank')])
|
||||
accounts = []
|
||||
for journal in journals:
|
||||
accounts.append(journal.default_account_id.id)
|
||||
accounts = self.env['account.account'].search([('id', 'in', accounts)])
|
||||
|
||||
accounts_res = self.with_context(data['form'].get('used_context', {}))._get_account_move_entry(
|
||||
accounts,
|
||||
init_balance,
|
||||
sortby,
|
||||
display_account)
|
||||
return {
|
||||
'doc_ids': docids,
|
||||
'doc_model': model,
|
||||
'data': data['form'],
|
||||
'docs': docs,
|
||||
'time': time,
|
||||
'Accounts': accounts_res,
|
||||
'print_journal': codes,
|
||||
}
|
||||
133
addons/base_accounting_kit/report/account_bank_book_template.xml
Normal file
@@ -0,0 +1,133 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<template id="report_bank_book">
|
||||
<t t-call="web.html_container">
|
||||
<t t-set="data_report_margin_top" t-value="12"/>
|
||||
<t t-set="data_report_header_spacing" t-value="9"/>
|
||||
<t t-set="data_report_dpi" t-value="110"/>
|
||||
<t t-call="web.internal_layout">
|
||||
<div class="page"><br/>
|
||||
<div class="row" style="top:60px;">
|
||||
<h2><span t-esc="env.company.name"/>: Bank Book Report</h2>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-4" style="width:40%;">
|
||||
<strong>Journals:</strong>
|
||||
<p t-esc="', '.join([ lt or '' for lt in print_journal ])"/>
|
||||
</div>
|
||||
<div class="col-xs-4" style="width:30%;">
|
||||
<strong>Display Account</strong>
|
||||
<p>
|
||||
<span t-if="data['display_account'] == 'all'">All accounts'</span>
|
||||
<span t-if="data['display_account'] == 'movement'">With movements</span>
|
||||
<span t-if="data['display_account'] == 'not_zero'">With balance not equal to zero</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-xs-4" style="width:30%;">
|
||||
<strong>Target Moves:</strong>
|
||||
<p t-if="data['target_move'] == 'all'">All Entries</p>
|
||||
<p t-if="data['target_move'] == 'posted'">All Posted Entries</p>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="row">
|
||||
<div style="width:70%;">
|
||||
<strong>Sorted By:</strong>
|
||||
<p t-if="data['sortby'] == 'sort_date'">Date</p>
|
||||
<p t-if="data['sortby'] == 'sort_journal_partner'">Journal and Partner</p>
|
||||
</div>
|
||||
<div style="width:30%;">
|
||||
<t t-if="data['date_from']">
|
||||
<strong>Date from :</strong>
|
||||
<span t-esc="data['date_from']"/>
|
||||
<br/>
|
||||
</t>
|
||||
<t t-if="data['date_to']">
|
||||
<strong>Date to :</strong>
|
||||
<span t-esc="data['date_to']"/>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<table class="table table-condensed">
|
||||
<thead>
|
||||
<tr class="text-center">
|
||||
<th>Date</th>
|
||||
<th>JRNL</th>
|
||||
<th>Partner</th>
|
||||
<th>Ref</th>
|
||||
<th>Move</th>
|
||||
<th>Entry Label</th>
|
||||
<th>Debit</th>
|
||||
<th>Credit</th>
|
||||
<th>Balance</th>
|
||||
<th groups="base.group_multi_currency">Currency</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<t t-foreach="Accounts" t-as="account">
|
||||
<tr style="font-weight: bold;">
|
||||
<td colspan="6">
|
||||
<span style="color: white;" t-esc="'..'"/>
|
||||
<span t-esc="account['code']"/>
|
||||
<span t-esc="account['name']"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-esc="account['debit']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-esc="account['credit']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-esc="account['balance']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td groups="base.group_multi_currency"/>
|
||||
</tr>
|
||||
<tr t-foreach="account['move_lines']" t-as="line">
|
||||
<td>
|
||||
<span t-esc="line['ldate']"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="line['lcode']"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="line['partner_name']"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-if="line['lref']" t-esc="line['lref']"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="line['move_name']"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="line['lname']"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-esc="line['debit']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-esc="line['credit']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-esc="line['balance']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
|
||||
<td t-if="line['amount_currency']" class="text-end" groups="base.group_multi_currency">
|
||||
<span t-esc="line['amount_currency'] if line['amount_currency'] > 0.00 else ''"/>
|
||||
<span t-esc="line['currency_code'] if line['amount_currency'] > 0.00 else ''"/>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
</odoo>
|
||||
220
addons/base_accounting_kit/report/account_cash_book.py
Normal file
@@ -0,0 +1,220 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from datetime import time
|
||||
from odoo import api, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class ReportCashBook(models.AbstractModel):
|
||||
_name = 'report.base_accounting_kit.report_cash_book'
|
||||
_description = 'Cash Book Report'
|
||||
|
||||
def _get_account_move_entry(self, accounts, init_balance, sortby, display_account):
|
||||
cr = self.env.cr
|
||||
move_line = self.env['account.move.line']
|
||||
move_lines = {x: [] for x in accounts.ids}
|
||||
|
||||
# ------------------------------
|
||||
# 1. Initial Balance
|
||||
# ------------------------------
|
||||
if init_balance:
|
||||
init_tables, init_where_clause, init_where_params = move_line.with_context(
|
||||
date_from=self.env.context.get('date_from'),
|
||||
date_to=False,
|
||||
initial_bal=True,
|
||||
)._query_get()
|
||||
|
||||
init_wheres = [""]
|
||||
if init_where_clause.strip():
|
||||
init_wheres.append(init_where_clause.strip())
|
||||
init_filters = " AND ".join(init_wheres)
|
||||
init_filters = init_filters.replace('account_move_line__move_id', 'm').replace(
|
||||
'account_move_line', 'l'
|
||||
)
|
||||
|
||||
sql = """
|
||||
SELECT 0 AS lid, l.account_id AS account_id, '' AS ldate,
|
||||
'' AS lcode, 0.0 AS amount_currency, '' AS lref,
|
||||
'Initial Balance' AS lname,
|
||||
COALESCE(SUM(l.debit),0.0) AS debit,
|
||||
COALESCE(SUM(l.credit),0.0) AS credit,
|
||||
COALESCE(SUM(l.debit),0) - COALESCE(SUM(l.credit), 0) AS balance,
|
||||
'' AS lpartner_id, '' AS move_name, '' AS mmove_id,
|
||||
'' AS currency_code, NULL AS currency_id,
|
||||
'' AS invoice_id, '' AS invoice_type, '' AS invoice_number,
|
||||
'' AS partner_name
|
||||
FROM account_move_line l
|
||||
LEFT JOIN account_move m ON (l.move_id=m.id)
|
||||
LEFT JOIN res_currency c ON (l.currency_id=c.id)
|
||||
LEFT JOIN res_partner p ON (l.partner_id=p.id)
|
||||
JOIN account_journal j ON (l.journal_id=j.id)
|
||||
WHERE l.account_id IN %s
|
||||
""" + init_filters + """
|
||||
GROUP BY l.account_id
|
||||
"""
|
||||
params = (tuple(accounts.ids) or (0,),) + tuple(init_where_params)
|
||||
cr.execute(sql, params)
|
||||
for row in cr.dictfetchall():
|
||||
move_lines[row.pop('account_id')].append(row)
|
||||
|
||||
# ------------------------------
|
||||
# 2. Sorting
|
||||
# ------------------------------
|
||||
sql_sort = 'l.date, l.move_id'
|
||||
if sortby == 'sort_journal_partner':
|
||||
sql_sort = 'j.code, p.name, l.move_id'
|
||||
|
||||
# ------------------------------
|
||||
# 3. Prepare SQL filters
|
||||
# ------------------------------
|
||||
tables, where_clause, where_params = move_line._query_get()
|
||||
wheres = [""]
|
||||
if where_clause.strip():
|
||||
wheres.append(where_clause.strip())
|
||||
filters = " AND ".join(wheres)
|
||||
filters = filters.replace('account_move_line__move_id', 'm').replace(
|
||||
'account_move_line', 'l'
|
||||
)
|
||||
|
||||
# ------------------------------
|
||||
# 4. Accounts fallback
|
||||
# ------------------------------
|
||||
if not accounts:
|
||||
# fallback: take receivable/payable accounts if none passed
|
||||
accounts = self.env['account.account'].search([
|
||||
('account_type', 'in', ['asset_receivable', 'liability_payable'])
|
||||
])
|
||||
|
||||
if not accounts.ids:
|
||||
return [] # no accounts, no results
|
||||
|
||||
account_ids = tuple(accounts.ids) or (0,)
|
||||
params = (account_ids,) + tuple(where_params)
|
||||
|
||||
# ------------------------------
|
||||
# 5. Main SQL query
|
||||
# ------------------------------
|
||||
sql = """
|
||||
SELECT l.id AS lid, l.account_id AS account_id, l.date AS ldate,
|
||||
j.code AS lcode, l.currency_id, l.amount_currency, l.ref AS lref,
|
||||
l.name AS lname, COALESCE(l.debit,0) AS debit,
|
||||
COALESCE(l.credit,0) AS credit,
|
||||
COALESCE(SUM(l.debit),0) - COALESCE(SUM(l.credit), 0) AS balance,
|
||||
m.name AS move_name, c.symbol AS currency_code, p.name AS partner_name
|
||||
FROM account_move_line l
|
||||
JOIN account_move m ON (l.move_id=m.id)
|
||||
LEFT JOIN res_currency c ON (l.currency_id=c.id)
|
||||
LEFT JOIN res_partner p ON (l.partner_id=p.id)
|
||||
JOIN account_journal j ON (l.journal_id=j.id)
|
||||
JOIN account_account acc ON (l.account_id = acc.id)
|
||||
WHERE l.account_id IN %s
|
||||
""" + filters + """
|
||||
GROUP BY l.id, l.account_id, l.date, j.code, l.currency_id,
|
||||
l.amount_currency, l.ref, l.name, m.name, c.symbol, p.name
|
||||
ORDER BY """ + sql_sort
|
||||
|
||||
cr.execute(sql, params)
|
||||
|
||||
# ------------------------------
|
||||
# 6. Attach lines to accounts
|
||||
# ------------------------------
|
||||
for row in cr.dictfetchall():
|
||||
balance = 0
|
||||
for line in move_lines.get(row['account_id'], []): # ✅ safe fallback
|
||||
balance += line['debit'] - line['credit']
|
||||
row['balance'] += balance
|
||||
|
||||
acc_id = row.pop('account_id')
|
||||
if acc_id not in move_lines:
|
||||
move_lines[acc_id] = [] # ✅ ensure list exists
|
||||
move_lines[acc_id].append(row)
|
||||
|
||||
# ------------------------------
|
||||
# 7. Build account results
|
||||
# ------------------------------
|
||||
account_res = []
|
||||
for account in accounts:
|
||||
account_company = self.env.company
|
||||
currency = account.currency_id or account_company.currency_id
|
||||
res = dict((fn, 0.0) for fn in ['credit', 'debit', 'balance'])
|
||||
res['code'] = account.code
|
||||
res['name'] = account.name
|
||||
res['move_lines'] = move_lines.get(account.id, []) # ✅ safe lookup
|
||||
|
||||
for line in res['move_lines']:
|
||||
res['debit'] += line['debit']
|
||||
res['credit'] += line['credit']
|
||||
res['balance'] = line['balance']
|
||||
|
||||
if display_account == 'all':
|
||||
account_res.append(res)
|
||||
elif display_account == 'movement' and res.get('move_lines'):
|
||||
account_res.append(res)
|
||||
elif display_account == 'not_zero' and not currency.is_zero(res['balance']):
|
||||
account_res.append(res)
|
||||
|
||||
return account_res
|
||||
|
||||
@api.model
|
||||
def _get_report_values(self, docids, data=None):
|
||||
if not data.get('form') or not self.env.context.get('active_model'):
|
||||
raise UserError(
|
||||
_("Form content is missing, this report cannot be printed."))
|
||||
|
||||
model = self.env.context.get('active_model')
|
||||
docs = self.env[model].browse(
|
||||
self.env.context.get('active_ids', []))
|
||||
init_balance = data['form'].get('initial_balance', True)
|
||||
sortby = data['form'].get('sortby', 'sort_date')
|
||||
display_account = 'movement'
|
||||
codes = []
|
||||
if data['form'].get('journal_ids', False):
|
||||
codes = [journal.code for journal in
|
||||
self.env['account.journal'].search(
|
||||
[('id', 'in', data['form']['journal_ids'])])]
|
||||
account_ids = data['form']['account_ids']
|
||||
accounts = self.env['account.account'].search(
|
||||
[('id', 'in', account_ids)])
|
||||
if not accounts:
|
||||
journals = self.env['account.journal'].search(
|
||||
[('type', '=', 'cash')])
|
||||
accounts = []
|
||||
for journal in journals:
|
||||
accounts.append(
|
||||
journal.default_account_id.id)
|
||||
accounts = self.env['account.account'].search(
|
||||
[('id', 'in', accounts)])
|
||||
accounts_res = self.with_context(
|
||||
data['form'].get('used_context', {}))._get_account_move_entry(
|
||||
accounts,
|
||||
init_balance,
|
||||
sortby,
|
||||
display_account)
|
||||
return {
|
||||
'doc_ids': docids,
|
||||
'doc_model': model,
|
||||
'data': data['form'],
|
||||
'docs': docs,
|
||||
'time': time,
|
||||
'Accounts': accounts_res,
|
||||
'print_journal': codes,
|
||||
}
|
||||
167
addons/base_accounting_kit/report/account_cash_book_template.xml
Normal file
@@ -0,0 +1,167 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<template id="report_cash_book">
|
||||
<t t-call="web.html_container">
|
||||
<t t-set="data_report_margin_top" t-value="12"/>
|
||||
<t t-set="data_report_header_spacing" t-value="9"/>
|
||||
<t t-set="data_report_dpi" t-value="110"/>
|
||||
<t t-call="web.internal_layout">
|
||||
<div class="page"><br/>
|
||||
<h2><span t-esc="env.company.name"/>: Cash Book Report
|
||||
</h2>
|
||||
<div class="row">
|
||||
<div class="col-xs-4" style="width:40%;">
|
||||
<strong>Journals:</strong>
|
||||
<p t-esc="', '.join([ lt or '' for lt in
|
||||
print_journal ])"/>
|
||||
</div>
|
||||
<div class="col-xs-4" style="width:30%;">
|
||||
<strong>Display Account</strong>
|
||||
<p>
|
||||
<span t-if="data['display_account'] == 'all'">
|
||||
All accounts'
|
||||
</span>
|
||||
<span t-if="data['display_account'] ==
|
||||
'movement'">
|
||||
With movements
|
||||
</span>
|
||||
<span t-if="data['display_account'] ==
|
||||
'not_zero'">
|
||||
With balance not equal to zero
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-xs-4" style="width:30%;">
|
||||
<strong>Target Moves:</strong>
|
||||
<p t-if="data['target_move'] == 'all'">All Entries
|
||||
</p>
|
||||
<p t-if="data['target_move'] == 'posted'">All Posted
|
||||
Entries
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="row">
|
||||
<div style="width:70%;">
|
||||
<strong>Sorted By:</strong>
|
||||
<p t-if="data['sortby'] == 'sort_date'">Date</p>
|
||||
<p t-if="data['sortby'] == 'sort_journal_partner'">
|
||||
Journal and Partner
|
||||
</p>
|
||||
</div>
|
||||
<div style="width:30%;">
|
||||
<t t-if="data['date_from']">
|
||||
<strong>Date from :</strong>
|
||||
<span t-esc="data['date_from']"/>
|
||||
<br/>
|
||||
</t>
|
||||
<t t-if="data['date_to']">
|
||||
<strong>Date to :</strong>
|
||||
<span t-esc="data['date_to']"/>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<table class="table table-condensed">
|
||||
<thead>
|
||||
<tr class="text-centre">
|
||||
<th>Date</th>
|
||||
<th>JRNL</th>
|
||||
<th>Partner</th>
|
||||
<th>Ref</th>
|
||||
<th>Move</th>
|
||||
<th>Entry Label</th>
|
||||
<th>Debit</th>
|
||||
<th>Credit</th>
|
||||
<th>Balance</th>
|
||||
<th groups="base.group_multi_currency">
|
||||
Currency
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<t t-foreach="Accounts" t-as="account">
|
||||
<tr style="font-weight: bold;">
|
||||
<td colspan="6">
|
||||
<span style="color: white;"
|
||||
t-esc="'..'"/>
|
||||
<span t-esc="account['code']"/>
|
||||
<span t-esc="account['name']"/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-esc="account['debit']"
|
||||
t-options="{'widget': 'monetary',
|
||||
'display_currency':
|
||||
env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-esc="account['credit']"
|
||||
t-options="{'widget':
|
||||
'monetary', 'display_currency':
|
||||
env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-esc="account['balance']"
|
||||
t-options="{'widget': 'monetary',
|
||||
'display_currency':
|
||||
env.company.currency_id}"/>
|
||||
</td>
|
||||
<td groups="base.group_multi_currency"/>
|
||||
</tr>
|
||||
<tr t-foreach="account['move_lines']"
|
||||
t-as="line">
|
||||
<td>
|
||||
<span t-esc="line['ldate']"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="line['lcode']"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="line['partner_name']"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-if="line['lref']"
|
||||
t-esc="line['lref']"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="line['move_name']"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="line['lname']"/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-esc="line['debit']"
|
||||
t-options="{'widget': 'monetary',
|
||||
'display_currency':
|
||||
env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-esc="line['credit']"
|
||||
t-options="{'widget': 'monetary',
|
||||
'display_currency':
|
||||
env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-esc="line['balance']"
|
||||
t-options="{'widget': 'monetary',
|
||||
'display_currency':
|
||||
env.company.currency_id}"/>
|
||||
</td>
|
||||
<td t-if="line['amount_currency']"
|
||||
class="text-end"
|
||||
groups="base.group_multi_currency">
|
||||
<span t-esc="line['amount_currency']
|
||||
if line['amount_currency'] > 0.00 else
|
||||
''"/>
|
||||
<span t-esc="line['currency_code'] if
|
||||
line['amount_currency'] > 0.00 else ''"/>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
</odoo>
|
||||
130
addons/base_accounting_kit/report/account_day_book.py
Normal file
@@ -0,0 +1,130 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
import time
|
||||
from datetime import timedelta, datetime
|
||||
from odoo import api, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class DayBookPdfReport(models.AbstractModel):
|
||||
_name = 'report.base_accounting_kit.day_book_report_template'
|
||||
_description = 'Day Book Report'
|
||||
|
||||
def _get_account_move_entry(self, accounts, form_data, pass_date):
|
||||
cr = self.env.cr
|
||||
move_line = self.env['account.move.line']
|
||||
tables, where_clause, where_params = move_line._query_get()
|
||||
wheres = [""]
|
||||
if where_clause.strip():
|
||||
wheres.append(where_clause.strip())
|
||||
if form_data['target_move'] == 'posted':
|
||||
target_move = "AND m.state = 'posted'"
|
||||
else:
|
||||
target_move = ''
|
||||
sql = ('''
|
||||
SELECT l.id AS lid, acc.name as accname, l.account_id AS
|
||||
account_id, l.date AS ldate, j.code AS lcode, l.currency_id,
|
||||
l.amount_currency, l.ref AS lref, l.name AS lname,
|
||||
COALESCE(l.debit,0) AS debit, COALESCE(l.credit,0) AS credit,
|
||||
COALESCE(SUM(l.debit),0) - COALESCE(SUM(l.credit), 0) AS
|
||||
balance,
|
||||
m.name AS move_name, c.symbol AS currency_code, p.name
|
||||
AS partner_name
|
||||
FROM account_move_line l
|
||||
JOIN account_move m ON (l.move_id=m.id)
|
||||
LEFT JOIN res_currency c ON (l.currency_id=c.id)
|
||||
LEFT JOIN res_partner p ON (l.partner_id=p.id)
|
||||
JOIN account_journal j ON (l.journal_id=j.id)
|
||||
JOIN account_account acc ON (l.account_id = acc.id)
|
||||
WHERE l.account_id IN %s AND l.journal_id IN %s '''
|
||||
+ target_move + ''' AND l.date = %s
|
||||
GROUP BY l.id, l.account_id, l.date,
|
||||
j.code, l.currency_id, l.amount_currency, l.ref,
|
||||
l.name, m.name, c.symbol, p.name , acc.name
|
||||
ORDER BY l.date DESC
|
||||
''')
|
||||
params = (
|
||||
tuple(accounts.ids), tuple(form_data['journal_ids']), pass_date)
|
||||
cr.execute(sql, params)
|
||||
data = cr.dictfetchall()
|
||||
res = {}
|
||||
debit = credit = balance = 0.00
|
||||
for line in data:
|
||||
debit += line['debit']
|
||||
credit += line['credit']
|
||||
balance += line['balance']
|
||||
res['debit'] = debit
|
||||
res['credit'] = credit
|
||||
res['balance'] = balance
|
||||
res['lines'] = data
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def _get_report_values(self, docids, data=None):
|
||||
if not data.get('form') or not self.env.context.get('active_model'):
|
||||
raise UserError(
|
||||
_("Form content is missing, this report cannot be printed."))
|
||||
|
||||
model = self.env.context.get('active_model')
|
||||
docs = self.env[model].browse(
|
||||
self.env.context.get('active_ids', []))
|
||||
form_data = data['form']
|
||||
codes = []
|
||||
if data['form'].get('journal_ids', False):
|
||||
codes = [journal.code for journal in
|
||||
self.env['account.journal'].search(
|
||||
[('id', 'in', data['form']['journal_ids'])])]
|
||||
active_acc = data['form']['account_ids']
|
||||
accounts = self.env['account.account'].search(
|
||||
[('id', 'in', active_acc)]) if data['form']['account_ids'] else \
|
||||
self.env['account.account'].search([])
|
||||
|
||||
date_start = datetime.strptime(form_data['date_from'],
|
||||
'%Y-%m-%d').date()
|
||||
date_end = datetime.strptime(form_data['date_to'], '%Y-%m-%d').date()
|
||||
days = date_end - date_start
|
||||
dates = []
|
||||
record = []
|
||||
for i in range(days.days + 1):
|
||||
dates.append(date_start + timedelta(days=i))
|
||||
for head in dates:
|
||||
pass_date = str(head)
|
||||
accounts_res = self.with_context(
|
||||
data['form'].get('used_context', {}))._get_account_move_entry(
|
||||
accounts, form_data, pass_date)
|
||||
if accounts_res['lines']:
|
||||
record.append({
|
||||
'date': head,
|
||||
'debit': accounts_res['debit'],
|
||||
'credit': accounts_res['credit'],
|
||||
'balance': accounts_res['balance'],
|
||||
'child_lines': accounts_res['lines']
|
||||
})
|
||||
return {
|
||||
'doc_ids': docids,
|
||||
'doc_model': model,
|
||||
'data': data['form'],
|
||||
'docs': docs,
|
||||
'time': time,
|
||||
'Accounts': record,
|
||||
'print_journal': codes,
|
||||
}
|
||||
115
addons/base_accounting_kit/report/account_day_book_template.xml
Normal file
@@ -0,0 +1,115 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<template id="day_book_report_template">
|
||||
<t t-call="web.html_container">
|
||||
<t t-set="data_report_margin_top" t-value="12"/>
|
||||
<t t-set="data_report_header_spacing" t-value="9"/>
|
||||
<t t-set="data_report_dpi" t-value="110"/>
|
||||
<t t-call="web.internal_layout">
|
||||
<div class="page"><br/>
|
||||
<h2><span t-esc="env.company.name"/>: Day Book Report
|
||||
</h2>
|
||||
<div class="row mt32" style="margin-bottom:3%;">
|
||||
<div class="col-7">
|
||||
<strong>Journals:</strong>
|
||||
<p t-esc="', '.join([ lt or '' for lt in print_journal ])"/>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<strong>Target Moves:</strong>
|
||||
<p t-if="data['target_move'] == 'all'">All Entries</p>
|
||||
<p t-if="data['target_move'] == 'posted'">All Posted Entries</p>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<t t-if="data['date_from']">
|
||||
<strong>Date from :</strong>
|
||||
<span t-esc="data['date_from']"/>
|
||||
<br/>
|
||||
</t>
|
||||
<t t-if="data['date_to']">
|
||||
<strong>Date to :</strong>
|
||||
<span t-esc="data['date_to']"/>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="table table-condensed">
|
||||
<thead>
|
||||
<tr class="text-center">
|
||||
<th>Date</th>
|
||||
<th>JRNL</th>
|
||||
<th>Partner</th>
|
||||
<th>Ref</th>
|
||||
<th>Move</th>
|
||||
<th>Entry Label</th>
|
||||
<th>Debit</th>
|
||||
<th>Credit</th>
|
||||
<th>Balance</th>
|
||||
<th groups="base.group_multi_currency">Currency</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<t t-foreach="Accounts" t-as="account">
|
||||
<tr style="font-weight: bold;background: #ededed;">
|
||||
<td colspan="6">
|
||||
<span style="color: white;" t-esc="'..'"/>
|
||||
<span t-esc="account['date']"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-esc="account['debit']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-esc="account['credit']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-esc="account['balance']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td groups="base.group_multi_currency"/>
|
||||
</tr>
|
||||
<tr t-foreach="account['child_lines']" t-as="line">
|
||||
<td>
|
||||
<span t-esc="line['ldate']"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="line['lcode']"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="line['partner_name']"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-if="line['lref']" t-esc="line['lref']"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="line['move_name']"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="line['lname']"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-esc="line['debit']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-esc="line['credit']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-esc="line['balance']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td t-if="line['amount_currency']" class="text-end" groups="base.group_multi_currency">
|
||||
<span t-esc="line['amount_currency'] if line['amount_currency'] > 0.00 else ''"/>
|
||||
<span t-esc="line['currency_code'] if line['amount_currency'] > 0.00 else ''"/>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
</odoo>
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from odoo import fields, models
|
||||
from odoo.tools.misc import get_lang
|
||||
|
||||
|
||||
class AccountCommonAccountReport(models.TransientModel):
|
||||
_name = 'account.common.account.report'
|
||||
_description = 'Account Common Account Report'
|
||||
_inherit = "account.report"
|
||||
|
||||
section_main_report_ids = fields.Many2many(string="Section Of",
|
||||
comodel_name='account.report',
|
||||
relation="account_common_report_section_rel",
|
||||
column1="sub_report_id",
|
||||
column2="main_report_id")
|
||||
section_report_ids = fields.Many2many(string="Sections",
|
||||
comodel_name='account.report',
|
||||
relation="account_common_report_section_rel",
|
||||
column1="main_report_id",
|
||||
column2="sub_report_id")
|
||||
display_account = fields.Selection(
|
||||
[('all', 'All'), ('movement', 'With movements'),
|
||||
('not_zero', 'With balance is not equal to 0')],
|
||||
string='Display Accounts', required=True, default='movement')
|
||||
target_move = fields.Selection([('posted', 'All Posted Entries'),
|
||||
('all', 'All Entries'),
|
||||
], string='Target Moves', required=True, default='posted')
|
||||
date_from = fields.Date(string='Start Date')
|
||||
date_to = fields.Date(string='End Date')
|
||||
company_id = fields.Many2one('res.company', string='Company', required=True, readonly=True,
|
||||
default=lambda self: self.env.company)
|
||||
|
||||
def _build_contexts(self, data):
|
||||
result = {}
|
||||
result['journal_ids'] = 'journal_ids' in data['form'] and data['form']['journal_ids'] or False
|
||||
result['state'] = 'target_move' in data['form'] and data['form']['target_move'] or ''
|
||||
result['date_from'] = data['form']['date_from'] or False
|
||||
result['date_to'] = data['form']['date_to'] or False
|
||||
result['strict_range'] = True if result['date_from'] else False
|
||||
result['company_id'] = data['form']['company_id'][0] or False
|
||||
return result
|
||||
|
||||
def _print_report(self, data):
|
||||
raise NotImplementedError()
|
||||
|
||||
def check_report(self):
|
||||
self.ensure_one()
|
||||
data = {}
|
||||
data['ids'] = self.env.context.get('active_ids', [])
|
||||
data['model'] = self.env.context.get('active_model', 'ir.ui.menu')
|
||||
data['form'] = self.read(['date_from', 'date_to', 'journal_ids', 'target_move', 'company_id'])[0]
|
||||
used_context = self._build_contexts(data)
|
||||
data['form']['used_context'] = dict(used_context, lang=get_lang(self.env).code)
|
||||
return self.with_context(discard_logo_check=True)._print_report(data)
|
||||
|
||||
def pre_print_report(self, data):
|
||||
data['form'].update(self.read(['display_account'])[0])
|
||||
return data
|
||||
215
addons/base_accounting_kit/report/cash_flow_report.py
Normal file
@@ -0,0 +1,215 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
import time
|
||||
from odoo import api, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class ReportFinancial(models.AbstractModel):
|
||||
_name = 'report.base_accounting_kit.report_cash_flow'
|
||||
_description = 'Cash Flow Report'
|
||||
|
||||
def _compute_account_balance(self, accounts):
|
||||
mapping = {
|
||||
'balance': "COALESCE(SUM(debit),0) - COALESCE(SUM(credit), 0) as balance",
|
||||
'debit': "COALESCE(SUM(debit), 0) as debit",
|
||||
'credit': "COALESCE(SUM(credit), 0) as credit",
|
||||
}
|
||||
|
||||
res = {}
|
||||
for account in accounts:
|
||||
res[account.id] = dict.fromkeys(mapping, 0.0)
|
||||
if accounts:
|
||||
tables, where_clause, where_params = self.env[
|
||||
'account.move.line']._query_get()
|
||||
tables = tables.replace('"', '') if tables else "account_move_line"
|
||||
wheres = [""]
|
||||
if where_clause.strip():
|
||||
wheres.append(where_clause.strip())
|
||||
filters = " AND ".join(wheres)
|
||||
request = "SELECT account_id as id, " + ', '.join(
|
||||
mapping.values()) + \
|
||||
" FROM " + tables + \
|
||||
" WHERE account_id IN %s " \
|
||||
+ filters + \
|
||||
" GROUP BY account_id"
|
||||
params = (tuple(accounts._ids),) + tuple(where_params)
|
||||
self.env.cr.execute(request, params)
|
||||
for row in self.env.cr.dictfetchall():
|
||||
res[row['id']] = row
|
||||
return res
|
||||
|
||||
def _compute_report_balance(self, reports):
|
||||
res = {}
|
||||
fields = ['credit', 'debit', 'balance']
|
||||
for report in reports:
|
||||
if report.id in res:
|
||||
continue
|
||||
res[report.id] = dict((fn, 0.0) for fn in fields)
|
||||
if report.type == 'accounts':
|
||||
# it's the sum of credit or debit
|
||||
res2 = self._compute_report_balance(report.parent_id)
|
||||
for key, value in res2.items():
|
||||
cash_in_operation = self.env.ref(
|
||||
'base_accounting_kit.cash_in_from_operation0')
|
||||
cash_out_operation = self.env.ref(
|
||||
'base_accounting_kit.cash_out_operation1')
|
||||
cash_in_financial = self.env.ref(
|
||||
'base_accounting_kit.cash_in_financial0')
|
||||
cash_out_financial = self.env.ref(
|
||||
'base_accounting_kit.cash_out_financial1')
|
||||
cash_in_investing = self.env.ref(
|
||||
'base_accounting_kit.cash_in_investing0')
|
||||
cash_out_investing = self.env.ref(
|
||||
'base_accounting_kit.cash_out_investing1')
|
||||
if report == cash_in_operation or report == cash_in_financial or report == cash_in_investing:
|
||||
res[report.id]['debit'] += value['debit']
|
||||
res[report.id]['balance'] += value['debit']
|
||||
elif report == cash_out_operation or report == cash_out_financial or report == cash_out_investing:
|
||||
res[report.id]['credit'] += value['credit']
|
||||
res[report.id]['balance'] += -(value['credit'])
|
||||
elif report.type == 'account_type':
|
||||
# it's the sum the leaf accounts with such an account type
|
||||
accounts = self.env['account.account'].search(
|
||||
[('account_type', 'in', report.account_type_ids)])
|
||||
res[report.id]['account'] = self._compute_account_balance(
|
||||
accounts)
|
||||
for value in res[report.id]['account'].values():
|
||||
for field in fields:
|
||||
res[report.id][field] += value.get(field)
|
||||
elif report.type == 'account_report' and report.account_report_id:
|
||||
# it's the amount of the linked
|
||||
res[report.id]['account'] = self._compute_account_balance(
|
||||
report.account_ids)
|
||||
for value in res[report.id]['account'].values():
|
||||
for field in fields:
|
||||
res[report.id][field] += value.get(field)
|
||||
|
||||
elif report.type == 'sum':
|
||||
# it's the sum of the linked accounts
|
||||
res[report.id]['account'] = self._compute_account_balance(
|
||||
report.account_ids)
|
||||
for values in res[report.id]['account'].values():
|
||||
for field in fields:
|
||||
res[report.id][field] += values.get(field)
|
||||
return res
|
||||
|
||||
def get_account_lines(self, data):
|
||||
lines = []
|
||||
account_report = self.env['account.financial.report'].search(
|
||||
[('id', '=', data['account_report_id'][0])])
|
||||
child_reports = account_report._get_children_by_order()
|
||||
res = self.with_context(
|
||||
data.get('used_context'))._compute_report_balance(child_reports)
|
||||
if data['enable_filter']:
|
||||
comparison_res = self.with_context(
|
||||
data.get('comparison_context'))._compute_report_balance(
|
||||
child_reports)
|
||||
for report_id, value in comparison_res.items():
|
||||
res[report_id]['comp_bal'] = value['balance']
|
||||
report_acc = res[report_id].get('account')
|
||||
if report_acc:
|
||||
for account_id, val in comparison_res[report_id].get(
|
||||
'account').items():
|
||||
report_acc[account_id]['comp_bal'] = val['balance']
|
||||
|
||||
for report in child_reports:
|
||||
vals = {
|
||||
'name': report.name,
|
||||
'balance': res[report.id]['balance'] * int(report.sign),
|
||||
'type': 'report',
|
||||
'level': bool(report.style_overwrite) and int(
|
||||
report.style_overwrite) or report.level,
|
||||
'account_type': report.type or False,
|
||||
# used to underline the financial report balances
|
||||
}
|
||||
if data['debit_credit']:
|
||||
vals['debit'] = res[report.id]['debit']
|
||||
vals['credit'] = res[report.id]['credit']
|
||||
|
||||
if data['enable_filter']:
|
||||
vals['balance_cmp'] = res[report.id]['comp_bal'] * int(
|
||||
report.sign)
|
||||
|
||||
lines.append(vals)
|
||||
if report.display_detail == 'no_detail':
|
||||
# the rest of the loop is used to display the details of the financial report, so it's not needed here.
|
||||
continue
|
||||
if res[report.id].get('account'):
|
||||
# if res[report.id].get('debit'):
|
||||
sub_lines = []
|
||||
for account_id, value in res[report.id]['account'].items():
|
||||
# if there are accounts to display, we add them to the
|
||||
# lines with a level equals to their level in
|
||||
# the COA + 1 (to avoid having them with a too low level
|
||||
# that would conflicts with the level of data
|
||||
# financial reports for Assets, liabilities...)
|
||||
flag = False
|
||||
account = self.env['account.account'].browse(account_id)
|
||||
vals = {
|
||||
'name': account.code + ' ' + account.name,
|
||||
'balance': value['balance'] * int(report.sign) or 0.0,
|
||||
'type': 'account',
|
||||
'level': report.display_detail == 'detail_with_hierarchy' and 4,
|
||||
'account_type': account.internal_type,
|
||||
}
|
||||
if data['debit_credit']:
|
||||
vals['debit'] = value['debit']
|
||||
vals['credit'] = value['credit']
|
||||
if not account.company_id.currency_id.is_zero(
|
||||
vals[
|
||||
'debit']) or not account.company_id.currency_id.is_zero(
|
||||
vals['credit']):
|
||||
flag = True
|
||||
if not account.company_id.currency_id.is_zero(
|
||||
vals['balance']):
|
||||
flag = True
|
||||
if data['enable_filter']:
|
||||
vals['balance_cmp'] = value['comp_bal'] * int(
|
||||
report.sign)
|
||||
if not account.company_id.currency_id.is_zero(
|
||||
vals['balance_cmp']):
|
||||
flag = True
|
||||
if flag:
|
||||
sub_lines.append(vals)
|
||||
lines += sorted(sub_lines,
|
||||
key=lambda sub_line: sub_line['name'])
|
||||
return lines
|
||||
|
||||
@api.model
|
||||
def _get_report_values(self, docids, data=None):
|
||||
if not data.get('form') or not self.env.context.get(
|
||||
'active_model') or not self.env.context.get('active_id'):
|
||||
raise UserError(
|
||||
_("Form content is missing, this report cannot be printed."))
|
||||
|
||||
model = self.env.context.get('active_model')
|
||||
docs = self.env[model].browse(self.env.context.get('active_id'))
|
||||
report_lines = self.get_account_lines(data.get('form'))
|
||||
return {
|
||||
'doc_ids': self.ids,
|
||||
'doc_model': model,
|
||||
'data': data['form'],
|
||||
'docs': docs,
|
||||
'time': time,
|
||||
'get_account_lines': report_lines,
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<template id="report_cash_flow">
|
||||
<t t-call="web.html_container">
|
||||
<t t-foreach="docs" t-as="o">
|
||||
<t t-call="web.internal_layout">
|
||||
<div class="page">
|
||||
<h2 t-esc="data['account_report_id'][1]"/>
|
||||
|
||||
<div class="row mt32 mb32">
|
||||
<div class="col-4">
|
||||
<strong>Target Moves:</strong>
|
||||
<p>
|
||||
<span t-if="data['target_move'] == 'all'">All Entries</span>
|
||||
<span t-if="data['target_move'] == 'posted'">All Posted Entries</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<p>
|
||||
<strong>Date from :</strong>
|
||||
<span t-esc="data['date_from']"/>
|
||||
<br/>
|
||||
<strong>Date to :</strong>
|
||||
<span t-esc="data['date_to']"/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-sm table-reports">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<strong>Name</strong>
|
||||
</th>
|
||||
<th class="text-right" t-if="data['debit_credit']">
|
||||
<strong>Debit</strong>
|
||||
</th>
|
||||
<th class="text-right" t-if="data['debit_credit']">
|
||||
<strong>Credit</strong>
|
||||
</th>
|
||||
<th class="text-right">
|
||||
<strong>Balance</strong>
|
||||
</th>
|
||||
<th class="text-right" t-if="data['enable_filter']">
|
||||
<strong t-esc="data['label_filter']"/>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="get_account_lines" t-as="a">
|
||||
<t t-if="a['level'] != 0">
|
||||
<t t-if="a.get('level') > 3">
|
||||
<t t-set="style" t-value="'font-weight: normal;'"/>
|
||||
</t>
|
||||
<t t-if="not a.get('level') > 3">
|
||||
<t t-set="style" t-value="'font-weight: bold;'"/>
|
||||
</t>
|
||||
<td>
|
||||
<span style="color: white;" t-esc="'..' * a.get('level', 0)"/>
|
||||
<span t-att-style="style" t-esc="a.get('name')"/>
|
||||
</td>
|
||||
<td t-if="data['debit_credit']" class="text-right" style="white-space: text-nowrap;">
|
||||
<span t-att-style="style" t-esc="a.get('debit')" t-if="data['debit_credit']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td t-if="data['debit_credit']" class="text-right" style="white-space: text-nowrap;">
|
||||
<span t-att-style="style" t-esc="a.get('credit')"
|
||||
t-if="data['debit_credit']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-right" style="white-space: text-nowrap;">
|
||||
<span t-att-style="style" t-esc="a.get('balance')"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-right" t-if="data['enable_filter']">
|
||||
<span t-att-style="style" t-esc="a.get('balance_cmp')"
|
||||
t-if="data['enable_filter']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
</t>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
</odoo>
|
||||
183
addons/base_accounting_kit/report/general_ledger_report.py
Normal file
@@ -0,0 +1,183 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
import time
|
||||
from odoo import api, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class ReportGeneralLedger(models.AbstractModel):
|
||||
_name = 'report.base_accounting_kit.report_general_ledger'
|
||||
_description = 'General Ledger Report'
|
||||
|
||||
def _get_account_move_entry(self, accounts, init_balance, sortby,
|
||||
display_account):
|
||||
"""
|
||||
:param:
|
||||
accounts: the recordset of accounts
|
||||
init_balance: boolean value of initial_balance
|
||||
sortby: sorting by date or partner and journal
|
||||
display_account: type of account(receivable, payable and both)
|
||||
|
||||
Returns a dictionary of accounts with following key and value {
|
||||
'code': account code,
|
||||
'name': account name,
|
||||
'debit': sum of total debit amount,
|
||||
'credit': sum of total credit amount,
|
||||
'balance': total balance,
|
||||
'amount_currency': sum of amount_currency,
|
||||
'move_lines': list of move line
|
||||
}
|
||||
"""
|
||||
cr = self.env.cr
|
||||
MoveLine = self.env['account.move.line']
|
||||
move_lines = {x: [] for x in accounts.ids}
|
||||
|
||||
# Prepare initial sql query and Get the initial move lines
|
||||
if init_balance:
|
||||
init_tables, init_where_clause, init_where_params = MoveLine.with_context(
|
||||
date_from=self.env.context.get('date_from'), date_to=False,
|
||||
initial_bal=True)._query_get()
|
||||
init_wheres = [""]
|
||||
if init_where_clause.strip():
|
||||
init_wheres.append(init_where_clause.strip())
|
||||
init_filters = " AND ".join(init_wheres)
|
||||
filters = init_filters.replace('account_move_line__move_id',
|
||||
'm').replace('account_move_line',
|
||||
'l')
|
||||
sql = ("""SELECT 0 AS lid, l.account_id AS account_id, ''
|
||||
AS ldate, '' AS lcode, 0.0 AS amount_currency, '' AS lref,
|
||||
'Initial Balance' AS lname, COALESCE(SUM(l.debit),0.0) AS debit,
|
||||
COALESCE(SUM(l.credit),0.0) AS credit, COALESCE(SUM(l.debit),0)
|
||||
- COALESCE(SUM(l.credit), 0) as balance, '' AS lpartner_id,\
|
||||
'' AS move_name, '' AS mmove_id, '' AS currency_code,\
|
||||
NULL AS currency_id,\
|
||||
'' AS invoice_id, '' AS invoice_type, '' AS invoice_number,\
|
||||
'' AS partner_name\
|
||||
FROM account_move_line l\
|
||||
LEFT JOIN account_move m ON (l.move_id=m.id)\
|
||||
LEFT JOIN res_currency c ON (l.currency_id=c.id)\
|
||||
LEFT JOIN res_partner p ON (l.partner_id=p.id)\
|
||||
LEFT JOIN account_move i ON (m.id =i.id)\
|
||||
JOIN account_journal j ON (l.journal_id=j.id)\
|
||||
WHERE l.account_id IN %s""" + filters +
|
||||
' GROUP BY l.account_id')
|
||||
params = (tuple(accounts.ids),) + tuple(init_where_params)
|
||||
cr.execute(sql, params)
|
||||
for row in cr.dictfetchall():
|
||||
move_lines[row.pop('account_id')].append(row)
|
||||
|
||||
sql_sort = 'l.date, l.move_id'
|
||||
if sortby == 'sort_journal_partner':
|
||||
sql_sort = 'j.code, p.name, l.move_id'
|
||||
|
||||
# Prepare sql query base on selected parameters from wizard
|
||||
tables, where_clause, where_params = MoveLine._query_get()
|
||||
wheres = [""]
|
||||
if where_clause.strip():
|
||||
wheres.append(where_clause.strip())
|
||||
filters = " AND ".join(wheres)
|
||||
filters = filters.replace('account_move_line__move_id', 'm').replace(
|
||||
'account_move_line', 'l')
|
||||
|
||||
# Get move lines base on sql query and Calculate the total balance of move lines
|
||||
sql = ('''SELECT l.id AS lid, l.account_id AS account_id,
|
||||
l.date AS ldate, j.code AS lcode, l.currency_id, l.amount_currency,
|
||||
l.ref AS lref, l.name AS lname, COALESCE(l.debit,0) AS debit,
|
||||
COALESCE(l.credit,0) AS credit, COALESCE(SUM(l.debit),0) -
|
||||
COALESCE(SUM(l.credit), 0) AS balance,\
|
||||
m.name AS move_name, c.symbol AS currency_code, p.name AS
|
||||
partner_name\
|
||||
FROM account_move_line l\
|
||||
JOIN account_move m ON (l.move_id=m.id)\
|
||||
LEFT JOIN res_currency c ON (l.currency_id=c.id)\
|
||||
LEFT JOIN res_partner p ON (l.partner_id=p.id)\
|
||||
JOIN account_journal j ON (l.journal_id=j.id)\
|
||||
JOIN account_account acc ON (l.account_id = acc.id) \
|
||||
WHERE l.account_id IN %s ''' + filters + ''' GROUP BY l.id,
|
||||
l.account_id, l.date, j.code, l.currency_id, l.amount_currency,
|
||||
l.ref, l.name, m.name, c.symbol, p.name ORDER BY ''' + sql_sort)
|
||||
params = (tuple(accounts.ids),) + tuple(where_params)
|
||||
cr.execute(sql, params)
|
||||
|
||||
for row in cr.dictfetchall():
|
||||
balance = 0
|
||||
for line in move_lines.get(row['account_id']):
|
||||
balance += line['debit'] - line['credit']
|
||||
row['balance'] += balance
|
||||
move_lines[row.pop('account_id')].append(row)
|
||||
|
||||
# Calculate the debit, credit and balance for Accounts
|
||||
account_res = []
|
||||
for account in accounts:
|
||||
account_company = self.env.company
|
||||
currency = account.currency_id and account.currency_id or account_company.currency_id
|
||||
res = dict((fn, 0.0) for fn in ['credit', 'debit', 'balance'])
|
||||
res['code'] = account.code
|
||||
res['name'] = account.name
|
||||
res['move_lines'] = move_lines[account.id]
|
||||
for line in res.get('move_lines'):
|
||||
res['debit'] += line['debit']
|
||||
res['credit'] += line['credit']
|
||||
res['balance'] = line['balance']
|
||||
if display_account == 'all':
|
||||
account_res.append(res)
|
||||
if display_account == 'movement' and res.get('move_lines'):
|
||||
account_res.append(res)
|
||||
if display_account == 'not_zero' and not currency.is_zero(
|
||||
res['balance']):
|
||||
account_res.append(res)
|
||||
|
||||
return account_res
|
||||
|
||||
@api.model
|
||||
def _get_report_values(self, docids, data=None):
|
||||
if not data.get('form') or not self.env.context.get('active_model'):
|
||||
raise UserError(
|
||||
_("Form content is missing, this report cannot be printed."))
|
||||
|
||||
model = self.env.context.get('active_model')
|
||||
docs = self.env[model].browse(
|
||||
self.env.context.get('active_ids', []))
|
||||
|
||||
init_balance = data['form'].get('initial_balance', True)
|
||||
sortby = data['form'].get('sortby', 'sort_date')
|
||||
display_account = data['form']['display_account']
|
||||
codes = []
|
||||
if data['form'].get('journal_ids', False):
|
||||
codes = [journal.code for journal in
|
||||
self.env['account.journal'].search(
|
||||
[('id', 'in', data['form']['journal_ids'])])]
|
||||
|
||||
accounts = docs if model == 'account.account' else self.env[
|
||||
'account.account'].search([])
|
||||
accounts_res = self.with_context(
|
||||
data['form'].get('used_context', {}))._get_account_move_entry(
|
||||
accounts, init_balance, sortby, display_account)
|
||||
return {
|
||||
'doc_ids': docids,
|
||||
'doc_model': model,
|
||||
'data': data['form'],
|
||||
'docs': docs,
|
||||
'time': time,
|
||||
'Accounts': accounts_res,
|
||||
'print_journal': codes,
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<template id="report_general_ledger">
|
||||
<t t-call="web.html_container">
|
||||
<t t-set="data_report_margin_top" t-value="12"/>
|
||||
<t t-set="data_report_header_spacing" t-value="9"/>
|
||||
<t t-set="data_report_dpi" t-value="110"/>
|
||||
<t t-call="web.internal_layout">
|
||||
<div class="page"><br/>
|
||||
<h2><span t-esc="env.company.name"/>: General ledger</h2>
|
||||
<div class="row mt32">
|
||||
<div class="col-4">
|
||||
<strong>Journals:</strong>
|
||||
<p t-esc="', '.join([ lt or '' for lt in print_journal ])"/>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<strong>Display Account</strong>
|
||||
<p>
|
||||
<span t-if="data['display_account'] == 'all'">All accounts'</span>
|
||||
<span t-if="data['display_account'] == 'movement'">With movements</span>
|
||||
<span t-if="data['display_account'] == 'not_zero'">With balance not equal to zero</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<strong>Target Moves:</strong>
|
||||
<p t-if="data['target_move'] == 'all'">All Entries</p>
|
||||
<p t-if="data['target_move'] == 'posted'">All Posted Entries</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb32">
|
||||
<div class="col-4">
|
||||
<strong>Sorted By:</strong>
|
||||
<p t-if="data['sortby'] == 'sort_date'">Date</p>
|
||||
<p t-if="data['sortby'] == 'sort_journal_partner'">Journal and Partner</p>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<t t-if="data['date_from']"><strong>Date from :</strong> <span t-esc="data['date_from']"/><br/></t>
|
||||
<t t-if="data['date_to']"><strong>Date to :</strong> <span t-esc="data['date_to']"/></t>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="table table-sm table-reports">
|
||||
<thead>
|
||||
<tr class="text-center">
|
||||
<th>Date</th>
|
||||
<th>JRNL</th>
|
||||
<th>Partner</th>
|
||||
<th>Ref</th>
|
||||
<th>Move</th>
|
||||
<th>Entry Label</th>
|
||||
<th>Debit</th>
|
||||
<th>Credit</th>
|
||||
<th>Balance</th>
|
||||
<th groups="base.group_multi_currency">Currency</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<t t-foreach="Accounts" t-as="account">
|
||||
<tr style="font-weight: bold;">
|
||||
<td colspan="6">
|
||||
<span style="color: white;" t-esc="'..'"/>
|
||||
<span t-esc="account['code']"/>
|
||||
<span t-esc="account['name']"/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-esc="account['debit']" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-esc="account['credit']" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-esc="account['balance']" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td groups="base.group_multi_currency"/>
|
||||
</tr>
|
||||
<tr t-foreach="account['move_lines']" t-as="line">
|
||||
<td><span t-esc="line['ldate']"/></td>
|
||||
<td><span t-esc="line['lcode']"/></td>
|
||||
<td><span t-esc="line['partner_name']"/></td>
|
||||
<td><span t-if="line['lref']" t-esc="line['lref']"/></td>
|
||||
<td><span t-esc="line['move_name']"/></td>
|
||||
<td><span t-esc="line['lname']"/></td>
|
||||
<td class="text-right">
|
||||
<span t-esc="line['debit']" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-esc="line['credit']"
|
||||
t-options="{'widget': 'monetary',
|
||||
'display_currency':
|
||||
env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-esc="line['balance']"
|
||||
t-options="{'widget': 'monetary',
|
||||
'display_currency':
|
||||
env.company.currency_id}"/>
|
||||
</td>
|
||||
<t t-if="line['amount_currency']">
|
||||
<td class="text-end"
|
||||
groups="base.group_multi_currency">
|
||||
<span t-esc="line['amount_currency']
|
||||
if line['amount_currency'] > 0.00
|
||||
else ''"/>
|
||||
<span t-esc="line['currency_code']
|
||||
if line['amount_currency'] > 0.00
|
||||
else ''"/>
|
||||
</td>
|
||||
</t>
|
||||
</tr>
|
||||
</t>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
</odoo>
|
||||
645
addons/base_accounting_kit/report/multiple_invoice_layouts.xml
Normal file
@@ -0,0 +1,645 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<template id="base_accounting_kit.standard">
|
||||
<div t-attf-class="header o_company_#{company.id}_layout"
|
||||
t-att-style="report_header_style">
|
||||
<div class="row">
|
||||
<div class="col-3 mb4">
|
||||
<img t-if="company.logo"
|
||||
t-att-src="image_data_uri(company.logo)"
|
||||
style="max-height: 45px;" alt="Logo"/>
|
||||
<!--Header-->
|
||||
<t t-if="mi_type == 'text'">
|
||||
<t t-if="txt_position == 'header'">
|
||||
<div class="row">
|
||||
<div t-if="txt_align == 'left'"
|
||||
class="text-left">
|
||||
<span t-esc="mi.copy_name"
|
||||
style="font-size: 20px; padding-left:25px; white-space:nowrap;"/>
|
||||
</div>
|
||||
<div t-if="txt_align == 'center'"
|
||||
class="text-center">
|
||||
<span t-esc="mi.copy_name" style="font-size: 20px;
|
||||
margin-left:340px; margin-right:340px; white-space:nowrap;"/>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-9 text-right" style="margin-top:22px;"
|
||||
t-field="company.report_header" name="moto"/>
|
||||
</div>
|
||||
<!--Header-->
|
||||
<t t-if="mi_type == 'text'">
|
||||
<t t-if="txt_position == 'header'">
|
||||
<div t-if="txt_align == 'right'" class="text-right">
|
||||
<span t-esc="mi.copy_name" style="font-size: 20px;"/>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
<div t-if="company.logo or company.report_header"
|
||||
class="row zero_min_height">
|
||||
<div class="col-12">
|
||||
<div style="border-bottom: 1px solid black;"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6" name="company_address">
|
||||
<span t-if="company.company_details"
|
||||
t-field="company.company_details"></span>
|
||||
</div>
|
||||
</div>
|
||||
<!--Watermark-->
|
||||
<t t-if="mi_type =='watermark'">
|
||||
<div style="opacity:0.15; font-size:100px; width:85%; text-align:center;top:500px; right:100px; position: fixed; z-index:99; -webkit-transform: rotate(-30deg);">
|
||||
<t t-esc="mi.copy_name"/>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
|
||||
<div t-attf-class="article o_report_layout_standard o_company_#{company.id}_layout {{ 'o_layout_background' if company.layout_background in ['Geometric', 'Custom'] else '' }}"
|
||||
t-attf-style="background-image: url({{ 'data:image/png;base64,%s' % company.layout_background_image.decode('utf-8') if company.layout_background_image and company.layout_background == 'Custom' else '/base/static/img/bg_background_template.jpg' if company.layout_background == 'Geometric' else ''}});"
|
||||
t-att-data-oe-model="o and o._name" t-att-data-oe-id="o and o.id"
|
||||
t-att-data-oe-lang="o and o.env.context.get('lang')">
|
||||
<div class="pt-5">
|
||||
<!-- This div ensures that the address is not cropped by the header. -->
|
||||
<t t-call="web.address_layout"/>
|
||||
</div>
|
||||
<t t-out="0"/>
|
||||
</div>
|
||||
<div t-attf-class="footer o_standard_footer o_company_#{company.id}_layout">
|
||||
<div class="text-center" style="border-top: 1px solid black;">
|
||||
<ul class="list-inline mb4">
|
||||
<div t-field="company.report_footer"/>
|
||||
<!--Footer-->
|
||||
<t t-if="mi_type == 'text'">
|
||||
<t t-if="txt_position == 'footer'">
|
||||
<div t-if="txt_align == 'right'" class="text-right">
|
||||
<span t-esc="mi.copy_name"
|
||||
style="font-size: 15px;"/>
|
||||
</div>
|
||||
<div t-if="txt_align == 'left'" class="text-left">
|
||||
<span t-esc="mi.copy_name"
|
||||
style="font-size: 15px;"/>
|
||||
</div>
|
||||
<div t-if="txt_align == 'center'"
|
||||
class="text-center;">
|
||||
<span t-esc="mi.copy_name"
|
||||
style="font-size: 15px;"/>
|
||||
</div>
|
||||
|
||||
</t>
|
||||
</t>
|
||||
|
||||
</ul>
|
||||
|
||||
<div t-if="report_type == 'pdf'" class="text-muted">
|
||||
Page:
|
||||
<span class="page"/>
|
||||
/
|
||||
<span class="topage"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="base_accounting_kit.boxed">
|
||||
<div t-attf-class="header o_company_#{company.id}_layout"
|
||||
t-att-style="report_header_style">
|
||||
<div class="o_boxed_header">
|
||||
<div class="row mb8">
|
||||
<div class="col-6">
|
||||
<img t-if="company.logo"
|
||||
t-att-src="image_data_uri(company.logo)"
|
||||
alt="Logo"/>
|
||||
<!--Header-->
|
||||
<t t-if="mi_type == 'text'">
|
||||
<t t-if="txt_position == 'header'">
|
||||
<div t-if="txt_align == 'left'">
|
||||
<span t-esc="mi.copy_name"
|
||||
style="font-size: 25px; white-space:nowrap;"/>
|
||||
</div>
|
||||
<div t-if="txt_align == 'center'"
|
||||
class="text-align: center">
|
||||
<span t-esc="mi.copy_name" style="font-size: 25px;
|
||||
margin-left:340px; margin-right:340px; white-space:nowrap;"/>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</div>
|
||||
|
||||
<div class="col-6 text-right mb4">
|
||||
<h4 class="mt0" t-field="company.report_header"/>
|
||||
<div name="company_address" class="float-right mb4">
|
||||
<span t-if="company.company_details"
|
||||
t-field="company.company_details"/>
|
||||
<!--Header-->
|
||||
<t t-if="mi_type == 'text'">
|
||||
<t t-if="txt_position == 'header'">
|
||||
<div t-if="txt_align == 'right'"
|
||||
class="float-right mb4">
|
||||
<span t-esc="mi.copy_name"
|
||||
style="font-size: 25px;"/>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
<br/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--Watermark-->
|
||||
<t t-if="mi_type =='watermark'">
|
||||
<div style="opacity:0.15; font-size:100px; width:85%; text-align:center;top:500px; right:100px; position: fixed; z-index:99; -webkit-transform: rotate(-30deg);">
|
||||
<t t-esc="mi.copy_name"/>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
|
||||
<div t-attf-class="article o_report_layout_boxed o_company_#{company.id}_layout {{ 'o_layout_background' if company.layout_background in ['Geometric', 'Custom'] else '' }}"
|
||||
t-attf-style="background-image: url({{ 'data:image/png;base64,%s' % company.layout_background_image.decode('utf-8') if company.layout_background_image and company.layout_background == 'Custom' else '/base/static/img/bg_background_template.jpg' }});"
|
||||
t-att-data-oe-model="o and o._name" t-att-data-oe-id="o and o.id"
|
||||
t-att-data-oe-lang="o and o.env.context.get('lang')">
|
||||
<div class="pt-5">
|
||||
<!-- This div ensures that the address is not cropped by the header. -->
|
||||
<t t-call="web.address_layout"/>
|
||||
</div>
|
||||
<t t-out="0"/>
|
||||
</div>
|
||||
|
||||
<div t-attf-class="footer o_boxed_footer o_company_#{company.id}_layout">
|
||||
<div class="text-center">
|
||||
<div t-field="company.report_footer"/>
|
||||
<!--Footer-->
|
||||
<t t-if="mi_type == 'text'">
|
||||
<t t-if="txt_position == 'footer'">
|
||||
<div t-if="txt_align == 'right'" class="text-right">
|
||||
<span t-esc="mi.copy_name"
|
||||
style="font-size: 20px;"/>
|
||||
</div>
|
||||
<div t-if="txt_align == 'left'" class="text-left">
|
||||
<span t-esc="mi.copy_name"
|
||||
style="font-size: 20px;"/>
|
||||
</div>
|
||||
<div t-if="txt_align == 'center'" class="text-center;">
|
||||
<span t-esc="mi.copy_name"
|
||||
style="font-size: 20px;"/>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<div t-if="report_type == 'pdf'">
|
||||
Page:
|
||||
<span class="page"/>
|
||||
/
|
||||
<span class="topage"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<template id="base_accounting_kit.bold">
|
||||
<div t-attf-class="header o_company_#{company.id}_layout"
|
||||
t-att-style="report_header_style">
|
||||
<div class="o_clean_header">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<img t-if="company.logo"
|
||||
t-att-src="image_data_uri(company.logo)"
|
||||
alt="Logo"/>
|
||||
<!--Header-->
|
||||
<t t-if="mi_type == 'text'">
|
||||
<t t-if="txt_position == 'header'">
|
||||
<div t-if="txt_align == 'left'">
|
||||
<br/>
|
||||
<span t-esc="mi.copy_name"
|
||||
style="font-size: 20px; padding-left:25px; white-space:nowrap;"/>
|
||||
</div>
|
||||
<div t-if="txt_align == 'center'"
|
||||
class="text-align: center">
|
||||
<br/>
|
||||
<span t-esc="mi.copy_name" style="font-size: 20px;
|
||||
margin-left:280px; margin-right:280px; white-space:nowrap;"/>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-5 offset-1" name="company_address">
|
||||
<ul class="list-unstyled">
|
||||
<strong>
|
||||
<li t-if="company.name">
|
||||
<span t-field="company.name"/>
|
||||
</li>
|
||||
</strong>
|
||||
<li t-if="forced_vat or company.vat">
|
||||
<t t-esc="company.country_id.vat_label or 'Tax ID'"/>
|
||||
:
|
||||
<span t-if="forced_vat" t-esc="forced_vat"/>
|
||||
<span t-else="" t-field="company.vat"/>
|
||||
</li>
|
||||
<li t-if="company.phone">Tel:
|
||||
<span class="o_force_ltr"
|
||||
t-field="company.phone"/>
|
||||
</li>
|
||||
<li t-if="company.email">
|
||||
<span t-field="company.email"/>
|
||||
</li>
|
||||
<li t-if="company.website">
|
||||
<span t-field="company.website"/>
|
||||
</li>
|
||||
<!--Header-->
|
||||
<t t-if="mi_type == 'text'">
|
||||
<t t-if="txt_position == 'header'">
|
||||
<div t-if="txt_align == 'right'">
|
||||
<span t-esc="mi.copy_name"
|
||||
style="font-size: 20px;"/>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--Watermark-->
|
||||
<t t-if="mi_type =='watermark'">
|
||||
<div style="opacity:0.15; font-size:100px; width:85%; text-align:center;top:500px; right:100px; position: fixed; z-index:99; -webkit-transform: rotate(-30deg);">
|
||||
<t t-esc="mi.copy_name"/>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
<div t-attf-class="article o_report_layout_bold o_company_#{company.id}_layout {{ 'o_layout_background' if company.layout_background in ['Geometric', 'Custom'] else '' }}"
|
||||
t-attf-style="background-image: url({{ 'data:image/png;base64,%s' % company.layout_background_image.decode('utf-8') if company.layout_background_image and company.layout_background == 'Custom' else '/base/static/img/bg_background_template.jpg' }});"
|
||||
t-att-data-oe-model="o and o._name" t-att-data-oe-id="o and o.id"
|
||||
t-att-data-oe-lang="o and o.env.context.get('lang')">
|
||||
<t t-call="web.address_layout"/>
|
||||
<t t-out="0"/>
|
||||
</div>
|
||||
<div t-attf-class="footer o_clean_footer o_company_#{company.id}_layout">
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<span t-field="company.report_footer"/>
|
||||
<!--Footer Left-->
|
||||
<t t-if="mi_type == 'text'">
|
||||
<t t-if="txt_position == 'footer'">
|
||||
<div t-if="txt_align == 'left'" class="text-left">
|
||||
<span t-esc="mi.copy_name"
|
||||
style="font-size: 18px; padding-left:25px; white-space:nowrap;"/>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<span t-if="company.company_details"
|
||||
t-field="company.company_details"/>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<h5 class="mt0 mb0" t-field="company.report_header"/>
|
||||
<!--Footer-->
|
||||
<t t-if="mi_type == 'text'">
|
||||
<t t-if="txt_position == 'footer'">
|
||||
<div t-if="txt_align == 'right'" class="text-right">
|
||||
<span t-esc="mi.copy_name"
|
||||
style="font-size: 18px;"/>
|
||||
</div>
|
||||
|
||||
<div t-if="txt_align == 'center'"
|
||||
class="text-center;">
|
||||
<span t-esc="mi.copy_name"
|
||||
style="font-size: 18px;"/>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-1">
|
||||
<ul t-if="report_type == 'pdf'"
|
||||
class="list-inline pagenumber float-right text-center">
|
||||
<li class="list-inline-item">
|
||||
<strong>
|
||||
<span class="page"/>
|
||||
</strong>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="base_accounting_kit.striped">
|
||||
<div t-attf-class="o_company_#{company.id}_layout header"
|
||||
t-att-style="report_header_style">
|
||||
<div class="o_background_header">
|
||||
<div class="float-right">
|
||||
<h3 class="mt0 text-right" t-field="company.report_header"/>
|
||||
</div>
|
||||
<img t-if="company.logo"
|
||||
t-att-src="image_data_uri(company.logo)" class="float-left"
|
||||
alt="Logo"/>
|
||||
<div class="float-left company_address">
|
||||
<span t-if="company.company_details"
|
||||
t-field="company.company_details"></span>
|
||||
</div>
|
||||
<!--Header-->
|
||||
<t t-if="mi_type == 'text'">
|
||||
<t t-if="txt_position == 'header'">
|
||||
<div t-if="txt_align == 'right'" class="text-right"
|
||||
style="position: relative; top: 50px;">
|
||||
<span t-esc="mi.copy_name"
|
||||
style="font-size: 20px;"/>
|
||||
</div>
|
||||
<div t-if="txt_align == 'center'" class="text-center">
|
||||
<br/>
|
||||
<span t-esc="mi.copy_name" style="font-size: 20px;
|
||||
margin-left:280px; margin-right:280px; white-space:nowrap;"/>
|
||||
</div>
|
||||
<div t-if="txt_align == 'left'" class="text-left"
|
||||
style="position: fixed; top: 70px; left:20px;">
|
||||
<br/>
|
||||
<span t-esc="mi.copy_name"
|
||||
style="font-size: 20px; white-space:nowrap;"/>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
<div class="clearfix mb8"/>
|
||||
</div>
|
||||
<!--Watermark-->
|
||||
<t t-if="mi_type =='watermark'">
|
||||
<div style="opacity:0.15; font-size:100px; width:85%; text-align:center;top:500px; right:100px; position: fixed; z-index:99; -webkit-transform: rotate(-30deg);">
|
||||
<t t-esc="mi.copy_name"/>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
<div t-attf-class="o_company_#{company.id}_layout article o_report_layout_striped {{ 'o_layout_background' if company.layout_background in ['Geometric', 'Custom'] else '' }}"
|
||||
t-attf-style="background-image: url({{ 'data:image/png;base64,%s' % company.layout_background_image.decode('utf-8') if company.layout_background_image and company.layout_background == 'Custom' else '/base/static/img/bg_background_template.jpg' }});"
|
||||
t-att-data-oe-model="o and o._name" t-att-data-oe-id="o and o.id"
|
||||
t-att-data-oe-lang="o and o.env.context.get('lang')">
|
||||
<t t-call="web.address_layout"/>
|
||||
<t t-out="0"/>
|
||||
</div>
|
||||
<div t-attf-class="o_company_#{company.id}_layout footer o_background_footer">
|
||||
<div class="text-center">
|
||||
<ul class="list-inline">
|
||||
<div t-field="company.report_footer"/>
|
||||
</ul>
|
||||
<!--Footer-->
|
||||
<t t-if="mi_type == 'text'">
|
||||
<t t-if="txt_position == 'footer'">
|
||||
<div t-if="txt_align == 'right'" class="text-right">
|
||||
<span t-esc="mi.copy_name"
|
||||
style="font-size: 15px;"/>
|
||||
</div>
|
||||
<div t-if="txt_align == 'left'" class="text-left">
|
||||
<span t-esc="mi.copy_name"
|
||||
style="font-size: 15px;"/>
|
||||
</div>
|
||||
<div t-if="txt_align == 'center'" class="text-center;">
|
||||
<span t-esc="mi.copy_name"
|
||||
style="font-size: 15px;"/>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<div t-if="report_type == 'pdf'" class="text-muted">
|
||||
Page:
|
||||
<span class="page"/>
|
||||
of
|
||||
<span class="topage"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<template id="multiple_invoice_wizard_preview">
|
||||
<t t-call="web.html_preview_container">
|
||||
<t t-call="base_accounting_kit.new_external_layout">
|
||||
<t t-if="mi_type == 'text'">
|
||||
<t t-if="txt_position == 'body'">
|
||||
<div t-if="body_txt_position == 'tr'"
|
||||
style="font-size:25px; text-align:right;">
|
||||
<span>Sample Name</span>
|
||||
</div>
|
||||
<div t-if="body_txt_position == 'tl'"
|
||||
style="font-size:25px; text-align:left;">
|
||||
<span>Sample Name</span>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
<div class="pt-5">
|
||||
<div class="address row">
|
||||
<div name="address" class="col-md-5 ml-auto">
|
||||
<address>
|
||||
<address class="mb-0" itemscope="itemscope"
|
||||
itemtype="http://schema.org/Organization">
|
||||
<div>
|
||||
<span itemprop="name">Deco Addict</span>
|
||||
</div>
|
||||
<div itemprop="address"
|
||||
itemscope="itemscope"
|
||||
itemtype="http://schema.org/PostalAddress">
|
||||
<div class="d-flex align-items-baseline">
|
||||
<span class="w-100 o_force_ltr"
|
||||
itemprop="streetAddress">77
|
||||
Santa Barbara
|
||||
Rd<br/>Pleasant Hill CA 94523
|
||||
<br/>United States
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</address>
|
||||
</address>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="page">
|
||||
<h2>
|
||||
<span>Invoice</span>
|
||||
<span>INV/2020/07/0003</span>
|
||||
</h2>
|
||||
<div id="informations" class="row mt32 mb32">
|
||||
<div class="col-auto mw-100 mb-2" name="invoice_date">
|
||||
<strong>Invoice Date:</strong>
|
||||
<p class="m-0">07/08/2020</p>
|
||||
</div>
|
||||
<div class="col-auto mw-100 mb-2" name="due_date">
|
||||
<strong>Due Date:</strong>
|
||||
<p class="m-0">08/07/2020</p>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-sm o_main_table"
|
||||
name="invoice_line_table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th name="th_description" class="text-left">
|
||||
<span>Description</span>
|
||||
</th>
|
||||
<th name="th_quantity" class="text-right">
|
||||
<span>Quantity</span>
|
||||
</th>
|
||||
<th name="th_priceunit"
|
||||
class="text-right d-none d-md-table-cell">
|
||||
<span>Unit Price</span>
|
||||
</th>
|
||||
<th name="th_taxes"
|
||||
class="text-left d-none d-md-table-cell">
|
||||
<span>Taxes</span>
|
||||
</th>
|
||||
<th name="th_subtotal" class="text-right">
|
||||
<span>Amount</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="invoice_tbody">
|
||||
<tr>
|
||||
<td name="account_invoice_line_name">
|
||||
<span>[FURN_8999] Three-Seat Sofa
|
||||
<br/>
|
||||
Three Seater Sofa with Lounger in Steel
|
||||
Grey Colour
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span>5.000</span>
|
||||
</td>
|
||||
<td class="text-right d-none d-md-table-cell">
|
||||
<span class="text-nowrap">1,500.00</span>
|
||||
</td>
|
||||
<td class="text-left d-none d-md-table-cell">
|
||||
<span id="line_tax_ids">15.00%</span>
|
||||
</td>
|
||||
<td class="text-right o_price_total">
|
||||
<span class="text-nowrap">$
|
||||
<span class="oe_currency_value">
|
||||
7,500.00
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td name="account_invoice_line_name">
|
||||
<span>[FURN_8220] Four Person Desk
|
||||
<br/>
|
||||
Four person modern office workstation
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span>5.000</span>
|
||||
</td>
|
||||
<td class="text-right d-none d-md-table-cell">
|
||||
<span class="text-nowrap">23,500.00</span>
|
||||
</td>
|
||||
<td class="text-left d-none d-md-table-cell">
|
||||
<span id="line_tax_ids">15.00%</span>
|
||||
</td>
|
||||
<td class="text-right o_price_total">
|
||||
<span class="text-nowrap">$
|
||||
<span class="oe_currency_value">
|
||||
117,500.00
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="clearfix">
|
||||
<div id="total" class="row">
|
||||
<div class="col-sm-7 col-md-6 ml-auto">
|
||||
<table class="table table-sm"
|
||||
style="page-break-inside: avoid;">
|
||||
<tbody>
|
||||
<tr class="border-black o_subtotal"
|
||||
style="">
|
||||
<td>
|
||||
<strong>Subtotal</strong>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span>$
|
||||
<span class="oe_currency_value">
|
||||
125,000.00
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="">
|
||||
<td>
|
||||
<span class="text-nowrap">Tax
|
||||
15%
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-right o_price_total">
|
||||
<span class="text-nowrap">$
|
||||
18,750.00
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="border-black o_total">
|
||||
<td>
|
||||
<strong>Total</strong>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span class="text-nowrap">$
|
||||
<span class="oe_currency_value">
|
||||
143,750.00
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
Please use the following communication for your payment
|
||||
:
|
||||
<b>
|
||||
<span>
|
||||
INV/2020/07/0003
|
||||
</span>
|
||||
</b>
|
||||
</p>
|
||||
<p name="payment_term">
|
||||
<span>Payment terms: 300 Days</span>
|
||||
</p>
|
||||
<t t-if="mi_type == 'text'">
|
||||
<t t-if="txt_position == 'body'">
|
||||
<div t-if="body_txt_position == 'br'"
|
||||
style="font-size:25px; text-align:right;">
|
||||
<span>Sample Name</span>
|
||||
</div>
|
||||
<div t-if="body_txt_position == 'bl'"
|
||||
style="font-size:25px; text-align:left;">
|
||||
<span>Sample Name</span>
|
||||
</div>
|
||||
|
||||
</t>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="new_external_layout">
|
||||
<t t-if="not o" t-set="o" t-value="doc"/>
|
||||
|
||||
<t t-if="not company">
|
||||
<!-- Multicompany -->
|
||||
<t t-if="company_id">
|
||||
<t t-set="company" t-value="company_id"/>
|
||||
</t>
|
||||
<t t-elif="o and 'company_id' in o">
|
||||
<t t-set="company" t-value="o.company_id.sudo()"/>
|
||||
</t>
|
||||
<t t-else="else">
|
||||
<t t-set="company" t-value="res_company"/>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-if="layout" t-call="{{layout}}">
|
||||
<t t-raw="0"/>
|
||||
</t>
|
||||
<t t-else="else" t-call="base_accounting_kit.standard">
|
||||
<t t-raw="0"/>
|
||||
</t>
|
||||
</template>
|
||||
</odoo>
|
||||
56
addons/base_accounting_kit/report/multiple_invoice_report.py
Normal file
@@ -0,0 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class ReportInvoiceMultiple(models.AbstractModel):
|
||||
_name = 'report.base_accounting_kit.report_multiple_invoice'
|
||||
_inherit = 'report.account.report_invoice'
|
||||
_description = 'Report Invoice Multiple'
|
||||
|
||||
@api.model
|
||||
def _get_report_values(self, docids, data=None):
|
||||
rslt = super()._get_report_values(docids, data)
|
||||
|
||||
inv = rslt['docs']
|
||||
layout = inv.journal_id.company_id.external_report_layout_id.key
|
||||
|
||||
if layout == 'web.external_layout_boxed':
|
||||
new_layout = 'base_accounting_kit.boxed'
|
||||
|
||||
elif layout == 'web.external_layout_bold':
|
||||
new_layout = 'base_accounting_kit.bold'
|
||||
|
||||
elif layout == 'web.external_layout_striped':
|
||||
new_layout = 'base_accounting_kit.striped'
|
||||
|
||||
else:
|
||||
new_layout = 'base_accounting_kit.standard'
|
||||
|
||||
rslt['mi_type'] = inv.journal_id.multiple_invoice_type
|
||||
rslt['mi_ids'] = inv.journal_id.multiple_invoice_ids
|
||||
rslt['txt_position'] = inv.journal_id.text_position
|
||||
rslt['body_txt_position'] = inv.journal_id.body_text_position
|
||||
rslt['txt_align'] = inv.journal_id.text_align
|
||||
rslt['layout'] = new_layout
|
||||
rslt['report_type'] = data.get('report_type') if data else ''
|
||||
return rslt
|
||||
@@ -0,0 +1,307 @@
|
||||
<odoo>
|
||||
<template id="report_multiple_invoice_new">
|
||||
<t t-call="base_accounting_kit.new_external_layout">
|
||||
<t t-set="o" t-value="o.with_context(lang=lang)"/>
|
||||
<t t-set="address">
|
||||
<address t-field="o.partner_id"
|
||||
t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": True}'/>
|
||||
<div t-if="o.partner_id.vat" class="mt16">
|
||||
<t t-if="o.company_id.country_id.vat_label"
|
||||
t-esc="o.company_id.country_id.vat_label"
|
||||
id="inv_tax_id_label"/>
|
||||
<t t-else="">Tax ID</t>:
|
||||
<span t-field="o.partner_id.vat"/>
|
||||
</div>
|
||||
</t>
|
||||
<div class="page">
|
||||
<t t-set="txt_style"
|
||||
t-value="'font-size:25px; text-align:center;top:0px; left:15px; position:absolute; z-index:99;'"/>
|
||||
<t t-if="body_txt_position == 'tr'">
|
||||
<t t-set="txt_style"
|
||||
t-value="'font-size:25px; text-align:center;top:0px; right:15px; position:absolute; z-index:99;'"/>
|
||||
</t>
|
||||
<t t-if="body_txt_position == 'br'">
|
||||
<t t-set="txt_style"
|
||||
t-value="'font-size:25px; text-align:right;'"/>
|
||||
</t>
|
||||
<t t-if="body_txt_position == 'bl'">
|
||||
<t t-set="txt_style"
|
||||
t-value="'font-size:25px; text-align:left;'"/>
|
||||
</t>
|
||||
<h2>
|
||||
<span t-if="o.move_type == 'out_invoice' and o.state == 'posted'">
|
||||
Invoice
|
||||
</span>
|
||||
<span t-if="o.move_type == 'out_invoice' and o.state == 'draft'">
|
||||
Draft Invoice
|
||||
</span>
|
||||
<span t-if="o.move_type == 'out_invoice' and o.state == 'cancel'">
|
||||
Cancelled Invoice
|
||||
</span>
|
||||
<span t-if="o.move_type == 'out_refund'">Credit Note</span>
|
||||
<span t-if="o.move_type == 'in_refund'">Vendor Credit Note
|
||||
</span>
|
||||
<span t-if="o.move_type == 'in_invoice'">Vendor Bill</span>
|
||||
<span t-if="o.name != '/'" t-field="o.name"/>
|
||||
</h2>
|
||||
|
||||
<div id="informations" class="row mt32 mb32">
|
||||
<div class="col-auto col-3 mw-100 mb-2"
|
||||
t-if="o.invoice_date" name="invoice_date">
|
||||
<strong>Invoice Date:</strong>
|
||||
<p class="m-0" t-field="o.invoice_date"/>
|
||||
</div>
|
||||
<div class="col-auto col-3 mw-100 mb-2"
|
||||
t-if="o.invoice_date_due and o.move_type == 'out_invoice' and o.state == 'posted'"
|
||||
name="due_date">
|
||||
<strong>Due Date:</strong>
|
||||
<p class="m-0" t-field="o.invoice_date_due"/>
|
||||
</div>
|
||||
<div class="col-auto col-3 mw-100 mb-2"
|
||||
t-if="o.invoice_origin" name="origin">
|
||||
<strong>Source:</strong>
|
||||
<p class="m-0" t-field="o.invoice_origin"/>
|
||||
</div>
|
||||
<div class="col-auto col-3 mw-100 mb-2"
|
||||
t-if="o.partner_id.ref" name="customer_code">
|
||||
<strong>Customer Code:</strong>
|
||||
<p class="m-0" t-field="o.partner_id.ref"/>
|
||||
</div>
|
||||
<div class="col-auto col-3 mw-100 mb-2" t-if="o.ref"
|
||||
name="reference">
|
||||
<strong>Reference:</strong>
|
||||
<p class="m-0" t-field="o.ref"/>
|
||||
</div>
|
||||
</div>
|
||||
<t t-set="display_discount"
|
||||
t-value="any(l.discount for l in o.invoice_line_ids)"/>
|
||||
<table class="table table-sm o_main_table"
|
||||
name="invoice_line_table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th name="th_description" class="text-left">
|
||||
<span>Description</span>
|
||||
</th>
|
||||
<th name="th_quantity" class="text-right">
|
||||
<span>Quantity</span>
|
||||
</th>
|
||||
<th name="th_priceunit"
|
||||
t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
|
||||
<span>Unit Price</span>
|
||||
</th>
|
||||
<th name="th_price_unit" t-if="display_discount"
|
||||
t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
|
||||
<span>Disc.%</span>
|
||||
</th>
|
||||
<th name="th_taxes"
|
||||
t-attf-class="text-left {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
|
||||
<span>Taxes</span>
|
||||
</th>
|
||||
<th name="th_subtotal" class="text-right">
|
||||
<span>
|
||||
Amount
|
||||
</span>
|
||||
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="invoice_tbody">
|
||||
<t t-set="current_subtotal" t-value="0"/>
|
||||
<t t-set="lines"
|
||||
t-value="o.invoice_line_ids.sorted(key=lambda l: (-l.sequence, l.date, l.move_name, -l.id), reverse=True)"/>
|
||||
<t t-foreach="lines" t-as="line">
|
||||
<t t-set="current_subtotal"
|
||||
t-value="current_subtotal + line.price_subtotal"
|
||||
groups="account.group_show_line_subtotals_tax_excluded"/>
|
||||
<t t-set="current_subtotal"
|
||||
t-value="current_subtotal + line.price_total"
|
||||
groups="account.group_show_line_subtotals_tax_included"/>
|
||||
<tr t-att-class="'bg-200 font-weight-bold o_line_section' if line.display_type == 'line_section' else 'font-italic o_line_note' if line.display_type == 'line_note' else ''">
|
||||
<t t-if="line.display_type not in ('line_section', 'line_note')"
|
||||
name="account_invoice_line_accountable">
|
||||
<td name="account_invoice_line_name">
|
||||
<span t-field="line.name"
|
||||
t-options="{'widget': 'text'}"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-field="line.quantity"/>
|
||||
<span t-field="line.product_uom_id"
|
||||
groups="uom.group_uom"/>
|
||||
</td>
|
||||
<td t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
|
||||
<span class="text-nowrap"
|
||||
t-field="line.price_unit"/>
|
||||
</td>
|
||||
<td t-if="display_discount"
|
||||
t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
|
||||
<span class="text-nowrap"
|
||||
t-field="line.discount"/>
|
||||
</td>
|
||||
<td t-attf-class="text-left {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
|
||||
<span t-esc="', '.join(map(lambda x: (x.description or x.name), line.tax_ids))"
|
||||
id="line_tax_ids"/>
|
||||
</td>
|
||||
<td class="text-right o_price_total">
|
||||
<span class="text-nowrap"
|
||||
t-field="line.price_subtotal"
|
||||
/>
|
||||
</td>
|
||||
</t>
|
||||
<t t-if="line.display_type == 'line_section'">
|
||||
<td colspan="99">
|
||||
<span t-field="line.name"
|
||||
t-options="{'widget': 'text'}"/>
|
||||
</td>
|
||||
<t t-set="current_section" t-value="line"/>
|
||||
<t t-set="current_subtotal" t-value="0"/>
|
||||
</t>
|
||||
<t t-if="line.display_type == 'line_note'">
|
||||
<td colspan="99">
|
||||
<span t-field="line.name"
|
||||
t-options="{'widget': 'text'}"/>
|
||||
</td>
|
||||
</t>
|
||||
</tr>
|
||||
|
||||
<t t-if="current_section and (line_last or lines[line_index+1].display_type == 'line_section')">
|
||||
<tr class="is-subtotal text-right">
|
||||
<td colspan="99">
|
||||
<strong class="mr16">Subtotal</strong>
|
||||
<span
|
||||
t-esc="current_subtotal"
|
||||
t-options='{"widget":
|
||||
"monetary", "display_currency":
|
||||
o.currency_id}'
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
</t>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="clearfix">
|
||||
<div id="right-elements" t-attf-class="#{'col-5' if report_type != 'html' else 'col-12 col-md-5'} ms-5 d-inline-block float-end">
|
||||
<div id="total" class="clearfix row mt-n3">
|
||||
<div class="ms-auto">
|
||||
<table class="o_total_table table table-borderless avoid-page-break-inside">
|
||||
|
||||
<!-- Tax totals summary (invoice currency) -->
|
||||
<t t-if="o.tax_totals" t-call="account.document_tax_totals">
|
||||
<t t-set="tax_totals" t-value="o.tax_totals"/>
|
||||
<t t-set="currency" t-value="o.currency_id"/>
|
||||
</t>
|
||||
|
||||
<!--Payments-->
|
||||
<t t-if="print_with_payments">
|
||||
<t t-if="o.payment_state != 'invoicing_legacy'">
|
||||
<t t-set="payments_vals" t-value="o.sudo().invoice_payments_widget and o.sudo().invoice_payments_widget['content'] or []"/>
|
||||
<t t-foreach="payments_vals" t-as="payment_vals">
|
||||
<tr t-if="payment_vals['is_exchange'] == 0">
|
||||
<td>
|
||||
<i class="oe_form_field text-end oe_payment_label">Paid on <t t-out="payment_vals['date']" t-options='{"widget": "date"}'>2021-09-19</t></i>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-out="payment_vals['amount']" t-options='{"widget": "monetary", "display_currency": o.currency_id}'>20.00</span>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
<t t-if="len(payments_vals) > 0">
|
||||
<tr class="fw-bold">
|
||||
<td>Amount Due</td>
|
||||
<td class="text-end">
|
||||
<span t-field="o.amount_residual">11.05</span>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<p class="text-end lh-sm" t-if="o.company_id.display_invoice_amount_total_words">
|
||||
Total amount in words: <br/>
|
||||
<small class="text-muted lh-sm"><span t-field="o.amount_total_words">Thirty one dollar and Five cents</span></small>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Tax totals summary (company currency) -->
|
||||
<t t-if="o.tax_totals.get('display_in_company_currency')">
|
||||
<t t-set="tax_totals" t-value="o.tax_totals"/>
|
||||
<t t-call="account.document_tax_totals_company_currency_template"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div class="oe_structure"/>
|
||||
</t>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<p t-if="o.move_type in ('out_invoice', 'in_refund') and o.payment_reference"
|
||||
name="payment_communication">
|
||||
Please use the following communication for your payment :
|
||||
<b>
|
||||
<span t-field="o.payment_reference"/>
|
||||
</b>
|
||||
</p>
|
||||
<p t-if="o.invoice_payment_term_id" name="payment_term">
|
||||
<span t-field="o.invoice_payment_term_id.note"/>
|
||||
</p>
|
||||
<p t-if="o.narration" name="comment">
|
||||
<span t-field="o.narration"/>
|
||||
</p>
|
||||
<p t-if="o.fiscal_position_id.note" name="note">
|
||||
<span t-field="o.fiscal_position_id.note"/>
|
||||
</p>
|
||||
<p t-if="o.invoice_incoterm_id" name="incoterm">
|
||||
<strong>Incoterm:</strong>
|
||||
<span t-field="o.invoice_incoterm_id.code"/>
|
||||
-
|
||||
<span t-field="o.invoice_incoterm_id.name"/>
|
||||
</p>
|
||||
<div id="qrcode" t-if="o.display_qr_code">
|
||||
<p t-if="qr_code_urls.get(o.id)">
|
||||
<strong class="text-center">Scan me with your banking
|
||||
app.
|
||||
</strong>
|
||||
<br/>
|
||||
<br/>
|
||||
<img class="border border-dark rounded"
|
||||
t-att-src="qr_code_urls[o.id]"/>
|
||||
</p>
|
||||
</div>
|
||||
<t t-if="mi_type == 'text'">
|
||||
<div t-if="txt_position == 'body'" t-att-style="txt_style">
|
||||
<span t-esc="mi.copy_name"/>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="report_multiple_invoice">
|
||||
<t t-call="web.html_container">
|
||||
<t t-foreach="docs" t-as="o">
|
||||
<t t-set="lang"
|
||||
t-value="o.invoice_user_id.sudo().lang if o.move_type in ('in_invoice', 'in_refund') else o.partner_id.lang"/>
|
||||
<t t-set="print_with_payments" t-value="True"/>
|
||||
<t t-if="o._get_name_invoice_report() == 'account.report_invoice_document'"
|
||||
t-call="account.report_invoice_document" t-lang="lang"/>
|
||||
<t t-foreach="mi_ids" t-as="mi">
|
||||
<t t-call="base_accounting_kit.report_multiple_invoice_new"
|
||||
t-lang="lang"/>
|
||||
</t>
|
||||
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<record id="report_multiple_invoice_copies" model="ir.actions.report">
|
||||
<field name="name">Multiple Invoice Copies</field>
|
||||
<field name="model">account.move</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">base_accounting_kit.report_multiple_invoice</field>
|
||||
<field name="report_file">base_accounting_kit.report_multiple_invoice</field>
|
||||
<field name="binding_model_id" ref="account.model_account_move"/>
|
||||
<field name="binding_type">report</field>
|
||||
</record>
|
||||
</odoo>
|
||||
97
addons/base_accounting_kit/report/report.xml
Normal file
@@ -0,0 +1,97 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<!-- # Financial report -->
|
||||
<record id="financial_report_pdf" model="ir.actions.report">
|
||||
<field name="name">Financial reports</field>
|
||||
<field name="model">financial.report</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">base_accounting_kit.report_financial</field>
|
||||
<field name="report_file">base_accounting_kit.report_financial</field>
|
||||
</record>
|
||||
<!-- # General ledger report -->
|
||||
<record id="action_report_general_ledger" model="ir.actions.report">
|
||||
<field name="name">General Ledger</field>
|
||||
<field name="model">account.report.general.ledger</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">base_accounting_kit.report_general_ledger</field>
|
||||
<field name="report_file">base_accounting_kit.report_general_ledger
|
||||
</field>
|
||||
</record>
|
||||
<!-- # Partner ledger report -->
|
||||
<record id="action_report_partnerledger" model="ir.actions.report">
|
||||
<field name="name">Partner Ledger</field>
|
||||
<field name="model">account.report.partner.ledger</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">base_accounting_kit.report_partnerledger</field>
|
||||
<field name="report_file">base_accounting_kit.report_partnerledger</field>
|
||||
</record>
|
||||
<!-- # Ageing report -->
|
||||
<record id="action_report_aged_partner_balance" model="ir.actions.report">
|
||||
<field name="name">Aged Partner Balance</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">base_accounting_kit.report_agedpartnerbalance</field>
|
||||
<field name="report_file">base_accounting_kit.report_agedpartnerbalance</field>
|
||||
</record>
|
||||
<!-- # Journal audit report -->
|
||||
<record id="action_report_journal" model="ir.actions.report">
|
||||
<field name="name">Journals Audit</field>
|
||||
<field name="model">account.common.journal.report</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">base_accounting_kit.report_journal_audit</field>
|
||||
<field name="report_file">base_accounting_kit.report_journal_audit</field>
|
||||
</record>
|
||||
<!-- # Tax report -->
|
||||
<record id="action_report_account_tax" model="ir.actions.report">
|
||||
<field name="name">Tax Report</field>
|
||||
<field name="model">kit.account.tax.report</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">base_accounting_kit.report_tax</field>
|
||||
<field name="report_file">base_accounting_kit.report_tax</field>
|
||||
</record>
|
||||
<!-- # Trial balance report -->
|
||||
<record id="action_report_trial_balance" model="ir.actions.report">
|
||||
<field name="name">Trial Balance</field>
|
||||
<field name="model">account.balance.report</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">base_accounting_kit.report_trial_balance</field>
|
||||
<field name="report_file">base_accounting_kit.report_trial_balance</field>
|
||||
</record>
|
||||
<!-- # CAsh flow statements -->
|
||||
<record id="action_report_cash_flow" model="ir.actions.report">
|
||||
<field name="name">Cash Flow Statement</field>
|
||||
<field name="model">account.financial.report</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">base_accounting_kit.report_cash_flow</field>
|
||||
<field name="report_file">base_accounting_kit.report_cash_flow</field>
|
||||
</record>
|
||||
<!-- # Accounting Bank Book Report -->
|
||||
<record id="action_report_bank_book" model="ir.actions.report">
|
||||
<field name="name">Bank Book Report</field>
|
||||
<field name="model">account.bank.book.report</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">base_accounting_kit.report_bank_book</field>
|
||||
<field name="report_file">base_accounting_kit.report_bank_book</field>
|
||||
<field name="attachment_use">False</field>
|
||||
</record>
|
||||
|
||||
<!-- # Accounting Cash Book Report -->
|
||||
<record id="action_report_cash_book" model="ir.actions.report">
|
||||
<field name="name">Cash Book Report</field>
|
||||
<field name="model">account.cash.book.report</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">base_accounting_kit.report_cash_book</field>
|
||||
<field name="report_file">base_accounting_kit.report_cash_book</field>
|
||||
<field name="attachment_use">False</field>
|
||||
</record>
|
||||
|
||||
<!-- # Accounting Day Book Report -->
|
||||
<record id="day_book_pdf_report" model="ir.actions.report">
|
||||
<field name="name">Day Book PDF Report</field>
|
||||
<field name="model">account.day.book.report</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">base_accounting_kit.day_book_report_template</field>
|
||||
<field name="report_file">base_accounting_kit.day_book_report_template</field>
|
||||
<field name="attachment_use">True</field>
|
||||
</record>
|
||||
</odoo>
|
||||
297
addons/base_accounting_kit/report/report_aged_partner.py
Normal file
@@ -0,0 +1,297 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
import time
|
||||
from datetime import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from odoo import api, models, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools import float_is_zero
|
||||
|
||||
|
||||
class ReportAgedPartnerBalance(models.AbstractModel):
|
||||
_name = 'report.base_accounting_kit.report_agedpartnerbalance'
|
||||
_description = 'Aged Partner Balance Report'
|
||||
|
||||
def _get_partner_move_lines(self, account_type, date_from, target_move,
|
||||
period_length):
|
||||
# This method can receive the context key 'include_nullified_amount' {Boolean}
|
||||
# Do an invoice and a payment and unreconcile. The amount will be nullified
|
||||
# By default, the partner wouldn't appear in this report.
|
||||
# The context key allow it to appear
|
||||
# In case of a period_length of 30 days as of 2019-02-08, we want the following periods:
|
||||
# Name Stop Start
|
||||
# 1 - 30 : 2019-02-07 - 2019-01-09
|
||||
# 31 - 60 : 2019-01-08 - 2018-12-10
|
||||
# 61 - 90 : 2018-12-09 - 2018-11-10
|
||||
# 91 - 120 : 2018-11-09 - 2018-10-11
|
||||
# +120 : 2018-10-10
|
||||
periods = {}
|
||||
start = datetime.strptime(date_from, "%Y-%m-%d")
|
||||
date_from = datetime.strptime(date_from, "%Y-%m-%d").date()
|
||||
for i in range(5)[::-1]:
|
||||
stop = start - relativedelta(days=period_length)
|
||||
period_name = str((5 - (i + 1)) * period_length + 1) + '-' + str(
|
||||
(5 - i) * period_length)
|
||||
period_stop = (start - relativedelta(days=1)).strftime('%Y-%m-%d')
|
||||
if i == 0:
|
||||
period_name = '+' + str(4 * period_length)
|
||||
periods[str(i)] = {
|
||||
'name': period_name,
|
||||
'stop': period_stop,
|
||||
'start': (i != 0 and stop.strftime('%Y-%m-%d') or False),
|
||||
}
|
||||
start = stop
|
||||
|
||||
res = []
|
||||
total = []
|
||||
cr = self.env.cr
|
||||
user_company = self.env.company
|
||||
user_currency = user_company.currency_id
|
||||
ResCurrency = self.env['res.currency'].with_context(date=date_from)
|
||||
company_ids = self._context.get('company_ids') or [user_company.id]
|
||||
move_state = ['draft', 'posted']
|
||||
if target_move == 'posted':
|
||||
move_state = ['posted']
|
||||
arg_list = (tuple(move_state), tuple(account_type))
|
||||
# build the reconciliation clause to see what partner needs to be printed
|
||||
reconciliation_clause = '(l.reconciled IS FALSE)'
|
||||
cr.execute(
|
||||
'SELECT debit_move_id, credit_move_id FROM account_partial_reconcile where max_date > %s',
|
||||
(date_from,))
|
||||
reconciled_after_date = []
|
||||
|
||||
for row in cr.fetchall():
|
||||
reconciled_after_date += [row[0], row[1]]
|
||||
|
||||
if reconciled_after_date:
|
||||
reconciliation_clause = '(l.reconciled IS FALSE OR l.id IN %s)'
|
||||
arg_list += (tuple(reconciled_after_date),)
|
||||
arg_list += (date_from, tuple(company_ids))
|
||||
query = '''
|
||||
SELECT DISTINCT l.partner_id, UPPER(res_partner.name)
|
||||
FROM account_move_line AS l left join res_partner on l.partner_id = res_partner.id, account_account, account_move am
|
||||
WHERE (l.account_id = account_account.id)
|
||||
AND (l.move_id = am.id)
|
||||
AND (am.state IN %s)
|
||||
AND (account_account.account_type IN %s)
|
||||
AND ''' + reconciliation_clause + '''
|
||||
AND (l.date <= %s)
|
||||
AND l.company_id IN %s
|
||||
ORDER BY UPPER(res_partner.name)'''
|
||||
cr.execute(query, arg_list)
|
||||
|
||||
partners = cr.dictfetchall()
|
||||
# put a total of 0
|
||||
for i in range(7):
|
||||
total.append(0)
|
||||
|
||||
# Build a string like (1,2,3) for easy use in SQL query
|
||||
partner_ids = [partner['partner_id'] for partner in partners if
|
||||
partner['partner_id']]
|
||||
lines = dict(
|
||||
(partner['partner_id'] or False, []) for partner in partners)
|
||||
if not partner_ids:
|
||||
return [], [], {}
|
||||
|
||||
# This dictionary will store the not due amount of all partners
|
||||
undue_amounts = {}
|
||||
query = '''SELECT l.id
|
||||
FROM account_move_line AS l, account_account, account_move am
|
||||
WHERE (l.account_id = account_account.id) AND (l.move_id = am.id)
|
||||
AND (am.state IN %s)
|
||||
AND (account_account.account_type IN %s)
|
||||
AND (COALESCE(l.date_maturity,l.date) >= %s)\
|
||||
AND ((l.partner_id IN %s) OR (l.partner_id IS NULL))
|
||||
AND (l.date <= %s)
|
||||
AND l.company_id IN %s'''
|
||||
cr.execute(query, (
|
||||
tuple(move_state), tuple(account_type), date_from,
|
||||
tuple(partner_ids), date_from, tuple(company_ids)))
|
||||
aml_ids = cr.fetchall()
|
||||
aml_ids = aml_ids and [x[0] for x in aml_ids] or []
|
||||
for line in self.env['account.move.line'].browse(aml_ids):
|
||||
partner_id = line.partner_id.id or False
|
||||
if partner_id not in undue_amounts:
|
||||
undue_amounts[partner_id] = 0.0
|
||||
line_amount = ResCurrency._get_conversion_rate(line.company_id.currency_id,
|
||||
user_currency, line.balance)
|
||||
if user_currency.is_zero(line_amount):
|
||||
continue
|
||||
for partial_line in line.matched_debit_ids:
|
||||
if partial_line.max_date <= date_from:
|
||||
line_amount += ResCurrency._get_conversion_rate(
|
||||
partial_line.company_id.currency_id, user_currency,
|
||||
partial_line.amount)
|
||||
for partial_line in line.matched_credit_ids:
|
||||
if partial_line.max_date <= date_from:
|
||||
line_amount -= ResCurrency._get_conversion_rate(
|
||||
partial_line.company_id.currency_id, user_currency,
|
||||
partial_line.amount)
|
||||
if not self.env.company.currency_id.is_zero(line_amount):
|
||||
undue_amounts[partner_id] += line_amount
|
||||
lines[partner_id].append({
|
||||
'line': line,
|
||||
'amount': line_amount,
|
||||
'period': 6,
|
||||
})
|
||||
# Use one query per period and store results in history (a list variable)
|
||||
# Each history will contain: history[1] = {'<partner_id>': <partner_debit-credit>}
|
||||
history = []
|
||||
for i in range(5):
|
||||
args_list = (
|
||||
tuple(move_state), tuple(account_type), tuple(partner_ids),)
|
||||
dates_query = '(COALESCE(l.date_maturity,l.date)'
|
||||
|
||||
if periods[str(i)]['start'] and periods[str(i)]['stop']:
|
||||
dates_query += ' BETWEEN %s AND %s)'
|
||||
args_list += (
|
||||
periods[str(i)]['start'], periods[str(i)]['stop'])
|
||||
elif periods[str(i)]['start']:
|
||||
dates_query += ' >= %s)'
|
||||
args_list += (periods[str(i)]['start'],)
|
||||
else:
|
||||
dates_query += ' <= %s)'
|
||||
args_list += (periods[str(i)]['stop'],)
|
||||
args_list += (date_from, tuple(company_ids))
|
||||
query = '''SELECT l.id
|
||||
FROM account_move_line AS l, account_account, account_move am
|
||||
WHERE (l.account_id = account_account.id) AND (l.move_id = am.id)
|
||||
AND (am.state IN %s)
|
||||
AND (account_account.account_type IN %s)
|
||||
AND ((l.partner_id IN %s) OR (l.partner_id IS NULL))
|
||||
AND ''' + dates_query + '''
|
||||
AND (l.date <= %s)
|
||||
AND l.company_id IN %s'''
|
||||
cr.execute(query, args_list)
|
||||
partners_amount = {}
|
||||
aml_ids = cr.fetchall()
|
||||
aml_ids = aml_ids and [x[0] for x in aml_ids] or []
|
||||
for line in self.env['account.move.line'].browse(aml_ids):
|
||||
partner_id = line.partner_id.id or False
|
||||
if partner_id not in partners_amount:
|
||||
partners_amount[partner_id] = 0.0
|
||||
line_amount = ResCurrency._get_conversion_rate(line.company_id.currency_id,
|
||||
user_currency, line.balance)
|
||||
if user_currency.is_zero(line_amount):
|
||||
continue
|
||||
for partial_line in line.matched_debit_ids:
|
||||
if partial_line.max_date <= date_from:
|
||||
line_amount += ResCurrency._get_conversion_rate(
|
||||
partial_line.company_id.currency_id, user_currency,
|
||||
partial_line.amount)
|
||||
for partial_line in line.matched_credit_ids:
|
||||
if partial_line.max_date <= date_from:
|
||||
line_amount -= ResCurrency._get_conversion_rate(
|
||||
partial_line.company_id.currency_id, user_currency,
|
||||
partial_line.amount)
|
||||
|
||||
if not self.env.company.currency_id.is_zero(
|
||||
line_amount):
|
||||
partners_amount[partner_id] += line_amount
|
||||
lines[partner_id].append({
|
||||
'line': line,
|
||||
'amount': line_amount,
|
||||
'period': i + 1,
|
||||
})
|
||||
history.append(partners_amount)
|
||||
for partner in partners:
|
||||
if partner['partner_id'] is None:
|
||||
partner['partner_id'] = False
|
||||
at_least_one_amount = False
|
||||
values = {}
|
||||
undue_amt = 0.0
|
||||
if partner[
|
||||
'partner_id'] in undue_amounts: # Making sure this partner actually was found by the query
|
||||
undue_amt = undue_amounts[partner['partner_id']]
|
||||
|
||||
total[6] = total[6] + undue_amt
|
||||
values['direction'] = undue_amt
|
||||
if not float_is_zero(values['direction'],
|
||||
precision_rounding=self.env.company.currency_id.rounding):
|
||||
at_least_one_amount = True
|
||||
|
||||
for i in range(5):
|
||||
during = False
|
||||
if partner['partner_id'] in history[i]:
|
||||
during = [history[i][partner['partner_id']]]
|
||||
# Adding counter
|
||||
total[(i)] = total[(i)] + (during and during[0] or 0)
|
||||
values[str(i)] = during and during[0] or 0.0
|
||||
if not float_is_zero(values[str(i)],
|
||||
precision_rounding=self.env.company.currency_id.rounding):
|
||||
at_least_one_amount = True
|
||||
values['total'] = sum(
|
||||
[values['direction']] + [values[str(i)] for i in range(5)])
|
||||
## Add for total
|
||||
total[(i + 1)] += values['total']
|
||||
values['partner_id'] = partner['partner_id']
|
||||
if partner['partner_id']:
|
||||
browsed_partner = self.env['res.partner'].browse(
|
||||
partner['partner_id'])
|
||||
values['name'] = browsed_partner.name and len(
|
||||
browsed_partner.name) >= 45 and browsed_partner.name[
|
||||
0:40] + '...' or browsed_partner.name
|
||||
values['trust'] = browsed_partner.trust
|
||||
else:
|
||||
values['name'] = _('Unknown Partner')
|
||||
values['trust'] = False
|
||||
|
||||
if at_least_one_amount or (
|
||||
self._context.get('include_nullified_amount') and lines[
|
||||
partner['partner_id']]):
|
||||
res.append(values)
|
||||
return res, total, lines
|
||||
|
||||
@api.model
|
||||
def _get_report_values(self, docids, data=None):
|
||||
if not data.get('form') or not self.env.context.get(
|
||||
'active_model') or not self.env.context.get('active_id'):
|
||||
raise UserError(
|
||||
_("Form content is missing, this report cannot be printed."))
|
||||
|
||||
total = []
|
||||
model = self.env.context.get('active_model')
|
||||
docs = self.env[model].browse(self.env.context.get('active_id'))
|
||||
|
||||
target_move = data['form'].get('target_move', 'all')
|
||||
date_from = data['form'].get('date_from', time.strftime('%Y-%m-%d'))
|
||||
|
||||
if data['form']['result_selection'] == 'customer':
|
||||
account_type = ['asset_receivable']
|
||||
elif data['form']['result_selection'] == 'supplier':
|
||||
account_type = ['liability_payable']
|
||||
else:
|
||||
account_type = ['liability_payable', 'asset_receivable']
|
||||
|
||||
movelines, total, dummy = self._get_partner_move_lines(account_type,
|
||||
date_from,
|
||||
target_move,
|
||||
data['form']['period_length'])
|
||||
return {
|
||||
'doc_ids': self.ids,
|
||||
'doc_model': model,
|
||||
'data': data['form'],
|
||||
'docs': docs,
|
||||
'time': time,
|
||||
'get_partner_lines': movelines,
|
||||
'get_direction': total,
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<template id="report_agedpartnerbalance">
|
||||
<t t-call="web.html_container">
|
||||
<t t-set="data_report_margin_top" t-value="12"/>
|
||||
<t t-set="data_report_header_spacing" t-value="9"/>
|
||||
<t t-set="data_report_dpi" t-value="110"/>
|
||||
<t t-call="web.internal_layout">
|
||||
<div class="page"><br/>
|
||||
<h2>Aged Partner Balance</h2>
|
||||
<div class="row mt32">
|
||||
<div class="col-3">
|
||||
<strong>Start Date:</strong>
|
||||
<p t-esc="data['date_from']"/>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<strong>Period Length (days)</strong>
|
||||
<p t-esc="data['period_length']"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb32">
|
||||
<div class="col-3">
|
||||
<strong>Partner's:</strong>
|
||||
<p>
|
||||
<span t-if="data['result_selection'] == 'customer'">Receivable Accounts</span>
|
||||
<span t-if="data['result_selection'] == 'supplier'">Payable Accounts</span>
|
||||
<span t-if="data['result_selection'] == 'customer_supplier'">Receivable and Payable Accounts</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<strong>Target Moves:</strong>
|
||||
<p>
|
||||
<span t-if="data['target_move'] == 'all'">All Entries</span>
|
||||
<span t-if="data['target_move'] == 'posted'">All Posted Entries</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="table table-sm table-reports">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Partners</th>
|
||||
<th class="text-right">
|
||||
<span>Not due</span>
|
||||
</th>
|
||||
<th class="text-right"><span t-esc="data['4']['name']"/></th>
|
||||
<th class="text-right"><span t-esc="data['3']['name']"/></th>
|
||||
<th class="text-right"><span t-esc="data['2']['name']"/></th>
|
||||
<th class="text-right"><span t-esc="data['1']['name']"/></th>
|
||||
<th class="text-right"><span t-esc="data['0']['name']"/></th>
|
||||
<th class="text-right">Total</th>
|
||||
</tr>
|
||||
<tr t-if="get_partner_lines">
|
||||
<th>Account Total</th>
|
||||
<th class="text-right"><span t-esc="get_direction[6]" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/></th>
|
||||
<th class="text-right"><span t-esc="get_direction[4]" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/></th>
|
||||
<th class="text-right"><span t-esc="get_direction[3]" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/></th>
|
||||
<th class="text-right"><span t-esc="get_direction[2]" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/></th>
|
||||
<th class="text-right"><span t-esc="get_direction[1]" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/></th>
|
||||
<th class="text-right"><span t-esc="get_direction[0]" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/></th>
|
||||
<th class="text-right"><span t-esc="get_direction[5]" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="get_partner_lines" t-as="partner">
|
||||
<td>
|
||||
<span t-esc="partner['name']"/>
|
||||
</td>
|
||||
<td class="text-start">
|
||||
<span t-esc="partner['direction']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-start">
|
||||
<span t-esc="partner['4']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-start">
|
||||
<span t-esc="partner['3']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-start">
|
||||
<span t-esc="partner['2']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-start">
|
||||
<span t-esc="partner['1']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-start">
|
||||
<span t-esc="partner['0']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-start">
|
||||
<span t-esc="partner['total']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
</odoo>
|
||||
157
addons/base_accounting_kit/report/report_financial.py
Normal file
@@ -0,0 +1,157 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Account Financial Report
|
||||
# ---------------------------------------------------------
|
||||
class AccountTypes(models.Model):
|
||||
_name = "account.account.type"
|
||||
|
||||
name = fields.Char(string='Account Type', required=True, translate=True)
|
||||
type = fields.Selection([
|
||||
('other', 'Regular'),
|
||||
('receivable', 'Receivable'),
|
||||
('payable', 'Payable'),
|
||||
('liquidity', 'Liquidity'),
|
||||
], required=True, default='other',
|
||||
help="The 'Internal Type' is used for features available on "
|
||||
"different types of accounts: liquidity type is for cash or "
|
||||
"bank accounts" \
|
||||
", payable/receivable is for vendor/customer accounts.")
|
||||
|
||||
|
||||
class AccountFinancialReport(models.Model):
|
||||
_name = "account.financial.report"
|
||||
_description = "Account Report"
|
||||
_rec_name = 'name'
|
||||
|
||||
@api.depends('parent_id', 'parent_id.level')
|
||||
def _get_level(self):
|
||||
"""Returns a dictionary with key=the ID of a record and
|
||||
value = the level of this
|
||||
record in the tree structure."""
|
||||
for report in self:
|
||||
level = 0
|
||||
if report.parent_id:
|
||||
level = report.parent_id.level + 1
|
||||
report.level = level
|
||||
|
||||
def _get_children_by_order(self):
|
||||
"""returns a recordset of all the children computed recursively,
|
||||
and sorted by sequence. Ready for the printing"""
|
||||
res = self
|
||||
children = self.search([('parent_id', 'in', self.ids)],
|
||||
order='sequence ASC')
|
||||
if children:
|
||||
for child in children:
|
||||
res += child._get_children_by_order()
|
||||
return res
|
||||
|
||||
name = fields.Char('Report Name', required=True, translate=True)
|
||||
parent_id = fields.Many2one('account.financial.report', 'Parent')
|
||||
children_ids = fields.One2many(
|
||||
'account.financial.report',
|
||||
'parent_id',
|
||||
'Account Report')
|
||||
sequence = fields.Integer('Sequence')
|
||||
level = fields.Integer(compute='_get_level', string='Level', store=True, recursive=True)
|
||||
type = fields.Selection(
|
||||
[('sum', 'View'),
|
||||
('accounts', 'Accounts'),
|
||||
('account_type', 'Account Type'),
|
||||
('account_report', 'Report Value')],
|
||||
'Type',
|
||||
default='sum')
|
||||
account_ids = fields.Many2many(
|
||||
'account.account',
|
||||
'account_account_financial_report',
|
||||
'report_line_id',
|
||||
'account_id',
|
||||
'Accounts')
|
||||
account_report_id = fields.Many2one(
|
||||
'account.financial.report',
|
||||
'Report Value')
|
||||
# account_type_ids = fields.Many2many(
|
||||
# 'account.account.type',
|
||||
# 'Account Types')
|
||||
account_type_ids = fields.Selection(
|
||||
selection=[
|
||||
("asset_receivable", "Receivable"),
|
||||
("asset_cash", "Bank and Cash"),
|
||||
("asset_current", "Current Assets"),
|
||||
("asset_non_current", "Non-current Assets"),
|
||||
("asset_prepayments", "Prepayments"),
|
||||
("asset_fixed", "Fixed Assets"),
|
||||
("liability_payable", "Payable"),
|
||||
("liability_credit_card", "Credit Card"),
|
||||
("liability_current", "Current Liabilities"),
|
||||
("liability_non_current", "Non-current Liabilities"),
|
||||
("equity", "Equity"),
|
||||
("equity_unaffected", "Current Year Earnings"),
|
||||
("income", "Income"),
|
||||
("income_other", "Other Income"),
|
||||
("expense", "Expenses"),
|
||||
("expense_depreciation", "Depreciation"),
|
||||
("expense_direct_cost", "Cost of Revenue"),
|
||||
("off_balance", "Off-Balance Sheet"),
|
||||
],
|
||||
string="Type",
|
||||
help="These types are defined according to your country. The type contains more information " \
|
||||
"about the account and its specificities."
|
||||
)
|
||||
|
||||
sign = fields.Selection(
|
||||
[("-1", 'Reverse balance sign'), ("1", 'Preserve balance sign')],
|
||||
'Sign on Reports', required=True, default="1",
|
||||
help='For accounts that are typically more'
|
||||
' debited than credited and that you'
|
||||
' would like to print as negative'
|
||||
' amounts in your reports, you should'
|
||||
' reverse the sign of the balance;'
|
||||
' e.g.: Expense account. The same applies'
|
||||
' for accounts that are typically more'
|
||||
' credited than debited and that you would'
|
||||
' like to print as positive amounts in'
|
||||
' your reports; e.g.: Income account.')
|
||||
display_detail = fields.Selection(
|
||||
[('no_detail', 'No detail'),
|
||||
('detail_flat', 'Display children flat'),
|
||||
('detail_with_hierarchy', 'Display children with hierarchy')],
|
||||
'Display details',
|
||||
default='detail_flat')
|
||||
style_overwrite = fields.Selection(
|
||||
[('0', 'Automatic formatting'),
|
||||
('1', 'Main Title 1 (bold, underlined)'),
|
||||
('2', 'Title 2 (bold)'),
|
||||
('3', 'Title 3 (bold, smaller)'),
|
||||
('4', 'Normal Text'),
|
||||
('5', 'Italic Text (smaller)'),
|
||||
('6', 'Smallest Text')],
|
||||
'Financial Report Style',
|
||||
default='0',
|
||||
help="You can set up here the format you want this"
|
||||
" record to be displayed. If you leave the"
|
||||
" automatic formatting, it will be computed"
|
||||
" based on the financial reports hierarchy "
|
||||
"(auto-computed field 'level').")
|
||||
145
addons/base_accounting_kit/report/report_financial_template.xml
Normal file
@@ -0,0 +1,145 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<template id="report_financial">
|
||||
<t t-call="web.html_container">
|
||||
<t t-call="web.internal_layout">
|
||||
<t t-set="data_report_margin_top" t-value="12"/>
|
||||
<t t-set="data_report_header_spacing" t-value="9"/>
|
||||
<t t-set="data_report_dpi" t-value="110"/>
|
||||
<div class="page">
|
||||
<h2 t-esc="data['form']['account_report_id'][1]"/>
|
||||
<div class="row mt32 mb32">
|
||||
<div class="col-4">
|
||||
<strong>Target Moves:</strong>
|
||||
<p>
|
||||
<span t-if="data['form']['target_move'] == 'all'">All Entries</span>
|
||||
<span t-if="data['form']['target_move'] == 'posted'">All Posted Entries</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<p>
|
||||
<t t-if="data['form']['date_from']">
|
||||
<strong>Date from :</strong>
|
||||
<span t-esc="data['form']['date_from']"/>
|
||||
<br/>
|
||||
</t>
|
||||
<t t-if="data['form']['date_to']">
|
||||
<strong>Date to :</strong>
|
||||
<span t-esc="data['form']['date_to']"/>
|
||||
</t>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-sm table-reports" t-if="data['form']['debit_credit'] == 1">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th class="text-right">Debit</th>
|
||||
<th class="text-right">Credit</th>
|
||||
<th class="text-right">Balance</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="report_lines" t-as="a">
|
||||
<t t-if="a['level'] != 0">
|
||||
<t t-if="a.get('level') > 3">
|
||||
<t t-set="style" t-value="'font-weight: normal;'"/>
|
||||
</t>
|
||||
<t t-if="not a.get('level') > 3">
|
||||
<t t-set="style" t-value="'font-weight: bold;'"/>
|
||||
</t>
|
||||
|
||||
<td>
|
||||
<span style="color: white;" t-esc="'..' * a.get('level', 0)"/>
|
||||
<span t-att-style="style" t-esc="a.get('name')"/>
|
||||
</td>
|
||||
<td class="text-right" style="white-space: text-nowrap;">
|
||||
<span t-att-style="style" t-esc="a.get('debit')"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-right" style="white-space: text-nowrap;">
|
||||
<span t-att-style="style" t-esc="a.get('credit')"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-right" style="white-space: text-nowrap;">
|
||||
<span t-att-style="style" t-esc="a.get('balance')"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
</t>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="table table-sm table-reports"
|
||||
t-if="not data['form']['enable_filter'] and not data['form']['debit_credit']">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th class="text-right">Balance</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="report_lines" t-as="a">
|
||||
<t t-if="a['level'] != 0">
|
||||
<t t-if="a.get('level') > 3">
|
||||
<t t-set="style" t-value="'font-weight: normal;'"/>
|
||||
</t>
|
||||
<t t-if="not a.get('level') > 3">
|
||||
<t t-set="style" t-value="'font-weight: bold;'"/>
|
||||
</t>
|
||||
|
||||
<td>
|
||||
<span style="color: white;" t-esc="'..' * a.get('level', 0)"/>
|
||||
<span t-att-style="style" t-esc="a.get('name')"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-att-style="style" t-esc="a.get('balance')"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
</t>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="table table-sm table-reports"
|
||||
t-if="data['form']['enable_filter'] == 1 and not data['form']['debit_credit']">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th class="text-right">Balance</th>
|
||||
<th class="text-right">
|
||||
<span>Comp</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="report_lines" t-as="a">
|
||||
<t t-if="a['level'] != 0">
|
||||
<t t-if="a.get('level') > 3">
|
||||
<t t-set="style" t-value="'font-weight: normal;'"/>
|
||||
</t>
|
||||
<t t-if="not a.get('level') > 3">
|
||||
<t t-set="style" t-value="'font-weight: bold;'"/>
|
||||
</t>
|
||||
<td>
|
||||
<span style="color: white;" t-esc="'..'"/>
|
||||
<span t-att-style="style" t-esc="a.get('name')"/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-att-style="style"
|
||||
t-esc="a.get('balance')"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-att-style="style"
|
||||
t-esc="a.get('balance_cmp')"/>
|
||||
</td>
|
||||
</t>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
</odoo>
|
||||
160
addons/base_accounting_kit/report/report_journal_audit.py
Normal file
@@ -0,0 +1,160 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
import time
|
||||
from odoo import api, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class ReportJournal(models.AbstractModel):
|
||||
_name = 'report.base_accounting_kit.report_journal_audit'
|
||||
_description = 'Journal Report'
|
||||
|
||||
def lines(self, target_move, journal_ids, sort_selection, data):
|
||||
if isinstance(journal_ids, int):
|
||||
journal_ids = [journal_ids]
|
||||
move_state = ['draft', 'posted']
|
||||
if target_move == 'posted':
|
||||
move_state = ['posted']
|
||||
|
||||
query_get_clause = self._get_query_get_clause(data)
|
||||
params = [tuple(move_state), tuple(journal_ids)] + query_get_clause[2]
|
||||
query = 'SELECT "account_move_line".id FROM ' + query_get_clause[
|
||||
0] + (', account_move am, account_account acc WHERE '
|
||||
'"account_move_line".account_id = acc.id AND '
|
||||
'"account_move_line".move_id=am.id AND am.state IN %s AND '
|
||||
'"account_move_line".journal_id IN %s AND ') + \
|
||||
query_get_clause[1] + ' ORDER BY '
|
||||
if sort_selection == 'date':
|
||||
query += '"account_move_line".date'
|
||||
else:
|
||||
query += 'am.name'
|
||||
query += ', "account_move_line".move_id'
|
||||
self.env.cr.execute(query, tuple(params))
|
||||
ids = (x[0] for x in self.env.cr.fetchall())
|
||||
return self.env['account.move.line'].browse(ids)
|
||||
|
||||
def _sum_debit(self, data, journal_id):
|
||||
move_state = ['draft', 'posted']
|
||||
if data['form'].get('target_move', 'all') == 'posted':
|
||||
move_state = ['posted']
|
||||
|
||||
query_get_clause = self._get_query_get_clause(data)
|
||||
params = [tuple(move_state), tuple(journal_id.ids)] + query_get_clause[
|
||||
2]
|
||||
self.env.cr.execute('SELECT SUM(debit) FROM ' + query_get_clause[
|
||||
0] + ', account_move am '
|
||||
'WHERE "account_move_line".move_id=am.id AND am.state IN %s'
|
||||
' AND "account_move_line".journal_id IN %s AND ' +
|
||||
query_get_clause[1] + ' ',
|
||||
tuple(params))
|
||||
return self.env.cr.fetchone()[0] or 0.0
|
||||
|
||||
def _sum_credit(self, data, journal_id):
|
||||
move_state = ['draft', 'posted']
|
||||
if data['form'].get('target_move', 'all') == 'posted':
|
||||
move_state = ['posted']
|
||||
|
||||
query_get_clause = self._get_query_get_clause(data)
|
||||
params = [tuple(move_state), tuple(journal_id.ids)] + query_get_clause[
|
||||
2]
|
||||
self.env.cr.execute('SELECT SUM(credit) FROM ' + query_get_clause[
|
||||
0] + ', account_move am '
|
||||
'WHERE "account_move_line".move_id=am.id AND am.state IN %s AND "account_move_line".journal_id IN %s AND ' +
|
||||
query_get_clause[1] + ' ',
|
||||
tuple(params))
|
||||
return self.env.cr.fetchone()[0] or 0.0
|
||||
|
||||
def _get_taxes(self, data, journal_id):
|
||||
move_state = ['draft', 'posted']
|
||||
if data['form'].get('target_move', 'all') == 'posted':
|
||||
move_state = ['posted']
|
||||
|
||||
query_get_clause = self._get_query_get_clause(data)
|
||||
params = [tuple(move_state), tuple(journal_id.ids)] + query_get_clause[
|
||||
2]
|
||||
query = """
|
||||
SELECT rel.account_tax_id, SUM("account_move_line".balance) AS base_amount
|
||||
FROM account_move_line_account_tax_rel rel, """ + query_get_clause[
|
||||
0] + """
|
||||
LEFT JOIN account_move am ON "account_move_line".move_id = am.id
|
||||
WHERE "account_move_line".id = rel.account_move_line_id
|
||||
AND am.state IN %s
|
||||
AND "account_move_line".journal_id IN %s
|
||||
AND """ + query_get_clause[1] + """
|
||||
GROUP BY rel.account_tax_id"""
|
||||
self.env.cr.execute(query, tuple(params))
|
||||
ids = []
|
||||
base_amounts = {}
|
||||
for row in self.env.cr.fetchall():
|
||||
ids.append(row[0])
|
||||
base_amounts[row[0]] = row[1]
|
||||
|
||||
res = {}
|
||||
for tax in self.env['account.tax'].browse(ids):
|
||||
self.env.cr.execute(
|
||||
'SELECT sum(debit - credit) FROM ' + query_get_clause[
|
||||
0] + ', account_move am '
|
||||
'WHERE "account_move_line".move_id=am.id AND am.state IN %s AND "account_move_line".journal_id IN %s AND ' +
|
||||
query_get_clause[1] + ' AND tax_line_id = %s',
|
||||
tuple(params + [tax.id]))
|
||||
res[tax] = {
|
||||
'base_amount': base_amounts[tax.id],
|
||||
'tax_amount': self.env.cr.fetchone()[0] or 0.0,
|
||||
}
|
||||
if journal_id.type == 'sale':
|
||||
# sales operation are credits
|
||||
res[tax]['base_amount'] = res[tax]['base_amount'] * -1
|
||||
res[tax]['tax_amount'] = res[tax]['tax_amount'] * -1
|
||||
return res
|
||||
|
||||
def _get_query_get_clause(self, data):
|
||||
return self.env['account.move.line'].with_context(
|
||||
data['form'].get('used_context', {}))._query_get()
|
||||
|
||||
@api.model
|
||||
def _get_report_values(self, docids, data=None):
|
||||
if not data.get('form'):
|
||||
raise UserError(
|
||||
_("Form content is missing, this report cannot be printed."))
|
||||
|
||||
target_move = data['form'].get('target_move', 'all')
|
||||
sort_selection = data['form'].get('sort_selection', 'date')
|
||||
|
||||
res = {}
|
||||
for journal in data['form']['journal_ids']:
|
||||
res[journal] = self.with_context(
|
||||
data['form'].get('used_context', {})).lines(target_move,
|
||||
journal,
|
||||
sort_selection,
|
||||
data)
|
||||
return {
|
||||
'doc_ids': data['form']['journal_ids'],
|
||||
'doc_model': self.env['account.journal'],
|
||||
'data': data,
|
||||
'docs': self.env['account.journal'].browse(
|
||||
data['form']['journal_ids']),
|
||||
'time': time,
|
||||
'lines': res,
|
||||
'sum_credit': self._sum_credit,
|
||||
'sum_debit': self._sum_debit,
|
||||
'get_taxes': self._get_taxes,
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<template id="report_journal_audit">
|
||||
<t t-call="web.html_container">
|
||||
<t t-call="web.internal_layout">
|
||||
<t t-foreach="docs" t-as="o">
|
||||
<t t-set="data_report_margin_top" t-value="12"/>
|
||||
<t t-set="data_report_header_spacing" t-value="9"/>
|
||||
<t t-set="data_report_dpi" t-value="110"/>
|
||||
<div class="page">
|
||||
<span t-esc="context_timestamp(datetime.datetime.now()).strftime('%Y-%m-%d %H:%M')"/>
|
||||
<h2>
|
||||
<t t-esc="o.name"/>
|
||||
Journal
|
||||
</h2>
|
||||
|
||||
<div class="row mt32">
|
||||
|
||||
<div class="col-3">
|
||||
<strong>Company:</strong>
|
||||
<p t-esc="env.company.name"/>
|
||||
</div>
|
||||
|
||||
<div class="col-3">
|
||||
<strong>Journal:</strong>
|
||||
<p t-esc="o.name"/>
|
||||
</div>
|
||||
|
||||
<div class="col-3">
|
||||
<strong>Entries Sorted By:</strong>
|
||||
<p t-if="data['form'].get('sort_selection') != 'l.date'">Journal Entry Number</p>
|
||||
<p t-if="data['form'].get('sort_selection') == 'l.date'">Date</p>
|
||||
</div>
|
||||
|
||||
<div class="col-3">
|
||||
<strong>Target Moves:</strong>
|
||||
<p t-if="data['form']['target_move'] == 'all'">All Entries</p>
|
||||
<p t-if="data['form']['target_move'] == 'posted'">All Posted Entries</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Move</th>
|
||||
<th>Date</th>
|
||||
<th>Account</th>
|
||||
<th>Partner</th>
|
||||
<th>Label</th>
|
||||
<th>Debit</th>
|
||||
<th>Credit</th>
|
||||
<th t-if="data['form']['amount_currency']">Currency</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr t-foreach="lines[o.id]" t-as="aml">
|
||||
<td>
|
||||
<span t-esc="aml.move_id.name != '/' and aml.move_id.name or ('*'+str(aml.move_id.id))"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-field="aml.date"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-field="aml.account_id.code"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="aml.sudo().partner_id and aml.sudo().partner_id.name and aml.sudo().partner_id.name[:23] or ''"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="aml.name and aml.name[:35]"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="aml.debit"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="aml.credit"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td t-if="data['form']['amount_currency'] and aml.amount_currency">
|
||||
<span t-esc="aml.amount_currency"
|
||||
t-options="{'widget': 'monetary', 'display_currency': aml.currency_id}"/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-4 pull-right">
|
||||
<table class="table table-sm">
|
||||
<tr>
|
||||
<td>
|
||||
<strong>Total</strong>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="sum_debit(data, o)"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="sum_credit(data, o)"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<table class="table table-sm table-reports">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="3">Tax Declaration</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Base Amount</th>
|
||||
<th>Tax Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<t t-set="taxes" t-value="get_taxes(data, o)"/>
|
||||
<tr t-foreach="taxes" t-as="tax">
|
||||
<td>
|
||||
<span t-esc="tax.name"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="taxes[tax]['base_amount']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="taxes[tax]['tax_amount']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<p style="page-break-after: always;"/>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
</odoo>
|
||||
170
addons/base_accounting_kit/report/report_partner_ledger.py
Normal file
@@ -0,0 +1,170 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
import time
|
||||
from odoo import api, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class ReportPartnerLedger(models.AbstractModel):
|
||||
_name = 'report.base_accounting_kit.report_partnerledger'
|
||||
_description = 'Partner Ledger Report'
|
||||
|
||||
def _lines(self, data, partner):
|
||||
full_account = []
|
||||
currency = self.env['res.currency']
|
||||
query_get_data = self.env['account.move.line'].with_context(
|
||||
data['form'].get('used_context', {}))._query_get()
|
||||
reconcile_clause = "" if data['form'][
|
||||
'reconciled'] else ' AND "account_move_line".full_reconcile_id IS NULL '
|
||||
params = [partner.id, tuple(data['computed']['move_state']),
|
||||
tuple(data['computed']['account_ids'])] + \
|
||||
query_get_data[2]
|
||||
query = """
|
||||
SELECT "account_move_line".id, "account_move_line".date, j.code,
|
||||
acc.name as a_name, "account_move_line".ref,
|
||||
m.name as move_name, "account_move_line".name,
|
||||
"account_move_line".debit, "account_move_line".credit,
|
||||
"account_move_line".amount_currency,
|
||||
"account_move_line".currency_id, c.symbol AS currency_code
|
||||
FROM """ + query_get_data[0] + """
|
||||
LEFT JOIN account_journal j ON ("account_move_line".journal_id = j.id)
|
||||
LEFT JOIN account_account acc ON ("account_move_line".account_id = acc.id)
|
||||
LEFT JOIN res_currency c ON ("account_move_line".currency_id=c.id)
|
||||
LEFT JOIN account_move m ON (m.id="account_move_line".move_id)
|
||||
WHERE "account_move_line".partner_id = %s
|
||||
AND m.state IN %s
|
||||
AND "account_move_line".account_id IN %s AND """ + \
|
||||
query_get_data[1] + reconcile_clause + """
|
||||
ORDER BY "account_move_line".date"""
|
||||
self.env.cr.execute(query, tuple(params))
|
||||
res = self.env.cr.dictfetchall()
|
||||
sum = 0.0
|
||||
lang_code = self.env.context.get('lang') or 'en_US'
|
||||
lang = self.env['res.lang']
|
||||
lang_id = lang._lang_get(lang_code)
|
||||
date_format = lang_id.date_format
|
||||
for r in res:
|
||||
r['date'] = r['date']
|
||||
r['displayed_name'] = '-'.join(
|
||||
r[field_name] for field_name in ('move_name', 'ref', 'name')
|
||||
if r[field_name] not in (None, '', '/')
|
||||
)
|
||||
sum += r['debit'] - r['credit']
|
||||
r['progress'] = sum
|
||||
r['currency_id'] = currency.browse(r.get('currency_id'))
|
||||
full_account.append(r)
|
||||
return full_account
|
||||
|
||||
def _sum_partner(self, data, partner, field):
|
||||
if field not in ['debit', 'credit', 'debit - credit']:
|
||||
return
|
||||
result = 0.0
|
||||
query_get_data = self.env['account.move.line'].with_context(
|
||||
data['form'].get('used_context', {}))._query_get()
|
||||
reconcile_clause = "" if data['form'][
|
||||
'reconciled'] else ' AND "account_move_line".full_reconcile_id IS NULL '
|
||||
|
||||
params = [partner.id, tuple(data['computed']['move_state']),
|
||||
tuple(data['computed']['account_ids'])] + \
|
||||
query_get_data[2]
|
||||
query = """SELECT sum(""" + field + """)
|
||||
FROM """ + query_get_data[0] + """, account_move AS m
|
||||
WHERE "account_move_line".partner_id = %s
|
||||
AND m.id = "account_move_line".move_id
|
||||
AND m.state IN %s
|
||||
AND account_id IN %s
|
||||
AND """ + query_get_data[1] + reconcile_clause
|
||||
self.env.cr.execute(query, tuple(params))
|
||||
|
||||
contemp = self.env.cr.fetchone()
|
||||
if contemp is not None:
|
||||
result = contemp[0] or 0.0
|
||||
return result
|
||||
|
||||
@api.model
|
||||
def _get_report_values(self, docids, data=None):
|
||||
if not data.get('form'):
|
||||
raise UserError(_("Form content is missing, this report cannot be printed."))
|
||||
|
||||
data['computed'] = {}
|
||||
|
||||
obj_partner = self.env['res.partner']
|
||||
query_get_data = self.env['account.move.line'].with_context(
|
||||
data['form'].get('used_context', {}))._query_get()
|
||||
|
||||
# move state
|
||||
data['computed']['move_state'] = ['draft', 'posted']
|
||||
if data['form'].get('target_move', 'all') == 'posted':
|
||||
data['computed']['move_state'] = ['posted']
|
||||
|
||||
# account types
|
||||
result_selection = data['form'].get('result_selection', 'customer')
|
||||
if result_selection == 'supplier':
|
||||
data['computed']['ACCOUNT_TYPE'] = ['liability_payable']
|
||||
elif result_selection == 'customer':
|
||||
data['computed']['ACCOUNT_TYPE'] = ['asset_receivable']
|
||||
else:
|
||||
data['computed']['ACCOUNT_TYPE'] = ['liability_payable', 'asset_receivable']
|
||||
|
||||
# fetch account ids
|
||||
self.env.cr.execute("""
|
||||
SELECT a.id
|
||||
FROM account_account a
|
||||
WHERE a.account_type IN %s
|
||||
AND a.active""", # ✅ changed here
|
||||
(tuple(data['computed']['ACCOUNT_TYPE']),)
|
||||
)
|
||||
data['computed']['account_ids'] = [a for (a,) in self.env.cr.fetchall()]
|
||||
|
||||
# prevent empty tuple issue
|
||||
account_ids = tuple(data['computed']['account_ids']) or (0,)
|
||||
params = [tuple(data['computed']['move_state']), account_ids] + query_get_data[2]
|
||||
|
||||
reconcile_clause = "" if data['form']['reconciled'] else \
|
||||
' AND "account_move_line".full_reconcile_id IS NULL '
|
||||
|
||||
query = """
|
||||
SELECT DISTINCT "account_move_line".partner_id
|
||||
FROM """ + query_get_data[0] + """, account_account AS account, account_move AS am
|
||||
WHERE "account_move_line".partner_id IS NOT NULL
|
||||
AND "account_move_line".account_id = account.id
|
||||
AND am.id = "account_move_line".move_id
|
||||
AND am.state IN %s
|
||||
AND "account_move_line".account_id IN %s
|
||||
AND account.active
|
||||
AND """ + query_get_data[1] + reconcile_clause # ✅ changed here
|
||||
|
||||
self.env.cr.execute(query, tuple(params))
|
||||
partner_ids = [res['partner_id'] for res in self.env.cr.dictfetchall()]
|
||||
|
||||
partners = obj_partner.browse(partner_ids)
|
||||
partners = sorted(partners, key=lambda x: (x.ref or '', x.name or ''))
|
||||
|
||||
return {
|
||||
'doc_ids': partner_ids,
|
||||
'doc_model': self.env['res.partner'],
|
||||
'data': data,
|
||||
'docs': partners,
|
||||
'time': time,
|
||||
'lines': self._lines,
|
||||
'sum_partner': self._sum_partner,
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<template id="report_partnerledger">
|
||||
<t t-call="web.html_container">
|
||||
<t t-call="web.internal_layout">
|
||||
<t t-set="data_report_margin_top" t-value="12"/>
|
||||
<t t-set="data_report_header_spacing" t-value="9"/>
|
||||
<t t-set="data_report_dpi" t-value="110"/>
|
||||
<div class="page">
|
||||
<h2>Partner Ledger</h2>
|
||||
<div class="row">
|
||||
<div class="col-3">
|
||||
<strong>Company:</strong>
|
||||
<p t-esc="env.company.name"/>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<t t-if="data['form']['date_from']">
|
||||
<strong>Date from :</strong>
|
||||
<span t-esc="data['form']['date_from']"/>
|
||||
<br/>
|
||||
</t>
|
||||
<t t-if="data['form']['date_to']">
|
||||
<strong>Date to :</strong>
|
||||
<span t-esc="data['form']['date_to']"/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<strong>Target Moves:</strong>
|
||||
<p t-if="data['form']['target_move'] == 'all'">All Entries</p>
|
||||
<p t-if="data['form']['target_move'] == 'posted'">All Posted Entries</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="table table-sm table-reports">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>JRNL</th>
|
||||
<th>Ref</th>
|
||||
<th>Debit</th>
|
||||
<th>Credit</th>
|
||||
<th>Balance</th>
|
||||
<th t-if="data['form']['amount_currency']">Currency</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<t t-foreach="docs" t-as="o">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
<strong t-esc="o.ref"/>
|
||||
-
|
||||
<strong t-esc="o.name"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<strong t-esc="sum_partner(data, o, 'debit')"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<strong t-esc="sum_partner(data, o, 'credit')"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<strong t-esc="sum_partner(data, o, 'debit - credit')"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr t-foreach="lines(data, o)" t-as="line">
|
||||
<td>
|
||||
<span t-esc="line['date']"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="line['code']"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="line['displayed_name']"/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-esc="line['debit']"
|
||||
t-options="{'widget': 'monetary',
|
||||
'display_currency':
|
||||
env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-esc="line['credit']"
|
||||
t-options="{'widget': 'monetary',
|
||||
'display_currency':
|
||||
env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-esc="line['progress']"
|
||||
t-options="{'widget': 'monetary',
|
||||
'display_currency':
|
||||
env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-end"
|
||||
t-if="data['form']['amount_currency']">
|
||||
<t t-if="line['currency_id']">
|
||||
<span t-esc="line['amount_currency']"
|
||||
t-options="{'widget': 'monetary',
|
||||
'display_currency': line['currency_id']}"/>
|
||||
</t>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</t>
|
||||
</table>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
</odoo>
|
||||
114
addons/base_accounting_kit/report/report_tax.py
Normal file
@@ -0,0 +1,114 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from _datetime import datetime
|
||||
from odoo import api, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class ReportTax(models.AbstractModel):
|
||||
_name = 'report.base_accounting_kit.report_tax'
|
||||
_description = 'Tax Report'
|
||||
|
||||
@api.model
|
||||
def _get_report_values(self, docids, data=None):
|
||||
if not data.get('form'):
|
||||
raise UserError(
|
||||
_("Form content is missing, this report cannot be printed."))
|
||||
return {
|
||||
'data': data['form'],
|
||||
'lines': self.get_lines(data.get('form')),
|
||||
}
|
||||
|
||||
def _sql_from_amls_one(self):
|
||||
sql = """SELECT "account_move_line".tax_line_id, COALESCE(SUM("account_move_line".debit-"account_move_line".credit), 0)
|
||||
FROM %s
|
||||
WHERE %s GROUP BY "account_move_line".tax_line_id"""
|
||||
return sql
|
||||
|
||||
def _sql_from_amls_two(self):
|
||||
sql = """SELECT r.account_tax_id, COALESCE(SUM("account_move_line".debit-"account_move_line".credit), 0)
|
||||
FROM %s
|
||||
INNER JOIN account_move_line_account_tax_rel r ON ("account_move_line".id = r.account_move_line_id)
|
||||
INNER JOIN account_tax t ON (r.account_tax_id = t.id)
|
||||
WHERE %s GROUP BY r.account_tax_id"""
|
||||
return sql
|
||||
|
||||
def _compute_from_amls(self, options, taxes):
|
||||
# compute the tax amount
|
||||
sql = self._sql_from_amls_one()
|
||||
tables, where_clause, where_params = self.env[
|
||||
'account.move.line']._query_get()
|
||||
|
||||
query = sql % (tables, where_clause)
|
||||
self.env.cr.execute(query, where_params)
|
||||
results = self.env.cr.fetchall()
|
||||
for result in results:
|
||||
if result[0] in taxes:
|
||||
taxes[result[0]]['tax'] = abs(result[1])
|
||||
|
||||
# compute the net amount
|
||||
sql2 = self._sql_from_amls_two()
|
||||
query = sql2 % (tables, where_clause)
|
||||
self.env.cr.execute(query, where_params)
|
||||
results = self.env.cr.fetchall()
|
||||
for result in results:
|
||||
if result[0] in taxes:
|
||||
taxes[result[0]]['net'] = abs(result[1])
|
||||
|
||||
@api.model
|
||||
def get_lines(self, options):
|
||||
taxes = {}
|
||||
for tax in self.env['account.tax'].search(
|
||||
[('type_tax_use', '!=', 'none')]):
|
||||
if tax.children_tax_ids:
|
||||
for child in tax.children_tax_ids:
|
||||
if child.type_tax_use != 'none':
|
||||
continue
|
||||
taxes[child.id] = {'tax': 0, 'net': 0, 'name': child.name,
|
||||
'type': tax.type_tax_use}
|
||||
else:
|
||||
taxes[tax.id] = {'tax': 0, 'net': 0, 'name': tax.name,
|
||||
'type': tax.type_tax_use}
|
||||
if options['date_from'] and not options['date_to']:
|
||||
self.with_context(date_from=options['date_from'],
|
||||
strict_range=True)._compute_from_amls(options,
|
||||
taxes)
|
||||
elif options['date_to'] and not options['date_from']:
|
||||
self.with_context(date_to=options['date_to'],
|
||||
strict_range=True)._compute_from_amls(options,
|
||||
taxes)
|
||||
elif options['date_from'] and options['date_to']:
|
||||
self.with_context(date_from=options['date_from'],
|
||||
date_to=options['date_to'],
|
||||
strict_range=True)._compute_from_amls(options,
|
||||
taxes)
|
||||
else:
|
||||
date_to = str(datetime.today().date())
|
||||
self.with_context(date_to=date_to,
|
||||
strict_range=True)._compute_from_amls(options,
|
||||
taxes)
|
||||
|
||||
groups = dict((tp, []) for tp in ['sale', 'purchase'])
|
||||
for tax in taxes.values():
|
||||
if tax['tax']:
|
||||
groups[tax['type']].append(tax)
|
||||
return groups
|
||||
75
addons/base_accounting_kit/report/report_tax_template.xml
Normal file
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<template id="report_tax">
|
||||
<t t-call="web.html_container">
|
||||
<t t-set="data_report_margin_top" t-value="12"/>
|
||||
<t t-set="data_report_header_spacing" t-value="9"/>
|
||||
<t t-set="data_report_dpi" t-value="110"/>
|
||||
<t t-call="web.internal_layout">
|
||||
<div class="page"><br/>
|
||||
<h3>Tax Report</h3>
|
||||
<div class="row">
|
||||
<div class="col-3">
|
||||
<strong>Company:</strong>
|
||||
<p t-esc="env.company.name"/>
|
||||
</div>
|
||||
<div>
|
||||
<t t-if="data['date_from']">
|
||||
<strong>Date from :</strong>
|
||||
<span t-esc="data['date_from']"/>
|
||||
</t>
|
||||
<br/>
|
||||
<t t-if="data['date_to']">
|
||||
<strong>Date to :</strong>
|
||||
<span t-esc="data['date_to']"/>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-sm table-reports">
|
||||
<thead>
|
||||
<tr class="text-centre">
|
||||
<th>Sale</th>
|
||||
<th>Net</th>
|
||||
<th>Tax</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr align="left" t-foreach="lines['sale']" t-as="line">
|
||||
<td>
|
||||
<span t-esc="line.get('name')"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-att-style="style" t-esc="line.get('net')"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-att-style="style" t-esc="line.get('tax')"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
</tr>
|
||||
<br/>
|
||||
<tr align="left">
|
||||
<td>
|
||||
<strong>Purchase</strong>
|
||||
</td>
|
||||
<td/>
|
||||
<td/>
|
||||
</tr>
|
||||
<tr align="left" t-foreach="lines['purchase']" t-as="line">
|
||||
<td>
|
||||
<span t-esc="line.get('name')"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-att-style="style" t-esc="line.get('net')"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-att-style="style" t-esc="line.get('tax')"
|
||||
t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
</odoo>
|
||||
112
addons/base_accounting_kit/report/report_trial_balance.py
Normal file
@@ -0,0 +1,112 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||
#
|
||||
# You can modify it under the terms of the GNU LESSER
|
||||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
# (LGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
import time
|
||||
from odoo import api, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class ReportTrialBalance(models.AbstractModel):
|
||||
_name = 'report.base_accounting_kit.report_trial_balance'
|
||||
_description = 'Trial Balance Report'
|
||||
|
||||
def _get_accounts(self, accounts, display_account):
|
||||
""" compute the balance, debit and credit for the provided accounts
|
||||
:Arguments:
|
||||
`accounts`: list of accounts record,
|
||||
`display_account`: it's used to display either all accounts or those accounts which balance is > 0
|
||||
:Returns a list of dictionary of Accounts with following key and value
|
||||
`name`: Account name,
|
||||
`code`: Account code,
|
||||
`credit`: total amount of credit,
|
||||
`debit`: total amount of debit,
|
||||
`balance`: total amount of balance,
|
||||
"""
|
||||
|
||||
account_result = {}
|
||||
# Prepare sql query base on selected parameters from wizard
|
||||
tables, where_clause, where_params = self.env[
|
||||
'account.move.line']._query_get()
|
||||
tables = tables.replace('"', '')
|
||||
if not tables:
|
||||
tables = 'account_move_line'
|
||||
wheres = [""]
|
||||
if where_clause.strip():
|
||||
wheres.append(where_clause.strip())
|
||||
filters = " AND ".join(wheres)
|
||||
# compute the balance, debit and credit for the provided accounts
|
||||
request = (
|
||||
"SELECT account_id AS id, SUM(debit) AS debit, "
|
||||
"SUM(credit) AS credit, (SUM(debit) - SUM(credit)) "
|
||||
"AS balance" +
|
||||
" FROM " + tables + " WHERE account_id IN %s " +
|
||||
filters + " GROUP BY account_id")
|
||||
params = (tuple(accounts.ids),) + tuple(where_params)
|
||||
self.env.cr.execute(request, params)
|
||||
for row in self.env.cr.dictfetchall():
|
||||
account_result[row.pop('id')] = row
|
||||
|
||||
account_res = []
|
||||
for account in accounts:
|
||||
res = dict((fn, 0.0) for fn in ['credit', 'debit', 'balance'])
|
||||
account_company = self.env.company
|
||||
currency = (account.currency_id and account.currency_id or
|
||||
account_company.currency_id)
|
||||
res['code'] = account.code
|
||||
res['name'] = account.name
|
||||
if account.id in account_result:
|
||||
res['debit'] = account_result[account.id].get('debit')
|
||||
res['credit'] = account_result[account.id].get('credit')
|
||||
res['balance'] = account_result[account.id].get('balance')
|
||||
if display_account == 'all':
|
||||
account_res.append(res)
|
||||
if display_account == 'not_zero' and not currency.is_zero(
|
||||
res['balance']):
|
||||
account_res.append(res)
|
||||
if display_account == 'movement' and (
|
||||
not currency.is_zero(res['debit']) or not currency.is_zero(
|
||||
res['credit'])):
|
||||
account_res.append(res)
|
||||
return account_res
|
||||
|
||||
@api.model
|
||||
def _get_report_values(self, docids, data=None):
|
||||
if not data.get('form') or not self.env.context.get('active_model'):
|
||||
raise UserError(
|
||||
_("Form content is missing, this report cannot be printed."))
|
||||
|
||||
model = self.env.context.get('active_model')
|
||||
docs = self.env[model].browse(
|
||||
self.env.context.get('active_ids', []))
|
||||
display_account = data['form'].get('display_account')
|
||||
accounts = docs if model == 'account.account' else self.env[
|
||||
'account.account'].search([])
|
||||
account_res = self.with_context(
|
||||
data['form'].get('used_context'))._get_accounts(accounts,
|
||||
display_account)
|
||||
return {
|
||||
'doc_ids': self.ids,
|
||||
'doc_model': model,
|
||||
'data': data['form'],
|
||||
'docs': docs,
|
||||
'time': time,
|
||||
'Accounts': account_res,
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<template id="report_trial_balance">
|
||||
<t t-call="web.html_container">
|
||||
<t t-set="data_report_margin_top" t-value="12"/>
|
||||
<t t-set="data_report_header_spacing" t-value="9"/>
|
||||
<t t-set="data_report_dpi" t-value="110"/>
|
||||
<t t-call="web.internal_layout">
|
||||
<div class="page"><br/>
|
||||
<h2><span t-esc="env.company.name"/>: Trial Balance</h2>
|
||||
|
||||
<div class="row mt32">
|
||||
<div class="col-4">
|
||||
<strong>Display Account:</strong>
|
||||
<p>
|
||||
<span t-if="data['display_account'] == 'all'">
|
||||
All accounts
|
||||
</span>
|
||||
<span t-if="data['display_account'] == 'movement'">
|
||||
With movements
|
||||
</span>
|
||||
<span t-if="data['display_account'] == 'not_zero'">
|
||||
With balance not equal to zero
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<p>
|
||||
<t t-if="data['date_from']">
|
||||
<strong>Date from :</strong>
|
||||
<span t-esc="data['date_from']"/>
|
||||
<br/>
|
||||
</t>
|
||||
<t t-if="data['date_to']">
|
||||
<strong>Date to :</strong>
|
||||
<span t-esc="data['date_to']"/>
|
||||
</t>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<strong>Target Moves:</strong>
|
||||
<p>
|
||||
<span t-if="data['target_move'] == 'all'">All
|
||||
Entries
|
||||
</span>
|
||||
<span t-if="data['target_move'] == 'posted'">All
|
||||
Posted Entries
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="table table-sm table-reports">
|
||||
<thead>
|
||||
<tr class="text-centre">
|
||||
<th>Code</th>
|
||||
<th>Account</th>
|
||||
<th class="text-right">Debit</th>
|
||||
<th class="text-right">Credit</th>
|
||||
<th class="text-right">Balance</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="Accounts" t-as="account">
|
||||
<td>
|
||||
<span t-att-style="style" t-esc="account['code']"/>
|
||||
</td>
|
||||
<td>
|
||||
<span style="color: white;" t-esc="'..'"/>
|
||||
<span t-att-style="style" t-esc="account['name']"/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-att-style="style"
|
||||
t-esc="account['debit']"
|
||||
t-options="{'widget': 'monetary',
|
||||
'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-att-style="style"
|
||||
t-esc="account['credit']"
|
||||
t-options="{'widget': 'monetary',
|
||||
'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-att-style="style"
|
||||
t-esc="account['balance']"
|
||||
t-options="{'widget': 'monetary',
|
||||
'display_currency': env.company.currency_id}"/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
</odoo>
|
||||
14
addons/base_accounting_kit/report/res_partner_reports.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<!-- Action for statement report -->
|
||||
<record id="res_partner_action" model="ir.actions.report">
|
||||
<field name="name">Statement Report</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">base_accounting_kit.res_partner_statement_report_template</field>
|
||||
<field name="report_file">base_accounting_kit.res_partner_statement_report_template</field>
|
||||
<field name="print_report_name">'Statement Report- %s' %(object.name)</field>
|
||||
<field name="binding_model_id" ref="model_res_partner"/>
|
||||
<field name="binding_type">report</field>
|
||||
</record>
|
||||
</odoo>
|
||||
76
addons/base_accounting_kit/report/res_partner_templates.xml
Normal file
@@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<!-- Statement report template -->
|
||||
<template id="res_partner_statement_report_template">
|
||||
<t t-call="web.html_container">
|
||||
<t t-call="web.external_layout">
|
||||
<div page="page">
|
||||
<h3>Payment Statement Report</h3>
|
||||
</div><br/>
|
||||
<table border="0">
|
||||
<tr><t t-esc="customer"/></tr><br/><br/>
|
||||
<tr><t t-if="street"> <t t-esc="street"/></t></tr><br/>
|
||||
<tr><t t-if="street2"> <t t-esc="street2"/></t></tr><br/>
|
||||
<tr><t t-if="city"> <t t-esc="city"/></t></tr><br/>
|
||||
<tr><t t-if="state"> <t t-esc="state"/></t></tr><br/>
|
||||
</table>
|
||||
<br/><br/>
|
||||
<table class="table" style="align-items: center;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Invoice/Bill Number</th>
|
||||
<th>Due Date</th>
|
||||
<th>Invoices/Debit</th>
|
||||
<th>Balance</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<t t-foreach="my_data" t-as="line">
|
||||
<tr>
|
||||
<td align="center"><t t-esc="line['invoice_date']"/></td>
|
||||
<td align="center"><t t-esc="line['name']"/></td>
|
||||
<td align="center"><t t-esc="line['invoice_date_due']"/></td>
|
||||
<td align="center">
|
||||
<t t-esc="currency"/>
|
||||
<t t-esc="line['sub_total']"/>
|
||||
</td>
|
||||
<td align="center">
|
||||
<t t-esc="currency"/>
|
||||
<t t-esc="line['balance']"/>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
</tbody>
|
||||
</table>
|
||||
<br/>
|
||||
<t t-if="total">
|
||||
<div class="clearfix" name="so_total_summary">
|
||||
<div id="total" class="row" name="total">
|
||||
<div t-attf-class="#{'col-6' if report_type != 'html' else 'col-sm-7 col-md-6'} ms-auto">
|
||||
<table class="table table-sm">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Total Amount:</td>
|
||||
<td>
|
||||
<t t-esc="currency"/>
|
||||
<t t-esc="total"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total Balance:</td>
|
||||
<td>
|
||||
<t t-esc="currency"/>
|
||||
<t t-esc="balance"/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
</odoo>
|
||||
49
addons/base_accounting_kit/security/ir.model.access.csv
Normal file
@@ -0,0 +1,49 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_financial_report_user,account_fin_rep_name_user,model_account_financial_report,account.group_account_user,1,1,1,1
|
||||
access_financial_report_manager,account_fin_rep_name_manager,model_account_financial_report,base_accounting_kit.group_account_chief,1,1,1,1
|
||||
access_account_followup_manager,account.followup.manager,model_account_followup,base_accounting_kit.group_account_chief,1,1,1,1
|
||||
access_account_followup_user,account.followup.user,model_account_followup,account.group_account_user,1,1,1,1
|
||||
access_followup_line,followup.line,model_followup_line,base_accounting_kit.group_account_chief,1,1,1,1
|
||||
access_account_followup_line_user,account.followup.line.user,model_followup_line,account.group_account_user,1,1,1,1
|
||||
|
||||
access_account_asset_category,account.asset.category,model_account_asset_category,account.group_account_user,1,0,0,0
|
||||
access_asset_modify_user,access_asset_modify_user,model_asset_modify,account.group_account_user,1,0,0,0
|
||||
access_asset_modify_manager,access_asset_modify_manager,model_asset_modify,base_accounting_kit.group_account_chief,1,1,1,1
|
||||
access_account_asset_asset,account.asset.asset,model_account_asset_asset,account.group_account_user,1,0,0,0
|
||||
access_account_asset_category_manager,account.asset.category,model_account_asset_category,base_accounting_kit.group_account_chief,1,1,1,1
|
||||
access_account_asset_asset_manager,account.asset.asset,model_account_asset_asset,base_accounting_kit.group_account_chief,1,1,1,1
|
||||
access_account_asset_depreciation_line,account.asset.depreciation.line,model_account_asset_depreciation_line,account.group_account_user,1,0,0,0
|
||||
access_account_asset_depreciation_line_manager,account.asset.depreciation.line,model_account_asset_depreciation_line,base_accounting_kit.group_account_chief,1,1,1,1
|
||||
access_asset_asset_report,asset.asset.report,model_asset_asset_report,account.group_account_user,1,0,0,0
|
||||
access_asset_asset_report_manager,asset.asset.report,model_asset_asset_report,base_accounting_kit.group_account_chief,1,1,1,1
|
||||
access_account_asset_category_invoicing_payment,account.asset.category,model_account_asset_category,account.group_account_invoice,1,0,0,0
|
||||
access_account_asset_asset_invoicing_payment,account.asset.asset,model_account_asset_asset,account.group_account_invoice,1,0,1,0
|
||||
access_account_asset_depreciation_line_invoicing_payment,account.asset.depreciation.line,model_account_asset_depreciation_line,account.group_account_invoice,1,0,1,0
|
||||
|
||||
access_account_aged_trial_balance,access.account.aged.trial.balance,model_account_aged_trial_balance,account.group_account_user,1,1,1,1
|
||||
access_account_account_bank_book_report,access.account.bank.book.report,model_account_bank_book_report,account.group_account_user,1,1,1,1
|
||||
access_account_cash_book_report,access.account.cash.book.report,model_account_cash_book_report,account.group_account_user,1,1,1,1
|
||||
access_account_day_book_report,access.account.day.book.report,model_account_day_book_report,account.group_account_user,1,1,1,1
|
||||
access_account_common_partner_report,access.account.common.partner.report,model_account_common_partner_report,account.group_account_user,1,1,1,1
|
||||
access_asset_depreciation_confirmation,access.asset.depreciation.confirmation,model_asset_depreciation_confirmation,account.group_account_user,1,1,1,1
|
||||
access_cash_flow_report,access.cash.flow.report,model_cash_flow_report,account.group_account_user,1,1,1,1
|
||||
access_financial_report,access.financial.report,model_financial_report,account.group_account_user,1,1,1,1
|
||||
access_report_base_accounting_kit_report_financial,access.report.base_accounting_kit.report_financial,model_report_base_accounting_kit_report_financial,account.group_account_user,1,1,1,1
|
||||
access_account_report_general_ledger,access.account.report.general.ledger,model_account_report_general_ledger,account.group_account_user,1,1,1,1
|
||||
access_account_print_journal,access.account.print.journal,model_account_print_journal,account.group_account_user,1,1,1,1
|
||||
access_account_report_partner_ledger,access.account.report.partner.ledger,model_account_report_partner_ledger,account.group_account_user,1,1,1,1
|
||||
access_account_common_account_report,access.account.common.account.report,model_account_common_account_report,account.group_account_user,1,1,1,1
|
||||
access_kit_account_tax_report,access.kit.account.tax.report,model_kit_account_tax_report,account.group_account_user,1,1,1,1
|
||||
access_account_balance_report,access.account.balance.report,model_account_balance_report,account.group_account_user,1,1,1,1
|
||||
|
||||
access_multiple_invoice,multiple_invoice,model_multiple_invoice,base_accounting_kit.group_account_chief,1,1,1,1
|
||||
access_multiple_invoice_layout,multiple_invoice_layout,model_multiple_invoice_layout,base_accounting_kit.group_account_chief,1,1,1,1
|
||||
access_account_common_journal_report,account.common.journal.report,model_account_common_journal_report,account.group_account_user,1,1,1,1
|
||||
|
||||
access_account_account_type,account.account.type,model_account_account_type,account.group_account_user,1,1,1,1
|
||||
|
||||
access_account_lock_date,access.account.lock.date,model_account_lock_date,account.group_account_user,1,1,1,1
|
||||
access_account_recurring_entries_line,access.account.recurring.entries.line,model_account_recurring_entries_line,account.group_account_user,1,1,1,1
|
||||
access_generate_recurring_entries,generate.recurring.entries.user,model_account_recurring_payments,account.group_account_user,1,1,1,1
|
||||
|
||||
access_import_bank_statement_user,access.import.bank.statement.user,model_import_bank_statement,base.group_user,1,1,1,1
|
||||
|
52
addons/base_accounting_kit/security/security.xml
Normal file
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record model="res.groups" id="group_account_dashboard">
|
||||
<field name="name">Access to Accounting Dashboard</field>
|
||||
</record>
|
||||
<record id="account_asset_category_multi_company_rule" model="ir.rule">
|
||||
<field name="name">Account Asset Category multi-company</field>
|
||||
<field ref="model_account_asset_category" name="model_id"/>
|
||||
<field eval="True" name="global"/>
|
||||
<field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="account_asset_asset_multi_company_rule" model="ir.rule">
|
||||
<field name="name">Account Asset multi-company</field>
|
||||
<field ref="model_account_asset_asset" name="model_id"/>
|
||||
<field eval="True" name="global"/>
|
||||
<field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]
|
||||
</field>
|
||||
</record>
|
||||
<record id="account.group_account_invoice" model="res.groups">
|
||||
<field name="name">Invoicing</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
|
||||
<field name="privilege_id" ref="account.res_groups_privilege_accounting"/>
|
||||
</record>
|
||||
<record id="account.group_account_user" model="res.groups">
|
||||
<field name="name">Accountant</field>
|
||||
<field name="sequence">2</field>
|
||||
<field name="implied_ids"
|
||||
eval="[(4, ref('account.group_account_invoice'))]"/>
|
||||
<field name="privilege_id" ref="account.res_groups_privilege_accounting"/>
|
||||
</record>
|
||||
|
||||
<record id="group_account_chief" model="res.groups">
|
||||
<field name="name">Chief Accountant</field>
|
||||
<field name="sequence">51</field>
|
||||
<field name="implied_ids" eval="[(4, ref('account.group_account_invoice')), (4, ref('account.group_account_user')),(4, ref('account.group_account_manager'))]"/>
|
||||
<field name="privilege_id" ref="account.res_groups_privilege_accounting"/>
|
||||
<field name="user_ids" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
|
||||
</record>
|
||||
<record id="account.group_account_manager" model="res.groups">
|
||||
<field name="name">Administrator</field>
|
||||
<field name="sequence">52</field>
|
||||
<field name="comment">Full access, including configuration rights.</field>
|
||||
<field name="privilege_id" ref="account.res_groups_privilege_accounting"/>
|
||||
<field name="implied_ids" eval="[(4, ref('account.group_account_invoice')),(4, ref('base_accounting_kit.group_account_chief'))]"/>
|
||||
<field name="user_ids" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 710 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 127 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 131 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 695 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 76 KiB |
BIN
addons/base_accounting_kit/static/description/banner.gif
Normal file
|
After Width: | Height: | Size: 14 MiB |
BIN
addons/base_accounting_kit/static/description/icon.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
1665
addons/base_accounting_kit/static/description/index.html
Normal file
278
addons/base_accounting_kit/views/account_asset_asset_views.xml
Normal file
@@ -0,0 +1,278 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<!-- Asset Form View -->
|
||||
<record model="ir.ui.view" id="account_asset_asset_view_form">
|
||||
<field name="name">account.asset.asset.view.form</field>
|
||||
<field name="model">account.asset.asset</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Asset">
|
||||
<header>
|
||||
<button name="validate" invisible="state != 'draft'" string="Confirm" type="object"
|
||||
class="oe_highlight"/>
|
||||
<button type="object" name="compute_depreciation_board" string="Compute Depreciation"
|
||||
invisible="state != 'draft'"/>
|
||||
<button name="set_to_close" invisible="state != 'open'" string="Sell or Dispose" type="object"
|
||||
class="oe_highlight"/>
|
||||
<button name="action_save_model" invisible="state != 'open' or category_id" string="Save as model" type="object"
|
||||
/>
|
||||
<button name="action_cancel_assets" string="Cancel Asset" type="object"
|
||||
invisible="state != 'open'"/>
|
||||
<button name="set_to_draft" string="Set to Draft" type="object"
|
||||
invisible="state != 'cancelled'"/>
|
||||
<button name="%(action_asset_modify)d" invisible="state != 'open'" string="Modify Depreciation"
|
||||
type="action"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,open"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button class="oe_stat_button" name="open_entries" type="object" icon="fa-pencil">
|
||||
<field string="Items" name="entry_count" widget="statinfo"/>
|
||||
</button>
|
||||
</div>
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1>
|
||||
<field name="name" placeholder="e.g. Laptop iBook"
|
||||
readonly="state != 'draft'"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="category_id" domain="[('type', '=', 'purchase')]"
|
||||
context="{'default_type': 'purchase'}"
|
||||
help="Category of asset" readonly="state != 'draft'"/>
|
||||
<field name="code" readonly="state != 'draft'"/>
|
||||
<field name="date" help="Date of asset"/>
|
||||
<field name="type" invisible="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="currency_id" groups="base.group_multi_currency" readonly="state != 'draft'"/>
|
||||
<field name="company_id" options="{'no_create': True}"
|
||||
groups="base.group_multi_company" readonly="state != 'draft'"/>
|
||||
<field name="value" widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"
|
||||
help="Gross value of asset"
|
||||
readonly="state != 'draft'"/>
|
||||
<field name="salvage_value" widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"
|
||||
readonly="state != 'draft'"
|
||||
invisible="type in 'sale'"/>
|
||||
<field name="value_residual" widget="monetary" options="{'currency_field': 'currency_id'}"/>
|
||||
<field name="partner_id" string="Vendor" readonly="state != 'draft'"
|
||||
domain="[('supplier_rank', '>', 0)]"/>
|
||||
<field name="invoice_id" string="Invoice"
|
||||
options="{'no_create': True}"
|
||||
readonly="state != 'draft'"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook colspan="4">
|
||||
<page string="Assets">
|
||||
<group>
|
||||
<group>
|
||||
<field name="type" invisible="1"/>
|
||||
<field name="company_id" options="{'no_create': True}" readonly="state != 'draft'"/>
|
||||
</group>
|
||||
<group string="Journal Entries">
|
||||
<field name="journal_id" readonly="state != 'draft'"/>
|
||||
<div>
|
||||
<label for="account_asset_id" invisible="type != 'purchase'"/>
|
||||
<label for="account_asset_id" string="Deferred Revenue Account"
|
||||
invisible="type != 'sale'"/>
|
||||
</div>
|
||||
<field name="account_asset_id" nolabel="1" invisible="not type" readonly="state != 'draft'"/>
|
||||
<div>
|
||||
<label for="account_depreciation_id" invisible="type != 'purchase'"/>
|
||||
<label for="account_depreciation_id" string="Recognition Income Account"
|
||||
invisible="type != 'sale'"/>
|
||||
</div>
|
||||
<field name="account_depreciation_id" nolabel="1" readonly="state != 'draft'"/>
|
||||
<div>
|
||||
<label for="account_depreciation_expense_id" invisible="type != 'purchase'"/>
|
||||
<label for="account_depreciation_expense_id" string="Recognition Account"
|
||||
invisible="type != 'sale'"/>
|
||||
</div>
|
||||
<field name="account_depreciation_expense_id" nolabel="1" readonly="state != 'draft'"/>
|
||||
<field name="account_analytic_id" readonly="state != 'draft'"/>
|
||||
</group>
|
||||
<group string="Periodicity">
|
||||
<field name="method_time" string="Time Method Based On" widget="radio"
|
||||
invisible="type != 'purchase'" readonly="state != 'draft'"/>
|
||||
<field name="method_number" string="Number of Entries"
|
||||
invisible="method_time != 'number' and not type"
|
||||
required="method_time in 'number'" readonly="state != 'draft'"/>
|
||||
<label for="method_period" string="One Entry Every"/>
|
||||
<div>
|
||||
<field name="method_period" nolabel="1" invisible="not type" class="oe_inline" readonly="state != 'draft'"/>
|
||||
months
|
||||
</div>
|
||||
<field name="method_end"
|
||||
required="method_time in 'end'"
|
||||
invisible="method_time != 'end'" readonly="state != 'draft'"/>
|
||||
</group>
|
||||
<group string="Additional Options">
|
||||
<field name="open_asset" readonly="state != 'draft'"/>
|
||||
<field name="group_entries" readonly="state != 'draft'"/>
|
||||
</group>
|
||||
<group invisible="type in 'sale'" string="Depreciation Method">
|
||||
<field name="method" readonly="state != 'draft'"/>
|
||||
<field name="method_progress_factor"
|
||||
invisible="method in 'linear'"
|
||||
required="method in 'degressive'" widget="percentage" readonly="state != 'draft'"/>
|
||||
<field name="prorata"
|
||||
invisible="method in 'end'" readonly="state != 'draft'"/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Depreciation Board" invisible="not depreciation_line_ids">
|
||||
<field name="depreciation_line_ids" mode="list"
|
||||
readonly="state not in ['draft', 'open']"
|
||||
options="{'reload_whole_on_button': true}">
|
||||
<list string="Depreciation Lines" decoration-info="(move_check == False)" create="0"
|
||||
editable="top">
|
||||
<field name="depreciation_date" readonly="1"/>
|
||||
<field name="depreciated_value" readonly="1"/>
|
||||
<field name="amount" widget="monetary" string="Depreciation" readonly="1"/>
|
||||
<field name="remaining_value" readonly="1" widget="monetary" string="Residual"/>
|
||||
<field name="move_check" widget="deprec_lines_toggler"
|
||||
invisible="parent_state != 'open'"/>
|
||||
<field name="move_posted_check" invisible="1"/>
|
||||
<field name="parent_state" invisible="1"/>
|
||||
</list>
|
||||
<form string="Depreciation Lines">
|
||||
<group>
|
||||
<group>
|
||||
<field name="parent_state" invisible="1"/>
|
||||
<field name="name"/>
|
||||
<field name="sequence"/>
|
||||
<field name="move_id"/>
|
||||
<field name="move_check"/>
|
||||
<field name="parent_state" invisible="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="amount" widget="monetary"/>
|
||||
<field name="depreciation_date"/>
|
||||
<field name="depreciated_value"/>
|
||||
<field name="remaining_value"/>
|
||||
</group>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<chatter/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<!--Asset Kanban View-->
|
||||
<record model="ir.ui.view" id="account_asset_asset_view_kanban">
|
||||
<field name="name">account.asset.asset.view.kanban</field>
|
||||
<field name="model">account.asset.asset</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban class="o_kanban_mobile">
|
||||
<field name="name"/>
|
||||
<field name="category_id"/>
|
||||
<field name="date"/>
|
||||
<field name="state"/>
|
||||
<templates>
|
||||
<t t-name="card">
|
||||
<div t-attf-class="oe_kanban_global_click">
|
||||
<div class="row mb4">
|
||||
<div class="col-xs-6">
|
||||
<strong>
|
||||
<span>
|
||||
<t t-esc="record.name.value"/>
|
||||
</span>
|
||||
</strong>
|
||||
</div>
|
||||
<div class="col-xs-6 pull-right text-right">
|
||||
<strong>
|
||||
<t t-esc="record.date.value"/>
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-6 text-muted">
|
||||
<span>
|
||||
<t t-esc="record.category_id.value"/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<span class="pull-right text-right">
|
||||
<field name="state" widget="label_selection"
|
||||
options="{'classes': {'draft': 'primary', 'open': 'success', 'close': 'default'}}"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
<!--Asset Tree View-->
|
||||
<record model="ir.ui.view" id="account_asset_asset_view_tree">
|
||||
<field name="name">account.asset.asset.view.tree</field>
|
||||
<field name="model">account.asset.asset</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Assets" decoration-info="(state == 'draft')" decoration-muted="(state == 'close')">
|
||||
<field name="name"/>
|
||||
<field name="category_id"/>
|
||||
<field name="date"/>
|
||||
<field name="partner_id" string="Vendor"/>
|
||||
<field name="value"/>
|
||||
<field name="value_residual" widget="monetary"/>
|
||||
<field name="currency_id" groups="base.group_multi_currency"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
<field name="state"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
<!--Asset Search View-->
|
||||
<record id="account_asset_asset_view_search" model="ir.ui.view">
|
||||
<field name="name">account.asset.asset.view.search</field>
|
||||
<field name="model">account.asset.asset</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Asset Account">
|
||||
<field name="name" string="Asset"/>
|
||||
<field name="date"/>
|
||||
<filter name="state" string="Current" domain="[('state','in', ('draft','open'))]"
|
||||
help="Assets in draft and open states"/>
|
||||
<filter name="state" string="Closed" domain="[('state','=', 'close')]" help="Assets in closed state"/>
|
||||
<field name="category_id"/>
|
||||
<field name="partner_id" filter_domain="[('partner_id','child_of',self)]"/>
|
||||
<group>
|
||||
<filter name="date" string="Month" domain="[]" context="{'group_by':'date'}"/>
|
||||
<filter name="category_id" string="Category" domain="[]" context="{'group_by':'category_id'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<!--Asset Action-->
|
||||
<record model="ir.actions.act_window" id="action_account_asset_asset_form">
|
||||
<field name="name">Assets</field>
|
||||
<field name="res_model">account.asset.asset</field>
|
||||
<field name="view_mode">list,kanban,form</field>
|
||||
<field name="view_id" ref="account_asset_asset_view_tree"/>
|
||||
<field name="domain">[('category_id.type', '=', 'purchase')]</field>
|
||||
</record>
|
||||
<!--Asset MenuItem-->
|
||||
<menuitem parent="account.menu_finance_entries"
|
||||
id="management_menu"
|
||||
name="Management"
|
||||
sequence="101" groups="account.group_account_user"/>
|
||||
<menuitem parent="base_accounting_kit.management_menu"
|
||||
id="menu_action_account_asset_asset_form"
|
||||
action="action_account_asset_asset_form"
|
||||
sequence="101" groups="account.group_account_user"/>
|
||||
<!-- <menuitem parent="base_accounting_kit.management_menu"-->
|
||||
<!-- id="menu_act_budget_view"-->
|
||||
<!-- name="Budgets"-->
|
||||
<!-- action="base_account_budget.act_budget_view" sequence="60"-->
|
||||
<!-- groups="account.group_account_user"/>-->
|
||||
<!-- Configuration -->
|
||||
<menuitem id="menu_finance_config_assets"
|
||||
name="Assets and Revenues"
|
||||
parent="account.menu_finance_configuration"
|
||||
sequence="25"/>
|
||||
</odoo>
|
||||
@@ -0,0 +1,146 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<!-- Asset Category Form View -->
|
||||
<record model="ir.ui.view" id="account_asset_category_view_form">
|
||||
<field name="name">account.asset.category.view.form</field>
|
||||
<field name="model">account.asset.category</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Asset category">
|
||||
<sheet>
|
||||
<group>
|
||||
<div class="oe_title">
|
||||
<label for="name" string="Asset Type"
|
||||
class="oe_edit_only"
|
||||
invisible="type != 'purchase'"/>
|
||||
<label for="name" string="Deferred Revenue Type"
|
||||
class="oe_edit_only"
|
||||
invisible="type in 'purchase'"/>
|
||||
<h1>
|
||||
<field name="name" placeholder="e.g. Computers"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<field name="type" invisible="1"/>
|
||||
<field name="company_id" options="{'no_create': True}"/>
|
||||
<field name="price"/>
|
||||
</group>
|
||||
<group string="Journal Entries">
|
||||
<field name="journal_id"/>
|
||||
<div>
|
||||
<label for="account_asset_id" invisible="type != 'purchase'"/>
|
||||
<label for="account_asset_id" string="Deferred Revenue Account" invisible="type != 'sale'"/>
|
||||
</div>
|
||||
<field name="account_asset_id" nolabel="1" invisible="not type"/>
|
||||
<div>
|
||||
<label for="account_depreciation_id" invisible="type != 'purchase'"/>
|
||||
<label for="account_depreciation_id" string="Recognition Income Account" invisible="type != 'sale'"/>
|
||||
</div>
|
||||
<field name="account_depreciation_id" nolabel="1" />
|
||||
<div>
|
||||
<label for="account_depreciation_expense_id" invisible="type != 'purchase'"/>
|
||||
<label for="account_depreciation_expense_id" string="Recognition Account" invisible="type != 'sale'"/>
|
||||
</div>
|
||||
<field name="account_depreciation_expense_id" nolabel="1"/>
|
||||
<field name="account_analytic_id"/>
|
||||
</group>
|
||||
<group string="Periodicity">
|
||||
<field name="method_time" string="Time Method Based On" widget="radio" invisible="type != 'purchase'"/>
|
||||
<field name="method_number" string="Number of Entries"
|
||||
invisible="method_time != 'number' and not type"
|
||||
required="method_time in 'number'"/>
|
||||
<label for="method_period" string="One Entry Every"/>
|
||||
<div>
|
||||
<field name="method_period" nolabel="1" invisible="not type" class="oe_inline"/>
|
||||
months
|
||||
</div>
|
||||
<field name="method_end"
|
||||
required="method_time in 'end'"
|
||||
invisible="method_time != 'end'"/>
|
||||
</group>
|
||||
<group string="Additional Options">
|
||||
<field name="open_asset"/>
|
||||
<field name="group_entries"/>
|
||||
</group>
|
||||
<group invisible="type in 'sale'" string="Depreciation Method">
|
||||
<field name="method"/>
|
||||
<field name="method_progress_factor"
|
||||
invisible="method in 'linear'"
|
||||
required="method in 'degressive'" widget="percentage"/>
|
||||
<field name="prorata"
|
||||
invisible="method in 'end'"/>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<!-- Asset Category Kanban View -->
|
||||
<record id="account_asset_category_view_kanban" model="ir.ui.view">
|
||||
<field name="name">account.asset.category.view.kanban</field>
|
||||
<field name="model">account.asset.category</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban class="o_kanban_mobile">
|
||||
<field name="name"/>
|
||||
<field name="journal_id"/>
|
||||
<field name="method"/>
|
||||
<templates>
|
||||
<t t-name="card">
|
||||
<div t-attf-class="oe_kanban_card oe_kanban_global_click">
|
||||
<div class="row mb4">
|
||||
<div class="col-xs-6">
|
||||
<strong><span><t t-esc="record.name.value"/></span></strong>
|
||||
</div>
|
||||
<div class="col-xs-6 text-right">
|
||||
<span class="badge"><strong><t t-esc="record.method.value"/></strong></span>
|
||||
</div>
|
||||
</div>
|
||||
<div> <t t-esc="record.journal_id.value"/></div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
<!-- Asset Category Tree View -->
|
||||
<record model="ir.ui.view" id="account_asset_category_view_tree">
|
||||
<field name="name">account.asset.category.view.tree</field>
|
||||
<field name="model">account.asset.category</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Asset category">
|
||||
<field name="name"/>
|
||||
<field name="journal_id"/>
|
||||
<field name="method"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
<!-- Asset Category Search View -->
|
||||
<record model="ir.ui.view" id="account_asset_category_view_search">
|
||||
<field name="name">account.asset.category.view.search</field>
|
||||
<field name="model">account.asset.category</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Asset Category">
|
||||
<filter name="type" string="Sales" domain="[('type','=', 'sale')]" help="Deferred Revenues"/>
|
||||
<filter name="type" string="Purchase" domain="[('type','=', 'purchase')]" help="Assets"/>
|
||||
<field name="name" string="Category"/>
|
||||
<field name="journal_id"/>
|
||||
<group>
|
||||
<filter name="type" string="Type" domain="[]" context="{'group_by':'type'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<!-- Asset Category Action -->
|
||||
<record model="ir.actions.act_window" id="action_account_asset_asset_list_normal_purchase">
|
||||
<field name="name">Asset Types</field>
|
||||
<field name="res_model">account.asset.category</field>
|
||||
<field name="domain">[('type', '=', 'purchase')]</field>
|
||||
<field name="view_mode">list,kanban,form</field>
|
||||
<field name="context">{'default_type': 'purchase'}</field>
|
||||
</record>
|
||||
<!-- Asset Category MenuItem -->
|
||||
<menuitem parent="account.account_account_menu"
|
||||
id="menu_action_account_asset_asset_list_normal_purchase"
|
||||
action="action_account_asset_asset_list_normal_purchase"
|
||||
sequence="6"/>
|
||||
</odoo>
|
||||
@@ -0,0 +1,287 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<!-- Account Bank Statement Line Search View-->
|
||||
<record id="account_bank_statement_line_view_search" model="ir.ui.view">
|
||||
<field name="name">account.bank.statement.line.view.search</field>
|
||||
<field name="model">account.bank.statement.line</field>
|
||||
<field name="priority">999</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<field name="name" string="Transaction"/>
|
||||
<field name="payment_ref"/>
|
||||
<field name="date"/>
|
||||
<field name="statement_id"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="journal_id"/>
|
||||
<field name="narration" string="Notes"/>
|
||||
<field name="amount"/>
|
||||
<field name="move_id" string="Journal Entry"/>
|
||||
<separator/>
|
||||
<filter name="deposits" string="Deposits"
|
||||
domain="[('amount','>',0.0)]"/>
|
||||
<filter name="payments" string="Payments"
|
||||
domain="[('amount','<',0.0)]"/>
|
||||
<separator/>
|
||||
<filter name="no_statement" string="No Statement"
|
||||
domain="[('statement_id','=',False)]"/>
|
||||
<filter name="invalid_statement" string="Invalid Statement"
|
||||
domain="[('statement_complete','=',False)]"/>
|
||||
<separator/>
|
||||
<filter name="matched" string="Matched"
|
||||
domain="[('is_reconciled','=',True)]"/>
|
||||
<filter name="not_matched" string="Not Matched"
|
||||
domain="[('is_reconciled','=',False)]"/>
|
||||
<filter name="to_check" string="To Check"
|
||||
domain="[('to_check','=',True)]"/>
|
||||
<separator/>
|
||||
<filter name="date" string="Date" date="date"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<!-- Kanban view on statement line injected inside the form (view_bank_reconcile_widget) -->
|
||||
<record id="account_bank_statement_line_view_kanban" model="ir.ui.view">
|
||||
<field name="name">account.bank.statement.line.view.kanban</field>
|
||||
<field name="model">account.bank.statement.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban js_class="custom_kanban"
|
||||
on_create="base_accounting_kit.action_bank_statement_line_form_bank_reconcile_widget">
|
||||
<field name="id" invisible="1"/>
|
||||
<field name="state" invisible="1"/>
|
||||
<field name="statement_complete" invisible="1"/>
|
||||
<field name="statement_valid" invisible="1"/>
|
||||
<field name="sequence" invisible="1"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="journal_id" invisible="1"/>
|
||||
<field name="statement_id" invisible="1"/>
|
||||
<field name="is_reconciled" invisible="1"/>
|
||||
<field name="to_check" invisible="1"/>
|
||||
<field name="partner_bank_id" invisible="1"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="foreign_currency_id" invisible="1"/>
|
||||
<field name="statement_line_id" invisible="1"/>
|
||||
<field name="lines_widget_json" invisible="1"/>
|
||||
<templates>
|
||||
<t t-name="card">
|
||||
<div t-attf-class="oe_kanban_card oe_kanban_global_click o_bank_reconcile_st_line_kanban_card"
|
||||
t-att-data-oe-id="record.id">
|
||||
<t t-set="text_amount_class" t-value="record.amount < 0.0 and 'text-danger'"/>
|
||||
|
||||
<!-- Statement Button -->
|
||||
<div t-if="!record.statement_complete.raw_value" class="statement_button text-center mb-2">
|
||||
<a role="button"
|
||||
class="btn btn-secondary btn-sm statement"
|
||||
tabindex="-1" type="action"
|
||||
name="%(base_accounting_kit.action_bank_statement_create_form_bank_reconcile_widget)d">
|
||||
Statement
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="container-fluid p-0">
|
||||
<!-- First Row -->
|
||||
<div class="row g-0 align-items-center mb-1">
|
||||
<div class="col-auto pe-2">
|
||||
<strong class="bold_date">
|
||||
<field name="date" class="d-inline"/>
|
||||
</strong>
|
||||
</div>
|
||||
<div class="col text-truncate">
|
||||
<em>
|
||||
<field name="partner_id" class="d-inline"/>
|
||||
</em>
|
||||
</div>
|
||||
<div t-if="record.statement_id.raw_value" class="col-auto ms-auto">
|
||||
<div t-attf-class="text-truncate {{!(record.statement_complete.raw_value and record.statement_valid.raw_value) and 'text-danger' or ''}}">
|
||||
<field name="statement_id" class="d-inline"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<strong t-att-class="text_amount_class">
|
||||
<field name="amount" class="d-inline"/>
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Second Row -->
|
||||
<div class="row g-0 align-items-center">
|
||||
<div class="col text-truncate pe-2">
|
||||
<field name="payment_ref" class="d-inline"/>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="o_field_many2manytags o_field_widget d-flex flex-wrap gap-1">
|
||||
<span t-if="record.to_check.raw_value"
|
||||
class="badge text-bg-warning fw-normal to_check">
|
||||
To Check
|
||||
</span>
|
||||
<div t-if="record.is_reconciled.raw_value"
|
||||
class="badge text-bg-success fw-normal">
|
||||
<i class="fa fa-check me-1"></i>
|
||||
Matched
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
<!-- Account Bank Statement Line Tree View -->
|
||||
<record id="account_bank_statement_line_view_tree" model="ir.ui.view">
|
||||
<field name="name">account.bank.statement.line.view.tree</field>
|
||||
<field name="model">account.bank.statement.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<list editable="top">
|
||||
<field name="statement_complete" column_invisible="1"/>
|
||||
<field name="statement_valid" column_invisible="1"/>
|
||||
<field name="state" column_invisible="1"/>
|
||||
<field name="is_reconciled" column_invisible="1"/>
|
||||
<field name="to_check" column_invisible="1"/>
|
||||
<field name="country_code" column_invisible="1"/>
|
||||
<field name="currency_id" column_invisible="1"/>
|
||||
<field name="sequence" widget="handle" column_invisible="1"/>
|
||||
<field name="date"
|
||||
readonly="is_reconciled == True and amount != 0"
|
||||
options="{'datepicker':{'warn_future':true}}"/>
|
||||
<field name="payment_ref" string="Label"/>
|
||||
<field name="partner_id" string="Partner"/>
|
||||
<field name="amount"
|
||||
readonly="is_reconciled == True and amount != 0"/>
|
||||
<field name="running_balance" string="Running Balance"/>
|
||||
<field name="statement_id" string="Statement"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
<!-- Account Bank Statement Line Form View -->
|
||||
<record id="account_bank_statement_line_view_form"
|
||||
model="ir.ui.view">
|
||||
<field name="name">account.bank.statement.line.view.form</field>
|
||||
<field name="model">account.bank.statement.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Add a Transaction">
|
||||
<field name="state" invisible="1"/>
|
||||
<field name="statement_complete" invisible="1"/>
|
||||
<field name="statement_valid" invisible="1"/>
|
||||
<field name="is_reconciled" invisible="1"/>
|
||||
<field name="suitable_journal_ids" invisible="1"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<group>
|
||||
<group>
|
||||
<field name="date"
|
||||
readonly="is_reconciled == True and amount != 0"/>
|
||||
<field name="payment_ref" required="1"
|
||||
readonly="is_reconciled == True and amount != 0"/>
|
||||
<field name="partner_id"
|
||||
readonly="is_reconciled == True and amount != 0"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="amount"
|
||||
readonly="is_reconciled == True and amount != 0"/>
|
||||
<field name="foreign_currency_id"
|
||||
groups="base.group_multi_currency"
|
||||
domain="[('id', '!=', currency_id)]"
|
||||
options="{'no_open': True, 'no_create': True}"
|
||||
readonly="is_reconciled == True and amount != 0"/>
|
||||
<field name="amount_currency"
|
||||
groups="base.group_multi_currency"
|
||||
invisible="foreign_currency_id == False"
|
||||
readonly="is_reconciled == True and amount != 0"/>
|
||||
<field name="journal_id"
|
||||
domain="[('type', 'in', ['bank', 'cash'])]"
|
||||
readonly="context.get('default_journal_id') and is_reconciled == True and amount != 0"/>
|
||||
</group>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<!-- Action Account Statement Line -->
|
||||
<record id="action_bank_statement_line_form_bank_reconcile_widget"
|
||||
model="ir.actions.act_window">
|
||||
<field name="name">Add a Transaction</field>
|
||||
<field name="res_model">account.bank.statement.line</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id"
|
||||
ref="account_bank_statement_line_view_form"/>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
<!--Account Bank Statement Line Form View-->
|
||||
<record id="view_bank_reconcile_widget_form" model="ir.ui.view">
|
||||
<field name="name">account.bank.statement.line.view.form</field>
|
||||
<field name="model">account.bank.statement.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<form class="form_bank">
|
||||
<field name="to_check" invisible="1"/>
|
||||
<field name="state" invisible="1"/>
|
||||
<field name="bank_state" invisible="1"/>
|
||||
<div class="o_bank_reconcile_stats_buttons">
|
||||
<div class="o_statusbar_buttons o_bank_reconcile_status_buttons_aside_left">
|
||||
<button name="button_validation"
|
||||
string="Validate" type="object"
|
||||
class="btn btn-primary" id="validationButton"
|
||||
invisible="bank_state != 'valid'"/>
|
||||
<button string="Validate"
|
||||
class="btn btn-secondary text-muted"
|
||||
invisible="bank_state != 'invalid'"/>
|
||||
<button name="button_reset"
|
||||
invisible="bank_state != 'reconciled'"
|
||||
string="Reset" type="object"/>
|
||||
<button name="button_to_check"
|
||||
string="To Check" type="object"
|
||||
class="btn btn-secondary"
|
||||
invisible="to_check == True"/>
|
||||
<button name="button_set_as_checked"
|
||||
string="Set as Checked"
|
||||
type="object"
|
||||
invisible="to_check == False"/>
|
||||
</div>
|
||||
<div class="bank_reconcile_models"
|
||||
style="margin-left: 880px;margin-top: 10px;"
|
||||
invisible="bank_state not in ('valid','invalid')">
|
||||
</div>
|
||||
</div>
|
||||
<field name="lines_widget"
|
||||
widget="bank_reconcile_widget_lines_widget"
|
||||
class="w-100"/>
|
||||
<notebook name="bank_reconcile_widget_notebook">
|
||||
<page string="Match Existing Entries"
|
||||
invisible="bank_state == 'reconciled'">
|
||||
<widget name="form_list_view"
|
||||
resModel="account.move.line"/>
|
||||
</page>
|
||||
<page name="manual_operations_tab"
|
||||
string="Manual Operations"
|
||||
invisible="bank_state == 'reconciled'">
|
||||
<group>
|
||||
<group>
|
||||
<field name="partner_id"
|
||||
string="Partner"/>
|
||||
<field name='account_id' string="Account"/>
|
||||
<field name="tax_ids" string="Taxes"
|
||||
widget="many2many_tags"/>
|
||||
<field name="analytic_distribution"
|
||||
widget="analytic_distribution"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="payment_ref" string="Label"
|
||||
id="payment_form"/>
|
||||
<field name="amount" string="Amount"/>
|
||||
<div class="d-flex">
|
||||
<span class="ml4 mr4">in</span>
|
||||
<field name="company_currency_id"
|
||||
style="width:120px"/>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
<page name="discuss_page" string="Discuss">
|
||||
<div class="bank_reconcile_widget_discuss_anchor">
|
||||
<chatter/>
|
||||
</div>
|
||||
</page>
|
||||
</notebook>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<!-- Account Bank Statement Form View -->
|
||||
<record id="account_bank_statement_view_form"
|
||||
model="ir.ui.view">
|
||||
<field name="name">account.bank.statement.view.form</field>
|
||||
<field name="model">account.bank.statement</field>
|
||||
<field name="priority">100</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="name" required="1"/>
|
||||
<field name="balance_start"/>
|
||||
<field name="balance_end_real"/>
|
||||
<field name="attachment_ids" widget="many2many_binary"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<!-- Action Account Bank Statement -->
|
||||
<record id="action_bank_statement_create_form_bank_reconcile_widget"
|
||||
model="ir.actions.act_window">
|
||||
<!-- Binding removed since the same can be achieved with multi-edit -->
|
||||
<field name="name">Create Statement</field>
|
||||
<field name="res_model">account.bank.statement</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id"
|
||||
ref="account_bank_statement_view_form"/>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
16
addons/base_accounting_kit/views/account_configuration.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<!--Inheriting account form view-->
|
||||
<record id="view_account_form" model="ir.ui.view">
|
||||
<field name="name">account.account.view.form.inherit.base.accounting.kit</field>
|
||||
<field name="model">account.account</field>
|
||||
<field name="inherit_id" ref="account.view_account_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//field[@name='active']" position="after">
|
||||
<field name="cash_flow_type" widget="selection"/>
|
||||
</xpath>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
88
addons/base_accounting_kit/views/account_followup.xml
Normal file
@@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<!-- FollowUp View Form -->
|
||||
<record id="account_followup_view_form" model="ir.ui.view">
|
||||
<field name="name">account.followup.view.form</field>
|
||||
<field name="model">account.followup</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Follow-up">
|
||||
<h1><field name="name"/></h1>
|
||||
<label for="company_id" groups="base.group_multi_company"/>
|
||||
<field name="company_id" options="{'no_create': True}" class="oe_inline"
|
||||
groups="base.group_multi_company"/>
|
||||
<p class="oe_grey">
|
||||
To remind customers of paying their invoices, you can
|
||||
define different actions depending on how severely
|
||||
overdue the customer is. These actions are bundled
|
||||
into follow-up levels that are triggered when the due
|
||||
date of an invoice has passed a certain
|
||||
number of days. If there are other overdue invoices for the
|
||||
same customer, the actions of the most
|
||||
overdue invoice will be executed.
|
||||
</p>
|
||||
<field name="followup_line_ids"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<!-- FollowUp View Tree -->
|
||||
<record id="account_followup_view_tree" model="ir.ui.view">
|
||||
<field name="name">account.followup.view.tree</field>
|
||||
<field name="model">account.followup</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Follow-up">
|
||||
<field name="company_id" />
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
<!-- FollowUp Search View -->
|
||||
<record id="account_followup_view_search" model="ir.ui.view">
|
||||
<field name="name">account.followup.view.search</field>
|
||||
<field name="model">account.followup</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Follow-up">
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<!-- FollowUp Kanban View -->
|
||||
<record id="account_followup_view_kanban" model="ir.ui.view">
|
||||
<field name="name">account.followup.view.kanban</field>
|
||||
<field name="model">account.followup</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban>
|
||||
<field name="name"/>
|
||||
<templates>
|
||||
<t t-name="card">
|
||||
<div t-attf-class="oe_kanban_global_click">
|
||||
<div>
|
||||
<strong><i class="fa fa-building" role="img" aria-label="Enterprise"/> <t t-esc="record.name.value"/></strong>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
<!-- Action FollowUp -->
|
||||
<record id="action_account_followup_definition_form" model="ir.actions.act_window">
|
||||
<field name="name">Payment Follow-ups</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">account.followup</field>
|
||||
<field name="search_view_id" ref="account_followup_view_search"/>
|
||||
<field name="view_mode">list,kanban,form</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Define follow-up levels and their related actions
|
||||
</p>
|
||||
<p>
|
||||
For each step, specify the actions to be taken and delay in days. It is
|
||||
possible to use print and e-mail templates to send specific messages to
|
||||
the customer.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
<!-- FollowUp MenuItem -->
|
||||
<menuitem action="action_account_followup_definition_form" id="account_followup_menu"
|
||||
parent="account.account_account_menu" name="Follow-up Levels"
|
||||
groups="base_accounting_kit.group_account_chief" sequence="2"/>
|
||||
</odoo>
|
||||
51
addons/base_accounting_kit/views/account_group.xml
Normal file
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<!-- Account Group Form View -->
|
||||
<record id="account_group_view_form" model="ir.ui.view">
|
||||
<field name="name">account.group.view.form</field>
|
||||
<field name="model">account.group</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Account Groups">
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1>
|
||||
<field name="name"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="code_prefix_start"/>
|
||||
<field name="code_prefix_end"/>
|
||||
<field name="company_id"/>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
<chatter/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<!-- Account Group Tree View -->
|
||||
<record id="account_group_view_tree" model="ir.ui.view">
|
||||
<field name="name">account.group.view.tree</field>
|
||||
<field name="model">account.group</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Account Groups">
|
||||
<field name="name"/>
|
||||
<field name="code_prefix_start"/>
|
||||
<field name="code_prefix_end"/>
|
||||
<field name="company_id"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
<!--Account Groups Action-->
|
||||
<record id="action_account_group" model="ir.actions.act_window">
|
||||
<field name="name">Account Groups</field>
|
||||
<field name="res_model">account.group</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
</record>
|
||||
<!--Account Groups MenuItem-->
|
||||
<menuitem id="menu_account_group" name="Account Groups"
|
||||
action="action_account_group" sequence="10"
|
||||
parent="account.account_account_menu"/>
|
||||
</odoo>
|
||||
28
addons/base_accounting_kit/views/account_journal_views.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<!--Inheriting Account Journal Kanban View-->
|
||||
<record id="account_journal_dashboard_kanban_view" model="ir.ui.view">
|
||||
<field name="name">account.journal.view.kanban.inherit.base.accounting.kit</field>
|
||||
<field name="model">account.journal</field>
|
||||
<field name="inherit_id" ref="account.account_journal_dashboard_kanban_view" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath
|
||||
expr="//kanban/templates//div[@id='dashboard_bank_cash_left']"
|
||||
position="inside">
|
||||
<!-- <t t-if="dashboard.number_to_reconcile > 0">-->
|
||||
<!-- <button class="btn btn-primary"-->
|
||||
<!-- type="object"-->
|
||||
<!-- name="action_open_reconcile">-->
|
||||
<!-- <t t-esc="dashboard.number_to_reconcile"/> to Reconcile-->
|
||||
<!-- </button>-->
|
||||
<!-- </t>-->
|
||||
<t t-if="journal_type == 'bank'">
|
||||
<a name="action_import_wizard" type="object"
|
||||
class="oe_inline"
|
||||
groups="account.group_account_invoice">Import
|
||||
</a>
|
||||
</t>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||