mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-28 13:28:35 +00:00
Compare commits
1005 Commits
v12.13.0
...
v12-pre-re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ad2684121 | ||
|
|
1413af8f3a | ||
|
|
9a1afeb13b | ||
|
|
13a9b304b7 | ||
|
|
ee7ac93ff3 | ||
|
|
3aa4411a0d | ||
|
|
7db947550b | ||
|
|
c74a6f1827 | ||
|
|
73243c8ac3 | ||
|
|
1800ce2780 | ||
|
|
e5ea16a6dd | ||
|
|
54cb62c7bf | ||
|
|
11ad77f491 | ||
|
|
95b0c743d4 | ||
|
|
fee83e8ba4 | ||
|
|
4c5b5607ba | ||
|
|
00b40c0f3c | ||
|
|
ac08379f2b | ||
|
|
753adf3ce4 | ||
|
|
fbbf29e829 | ||
|
|
ce2aa767b2 | ||
|
|
c9927efcae | ||
|
|
e9dbb46a06 | ||
|
|
39125a78e0 | ||
|
|
2a00164059 | ||
|
|
7d1953bb3b | ||
|
|
33eeb64fec | ||
|
|
bbffb5d91e | ||
|
|
260b9c1885 | ||
|
|
034e8bd028 | ||
|
|
eb8b03f590 | ||
|
|
abb4d99ca8 | ||
|
|
a6fd5a69e8 | ||
|
|
849c795113 | ||
|
|
3647a24f60 | ||
|
|
67f17c7a0c | ||
|
|
24c004b537 | ||
|
|
1fce25180c | ||
|
|
23946cea2e | ||
|
|
1b78c501a1 | ||
|
|
d09ed0a578 | ||
|
|
9b83e3856a | ||
|
|
656686f2b1 | ||
|
|
8e7ee953c1 | ||
|
|
d2af2b31e5 | ||
|
|
f79651c54d | ||
|
|
80ba0cf978 | ||
|
|
30c1a7bb20 | ||
|
|
d119b4321d | ||
|
|
4b1befb691 | ||
|
|
fa5e018330 | ||
|
|
8c84d6a1d9 | ||
|
|
f76139dceb | ||
|
|
97544e2bbc | ||
|
|
117718d410 | ||
|
|
bcb0683be1 | ||
|
|
bf5f0a226e | ||
|
|
55aaefb53c | ||
|
|
52ed3c219b | ||
|
|
1e378cfde0 | ||
|
|
29f5d434d8 | ||
|
|
aa0e21e84b | ||
|
|
34bf2004bb | ||
|
|
7ac656d737 | ||
|
|
ba537b0b93 | ||
|
|
89b0e5023a | ||
|
|
a0e4708470 | ||
|
|
e60d7ac09e | ||
|
|
d6555e8632 | ||
|
|
14602178e7 | ||
|
|
5f2819c9b1 | ||
|
|
5bc7d63646 | ||
|
|
94162e0139 | ||
|
|
e2e751e26f | ||
|
|
70244cee89 | ||
|
|
ae6e69ebd4 | ||
|
|
6fdf4dd03e | ||
|
|
4aede0ea3f | ||
|
|
de660bf9c4 | ||
|
|
391bf86e0a | ||
|
|
452c613974 | ||
|
|
dd7b02ad7d | ||
|
|
7cc02bf861 | ||
|
|
678960209c | ||
|
|
e4995dc9ed | ||
|
|
7cf5dc7dab | ||
|
|
e676a09c18 | ||
|
|
6431243ce3 | ||
|
|
49f93b347c | ||
|
|
27e9e47ba8 | ||
|
|
c9f51d3cec | ||
|
|
9ae3f26dbf | ||
|
|
ad736f1789 | ||
|
|
16ece12516 | ||
|
|
c9e75d2ab5 | ||
|
|
985fdade7e | ||
|
|
10c34da174 | ||
|
|
d1480be596 | ||
|
|
93a744dc12 | ||
|
|
a2b98bb80c | ||
|
|
4fdff12242 | ||
|
|
9c9cde48ad | ||
|
|
999a3f1305 | ||
|
|
8139672c7a | ||
|
|
7169a4c113 | ||
|
|
bc960ab35f | ||
|
|
f9e9c5f637 | ||
|
|
fbc8fb36dd | ||
|
|
6359e69503 | ||
|
|
1d68d12b7a | ||
|
|
b7a74aa578 | ||
|
|
efddcbe42e | ||
|
|
6f6e390863 | ||
|
|
7843c3d51a | ||
|
|
8871bd4bfb | ||
|
|
d0a7141e35 | ||
|
|
6fe28e83e2 | ||
|
|
abf353a286 | ||
|
|
cef6a8434a | ||
|
|
dc76094a9f | ||
|
|
1e428f9531 | ||
|
|
710c1c1786 | ||
|
|
aa358b021e | ||
|
|
8bc37da20d | ||
|
|
914709099f | ||
|
|
87dddbe868 | ||
|
|
ca42b16d3a | ||
|
|
73666982c7 | ||
|
|
48bd1965e4 | ||
|
|
ab84579b0e | ||
|
|
8064792b8c | ||
|
|
fe6c96cab9 | ||
|
|
d5e89d98c2 | ||
|
|
f8fa3860d9 | ||
|
|
d8a7abcd02 | ||
|
|
5bb9de8614 | ||
|
|
0fd50e0426 | ||
|
|
bdfc300896 | ||
|
|
14292456cf | ||
|
|
24d67c35b7 | ||
|
|
8d71fcb948 | ||
|
|
2945e604aa | ||
|
|
ea085b3a76 | ||
|
|
611966c139 | ||
|
|
d650b55f52 | ||
|
|
8eca908365 | ||
|
|
09d5ddc42b | ||
|
|
abd53b114c | ||
|
|
b1e932a6f8 | ||
|
|
e6a3e6beb7 | ||
|
|
239974c73e | ||
|
|
27ecb54b8c | ||
|
|
6b4b80a4a4 | ||
|
|
68225bbcad | ||
|
|
b549287b94 | ||
|
|
12c3e5dfd6 | ||
|
|
c908add82e | ||
|
|
c74f0f3530 | ||
|
|
2e5a358e96 | ||
|
|
01f8833bd1 | ||
|
|
5e37949411 | ||
|
|
c5a0c22352 | ||
|
|
fc0459fba9 | ||
|
|
9a1caddacc | ||
|
|
b22cbb3122 | ||
|
|
f4a9e52cbe | ||
|
|
24e1786e49 | ||
|
|
5399891b25 | ||
|
|
1b8670b263 | ||
|
|
23db6a8e3a | ||
|
|
4c58fb40a8 | ||
|
|
df045a61ed | ||
|
|
6f7410073f | ||
|
|
0ecf8f5d66 | ||
|
|
dfc68950c1 | ||
|
|
ee7de6b107 | ||
|
|
b2a090f073 | ||
|
|
704f7b57b8 | ||
|
|
8070d76450 | ||
|
|
266563a99a | ||
|
|
7270b89133 | ||
|
|
57a68c317e | ||
|
|
fc44712976 | ||
|
|
a80d9d81c8 | ||
|
|
6a30025209 | ||
|
|
b9fb2349d6 | ||
|
|
2c30ad9465 | ||
|
|
67f80216fa | ||
|
|
2a8b3841bf | ||
|
|
83b6746cd1 | ||
|
|
94f0aae625 | ||
|
|
0c4f67b884 | ||
|
|
fe1d985432 | ||
|
|
57832fc9f8 | ||
|
|
56a21343f4 | ||
|
|
bda432303c | ||
|
|
371d124a0e | ||
|
|
3c5b33c241 | ||
|
|
85dca013ee | ||
|
|
d789dd3897 | ||
|
|
a1a3f674a2 | ||
|
|
7bb95f0a80 | ||
|
|
5c450cd13f | ||
|
|
1c3c2b3006 | ||
|
|
688a5ac048 | ||
|
|
21c15b89ab | ||
|
|
6a1ccf94a2 | ||
|
|
3302ed4658 | ||
|
|
fa3ca02557 | ||
|
|
1853f5da80 | ||
|
|
dd82bbf78d | ||
|
|
5271ce36de | ||
|
|
88bab1e3ad | ||
|
|
6ec804d77f | ||
|
|
7fa3132a66 | ||
|
|
3d3ee778ed | ||
|
|
65b7b57c70 | ||
|
|
4d9c9db295 | ||
|
|
13927e35cf | ||
|
|
18913756eb | ||
|
|
ccbdf25d44 | ||
|
|
ad1f1e000b | ||
|
|
5cced71ce4 | ||
|
|
aea8773503 | ||
|
|
df51aa3087 | ||
|
|
2ded94c338 | ||
|
|
0b45d5e1ec | ||
|
|
5946c2f28e | ||
|
|
cb2f2d9e73 | ||
|
|
39bbc7a245 | ||
|
|
062520f1b9 | ||
|
|
f644f6feba | ||
|
|
9de5fe2105 | ||
|
|
e163cb89bf | ||
|
|
9d38a0eede | ||
|
|
c0b66b8203 | ||
|
|
96c099c609 | ||
|
|
b5be828f3a | ||
|
|
059b8b567e | ||
|
|
cfdaf7c4aa | ||
|
|
5b80679ede | ||
|
|
a0783de232 | ||
|
|
98d90f4e36 | ||
|
|
c843da04a4 | ||
|
|
34f1d3a88e | ||
|
|
bd4461b8cb | ||
|
|
06063f7aa6 | ||
|
|
472c9e534a | ||
|
|
54c9704fe2 | ||
|
|
673dcb855d | ||
|
|
e3ebe4b33b | ||
|
|
baefc6fd26 | ||
|
|
b95b4e8064 | ||
|
|
08399f9015 | ||
|
|
4481d4e965 | ||
|
|
3f459c500f | ||
|
|
e787d05f66 | ||
|
|
4eab67347f | ||
|
|
cfe20cf610 | ||
|
|
73a7bb88fb | ||
|
|
d2d025b586 | ||
|
|
061c82a5cf | ||
|
|
e6eab3b5f8 | ||
|
|
16c3a76c49 | ||
|
|
dd78ffb176 | ||
|
|
583a8bf580 | ||
|
|
2c63f833b6 | ||
|
|
d5d7ef7349 | ||
|
|
ad2d74576b | ||
|
|
4ced989169 | ||
|
|
a59523970b | ||
|
|
9f2b5ba565 | ||
|
|
878b10eff5 | ||
|
|
83df26197b | ||
|
|
fb87ed3f62 | ||
|
|
2541ab178a | ||
|
|
ac58e05bbb | ||
|
|
34b6f9389c | ||
|
|
6678450ba0 | ||
|
|
fa98c4b8d7 | ||
|
|
79c7be770c | ||
|
|
8acc3fbbf4 | ||
|
|
2e04026fd6 | ||
|
|
3707b004c4 | ||
|
|
f8dcd06dad | ||
|
|
15af590482 | ||
|
|
9cc129c400 | ||
|
|
35c4a7a05d | ||
|
|
883598890d | ||
|
|
2479e070c4 | ||
|
|
8d31d21b88 | ||
|
|
53a32fecdb | ||
|
|
b904304268 | ||
|
|
bd742d7ad9 | ||
|
|
2a1365a278 | ||
|
|
56531908aa | ||
|
|
f2bede6275 | ||
|
|
a52ea48a21 | ||
|
|
5bd58ef85c | ||
|
|
a0cfe449df | ||
|
|
19193e8f1c | ||
|
|
ebd230e748 | ||
|
|
bf91ee9b99 | ||
|
|
bb3c44dbb3 | ||
|
|
7a61696a25 | ||
|
|
28432128be | ||
|
|
b135569ca6 | ||
|
|
ce3a680324 | ||
|
|
786d78bce2 | ||
|
|
9e3e09f701 | ||
|
|
c2e2cf4933 | ||
|
|
ea50ab8696 | ||
|
|
c22785877d | ||
|
|
14eeee5592 | ||
|
|
4f4f60942c | ||
|
|
437c771701 | ||
|
|
f29cdb39a1 | ||
|
|
063ab520d3 | ||
|
|
b2ae422ed7 | ||
|
|
6da7f65848 | ||
|
|
4c6d068a73 | ||
|
|
ef09599a9f | ||
|
|
a1104f040d | ||
|
|
c6a1e3b450 | ||
|
|
238df8b3ec | ||
|
|
4db8d13900 | ||
|
|
6cf37d58f1 | ||
|
|
386b7fd2c3 | ||
|
|
f9f10ed743 | ||
|
|
2e2f3f9578 | ||
|
|
55d45532b4 | ||
|
|
07e7f73488 | ||
|
|
017d168b03 | ||
|
|
a9d48e5fbf | ||
|
|
5ae40871d8 | ||
|
|
86a163fce5 | ||
|
|
266f24bb74 | ||
|
|
aa90dc6aa3 | ||
|
|
5eb8eb8aa7 | ||
|
|
a69b8aa18d | ||
|
|
4450a075bd | ||
|
|
3aede7da61 | ||
|
|
2e8b06cf9f | ||
|
|
27f120bc7c | ||
|
|
5feb20d94b | ||
|
|
27fbfbf34e | ||
|
|
d389261230 | ||
|
|
e9f8e5814a | ||
|
|
16193a614e | ||
|
|
2b2e205b74 | ||
|
|
9272ea838f | ||
|
|
80d9576bb9 | ||
|
|
c18a8c58dd | ||
|
|
473fb55724 | ||
|
|
05a3c5a6bc | ||
|
|
c39e206fc7 | ||
|
|
33539d49f1 | ||
|
|
91a929df84 | ||
|
|
19a27d3f46 | ||
|
|
d5fa3a2b8f | ||
|
|
e34f73c60b | ||
|
|
a49ea67f16 | ||
|
|
d7bf38842b | ||
|
|
4f0c5e2cc7 | ||
|
|
be65ae7c86 | ||
|
|
b0e53230bd | ||
|
|
f452ab56c6 | ||
|
|
1eb3d16667 | ||
|
|
c1c8bb6d98 | ||
|
|
211a4cb833 | ||
|
|
472bad352d | ||
|
|
eea1ec42f4 | ||
|
|
b96c7e0a0f | ||
|
|
37604c8d99 | ||
|
|
edfbb5d3be | ||
|
|
ddd85334a3 | ||
|
|
81ae1f363c | ||
|
|
2f20ef0f4c | ||
|
|
e8935457a0 | ||
|
|
64ec1728ab | ||
|
|
dbb6721412 | ||
|
|
afbbdcb802 | ||
|
|
37adbe5a85 | ||
|
|
d7a5853587 | ||
|
|
6b799dbef0 | ||
|
|
be8ecdf3d6 | ||
|
|
85f1561e5e | ||
|
|
2bf8610698 | ||
|
|
23364b374e | ||
|
|
82c162dfe7 | ||
|
|
7fea8a95e6 | ||
|
|
85a2d27736 | ||
|
|
106e0f885a | ||
|
|
c37ce8b8d3 | ||
|
|
5ac5d86d84 | ||
|
|
a180d2baa8 | ||
|
|
14717eeac6 | ||
|
|
179d82615f | ||
|
|
1618c92142 | ||
|
|
1118949867 | ||
|
|
31cd9dcee1 | ||
|
|
e42e75178a | ||
|
|
078e154966 | ||
|
|
3365f01963 | ||
|
|
469827da85 | ||
|
|
d3240f9510 | ||
|
|
2341f47631 | ||
|
|
b57a523dfe | ||
|
|
8d101d362d | ||
|
|
a9cfe1cfa4 | ||
|
|
dccd2ca69f | ||
|
|
314e151f1d | ||
|
|
c002465903 | ||
|
|
859a5f4dbe | ||
|
|
58d7fb5c94 | ||
|
|
744a7b1bad | ||
|
|
d9719fe41d | ||
|
|
50c1d24ee9 | ||
|
|
ea221f7e63 | ||
|
|
3799c1a662 | ||
|
|
56a8b90598 | ||
|
|
044ad91481 | ||
|
|
39a5e5abd1 | ||
|
|
407b351d55 | ||
|
|
6f64ec3574 | ||
|
|
e17ad9e620 | ||
|
|
505b69f6b2 | ||
|
|
485ef74c58 | ||
|
|
5cf9fbc27b | ||
|
|
4ef49eab66 | ||
|
|
f6d61f2dec | ||
|
|
7aaf3a71e8 | ||
|
|
c51ac1c68b | ||
|
|
04b106a515 | ||
|
|
e79f3e67ed | ||
|
|
f8d24b803c | ||
|
|
c516fc7ca4 | ||
|
|
c9b8423007 | ||
|
|
42675929fa | ||
|
|
a8de2321a5 | ||
|
|
748f8145b6 | ||
|
|
7a40a2b2e2 | ||
|
|
35c2d2324d | ||
|
|
f2b5f3a842 | ||
|
|
d2442da6f2 | ||
|
|
32bdb691df | ||
|
|
5050541401 | ||
|
|
87f0ed6477 | ||
|
|
6df18856db | ||
|
|
288b384ab5 | ||
|
|
cffcbf7c47 | ||
|
|
c69fda4440 | ||
|
|
507ec2184c | ||
|
|
75cfbe132b | ||
|
|
f88e908598 | ||
|
|
8791d11359 | ||
|
|
bbec5ccc83 | ||
|
|
8862166223 | ||
|
|
2eea0f003f | ||
|
|
a250d415aa | ||
|
|
4d83564188 | ||
|
|
7e01cacf8c | ||
|
|
830626d86a | ||
|
|
78e413aaaa | ||
|
|
f9da40e9af | ||
|
|
e6a8e1a23b | ||
|
|
e0e5783b3e | ||
|
|
0abed806a3 | ||
|
|
79e3700499 | ||
|
|
e898ce1f20 | ||
|
|
3ddebae3fb | ||
|
|
d5abab4208 | ||
|
|
00a40c95ea | ||
|
|
750da2f946 | ||
|
|
ff524e676f | ||
|
|
d8fc6ec471 | ||
|
|
4e12c70897 | ||
|
|
7e97138beb | ||
|
|
00865b9af3 | ||
|
|
7d1a431d9c | ||
|
|
fe9e2ec008 | ||
|
|
ca4c12b44e | ||
|
|
cdd950dee5 | ||
|
|
21b17cf407 | ||
|
|
6324cc238e | ||
|
|
cdc0b8e435 | ||
|
|
7049cca855 | ||
|
|
ba95d32876 | ||
|
|
bf1e0139e2 | ||
|
|
bf490c3a27 | ||
|
|
40b37f09fd | ||
|
|
04e0b35154 | ||
|
|
82b4749166 | ||
|
|
ef06f3288f | ||
|
|
945c0e86de | ||
|
|
2cae3d7b38 | ||
|
|
03933f8461 | ||
|
|
ba398bc779 | ||
|
|
022a46fdcd | ||
|
|
d092b6c25a | ||
|
|
5add3ea210 | ||
|
|
909346f222 | ||
|
|
d9a0e0c708 | ||
|
|
839edaf613 | ||
|
|
11f2da72b1 | ||
|
|
61298c3489 | ||
|
|
2cf2697e74 | ||
|
|
1b0870a440 | ||
|
|
5c107b99bd | ||
|
|
edec82bb9e | ||
|
|
45dbfaa7f9 | ||
|
|
7d666ccfb4 | ||
|
|
246ef881a0 | ||
|
|
5f3635fbaf | ||
|
|
ec59c6cebc | ||
|
|
be58194678 | ||
|
|
00306d8fd0 | ||
|
|
da83ec0f0d | ||
|
|
709d50ad21 | ||
|
|
7f69820f04 | ||
|
|
a0c8106342 | ||
|
|
8c8ab4140e | ||
|
|
2274162107 | ||
|
|
6f2a34f6d4 | ||
|
|
e512118320 | ||
|
|
aa2a7abe4e | ||
|
|
79ff4d0e8f | ||
|
|
37a64b6485 | ||
|
|
9f848fe65c | ||
|
|
65680ada87 | ||
|
|
a8cda3f013 | ||
|
|
c51b4d0416 | ||
|
|
67cfb6c370 | ||
|
|
54e9063215 | ||
|
|
554ce04e0d | ||
|
|
08f082089e | ||
|
|
d22279ba38 | ||
|
|
d01cae95d9 | ||
|
|
b510d20805 | ||
|
|
b1054a3ddc | ||
|
|
f454ab4ce2 | ||
|
|
5c68fe4d86 | ||
|
|
68e476c7fe | ||
|
|
8f5425d99b | ||
|
|
6af53a645a | ||
|
|
b9cbdeaa95 | ||
|
|
0ce03cc36d | ||
|
|
4ee433a51c | ||
|
|
e33179d130 | ||
|
|
53bcf704b3 | ||
|
|
f4f05dd5ad | ||
|
|
5b554a5e20 | ||
|
|
79ac9dc486 | ||
|
|
f0d591351b | ||
|
|
c6143c4542 | ||
|
|
5a56b63eed | ||
|
|
5d92e2481d | ||
|
|
ac603a895d | ||
|
|
9f0d567ea5 | ||
|
|
4ea03c23a4 | ||
|
|
209aa447f7 | ||
|
|
eabaa16f13 | ||
|
|
27b68be854 | ||
|
|
85cc1d68f0 | ||
|
|
61e6760a6b | ||
|
|
16e879bc1a | ||
|
|
8866801932 | ||
|
|
4965af2321 | ||
|
|
fb65644026 | ||
|
|
2e1f56e117 | ||
|
|
3b62e05a5c | ||
|
|
988fd97625 | ||
|
|
628dcb1ff9 | ||
|
|
e6cd9ccea0 | ||
|
|
ac2fc8470b | ||
|
|
6dcf334199 | ||
|
|
5822cfb454 | ||
|
|
a15c02ec71 | ||
|
|
0938db8730 | ||
|
|
537a236dc9 | ||
|
|
68d2a1f02b | ||
|
|
4dad6742e1 | ||
|
|
44877d5a60 | ||
|
|
05589e9651 | ||
|
|
9958639a9a | ||
|
|
df1686611b | ||
|
|
1041aef097 | ||
|
|
0e00bff182 | ||
|
|
160585321d | ||
|
|
965bc9bb80 | ||
|
|
bfb68e4e80 | ||
|
|
3d7f833899 | ||
|
|
1866965508 | ||
|
|
a874587156 | ||
|
|
e7ea94ae6f | ||
|
|
7015609f7f | ||
|
|
53b05c2850 | ||
|
|
e14bb5e978 | ||
|
|
4db3ff1673 | ||
|
|
03cd4135d2 | ||
|
|
e984632221 | ||
|
|
65289334e3 | ||
|
|
2e38f73732 | ||
|
|
52b983eb8f | ||
|
|
2e16dcc8fa | ||
|
|
4e12466bef | ||
|
|
e0ea4265a8 | ||
|
|
233cad9e02 | ||
|
|
718e517144 | ||
|
|
4f3f935a7a | ||
|
|
94db0bba44 | ||
|
|
acc79f739b | ||
|
|
bdf4bfe98f | ||
|
|
9e5388ac53 | ||
|
|
a6666dbf97 | ||
|
|
4b969a5b9b | ||
|
|
b6faadab8d | ||
|
|
38f06a1a26 | ||
|
|
b54df979c4 | ||
|
|
1b969ff140 | ||
|
|
bf2b4bc6f7 | ||
|
|
9d805272be | ||
|
|
d4255d77f4 | ||
|
|
13467e42f3 | ||
|
|
1679b0adbb | ||
|
|
e7a129dde8 | ||
|
|
b12d08a85b | ||
|
|
fe054511c8 | ||
|
|
c3e0ff93d7 | ||
|
|
4d15506791 | ||
|
|
a367cc5c99 | ||
|
|
9599420878 | ||
|
|
c9b6db27cf | ||
|
|
d72d2367ab | ||
|
|
d9e4ebc00c | ||
|
|
a4bd8ba6f9 | ||
|
|
6f5a9c8049 | ||
|
|
48449e6d0a | ||
|
|
31596228ff | ||
|
|
017543d59d | ||
|
|
cca0eaaa29 | ||
|
|
b52a9283bb | ||
|
|
39dabe7381 | ||
|
|
1c213a3dbb | ||
|
|
e7eb9d4e20 | ||
|
|
d94a97b6ad | ||
|
|
d819330443 | ||
|
|
eb88b63ddd | ||
|
|
86bbb3e9a0 | ||
|
|
b9d62d7f8e | ||
|
|
44dd9f6fe5 | ||
|
|
a683b14c64 | ||
|
|
becae59af0 | ||
|
|
e6d300e35c | ||
|
|
c2a41371d2 | ||
|
|
6d3fc0d020 | ||
|
|
81cbd0dc50 | ||
|
|
1bbda81c9f | ||
|
|
21461823f1 | ||
|
|
e9b580aba6 | ||
|
|
7f794e5bb6 | ||
|
|
6db07c0278 | ||
|
|
c28379075a | ||
|
|
95c3258ee4 | ||
|
|
00e095278d | ||
|
|
53d98ff635 | ||
|
|
64f4f86948 | ||
|
|
983df4463a | ||
|
|
b9c71e59e0 | ||
|
|
8427dd2ca8 | ||
|
|
8b43faa203 | ||
|
|
5ee6417228 | ||
|
|
71349d5a49 | ||
|
|
105a9fdff1 | ||
|
|
bd0cb9d2ed | ||
|
|
ffbb767072 | ||
|
|
723e4f2a22 | ||
|
|
a738e8ca87 | ||
|
|
b0b36d2caa | ||
|
|
98d8718979 | ||
|
|
0de091a749 | ||
|
|
2a92d1b94a | ||
|
|
af60e85400 | ||
|
|
45d8204e1e | ||
|
|
56d5b68046 | ||
|
|
dafbb80ec5 | ||
|
|
f226927b6a | ||
|
|
8cfde675ac | ||
|
|
599d7a686e | ||
|
|
2d073016ee | ||
|
|
12f95c41b9 | ||
|
|
7fb22fd652 | ||
|
|
284a19c99d | ||
|
|
1a401b1427 | ||
|
|
b2db8031d5 | ||
|
|
58fae20ff9 | ||
|
|
2cf9cc02e1 | ||
|
|
9b893643c0 | ||
|
|
126de4d0e5 | ||
|
|
18635e4dd1 | ||
|
|
4b75d528c9 | ||
|
|
5220e9201b | ||
|
|
ee7bcc0350 | ||
|
|
1f2ec25493 | ||
|
|
e46a72b101 | ||
|
|
b4dfba54b6 | ||
|
|
9daf4418ea | ||
|
|
695e551972 | ||
|
|
e6a07d5508 | ||
|
|
d67affdfae | ||
|
|
cde452add7 | ||
|
|
5c74cdd0d2 | ||
|
|
58b1e3b866 | ||
|
|
b16d625b4f | ||
|
|
96f0b0e30a | ||
|
|
390cac2160 | ||
|
|
64e9275c4a | ||
|
|
42b028a3ba | ||
|
|
5327637b37 | ||
|
|
5d6849ac7f | ||
|
|
57e6759088 | ||
|
|
9760eb9d57 | ||
|
|
ee0b08ae9c | ||
|
|
22e770353f | ||
|
|
bbce16e00c | ||
|
|
2bd620fac8 | ||
|
|
3dbf88ba6f | ||
|
|
1cd57a324c | ||
|
|
fe56b800a6 | ||
|
|
ccdfbd4f40 | ||
|
|
f7f809aae8 | ||
|
|
3f399ee806 | ||
|
|
7bbe383232 | ||
|
|
1fcd1e4dc0 | ||
|
|
800a9ae7c3 | ||
|
|
a63b5c1a9e | ||
|
|
74bcb2977f | ||
|
|
96946fa4a0 | ||
|
|
e2fb0c567d | ||
|
|
e8f4eb02be | ||
|
|
5a1d912e6d | ||
|
|
e152f9a89a | ||
|
|
6a718a3fc6 | ||
|
|
5062b78718 | ||
|
|
eed12444e1 | ||
|
|
326fccfa00 | ||
|
|
e7476914cd | ||
|
|
b1507fdaae | ||
|
|
f76b0dea44 | ||
|
|
ede082657f | ||
|
|
d0303b3fb1 | ||
|
|
91a3ab8e32 | ||
|
|
cfb6e3aa30 | ||
|
|
4fe48a30c4 | ||
|
|
af29bde0e8 | ||
|
|
b83e131b1f | ||
|
|
51015178da | ||
|
|
e39dd079c4 | ||
|
|
7459dc4105 | ||
|
|
0796d6b5d0 | ||
|
|
5b08241dc6 | ||
|
|
5318a159a9 | ||
|
|
26723d10af | ||
|
|
3ec6bc1838 | ||
|
|
29f92e0405 | ||
|
|
2c9f33a916 | ||
|
|
b09da9bcfc | ||
|
|
771a7276c9 | ||
|
|
2c6c1aa97a | ||
|
|
482996f8d1 | ||
|
|
785579e67b | ||
|
|
74224d90bf | ||
|
|
e5d4b4c1ed | ||
|
|
39fa5e3266 | ||
|
|
f940a3494f | ||
|
|
f08c2a5cef | ||
|
|
d8b4044ee1 | ||
|
|
78e737de5a | ||
|
|
02feab9f3c | ||
|
|
3ef60dbfdb | ||
|
|
75a54361d7 | ||
|
|
42b24cc9eb | ||
|
|
ca05945e92 | ||
|
|
a69b9f95cc | ||
|
|
f9e62b74d0 | ||
|
|
2b3a20ed5b | ||
|
|
3252d9d943 | ||
|
|
9655edabd9 | ||
|
|
2704327029 | ||
|
|
3d84d324d7 | ||
|
|
3e526b8ca8 | ||
|
|
47bdbf1e78 | ||
|
|
0cb6ce3a89 | ||
|
|
5597737dca | ||
|
|
77b9fa0d06 | ||
|
|
224006aebd | ||
|
|
c630be6781 | ||
|
|
51a153d8c2 | ||
|
|
707b630851 | ||
|
|
fdacb5643b | ||
|
|
ec89a65859 | ||
|
|
1e4a6e845b | ||
|
|
0433fa58f3 | ||
|
|
5c72ad2498 | ||
|
|
c976d080ea | ||
|
|
fac940042b | ||
|
|
3e36d774d2 | ||
|
|
507c46d9a4 | ||
|
|
36706b7cb0 | ||
|
|
18471131cc | ||
|
|
d920f9da59 | ||
|
|
f041af53db | ||
|
|
5111882162 | ||
|
|
e37604baeb | ||
|
|
0e91805719 | ||
|
|
3377e74fd0 | ||
|
|
d87094ebb2 | ||
|
|
c7d68a7f41 | ||
|
|
3bbed22f04 | ||
|
|
aab91990e5 | ||
|
|
2e23050338 | ||
|
|
5ea276d29d | ||
|
|
7e18df31bf | ||
|
|
2c29933118 | ||
|
|
9525bffcd9 | ||
|
|
6ba0b69469 | ||
|
|
9f93a4069a | ||
|
|
a73c662a38 | ||
|
|
d3074c32fd | ||
|
|
fe56015cbf | ||
|
|
789f1007b9 | ||
|
|
f202a49d80 | ||
|
|
8b7d4a3f2e | ||
|
|
b9efb4577a | ||
|
|
b5217ee9d7 | ||
|
|
e8610014de | ||
|
|
780982dcc7 | ||
|
|
26b802d44a | ||
|
|
519f5b5411 | ||
|
|
aebeb573e4 | ||
|
|
e1b96d70db | ||
|
|
31e2c80e33 | ||
|
|
09e1b5314b | ||
|
|
40ba013636 | ||
|
|
16427c5cc8 | ||
|
|
7365566894 | ||
|
|
6b86586eb1 | ||
|
|
1ff8ae494d | ||
|
|
02f856e968 | ||
|
|
38e681e8b2 | ||
|
|
ecffa80216 | ||
|
|
cee0706a1f | ||
|
|
bd49da45e3 | ||
|
|
04458e7413 | ||
|
|
f5e4f75fd0 | ||
|
|
80f5734305 | ||
|
|
c5096e0441 | ||
|
|
20b5b30e34 | ||
|
|
d196f14e85 | ||
|
|
6beee63d06 | ||
|
|
c82c955bc2 | ||
|
|
dc4b0921f6 | ||
|
|
72ed0fbbb4 | ||
|
|
020e6e980a | ||
|
|
70e0711893 | ||
|
|
8693ac81a2 | ||
|
|
56116f28af | ||
|
|
eeb99d1b98 | ||
|
|
5d23c0ce8e | ||
|
|
fb6534cb65 | ||
|
|
9876fa8360 | ||
|
|
20bd91f0d9 | ||
|
|
06b6027674 | ||
|
|
91580258d7 | ||
|
|
51ddf376da | ||
|
|
db4fec0314 | ||
|
|
6d63a1e90a | ||
|
|
589a49a38a | ||
|
|
464fc1f087 | ||
|
|
e3c2f0a221 | ||
|
|
77b8ced4a4 | ||
|
|
fb9dde38f6 | ||
|
|
ca944316b4 | ||
|
|
6d5a8aea9a | ||
|
|
eb9f216b40 | ||
|
|
63bfa30bc1 | ||
|
|
778141c569 | ||
|
|
b7ee2a44a0 | ||
|
|
3f9390c077 | ||
|
|
495ac7ba6b | ||
|
|
ad19268b4c | ||
|
|
54405a4b64 | ||
|
|
86f87e9b53 | ||
|
|
049836dff8 | ||
|
|
c313f05a8e | ||
|
|
b77375c72b | ||
|
|
6c064a1f95 | ||
|
|
9182c2e09d | ||
|
|
271f35fef5 | ||
|
|
37e4c91ec2 | ||
|
|
12d9bc3787 | ||
|
|
5eb430fe51 | ||
|
|
31ee8b4011 | ||
|
|
9363725342 | ||
|
|
e1e3dbcae2 | ||
|
|
8616684c24 | ||
|
|
90a57c1f8c | ||
|
|
ba1cd74189 | ||
|
|
c3719b46b2 | ||
|
|
e50804fb9d | ||
|
|
cc704044bd | ||
|
|
e6646efef3 | ||
|
|
57dd4689e0 | ||
|
|
08b4736ee0 | ||
|
|
662a0fcfe9 | ||
|
|
d6f3be0fc1 | ||
|
|
154c433a86 | ||
|
|
d3a6b9ed81 | ||
|
|
9c00b22703 | ||
|
|
b3e0f482ac | ||
|
|
61824d3735 | ||
|
|
ada41295bb | ||
|
|
08e89b88f4 | ||
|
|
cc43805f25 | ||
|
|
ec28262988 | ||
|
|
22754a9c58 | ||
|
|
6a9af076e5 | ||
|
|
816235e99c | ||
|
|
dfbdd0b2ee | ||
|
|
59a757b3e3 | ||
|
|
389610fb45 | ||
|
|
175b26e6e8 | ||
|
|
cea1a15dcd | ||
|
|
7a40ad340f | ||
|
|
db13eb56c4 | ||
|
|
cf2ebc0d6f | ||
|
|
9003718481 | ||
|
|
18666b6a74 | ||
|
|
395ecf2392 | ||
|
|
221e5993a8 | ||
|
|
8eda5aca09 | ||
|
|
bdfd30760a | ||
|
|
418818f0ec | ||
|
|
d971917ecf | ||
|
|
dda44bb1f5 | ||
|
|
448ab6e3df | ||
|
|
cc3e0bf806 | ||
|
|
93b3c2ce06 | ||
|
|
7c427aa890 | ||
|
|
b6509ead64 | ||
|
|
f8346ce62f | ||
|
|
e73a9deb42 | ||
|
|
5669a5f618 | ||
|
|
18afbc0617 | ||
|
|
f396b35622 | ||
|
|
3960c49fc6 | ||
|
|
7e6c79819a | ||
|
|
533954cd12 | ||
|
|
7ee2b0ed3a | ||
|
|
8e37316832 | ||
|
|
2860f62772 | ||
|
|
4f008f59fc | ||
|
|
1f10da4d66 | ||
|
|
7036635007 | ||
|
|
c1719ef54b | ||
|
|
c8201eba33 | ||
|
|
b2bd8ef144 | ||
|
|
e523cfbb02 | ||
|
|
d8705240a0 | ||
|
|
ab1415c0cc | ||
|
|
2f12eed44d | ||
|
|
c9e93fd9b1 | ||
|
|
82db751a52 | ||
|
|
2d936fe5ca | ||
|
|
d0541d77b3 | ||
|
|
5f148d3d3a | ||
|
|
6c894f8f41 | ||
|
|
5effbeef49 | ||
|
|
1fa8dcb15b | ||
|
|
acd5929ac3 | ||
|
|
0c511b31fd | ||
|
|
17a8874c50 | ||
|
|
439c4e11bf | ||
|
|
a76b1c530d | ||
|
|
aef8010bc1 | ||
|
|
e9b7b69435 | ||
|
|
eb6fb6fffa | ||
|
|
ab5d596959 | ||
|
|
203fc2b940 | ||
|
|
8e7a755aeb | ||
|
|
334c282f7b | ||
|
|
3ae9bbc0a7 | ||
|
|
8b8457c5d3 | ||
|
|
eabe1af280 | ||
|
|
7bbdbaa2fa | ||
|
|
28a5169646 | ||
|
|
c7ec475429 | ||
|
|
433faa705e | ||
|
|
2b4da0d518 | ||
|
|
8d7e26c7dd | ||
|
|
6ff846f9b0 | ||
|
|
f956a2cf72 | ||
|
|
1fb3a29cf9 | ||
|
|
4b8d4a1cbe | ||
|
|
90eb489392 |
32
.flake8
Normal file
32
.flake8
Normal file
@@ -0,0 +1,32 @@
|
||||
[flake8]
|
||||
ignore =
|
||||
E121,
|
||||
E126,
|
||||
E127,
|
||||
E128,
|
||||
E203,
|
||||
E225,
|
||||
E226,
|
||||
E231,
|
||||
E241,
|
||||
E251,
|
||||
E261,
|
||||
E265,
|
||||
E302,
|
||||
E303,
|
||||
E305,
|
||||
E402,
|
||||
E501,
|
||||
E741,
|
||||
W291,
|
||||
W292,
|
||||
W293,
|
||||
W391,
|
||||
W503,
|
||||
W504,
|
||||
F403,
|
||||
B007,
|
||||
B950,
|
||||
W191,
|
||||
|
||||
max-line-length = 200
|
||||
26
.github/workflows/backport.yml
vendored
Normal file
26
.github/workflows/backport.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Backport
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- closed
|
||||
- labeled
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: Checkout Actions
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: "frappe/backport"
|
||||
path: ./actions
|
||||
ref: develop
|
||||
- name: Install Actions
|
||||
run: npm install --production --prefix ./actions
|
||||
- name: Run backport
|
||||
uses: ./actions/backport
|
||||
with:
|
||||
token: ${{secrets.BACKPORT_BOT_TOKEN}}
|
||||
labelsToAdd: "backport"
|
||||
title: "{{originalTitle}}"
|
||||
@@ -40,8 +40,7 @@ install:
|
||||
- cd ~
|
||||
- nvm install 10
|
||||
|
||||
- git clone https://github.com/frappe/bench --depth 1
|
||||
- pip install -e ./bench
|
||||
- pip install -U frappe-bench --only-binary='all'
|
||||
|
||||
- git clone https://github.com/frappe/frappe --branch $TRAVIS_BRANCH --depth 1
|
||||
- bench init --skip-assets --frappe-path ~/frappe --python $(which python) frappe-bench
|
||||
|
||||
0
FETCH_HEAD
Normal file
0
FETCH_HEAD
Normal file
@@ -5,7 +5,7 @@ import frappe
|
||||
from erpnext.hooks import regional_overrides
|
||||
from frappe.utils import getdate
|
||||
|
||||
__version__ = '12.13.0'
|
||||
__version__ = '12.29.0'
|
||||
|
||||
def get_default_company(user=None):
|
||||
'''Get default company for user'''
|
||||
|
||||
@@ -6,8 +6,8 @@ import frappe, json
|
||||
from frappe import _
|
||||
from frappe.utils import add_to_date, date_diff, getdate, nowdate, get_last_day, formatdate, get_link_to_form
|
||||
from erpnext.accounts.report.general_ledger.general_ledger import execute
|
||||
from frappe.core.page.dashboard.dashboard import cache_source, get_from_date_from_timespan
|
||||
from frappe.desk.doctype.dashboard_chart.dashboard_chart import get_period_ending
|
||||
from frappe.core.page.dashboard.dashboard import cache_source
|
||||
from frappe.utils.dateutils import get_from_date_from_timespan, get_period_ending
|
||||
|
||||
from frappe.utils.nestedset import get_descendants_of
|
||||
|
||||
|
||||
@@ -910,75 +910,8 @@
|
||||
},
|
||||
"is_group": 1
|
||||
},
|
||||
"Passiva": {
|
||||
"Passiva - Verbindlichkeiten": {
|
||||
"root_type": "Liability",
|
||||
"A - Eigenkapital": {
|
||||
"account_type": "Equity",
|
||||
"is_group": 1,
|
||||
"I - Gezeichnetes Kapital": {
|
||||
"account_type": "Equity",
|
||||
"is_group": 1
|
||||
},
|
||||
"II - Kapitalr\u00fccklage": {
|
||||
"account_type": "Equity",
|
||||
"is_group": 1
|
||||
},
|
||||
"III - Gewinnr\u00fccklagen": {
|
||||
"account_type": "Equity",
|
||||
"1 - gesetzliche R\u00fccklage": {
|
||||
"account_type": "Equity",
|
||||
"is_group": 1
|
||||
},
|
||||
"2 - R\u00fccklage f. Anteile an einem herrschenden oder mehrheitlich beteiligten Unternehmen": {
|
||||
"account_type": "Equity",
|
||||
"is_group": 1
|
||||
},
|
||||
"3 - satzungsm\u00e4\u00dfige R\u00fccklagen": {
|
||||
"account_type": "Equity",
|
||||
"is_group": 1
|
||||
},
|
||||
"4 - andere Gewinnr\u00fccklagen": {
|
||||
"account_type": "Equity",
|
||||
"is_group": 1,
|
||||
"Gewinnr\u00fccklagen aus den \u00dcbergangsvorschriften BilMoG": {
|
||||
"is_group": 1,
|
||||
"Gewinnr\u00fccklagen (BilMoG)": {
|
||||
"account_number": "2963"
|
||||
},
|
||||
"Gewinnr\u00fccklagen aus Zuschreibung Sachanlageverm\u00f6gen (BilMoG)": {
|
||||
"account_number": "2964"
|
||||
},
|
||||
"Gewinnr\u00fccklagen aus Zuschreibung Finanzanlageverm\u00f6gen (BilMoG)": {
|
||||
"account_number": "2965"
|
||||
},
|
||||
"Gewinnr\u00fccklagen aus Aufl\u00f6sung der Sonderposten mit R\u00fccklageanteil (BilMoG)": {
|
||||
"account_number": "2966"
|
||||
}
|
||||
},
|
||||
"Latente Steuern (Gewinnr\u00fccklage Haben) aus erfolgsneutralen Verrechnungen": {
|
||||
"account_number": "2967"
|
||||
},
|
||||
"Latente Steuern (Gewinnr\u00fccklage Soll) aus erfolgsneutralen Verrechnungen": {
|
||||
"account_number": "2968"
|
||||
},
|
||||
"Rechnungsabgrenzungsposten (Gewinnr\u00fccklage Soll) aus erfolgsneutralen Verrechnungen": {
|
||||
"account_number": "2969"
|
||||
}
|
||||
},
|
||||
"is_group": 1
|
||||
},
|
||||
"IV - Gewinnvortrag/Verlustvortrag": {
|
||||
"account_type": "Equity",
|
||||
"is_group": 1
|
||||
},
|
||||
"V - Jahres\u00fcberschu\u00df/Jahresfehlbetrag": {
|
||||
"account_type": "Equity",
|
||||
"is_group": 1
|
||||
},
|
||||
"Einlagen stiller Gesellschafter": {
|
||||
"account_number": "9295"
|
||||
}
|
||||
},
|
||||
"B - R\u00fcckstellungen": {
|
||||
"is_group": 1,
|
||||
"1 - R\u00fcckstellungen f. Pensionen und \u00e4hnliche Verplicht.": {
|
||||
@@ -1595,6 +1528,143 @@
|
||||
},
|
||||
"is_group": 1
|
||||
},
|
||||
"Passiva - Eigenkapital": {
|
||||
"root_type": "Equity",
|
||||
"A - Eigenkapital": {
|
||||
"account_type": "Equity",
|
||||
"is_group": 1,
|
||||
"I - Gezeichnetes Kapital": {
|
||||
"account_type": "Equity",
|
||||
"is_group": 1,
|
||||
"Gezeichnetes Kapital": {
|
||||
"account_number": "2900",
|
||||
"account_type": "Equity"
|
||||
},
|
||||
"Gesch\u00e4ftsguthaben der verbleibenden Mitglieder": {
|
||||
"account_number": "2901"
|
||||
},
|
||||
"Gesch\u00e4ftsguthaben der ausscheidenden Mitglieder": {
|
||||
"account_number": "2902"
|
||||
},
|
||||
"Gesch\u00e4ftsguthaben aus gek\u00fcndigten Gesch\u00e4ftsanteilen": {
|
||||
"account_number": "2903"
|
||||
},
|
||||
"R\u00fcckst\u00e4ndige f\u00e4llige Einzahlungen auf Gesch\u00e4ftsanteile, vermerkt": {
|
||||
"account_number": "2906"
|
||||
},
|
||||
"Gegenkonto R\u00fcckst\u00e4ndige f\u00e4llige Einzahlungen auf Gesch\u00e4ftsanteile, vermerkt": {
|
||||
"account_number": "2907"
|
||||
},
|
||||
"Kapitalerh\u00f6hung aus Gesellschaftsmitteln": {
|
||||
"account_number": "2908"
|
||||
},
|
||||
"Ausstehende Einlagen auf das gezeichnete Kapital, nicht eingefordert": {
|
||||
"account_number": "2910"
|
||||
}
|
||||
},
|
||||
"II - Kapitalr\u00fccklage": {
|
||||
"account_type": "Equity",
|
||||
"is_group": 1,
|
||||
"Kapitalr\u00fccklage": {
|
||||
"account_number": "2920"
|
||||
},
|
||||
"Kapitalr\u00fccklage durch Ausgabe von Anteilen \u00fcber Nennbetrag": {
|
||||
"account_number": "2925"
|
||||
},
|
||||
"Kapitalr\u00fccklage durch Ausgabe von Schuldverschreibungen": {
|
||||
"account_number": "2926"
|
||||
},
|
||||
"Kapitalr\u00fccklage durch Zuzahlungen gegen Gew\u00e4hrung eines Vorzugs": {
|
||||
"account_number": "2927"
|
||||
},
|
||||
"Kapitalr\u00fccklage durch Zuzahlungen in das Eigenkapital": {
|
||||
"account_number": "2928"
|
||||
},
|
||||
"Nachschusskapital (Gegenkonto 1299)": {
|
||||
"account_number": "2929"
|
||||
}
|
||||
},
|
||||
"III - Gewinnr\u00fccklagen": {
|
||||
"account_type": "Equity",
|
||||
"1 - gesetzliche R\u00fccklage": {
|
||||
"account_type": "Equity",
|
||||
"is_group": 1,
|
||||
"Gesetzliche R\u00fccklage": {
|
||||
"account_number": "2930"
|
||||
}
|
||||
},
|
||||
"2 - R\u00fccklage f. Anteile an einem herrschenden oder mehrheitlich beteiligten Unternehmen": {
|
||||
"account_type": "Equity",
|
||||
"is_group": 1,
|
||||
"R\u00fccklage f. Anteile an einem herrschenden oder mehrheitlich beteiligten Unternehmen": {
|
||||
"account_number": "2935"
|
||||
}
|
||||
},
|
||||
"3 - satzungsm\u00e4\u00dfige R\u00fccklagen": {
|
||||
"account_type": "Equity",
|
||||
"is_group": 1,
|
||||
"Satzungsm\u00e4\u00dfige R\u00fccklagen": {
|
||||
"account_number": "2950"
|
||||
}
|
||||
},
|
||||
"4 - andere Gewinnr\u00fccklagen": {
|
||||
"account_type": "Equity",
|
||||
"is_group": 1,
|
||||
"Andere Gewinnr\u00fccklagen": {
|
||||
"account_number": "2960"
|
||||
},
|
||||
"Andere Gewinnr\u00fccklagen aus dem Erwerb eigener Anteile": {
|
||||
"account_number": "2961"
|
||||
},
|
||||
"Eigenkapitalanteil von Wertaufholungen": {
|
||||
"account_number": "2962"
|
||||
},
|
||||
"Gewinnr\u00fccklagen aus den \u00dcbergangsvorschriften BilMoG": {
|
||||
"is_group": 1,
|
||||
"Gewinnr\u00fccklagen (BilMoG)": {
|
||||
"account_number": "2963"
|
||||
},
|
||||
"Gewinnr\u00fccklagen aus Zuschreibung Sachanlageverm\u00f6gen (BilMoG)": {
|
||||
"account_number": "2964"
|
||||
},
|
||||
"Gewinnr\u00fccklagen aus Zuschreibung Finanzanlageverm\u00f6gen (BilMoG)": {
|
||||
"account_number": "2965"
|
||||
},
|
||||
"Gewinnr\u00fccklagen aus Aufl\u00f6sung der Sonderposten mit R\u00fccklageanteil (BilMoG)": {
|
||||
"account_number": "2966"
|
||||
}
|
||||
},
|
||||
"Latente Steuern (Gewinnr\u00fccklage Haben) aus erfolgsneutralen Verrechnungen": {
|
||||
"account_number": "2967"
|
||||
},
|
||||
"Latente Steuern (Gewinnr\u00fccklage Soll) aus erfolgsneutralen Verrechnungen": {
|
||||
"account_number": "2968"
|
||||
},
|
||||
"Rechnungsabgrenzungsposten (Gewinnr\u00fccklage Soll) aus erfolgsneutralen Verrechnungen": {
|
||||
"account_number": "2969"
|
||||
}
|
||||
},
|
||||
"is_group": 1
|
||||
},
|
||||
"IV - Gewinnvortrag/Verlustvortrag": {
|
||||
"account_type": "Equity",
|
||||
"is_group": 1,
|
||||
"Gewinnvortrag vor Verwendung": {
|
||||
"account_number": "2970"
|
||||
},
|
||||
"Verlustvortrag vor Verwendung": {
|
||||
"account_number": "2978"
|
||||
}
|
||||
},
|
||||
"V - Jahres\u00fcberschu\u00df/Jahresfehlbetrag": {
|
||||
"account_type": "Equity",
|
||||
"is_group": 1
|
||||
},
|
||||
"Einlagen stiller Gesellschafter": {
|
||||
"account_number": "9295"
|
||||
}
|
||||
}
|
||||
},
|
||||
"1 - Umsatzerl\u00f6se": {
|
||||
"root_type": "Income",
|
||||
"is_group": 1,
|
||||
|
||||
@@ -72,16 +72,14 @@
|
||||
"138-Autres subventions d'investissement (m\u00eame ventilation que celle du compte 131)": {},
|
||||
"139-Subventions d'investissement inscrites au compte de r\u00e9sultat": {
|
||||
"1391-Subventions d'\u00e9quipement": {
|
||||
"13911-Subventions d'\u00e9quipement": {
|
||||
"13911-Etat": {},
|
||||
"13912-R\u00e9gions": {},
|
||||
"13913-D\u00e9partements": {},
|
||||
"13914-Communes": {},
|
||||
"13915-Collectivit\u00e9s publiques": {},
|
||||
"13916-Entreprises publiques": {},
|
||||
"13917-Entreprises et organismes priv\u00e9s": {},
|
||||
"13918-Autres": {}
|
||||
}
|
||||
"13911-Etat": {},
|
||||
"13912-R\u00e9gions": {},
|
||||
"13913-D\u00e9partements": {},
|
||||
"13914-Communes": {},
|
||||
"13915-Collectivit\u00e9s publiques": {},
|
||||
"13916-Entreprises publiques": {},
|
||||
"13917-Entreprises et organismes priv\u00e9s": {},
|
||||
"13918-Autres": {}
|
||||
},
|
||||
"1398-Autres subventions d'investissement (m\u00eame ventilation que celle du compte 1391)": {}
|
||||
}
|
||||
@@ -536,8 +534,8 @@
|
||||
"3312-Produits en cours P2": {}
|
||||
},
|
||||
"335-Travaux en cours": {
|
||||
"Travaux en cours T1": {},
|
||||
"Travaux en cours T2": {}
|
||||
"3351-Travaux en cours T1": {},
|
||||
"3352-Travaux en cours T2": {}
|
||||
}
|
||||
},
|
||||
"34-En-cours de production de services": {
|
||||
@@ -595,7 +593,9 @@
|
||||
"371-Marchandises (ou groupe) A": {},
|
||||
"372-Marchandises (ou groupe) B": {}
|
||||
},
|
||||
"38-Stocks en voie d'acheminement, mis en d\u00e9p\u00f4t ou donn\u00e9s en consignation (en cas d'inventaire permanent en comptabilit\u00e9 g\u00e9n\u00e9rale)": {},
|
||||
"38-Stocks en voie d'acheminement, mis en d\u00e9p\u00f4t ou donn\u00e9s en consignation (en cas d'inventaire permanent en comptabilit\u00e9 g\u00e9n\u00e9rale)": {
|
||||
"account_type": "Stock"
|
||||
},
|
||||
"39-D\u00e9pr\u00e9ciations des stocks et en-cours": {
|
||||
"391-D\u00e9pr\u00e9ciations des mati\u00e8res premi\u00e8res (et fournitures)": {
|
||||
"3911-Mati\u00e8res (ou groupe) A": {},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -245,6 +245,9 @@ def get():
|
||||
"account_number": "2200"
|
||||
},
|
||||
_("Duties and Taxes"): {
|
||||
_("TDS Payable"): {
|
||||
"account_number": "2310"
|
||||
},
|
||||
"account_type": "Tax",
|
||||
"is_group": 1,
|
||||
"account_number": "2300"
|
||||
|
||||
@@ -41,6 +41,8 @@ frappe.ui.form.on('Accounting Dimension', {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
frm.toggle_enable('document_type', frm.doc.__islocal);
|
||||
},
|
||||
|
||||
document_type: function(frm) {
|
||||
|
||||
@@ -29,6 +29,16 @@ class AccountingDimension(Document):
|
||||
if exists and self.is_new():
|
||||
frappe.throw("Document Type already used as a dimension")
|
||||
|
||||
if not self.is_new():
|
||||
self.validate_document_type_change()
|
||||
|
||||
def validate_document_type_change(self):
|
||||
doctype_before_save = frappe.db.get_value("Accounting Dimension", self.name, "document_type")
|
||||
if doctype_before_save != self.document_type:
|
||||
message = _("Cannot change Reference Document Type.")
|
||||
message += _("Please create a new Accounting Dimension if required.")
|
||||
frappe.throw(message)
|
||||
|
||||
def after_insert(self):
|
||||
if frappe.flags.in_test:
|
||||
make_dimension_in_accounting_doctypes(doc=self)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
frappe.provide('erpnext.integrations');
|
||||
|
||||
frappe.ui.form.on('Bank', {
|
||||
onload: function(frm) {
|
||||
@@ -7,6 +8,12 @@ frappe.ui.form.on('Bank', {
|
||||
},
|
||||
refresh: function(frm) {
|
||||
add_fields_to_mapping_table(frm);
|
||||
|
||||
if (frm.doc.plaid_access_token) {
|
||||
frm.add_custom_button(__('Refresh Plaid Link'), () => {
|
||||
new erpnext.integrations.refreshPlaidLink(frm.doc.plaid_access_token);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -27,4 +34,79 @@ let add_fields_to_mapping_table = function (frm) {
|
||||
frm.doc.name).options = options;
|
||||
|
||||
frm.fields_dict.bank_transaction_mapping.grid.refresh();
|
||||
};
|
||||
|
||||
erpnext.integrations.refreshPlaidLink = class refreshPlaidLink {
|
||||
constructor(access_token) {
|
||||
this.access_token = access_token;
|
||||
this.plaidUrl = 'https://cdn.plaid.com/link/v2/stable/link-initialize.js';
|
||||
this.init_config();
|
||||
}
|
||||
|
||||
async init_config() {
|
||||
this.plaid_env = await frappe.db.get_single_value('Plaid Settings', 'plaid_env');
|
||||
this.token = await this.get_link_token_for_update();
|
||||
this.init_plaid();
|
||||
}
|
||||
|
||||
async get_link_token_for_update() {
|
||||
const token = frappe.xcall(
|
||||
'erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.get_link_token_for_update',
|
||||
{ access_token: this.access_token }
|
||||
)
|
||||
if (!token) {
|
||||
frappe.throw(__('Cannot retrieve link token for update. Check Error Log for more information'));
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
init_plaid() {
|
||||
const me = this;
|
||||
me.loadScript(me.plaidUrl)
|
||||
.then(() => {
|
||||
me.onScriptLoaded(me);
|
||||
})
|
||||
.then(() => {
|
||||
if (me.linkHandler) {
|
||||
me.linkHandler.open();
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
me.onScriptError(error);
|
||||
});
|
||||
}
|
||||
|
||||
loadScript(src) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
if (document.querySelector("script[src='" + src + "']")) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
const el = document.createElement('script');
|
||||
el.type = 'text/javascript';
|
||||
el.async = true;
|
||||
el.src = src;
|
||||
el.addEventListener('load', resolve);
|
||||
el.addEventListener('error', reject);
|
||||
el.addEventListener('abort', reject);
|
||||
document.head.appendChild(el);
|
||||
});
|
||||
}
|
||||
|
||||
onScriptLoaded(me) {
|
||||
me.linkHandler = Plaid.create({
|
||||
env: me.plaid_env,
|
||||
token: me.token,
|
||||
onSuccess: me.plaid_success
|
||||
});
|
||||
}
|
||||
|
||||
onScriptError(error) {
|
||||
frappe.msgprint(__("There was an issue connecting to Plaid's authentication server. Check browser console for more information"));
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
plaid_success(token, response) {
|
||||
frappe.show_alert({ message: __('Plaid Link Updated'), indicator: 'green' });
|
||||
}
|
||||
};
|
||||
@@ -21,6 +21,14 @@ frappe.ui.form.on("Bank Reconciliation", {
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query("bank_account", function() {
|
||||
return {
|
||||
"filters": {
|
||||
"is_company_account": 1
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_value("from_date", frappe.datetime.month_start());
|
||||
frm.set_value("to_date", frappe.datetime.month_end());
|
||||
},
|
||||
|
||||
@@ -271,6 +271,7 @@
|
||||
"label": "Debit",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "currency",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
@@ -304,6 +305,7 @@
|
||||
"label": "Credit",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "currency",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
@@ -632,6 +634,7 @@
|
||||
"label": "Allocated Amount",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "currency",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
@@ -731,6 +734,7 @@
|
||||
"label": "Unallocated Amount",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "currency",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
@@ -755,7 +759,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-05-11 05:27:55.244721",
|
||||
"modified": "2021-11-26 12:44:55.244721",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Bank Transaction",
|
||||
|
||||
@@ -9,11 +9,13 @@ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sal
|
||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
|
||||
from erpnext.accounts.page.bank_reconciliation.bank_reconciliation import reconcile, get_linked_payments
|
||||
from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
|
||||
|
||||
test_dependencies = ["Item", "Cost Center"]
|
||||
|
||||
class TestBankTransaction(unittest.TestCase):
|
||||
def setUp(self):
|
||||
make_pos_profile()
|
||||
add_transactions()
|
||||
add_payments()
|
||||
|
||||
@@ -27,6 +29,9 @@ class TestBankTransaction(unittest.TestCase):
|
||||
frappe.db.sql("""delete from `tabPayment Entry Reference`""")
|
||||
frappe.db.sql("""delete from `tabPayment Entry`""")
|
||||
|
||||
# Delete POS Profile
|
||||
frappe.db.sql("delete from `tabPOS Profile`")
|
||||
|
||||
frappe.flags.test_bank_transactions_created = False
|
||||
frappe.flags.test_payments_created = False
|
||||
|
||||
|
||||
@@ -7,19 +7,19 @@ DEFAULT_MAPPERS = [
|
||||
'section_header': 'Cash flows from operating activities',
|
||||
'section_leader': 'Adjustments for',
|
||||
'section_name': 'Operating Activities',
|
||||
'position': 0,
|
||||
'position': 1,
|
||||
'section_subtotal': 'Cash generated from operations',
|
||||
},
|
||||
{
|
||||
'doctype': 'Cash Flow Mapper',
|
||||
'position': 1,
|
||||
'position': 2,
|
||||
'section_footer': 'Net cash used in investing activities',
|
||||
'section_header': 'Cash flows from investing activities',
|
||||
'section_name': 'Investing Activities'
|
||||
},
|
||||
{
|
||||
'doctype': 'Cash Flow Mapper',
|
||||
'position': 2,
|
||||
'position': 3,
|
||||
'section_footer': 'Net cash used in financing activites',
|
||||
'section_header': 'Cash flows from financing activities',
|
||||
'section_name': 'Financing Activities',
|
||||
|
||||
@@ -23,13 +23,13 @@ class CashierClosing(Document):
|
||||
where posting_date=%s and posting_time>=%s and posting_time<=%s and owner=%s
|
||||
""", (self.date, self.from_time, self.time, self.user))
|
||||
self.outstanding_amount = flt(values[0][0] if values else 0)
|
||||
|
||||
|
||||
def make_calculations(self):
|
||||
total = 0.00
|
||||
for i in self.payments:
|
||||
total += flt(i.amount)
|
||||
|
||||
self.net_amount = total + self.outstanding_amount + self.expense - self.custody + self.returns
|
||||
self.net_amount = total + self.outstanding_amount + flt(self.expense) - flt(self.custody) + flt(self.returns)
|
||||
|
||||
def validate_time(self):
|
||||
if self.from_time >= self.time:
|
||||
|
||||
@@ -81,10 +81,11 @@ class ExchangeRateRevaluation(Document):
|
||||
sum(debit) - sum(credit) as balance
|
||||
from `tabGL Entry`
|
||||
where account in (%s)
|
||||
group by account, party_type, party
|
||||
and posting_date <= %s
|
||||
group by account, NULLIF(party_type,''), NULLIF(party,'')
|
||||
having sum(debit) != sum(credit)
|
||||
order by account
|
||||
""" % ', '.join(['%s']*len(accounts)), tuple(accounts), as_dict=1)
|
||||
""" % (', '.join(['%s']*len(accounts)), '%s'), tuple(accounts + [self.posting_date]), as_dict=1)
|
||||
|
||||
return account_details
|
||||
|
||||
@@ -124,9 +125,9 @@ class ExchangeRateRevaluation(Document):
|
||||
"party_type": d.get("party_type"),
|
||||
"party": d.get("party"),
|
||||
"account_currency": d.get("account_currency"),
|
||||
"balance": d.get("balance_in_account_currency"),
|
||||
dr_or_cr: abs(d.get("balance_in_account_currency")),
|
||||
"exchange_rate":d.get("new_exchange_rate"),
|
||||
"balance": flt(d.get("balance_in_account_currency"), d.precision("balance_in_account_currency")),
|
||||
dr_or_cr: flt(abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")),
|
||||
"exchange_rate": flt(d.get("new_exchange_rate"), d.precision("new_exchange_rate")),
|
||||
"reference_type": "Exchange Rate Revaluation",
|
||||
"reference_name": self.name,
|
||||
})
|
||||
@@ -135,9 +136,9 @@ class ExchangeRateRevaluation(Document):
|
||||
"party_type": d.get("party_type"),
|
||||
"party": d.get("party"),
|
||||
"account_currency": d.get("account_currency"),
|
||||
"balance": d.get("balance_in_account_currency"),
|
||||
reverse_dr_or_cr: abs(d.get("balance_in_account_currency")),
|
||||
"exchange_rate": d.get("current_exchange_rate"),
|
||||
"balance": flt(d.get("balance_in_account_currency"), d.precision("balance_in_account_currency")),
|
||||
reverse_dr_or_cr: flt(abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")),
|
||||
"exchange_rate": flt(d.get("current_exchange_rate"), d.precision("current_exchange_rate")),
|
||||
"reference_type": "Exchange Rate Revaluation",
|
||||
"reference_name": self.name
|
||||
})
|
||||
@@ -166,9 +167,9 @@ def get_account_details(account, company, posting_date, party_type=None, party=N
|
||||
|
||||
account_details = {}
|
||||
company_currency = erpnext.get_company_currency(company)
|
||||
balance = get_balance_on(account, party_type=party_type, party=party, in_account_currency=False)
|
||||
balance = get_balance_on(account, date=posting_date, party_type=party_type, party=party, in_account_currency=False)
|
||||
if balance:
|
||||
balance_in_account_currency = get_balance_on(account, party_type=party_type, party=party)
|
||||
balance_in_account_currency = get_balance_on(account, date=posting_date, party_type=party_type, party=party)
|
||||
current_exchange_rate = balance / balance_in_account_currency if balance_in_account_currency else 0
|
||||
new_exchange_rate = get_exchange_rate(account_currency, company_currency, posting_date)
|
||||
new_balance_in_base_currency = balance_in_account_currency * new_exchange_rate
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe, unittest
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe.utils import now_datetime
|
||||
|
||||
from erpnext.accounts.doctype.fiscal_year.fiscal_year import FiscalYearIncorrectDate
|
||||
|
||||
test_records = frappe.get_test_records('Fiscal Year')
|
||||
test_ignore = ["Company"]
|
||||
|
||||
class TestFiscalYear(unittest.TestCase):
|
||||
@@ -23,3 +25,29 @@ class TestFiscalYear(unittest.TestCase):
|
||||
})
|
||||
|
||||
self.assertRaises(FiscalYearIncorrectDate, fy.insert)
|
||||
|
||||
|
||||
def test_record_generator():
|
||||
test_records = [
|
||||
{
|
||||
"doctype": "Fiscal Year",
|
||||
"year": "_Test Short Fiscal Year 2011",
|
||||
"is_short_year": 1,
|
||||
"year_end_date": "2011-04-01",
|
||||
"year_start_date": "2011-12-31"
|
||||
}
|
||||
]
|
||||
|
||||
start = 2012
|
||||
end = now_datetime().year + 5
|
||||
for year in range(start, end):
|
||||
test_records.append({
|
||||
"doctype": "Fiscal Year",
|
||||
"year": "_Test Fiscal Year {}".format(year),
|
||||
"year_start_date": "{}-01-01".format(year),
|
||||
"year_end_date": "{}-12-31".format(year)
|
||||
})
|
||||
|
||||
return test_records
|
||||
|
||||
test_records = test_record_generator()
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
[
|
||||
{
|
||||
"doctype": "Fiscal Year",
|
||||
"year": "_Test Fiscal Year 2012",
|
||||
"year_end_date": "2012-12-31",
|
||||
"year_start_date": "2012-01-01"
|
||||
},
|
||||
{
|
||||
"doctype": "Fiscal Year",
|
||||
"year": "_Test Fiscal Year 2013",
|
||||
"year_end_date": "2013-12-31",
|
||||
"year_start_date": "2013-01-01"
|
||||
},
|
||||
{
|
||||
"doctype": "Fiscal Year",
|
||||
"year": "_Test Fiscal Year 2014",
|
||||
"year_end_date": "2014-12-31",
|
||||
"year_start_date": "2014-01-01"
|
||||
},
|
||||
{
|
||||
"doctype": "Fiscal Year",
|
||||
"year": "_Test Fiscal Year 2015",
|
||||
"year_end_date": "2015-12-31",
|
||||
"year_start_date": "2015-01-01"
|
||||
},
|
||||
{
|
||||
"doctype": "Fiscal Year",
|
||||
"year": "_Test Fiscal Year 2016",
|
||||
"year_end_date": "2016-12-31",
|
||||
"year_start_date": "2016-01-01"
|
||||
},
|
||||
{
|
||||
"doctype": "Fiscal Year",
|
||||
"year": "_Test Fiscal Year 2017",
|
||||
"year_end_date": "2017-12-31",
|
||||
"year_start_date": "2017-01-01"
|
||||
},
|
||||
{
|
||||
"doctype": "Fiscal Year",
|
||||
"year": "_Test Fiscal Year 2018",
|
||||
"year_end_date": "2018-12-31",
|
||||
"year_start_date": "2018-01-01"
|
||||
},
|
||||
{
|
||||
"doctype": "Fiscal Year",
|
||||
"year": "_Test Fiscal Year 2019",
|
||||
"year_end_date": "2019-12-31",
|
||||
"year_start_date": "2019-01-01"
|
||||
},
|
||||
{
|
||||
"doctype": "Fiscal Year",
|
||||
"year": "_Test Fiscal Year 2020",
|
||||
"year_end_date": "2020-12-31",
|
||||
"year_start_date": "2020-01-01"
|
||||
},
|
||||
{
|
||||
"doctype": "Fiscal Year",
|
||||
"year": "_Test Fiscal Year 2021",
|
||||
"year_end_date": "2021-12-31",
|
||||
"year_start_date": "2021-01-01"
|
||||
},
|
||||
{
|
||||
"doctype": "Fiscal Year",
|
||||
"year": "_Test Short Fiscal Year 2021",
|
||||
"is_short_year": 1,
|
||||
"year_end_date": "2021-12-31",
|
||||
"year_start_date": "2021-04-01"
|
||||
}
|
||||
]
|
||||
@@ -97,8 +97,7 @@ class GLEntry(Document):
|
||||
|
||||
def check_pl_account(self):
|
||||
if self.is_opening=='Yes' and \
|
||||
frappe.db.get_value("Account", self.account, "report_type")=="Profit and Loss" and \
|
||||
self.voucher_type not in ['Purchase Invoice', 'Sales Invoice']:
|
||||
frappe.db.get_value("Account", self.account, "report_type")=="Profit and Loss":
|
||||
frappe.throw(_("{0} {1}: 'Profit and Loss' type account {2} not allowed in Opening Entry")
|
||||
.format(self.voucher_type, self.voucher_no, self.account))
|
||||
|
||||
@@ -138,7 +137,8 @@ class GLEntry(Document):
|
||||
frappe.throw(_("{0} {1}: Cost Center {2} does not belong to Company {3}")
|
||||
.format(self.voucher_type, self.voucher_no, self.cost_center, self.company))
|
||||
|
||||
if not self.flags.from_repost and self.cost_center and _check_is_group():
|
||||
if not self.flags.from_repost and not self.voucher_type == 'Period Closing Voucher' \
|
||||
and self.cost_center and _check_is_group():
|
||||
frappe.throw(_("""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot
|
||||
be used in transactions""").format(self.voucher_type, self.voucher_no, frappe.bold(self.cost_center)))
|
||||
|
||||
@@ -293,4 +293,8 @@ def rename_temporarily_named_docs(doctype):
|
||||
oldname = doc.name
|
||||
set_name_from_naming_options(frappe.get_meta(doctype).autoname, doc)
|
||||
newname = doc.name
|
||||
frappe.db.sql("""UPDATE `tab{}` SET name = %s, to_rename = 0 where name = %s""".format(doctype), (newname, oldname))
|
||||
frappe.db.sql(
|
||||
"UPDATE `tab{}` SET name = %s, to_rename = 0 where name = %s".format(doctype),
|
||||
(newname, oldname),
|
||||
auto_commit=True
|
||||
)
|
||||
|
||||
@@ -29,7 +29,11 @@ class JournalEntry(AccountsController):
|
||||
self.validate_entries_for_advance()
|
||||
self.validate_multi_currency()
|
||||
self.set_amounts_in_company_currency()
|
||||
self.validate_total_debit_and_credit()
|
||||
|
||||
# Do not validate while importing via data import
|
||||
if not frappe.flags.in_import:
|
||||
self.validate_total_debit_and_credit()
|
||||
|
||||
self.validate_against_jv()
|
||||
self.validate_reference_doc()
|
||||
self.set_against_account()
|
||||
@@ -207,11 +211,11 @@ class JournalEntry(AccountsController):
|
||||
if d.reference_type=="Journal Entry":
|
||||
account_root_type = frappe.db.get_value("Account", d.account, "root_type")
|
||||
if account_root_type == "Asset" and flt(d.debit) > 0:
|
||||
frappe.throw(_("For {0}, only credit accounts can be linked against another debit entry")
|
||||
.format(d.account))
|
||||
frappe.throw(_("Row #{0}: For {1}, you can select reference document only if account gets credited")
|
||||
.format(d.idx, d.account))
|
||||
elif account_root_type == "Liability" and flt(d.credit) > 0:
|
||||
frappe.throw(_("For {0}, only debit accounts can be linked against another credit entry")
|
||||
.format(d.account))
|
||||
frappe.throw(_("Row #{0}: For {1}, you can select reference document only if account gets debited")
|
||||
.format(d.idx, d.account))
|
||||
|
||||
if d.reference_name == self.name:
|
||||
frappe.throw(_("You can not enter current voucher in 'Against Journal Entry' column"))
|
||||
@@ -1047,4 +1051,4 @@ def make_reverse_journal_entry(source_name, target_doc=None):
|
||||
},
|
||||
}, target_doc)
|
||||
|
||||
return doclist
|
||||
return doclist
|
||||
@@ -38,7 +38,10 @@ def get_loyalty_details(customer, loyalty_program, expiry_date=None, company=Non
|
||||
@frappe.whitelist()
|
||||
def get_loyalty_program_details_with_points(customer, loyalty_program=None, expiry_date=None, company=None, silent=False, include_expired_entry=False, current_transaction_amount=0):
|
||||
lp_details = get_loyalty_program_details(customer, loyalty_program, company=company, silent=silent)
|
||||
loyalty_program = frappe.get_doc("Loyalty Program", loyalty_program)
|
||||
loyalty_program_name = loyalty_program or lp_details.loyalty_program
|
||||
if not loyalty_program_name: return
|
||||
|
||||
loyalty_program = frappe.get_doc("Loyalty Program", loyalty_program_name)
|
||||
lp_details.update(get_loyalty_details(customer, loyalty_program.name, expiry_date, company, include_expired_entry))
|
||||
|
||||
# sort collection rule, first item on list will be lowest min_spent
|
||||
|
||||
@@ -181,7 +181,8 @@ class OpeningInvoiceCreationTool(Document):
|
||||
"due_date": row.due_date,
|
||||
"posting_date": row.posting_date,
|
||||
frappe.scrub(party_type): row.party,
|
||||
"doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice"
|
||||
"doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice",
|
||||
"update_stock": 0
|
||||
})
|
||||
|
||||
accounting_dimension = get_accounting_dimensions()
|
||||
|
||||
@@ -7,17 +7,25 @@ import frappe
|
||||
import unittest
|
||||
|
||||
test_dependencies = ["Customer", "Supplier"]
|
||||
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
|
||||
from erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool import get_temporary_opening_account
|
||||
from erpnext.controllers.accounts_controller import AccountMissingError
|
||||
|
||||
class TestOpeningInvoiceCreationTool(unittest.TestCase):
|
||||
def make_invoices(self, invoice_type="Sales"):
|
||||
def setUp(self):
|
||||
if not frappe.db.exists("Company", "_Test Opening Invoice Company"):
|
||||
make_company()
|
||||
|
||||
def make_invoices(self, invoice_type="Sales", company=None, party_1=None, party_2=None):
|
||||
doc = frappe.get_single("Opening Invoice Creation Tool")
|
||||
args = get_opening_invoice_creation_dict(invoice_type=invoice_type)
|
||||
args = get_opening_invoice_creation_dict(invoice_type=invoice_type, company=company,
|
||||
party_1=party_1, party_2=party_2)
|
||||
doc.update(args)
|
||||
return doc.make_invoices()
|
||||
|
||||
def test_opening_sales_invoice_creation(self):
|
||||
invoices = self.make_invoices()
|
||||
property_setter = make_property_setter("Sales Invoice", "update_stock", "default", 1, "Check")
|
||||
invoices = self.make_invoices(company="_Test Opening Invoice Company")
|
||||
|
||||
self.assertEqual(len(invoices), 2)
|
||||
expected_value = {
|
||||
@@ -27,6 +35,13 @@ class TestOpeningInvoiceCreationTool(unittest.TestCase):
|
||||
}
|
||||
self.check_expected_values(invoices, expected_value)
|
||||
|
||||
si = frappe.get_doc("Sales Invoice", invoices[0])
|
||||
|
||||
# Check if update stock is not enabled
|
||||
self.assertEqual(si.update_stock, 0)
|
||||
|
||||
property_setter.delete()
|
||||
|
||||
def check_expected_values(self, invoices, expected_value, invoice_type="Sales"):
|
||||
doctype = "Sales Invoice" if invoice_type == "Sales" else "Purchase Invoice"
|
||||
|
||||
@@ -36,7 +51,7 @@ class TestOpeningInvoiceCreationTool(unittest.TestCase):
|
||||
self.assertEqual(si.get(field, ""), expected_value[invoice_idx][field_idx])
|
||||
|
||||
def test_opening_purchase_invoice_creation(self):
|
||||
invoices = self.make_invoices(invoice_type="Purchase")
|
||||
invoices = self.make_invoices(invoice_type="Purchase", company="_Test Opening Invoice Company")
|
||||
|
||||
self.assertEqual(len(invoices), 2)
|
||||
expected_value = {
|
||||
@@ -46,6 +61,28 @@ class TestOpeningInvoiceCreationTool(unittest.TestCase):
|
||||
}
|
||||
self.check_expected_values(invoices, expected_value, invoice_type="Purchase", )
|
||||
|
||||
def test_opening_sales_invoice_creation_with_missing_debit_account(self):
|
||||
company = "_Test Opening Invoice Company"
|
||||
party_1, party_2 = make_customer("Customer A"), make_customer("Customer B")
|
||||
|
||||
old_default_receivable_account = frappe.db.get_value("Company", company, "default_receivable_account")
|
||||
frappe.db.set_value("Company", company, "default_receivable_account", "")
|
||||
|
||||
if not frappe.db.exists("Cost Center", "_Test Opening Invoice Company - _TOIC"):
|
||||
cc = frappe.get_doc({"doctype": "Cost Center", "cost_center_name": "_Test Opening Invoice Company",
|
||||
"is_group": 1, "company": "_Test Opening Invoice Company"})
|
||||
cc.insert(ignore_mandatory=True)
|
||||
cc2 = frappe.get_doc({"doctype": "Cost Center", "cost_center_name": "Main", "is_group": 0,
|
||||
"company": "_Test Opening Invoice Company", "parent_cost_center": cc.name})
|
||||
cc2.insert()
|
||||
|
||||
frappe.db.set_value("Company", company, "cost_center", "Main - _TOIC")
|
||||
|
||||
self.assertRaises(AccountMissingError, self.make_invoices, company="_Test Opening Invoice Company", party_1=party_1, party_2=party_2)
|
||||
|
||||
# teardown
|
||||
frappe.db.set_value("Company", company, "default_receivable_account", old_default_receivable_account)
|
||||
|
||||
def get_opening_invoice_creation_dict(**args):
|
||||
party = "Customer" if args.get("invoice_type", "Sales") == "Sales" else "Supplier"
|
||||
company = args.get("company", "_Test Company")
|
||||
@@ -57,7 +94,7 @@ def get_opening_invoice_creation_dict(**args):
|
||||
{
|
||||
"qty": 1.0,
|
||||
"outstanding_amount": 300,
|
||||
"party": "_Test {0}".format(party),
|
||||
"party": args.get("party_1") or "_Test {0}".format(party),
|
||||
"item_name": "Opening Item",
|
||||
"due_date": "2016-09-10",
|
||||
"posting_date": "2016-09-05",
|
||||
@@ -66,7 +103,7 @@ def get_opening_invoice_creation_dict(**args):
|
||||
{
|
||||
"qty": 2.0,
|
||||
"outstanding_amount": 250,
|
||||
"party": "_Test {0} 1".format(party),
|
||||
"party": args.get("party_2") or "_Test {0} 1".format(party),
|
||||
"item_name": "Opening Item",
|
||||
"due_date": "2016-09-10",
|
||||
"posting_date": "2016-09-05",
|
||||
@@ -76,4 +113,31 @@ def get_opening_invoice_creation_dict(**args):
|
||||
})
|
||||
|
||||
invoice_dict.update(args)
|
||||
return invoice_dict
|
||||
return invoice_dict
|
||||
|
||||
def make_company():
|
||||
if frappe.db.exists("Company", "_Test Opening Invoice Company"):
|
||||
return frappe.get_doc("Company", "_Test Opening Invoice Company")
|
||||
|
||||
company = frappe.new_doc("Company")
|
||||
company.company_name = "_Test Opening Invoice Company"
|
||||
company.abbr = "_TOIC"
|
||||
company.default_currency = "INR"
|
||||
company.country = "India"
|
||||
company.insert()
|
||||
return company
|
||||
|
||||
def make_customer(customer=None):
|
||||
customer_name = customer or "Opening Customer"
|
||||
customer = frappe.get_doc({
|
||||
"doctype": "Customer",
|
||||
"customer_name": customer_name,
|
||||
"customer_group": "All Customer Groups",
|
||||
"customer_type": "Company",
|
||||
"territory": "All Territories"
|
||||
})
|
||||
if not frappe.db.exists("Customer", customer_name):
|
||||
customer.insert(ignore_permissions=True)
|
||||
return customer.name
|
||||
else:
|
||||
return frappe.db.exists("Customer", customer_name)
|
||||
@@ -187,7 +187,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
|
||||
frm.toggle_display("base_received_amount", (
|
||||
frm.doc.paid_to_account_currency != company_currency &&
|
||||
frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency
|
||||
frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency
|
||||
&& frm.doc.base_paid_amount != frm.doc.base_received_amount
|
||||
));
|
||||
|
||||
@@ -386,6 +386,8 @@ frappe.ui.form.on('Payment Entry', {
|
||||
|
||||
set_account_currency_and_balance: function(frm, account, currency_field,
|
||||
balance_field, callback_function) {
|
||||
|
||||
var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
|
||||
if (frm.doc.posting_date && account) {
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_account_details",
|
||||
@@ -412,6 +414,14 @@ frappe.ui.form.on('Payment Entry', {
|
||||
|
||||
if(!frm.doc.paid_amount && frm.doc.received_amount)
|
||||
frm.events.received_amount(frm);
|
||||
|
||||
if (frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency
|
||||
&& frm.doc.paid_amount != frm.doc.received_amount) {
|
||||
if (company_currency != frm.doc.paid_from_account_currency &&
|
||||
frm.doc.payment_type == "Pay") {
|
||||
frm.doc.paid_amount = frm.doc.received_amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
() => {
|
||||
@@ -583,12 +593,22 @@ frappe.ui.form.on('Payment Entry', {
|
||||
{fieldtype:"Column Break"},
|
||||
{fieldtype:"Float", label: __("Less Than Amount"), fieldname:"outstanding_amt_less_than"},
|
||||
{fieldtype:"Section Break"},
|
||||
{fieldtype:"Link", label:__("Cost Center"), fieldname:"cost_center", options:"Cost Center",
|
||||
"get_query": function() {
|
||||
return {
|
||||
"filters": {"company": frm.doc.company}
|
||||
}
|
||||
}
|
||||
},
|
||||
{fieldtype:"Column Break"},
|
||||
{fieldtype:"Section Break"},
|
||||
{fieldtype:"Check", label: __("Allocate Payment Amount"), fieldname:"allocate_payment_amount", default:1},
|
||||
];
|
||||
|
||||
frappe.prompt(fields, function(filters){
|
||||
frappe.flags.allocate_payment_amount = true;
|
||||
frm.events.validate_filters_data(frm, filters);
|
||||
frm.doc.cost_center = filters.cost_center;
|
||||
frm.events.get_outstanding_documents(frm, filters);
|
||||
}, __("Filters"), __("Get Outstanding Documents"));
|
||||
},
|
||||
@@ -1031,18 +1051,10 @@ frappe.ui.form.on('Payment Entry', {
|
||||
},
|
||||
callback: function(r, rt) {
|
||||
if(r.message) {
|
||||
frappe.run_serially([
|
||||
() => {
|
||||
|
||||
frm.set_value("paid_from_account_balance", r.message.paid_from_account_balance);
|
||||
frm.set_value("paid_to_account_balance", r.message.paid_to_account_balance);
|
||||
frm.set_value("party_balance", r.message.party_balance);
|
||||
},
|
||||
() => {
|
||||
if(frm.doc.payment_type != "Internal") {
|
||||
frm.clear_table("references");
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -531,7 +531,8 @@
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Title",
|
||||
"print_hide": 1
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "party",
|
||||
@@ -575,7 +576,7 @@
|
||||
}
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"modified": "2019-11-06 12:59:43.151721",
|
||||
"modified": "2021-03-10 13:05:16.958866",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Entry",
|
||||
@@ -619,4 +620,4 @@
|
||||
"sort_order": "DESC",
|
||||
"title_field": "title",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -441,6 +441,10 @@ class PaymentEntry(AccountsController):
|
||||
.format(total_negative_outstanding), InvalidPaymentEntry)
|
||||
|
||||
def set_title(self):
|
||||
if frappe.flags.in_import and self.title:
|
||||
# do not set title dynamically if title exists during data import.
|
||||
return
|
||||
|
||||
if self.payment_type in ("Receive", "Pay"):
|
||||
self.title = self.party
|
||||
else:
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
frappe.listview_settings['Payment Entry'] = {
|
||||
|
||||
onload: function(listview) {
|
||||
listview.page.fields_dict.party_type.get_query = function() {
|
||||
return {
|
||||
"filters": {
|
||||
"name": ["in", Object.keys(frappe.boot.party_account_types)],
|
||||
}
|
||||
if (listview.page.fields_dict.party_type) {
|
||||
listview.page.fields_dict.party_type.get_query = function() {
|
||||
return {
|
||||
"filters": {
|
||||
"name": ["in", Object.keys(frappe.boot.party_account_types)],
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -37,6 +37,11 @@ frappe.ui.form.on("Payment Reconciliation Payment", {
|
||||
erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.extend({
|
||||
onload: function() {
|
||||
var me = this;
|
||||
|
||||
this.frm.set_query("party", function() {
|
||||
check_mandatory(me.frm);
|
||||
});
|
||||
|
||||
this.frm.set_query("party_type", function() {
|
||||
return {
|
||||
"filters": {
|
||||
@@ -46,37 +51,39 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
||||
});
|
||||
|
||||
this.frm.set_query('receivable_payable_account', function() {
|
||||
if(!me.frm.doc.company || !me.frm.doc.party_type) {
|
||||
frappe.msgprint(__("Please select Company and Party Type first"));
|
||||
} else {
|
||||
return{
|
||||
filters: {
|
||||
"company": me.frm.doc.company,
|
||||
"is_group": 0,
|
||||
"account_type": frappe.boot.party_account_types[me.frm.doc.party_type]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
check_mandatory(me.frm);
|
||||
return {
|
||||
filters: {
|
||||
"company": me.frm.doc.company,
|
||||
"is_group": 0,
|
||||
"account_type": frappe.boot.party_account_types[me.frm.doc.party_type]
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
this.frm.set_query('bank_cash_account', function() {
|
||||
if(!me.frm.doc.company) {
|
||||
frappe.msgprint(__("Please select Company first"));
|
||||
} else {
|
||||
return{
|
||||
filters:[
|
||||
['Account', 'company', '=', me.frm.doc.company],
|
||||
['Account', 'is_group', '=', 0],
|
||||
['Account', 'account_type', 'in', ['Bank', 'Cash']]
|
||||
]
|
||||
};
|
||||
}
|
||||
check_mandatory(me.frm, true);
|
||||
return {
|
||||
filters:[
|
||||
['Account', 'company', '=', me.frm.doc.company],
|
||||
['Account', 'is_group', '=', 0],
|
||||
['Account', 'account_type', 'in', ['Bank', 'Cash']]
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
this.frm.set_value('party_type', '');
|
||||
this.frm.set_value('party', '');
|
||||
this.frm.set_value('receivable_payable_account', '');
|
||||
|
||||
var check_mandatory = (frm, only_company=false) => {
|
||||
var title = __("Mandatory");
|
||||
if (only_company && !frm.doc.company) {
|
||||
frappe.throw({message: __("Please Select a Company First"), title: title});
|
||||
} else if (!frm.doc.company || !frm.doc.party_type) {
|
||||
frappe.throw({message: __("Please Select Both Company and Party Type First"), title: title});
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
@@ -90,7 +97,7 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
||||
|
||||
party: function() {
|
||||
var me = this
|
||||
if(!me.frm.doc.receivable_payable_account && me.frm.doc.party_type && me.frm.doc.party) {
|
||||
if (!me.frm.doc.receivable_payable_account && me.frm.doc.party_type && me.frm.doc.party) {
|
||||
return frappe.call({
|
||||
method: "erpnext.accounts.party.get_party_account",
|
||||
args: {
|
||||
@@ -99,7 +106,7 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
||||
party: me.frm.doc.party
|
||||
},
|
||||
callback: function(r) {
|
||||
if(!r.exc && r.message) {
|
||||
if (!r.exc && r.message) {
|
||||
me.frm.set_value("receivable_payable_account", r.message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,18 +88,18 @@ class PaymentReconciliation(Document):
|
||||
voucher_type = ('Sales Invoice'
|
||||
if self.party_type == 'Customer' else "Purchase Invoice")
|
||||
|
||||
return frappe.db.sql(""" SELECT `tab{doc}`.name as reference_name, %(voucher_type)s as reference_type,
|
||||
(sum(`tabGL Entry`.{dr_or_cr}) - sum(`tabGL Entry`.{reconciled_dr_or_cr})) as amount,
|
||||
return frappe.db.sql(""" SELECT doc.name as reference_name, %(voucher_type)s as reference_type,
|
||||
(sum(gl.{dr_or_cr}) - sum(gl.{reconciled_dr_or_cr})) as amount,
|
||||
account_currency as currency
|
||||
FROM `tab{doc}`, `tabGL Entry`
|
||||
FROM `tab{doc}` doc, `tabGL Entry` gl
|
||||
WHERE
|
||||
(`tab{doc}`.name = `tabGL Entry`.against_voucher or `tab{doc}`.name = `tabGL Entry`.voucher_no)
|
||||
and `tab{doc}`.{party_type_field} = %(party)s
|
||||
and `tab{doc}`.is_return = 1 and `tab{doc}`.return_against IS NULL
|
||||
and `tabGL Entry`.against_voucher_type = %(voucher_type)s
|
||||
and `tab{doc}`.docstatus = 1 and `tabGL Entry`.party = %(party)s
|
||||
and `tabGL Entry`.party_type = %(party_type)s and `tabGL Entry`.account = %(account)s
|
||||
GROUP BY `tab{doc}`.name
|
||||
(doc.name = gl.against_voucher or doc.name = gl.voucher_no)
|
||||
and doc.{party_type_field} = %(party)s
|
||||
and doc.is_return = 1 and ifnull(doc.return_against, "") = ""
|
||||
and gl.against_voucher_type = %(voucher_type)s
|
||||
and doc.docstatus = 1 and gl.party = %(party)s
|
||||
and gl.party_type = %(party_type)s and gl.account = %(account)s
|
||||
GROUP BY doc.name
|
||||
Having
|
||||
amount > 0
|
||||
""".format(
|
||||
@@ -303,4 +303,4 @@ def reconcile_dr_cr_note(dr_cr_notes, company):
|
||||
]
|
||||
})
|
||||
|
||||
jv.submit()
|
||||
jv.submit()
|
||||
|
||||
@@ -1,283 +1,95 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "ACC-PCV-.YYYY.-.#####",
|
||||
"beta": 0,
|
||||
"creation": "2013-01-10 16:34:07",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 0,
|
||||
"engine": "InnoDB",
|
||||
"actions": [],
|
||||
"autoname": "ACC-PCV-.YYYY.-.#####",
|
||||
"creation": "2013-01-10 16:34:07",
|
||||
"doctype": "DocType",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"transaction_date",
|
||||
"posting_date",
|
||||
"fiscal_year",
|
||||
"amended_from",
|
||||
"company",
|
||||
"cost_center_wise_pnl",
|
||||
"column_break1",
|
||||
"closing_account_head",
|
||||
"remarks"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "transaction_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Transaction Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "transaction_date",
|
||||
"oldfieldtype": "Date",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "transaction_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Transaction Date",
|
||||
"oldfieldname": "transaction_date",
|
||||
"oldfieldtype": "Date"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "posting_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Posting Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "posting_date",
|
||||
"oldfieldtype": "Date",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "posting_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Posting Date",
|
||||
"oldfieldname": "posting_date",
|
||||
"oldfieldtype": "Date",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "fiscal_year",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Closing Fiscal Year",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "fiscal_year",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "Fiscal Year",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "fiscal_year",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Closing Fiscal Year",
|
||||
"oldfieldname": "fiscal_year",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "Fiscal Year",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 1,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Amended From",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "amended_from",
|
||||
"oldfieldtype": "Data",
|
||||
"options": "Period Closing Voucher",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Amended From",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "amended_from",
|
||||
"oldfieldtype": "Data",
|
||||
"options": "Period Closing Voucher",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Company",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "company",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "Company",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"label": "Company",
|
||||
"oldfieldname": "company",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "Company",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break1",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldtype": "Column Break",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "column_break1",
|
||||
"fieldtype": "Column Break",
|
||||
"oldfieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "The account head under Liability or Equity, in which Profit/Loss will be booked",
|
||||
"fieldname": "closing_account_head",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Closing Account Head",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "closing_account_head",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Account",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"description": "The account head under Liability or Equity, in which Profit/Loss will be booked",
|
||||
"fieldname": "closing_account_head",
|
||||
"fieldtype": "Link",
|
||||
"label": "Closing Account Head",
|
||||
"oldfieldname": "closing_account_head",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Account",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "remarks",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Remarks",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "remarks",
|
||||
"oldfieldtype": "Small Text",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"fieldname": "remarks",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Remarks",
|
||||
"oldfieldname": "remarks",
|
||||
"oldfieldtype": "Small Text",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "cost_center_wise_pnl",
|
||||
"fieldtype": "Check",
|
||||
"label": "Book Cost Center-Wise Profit/Loss"
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
@@ -291,60 +103,43 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-08-21 16:15:49.089450",
|
||||
"modified": "2021-05-20 15:27:37.210458",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Period Closing Voucher",
|
||||
"owner": "jai@webnotestech.com",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 1,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"amend": 1,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
},
|
||||
{
|
||||
"amend": 1,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"amend": 1,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts Manager",
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"search_fields": "posting_date, fiscal_year",
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "closing_account_head",
|
||||
"track_changes": 0,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
],
|
||||
"search_fields": "posting_date, fiscal_year",
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "closing_account_head"
|
||||
}
|
||||
@@ -50,63 +50,96 @@ class PeriodClosingVoucher(AccountsController):
|
||||
|
||||
def make_gl_entries(self):
|
||||
gl_entries = []
|
||||
net_pl_balance = 0
|
||||
dimension_fields = ['t1.cost_center']
|
||||
net_pl_balance = 0
|
||||
|
||||
accounting_dimensions = get_accounting_dimensions()
|
||||
for dimension in accounting_dimensions:
|
||||
dimension_fields.append('t1.{0}'.format(dimension))
|
||||
|
||||
dimension_filters, default_dimensions = get_dimension_filters()
|
||||
|
||||
pl_accounts = self.get_pl_balances(dimension_fields)
|
||||
pl_accounts = self.get_pl_balances()
|
||||
|
||||
for acc in pl_accounts:
|
||||
if flt(acc.balance_in_company_currency):
|
||||
if flt(acc.bal_in_company_currency):
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
"account": acc.account,
|
||||
"cost_center": acc.cost_center,
|
||||
"account_currency": acc.account_currency,
|
||||
"debit_in_account_currency": abs(flt(acc.balance_in_account_currency)) \
|
||||
if flt(acc.balance_in_account_currency) < 0 else 0,
|
||||
"debit": abs(flt(acc.balance_in_company_currency)) \
|
||||
if flt(acc.balance_in_company_currency) < 0 else 0,
|
||||
"credit_in_account_currency": abs(flt(acc.balance_in_account_currency)) \
|
||||
if flt(acc.balance_in_account_currency) > 0 else 0,
|
||||
"credit": abs(flt(acc.balance_in_company_currency)) \
|
||||
if flt(acc.balance_in_company_currency) > 0 else 0
|
||||
"debit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) < 0 else 0,
|
||||
"debit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) < 0 else 0,
|
||||
"credit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) > 0 else 0,
|
||||
"credit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0
|
||||
}, item=acc))
|
||||
|
||||
net_pl_balance += flt(acc.balance_in_company_currency)
|
||||
net_pl_balance += flt(acc.bal_in_company_currency)
|
||||
|
||||
if net_pl_balance:
|
||||
cost_center = frappe.db.get_value("Company", self.company, "cost_center")
|
||||
gl_entry = self.get_gl_dict({
|
||||
"account": self.closing_account_head,
|
||||
"debit_in_account_currency": abs(net_pl_balance) if net_pl_balance > 0 else 0,
|
||||
"debit": abs(net_pl_balance) if net_pl_balance > 0 else 0,
|
||||
"credit_in_account_currency": abs(net_pl_balance) if net_pl_balance < 0 else 0,
|
||||
"credit": abs(net_pl_balance) if net_pl_balance < 0 else 0,
|
||||
"cost_center": cost_center
|
||||
})
|
||||
|
||||
for dimension in accounting_dimensions:
|
||||
gl_entry.update({
|
||||
dimension: default_dimensions.get(self.company, {}).get(dimension)
|
||||
})
|
||||
|
||||
gl_entries.append(gl_entry)
|
||||
if self.cost_center_wise_pnl:
|
||||
costcenter_wise_gl_entries = self.get_costcenter_wise_pnl_gl_entries(pl_accounts)
|
||||
gl_entries += costcenter_wise_gl_entries
|
||||
else:
|
||||
gl_entry = self.get_pnl_gl_entry(net_pl_balance)
|
||||
gl_entries.append(gl_entry)
|
||||
|
||||
from erpnext.accounts.general_ledger import make_gl_entries
|
||||
make_gl_entries(gl_entries)
|
||||
|
||||
def get_pnl_gl_entry(self, net_pl_balance):
|
||||
cost_center = frappe.db.get_value("Company", self.company, "cost_center")
|
||||
gl_entry = self.get_gl_dict({
|
||||
"account": self.closing_account_head,
|
||||
"debit_in_account_currency": abs(net_pl_balance) if net_pl_balance > 0 else 0,
|
||||
"debit": abs(net_pl_balance) if net_pl_balance > 0 else 0,
|
||||
"credit_in_account_currency": abs(net_pl_balance) if net_pl_balance < 0 else 0,
|
||||
"credit": abs(net_pl_balance) if net_pl_balance < 0 else 0,
|
||||
"cost_center": cost_center
|
||||
})
|
||||
|
||||
self.update_default_dimensions(gl_entry)
|
||||
|
||||
return gl_entry
|
||||
|
||||
def get_costcenter_wise_pnl_gl_entries(self, pl_accounts):
|
||||
company_cost_center = frappe.db.get_value("Company", self.company, "cost_center")
|
||||
gl_entries = []
|
||||
|
||||
for acc in pl_accounts:
|
||||
if flt(acc.bal_in_company_currency):
|
||||
gl_entry = self.get_gl_dict({
|
||||
"account": self.closing_account_head,
|
||||
"cost_center": acc.cost_center or company_cost_center,
|
||||
"account_currency": acc.account_currency,
|
||||
"debit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) > 0 else 0,
|
||||
"debit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0,
|
||||
"credit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) < 0 else 0,
|
||||
"credit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) < 0 else 0
|
||||
}, item=acc)
|
||||
|
||||
self.update_default_dimensions(gl_entry)
|
||||
|
||||
gl_entries.append(gl_entry)
|
||||
|
||||
return gl_entries
|
||||
|
||||
def update_default_dimensions(self, gl_entry):
|
||||
if not self.accounting_dimensions:
|
||||
self.accounting_dimensions = get_accounting_dimensions()
|
||||
|
||||
_, default_dimensions = get_dimension_filters()
|
||||
for dimension in self.accounting_dimensions:
|
||||
gl_entry.update({
|
||||
dimension: default_dimensions.get(self.company, {}).get(dimension)
|
||||
})
|
||||
|
||||
def get_pl_balances(self):
|
||||
"""Get balance for dimension-wise pl accounts"""
|
||||
|
||||
dimension_fields = ['t1.cost_center']
|
||||
|
||||
self.accounting_dimensions = get_accounting_dimensions()
|
||||
for dimension in self.accounting_dimensions:
|
||||
dimension_fields.append('t1.{0}'.format(dimension))
|
||||
|
||||
def get_pl_balances(self, dimension_fields):
|
||||
"""Get balance for pl accounts"""
|
||||
return frappe.db.sql("""
|
||||
select
|
||||
t1.account, t2.account_currency, {dimension_fields},
|
||||
sum(t1.debit_in_account_currency) - sum(t1.credit_in_account_currency) as balance_in_account_currency,
|
||||
sum(t1.debit) - sum(t1.credit) as balance_in_company_currency
|
||||
sum(t1.debit_in_account_currency) - sum(t1.credit_in_account_currency) as bal_in_account_currency,
|
||||
sum(t1.debit) - sum(t1.credit) as bal_in_company_currency
|
||||
from `tabGL Entry` t1, `tabAccount` t2
|
||||
where t1.account = t2.name and t2.report_type = 'Profit and Loss'
|
||||
and t2.docstatus < 2 and t2.company = %s
|
||||
|
||||
@@ -8,6 +8,7 @@ import frappe
|
||||
from frappe.utils import flt, today
|
||||
from erpnext.accounts.utils import get_fiscal_year, now
|
||||
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
|
||||
class TestPeriodClosingVoucher(unittest.TestCase):
|
||||
def test_closing_entry(self):
|
||||
@@ -65,6 +66,58 @@ class TestPeriodClosingVoucher(unittest.TestCase):
|
||||
self.assertEqual(gle_for_random_expense_account[0].amount_in_account_currency,
|
||||
-1*random_expense_account[0].balance_in_account_currency)
|
||||
|
||||
def test_cost_center_wise_posting(self):
|
||||
frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'")
|
||||
|
||||
company = create_company()
|
||||
surplus_account = create_account()
|
||||
|
||||
cost_center1 = create_cost_center("Test Cost Center 1")
|
||||
cost_center2 = create_cost_center("Test Cost Center 2")
|
||||
|
||||
create_sales_invoice(
|
||||
company=company,
|
||||
cost_center=cost_center1,
|
||||
income_account="Sales - TPC",
|
||||
expense_account="Cost of Goods Sold - TPC",
|
||||
rate=400,
|
||||
debit_to="Debtors - TPC"
|
||||
)
|
||||
create_sales_invoice(
|
||||
company=company,
|
||||
cost_center=cost_center2,
|
||||
income_account="Sales - TPC",
|
||||
expense_account="Cost of Goods Sold - TPC",
|
||||
rate=200,
|
||||
debit_to="Debtors - TPC"
|
||||
)
|
||||
|
||||
pcv = frappe.get_doc({
|
||||
"transaction_date": today(),
|
||||
"posting_date": today(),
|
||||
"fiscal_year": get_fiscal_year(today())[0],
|
||||
"company": "Test PCV Company",
|
||||
"cost_center_wise_pnl": 1,
|
||||
"closing_account_head": surplus_account,
|
||||
"remarks": "Test",
|
||||
"doctype": "Period Closing Voucher"
|
||||
})
|
||||
pcv.insert()
|
||||
pcv.submit()
|
||||
|
||||
expected_gle = (
|
||||
('Sales - TPC', 200.0, 0.0, cost_center2),
|
||||
(surplus_account, 0.0, 200.0, cost_center2),
|
||||
('Sales - TPC', 400.0, 0.0, cost_center1),
|
||||
(surplus_account, 0.0, 400.0, cost_center1)
|
||||
)
|
||||
|
||||
pcv_gle = frappe.db.sql("""
|
||||
select account, debit, credit, cost_center from `tabGL Entry` where voucher_no=%s
|
||||
""", (pcv.name))
|
||||
|
||||
self.assertTrue(pcv_gle, expected_gle)
|
||||
|
||||
def make_period_closing_voucher(self):
|
||||
pcv = frappe.get_doc({
|
||||
"doctype": "Period Closing Voucher",
|
||||
@@ -80,6 +133,38 @@ class TestPeriodClosingVoucher(unittest.TestCase):
|
||||
|
||||
return pcv
|
||||
|
||||
def create_company():
|
||||
company = frappe.get_doc({
|
||||
'doctype': 'Company',
|
||||
'company_name': "Test PCV Company",
|
||||
'country': 'United States',
|
||||
'default_currency': 'USD'
|
||||
})
|
||||
company.insert(ignore_if_duplicate = True)
|
||||
return company.name
|
||||
|
||||
def create_account():
|
||||
account = frappe.get_doc({
|
||||
"account_name": "Reserve and Surplus",
|
||||
"is_group": 0,
|
||||
"company": "Test PCV Company",
|
||||
"root_type": "Liability",
|
||||
"report_type": "Balance Sheet",
|
||||
"account_currency": "USD",
|
||||
"parent_account": "Current Liabilities - TPC",
|
||||
"doctype": "Account"
|
||||
}).insert(ignore_if_duplicate = True)
|
||||
return account.name
|
||||
|
||||
def create_cost_center(cc_name):
|
||||
costcenter = frappe.get_doc({
|
||||
"company": "Test PCV Company",
|
||||
"cost_center_name": cc_name,
|
||||
"doctype": "Cost Center",
|
||||
"parent_cost_center": "Test PCV Company - TPC"
|
||||
})
|
||||
costcenter.insert(ignore_if_duplicate = True)
|
||||
return costcenter.name
|
||||
|
||||
test_dependencies = ["Customer", "Cost Center"]
|
||||
test_records = frappe.get_test_records("Period Closing Voucher")
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_rename": 1,
|
||||
"autoname": "Prompt",
|
||||
"creation": "2013-05-24 12:15:51",
|
||||
@@ -22,6 +23,7 @@
|
||||
"allow_user_to_edit_discount",
|
||||
"allow_print_before_pay",
|
||||
"display_items_in_stock",
|
||||
"hide_unavailable_items",
|
||||
"section_break_15",
|
||||
"applicable_for_users",
|
||||
"section_break_11",
|
||||
@@ -389,11 +391,18 @@
|
||||
"fieldtype": "Link",
|
||||
"label": "Tax Category",
|
||||
"options": "Tax Category"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "hide_unavailable_items",
|
||||
"fieldtype": "Check",
|
||||
"label": "Hide Unavailable Items"
|
||||
}
|
||||
],
|
||||
"icon": "icon-cog",
|
||||
"idx": 1,
|
||||
"modified": "2020-01-24 15:52:03.797701",
|
||||
"links": [],
|
||||
"modified": "2020-10-16 04:33:57.283873",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "POS Profile",
|
||||
|
||||
@@ -1,123 +1,39 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2017-10-27 16:46:06.060930",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"actions": [],
|
||||
"creation": "2017-10-27 16:46:06.060930",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"default",
|
||||
"user"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "default",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Default",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"default": "0",
|
||||
"fieldname": "default",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Default"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "user",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "User",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "User",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
"fieldname": "user",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "User",
|
||||
"options": "User"
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-11-23 17:13:16.005475",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "POS Profile User",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-16 04:33:27.594859",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "POS Profile User",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -404,6 +404,7 @@
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:doc.rate_or_discount==\"Rate\"",
|
||||
"fieldname": "rate",
|
||||
"fieldtype": "Currency",
|
||||
@@ -467,6 +468,7 @@
|
||||
"options": "UOM"
|
||||
},
|
||||
{
|
||||
"description": "If rate is zero them item will be treated as \"Free Item\"",
|
||||
"fieldname": "free_item_rate",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Rate"
|
||||
@@ -554,7 +556,8 @@
|
||||
],
|
||||
"icon": "fa fa-gift",
|
||||
"idx": 1,
|
||||
"modified": "2019-12-18 17:29:22.957077",
|
||||
"links": [],
|
||||
"modified": "2020-12-04 00:36:24.698219",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Pricing Rule",
|
||||
|
||||
@@ -49,9 +49,10 @@ class PricingRule(Document):
|
||||
if self.apply_on == apply_on and len(self.get(field) or []) < 1:
|
||||
throw(_("{0} is not added in the table").format(apply_on), frappe.MandatoryError)
|
||||
|
||||
tocheck = frappe.scrub(self.get("applicable_for", ""))
|
||||
if tocheck and not self.get(tocheck):
|
||||
throw(_("{0} is required").format(self.meta.get_label(tocheck)), frappe.MandatoryError)
|
||||
if self.get("applicable_for", "") is not None:
|
||||
tocheck = frappe.scrub(self.get("applicable_for", ""))
|
||||
if tocheck and not self.get(tocheck):
|
||||
throw(_("{0} is required").format(self.meta.get_label(tocheck)), frappe.MandatoryError)
|
||||
|
||||
if self.apply_rule_on_other:
|
||||
o_field = 'other_' + frappe.scrub(self.apply_rule_on_other)
|
||||
@@ -130,7 +131,7 @@ class PricingRule(Document):
|
||||
for d in self.items:
|
||||
max_discount = frappe.get_cached_value("Item", d.item_code, "max_discount")
|
||||
if max_discount and flt(self.discount_percentage) > flt(max_discount):
|
||||
throw(_("Max discount allowed for item: {0} is {1}%").format(self.item_code, max_discount))
|
||||
throw(_("Max discount allowed for item: {0} is {1}%").format(d.item_code, max_discount))
|
||||
|
||||
def validate_price_list_with_currency(self):
|
||||
if self.currency and self.for_price_list:
|
||||
@@ -329,20 +330,30 @@ def get_pricing_rule_details(args, pricing_rule):
|
||||
def apply_price_discount_rule(pricing_rule, item_details, args):
|
||||
item_details.pricing_rule_for = pricing_rule.rate_or_discount
|
||||
|
||||
if ((pricing_rule.margin_type == 'Amount' and pricing_rule.currency == args.currency)
|
||||
or (pricing_rule.margin_type == 'Percentage')):
|
||||
item_details.margin_type = pricing_rule.margin_type
|
||||
item_details.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
|
||||
else:
|
||||
item_details.margin_type = None
|
||||
item_details.margin_rate_or_amount = 0.0
|
||||
for apply_on in ['Percentage', 'Amount']:
|
||||
if pricing_rule.margin_type != apply_on:
|
||||
continue
|
||||
|
||||
field = 'margin_rate_or_amount'
|
||||
if field not in item_details:
|
||||
item_details.setdefault(field, 0)
|
||||
item_details.setdefault('margin_type', apply_on)
|
||||
|
||||
item_details[field] += (pricing_rule.get(field, 0)
|
||||
if pricing_rule else args.get(field, 0))
|
||||
|
||||
if pricing_rule.rate_or_discount == 'Rate':
|
||||
pricing_rule_rate = 0.0
|
||||
if pricing_rule.currency == args.currency:
|
||||
pricing_rule_rate = pricing_rule.rate
|
||||
|
||||
if pricing_rule_rate:
|
||||
# Override already set price list rate (from item price)
|
||||
# if pricing_rule_rate > 0
|
||||
item_details.update({
|
||||
"price_list_rate": pricing_rule_rate * args.get("conversion_factor", 1),
|
||||
})
|
||||
item_details.update({
|
||||
"price_list_rate": pricing_rule_rate * args.get("conversion_factor", 1),
|
||||
"discount_percentage": 0.0
|
||||
})
|
||||
|
||||
|
||||
@@ -385,7 +385,7 @@ class TestPricingRule(unittest.TestCase):
|
||||
so.load_from_db()
|
||||
self.assertEqual(so.items[1].is_free_item, 1)
|
||||
self.assertEqual(so.items[1].item_code, "_Test Item 2")
|
||||
|
||||
|
||||
def test_cumulative_pricing_rule(self):
|
||||
frappe.delete_doc_if_exists('Pricing Rule', '_Test Cumulative Pricing Rule')
|
||||
test_record = {
|
||||
@@ -430,6 +430,59 @@ class TestPricingRule(unittest.TestCase):
|
||||
|
||||
self.assertTrue(details)
|
||||
|
||||
def test_item_price_with_pricing_rule(self):
|
||||
item = make_item("Water Flask")
|
||||
make_item_price("Water Flask", "_Test Price List", 100)
|
||||
|
||||
pricing_rule_record = {
|
||||
"doctype": "Pricing Rule",
|
||||
"title": "_Test Water Flask Rule",
|
||||
"apply_on": "Item Code",
|
||||
"items": [{
|
||||
"item_code": "Water Flask",
|
||||
}],
|
||||
"selling": 1,
|
||||
"currency": "INR",
|
||||
"rate_or_discount": "Rate",
|
||||
"rate": 0,
|
||||
"margin_type": "Percentage",
|
||||
"margin_rate_or_amount": 2,
|
||||
"company": "_Test Company"
|
||||
}
|
||||
rule = frappe.get_doc(pricing_rule_record)
|
||||
rule.insert()
|
||||
|
||||
si = create_sales_invoice(do_not_save=True, item_code="Water Flask")
|
||||
si.selling_price_list = "_Test Price List"
|
||||
si.save()
|
||||
|
||||
# If rate in Rule is 0, give preference to Item Price if it exists
|
||||
self.assertEqual(si.items[0].price_list_rate, 100)
|
||||
self.assertEqual(si.items[0].margin_rate_or_amount, 2)
|
||||
self.assertEqual(si.items[0].rate_with_margin, 102)
|
||||
self.assertEqual(si.items[0].rate, 102)
|
||||
|
||||
si.delete()
|
||||
rule.delete()
|
||||
frappe.get_doc("Item Price", {"item_code": "Water Flask"}).delete()
|
||||
item.delete()
|
||||
|
||||
def test_pricing_rule_for_transaction(self):
|
||||
make_item("Water Flask 1")
|
||||
frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
|
||||
make_pricing_rule(selling=1, min_qty=5, price_or_product_discount="Product",
|
||||
apply_on="Transaction", free_item="Water Flask 1", free_qty=1, free_item_rate=10)
|
||||
|
||||
si = create_sales_invoice(qty=5, do_not_submit=True)
|
||||
self.assertEquals(len(si.items), 2)
|
||||
self.assertEquals(si.items[1].rate, 10)
|
||||
|
||||
si1 = create_sales_invoice(qty=2, do_not_submit=True)
|
||||
self.assertEquals(len(si1.items), 1)
|
||||
|
||||
for doc in [si, si1]:
|
||||
doc.delete()
|
||||
|
||||
def make_pricing_rule(**args):
|
||||
args = frappe._dict(args)
|
||||
|
||||
@@ -447,15 +500,23 @@ def make_pricing_rule(**args):
|
||||
"rate_or_discount": args.rate_or_discount or "Discount Percentage",
|
||||
"discount_percentage": args.discount_percentage or 0.0,
|
||||
"rate": args.rate or 0.0,
|
||||
"margin_type": args.margin_type,
|
||||
"margin_rate_or_amount": args.margin_rate_or_amount or 0.0
|
||||
"margin_rate_or_amount": args.margin_rate_or_amount or 0.0,
|
||||
"condition": args.condition or '',
|
||||
"apply_multiple_pricing_rules": args.apply_multiple_pricing_rules or 0
|
||||
})
|
||||
|
||||
for field in ["free_item", "free_qty", "free_item_rate", "priority",
|
||||
"margin_type", "price_or_product_discount"]:
|
||||
if args.get(field):
|
||||
doc.set(field, args.get(field))
|
||||
|
||||
apply_on = doc.apply_on.replace(' ', '_').lower()
|
||||
child_table = {'Item Code': 'items', 'Item Group': 'item_groups', 'Brand': 'brands'}
|
||||
doc.append(child_table.get(doc.apply_on), {
|
||||
apply_on: args.get(apply_on) or "_Test Item"
|
||||
})
|
||||
|
||||
if doc.apply_on != "Transaction":
|
||||
doc.append(child_table.get(doc.apply_on), {
|
||||
apply_on: args.get(apply_on) or "_Test Item"
|
||||
})
|
||||
|
||||
doc.insert(ignore_permissions=True)
|
||||
if args.get(apply_on) and apply_on != "item_code":
|
||||
|
||||
@@ -453,6 +453,9 @@ def apply_pricing_rule_on_transaction(doc):
|
||||
pricing_rules = filter_pricing_rules_for_qty_amount(doc.total_qty,
|
||||
doc.total, pricing_rules)
|
||||
|
||||
if not pricing_rules:
|
||||
remove_free_item(doc)
|
||||
|
||||
for d in pricing_rules:
|
||||
if d.price_or_product_discount == 'Price':
|
||||
if d.apply_discount_on:
|
||||
@@ -464,7 +467,7 @@ def apply_pricing_rule_on_transaction(doc):
|
||||
|
||||
if not d.get(pr_field): continue
|
||||
|
||||
if d.validate_applied_rule and doc.get(field) < d.get(pr_field):
|
||||
if d.validate_applied_rule and doc.get(field) is not None and doc.get(field) < d.get(pr_field):
|
||||
frappe.msgprint(_("User has not applied rule on the invoice {0}")
|
||||
.format(doc.name))
|
||||
else:
|
||||
@@ -476,6 +479,12 @@ def apply_pricing_rule_on_transaction(doc):
|
||||
get_product_discount_rule(d, item_details, doc=doc)
|
||||
apply_pricing_rule_for_free_items(doc, item_details.free_item_data)
|
||||
doc.set_missing_values()
|
||||
doc.calculate_taxes_and_totals()
|
||||
|
||||
def remove_free_item(doc):
|
||||
for d in doc.items:
|
||||
if d.is_free_item:
|
||||
doc.remove(d)
|
||||
|
||||
def get_applied_pricing_rules(pricing_rules):
|
||||
if pricing_rules:
|
||||
@@ -488,7 +497,7 @@ def get_applied_pricing_rules(pricing_rules):
|
||||
|
||||
def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
|
||||
free_item = pricing_rule.free_item
|
||||
if pricing_rule.same_item:
|
||||
if pricing_rule.same_item and pricing_rule.get("apply_on") != 'Transaction':
|
||||
free_item = item_details.item_code or args.item_code
|
||||
|
||||
if not free_item:
|
||||
@@ -517,13 +526,17 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
|
||||
if item_details.get("parenttype") == 'Sales Order':
|
||||
item_details.free_item_data['delivery_date'] = doc.delivery_date if doc else today()
|
||||
|
||||
company = args.get('company') or doc.company
|
||||
item_details.free_item_data['income_account'] = get_default_income_account(
|
||||
args=args,
|
||||
item=get_item_defaults(free_item, company),
|
||||
item_group=get_item_group_defaults(free_item, company),
|
||||
brand=get_brand_defaults(free_item, company),
|
||||
)
|
||||
company = doc.company
|
||||
if args and args.get("company"):
|
||||
company = args.get("company")
|
||||
|
||||
if args:
|
||||
item_details.free_item_data['income_account'] = get_default_income_account(
|
||||
args=args,
|
||||
item=get_item_defaults(free_item, company),
|
||||
item_group=get_item_group_defaults(free_item, company),
|
||||
brand=get_brand_defaults(free_item, company),
|
||||
)
|
||||
|
||||
def apply_pricing_rule_for_free_items(doc, pricing_rule_args, set_missing_values=False):
|
||||
if pricing_rule_args.get('item_code'):
|
||||
|
||||
@@ -509,7 +509,7 @@ frappe.ui.form.on("Purchase Invoice", {
|
||||
},
|
||||
|
||||
onload: function(frm) {
|
||||
if(frm.doc.__onload) {
|
||||
if(frm.doc.__onload && frm.is_new()) {
|
||||
if(frm.doc.supplier) {
|
||||
frm.doc.apply_tds = frm.doc.__onload.supplier_tds ? 1 : 0;
|
||||
}
|
||||
|
||||
@@ -142,6 +142,11 @@ class PurchaseInvoice(BuyingController):
|
||||
throw(_("Conversion rate cannot be 0 or 1"))
|
||||
|
||||
def validate_credit_to_acc(self):
|
||||
if not self.credit_to:
|
||||
self.credit_to = get_party_account("Supplier", self.supplier, self.company)
|
||||
if not self.credit_to:
|
||||
self.raise_missing_debit_credit_account_error("Supplier", self.supplier)
|
||||
|
||||
account = frappe.db.get_value("Account", self.credit_to,
|
||||
["account_type", "report_type", "account_currency"], as_dict=True)
|
||||
|
||||
@@ -242,8 +247,15 @@ class PurchaseInvoice(BuyingController):
|
||||
else:
|
||||
item.expense_account = stock_not_billed_account
|
||||
elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category):
|
||||
item.expense_account = get_asset_category_account('fixed_asset_account', item=item.item_code,
|
||||
asset_category_account = get_asset_category_account('fixed_asset_account', item=item.item_code,
|
||||
company = self.company)
|
||||
if not asset_category_account:
|
||||
form_link = get_link_to_form('Asset Category', asset_category)
|
||||
throw(
|
||||
_("Please set Fixed Asset Account in {} against {}.").format(form_link, self.company),
|
||||
title=_("Missing Account")
|
||||
)
|
||||
item.expense_account = asset_category_account
|
||||
elif item.is_fixed_asset and item.pr_detail:
|
||||
item.expense_account = asset_received_but_not_billed
|
||||
elif not item.expense_account and for_validate:
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
}
|
||||
],
|
||||
"grand_total": 0,
|
||||
"naming_series": "_T-BILL",
|
||||
"naming_series": "T-PINV-",
|
||||
"taxes": [
|
||||
{
|
||||
"account_head": "_Test Account Shipping Charges - _TC",
|
||||
@@ -168,7 +168,7 @@
|
||||
}
|
||||
],
|
||||
"grand_total": 0,
|
||||
"naming_series": "_T-Purchase Invoice-",
|
||||
"naming_series": "T-PINV-",
|
||||
"taxes": [
|
||||
{
|
||||
"account_head": "_Test Account Shipping Charges - _TC",
|
||||
|
||||
@@ -153,8 +153,8 @@ def update_multi_mode_option(doc, pos_profile):
|
||||
|
||||
def get_mode_of_payment(doc):
|
||||
return frappe.db.sql("""
|
||||
select mpa.default_account, mpa.parent, mp.type as type
|
||||
from `tabMode of Payment Account` mpa,`tabMode of Payment` mp
|
||||
select mpa.default_account, mpa.parent, mp.type as type
|
||||
from `tabMode of Payment Account` mpa,`tabMode of Payment` mp
|
||||
where mpa.parent = mp.name and mpa.company = %(company)s and mp.enabled = 1""",
|
||||
{'company': doc.company}, as_dict=1)
|
||||
|
||||
@@ -175,6 +175,13 @@ def get_items_list(pos_profile, company):
|
||||
if args_list:
|
||||
cond = "and i.item_group in (%s)" % (', '.join(['%s'] * len(args_list)))
|
||||
|
||||
bin_join = bin_cond = ""
|
||||
if pos_profile.get('hide_unavailable_items'):
|
||||
bin_join = ",`tabBin` b"
|
||||
bin_cond = "and i.item_code = b.item_code and ifnull(b.actual_qty, 0) > 0 "
|
||||
if pos_profile.get('warehouse'):
|
||||
bin_cond += "and b.warehouse = {}".format(frappe.db.escape(pos_profile.get('warehouse')))
|
||||
|
||||
return frappe.db.sql("""
|
||||
select
|
||||
i.name, i.item_code, i.item_name, i.description, i.item_group, i.has_batch_no,
|
||||
@@ -186,11 +193,13 @@ def get_items_list(pos_profile, company):
|
||||
left join `tabItem Default` id on id.parent = i.name and id.company = %s
|
||||
left join `tabItem Tax` it on it.parent = i.name
|
||||
left join `tabUOM Conversion Detail` c on i.name = c.parent and i.sales_uom = c.uom
|
||||
{bin_join}
|
||||
where
|
||||
i.disabled = 0 and i.has_variants = 0 and i.is_sales_item = 1
|
||||
{cond}
|
||||
{bin_cond}
|
||||
group by i.item_code
|
||||
""".format(cond=cond), tuple([company] + args_list), as_dict=1)
|
||||
""".format(cond=cond, bin_join=bin_join, bin_cond=bin_cond), tuple([company] + args_list), as_dict=1)
|
||||
|
||||
|
||||
def get_item_groups(pos_profile):
|
||||
@@ -385,6 +394,14 @@ def get_pricing_rule_data(doc):
|
||||
between ifnull(valid_from, '2000-01-01') and ifnull(valid_upto, '2500-12-31')
|
||||
order by priority desc, name desc""",
|
||||
{'company': doc.company, 'price_list': doc.selling_price_list, 'date': nowdate()}, as_dict=1)
|
||||
|
||||
for row in pricing_rules:
|
||||
if row.apply_on:
|
||||
doctype = "Pricing Rule " + row.apply_on
|
||||
apply_on = frappe.scrub(row.apply_on)
|
||||
row[apply_on] = [d.get(apply_on) for d in frappe.get_all(doctype,
|
||||
filters = {"parent": row.name}, fields = [apply_on])]
|
||||
|
||||
return pricing_rules
|
||||
|
||||
|
||||
@@ -425,10 +442,10 @@ def make_invoice(pos_profile, doc_list={}, email_queue_list={}, customers_list={
|
||||
name_list.append(name)
|
||||
|
||||
email_queue = make_email_queue(email_queue_list)
|
||||
|
||||
|
||||
if isinstance(pos_profile, string_types):
|
||||
pos_profile = json.loads(pos_profile)
|
||||
|
||||
|
||||
customers = get_customers_list(pos_profile)
|
||||
return {
|
||||
'invoice': name_list,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
{% include "erpnext/regional/india/taxes.js" %}
|
||||
{% include "erpnext/regional/india/e_invoice/einvoice.js" %}
|
||||
|
||||
erpnext.setup_auto_gst_taxation('Sales Invoice');
|
||||
erpnext.setup_einvoice_actions('Sales Invoice')
|
||||
|
||||
frappe.ui.form.on("Sales Invoice", {
|
||||
setup: function(frm) {
|
||||
@@ -12,6 +14,16 @@ frappe.ui.form.on("Sales Invoice", {
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query('transporter_address', function (doc) {
|
||||
return {
|
||||
query: 'frappe.contacts.doctype.address.address.address_query',
|
||||
filters: {
|
||||
link_doctype: 'Supplier',
|
||||
link_name: doc.transporter
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
frm.set_query('driver', function(doc) {
|
||||
return {
|
||||
filters: {
|
||||
|
||||
@@ -825,45 +825,43 @@ frappe.ui.form.on('Sales Invoice', {
|
||||
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.create_invoice_discounting",
|
||||
frm: frm
|
||||
});
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
frappe.ui.form.on('Sales Invoice Timesheet', {
|
||||
calculate_timesheet_totals: function(frm) {
|
||||
frm.set_value("total_billing_amount",
|
||||
frm.doc.timesheets.reduce((a, b) => a + (b["billing_amount"] || 0.0), 0.0));
|
||||
frm.set_value("total_billing_hours",
|
||||
frm.doc.timesheets.reduce((a, b) => a + (b["billing_hours"] || 0.0), 0.0));
|
||||
}
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Sales Invoice Timesheet", {
|
||||
time_sheet: function(frm, cdt, cdn){
|
||||
var d = locals[cdt][cdn];
|
||||
if(d.time_sheet) {
|
||||
frappe.call({
|
||||
method: "erpnext.projects.doctype.timesheet.timesheet.get_timesheet_data",
|
||||
args: {
|
||||
'name': d.time_sheet,
|
||||
'project': frm.doc.project || null
|
||||
"name": d.time_sheet,
|
||||
"project": frm.doc.project || null
|
||||
},
|
||||
callback: function(r, rt) {
|
||||
callback: function(r) {
|
||||
if(r.message){
|
||||
data = r.message;
|
||||
frappe.model.set_value(cdt, cdn, "billing_hours", data.billing_hours);
|
||||
frappe.model.set_value(cdt, cdn, "billing_amount", data.billing_amount);
|
||||
frappe.model.set_value(cdt, cdn, "timesheet_detail", data.timesheet_detail);
|
||||
calculate_total_billing_amount(frm)
|
||||
frappe.model.set_value(cdt, cdn, "billing_hours", r.message.billing_hours);
|
||||
frappe.model.set_value(cdt, cdn, "billing_amount", r.message.billing_amount);
|
||||
frappe.model.set_value(cdt, cdn, "timesheet_detail", r.message.timesheet_detail);
|
||||
frm.trigger("calculate_timesheet_totals");
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
timesheets_remove: function(frm, cdt, cdn) {
|
||||
frm.trigger("calculate_timesheet_totals");
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
var calculate_total_billing_amount = function(frm) {
|
||||
var doc = frm.doc;
|
||||
|
||||
doc.total_billing_amount = 0.0
|
||||
if(doc.timesheets) {
|
||||
$.each(doc.timesheets, function(index, data){
|
||||
doc.total_billing_amount += data.billing_amount
|
||||
})
|
||||
}
|
||||
|
||||
refresh_field('total_billing_amount')
|
||||
}
|
||||
|
||||
var select_loyalty_program = function(frm, loyalty_programs) {
|
||||
var dialog = new frappe.ui.Dialog({
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
"time_sheet_list",
|
||||
"timesheets",
|
||||
"total_billing_amount",
|
||||
"total_billing_hours",
|
||||
"section_break_30",
|
||||
"total_qty",
|
||||
"base_total",
|
||||
@@ -1564,12 +1565,20 @@
|
||||
{
|
||||
"fieldname": "dimension_col_break",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "total_billing_hours",
|
||||
"fieldtype": "Float",
|
||||
"label": "Total Billing Hours",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-file-text",
|
||||
"idx": 181,
|
||||
"is_submittable": 1,
|
||||
"modified": "2020-07-01 12:41:29.484813",
|
||||
"modified": "2021-07-26 14:01:34.605644",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe, erpnext
|
||||
import frappe.defaults
|
||||
from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days, cstr, nowdate
|
||||
from frappe.utils import cint, flt, getdate, add_days, cstr, nowdate, formatdate
|
||||
from frappe import _, msgprint, throw
|
||||
from erpnext.accounts.party import get_party_account, get_due_date
|
||||
from erpnext.controllers.stock_controller import update_gl_entries_after
|
||||
@@ -225,9 +225,9 @@ class SalesInvoice(SellingController):
|
||||
frappe.throw(_("At least one mode of payment is required for POS invoice."))
|
||||
|
||||
def before_cancel(self):
|
||||
super(SalesInvoice, self).before_cancel()
|
||||
self.update_time_sheet(None)
|
||||
|
||||
|
||||
def on_cancel(self):
|
||||
super(SalesInvoice, self).on_cancel()
|
||||
|
||||
@@ -398,6 +398,8 @@ class SalesInvoice(SellingController):
|
||||
from erpnext.stock.get_item_details import get_pos_profile_item_details, get_pos_profile
|
||||
if not self.pos_profile:
|
||||
pos_profile = get_pos_profile(self.company) or {}
|
||||
if not pos_profile:
|
||||
frappe.throw(_("No POS Profile found. Please create a New POS Profile first"))
|
||||
self.pos_profile = pos_profile.get('name')
|
||||
|
||||
pos = {}
|
||||
@@ -448,7 +450,7 @@ class SalesInvoice(SellingController):
|
||||
# set pos values in items
|
||||
for item in self.get("items"):
|
||||
if item.get('item_code'):
|
||||
profile_details = get_pos_profile_item_details(pos, frappe._dict(item.as_dict()), pos)
|
||||
profile_details = get_pos_profile_item_details(pos, frappe._dict(item.as_dict()), pos, update_data=True)
|
||||
for fname, val in iteritems(profile_details):
|
||||
if (not for_validate) or (for_validate and not item.get(fname)):
|
||||
item.set(fname, val)
|
||||
@@ -467,6 +469,11 @@ class SalesInvoice(SellingController):
|
||||
return frappe.db.sql("select abbr from tabCompany where name=%s", self.company)[0][0]
|
||||
|
||||
def validate_debit_to_acc(self):
|
||||
if not self.debit_to:
|
||||
self.debit_to = get_party_account("Customer", self.customer, self.company)
|
||||
if not self.debit_to:
|
||||
self.raise_missing_debit_credit_account_error("Customer", self.customer)
|
||||
|
||||
account = frappe.get_cached_value("Account", self.debit_to,
|
||||
["account_type", "report_type", "account_currency"], as_dict=True)
|
||||
|
||||
@@ -530,7 +537,12 @@ class SalesInvoice(SellingController):
|
||||
self.against_income_account = ','.join(against_acc)
|
||||
|
||||
def add_remarks(self):
|
||||
if not self.remarks: self.remarks = 'No Remarks'
|
||||
if not self.remarks:
|
||||
if self.po_no and self.po_date:
|
||||
self.remarks = _("Against Customer Order {0} dated {1}").format(self.po_no,
|
||||
formatdate(self.po_date))
|
||||
else:
|
||||
self.remarks = _("No Remarks")
|
||||
|
||||
def validate_auto_set_posting_time(self):
|
||||
# Don't auto set the posting date and time if invoice is amended
|
||||
@@ -570,7 +582,8 @@ class SalesInvoice(SellingController):
|
||||
|
||||
def validate_pos(self):
|
||||
if self.is_return:
|
||||
if flt(self.paid_amount) + flt(self.write_off_amount) - flt(self.grand_total) > \
|
||||
invoice_total = self.rounded_total or self.grand_total
|
||||
if flt(self.paid_amount) + flt(self.write_off_amount) - flt(invoice_total) > \
|
||||
1.0/(10.0**(self.precision("grand_total") + 1.0)):
|
||||
frappe.throw(_("Paid amount + Write Off Amount can not be greater than Grand Total"))
|
||||
|
||||
@@ -670,12 +683,11 @@ class SalesInvoice(SellingController):
|
||||
self.calculate_billing_amount_for_timesheet()
|
||||
|
||||
def calculate_billing_amount_for_timesheet(self):
|
||||
total_billing_amount = 0.0
|
||||
for data in self.timesheets:
|
||||
if data.billing_amount:
|
||||
total_billing_amount += data.billing_amount
|
||||
def timesheet_sum(field):
|
||||
return sum((ts.get(field) or 0.0) for ts in self.timesheets)
|
||||
|
||||
self.total_billing_amount = total_billing_amount
|
||||
self.total_billing_amount = timesheet_sum("billing_amount")
|
||||
self.total_billing_hours = timesheet_sum("billing_hours")
|
||||
|
||||
def get_warehouse(self):
|
||||
user_pos_profile = frappe.db.sql("""select name, warehouse from `tabPOS Profile`
|
||||
@@ -1394,6 +1406,7 @@ def make_delivery_note(source_name, target_doc=None):
|
||||
def set_missing_values(source, target):
|
||||
target.ignore_pricing_rule = 1
|
||||
target.run_method("set_missing_values")
|
||||
target.run_method("set_po_nos")
|
||||
target.run_method("calculate_taxes_and_totals")
|
||||
|
||||
def update_item(source_doc, target_doc, source_parent):
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"base_grand_total": 561.8,
|
||||
"grand_total": 561.8,
|
||||
"is_pos": 0,
|
||||
"naming_series": "_T-Sales Invoice-",
|
||||
"naming_series": "T-SINV-",
|
||||
"base_net_total": 500.0,
|
||||
"taxes": [
|
||||
{
|
||||
@@ -103,7 +103,7 @@
|
||||
"base_grand_total": 630.0,
|
||||
"grand_total": 630.0,
|
||||
"is_pos": 0,
|
||||
"naming_series": "_T-Sales Invoice-",
|
||||
"naming_series": "T-SINV-",
|
||||
"base_net_total": 500.0,
|
||||
"taxes": [
|
||||
{
|
||||
@@ -174,7 +174,7 @@
|
||||
],
|
||||
"grand_total": 0,
|
||||
"is_pos": 0,
|
||||
"naming_series": "_T-Sales Invoice-",
|
||||
"naming_series": "T-SINV-",
|
||||
"taxes": [
|
||||
{
|
||||
"account_head": "_Test Account Shipping Charges - _TC",
|
||||
@@ -300,7 +300,7 @@
|
||||
],
|
||||
"grand_total": 0,
|
||||
"is_pos": 0,
|
||||
"naming_series": "_T-Sales Invoice-",
|
||||
"naming_series": "T-SINV-",
|
||||
"taxes": [
|
||||
{
|
||||
"account_head": "_Test Account Excise Duty - _TC",
|
||||
|
||||
@@ -690,7 +690,8 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
self.assertFalse(gle)
|
||||
|
||||
def test_pos_gl_entry_with_perpetual_inventory(self):
|
||||
make_pos_profile()
|
||||
make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1",
|
||||
expense_account = "Cost of Goods Sold - TCP1", warehouse="Stores - TCP1", cost_center = "Main - TCP1", write_off_account="_Test Write Off - TCP1")
|
||||
|
||||
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",supplier_warehouse= "Work In Progress - TCP1", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
|
||||
|
||||
@@ -773,7 +774,8 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
|
||||
def test_pos_change_amount(self):
|
||||
make_pos_profile()
|
||||
make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1",
|
||||
expense_account = "Cost of Goods Sold - TCP1", warehouse="Stores - TCP1", cost_center = "Main - TCP1", write_off_account="_Test Write Off - TCP1")
|
||||
|
||||
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",supplier_warehouse= "Work In Progress - TCP1", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
|
||||
|
||||
@@ -795,7 +797,8 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
def test_make_pos_invoice(self):
|
||||
from erpnext.accounts.doctype.sales_invoice.pos import make_invoice
|
||||
|
||||
pos_profile = make_pos_profile()
|
||||
pos_profile = make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1",
|
||||
expense_account = "Cost of Goods Sold - TCP1", warehouse="Stores - TCP1", cost_center = "Main - TCP1", write_off_account="_Test Write Off - TCP1")
|
||||
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",supplier_warehouse= "Work In Progress - TCP1", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
|
||||
pos = create_sales_invoice(company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1", do_not_save=True)
|
||||
|
||||
@@ -1838,93 +1841,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
self.assertEqual(target_doc.supplier, "_Test Internal Supplier")
|
||||
|
||||
def test_eway_bill_json(self):
|
||||
if not frappe.db.exists('Address', '_Test Address for Eway bill-Billing'):
|
||||
address = frappe.get_doc({
|
||||
"address_line1": "_Test Address Line 1",
|
||||
"address_title": "_Test Address for Eway bill",
|
||||
"address_type": "Billing",
|
||||
"city": "_Test City",
|
||||
"state": "Test State",
|
||||
"country": "India",
|
||||
"doctype": "Address",
|
||||
"is_primary_address": 1,
|
||||
"phone": "+91 0000000000",
|
||||
"gstin": "27AAECE4835E1ZR",
|
||||
"gst_state": "Maharashtra",
|
||||
"gst_state_number": "27",
|
||||
"pincode": "401108"
|
||||
}).insert()
|
||||
|
||||
address.append("links", {
|
||||
"link_doctype": "Company",
|
||||
"link_name": "_Test Company"
|
||||
})
|
||||
|
||||
address.save()
|
||||
|
||||
if not frappe.db.exists('Address', '_Test Customer-Address for Eway bill-Shipping'):
|
||||
address = frappe.get_doc({
|
||||
"address_line1": "_Test Address Line 1",
|
||||
"address_title": "_Test Customer-Address for Eway bill",
|
||||
"address_type": "Shipping",
|
||||
"city": "_Test City",
|
||||
"state": "Test State",
|
||||
"country": "India",
|
||||
"doctype": "Address",
|
||||
"is_primary_address": 1,
|
||||
"phone": "+91 0000000000",
|
||||
"gst_state": "Maharashtra",
|
||||
"gst_state_number": "27",
|
||||
"pincode": "410038"
|
||||
}).insert()
|
||||
|
||||
address.append("links", {
|
||||
"link_doctype": "Customer",
|
||||
"link_name": "_Test Customer"
|
||||
})
|
||||
|
||||
address.save()
|
||||
|
||||
gst_settings = frappe.get_doc("GST Settings")
|
||||
|
||||
gst_account = frappe.get_all(
|
||||
"GST Account",
|
||||
fields=["cgst_account", "sgst_account", "igst_account"],
|
||||
filters = {"company": "_Test Company"})
|
||||
|
||||
if not gst_account:
|
||||
gst_settings.append("gst_accounts", {
|
||||
"company": "_Test Company",
|
||||
"cgst_account": "CGST - _TC",
|
||||
"sgst_account": "SGST - _TC",
|
||||
"igst_account": "IGST - _TC",
|
||||
})
|
||||
|
||||
gst_settings.save()
|
||||
|
||||
si = create_sales_invoice(do_not_save =1, rate = '60000')
|
||||
|
||||
si.distance = 2000
|
||||
si.company_address = "_Test Address for Eway bill-Billing"
|
||||
si.customer_address = "_Test Customer-Address for Eway bill-Shipping"
|
||||
si.vehicle_no = "KA12KA1234"
|
||||
si.gst_category = "Registered Regular"
|
||||
|
||||
si.append("taxes", {
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "CGST - _TC",
|
||||
"cost_center": "Main - _TC",
|
||||
"description": "CGST @ 9.0",
|
||||
"rate": 9
|
||||
})
|
||||
|
||||
si.append("taxes", {
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "SGST - _TC",
|
||||
"cost_center": "Main - _TC",
|
||||
"description": "SGST @ 9.0",
|
||||
"rate": 9
|
||||
})
|
||||
si = make_sales_invoice_for_ewaybill()
|
||||
|
||||
si.submit()
|
||||
|
||||
@@ -1940,27 +1857,206 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
self.assertEqual(data['billLists'][0]['sgstValue'], 5400)
|
||||
self.assertEqual(data['billLists'][0]['vehicleNo'], 'KA12KA1234')
|
||||
self.assertEqual(data['billLists'][0]['itemList'][0]['taxableAmount'], 60000)
|
||||
|
||||
def test_einvoice_submission_without_irn(self):
|
||||
# init
|
||||
einvoice_settings = frappe.get_doc('E Invoice Settings')
|
||||
einvoice_settings.enable = 1
|
||||
einvoice_settings.applicable_from = nowdate()
|
||||
einvoice_settings.append('credentials', {
|
||||
'company': '_Test Company',
|
||||
'gstin': '27AAECE4835E1ZR',
|
||||
'username': 'test',
|
||||
'password': 'test'
|
||||
})
|
||||
einvoice_settings.save()
|
||||
|
||||
def test_item_tax_validity(self):
|
||||
item = frappe.get_doc("Item", "_Test Item 2")
|
||||
country = frappe.flags.country
|
||||
frappe.flags.country = 'India'
|
||||
|
||||
if item.taxes:
|
||||
item.taxes = []
|
||||
item.save()
|
||||
si = make_sales_invoice_for_ewaybill()
|
||||
self.assertRaises(frappe.ValidationError, si.submit)
|
||||
|
||||
item.append("taxes", {
|
||||
"item_tax_template": "_Test Item Tax Template 1",
|
||||
"valid_from": add_days(nowdate(), 1)
|
||||
si.irn = 'test_irn'
|
||||
si.submit()
|
||||
|
||||
# reset
|
||||
einvoice_settings = frappe.get_doc('E Invoice Settings')
|
||||
einvoice_settings.enable = 0
|
||||
frappe.flags.country = country
|
||||
|
||||
def test_einvoice_json(self):
|
||||
from erpnext.regional.india.e_invoice.utils import make_einvoice
|
||||
|
||||
si = make_sales_invoice_for_ewaybill()
|
||||
si.naming_series = 'INV-2020-.#####'
|
||||
si.items = []
|
||||
si.append("items", {
|
||||
"item_code": "_Test Item",
|
||||
"uom": "Nos",
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
"qty": 2000,
|
||||
"rate": 12,
|
||||
"income_account": "Sales - _TC",
|
||||
"expense_account": "Cost of Goods Sold - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
})
|
||||
si.append("items", {
|
||||
"item_code": "_Test Item 2",
|
||||
"uom": "Nos",
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
"qty": 420,
|
||||
"rate": 15,
|
||||
"income_account": "Sales - _TC",
|
||||
"expense_account": "Cost of Goods Sold - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
})
|
||||
si.discount_amount = 100
|
||||
si.save()
|
||||
|
||||
einvoice = make_einvoice(si)
|
||||
|
||||
total_item_ass_value = 0
|
||||
total_item_cgst_value = 0
|
||||
total_item_sgst_value = 0
|
||||
total_item_igst_value = 0
|
||||
total_item_value = 0
|
||||
|
||||
for item in einvoice['ItemList']:
|
||||
total_item_ass_value += item['AssAmt']
|
||||
total_item_cgst_value += item['CgstAmt']
|
||||
total_item_sgst_value += item['SgstAmt']
|
||||
total_item_igst_value += item['IgstAmt']
|
||||
total_item_value += item['TotItemVal']
|
||||
|
||||
self.assertTrue(item['AssAmt'], item['TotAmt'] - item['Discount'])
|
||||
self.assertTrue(item['TotItemVal'], item['AssAmt'] + item['CgstAmt'] + item['SgstAmt'] + item['IgstAmt'])
|
||||
|
||||
value_details = einvoice['ValDtls']
|
||||
|
||||
self.assertEqual(einvoice['Version'], '1.1')
|
||||
self.assertTrue(abs(value_details['AssVal'] - total_item_ass_value) <= 1)
|
||||
self.assertTrue(abs(value_details['CgstVal'] - total_item_cgst_value) <= 1)
|
||||
self.assertTrue(abs(value_details['SgstVal'] - total_item_sgst_value) <= 1)
|
||||
self.assertTrue(abs(value_details['IgstVal'] - total_item_igst_value) <= 1)
|
||||
|
||||
calculated_invoice_value = \
|
||||
value_details['AssVal'] + value_details['CgstVal'] \
|
||||
+ value_details['SgstVal'] + value_details['IgstVal'] \
|
||||
+ value_details['OthChrg'] - value_details['Discount']
|
||||
|
||||
self.assertTrue(abs(value_details['TotInvVal'] - calculated_invoice_value) <= 1)
|
||||
self.assertTrue(einvoice['EwbDtls'])
|
||||
|
||||
def make_test_address_for_ewaybill():
|
||||
if not frappe.db.exists('Address', '_Test Address for Eway bill-Billing'):
|
||||
address = frappe.get_doc({
|
||||
"address_line1": "_Test Address Line 1",
|
||||
"address_title": "_Test Address for Eway bill",
|
||||
"address_type": "Billing",
|
||||
"city": "_Test City",
|
||||
"state": "Test State",
|
||||
"country": "India",
|
||||
"doctype": "Address",
|
||||
"is_primary_address": 1,
|
||||
"phone": "+910000000000",
|
||||
"gstin": "27AAECE4835E1ZR",
|
||||
"gst_state": "Maharashtra",
|
||||
"gst_state_number": "27",
|
||||
"pincode": "401108"
|
||||
}).insert()
|
||||
|
||||
address.append("links", {
|
||||
"link_doctype": "Company",
|
||||
"link_name": "_Test Company"
|
||||
})
|
||||
|
||||
item.save()
|
||||
address.save()
|
||||
|
||||
sales_invoice = create_sales_invoice(item = "_Test Item 2", do_not_save=1)
|
||||
sales_invoice.items[0].item_tax_template = "_Test Item Tax Template 1"
|
||||
self.assertRaises(frappe.ValidationError, sales_invoice.save)
|
||||
if not frappe.db.exists('Address', '_Test Customer-Address for Eway bill-Shipping'):
|
||||
address = frappe.get_doc({
|
||||
"address_line1": "_Test Address Line 1",
|
||||
"address_title": "_Test Customer-Address for Eway bill",
|
||||
"address_type": "Shipping",
|
||||
"city": "_Test City",
|
||||
"state": "Test State",
|
||||
"country": "India",
|
||||
"doctype": "Address",
|
||||
"is_primary_address": 1,
|
||||
"phone": "+910000000000",
|
||||
"gstin": "27AACCM7806M1Z3",
|
||||
"gst_state": "Maharashtra",
|
||||
"gst_state_number": "27",
|
||||
"pincode": "410038"
|
||||
}).insert()
|
||||
|
||||
item.taxes = []
|
||||
item.save()
|
||||
address.append("links", {
|
||||
"link_doctype": "Customer",
|
||||
"link_name": "_Test Customer"
|
||||
})
|
||||
|
||||
address.save()
|
||||
|
||||
def make_test_transporter_for_ewaybill():
|
||||
if not frappe.db.exists('Supplier', '_Test Transporter'):
|
||||
frappe.get_doc({
|
||||
"doctype": "Supplier",
|
||||
"supplier_name": "_Test Transporter",
|
||||
"country": "India",
|
||||
"supplier_group": "_Test Supplier Group",
|
||||
"supplier_type": "Company",
|
||||
"is_transporter": 1
|
||||
}).insert()
|
||||
|
||||
def make_sales_invoice_for_ewaybill():
|
||||
make_test_address_for_ewaybill()
|
||||
make_test_transporter_for_ewaybill()
|
||||
|
||||
gst_settings = frappe.get_doc("GST Settings")
|
||||
|
||||
gst_account = frappe.get_all(
|
||||
"GST Account",
|
||||
fields=["cgst_account", "sgst_account", "igst_account"],
|
||||
filters = {"company": "_Test Company"}
|
||||
)
|
||||
|
||||
if not gst_account:
|
||||
gst_settings.append("gst_accounts", {
|
||||
"company": "_Test Company",
|
||||
"cgst_account": "CGST - _TC",
|
||||
"sgst_account": "SGST - _TC",
|
||||
"igst_account": "IGST - _TC",
|
||||
})
|
||||
|
||||
gst_settings.save()
|
||||
|
||||
si = create_sales_invoice(do_not_save=1, rate='60000')
|
||||
|
||||
si.distance = 2000
|
||||
si.company_address = "_Test Address for Eway bill-Billing"
|
||||
si.customer_address = "_Test Customer-Address for Eway bill-Shipping"
|
||||
si.vehicle_no = "KA12KA1234"
|
||||
si.gst_category = "Registered Regular"
|
||||
si.mode_of_transport = 'Road'
|
||||
si.transporter = '_Test Transporter'
|
||||
|
||||
si.append("taxes", {
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "CGST - _TC",
|
||||
"cost_center": "Main - _TC",
|
||||
"description": "CGST @ 9.0",
|
||||
"rate": 9
|
||||
})
|
||||
|
||||
si.append("taxes", {
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "SGST - _TC",
|
||||
"cost_center": "Main - _TC",
|
||||
"description": "SGST @ 9.0",
|
||||
"rate": 9
|
||||
})
|
||||
|
||||
return si
|
||||
|
||||
def create_sales_invoice(**args):
|
||||
si = frappe.new_doc("Sales Invoice")
|
||||
@@ -1978,6 +2074,7 @@ def create_sales_invoice(**args):
|
||||
si.return_against = args.return_against
|
||||
si.currency=args.currency or "INR"
|
||||
si.conversion_rate = args.conversion_rate or 1
|
||||
si.naming_series = args.naming_series or "T-SINV-"
|
||||
|
||||
si.append("items", {
|
||||
"item_code": args.item or args.item_code or "_Test Item",
|
||||
|
||||
@@ -34,6 +34,9 @@ def valdiate_taxes_and_charges_template(doc):
|
||||
|
||||
validate_disabled(doc)
|
||||
|
||||
# Validate with existing taxes and charges template for unique tax category
|
||||
validate_for_tax_category(doc)
|
||||
|
||||
for tax in doc.get("taxes"):
|
||||
validate_taxes_and_charges(tax)
|
||||
validate_inclusive_tax(tax, doc)
|
||||
@@ -41,3 +44,7 @@ def valdiate_taxes_and_charges_template(doc):
|
||||
def validate_disabled(doc):
|
||||
if doc.is_default and doc.disabled:
|
||||
frappe.throw(_("Disabled template must not be default template"))
|
||||
|
||||
def validate_for_tax_category(doc):
|
||||
if frappe.db.exists(doc.doctype, {"company": doc.company, "tax_category": doc.tax_category, "disabled": 0}):
|
||||
frappe.throw(_("A template with tax category {0} already exists. Only one template is allowed with each tax category").format(frappe.bold(doc.tax_category)))
|
||||
|
||||
@@ -169,7 +169,7 @@ class ShareTransfer(Document):
|
||||
|
||||
def folio_no_validation(self):
|
||||
shareholders = ['from_shareholder', 'to_shareholder']
|
||||
shareholders = [shareholder for shareholder in shareholders if self.get(shareholder) is not '']
|
||||
shareholders = [shareholder for shareholder in shareholders if self.get(shareholder) != '']
|
||||
for shareholder in shareholders:
|
||||
doc = self.get_shareholder_doc(self.get(shareholder))
|
||||
if doc.company != self.company:
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"cancelation_date",
|
||||
"trial_period_start",
|
||||
"trial_period_end",
|
||||
"generate_new_invoices_past_due_date",
|
||||
"column_break_11",
|
||||
"current_invoice_start",
|
||||
"current_invoice_end",
|
||||
@@ -29,6 +30,7 @@
|
||||
"additional_discount_percentage",
|
||||
"additional_discount_amount",
|
||||
"sb_3",
|
||||
"submit_invoice",
|
||||
"invoices",
|
||||
"accounting_dimensions_section",
|
||||
"dimension_col_break"
|
||||
@@ -183,8 +185,7 @@
|
||||
"fieldname": "invoices",
|
||||
"fieldtype": "Table",
|
||||
"label": "Invoices",
|
||||
"options": "Subscription Invoice",
|
||||
"read_only": 1
|
||||
"options": "Subscription Invoice"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
@@ -195,9 +196,22 @@
|
||||
{
|
||||
"fieldname": "dimension_col_break",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "New invoices will be generated as per schedule even if current invoices are unpaid or past due date",
|
||||
"fieldname": "generate_new_invoices_past_due_date",
|
||||
"fieldtype": "Check",
|
||||
"label": "Generate New Invoices Past Due Date"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "submit_invoice",
|
||||
"fieldtype": "Check",
|
||||
"label": "Submit Invoice Automatically"
|
||||
}
|
||||
],
|
||||
"modified": "2020-08-27 23:30:02.504042",
|
||||
"modified": "2021-05-03 13:35:21.422940",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Subscription",
|
||||
|
||||
@@ -288,8 +288,11 @@ class Subscription(Document):
|
||||
invoice.to_date = self.current_invoice_end
|
||||
|
||||
invoice.flags.ignore_mandatory = True
|
||||
invoice.set_missing_values()
|
||||
invoice.save()
|
||||
invoice.submit()
|
||||
|
||||
if self.submit_invoice:
|
||||
invoice.submit()
|
||||
|
||||
return invoice
|
||||
|
||||
@@ -298,7 +301,8 @@ class Subscription(Document):
|
||||
Returns the `Item`s linked to `Subscription Plan`
|
||||
"""
|
||||
if prorate:
|
||||
prorate_factor = get_prorata_factor(self.current_invoice_end, self.current_invoice_start)
|
||||
prorate_factor = get_prorata_factor(self.current_invoice_end, self.current_invoice_start,
|
||||
self.generate_invoice_at_period_start)
|
||||
|
||||
items = []
|
||||
customer = self.customer
|
||||
@@ -333,7 +337,7 @@ class Subscription(Document):
|
||||
if not self.generate_invoice_at_period_start:
|
||||
return False
|
||||
|
||||
if self.is_new_subscription():
|
||||
if self.is_new_subscription() and getdate(nowdate()) >= getdate(self.current_invoice_start):
|
||||
return True
|
||||
|
||||
# Check invoice dates and make sure it doesn't have outstanding invoices
|
||||
@@ -408,6 +412,15 @@ class Subscription(Document):
|
||||
else:
|
||||
self.set_status_grace_period()
|
||||
|
||||
if getdate() > getdate(self.current_invoice_end):
|
||||
self.update_subscription_period(add_days(self.current_invoice_end, 1))
|
||||
|
||||
# Generate invoices periodically even if current invoice are unpaid
|
||||
if self.generate_new_invoices_past_due_date and not self.is_current_invoice_generated() and (self.is_postpaid_to_invoice()
|
||||
or self.is_prepaid_to_invoice()):
|
||||
prorate = frappe.db.get_single_value('Subscription Settings', 'prorate')
|
||||
self.generate_invoice(prorate)
|
||||
|
||||
@staticmethod
|
||||
def is_not_outstanding(invoice):
|
||||
"""
|
||||
@@ -459,11 +472,13 @@ class Subscription(Document):
|
||||
if invoice:
|
||||
return invoice.precision('grand_total')
|
||||
|
||||
|
||||
def get_prorata_factor(period_end, period_start):
|
||||
diff = flt(date_diff(nowdate(), period_start) + 1)
|
||||
plan_days = flt(date_diff(period_end, period_start) + 1)
|
||||
prorate_factor = diff / plan_days
|
||||
def get_prorata_factor(period_end, period_start, is_prepaid):
|
||||
if is_prepaid:
|
||||
prorate_factor = 1
|
||||
else:
|
||||
diff = flt(date_diff(nowdate(), period_start) + 1)
|
||||
plan_days = flt(date_diff(period_end, period_start) + 1)
|
||||
prorate_factor = diff / plan_days
|
||||
|
||||
return prorate_factor
|
||||
|
||||
|
||||
@@ -209,7 +209,7 @@ class TestSubscription(unittest.TestCase):
|
||||
subscription = frappe.new_doc('Subscription')
|
||||
subscription.customer = '_Test Customer'
|
||||
subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
|
||||
subscription.start = '2018-01-01'
|
||||
subscription.start = add_days(nowdate(), -1000)
|
||||
subscription.days_until_due = 1
|
||||
subscription.insert()
|
||||
subscription.process() # generate first invoice
|
||||
@@ -291,7 +291,8 @@ class TestSubscription(unittest.TestCase):
|
||||
|
||||
self.assertEqual(
|
||||
flt(
|
||||
get_prorata_factor(subscription.current_invoice_end, subscription.current_invoice_start),
|
||||
get_prorata_factor(subscription.current_invoice_end, subscription.current_invoice_start,
|
||||
subscription.generate_invoice_at_period_start),
|
||||
2),
|
||||
flt(prorate_factor, 2)
|
||||
)
|
||||
@@ -528,9 +529,7 @@ class TestSubscription(unittest.TestCase):
|
||||
current_inv = subscription.get_current_invoice()
|
||||
self.assertEqual(current_inv.status, "Unpaid")
|
||||
|
||||
diff = flt(date_diff(nowdate(), subscription.current_invoice_start) + 1)
|
||||
plan_days = flt(date_diff(subscription.current_invoice_end, subscription.current_invoice_start) + 1)
|
||||
prorate_factor = flt(diff / plan_days)
|
||||
prorate_factor = 1
|
||||
|
||||
self.assertEqual(flt(current_inv.grand_total, 2), flt(prorate_factor * 900, 2))
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@ def get_tds_amount(suppliers, net_total, company, tax_details, fiscal_year_detai
|
||||
debit_note_amount = get_debit_note_amount(suppliers, year_start_date, year_end_date)
|
||||
supplier_credit_amount -= debit_note_amount
|
||||
|
||||
if ((tax_details.get('threshold', 0) and supplier_credit_amount >= tax_details.threshold)
|
||||
if ((tax_details.get('threshold', 0) and net_total >= tax_details.threshold)
|
||||
or (tax_details.get('cumulative_threshold', 0) and supplier_credit_amount >= tax_details.cumulative_threshold)):
|
||||
|
||||
if ldc and is_valid_certificate(ldc.valid_from, ldc.valid_upto, posting_date, tds_deducted, net_total,
|
||||
|
||||
@@ -83,47 +83,6 @@ class TestTaxWithholdingCategory(unittest.TestCase):
|
||||
for d in invoices:
|
||||
d.cancel()
|
||||
|
||||
def test_single_threshold_tds_with_previous_vouchers(self):
|
||||
invoices = []
|
||||
frappe.db.set_value("Supplier", "Test TDS Supplier2", "tax_withholding_category", "Single Threshold TDS")
|
||||
pi = create_purchase_invoice(supplier="Test TDS Supplier2")
|
||||
pi.submit()
|
||||
invoices.append(pi)
|
||||
|
||||
pi = create_purchase_invoice(supplier="Test TDS Supplier2")
|
||||
pi.submit()
|
||||
invoices.append(pi)
|
||||
|
||||
self.assertEqual(pi.taxes_and_charges_deducted, 2000)
|
||||
self.assertEqual(pi.grand_total, 8000)
|
||||
|
||||
# delete invoices to avoid clashing
|
||||
for d in invoices:
|
||||
d.cancel()
|
||||
|
||||
def test_single_threshold_tds_with_previous_vouchers_and_no_tds(self):
|
||||
invoices = []
|
||||
frappe.db.set_value("Supplier", "Test TDS Supplier2", "tax_withholding_category", "Single Threshold TDS")
|
||||
pi = create_purchase_invoice(supplier="Test TDS Supplier2")
|
||||
pi.submit()
|
||||
invoices.append(pi)
|
||||
|
||||
# TDS not applied
|
||||
pi = create_purchase_invoice(supplier="Test TDS Supplier2", do_not_apply_tds=True)
|
||||
pi.submit()
|
||||
invoices.append(pi)
|
||||
|
||||
pi = create_purchase_invoice(supplier="Test TDS Supplier2")
|
||||
pi.submit()
|
||||
invoices.append(pi)
|
||||
|
||||
self.assertEqual(pi.taxes_and_charges_deducted, 2000)
|
||||
self.assertEqual(pi.grand_total, 8000)
|
||||
|
||||
# delete invoices to avoid clashing
|
||||
for d in invoices:
|
||||
d.cancel()
|
||||
|
||||
def create_purchase_invoice(**args):
|
||||
# return sales invoice doc object
|
||||
item = frappe.get_doc('Item', {'item_name': 'TDS Item'})
|
||||
|
||||
@@ -147,7 +147,7 @@ def make_entry(args, adv_adj, update_outstanding, from_repost=False):
|
||||
gle.submit()
|
||||
|
||||
def validate_account_for_perpetual_inventory(gl_map):
|
||||
if cint(erpnext.is_perpetual_inventory_enabled(gl_map[0].company)):
|
||||
if cint(erpnext.is_perpetual_inventory_enabled(gl_map[0].company)) and gl_map[0].voucher_type=="Journal Entry":
|
||||
account_list = [gl_entries.account for gl_entries in gl_map]
|
||||
|
||||
aii_accounts = [d.name for d in frappe.get_all("Account",
|
||||
@@ -160,13 +160,12 @@ def validate_account_for_perpetual_inventory(gl_map):
|
||||
account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(account,
|
||||
gl_map[0].posting_date, gl_map[0].company)
|
||||
|
||||
if gl_map[0].voucher_type=="Journal Entry":
|
||||
# In case of Journal Entry, there are no corresponding SL entries,
|
||||
# hence deducting currency amount
|
||||
account_bal -= flt(gl_map[0].debit) - flt(gl_map[0].credit)
|
||||
if account_bal == stock_bal:
|
||||
frappe.throw(_("Account: {0} can only be updated via Stock Transactions")
|
||||
.format(account), StockAccountInvalidTransaction)
|
||||
# In case of Journal Entry, there are no corresponding SL entries,
|
||||
# hence deducting currency amount
|
||||
account_bal -= flt(gl_map[0].debit) - flt(gl_map[0].credit)
|
||||
if account_bal == stock_bal:
|
||||
frappe.throw(_("Account: {0} can only be updated via Stock Transactions")
|
||||
.format(account), StockAccountInvalidTransaction)
|
||||
|
||||
# This has been comment for a temporary, will add this code again on release of immutable ledger
|
||||
# elif account_bal != stock_bal:
|
||||
@@ -240,10 +239,10 @@ def make_round_off_gle(gl_map, debit_credit_diff, precision):
|
||||
for d in gl_map:
|
||||
if d.account == round_off_account:
|
||||
round_off_gle = d
|
||||
if d.debit_in_account_currency:
|
||||
debit_credit_diff -= flt(d.debit_in_account_currency)
|
||||
if d.debit:
|
||||
debit_credit_diff -= flt(d.debit)
|
||||
else:
|
||||
debit_credit_diff += flt(d.credit_in_account_currency)
|
||||
debit_credit_diff += flt(d.credit)
|
||||
round_off_account_exists = True
|
||||
|
||||
if round_off_account_exists and abs(debit_credit_diff) <= (1.0 / (10**precision)):
|
||||
@@ -252,7 +251,7 @@ def make_round_off_gle(gl_map, debit_credit_diff, precision):
|
||||
|
||||
if not round_off_gle:
|
||||
for k in ["voucher_type", "voucher_no", "company",
|
||||
"posting_date", "remarks", "is_opening"]:
|
||||
"posting_date", "remarks"]:
|
||||
round_off_gle[k] = gl_map[0][k]
|
||||
|
||||
round_off_gle.update({
|
||||
@@ -264,6 +263,7 @@ def make_round_off_gle(gl_map, debit_credit_diff, precision):
|
||||
"cost_center": round_off_cost_center,
|
||||
"party_type": None,
|
||||
"party": None,
|
||||
"is_opening": "No",
|
||||
"against_voucher_type": None,
|
||||
"against_voucher": None
|
||||
})
|
||||
@@ -293,7 +293,8 @@ def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
|
||||
select account, posting_date, party_type, party, cost_center, fiscal_year,voucher_type,
|
||||
voucher_no, against_voucher_type, against_voucher, cost_center, company
|
||||
from `tabGL Entry`
|
||||
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no), as_dict=True)
|
||||
where voucher_type=%s and voucher_no=%s
|
||||
for update""", (voucher_type, voucher_no), as_dict=True)
|
||||
|
||||
if gl_entries:
|
||||
validate_accounting_period(gl_entries)
|
||||
|
||||
@@ -260,7 +260,11 @@ def check_amount_vs_description(amount_matching, description_matching):
|
||||
continue
|
||||
|
||||
if "reference_no" in am_match and "reference_no" in des_match:
|
||||
if difflib.SequenceMatcher(lambda x: x == " ", am_match["reference_no"], des_match["reference_no"]).ratio() > 70:
|
||||
# Sequence Matcher does not handle None as input
|
||||
am_reference = am_match["reference_no"] or ""
|
||||
des_reference = des_match["reference_no"] or ""
|
||||
|
||||
if difflib.SequenceMatcher(lambda x: x == " ", am_reference, des_reference).ratio() > 70:
|
||||
if am_match not in result:
|
||||
result.append(am_match)
|
||||
if result:
|
||||
|
||||
@@ -81,7 +81,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
me.page.set_indicator(__("Online"), "green")
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
onload: function () {
|
||||
@@ -278,6 +278,14 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
})
|
||||
},
|
||||
|
||||
set_pos_profile_title(pos_profile) {
|
||||
this.page.set_title_sub(
|
||||
`<span class="indicator blue">
|
||||
<a class="text-muted" href="#Form/POS Profile/${pos_profile}">${pos_profile}</a>
|
||||
</span>`
|
||||
);
|
||||
},
|
||||
|
||||
get_data_from_server: function (callback) {
|
||||
var me = this;
|
||||
frappe.call({
|
||||
@@ -286,6 +294,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
freeze_message: __("Master data syncing, it might take some time"),
|
||||
callback: function (r) {
|
||||
localStorage.setItem('doc', JSON.stringify(r.message.doc));
|
||||
me.set_pos_profile_title(r.message.pos_profile.name);
|
||||
me.init_master_data(r)
|
||||
me.set_interval_for_si_sync();
|
||||
me.check_internet_connection();
|
||||
@@ -2009,34 +2018,57 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
|
||||
apply_pricing_rule: function () {
|
||||
var me = this;
|
||||
|
||||
var remove_item = false;
|
||||
$.each(this.frm.doc["items"], function (n, item) {
|
||||
var pricing_rule = me.get_pricing_rule(item)
|
||||
me.validate_pricing_rule(pricing_rule)
|
||||
if (pricing_rule.length) {
|
||||
item.pricing_rule = pricing_rule[0].name;
|
||||
item.margin_type = pricing_rule[0].margin_type;
|
||||
item.price_list_rate = pricing_rule[0].price || item.price_list_rate;
|
||||
item.margin_rate_or_amount = pricing_rule[0].margin_rate_or_amount;
|
||||
item.discount_percentage = pricing_rule[0].discount_percentage || 0.0;
|
||||
me.apply_pricing_rule_on_item(item)
|
||||
if (pricing_rule[0].price_or_product_discount == "Price") {
|
||||
item.pricing_rule = pricing_rule[0].name;
|
||||
item.margin_type = pricing_rule[0].margin_type;
|
||||
item.price_list_rate = pricing_rule[0].price || item.price_list_rate;
|
||||
item.margin_rate_or_amount = pricing_rule[0].margin_rate_or_amount;
|
||||
item.discount_percentage = pricing_rule[0].discount_percentage || 0.0;
|
||||
me.apply_pricing_rule_on_item(item)
|
||||
} else {
|
||||
me.child = frappe.model.add_child(me.frm.doc, me.frm.doc.doctype + " Item", "items");
|
||||
me.child.item_code = pricing_rule[0].same_item ? item.item_code : pricing_rule[0].free_item;
|
||||
me.child.item_name = pricing_rule[0].same_item ? item.item_name : pricing_rule[0].free_item;
|
||||
me.child.stock_uom = pricing_rule[0].same_item ? item.stock_uom : pricing_rule[0].free_item_uom;
|
||||
me.child.uom = pricing_rule[0].same_item ? item.uom : pricing_rule[0].free_item_uom;
|
||||
me.child.conversion_factor = 1;
|
||||
me.child.qty = pricing_rule.qty || 1;
|
||||
me.child.is_free_item = 1;
|
||||
me.child.brand = pricing_rule[0].same_item ? item.brand : "";
|
||||
me.child.description = pricing_rule[0].same_item ? item.description : pricing_rule[0].free_item;
|
||||
}
|
||||
} else if (item.pricing_rule) {
|
||||
item.price_list_rate = me.price_list_data[item.item_code]
|
||||
item.margin_rate_or_amount = 0.0;
|
||||
item.discount_percentage = 0.0;
|
||||
item.pricing_rule = null;
|
||||
me.apply_pricing_rule_on_item(item)
|
||||
} else if (item.is_free_item) {
|
||||
remove_item = true;
|
||||
item.qty = 0
|
||||
}
|
||||
|
||||
if(item.discount_percentage > 0) {
|
||||
me.apply_pricing_rule_on_item(item)
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
if (remove_item) {
|
||||
this.remove_zero_qty_items_from_cart();
|
||||
}
|
||||
},
|
||||
|
||||
get_pricing_rule: function (item) {
|
||||
var me = this;
|
||||
return $.grep(this.pricing_rules, function (data) {
|
||||
if (item.qty >= data.min_qty && (item.qty <= (data.max_qty ? data.max_qty : item.qty))) {
|
||||
me.get_mixed_min_max_qty_and_amt(data, item);
|
||||
if (data.mixed_qty >= data.min_qty && (data.mixed_qty <= (data.max_qty ? data.max_qty : data.mixed_qty))) {
|
||||
if (me.validate_item_condition(data, item)) {
|
||||
if (in_list(['Customer', 'Customer Group', 'Territory', 'Campaign'], data.applicable_for)) {
|
||||
return me.validate_condition(data)
|
||||
@@ -2048,11 +2080,26 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
})
|
||||
},
|
||||
|
||||
get_mixed_min_max_qty_and_amt: function(data, item) {
|
||||
var apply_on = frappe.model.scrub(data.apply_on);
|
||||
data.mixed_qty = 0.0
|
||||
if (data.mixed_conditions && in_list(data[apply_on], item[apply_on])) {
|
||||
this.frm.doc.items.forEach(d => {
|
||||
if (in_list(data[apply_on], d[apply_on])) {
|
||||
data.mixed_qty += d.qty;
|
||||
data.mixed_amt += d.amount;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
data.mixed_qty = item.qty;
|
||||
data.mixed_amt = item.amount;
|
||||
}
|
||||
},
|
||||
|
||||
validate_item_condition: function (data, item) {
|
||||
var apply_on = frappe.model.scrub(data.apply_on);
|
||||
|
||||
return (data.apply_on == 'Item Group')
|
||||
? this.validate_item_group(data.item_group, item.item_group) : (data[apply_on] == item[apply_on]);
|
||||
return in_list(data[apply_on], item[apply_on]);
|
||||
},
|
||||
|
||||
validate_item_group: function (pr_item_group, cart_item_group) {
|
||||
|
||||
@@ -60,7 +60,7 @@ def _get_party_details(party=None, account=None, party_type="Customer", company=
|
||||
billing_address=party_address, shipping_address=shipping_address)
|
||||
|
||||
if fetch_payment_terms_template:
|
||||
party_details["payment_terms_template"] = get_pyt_term_template(party.name, party_type, company)
|
||||
party_details["payment_terms_template"] = get_payment_terms_template(party.name, party_type, company)
|
||||
|
||||
if not party_details.get("currency"):
|
||||
party_details["currency"] = currency
|
||||
@@ -204,7 +204,7 @@ def set_account_and_due_date(party, account, party_type, company, posting_date,
|
||||
return out
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_party_account(party_type, party, company):
|
||||
def get_party_account(party_type, party, company=None):
|
||||
"""Returns the account for the given `party`.
|
||||
Will first search in party (Customer / Supplier) record, if not found,
|
||||
will search in group (Customer Group / Supplier Group),
|
||||
@@ -318,7 +318,7 @@ def get_due_date(posting_date, party_type, party, company=None, bill_date=None):
|
||||
due_date = None
|
||||
if (bill_date or posting_date) and party:
|
||||
due_date = bill_date or posting_date
|
||||
template_name = get_pyt_term_template(party, party_type, company)
|
||||
template_name = get_payment_terms_template(party, party_type, company)
|
||||
|
||||
if template_name:
|
||||
due_date = get_due_date_from_template(template_name, posting_date, bill_date).strftime("%Y-%m-%d")
|
||||
@@ -425,7 +425,7 @@ def set_taxes(party, party_type, posting_date, company, customer_group=None, sup
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_pyt_term_template(party_name, party_type, company=None):
|
||||
def get_payment_terms_template(party_name, party_type, company=None):
|
||||
if party_type not in ("Customer", "Supplier"):
|
||||
return
|
||||
template = None
|
||||
|
||||
166
erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.html
Normal file
166
erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.html
Normal file
@@ -0,0 +1,166 @@
|
||||
{%- from "templates/print_formats/standard_macros.html" import add_header, render_field, print_value -%}
|
||||
{%- set einvoice = json.loads(doc.signed_einvoice) -%}
|
||||
|
||||
<div class="page-break">
|
||||
<div {% if print_settings.repeat_header_footer %} id="header-html" class="hidden-pdf" {% endif %}>
|
||||
{% if letter_head and not no_letterhead %}
|
||||
<div class="letter-head">{{ letter_head }}</div>
|
||||
{% endif %}
|
||||
<div class="print-heading">
|
||||
<h2>E Invoice<br><small>{{ doc.name }}</small></h2>
|
||||
</div>
|
||||
</div>
|
||||
{% if print_settings.repeat_header_footer %}
|
||||
<div id="footer-html" class="visible-pdf">
|
||||
{% if not no_letterhead and footer %}
|
||||
<div class="letter-head-footer">
|
||||
{{ footer }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<p class="text-center small page-number visible-pdf">
|
||||
{{ _("Page {0} of {1}").format('<span class="page"></span>', '<span class="topage"></span>') }}
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="row section-break" style="border-bottom: 1px solid #d1d8dd; padding-bottom: 10px;">
|
||||
<h5 class="font-bold" style="margin-left: 15px; margin-top: 0px;">1. Transaction Details</h5>
|
||||
<div class="col-xs-7 column-break">
|
||||
<div class="row data-field">
|
||||
<div class="col-xs-4"><label>IRN</label></div>
|
||||
<div class="col-xs-8 value">{{ einvoice.Irn }}</div>
|
||||
</div>
|
||||
<div class="row data-field">
|
||||
<div class="col-xs-4"><label>Ack. No</label></div>
|
||||
<div class="col-xs-8 value">{{ einvoice.AckNo }}</div>
|
||||
</div>
|
||||
<div class="row data-field">
|
||||
<div class="col-xs-4"><label>Ack. Date</label></div>
|
||||
<div class="col-xs-8 value">{{ frappe.utils.format_datetime(einvoice.AckDt, "dd/MM/yyyy hh:mm:ss") }}</div>
|
||||
</div>
|
||||
<div class="row data-field">
|
||||
<div class="col-xs-4"><label>Category</label></div>
|
||||
<div class="col-xs-8 value">{{ einvoice.TranDtls.SupTyp }}</div>
|
||||
</div>
|
||||
<div class="row data-field">
|
||||
<div class="col-xs-4"><label>Document Type</label></div>
|
||||
<div class="col-xs-8 value">{{ einvoice.DocDtls.Typ }}</div>
|
||||
</div>
|
||||
<div class="row data-field">
|
||||
<div class="col-xs-4"><label>Document No</label></div>
|
||||
<div class="col-xs-8 value">{{ einvoice.DocDtls.No }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-5 column-break">
|
||||
<img src="{{ doc.qrcode_image }}" alt="QRCode Image" style="
|
||||
width: 175px; height: 175px;
|
||||
float: right; border: 1px solid gray;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row section-break" style="border-bottom: 1px solid #d1d8dd; padding-bottom: 10px;">
|
||||
<h5 class="font-bold" style="margin-left: 15px; margin-bottom: 0px;">2. Party Details</h5>
|
||||
{%- set seller = einvoice.SellerDtls -%}
|
||||
<div class="col-xs-6 column-break">
|
||||
<h5 style="margin-bottom: 5px;">Seller</h5>
|
||||
<p>{{ seller.Gstin }}</p>
|
||||
<p>{{ seller.LglNm }}</p>
|
||||
<p>{{ seller.Addr1 }}</p>
|
||||
{%- if seller.Addr2 -%} <p>{{ seller.Addr2 }}</p> {% endif %}
|
||||
<p>{{ seller.Loc }}</p>
|
||||
<p>{{ frappe.db.get_value("Address", doc.company_address, "gst_state") }} - {{ seller.Pin }}</p>
|
||||
|
||||
{%- if einvoice.ShipDtls -%}
|
||||
{%- set shipping = einvoice.ShipDtls -%}
|
||||
<h5 style="margin-bottom: 5px;">Shipping</h5>
|
||||
<p>{{ shipping.Gstin }}</p>
|
||||
<p>{{ shipping.LglNm }}</p>
|
||||
<p>{{ shipping.Addr1 }}</p>
|
||||
{%- if shipping.Addr2 -%} <p>{{ shipping.Addr2 }}</p> {% endif %}
|
||||
<p>{{ shipping.Loc }}</p>
|
||||
<p>{{ frappe.db.get_value("Address", doc.shipping_address_name, "gst_state") }} - {{ shipping.Pin }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{%- set buyer = einvoice.BuyerDtls -%}
|
||||
<div class="col-xs-6 column-break">
|
||||
<h5 style="margin-bottom: 5px;">Buyer</h5>
|
||||
<p>{{ buyer.Gstin }}</p>
|
||||
<p>{{ buyer.LglNm }}</p>
|
||||
<p>{{ buyer.Addr1 }}</p>
|
||||
{%- if buyer.Addr2 -%} <p>{{ buyer.Addr2 }}</p> {% endif %}
|
||||
<p>{{ buyer.Loc }}</p>
|
||||
<p>{{ frappe.db.get_value("Address", doc.customer_address, "gst_state") }} - {{ buyer.Pin }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style="overflow-x: auto;">
|
||||
<h5 class="font-bold" style="margin-bottom: 0px;">3. Item Details</h5>
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-left" style="width: 3%;">Sr. No.</th>
|
||||
<th class="text-left">Item</th>
|
||||
<th class="text-left" style="width: 10%;">HSN Code</th>
|
||||
<th class="text-left" style="width: 5%;">Qty</th>
|
||||
<th class="text-left" style="width: 5%;">UOM</th>
|
||||
<th class="text-left">Rate</th>
|
||||
<th class="text-left" style="width: 5%;">Discount</th>
|
||||
<th class="text-left">Taxable Amount</th>
|
||||
<th class="text-left" style="width: 7%;">Tax Rate</th>
|
||||
<th class="text-left" style="width: 5%;">Other Charges</th>
|
||||
<th class="text-left">Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in einvoice.ItemList %}
|
||||
<tr>
|
||||
<td class="text-left" style="width: 3%;">{{ item.SlNo }}</td>
|
||||
<td class="text-left">{{ item.PrdDesc }}</td>
|
||||
<td class="text-left" style="width: 10%;">{{ item.HsnCd }}</td>
|
||||
<td class="text-right" style="width: 5%;">{{ item.Qty }}</td>
|
||||
<td class="text-left" style="width: 5%;">{{ item.Unit }}</td>
|
||||
<td class="text-right">{{ frappe.utils.fmt_money(item.UnitPrice, None, "INR") }}</td>
|
||||
<td class="text-right" style="width: 5%;">{{ frappe.utils.fmt_money(item.Discount, None, "INR") }}</td>
|
||||
<td class="text-right">{{ frappe.utils.fmt_money(item.AssAmt, None, "INR") }}</td>
|
||||
<td class="text-right" style="width: 7%;">{{ item.GstRt + item.CesRt }} %</td>
|
||||
<td class="text-right" style="width: 5%;">{{ frappe.utils.fmt_money(0, None, "INR") }}</td>
|
||||
<td class="text-right">{{ frappe.utils.fmt_money(item.TotItemVal, None, "INR") }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div style="overflow-x: auto;">
|
||||
<h5 class="font-bold" style="margin-bottom: 0px;">4. Value Details</h5>
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-left">Taxable Amount</th>
|
||||
<th class="text-left">CGST</th>
|
||||
<th class="text-left"">SGST</th>
|
||||
<th class="text-left">IGST</th>
|
||||
<th class="text-left">CESS</th>
|
||||
<th class="text-left" style="width: 10%;">State CESS</th>
|
||||
<th class="text-left">Discount</th>
|
||||
<th class="text-left" style="width: 10%;">Other Charges</th>
|
||||
<th class="text-left" style="width: 10%;">Round Off</th>
|
||||
<th class="text-left">Total Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{%- set value_details = einvoice.ValDtls -%}
|
||||
<tr>
|
||||
<td class="text-right">{{ frappe.utils.fmt_money(value_details.AssVal, None, "INR") }}</td>
|
||||
<td class="text-right">{{ frappe.utils.fmt_money(value_details.CgstVal, None, "INR") }}</td>
|
||||
<td class="text-right">{{ frappe.utils.fmt_money(value_details.SgstVal, None, "INR") }}</td>
|
||||
<td class="text-right">{{ frappe.utils.fmt_money(value_details.IgstVal, None, "INR") }}</td>
|
||||
<td class="text-right">{{ frappe.utils.fmt_money(value_details.CesVal, None, "INR") }}</td>
|
||||
<td class="text-right">{{ frappe.utils.fmt_money(0, None, "INR") }}</td>
|
||||
<td class="text-right">{{ frappe.utils.fmt_money(value_details.Discount, None, "INR") }}</td>
|
||||
<td class="text-right">{{ frappe.utils.fmt_money(value_details.OthChrg, None, "INR") }}</td>
|
||||
<td class="text-right">{{ frappe.utils.fmt_money(value_details.RndOffAmt, None, "INR") }}</td>
|
||||
<td class="text-right">{{ frappe.utils.fmt_money(value_details.TotInvVal, None, "INR") }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"align_labels_right": 1,
|
||||
"creation": "2020-10-10 18:01:21.032914",
|
||||
"custom_format": 0,
|
||||
"default_print_language": "en-US",
|
||||
"disabled": 1,
|
||||
"doc_type": "Sales Invoice",
|
||||
"docstatus": 0,
|
||||
"doctype": "Print Format",
|
||||
"font": "Default",
|
||||
"html": "",
|
||||
"idx": 0,
|
||||
"line_breaks": 1,
|
||||
"modified": "2020-10-23 19:54:40.634936",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "GST E-Invoice",
|
||||
"owner": "Administrator",
|
||||
"print_format_builder": 0,
|
||||
"print_format_type": "Jinja",
|
||||
"raw_printing": 0,
|
||||
"show_section_headings": 1,
|
||||
"standard": "Yes"
|
||||
}
|
||||
@@ -49,7 +49,10 @@ class TestAccountBalance(unittest.TestCase):
|
||||
},
|
||||
]
|
||||
|
||||
self.assertEqual(expected_data, report[1])
|
||||
expected_data = sorted(expected_data, key=lambda k:k['account'])
|
||||
output = sorted(report[1], key=lambda k:k['account'])
|
||||
|
||||
self.assertEqual(expected_data, output)
|
||||
|
||||
def make_sales_invoice():
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
@@ -42,11 +42,13 @@
|
||||
|
||||
{% if(filters.show_future_payments) { %}
|
||||
{% var balance_row = data.slice(-1).pop();
|
||||
var range1 = report.columns[11].label;
|
||||
var range2 = report.columns[12].label;
|
||||
var range3 = report.columns[13].label;
|
||||
var range4 = report.columns[14].label;
|
||||
var range5 = report.columns[15].label;
|
||||
var start = filters.based_on_payment_terms ? 13 : 11;
|
||||
var range1 = report.columns[start].label;
|
||||
var range2 = report.columns[start+1].label;
|
||||
var range3 = report.columns[start+2].label;
|
||||
var range4 = report.columns[start+3].label;
|
||||
var range5 = report.columns[start+4].label;
|
||||
var range6 = report.columns[start+5].label;
|
||||
%}
|
||||
{% if(balance_row) { %}
|
||||
<table class="table table-bordered table-condensed">
|
||||
@@ -70,20 +72,34 @@
|
||||
<th>{%= __(range3) %}</th>
|
||||
<th>{%= __(range4) %}</th>
|
||||
<th>{%= __(range5) %}</th>
|
||||
<th>{%= __(range6) %}</th>
|
||||
<th>{%= __("Total") %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{%= __("Total Outstanding") %}</td>
|
||||
<td class="text-right">{%= format_number(balance_row["range1"], null, 2) %}</td>
|
||||
<td class="text-right">{%= format_currency(balance_row["range2"]) %}</td>
|
||||
<td class="text-right">{%= format_currency(balance_row["range3"]) %}</td>
|
||||
<td class="text-right">{%= format_currency(balance_row["range4"]) %}</td>
|
||||
<td class="text-right">{%= format_currency(balance_row["range5"]) %}</td>
|
||||
<td class="text-right">
|
||||
{%= format_number(balance_row["age"], null, 2) %}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
{%= format_currency(balance_row["range1"], data[data.length-1]["currency"]) %}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
{%= format_currency(balance_row["range2"], data[data.length-1]["currency"]) %}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
{%= format_currency(balance_row["range3"], data[data.length-1]["currency"]) %}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
{%= format_currency(balance_row["range4"], data[data.length-1]["currency"]) %}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
{%= format_currency(balance_row["range5"], data[data.length-1]["currency"]) %}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
{%= format_currency(flt(balance_row["outstanding"]), data[data.length-1]["currency"]) %}
|
||||
</td>
|
||||
</td>
|
||||
</tr>
|
||||
<td>{%= __("Future Payments") %}</td>
|
||||
<td></td>
|
||||
@@ -91,6 +107,7 @@
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td class="text-right">
|
||||
{%= format_currency(flt(balance_row[("future_amount")]), data[data.length-1]["currency"]) %}
|
||||
</td>
|
||||
@@ -101,6 +118,7 @@
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th class="text-right">
|
||||
{%= format_currency(flt(balance_row["outstanding"] - balance_row[("future_amount")]), data[data.length-1]["currency"]) %}</th>
|
||||
</tr>
|
||||
@@ -218,15 +236,15 @@
|
||||
<td></td>
|
||||
<td style="text-align: right"><b>{%= __("Total") %}</b></td>
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["invoiced"], data[0]["currency"] ) %}</td>
|
||||
{%= format_currency(data[i]["invoiced"], data[i]["currency"] ) %}</td>
|
||||
|
||||
{% if(!filters.show_future_payments) { %}
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["paid"], data[0]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[0]["currency"]) %} </td>
|
||||
{%= format_currency(data[i]["paid"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %} </td>
|
||||
{% } %}
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["outstanding"], data[0]["currency"]) %}</td>
|
||||
{%= format_currency(data[i]["outstanding"], data[i]["currency"]) %}</td>
|
||||
|
||||
{% if(filters.show_future_payments) { %}
|
||||
{% if(report.report_name === "Accounts Receivable") { %}
|
||||
@@ -234,8 +252,8 @@
|
||||
{%= data[i]["po_no"] %}</td>
|
||||
{% } %}
|
||||
<td style="text-align: right">{%= data[i]["future_ref"] %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["future_amount"], data[0]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["remaining_balance"], data[0]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["future_amount"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["remaining_balance"], data[i]["currency"]) %}</td>
|
||||
{% } %}
|
||||
{% } %}
|
||||
{% } else { %}
|
||||
@@ -256,10 +274,10 @@
|
||||
{% } else { %}
|
||||
<td><b>{%= __("Total") %}</b></td>
|
||||
{% } %}
|
||||
<td style="text-align: right">{%= format_currency(data[i]["invoiced"], data[0]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["paid"], data[0]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[0]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["outstanding"], data[0]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["invoiced"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["paid"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["outstanding"], data[i]["currency"]) %}</td>
|
||||
{% } %}
|
||||
{% } %}
|
||||
</tr>
|
||||
|
||||
@@ -160,6 +160,8 @@ class ReceivablePayableReport(object):
|
||||
else:
|
||||
# advance / unlinked payment or other adjustment
|
||||
row.paid -= gle_balance
|
||||
if gle.cost_center:
|
||||
row.cost_center = gle.cost_center
|
||||
|
||||
def update_sub_total_row(self, row, party):
|
||||
total_row = self.total_row_map.get(party)
|
||||
@@ -210,7 +212,6 @@ class ReceivablePayableReport(object):
|
||||
for key, row in self.voucher_balance.items():
|
||||
row.outstanding = flt(row.invoiced - row.paid - row.credit_note, self.currency_precision)
|
||||
row.invoice_grand_total = row.invoiced
|
||||
|
||||
if abs(row.outstanding) > 1.0/10 ** self.currency_precision:
|
||||
# non-zero oustanding, we must consider this row
|
||||
|
||||
@@ -577,7 +578,7 @@ class ReceivablePayableReport(object):
|
||||
|
||||
self.gl_entries = frappe.db.sql("""
|
||||
select
|
||||
name, posting_date, account, party_type, party, voucher_type, voucher_no,
|
||||
name, posting_date, account, party_type, party, voucher_type, voucher_no, cost_center,
|
||||
against_voucher_type, against_voucher, account_currency, remarks, {0}
|
||||
from
|
||||
`tabGL Entry`
|
||||
@@ -741,6 +742,7 @@ class ReceivablePayableReport(object):
|
||||
self.add_column(_("Customer Contact"), fieldname='customer_primary_contact',
|
||||
fieldtype='Link', options='Contact')
|
||||
|
||||
self.add_column(label=_('Cost Center'), fieldname='cost_center', fieldtype='Data')
|
||||
self.add_column(label=_('Voucher Type'), fieldname='voucher_type', fieldtype='Data')
|
||||
self.add_column(label=_('Voucher No'), fieldname='voucher_no', fieldtype='Dynamic Link',
|
||||
options='voucher_type', width=180)
|
||||
|
||||
@@ -15,15 +15,51 @@ def execute(filters=None):
|
||||
return columns, data
|
||||
|
||||
def get_columns():
|
||||
return [
|
||||
_("Payment Document") + "::130",
|
||||
_("Payment Entry") + ":Dynamic Link/"+_("Payment Document")+":110",
|
||||
_("Posting Date") + ":Date:100",
|
||||
_("Cheque/Reference No") + "::120",
|
||||
_("Clearance Date") + ":Date:100",
|
||||
_("Against Account") + ":Link/Account:170",
|
||||
_("Amount") + ":Currency:120"
|
||||
]
|
||||
columns = [{
|
||||
"label": _("Payment Document Type"),
|
||||
"fieldname": "payment_document_type",
|
||||
"fieldtype": "Link",
|
||||
"options": "Doctype",
|
||||
"width": 130
|
||||
},
|
||||
{
|
||||
"label": _("Payment Entry"),
|
||||
"fieldname": "payment_entry",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"options": "payment_document_type",
|
||||
"width": 140
|
||||
},
|
||||
{
|
||||
"label": _("Posting Date"),
|
||||
"fieldname": "posting_date",
|
||||
"fieldtype": "Date",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Cheque/Reference No"),
|
||||
"fieldname": "cheque_no",
|
||||
"width": 120
|
||||
},
|
||||
{
|
||||
"label": _("Clearance Date"),
|
||||
"fieldname": "clearance_date",
|
||||
"fieldtype": "Date",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Against Account"),
|
||||
"fieldname": "against",
|
||||
"fieldtype": "Link",
|
||||
"options": "Account",
|
||||
"width": 170
|
||||
},
|
||||
{
|
||||
"label": _("Amount"),
|
||||
"fieldname": "amount",
|
||||
"width": 120
|
||||
}]
|
||||
|
||||
return columns
|
||||
|
||||
def get_conditions(filters):
|
||||
conditions = ""
|
||||
|
||||
@@ -165,7 +165,7 @@ def add_data_for_operating_activities(
|
||||
if profit_data:
|
||||
profit_data.update({
|
||||
"indent": 1,
|
||||
"parent_account": get_mapper_for(light_mappers, position=0)['section_header']
|
||||
"parent_account": get_mapper_for(light_mappers, position=1)['section_header']
|
||||
})
|
||||
data.append(profit_data)
|
||||
section_data.append(profit_data)
|
||||
@@ -312,10 +312,10 @@ def add_data_for_other_activities(
|
||||
def compute_data(filters, company_currency, profit_data, period_list, light_mappers, full_mapper):
|
||||
data = []
|
||||
|
||||
operating_activities_mapper = get_mapper_for(light_mappers, position=0)
|
||||
operating_activities_mapper = get_mapper_for(light_mappers, position=1)
|
||||
other_mappers = [
|
||||
get_mapper_for(light_mappers, position=1),
|
||||
get_mapper_for(light_mappers, position=2)
|
||||
get_mapper_for(light_mappers, position=2),
|
||||
get_mapper_for(light_mappers, position=3)
|
||||
]
|
||||
|
||||
if operating_activities_mapper:
|
||||
@@ -396,7 +396,7 @@ def _get_account_type_based_data(filters, account_names, period_list, accumulate
|
||||
gl_sum = frappe.db.sql_list("""
|
||||
select sum(credit) - sum(debit)
|
||||
from `tabGL Entry`
|
||||
where company=%s and posting_date >= %s and posting_date <= %s
|
||||
where company=%s and posting_date >= %s and posting_date <= %s
|
||||
and voucher_type != 'Period Closing Voucher'
|
||||
and account in ( SELECT name FROM tabAccount WHERE name IN (%s)
|
||||
OR parent_account IN (%s))
|
||||
@@ -405,7 +405,7 @@ def _get_account_type_based_data(filters, account_names, period_list, accumulate
|
||||
gl_sum = frappe.db.sql_list("""
|
||||
select sum(credit) - sum(debit)
|
||||
from `tabGL Entry`
|
||||
where company=%s and posting_date >= %s and posting_date <= %s
|
||||
where company=%s and posting_date >= %s and posting_date <= %s
|
||||
and voucher_type != 'Period Closing Voucher'
|
||||
and account in ( SELECT name FROM tabAccount WHERE name IN (%s)
|
||||
OR parent_account IN (%s))
|
||||
|
||||
@@ -206,7 +206,7 @@ def get_data(companies, root_type, balance_must_be, fiscal_year, filters=None, i
|
||||
|
||||
set_gl_entries_by_account(fiscal_year.year_start_date,
|
||||
fiscal_year.year_end_date, root.lft, root.rgt, filters,
|
||||
gl_entries_by_account, accounts_by_name, ignore_closing_entries=False)
|
||||
gl_entries_by_account, accounts_by_name, accounts, ignore_closing_entries=False)
|
||||
|
||||
calculate_values(accounts_by_name, gl_entries_by_account, companies, fiscal_year, filters)
|
||||
accumulate_values_into_parents(accounts, accounts_by_name, companies)
|
||||
@@ -224,8 +224,7 @@ def get_company_currency(filters=None):
|
||||
def calculate_values(accounts_by_name, gl_entries_by_account, companies, fiscal_year, filters):
|
||||
for entries in gl_entries_by_account.values():
|
||||
for entry in entries:
|
||||
key = entry.account_number or entry.account_name
|
||||
d = accounts_by_name.get(key)
|
||||
d = accounts_by_name.get(entry.account_name)
|
||||
if d:
|
||||
for company in companies:
|
||||
# check if posting date is within the period
|
||||
@@ -240,7 +239,8 @@ def accumulate_values_into_parents(accounts, accounts_by_name, companies):
|
||||
"""accumulate children's values in parent accounts"""
|
||||
for d in reversed(accounts):
|
||||
if d.parent_account:
|
||||
account = d.parent_account.split('-')[0].strip()
|
||||
account = d.parent_account_name
|
||||
|
||||
if not accounts_by_name.get(account):
|
||||
continue
|
||||
|
||||
@@ -251,16 +251,34 @@ def accumulate_values_into_parents(accounts, accounts_by_name, companies):
|
||||
accounts_by_name[account]["opening_balance"] = \
|
||||
accounts_by_name[account].get("opening_balance", 0.0) + d.get("opening_balance", 0.0)
|
||||
|
||||
|
||||
def get_account_heads(root_type, companies, filters):
|
||||
accounts = get_accounts(root_type, filters)
|
||||
|
||||
if not accounts:
|
||||
return None, None
|
||||
|
||||
accounts = update_parent_account_names(accounts)
|
||||
|
||||
accounts, accounts_by_name, parent_children_map = filter_accounts(accounts)
|
||||
|
||||
return accounts, accounts_by_name
|
||||
|
||||
def update_parent_account_names(accounts):
|
||||
"""Update parent_account_name in accounts list.
|
||||
|
||||
parent_name is `name` of parent account which could have other prefix
|
||||
of account_number and suffix of company abbr. This function adds key called
|
||||
`parent_account_name` which does not have such prefix/suffix.
|
||||
"""
|
||||
name_to_account_map = { d.name : d.account_name for d in accounts }
|
||||
|
||||
for account in accounts:
|
||||
if account.parent_account:
|
||||
account["parent_account_name"] = name_to_account_map[account.parent_account]
|
||||
|
||||
return accounts
|
||||
|
||||
def get_companies(filters):
|
||||
companies = {}
|
||||
all_companies = get_subsidiary_companies(filters.get('company'))
|
||||
@@ -325,7 +343,7 @@ def prepare_data(accounts, fiscal_year, balance_must_be, companies, company_curr
|
||||
return data
|
||||
|
||||
def set_gl_entries_by_account(from_date, to_date, root_lft, root_rgt, filters, gl_entries_by_account,
|
||||
accounts_by_name, ignore_closing_entries=False):
|
||||
accounts_by_name, accounts, ignore_closing_entries=False):
|
||||
"""Returns a dict like { "account": [gl entries], ... }"""
|
||||
|
||||
company_lft, company_rgt = frappe.get_cached_value('Company',
|
||||
@@ -367,16 +385,32 @@ def set_gl_entries_by_account(from_date, to_date, root_lft, root_rgt, filters, g
|
||||
convert_to_presentation_currency(gl_entries, currency_info)
|
||||
|
||||
for entry in gl_entries:
|
||||
key = entry.account_number or entry.account_name
|
||||
validate_entries(key, entry, accounts_by_name)
|
||||
gl_entries_by_account.setdefault(key, []).append(entry)
|
||||
account_name = entry.account_name
|
||||
validate_entries(account_name, entry, accounts_by_name, accounts)
|
||||
gl_entries_by_account.setdefault(account_name, []).append(entry)
|
||||
|
||||
return gl_entries_by_account
|
||||
|
||||
def validate_entries(key, entry, accounts_by_name):
|
||||
def get_account_details(account):
|
||||
return frappe.get_cached_value('Account', account, ['name', 'report_type', 'root_type', 'company',
|
||||
'is_group', 'account_name', 'account_number', 'parent_account', 'lft', 'rgt'], as_dict=1)
|
||||
|
||||
def validate_entries(key, entry, accounts_by_name, accounts):
|
||||
if key not in accounts_by_name:
|
||||
field = "Account number" if entry.account_number else "Account name"
|
||||
frappe.throw(_("{0} {1} is not present in the parent company").format(field, key))
|
||||
args = get_account_details(entry.account)
|
||||
|
||||
if args.parent_account:
|
||||
parent_args = get_account_details(args.parent_account)
|
||||
|
||||
args.update({
|
||||
'lft': parent_args.lft + 1,
|
||||
'rgt': parent_args.rgt - 1,
|
||||
'root_type': parent_args.root_type,
|
||||
'report_type': parent_args.report_type
|
||||
})
|
||||
|
||||
accounts_by_name.setdefault(key, args)
|
||||
accounts.append(args)
|
||||
|
||||
def get_additional_conditions(from_date, ignore_closing_entries, filters):
|
||||
additional_conditions = []
|
||||
@@ -422,8 +456,7 @@ def filter_accounts(accounts, depth=10):
|
||||
parent_children_map = {}
|
||||
accounts_by_name = {}
|
||||
for d in accounts:
|
||||
key = d.account_number or d.account_name
|
||||
accounts_by_name[key] = d
|
||||
accounts_by_name[d.account_name] = d
|
||||
parent_children_map.setdefault(d.parent_account or None, []).append(d)
|
||||
|
||||
filtered_accounts = []
|
||||
|
||||
@@ -294,7 +294,7 @@ def get_accounts(company, root_type):
|
||||
where company=%s and root_type=%s order by lft""", (company, root_type), as_dict=True)
|
||||
|
||||
|
||||
def filter_accounts(accounts, depth=10):
|
||||
def filter_accounts(accounts, depth=20):
|
||||
parent_children_map = {}
|
||||
accounts_by_name = {}
|
||||
for d in accounts:
|
||||
|
||||
@@ -36,5 +36,20 @@ frappe.query_reports["Gross Profit"] = {
|
||||
"options": "Invoice\nItem Code\nItem Group\nBrand\nWarehouse\nCustomer\nCustomer Group\nTerritory\nSales Person\nProject",
|
||||
"default": "Invoice"
|
||||
},
|
||||
]
|
||||
],
|
||||
"tree": true,
|
||||
"name_field": "parent",
|
||||
"parent_field": "parent_invoice",
|
||||
"initial_depth": 3,
|
||||
"formatter": function(value, row, column, data, default_formatter) {
|
||||
value = default_formatter(value, row, column, data);
|
||||
|
||||
if (data && data.indent == 0.0) {
|
||||
value = $(`<span>${value}</span>`);
|
||||
var $value = $(value).css("font-weight", "bold");
|
||||
value = $value.wrap("<p></p>").parent().html();
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
{
|
||||
"add_total_row": 1,
|
||||
"add_total_row": 0,
|
||||
"columns": [],
|
||||
"creation": "2013-02-25 17:03:34",
|
||||
"disable_prepared_report": 0,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"filters": [],
|
||||
"idx": 3,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2020-08-13 11:26:39.112352",
|
||||
"modified": "2021-08-19 18:57:07.468202",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Gross Profit",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "Sales Invoice",
|
||||
"report_name": "Gross Profit",
|
||||
"report_type": "Script Report",
|
||||
|
||||
@@ -41,16 +41,44 @@ def execute(filters=None):
|
||||
|
||||
columns = get_columns(group_wise_columns, filters)
|
||||
|
||||
for src in gross_profit_data.grouped_data:
|
||||
if filters.group_by == 'Invoice':
|
||||
get_data_when_grouped_by_invoice(columns, gross_profit_data, filters, group_wise_columns, data)
|
||||
|
||||
else:
|
||||
get_data_when_not_grouped_by_invoice(gross_profit_data, filters, group_wise_columns, data)
|
||||
|
||||
return columns, data
|
||||
|
||||
def get_data_when_grouped_by_invoice(columns, gross_profit_data, filters, group_wise_columns, data):
|
||||
column_names = get_column_names()
|
||||
|
||||
# to display item as Item Code: Item Name
|
||||
columns[0] = 'Sales Invoice:Link/Item:300'
|
||||
# removing Item Code and Item Name columns
|
||||
del columns[4:6]
|
||||
|
||||
for src in gross_profit_data.si_list:
|
||||
row = frappe._dict()
|
||||
row.indent = src.indent
|
||||
row.parent_invoice = src.parent_invoice
|
||||
row.currency = filters.currency
|
||||
|
||||
for col in group_wise_columns.get(scrub(filters.group_by)):
|
||||
row[column_names[col]] = src.get(col)
|
||||
|
||||
data.append(row)
|
||||
|
||||
def get_data_when_not_grouped_by_invoice(gross_profit_data, filters, group_wise_columns, data):
|
||||
for idx, src in enumerate(gross_profit_data.grouped_data):
|
||||
row = []
|
||||
for col in group_wise_columns.get(scrub(filters.group_by)):
|
||||
row.append(src.get(col))
|
||||
|
||||
row.append(filters.currency)
|
||||
if idx == len(gross_profit_data.grouped_data)-1:
|
||||
row[0] = frappe.bold("Total")
|
||||
data.append(row)
|
||||
|
||||
return columns, data
|
||||
|
||||
def get_columns(group_wise_columns, filters):
|
||||
columns = []
|
||||
column_map = frappe._dict({
|
||||
@@ -91,12 +119,38 @@ def get_columns(group_wise_columns, filters):
|
||||
|
||||
return columns
|
||||
|
||||
def get_column_names():
|
||||
return frappe._dict({
|
||||
'parent': 'sales_invoice',
|
||||
'customer': 'customer',
|
||||
'customer_group': 'customer_group',
|
||||
'posting_date': 'posting_date',
|
||||
'item_code': 'item_code',
|
||||
'item_name': 'item_name',
|
||||
'item_group': 'item_group',
|
||||
'brand': 'brand',
|
||||
'description': 'description',
|
||||
'warehouse': 'warehouse',
|
||||
'qty': 'qty',
|
||||
'base_rate': 'avg._selling_rate',
|
||||
'buying_rate': 'valuation_rate',
|
||||
'base_amount': 'selling_amount',
|
||||
'buying_amount': 'buying_amount',
|
||||
'gross_profit': 'gross_profit',
|
||||
'gross_profit_percent': 'gross_profit_%',
|
||||
'project': 'project'
|
||||
})
|
||||
|
||||
class GrossProfitGenerator(object):
|
||||
def __init__(self, filters=None):
|
||||
self.data = []
|
||||
self.average_buying_rate = {}
|
||||
self.filters = frappe._dict(filters)
|
||||
self.load_invoice_items()
|
||||
|
||||
if filters.group_by == 'Invoice':
|
||||
self.group_items_by_invoice()
|
||||
|
||||
self.load_stock_ledger_entries()
|
||||
self.load_product_bundle()
|
||||
self.load_non_stock_items()
|
||||
@@ -110,7 +164,12 @@ class GrossProfitGenerator(object):
|
||||
self.currency_precision = cint(frappe.db.get_default("currency_precision")) or 3
|
||||
self.float_precision = cint(frappe.db.get_default("float_precision")) or 2
|
||||
|
||||
for row in self.si_list:
|
||||
grouped_by_invoice = True if self.filters.get("group_by") == "Invoice" else False
|
||||
|
||||
if grouped_by_invoice:
|
||||
buying_amount = 0
|
||||
|
||||
for row in reversed(self.si_list):
|
||||
if self.skip_row(row, self.product_bundles):
|
||||
continue
|
||||
|
||||
@@ -132,12 +191,20 @@ class GrossProfitGenerator(object):
|
||||
row.buying_amount = flt(self.get_buying_amount(row, row.item_code),
|
||||
self.currency_precision)
|
||||
|
||||
if grouped_by_invoice:
|
||||
if row.indent == 1.0:
|
||||
buying_amount += row.buying_amount
|
||||
elif row.indent == 0.0:
|
||||
row.buying_amount = buying_amount
|
||||
buying_amount = 0
|
||||
|
||||
# get buying rate
|
||||
if row.qty:
|
||||
row.buying_rate = flt(row.buying_amount / row.qty, self.float_precision)
|
||||
row.base_rate = flt(row.base_amount / row.qty, self.float_precision)
|
||||
else:
|
||||
row.buying_rate, row.base_rate = 0.0, 0.0
|
||||
if self.is_not_invoice_row(row):
|
||||
row.buying_rate, row.base_rate = 0.0, 0.0
|
||||
|
||||
# calculate gross profit
|
||||
row.gross_profit = flt(row.base_amount - row.buying_amount, self.currency_precision)
|
||||
@@ -154,6 +221,15 @@ class GrossProfitGenerator(object):
|
||||
|
||||
def get_average_rate_based_on_group_by(self):
|
||||
# sum buying / selling totals for group
|
||||
self.totals = frappe._dict(
|
||||
qty=0,
|
||||
base_amount=0,
|
||||
buying_amount=0,
|
||||
gross_profit=0,
|
||||
gross_profit_percent=0,
|
||||
base_rate=0,
|
||||
buying_rate=0
|
||||
)
|
||||
for key in list(self.grouped):
|
||||
if self.filters.get("group_by") != "Invoice":
|
||||
for i, row in enumerate(self.grouped[key]):
|
||||
@@ -165,6 +241,7 @@ class GrossProfitGenerator(object):
|
||||
new_row.base_amount += flt(row.base_amount, self.currency_precision)
|
||||
new_row = self.set_average_rate(new_row)
|
||||
self.grouped_data.append(new_row)
|
||||
self.add_to_totals(new_row)
|
||||
else:
|
||||
for i, row in enumerate(self.grouped[key]):
|
||||
if row.parent in self.returned_invoices \
|
||||
@@ -173,19 +250,32 @@ class GrossProfitGenerator(object):
|
||||
for returned_item_row in returned_item_rows:
|
||||
row.qty += returned_item_row.qty
|
||||
row.base_amount += flt(returned_item_row.base_amount, self.currency_precision)
|
||||
row.buying_amount = flt(row.qty * row.buying_rate, self.currency_precision)
|
||||
if row.qty or row.base_amount:
|
||||
row.buying_amount = flt(flt(row.qty) * flt(row.buying_rate), self.currency_precision)
|
||||
if (flt(row.qty) or row.base_amount) and self.is_not_invoice_row(row):
|
||||
row = self.set_average_rate(row)
|
||||
self.grouped_data.append(row)
|
||||
self.add_to_totals(row)
|
||||
self.set_average_gross_profit(self.totals)
|
||||
self.grouped_data.append(self.totals)
|
||||
|
||||
def is_not_invoice_row(self, row):
|
||||
return (self.filters.get("group_by") == "Invoice" and row.indent != 0.0) or self.filters.get("group_by") != "Invoice"
|
||||
|
||||
def set_average_rate(self, new_row):
|
||||
self.set_average_gross_profit(new_row)
|
||||
new_row.buying_rate = flt(new_row.buying_amount / new_row.qty, self.float_precision) if new_row.qty else 0
|
||||
new_row.base_rate = flt(new_row.base_amount / new_row.qty, self.float_precision) if new_row.qty else 0
|
||||
return new_row
|
||||
|
||||
def set_average_gross_profit(self, new_row):
|
||||
new_row.gross_profit = flt(new_row.base_amount - new_row.buying_amount, self.currency_precision)
|
||||
new_row.gross_profit_percent = flt(((new_row.gross_profit / new_row.base_amount) * 100.0), self.currency_precision) \
|
||||
if new_row.base_amount else 0
|
||||
new_row.buying_rate = flt(new_row.buying_amount / new_row.qty, self.float_precision) if new_row.qty else 0
|
||||
new_row.base_rate = flt(new_row.base_amount / new_row.qty, self.float_precision) if new_row.qty else 0
|
||||
|
||||
return new_row
|
||||
def add_to_totals(self, new_row):
|
||||
for key in self.totals:
|
||||
if new_row.get(key):
|
||||
self.totals[key] += new_row[key]
|
||||
|
||||
def get_returned_invoice_items(self):
|
||||
returned_invoices = frappe.db.sql("""
|
||||
@@ -334,6 +424,109 @@ class GrossProfitGenerator(object):
|
||||
.format(conditions=conditions, sales_person_cols=sales_person_cols,
|
||||
sales_team_table=sales_team_table, match_cond = get_match_cond('Sales Invoice')), self.filters, as_dict=1)
|
||||
|
||||
def group_items_by_invoice(self):
|
||||
"""
|
||||
Turns list of Sales Invoice Items to a tree of Sales Invoices with their Items as children.
|
||||
"""
|
||||
|
||||
parents = []
|
||||
|
||||
for row in self.si_list:
|
||||
if row.parent not in parents:
|
||||
parents.append(row.parent)
|
||||
|
||||
parents_index = 0
|
||||
for index, row in enumerate(self.si_list):
|
||||
if parents_index < len(parents) and row.parent == parents[parents_index]:
|
||||
invoice = self.get_invoice_row(row)
|
||||
self.si_list.insert(index, invoice)
|
||||
parents_index += 1
|
||||
|
||||
else:
|
||||
# skipping the bundle items rows
|
||||
if not row.indent:
|
||||
row.indent = 1.0
|
||||
row.parent_invoice = row.parent
|
||||
row.parent = row.item_code
|
||||
|
||||
if frappe.db.exists('Product Bundle', row.item_code):
|
||||
self.add_bundle_items(row, index)
|
||||
|
||||
def get_invoice_row(self, row):
|
||||
return frappe._dict({
|
||||
'parent_invoice': "",
|
||||
'indent': 0.0,
|
||||
'parent': row.parent,
|
||||
'posting_date': row.posting_date,
|
||||
'posting_time': row.posting_time,
|
||||
'project': row.project,
|
||||
'update_stock': row.update_stock,
|
||||
'customer': row.customer,
|
||||
'customer_group': row.customer_group,
|
||||
'item_code': None,
|
||||
'item_name': None,
|
||||
'description': None,
|
||||
'warehouse': None,
|
||||
'item_group': None,
|
||||
'brand': None,
|
||||
'dn_detail': None,
|
||||
'delivery_note': None,
|
||||
'qty': None,
|
||||
'item_row': None,
|
||||
'is_return': row.is_return,
|
||||
'cost_center': row.cost_center,
|
||||
'base_net_amount': frappe.db.get_value('Sales Invoice', row.parent, 'base_net_total')
|
||||
})
|
||||
|
||||
def add_bundle_items(self, product_bundle, index):
|
||||
bundle_items = self.get_bundle_items(product_bundle)
|
||||
|
||||
for i, item in enumerate(bundle_items):
|
||||
bundle_item = self.get_bundle_item_row(product_bundle, item)
|
||||
self.si_list.insert((index+i+1), bundle_item)
|
||||
|
||||
def get_bundle_items(self, product_bundle):
|
||||
return frappe.get_all(
|
||||
'Product Bundle Item',
|
||||
filters = {
|
||||
'parent': product_bundle.item_code
|
||||
},
|
||||
fields = ['item_code', 'qty']
|
||||
)
|
||||
|
||||
def get_bundle_item_row(self, product_bundle, item):
|
||||
item_name, description, item_group, brand = self.get_bundle_item_details(item.item_code)
|
||||
|
||||
return frappe._dict({
|
||||
'parent_invoice': product_bundle.item_code,
|
||||
'indent': product_bundle.indent + 1,
|
||||
'parent': item.item_code,
|
||||
'posting_date': product_bundle.posting_date,
|
||||
'posting_time': product_bundle.posting_time,
|
||||
'project': product_bundle.project,
|
||||
'customer': product_bundle.customer,
|
||||
'customer_group': product_bundle.customer_group,
|
||||
'item_code': item.item_code,
|
||||
'item_name': item_name,
|
||||
'description': description,
|
||||
'warehouse': product_bundle.warehouse,
|
||||
'item_group': item_group,
|
||||
'brand': brand,
|
||||
'dn_detail': product_bundle.dn_detail,
|
||||
'delivery_note': product_bundle.delivery_note,
|
||||
'qty': (flt(product_bundle.qty) * flt(item.qty)),
|
||||
'item_row': None,
|
||||
'is_return': product_bundle.is_return,
|
||||
'cost_center': product_bundle.cost_center
|
||||
})
|
||||
|
||||
def get_bundle_item_details(self, item_code):
|
||||
return frappe.db.get_value(
|
||||
'Item',
|
||||
item_code,
|
||||
['item_name', 'description', 'item_group', 'brand']
|
||||
)
|
||||
|
||||
def load_stock_ledger_entries(self):
|
||||
res = frappe.db.sql("""select item_code, voucher_type, voucher_no,
|
||||
voucher_detail_no, stock_value, warehouse, actual_qty as qty
|
||||
|
||||
@@ -8,6 +8,7 @@ from frappe.utils import flt
|
||||
from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import (get_tax_accounts,
|
||||
get_grand_total, add_total_row, get_display_value, get_group_by_and_display_fields, add_sub_total_row,
|
||||
get_group_by_conditions)
|
||||
from erpnext.selling.report.item_wise_sales_history.item_wise_sales_history import get_item_details
|
||||
|
||||
def execute(filters=None):
|
||||
return _execute(filters)
|
||||
@@ -23,7 +24,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
aii_account_map = get_aii_accounts()
|
||||
if item_list:
|
||||
itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency,
|
||||
doctype="Purchase Invoice", tax_doctype="Purchase Taxes and Charges")
|
||||
doctype='Purchase Invoice', tax_doctype='Purchase Taxes and Charges')
|
||||
|
||||
po_pr_map = get_purchase_receipts_against_purchase_order(item_list)
|
||||
|
||||
@@ -35,10 +36,14 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
if filters.get('group_by'):
|
||||
grand_total = get_grand_total(filters, 'Purchase Invoice')
|
||||
|
||||
item_details = get_item_details()
|
||||
|
||||
for d in item_list:
|
||||
if not d.stock_qty:
|
||||
continue
|
||||
|
||||
item_record = item_details.get(d.item_code)
|
||||
|
||||
purchase_receipt = None
|
||||
if d.purchase_receipt:
|
||||
purchase_receipt = d.purchase_receipt
|
||||
@@ -49,8 +54,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
|
||||
row = {
|
||||
'item_code': d.item_code,
|
||||
'item_name': d.item_name,
|
||||
'item_group': d.item_group,
|
||||
'item_name': item_record.item_name if item_record else d.item_name,
|
||||
'item_group': item_record.item_group if item_record else d.item_group,
|
||||
'description': d.description,
|
||||
'invoice': d.parent,
|
||||
'posting_date': d.posting_date,
|
||||
@@ -82,10 +87,10 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
for tax in tax_columns:
|
||||
item_tax = itemised_tax.get(d.name, {}).get(tax, {})
|
||||
row.update({
|
||||
frappe.scrub(tax + ' Rate'): item_tax.get("tax_rate", 0),
|
||||
frappe.scrub(tax + ' Amount'): item_tax.get("tax_amount", 0),
|
||||
frappe.scrub(tax + ' Rate'): item_tax.get('tax_rate', 0),
|
||||
frappe.scrub(tax + ' Amount'): item_tax.get('tax_amount', 0),
|
||||
})
|
||||
total_tax += flt(item_tax.get("tax_amount"))
|
||||
total_tax += flt(item_tax.get('tax_amount'))
|
||||
|
||||
row.update({
|
||||
'total_tax': total_tax,
|
||||
@@ -317,8 +322,9 @@ def get_items(filters, additional_query_columns):
|
||||
select
|
||||
`tabPurchase Invoice Item`.`name`, `tabPurchase Invoice Item`.`parent`,
|
||||
`tabPurchase Invoice`.posting_date, `tabPurchase Invoice`.credit_to, `tabPurchase Invoice`.company,
|
||||
`tabPurchase Invoice`.supplier, `tabPurchase Invoice`.remarks, `tabPurchase Invoice`.base_net_total, `tabPurchase Invoice Item`.`item_code`,
|
||||
`tabPurchase Invoice Item`.`item_name`, `tabPurchase Invoice Item`.`item_group`, `tabPurchase Invoice Item`.description,
|
||||
`tabPurchase Invoice`.supplier, `tabPurchase Invoice`.remarks, `tabPurchase Invoice`.base_net_total,
|
||||
`tabPurchase Invoice Item`.`item_code`, `tabPurchase Invoice Item`.description,
|
||||
`tabPurchase Invoice Item`.`item_name`, `tabPurchase Invoice Item`.`item_group`,
|
||||
`tabPurchase Invoice Item`.`project`, `tabPurchase Invoice Item`.`purchase_order`,
|
||||
`tabPurchase Invoice Item`.`purchase_receipt`, `tabPurchase Invoice Item`.`po_detail`,
|
||||
`tabPurchase Invoice Item`.`expense_account`, `tabPurchase Invoice Item`.`stock_qty`,
|
||||
|
||||
@@ -8,6 +8,7 @@ from frappe.utils import flt, cstr
|
||||
from frappe.model.meta import get_field_precision
|
||||
from frappe.utils.xlsxutils import handle_html
|
||||
from erpnext.accounts.report.sales_register.sales_register import get_mode_of_payments
|
||||
from erpnext.selling.report.item_wise_sales_history.item_wise_sales_history import get_item_details, get_customer_details
|
||||
|
||||
def execute(filters=None):
|
||||
return _execute(filters)
|
||||
@@ -17,7 +18,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
filters.update({"from_date": filters.get("date_range") and filters.get("date_range")[0], "to_date": filters.get("date_range") and filters.get("date_range")[1]})
|
||||
columns = get_columns(additional_table_columns, filters)
|
||||
|
||||
company_currency = frappe.get_cached_value('Company', filters.get("company"), "default_currency")
|
||||
company_currency = frappe.get_cached_value('Company', filters.get('company'), 'default_currency')
|
||||
|
||||
item_list = get_items(filters, additional_query_columns)
|
||||
if item_list:
|
||||
@@ -34,7 +35,13 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
if filters.get('group_by'):
|
||||
grand_total = get_grand_total(filters, 'Sales Invoice')
|
||||
|
||||
customer_details = get_customer_details()
|
||||
item_details = get_item_details()
|
||||
|
||||
for d in item_list:
|
||||
customer_record = customer_details.get(d.customer)
|
||||
item_record = item_details.get(d.item_code)
|
||||
|
||||
delivery_note = None
|
||||
if d.delivery_note:
|
||||
delivery_note = d.delivery_note
|
||||
@@ -46,14 +53,14 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
|
||||
row = {
|
||||
'item_code': d.item_code,
|
||||
'item_name': d.item_name,
|
||||
'item_group': d.item_group,
|
||||
'item_name': item_record.item_name if item_record else d.item_name,
|
||||
'item_group': item_record.item_group if item_record else d.item_group,
|
||||
'description': d.description,
|
||||
'invoice': d.parent,
|
||||
'posting_date': d.posting_date,
|
||||
'customer': d.customer,
|
||||
'customer_name': d.customer_name,
|
||||
'customer_group': d.customer_group,
|
||||
'customer_name': customer_record.customer_name,
|
||||
'customer_group': customer_record.customer_group,
|
||||
}
|
||||
|
||||
if additional_query_columns:
|
||||
@@ -91,10 +98,10 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
for tax in tax_columns:
|
||||
item_tax = itemised_tax.get(d.name, {}).get(tax, {})
|
||||
row.update({
|
||||
frappe.scrub(tax + ' Rate'): item_tax.get("tax_rate", 0),
|
||||
frappe.scrub(tax + ' Amount'): item_tax.get("tax_amount", 0),
|
||||
frappe.scrub(tax + ' Rate'): item_tax.get('tax_rate', 0),
|
||||
frappe.scrub(tax + ' Amount'): item_tax.get('tax_amount', 0),
|
||||
})
|
||||
total_tax += flt(item_tax.get("tax_amount"))
|
||||
total_tax += flt(item_tax.get('tax_amount'))
|
||||
|
||||
row.update({
|
||||
'total_tax': total_tax,
|
||||
@@ -227,7 +234,7 @@ def get_columns(additional_table_columns, filters):
|
||||
if filters.get('group_by') != 'Terriotory':
|
||||
columns.extend([
|
||||
{
|
||||
'label': _("Territory"),
|
||||
'label': _('Territory'),
|
||||
'fieldname': 'territory',
|
||||
'fieldtype': 'Link',
|
||||
'options': 'Territory',
|
||||
@@ -382,13 +389,13 @@ def get_items(filters, additional_query_columns):
|
||||
`tabSales Invoice`.posting_date, `tabSales Invoice`.debit_to,
|
||||
`tabSales Invoice`.project, `tabSales Invoice`.customer, `tabSales Invoice`.remarks,
|
||||
`tabSales Invoice`.territory, `tabSales Invoice`.company, `tabSales Invoice`.base_net_total,
|
||||
`tabSales Invoice Item`.item_code, `tabSales Invoice Item`.item_name,
|
||||
`tabSales Invoice Item`.item_group, `tabSales Invoice Item`.description, `tabSales Invoice Item`.sales_order,
|
||||
`tabSales Invoice Item`.delivery_note, `tabSales Invoice Item`.income_account,
|
||||
`tabSales Invoice Item`.cost_center, `tabSales Invoice Item`.stock_qty,
|
||||
`tabSales Invoice Item`.stock_uom, `tabSales Invoice Item`.base_net_rate,
|
||||
`tabSales Invoice Item`.base_net_amount, `tabSales Invoice`.customer_name,
|
||||
`tabSales Invoice`.customer_group, `tabSales Invoice Item`.so_detail,
|
||||
`tabSales Invoice Item`.item_code, `tabSales Invoice Item`.description,
|
||||
`tabSales Invoice Item`.`item_name`, `tabSales Invoice Item`.`item_group`,
|
||||
`tabSales Invoice Item`.sales_order, `tabSales Invoice Item`.delivery_note,
|
||||
`tabSales Invoice Item`.income_account, `tabSales Invoice Item`.cost_center,
|
||||
`tabSales Invoice Item`.stock_qty, `tabSales Invoice Item`.stock_uom,
|
||||
`tabSales Invoice Item`.base_net_rate, `tabSales Invoice Item`.base_net_amount,
|
||||
`tabSales Invoice`.customer_name, `tabSales Invoice`.customer_group, `tabSales Invoice Item`.so_detail,
|
||||
`tabSales Invoice`.update_stock, `tabSales Invoice Item`.uom, `tabSales Invoice Item`.qty {0}
|
||||
from `tabSales Invoice`, `tabSales Invoice Item`
|
||||
where `tabSales Invoice`.name = `tabSales Invoice Item`.parent
|
||||
@@ -425,14 +432,14 @@ def get_deducted_taxes():
|
||||
return frappe.db.sql_list("select name from `tabPurchase Taxes and Charges` where add_deduct_tax = 'Deduct'")
|
||||
|
||||
def get_tax_accounts(item_list, columns, company_currency,
|
||||
doctype="Sales Invoice", tax_doctype="Sales Taxes and Charges"):
|
||||
doctype='Sales Invoice', tax_doctype='Sales Taxes and Charges'):
|
||||
import json
|
||||
item_row_map = {}
|
||||
tax_columns = []
|
||||
invoice_item_row = {}
|
||||
itemised_tax = {}
|
||||
|
||||
tax_amount_precision = get_field_precision(frappe.get_meta(tax_doctype).get_field("tax_amount"),
|
||||
tax_amount_precision = get_field_precision(frappe.get_meta(tax_doctype).get_field('tax_amount'),
|
||||
currency=company_currency) or 2
|
||||
|
||||
for d in item_list:
|
||||
@@ -477,8 +484,8 @@ def get_tax_accounts(item_list, columns, company_currency,
|
||||
tax_rate = tax_data
|
||||
tax_amount = 0
|
||||
|
||||
if charge_type == "Actual" and not tax_rate:
|
||||
tax_rate = "NA"
|
||||
if charge_type == 'Actual' and not tax_rate:
|
||||
tax_rate = 'NA'
|
||||
|
||||
item_net_amount = sum([flt(d.base_net_amount)
|
||||
for d in item_row_map.get(parent, {}).get(item_code, [])])
|
||||
@@ -492,17 +499,17 @@ def get_tax_accounts(item_list, columns, company_currency,
|
||||
if (doctype == 'Purchase Invoice' and name in deducted_tax) else tax_value)
|
||||
|
||||
itemised_tax.setdefault(d.name, {})[description] = frappe._dict({
|
||||
"tax_rate": tax_rate,
|
||||
"tax_amount": tax_value
|
||||
'tax_rate': tax_rate,
|
||||
'tax_amount': tax_value
|
||||
})
|
||||
|
||||
except ValueError:
|
||||
continue
|
||||
elif charge_type == "Actual" and tax_amount:
|
||||
elif charge_type == 'Actual' and tax_amount:
|
||||
for d in invoice_item_row.get(parent, []):
|
||||
itemised_tax.setdefault(d.name, {})[description] = frappe._dict({
|
||||
"tax_rate": "NA",
|
||||
"tax_amount": flt((tax_amount * d.base_net_amount) / d.base_net_total,
|
||||
'tax_rate': 'NA',
|
||||
'tax_amount': flt((tax_amount * d.base_net_amount) / d.base_net_total,
|
||||
tax_amount_precision)
|
||||
})
|
||||
|
||||
@@ -564,7 +571,7 @@ def add_total_row(data, filters, prev_group_by_value, item, total_row_map,
|
||||
})
|
||||
|
||||
total_row_map.setdefault('total_row', {
|
||||
subtotal_display_field: "Total",
|
||||
subtotal_display_field: 'Total',
|
||||
'stock_qty': 0.0,
|
||||
'amount': 0.0,
|
||||
'bold': 1,
|
||||
|
||||
@@ -59,23 +59,111 @@ def validate_filters(filters):
|
||||
|
||||
def get_columns(filters):
|
||||
return [
|
||||
_("Payment Document") + ":: 100",
|
||||
_("Payment Entry") + ":Dynamic Link/"+_("Payment Document")+":140",
|
||||
_("Party Type") + "::100",
|
||||
_("Party") + ":Dynamic Link/Party Type:140",
|
||||
_("Posting Date") + ":Date:100",
|
||||
_("Invoice") + (":Link/Purchase Invoice:130" if filters.get("payment_type") == _("Outgoing") else ":Link/Sales Invoice:130"),
|
||||
_("Invoice Posting Date") + ":Date:130",
|
||||
_("Payment Due Date") + ":Date:130",
|
||||
_("Debit") + ":Currency:120",
|
||||
_("Credit") + ":Currency:120",
|
||||
_("Remarks") + "::150",
|
||||
_("Age") +":Int:40",
|
||||
"0-30:Currency:100",
|
||||
"30-60:Currency:100",
|
||||
"60-90:Currency:100",
|
||||
_("90-Above") + ":Currency:100",
|
||||
_("Delay in payment (Days)") + "::150"
|
||||
{
|
||||
"fieldname": "payment_document",
|
||||
"label": _("Payment Document Type"),
|
||||
"fieldtype": "Data",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"fieldname": "payment_entry",
|
||||
"label": _("Payment Document"),
|
||||
"fieldtype": "Dynamic Link",
|
||||
"options": "payment_document",
|
||||
"width": 160
|
||||
},
|
||||
{
|
||||
"fieldname": "party_type",
|
||||
"label": _("Party Type"),
|
||||
"fieldtype": "Data",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"fieldname": "party",
|
||||
"label": _("Party"),
|
||||
"fieldtype": "Dynamic Link",
|
||||
"options": "party_type",
|
||||
"width": 160
|
||||
},
|
||||
{
|
||||
"fieldname": "posting_date",
|
||||
"label": _("Posting Date"),
|
||||
"fieldtype": "Date",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"fieldname": "invoice",
|
||||
"label": _("Invoice"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Purchase Invoice" if filters.get("payment_type") == _("Outgoing") else "Sales Invoice",
|
||||
"width": 160
|
||||
},
|
||||
{
|
||||
"fieldname": "invoice_posting_date",
|
||||
"label": _("Invoice Posting Date"),
|
||||
"fieldtype": "Date",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"fieldname": "due_date",
|
||||
"label": _("Payment Due Date"),
|
||||
"fieldtype": "Date",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"fieldname": "debit",
|
||||
"label": _("Debit"),
|
||||
"fieldtype": "Currency",
|
||||
"width": 140
|
||||
},
|
||||
{
|
||||
"fieldname": "credit",
|
||||
"label": _("Credit"),
|
||||
"fieldtype": "Currency",
|
||||
"width": 140
|
||||
},
|
||||
{
|
||||
"fieldname": "remarks",
|
||||
"label": _("Remarks"),
|
||||
"fieldtype": "Data",
|
||||
"width": 200
|
||||
},
|
||||
{
|
||||
"fieldname": "age",
|
||||
"label": _("Age"),
|
||||
"fieldtype": "Int",
|
||||
"width": 50
|
||||
},
|
||||
{
|
||||
"fieldname": "range1",
|
||||
"label": _("0-30"),
|
||||
"fieldtype": "Currency",
|
||||
"width": 140
|
||||
},
|
||||
{
|
||||
"fieldname": "range2",
|
||||
"label": _("30-60"),
|
||||
"fieldtype": "Currency",
|
||||
"width": 140
|
||||
},
|
||||
{
|
||||
"fieldname": "range3",
|
||||
"label": _("60-90"),
|
||||
"fieldtype": "Currency",
|
||||
"width": 140
|
||||
},
|
||||
{
|
||||
"fieldname": "range4",
|
||||
"label": _("90 Above"),
|
||||
"fieldtype": "Currency",
|
||||
"width": 140
|
||||
},
|
||||
{
|
||||
"fieldname": "delay_in_payment",
|
||||
"label": _("Delay in payment (Days)"),
|
||||
"fieldtype": "Int",
|
||||
"width": 100
|
||||
}
|
||||
]
|
||||
|
||||
def get_conditions(filters):
|
||||
|
||||
@@ -75,7 +75,10 @@ def get_fiscal_years(transaction_date=None, fiscal_year=None, label="Date", verb
|
||||
else:
|
||||
return ((fy.name, fy.year_start_date, fy.year_end_date),)
|
||||
|
||||
error_msg = _("""{0} {1} not in any active Fiscal Year.""").format(label, formatdate(transaction_date))
|
||||
error_msg = _("""{0} {1} is not in any active Fiscal Year""").format(label, formatdate(transaction_date))
|
||||
if company:
|
||||
error_msg = _("""{0} for {1}""").format(error_msg, frappe.bold(company))
|
||||
|
||||
if verbose==1: frappe.msgprint(error_msg)
|
||||
raise FiscalYearError(error_msg)
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@ frappe.ui.form.on('Asset', {
|
||||
frappe.ui.form.trigger("Asset", "is_existing_asset");
|
||||
frm.toggle_display("next_depreciation_date", frm.doc.docstatus < 1);
|
||||
frm.events.make_schedules_editable(frm);
|
||||
frm.trigger("toggle_make_depreciation_entry");
|
||||
|
||||
if (frm.doc.docstatus==1) {
|
||||
if (in_list(["Submitted", "Partially Depreciated", "Fully Depreciated"], frm.doc.status)) {
|
||||
@@ -136,6 +137,20 @@ frappe.ui.form.on('Asset', {
|
||||
|
||||
if (frm.doc.docstatus == 0) {
|
||||
frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation);
|
||||
frm.set_df_property('depreciation_start_date', 'reqd', 1, frm.doc.name, 'finance_books');
|
||||
frm.refresh_field('finance_books');
|
||||
}
|
||||
},
|
||||
|
||||
toggle_make_depreciation_entry: function(frm) {
|
||||
if (frm.doc.calculate_depreciation){
|
||||
if (in_list(["Submitted", "Partially Depreciated"], frm.doc.status)){
|
||||
frm.fields_dict['schedules'].grid.set_column_disp('make_depreciation_entry', true);
|
||||
} else {
|
||||
frm.fields_dict['schedules'].grid.set_column_disp('make_depreciation_entry', false);
|
||||
}
|
||||
|
||||
frm.refresh_field('schedules');
|
||||
}
|
||||
},
|
||||
|
||||
@@ -232,7 +247,7 @@ frappe.ui.form.on('Asset', {
|
||||
|
||||
|
||||
item_code: function(frm) {
|
||||
if(frm.doc.item_code) {
|
||||
if(frm.doc.item_code && frm.doc.calculate_depreciation) {
|
||||
frm.trigger('set_finance_book');
|
||||
}
|
||||
},
|
||||
@@ -323,6 +338,10 @@ frappe.ui.form.on('Asset', {
|
||||
|
||||
calculate_depreciation: function(frm) {
|
||||
frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation);
|
||||
|
||||
if (frm.doc.calculate_depreciation) {
|
||||
frm.trigger('set_finance_book');
|
||||
}
|
||||
},
|
||||
|
||||
gross_purchase_amount: function(frm) {
|
||||
|
||||
@@ -122,11 +122,6 @@ class Asset(AccountsController):
|
||||
if self.is_existing_asset:
|
||||
return
|
||||
|
||||
docname = self.purchase_receipt or self.purchase_invoice
|
||||
if docname:
|
||||
doctype = 'Purchase Receipt' if self.purchase_receipt else 'Purchase Invoice'
|
||||
date = frappe.db.get_value(doctype, docname, 'posting_date')
|
||||
|
||||
if self.available_for_use_date and getdate(self.available_for_use_date) < getdate(self.purchase_date):
|
||||
frappe.throw(_("Available-for-use Date should be after purchase date"))
|
||||
|
||||
@@ -404,9 +399,10 @@ class Asset(AccountsController):
|
||||
if accumulated_depreciation_after_full_schedule:
|
||||
accumulated_depreciation_after_full_schedule = max(accumulated_depreciation_after_full_schedule)
|
||||
|
||||
asset_value_after_full_schedule = flt(flt(self.gross_purchase_amount) -
|
||||
flt(accumulated_depreciation_after_full_schedule),
|
||||
self.precision('gross_purchase_amount'))
|
||||
asset_value_after_full_schedule = flt(
|
||||
flt(self.gross_purchase_amount) -
|
||||
flt(self.opening_accumulated_depreciation) -
|
||||
flt(accumulated_depreciation_after_full_schedule), self.precision('gross_purchase_amount'))
|
||||
|
||||
if (row.expected_value_after_useful_life and
|
||||
row.expected_value_after_useful_life < asset_value_after_full_schedule):
|
||||
|
||||
@@ -34,6 +34,8 @@ def make_depreciation_entry(asset_name, date=None):
|
||||
date = today()
|
||||
|
||||
asset = frappe.get_doc("Asset", asset_name)
|
||||
validate_asset(asset)
|
||||
|
||||
fixed_asset_account, accumulated_depreciation_account, depreciation_expense_account = \
|
||||
get_depreciation_accounts(asset)
|
||||
|
||||
@@ -59,7 +61,7 @@ def make_depreciation_entry(asset_name, date=None):
|
||||
"credit_in_account_currency": d.depreciation_amount,
|
||||
"reference_type": "Asset",
|
||||
"reference_name": asset.name,
|
||||
"cost_center": ""
|
||||
"cost_center": depreciation_cost_center
|
||||
}
|
||||
|
||||
debit_entry = {
|
||||
@@ -101,6 +103,10 @@ def make_depreciation_entry(asset_name, date=None):
|
||||
|
||||
return asset
|
||||
|
||||
def validate_asset(asset):
|
||||
if asset.status not in ['Submitted', 'Partially Depreciated']:
|
||||
frappe.throw(_("Cannot depreciate {0} Asset").format(asset.status))
|
||||
|
||||
def get_depreciation_accounts(asset):
|
||||
fixed_asset_account = accumulated_depreciation_account = depreciation_expense_account = None
|
||||
|
||||
|
||||
@@ -50,6 +50,5 @@ frappe.ui.form.on('Asset Category', {
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2018-05-08 14:44:37.095570",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
@@ -54,8 +53,7 @@
|
||||
"fieldname": "depreciation_start_date",
|
||||
"fieldtype": "Date",
|
||||
"in_list_view": 1,
|
||||
"label": "Depreciation Posting Date",
|
||||
"reqd": 1
|
||||
"label": "Depreciation Posting Date"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
@@ -83,10 +81,8 @@
|
||||
"label": "Rate of Depreciation"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-09-16 12:11:30.631788",
|
||||
"modified": "2020-12-30 15:43:03.188256",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Assets",
|
||||
"name": "Asset Finance Book",
|
||||
|
||||
@@ -108,7 +108,7 @@ def update_maintenance_log(asset_maintenance, item_code, item_name, task):
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_team_members(doctype, txt, searchfield, start, page_len, filters):
|
||||
return frappe.db.get_values('Maintenance Team Member', { 'parent': filters.get("maintenance_team") })
|
||||
return frappe.db.get_values('Maintenance Team Member', { 'parent': filters.get("maintenance_team") }, "team_member")
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_maintenance_log(asset_name):
|
||||
|
||||
@@ -12,8 +12,8 @@ from frappe.model.document import Document
|
||||
class AssetValueAdjustment(Document):
|
||||
def validate(self):
|
||||
self.validate_date()
|
||||
self.set_difference_amount()
|
||||
self.set_current_asset_value()
|
||||
self.set_difference_amount()
|
||||
|
||||
def on_submit(self):
|
||||
self.make_depreciation_entry()
|
||||
|
||||
@@ -141,31 +141,30 @@ def get_data(filters):
|
||||
|
||||
assets_record = frappe.db.get_all("Asset",
|
||||
filters=conditions,
|
||||
fields=["name", "asset_name", "department", "cost_center", "purchase_receipt",
|
||||
fields=["name as asset_id", "asset_name", "department", "cost_center", "purchase_receipt",
|
||||
"asset_category", "purchase_date", "gross_purchase_amount", "location",
|
||||
"available_for_use_date", "status", "purchase_invoice", "opening_accumulated_depreciation"])
|
||||
|
||||
for asset in assets_record:
|
||||
asset_value = asset.gross_purchase_amount - flt(asset.opening_accumulated_depreciation) \
|
||||
- flt(depreciation_amount_map.get(asset.name))
|
||||
if asset_value:
|
||||
row = {
|
||||
"asset_id": asset.name,
|
||||
"asset_name": asset.asset_name,
|
||||
"status": asset.status,
|
||||
"department": asset.department,
|
||||
"cost_center": asset.cost_center,
|
||||
"vendor_name": pr_supplier_map.get(asset.purchase_receipt) or pi_supplier_map.get(asset.purchase_invoice),
|
||||
"gross_purchase_amount": asset.gross_purchase_amount,
|
||||
"opening_accumulated_depreciation": asset.opening_accumulated_depreciation,
|
||||
"depreciated_amount": depreciation_amount_map.get(asset.name) or 0.0,
|
||||
"available_for_use_date": asset.available_for_use_date,
|
||||
"location": asset.location,
|
||||
"asset_category": asset.asset_category,
|
||||
"purchase_date": asset.purchase_date,
|
||||
"asset_value": asset_value
|
||||
}
|
||||
data.append(row)
|
||||
row = {
|
||||
"asset_id": asset.asset_id,
|
||||
"asset_name": asset.asset_name,
|
||||
"status": asset.status,
|
||||
"department": asset.department,
|
||||
"cost_center": asset.cost_center,
|
||||
"vendor_name": pr_supplier_map.get(asset.purchase_receipt) or pi_supplier_map.get(asset.purchase_invoice),
|
||||
"gross_purchase_amount": asset.gross_purchase_amount,
|
||||
"opening_accumulated_depreciation": asset.opening_accumulated_depreciation,
|
||||
"depreciated_amount": depreciation_amount_map.get(asset.asset_id) or 0.0,
|
||||
"available_for_use_date": asset.available_for_use_date,
|
||||
"location": asset.location,
|
||||
"asset_category": asset.asset_category,
|
||||
"purchase_date": asset.purchase_date,
|
||||
"asset_value": asset_value
|
||||
}
|
||||
data.append(row)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"description": "Settings for Buying Module",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Other",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"supp_master_name",
|
||||
"supplier_group",
|
||||
@@ -92,7 +93,7 @@
|
||||
"icon": "fa fa-cog",
|
||||
"idx": 1,
|
||||
"issingle": 1,
|
||||
"modified": "2019-08-20 13:13:09.055189",
|
||||
"modified": "2021-03-02 18:16:03.947813",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Buying Settings",
|
||||
@@ -107,5 +108,8 @@
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
]
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -355,7 +355,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
||||
material_request_type: "Purchase",
|
||||
docstatus: 1,
|
||||
status: ["!=", "Stopped"],
|
||||
per_ordered: ["<", 99.99],
|
||||
per_ordered: ["<", 100],
|
||||
}
|
||||
})
|
||||
}, __("Get items from"));
|
||||
|
||||
@@ -1056,7 +1056,7 @@
|
||||
"idx": 105,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-09-14 14:36:12.418690",
|
||||
"modified": "2021-01-22 20:27:11.418690",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Purchase Order",
|
||||
|
||||
@@ -366,7 +366,6 @@ def make_purchase_receipt(source_name, target_doc=None):
|
||||
"Purchase Order": {
|
||||
"doctype": "Purchase Receipt",
|
||||
"field_map": {
|
||||
"per_billed": "per_billed",
|
||||
"supplier_warehouse":"supplier_warehouse"
|
||||
},
|
||||
"validation": {
|
||||
|
||||
@@ -89,7 +89,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
frappe.db.set_value("Accounts Settings", None, "over_billing_allowance", 0)
|
||||
|
||||
|
||||
def test_update_child_qty_rate(self):
|
||||
def test_update_child(self):
|
||||
mr = make_material_request(qty=10)
|
||||
po = make_purchase_order(mr.name)
|
||||
po.supplier = "_Test Supplier"
|
||||
@@ -119,7 +119,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3)
|
||||
|
||||
|
||||
def test_add_new_item_in_update_child_qty_rate(self):
|
||||
def test_update_child_adding_new_item(self):
|
||||
po = create_purchase_order(do_not_save=1)
|
||||
po.items[0].qty = 4
|
||||
po.save()
|
||||
@@ -145,7 +145,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
self.assertEqual(po.status, 'To Receive and Bill')
|
||||
|
||||
|
||||
def test_remove_item_in_update_child_qty_rate(self):
|
||||
def test_update_child_removing_item(self):
|
||||
po = create_purchase_order(do_not_save=1)
|
||||
po.items[0].qty = 4
|
||||
po.save()
|
||||
@@ -185,7 +185,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
self.assertEquals(len(po.get('items')), 1)
|
||||
self.assertEqual(po.status, 'To Receive and Bill')
|
||||
|
||||
def test_update_child_qty_rate_perm(self):
|
||||
def test_update_child_perm(self):
|
||||
po = create_purchase_order(item_code= "_Test Item", qty=4)
|
||||
|
||||
user = 'test@example.com'
|
||||
@@ -855,7 +855,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
},
|
||||
{
|
||||
"item_code":item_code,"rm_item_code":"Sub Contracted Raw Material 4","item_name":"_Test Item",
|
||||
"qty":250,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos", "name": po.supplied_items[1].name
|
||||
"qty":250,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos"
|
||||
},
|
||||
]
|
||||
|
||||
@@ -864,6 +864,10 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
se = frappe.get_doc(make_subcontract_transfer_entry(po.name, rm_item_string))
|
||||
se.submit()
|
||||
|
||||
# Test po_detail field has value or not
|
||||
for item_row in se.items:
|
||||
self.assertEqual(item_row.po_detail, po.supplied_items[item_row.idx - 1].name)
|
||||
|
||||
po_doc = frappe.get_doc("Purchase Order", po.name)
|
||||
for row in po_doc.supplied_items:
|
||||
# Valid that whether transferred quantity is matching with supplied qty or not in the purchase order
|
||||
|
||||
@@ -271,7 +271,7 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e
|
||||
material_request_type: "Purchase",
|
||||
docstatus: 1,
|
||||
status: ["!=", "Stopped"],
|
||||
per_ordered: ["<", 99.99]
|
||||
per_ordered: ["<", 100]
|
||||
}
|
||||
})
|
||||
}, __("Get items from"));
|
||||
@@ -316,7 +316,7 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e
|
||||
material_request_type: "Purchase",
|
||||
docstatus: 1,
|
||||
status: ["!=", "Stopped"],
|
||||
per_ordered: ["<", 99.99]
|
||||
per_ordered: ["<", 100]
|
||||
}
|
||||
});
|
||||
$(btn).done_working();
|
||||
|
||||
@@ -108,6 +108,10 @@ class RequestforQuotation(BuyingController):
|
||||
'link_doctype': 'Supplier',
|
||||
'link_name': rfq_supplier.supplier
|
||||
})
|
||||
contact.append('email_ids', {
|
||||
'email_id': user.name,
|
||||
'is_primary': 1
|
||||
})
|
||||
|
||||
if not contact.email_id and not contact.user:
|
||||
contact.email_id = user.name
|
||||
@@ -275,19 +279,21 @@ def add_items(sq_doc, supplier, items):
|
||||
create_rfq_items(sq_doc, supplier, data)
|
||||
|
||||
def create_rfq_items(sq_doc, supplier, data):
|
||||
sq_doc.append('items', {
|
||||
"item_code": data.item_code,
|
||||
"item_name": data.item_name,
|
||||
"description": data.description,
|
||||
"qty": data.qty,
|
||||
"rate": data.rate,
|
||||
"conversion_factor": data.conversion_factor if data.conversion_factor else None,
|
||||
"supplier_part_no": frappe.db.get_value("Item Supplier", {'parent': data.item_code, 'supplier': supplier}, "supplier_part_no"),
|
||||
"warehouse": data.warehouse or '',
|
||||
args = {}
|
||||
|
||||
for field in ['item_code', 'item_name', 'description', 'qty', 'rate', 'conversion_factor',
|
||||
'warehouse', 'material_request', 'material_request_item', 'stock_qty']:
|
||||
args[field] = data.get(field)
|
||||
|
||||
args.update({
|
||||
"request_for_quotation_item": data.name,
|
||||
"request_for_quotation": data.parent
|
||||
"request_for_quotation": data.parent,
|
||||
"supplier_part_no": frappe.db.get_value("Item Supplier",
|
||||
{'parent': data.item_code, 'supplier': supplier}, "supplier_part_no")
|
||||
})
|
||||
|
||||
sq_doc.append('items', args)
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_pdf(doctype, name, supplier_idx):
|
||||
doc = get_rfq_doc(doctype, name, supplier_idx)
|
||||
|
||||
@@ -46,7 +46,7 @@ erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.ext
|
||||
material_request_type: "Purchase",
|
||||
docstatus: 1,
|
||||
status: ["!=", "Stopped"],
|
||||
per_ordered: ["<", 99.99]
|
||||
per_ordered: ["<", 100]
|
||||
}
|
||||
})
|
||||
}, __("Get items from"));
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"actions": [],
|
||||
"autoname": "hash",
|
||||
"creation": "2013-05-22 12:43:10",
|
||||
"doctype": "DocType",
|
||||
@@ -237,7 +236,7 @@
|
||||
"fieldname": "rate",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Rate ",
|
||||
"label": "Rate",
|
||||
"oldfieldname": "import_rate",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "currency"
|
||||
@@ -531,9 +530,9 @@
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-04-07 18:35:51.175947",
|
||||
"modified": "2020-10-19 17:16:06.731729",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Supplier Quotation Item",
|
||||
|
||||
@@ -35,9 +35,7 @@ def update_last_purchase_rate(doc, is_submit):
|
||||
frappe.throw(_("UOM Conversion factor is required in row {0}").format(d.idx))
|
||||
|
||||
# update last purchsae rate
|
||||
if last_purchase_rate:
|
||||
frappe.db.sql("""update `tabItem` set last_purchase_rate = %s where name = %s""",
|
||||
(flt(last_purchase_rate), d.item_code))
|
||||
frappe.db.set_value('Item', d.item_code, 'last_purchase_rate', flt(last_purchase_rate))
|
||||
|
||||
def validate_for_items(doc):
|
||||
items = []
|
||||
|
||||
36
erpnext/change_log/v12/v12_14_0.md
Normal file
36
erpnext/change_log/v12/v12_14_0.md
Normal file
@@ -0,0 +1,36 @@
|
||||
## ERPNext v12.14.0 Release Note
|
||||
|
||||
### Fixes and Enhancements
|
||||
|
||||
- Incorrect backflush qty in manufacture entry ([#23878](https://github.com/frappe/erpnext/pull/23878))
|
||||
- Fuel expense amount of vehicle log ([#23634](https://github.com/frappe/erpnext/pull/23634))
|
||||
- Extra material received against send to warehouse entry ([#23645](https://github.com/frappe/erpnext/pull/23645))
|
||||
- Show accounts in financial statements upto level 20 ([#23719](https://github.com/frappe/erpnext/pull/23719))
|
||||
- Remove Production Order reference from Item Validation ([#23733](https://github.com/frappe/erpnext/pull/23733))
|
||||
- Place of Supply fix in Sales Invoices ([#23786](https://github.com/frappe/erpnext/pull/23786))
|
||||
- Filter Leave Type based on allocation for a particular employee ([#22050](https://github.com/frappe/erpnext/pull/22050))
|
||||
- Incorrect assign to in Maintenance Schedule ([#23830](https://github.com/frappe/erpnext/pull/23830))
|
||||
- Manually set serial nos override with current available serial nos ([#23651](https://github.com/frappe/erpnext/pull/23651))
|
||||
- SO to PO flow improvement ([#23357](https://github.com/frappe/erpnext/pull/23357))
|
||||
- Payment Terms not fetched in Purchase Invoice from Purchase Receipt ([#23866](https://github.com/frappe/erpnext/pull/23866))
|
||||
- Re-linking bank accounts with plaid ([#23913](https://github.com/frappe/erpnext/pull/23913))
|
||||
- Incorrect outstanding amount for multicurrency with Reverse Charge ([#23863](https://github.com/frappe/erpnext/pull/23863))
|
||||
- Overproduction, not allowed to transfer extra materials ([#23647](https://github.com/frappe/erpnext/pull/23647))
|
||||
- Consider rounded_total in returns ([#23631](https://github.com/frappe/erpnext/pull/23631))
|
||||
- Default cost center in item master not set in stock entry ([#23816](https://github.com/frappe/erpnext/pull/23816))
|
||||
- Asset finance book posting date fix ([#23780](https://github.com/frappe/erpnext/pull/23780))
|
||||
- Added column cost_center to receivable reports ([#23837](https://github.com/frappe/erpnext/pull/23837))
|
||||
- Override field_map for job card gantt ([#23740](https://github.com/frappe/erpnext/pull/23740))
|
||||
- Added filter show in website for filtering product ([#23637](https://github.com/frappe/erpnext/pull/23637))
|
||||
- Serial no field is blank in stock reconciliation ([#23646](https://github.com/frappe/erpnext/pull/23646))
|
||||
- Copying po no when mapping doc ([#23730](https://github.com/frappe/erpnext/pull/23730))
|
||||
- Show form buttons only if permissions exist ([#23889](https://github.com/frappe/erpnext/pull/23889))
|
||||
- Cannot auto unlink payments for credit/debit notes ([#23690](https://github.com/frappe/erpnext/pull/23690))
|
||||
- None type error if the Pricing Rule applicable_for is None ([#23664](https://github.com/frappe/erpnext/pull/23664))
|
||||
- Don't copy terms, discount and required by from SO to PO ([#23904](https://github.com/frappe/erpnext/pull/23904))
|
||||
- Add Taxes if missing via Update Items ([#23705](https://github.com/frappe/erpnext/pull/23705))
|
||||
- Don't overrule Item Price via Pricing Rule Rate if 0 ([#23915](https://github.com/frappe/erpnext/pull/23915))
|
||||
- Show only available items in point of sale ([#23667](https://github.com/frappe/erpnext/pull/23667))
|
||||
- Auto State-wise gst tax template ([#23859](https://github.com/frappe/erpnext/pull/23859))
|
||||
- Stock ageing report not working ([#23924](https://github.com/frappe/erpnext/pull/23924))
|
||||
- Validate duplicate packing item in Product Bundle ([#23898](https://github.com/frappe/erpnext/pull/23898))
|
||||
40
erpnext/change_log/v12/v12_15_0.md
Normal file
40
erpnext/change_log/v12/v12_15_0.md
Normal file
@@ -0,0 +1,40 @@
|
||||
## ERPNext v12.15.0 Release Note
|
||||
|
||||
### Fixes and Enhancements
|
||||
|
||||
- BOM stock report color showing always red ([#23993](https://github.com/frappe/erpnext/pull/23993))
|
||||
- Clear error message when approval not availab ([#23972](https://github.com/frappe/erpnext/pull/23972))
|
||||
- Show tax amount in base currencies ([#24071](https://github.com/frappe/erpnext/pull/24071))
|
||||
- Depreciation Posting Date is mandatory even if Calculate Depreciation is not checked ([#24037](https://github.com/frappe/erpnext/pull/24037))
|
||||
- Handle Account and Item None not found in Opening Invoice Creation Tool ([#24103](https://github.com/frappe/erpnext/pull/24103))
|
||||
- Opening invoices in GSTR-1 report ([#24020](https://github.com/frappe/erpnext/pull/24020))
|
||||
- Incorrect balance value in stock balance report ([#23997](https://github.com/frappe/erpnext/pull/23997))
|
||||
- Columns mismatch in AR report([#24085](https://github.com/frappe/erpnext/pull/24085))
|
||||
- Job card error handling for operations field ([#23996](https://github.com/frappe/erpnext/pull/23996))
|
||||
- Set proper state code in ewaybill JSON when GST category is SEZ ([#23954](https://github.com/frappe/erpnext/pull/23954))
|
||||
- PO orverride ([#24023](https://github.com/frappe/erpnext/pull/24023))
|
||||
- Invoice generation for Unpaid subscriptions ([#23966](https://github.com/frappe/erpnext/pull/23966))
|
||||
- Throw an error when no pos profile exist ([#24026](https://github.com/frappe/erpnext/pull/24026))
|
||||
- Purchase receipt to purchase invoice bill date mapping ([#23968](https://github.com/frappe/erpnext/pull/23968))
|
||||
- Validation for duplicate Tax Category ([#24175](https://github.com/frappe/erpnext/pull/24175))
|
||||
- Double exception in payroll ([#24080](https://github.com/frappe/erpnext/pull/24080))
|
||||
- Sales invoice add button on sales order dashboard ([#24081](https://github.com/frappe/erpnext/pull/24081))
|
||||
- Hide Ex-Employees from Employee Tree and minor message UX ([#23927](https://github.com/frappe/erpnext/pull/23927))
|
||||
- Get value of allow_items_in_stock even if not an exact match ([#24099](https://github.com/frappe/erpnext/pull/24099))
|
||||
- Incorrect delink serial no and batch ([#23958](https://github.com/frappe/erpnext/pull/23958))
|
||||
- Pricing rule with transaction not working for additional product ([#24064](https://github.com/frappe/erpnext/pull/24064))
|
||||
- Check if list view standard filter exists in Payment Entry ([#23929](https://github.com/frappe/erpnext/pull/23929))
|
||||
- Do not fetch items until POS Profile is set ([#24076](https://github.com/frappe/erpnext/pull/24076))
|
||||
- Taxation fixes for India ([#24162](https://github.com/frappe/erpnext/pull/24162))
|
||||
- Don't cancel job card if manufacturing entry has made ([#24034](https://github.com/frappe/erpnext/pull/24034))
|
||||
- Payment Reconciliation client side validations ([#23930](https://github.com/frappe/erpnext/pull/23930))
|
||||
- Item Link Formatter Behaviour ([#23931](https://github.com/frappe/erpnext/pull/23931))
|
||||
- Asset with value zero doesn't show up in fixed asset register ([#24098](https://github.com/frappe/erpnext/pull/24098))
|
||||
- Allow add to cart for any item if allow_items_not_in_stock is enabled ([#24084](https://github.com/frappe/erpnext/pull/24084))
|
||||
- Incoming rate for finished good ([#24013](https://github.com/frappe/erpnext/pull/24013))
|
||||
- Incorrect stock ledger entries for stock reco ([#23938](https://github.com/frappe/erpnext/pull/23938))
|
||||
- Function imports in account_balance_timeline.py ([#24097](https://github.com/frappe/erpnext/pull/24097))
|
||||
- Sequence Matcher error in Bank Reconciliation ([#23539](https://github.com/frappe/erpnext/pull/23539))
|
||||
- Shipping charges not sync from shopify ([#24009](https://github.com/frappe/erpnext/pull/24009))
|
||||
- Delete Receive at Warehouse entry on cancellation of Send to War… ([#24068](https://github.com/frappe/erpnext/pull/24068))
|
||||
- Get formatted value in 'taxes' print template ([#24036](https://github.com/frappe/erpnext/pull/24036))
|
||||
11
erpnext/change_log/v12/v12_16_0.md
Normal file
11
erpnext/change_log/v12/v12_16_0.md
Normal file
@@ -0,0 +1,11 @@
|
||||
## ERPNext v12.16.0 Release Note
|
||||
|
||||
### Feature
|
||||
|
||||
- GST E-invoicing for India ([#24184](https://github.com/frappe/erpnext/pull/24184))
|
||||
|
||||
### Fixes
|
||||
|
||||
- Do not override the manually added valuation rate in stock entry ([#24221](https://github.com/frappe/erpnext/pull/24221))
|
||||
|
||||
- Do not manufacture same serial no multiple times ([#24163](https://github.com/frappe/erpnext/pull/24163))
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user