mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-26 04:18:35 +00:00
Compare commits
673 Commits
v12.16.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 | ||
|
|
e7eb9d4e20 | ||
|
|
d94a97b6ad | ||
|
|
d819330443 | ||
|
|
eb88b63ddd | ||
|
|
86bbb3e9a0 | ||
|
|
b9d62d7f8e | ||
|
|
44dd9f6fe5 | ||
|
|
a683b14c64 | ||
|
|
becae59af0 | ||
|
|
e6d300e35c | ||
|
|
c2a41371d2 | ||
|
|
6d3fc0d020 | ||
|
|
81cbd0dc50 | ||
|
|
1bbda81c9f | ||
|
|
21461823f1 | ||
|
|
e9b580aba6 | ||
|
|
7f794e5bb6 | ||
|
|
6db07c0278 | ||
|
|
c28379075a | ||
|
|
64f4f86948 | ||
|
|
983df4463a | ||
|
|
b9c71e59e0 | ||
|
|
8427dd2ca8 | ||
|
|
105a9fdff1 | ||
|
|
bd0cb9d2ed | ||
|
|
ffbb767072 | ||
|
|
723e4f2a22 | ||
|
|
a738e8ca87 | ||
|
|
b0b36d2caa | ||
|
|
0de091a749 | ||
|
|
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 | ||
|
|
3dbf88ba6f | ||
|
|
1cd57a324c | ||
|
|
e8f4eb02be | ||
|
|
f76b0dea44 | ||
|
|
3252d9d943 | ||
|
|
9655edabd9 | ||
|
|
2704327029 | ||
|
|
3e526b8ca8 | ||
|
|
c630be6781 | ||
|
|
51a153d8c2 | ||
|
|
707b630851 | ||
|
|
c82c955bc2 | ||
|
|
dc4b0921f6 | ||
|
|
72ed0fbbb4 | ||
|
|
020e6e980a | ||
|
|
70e0711893 | ||
|
|
8693ac81a2 | ||
|
|
56116f28af | ||
|
|
eeb99d1b98 | ||
|
|
5d23c0ce8e | ||
|
|
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 |
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
|
||||
|
||||
@@ -5,7 +5,7 @@ import frappe
|
||||
from erpnext.hooks import regional_overrides
|
||||
from frappe.utils import getdate
|
||||
|
||||
__version__ = '12.16.0'
|
||||
__version__ = '12.29.0'
|
||||
|
||||
def get_default_company(user=None):
|
||||
'''Get default company for user'''
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -109,5 +109,4 @@ erpnext.integrations.refreshPlaidLink = class refreshPlaidLink {
|
||||
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",
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -131,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:
|
||||
@@ -330,13 +330,17 @@ 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
|
||||
|
||||
@@ -467,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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -247,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)
|
||||
|
||||
@@ -394,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
|
||||
|
||||
|
||||
@@ -434,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,
|
||||
|
||||
@@ -14,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
|
||||
@@ -450,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)
|
||||
@@ -537,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
|
||||
@@ -678,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`
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -1860,7 +1860,17 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
def test_einvoice_submission_without_irn(self):
|
||||
# init
|
||||
frappe.db.set_value('E Invoice Settings', 'E Invoice Settings', 'enable', 1)
|
||||
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()
|
||||
|
||||
country = frappe.flags.country
|
||||
frappe.flags.country = 'India'
|
||||
|
||||
@@ -1871,29 +1881,13 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
si.submit()
|
||||
|
||||
# reset
|
||||
frappe.db.set_value('E Invoice Settings', 'E Invoice Settings', 'enable', 0)
|
||||
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
|
||||
|
||||
customer_gstin = '27AACCM7806M1Z3'
|
||||
customer_gstin_dtls = {
|
||||
'LegalName': '_Test Customer', 'TradeName': '_Test Customer', 'AddrLoc': '_Test City',
|
||||
'StateCode': '27', 'AddrPncd': '410038', 'AddrBno': '_Test Bldg',
|
||||
'AddrBnm': '100', 'AddrFlno': '200', 'AddrSt': '_Test Street'
|
||||
}
|
||||
company_gstin = '27AAECE4835E1ZR'
|
||||
company_gstin_dtls = {
|
||||
'LegalName': '_Test Company', 'TradeName': '_Test Company', 'AddrLoc': '_Test City',
|
||||
'StateCode': '27', 'AddrPncd': '401108', 'AddrBno': '_Test Bldg',
|
||||
'AddrBnm': '100', 'AddrFlno': '200', 'AddrSt': '_Test Street'
|
||||
}
|
||||
# set cache gstin details to avoid fetching details which will require connection to GSP servers
|
||||
frappe.local.gstin_cache = {}
|
||||
frappe.local.gstin_cache[customer_gstin] = customer_gstin_dtls
|
||||
frappe.local.gstin_cache[company_gstin] = company_gstin_dtls
|
||||
|
||||
si = make_sales_invoice_for_ewaybill()
|
||||
si.naming_series = 'INV-2020-.#####'
|
||||
si.items = []
|
||||
@@ -1901,8 +1895,8 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
"item_code": "_Test Item",
|
||||
"uom": "Nos",
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
"qty": 2,
|
||||
"rate": 100,
|
||||
"qty": 2000,
|
||||
"rate": 12,
|
||||
"income_account": "Sales - _TC",
|
||||
"expense_account": "Cost of Goods Sold - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
@@ -1911,31 +1905,50 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
"item_code": "_Test Item 2",
|
||||
"uom": "Nos",
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
"qty": 4,
|
||||
"rate": 150,
|
||||
"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 = sum([d['AssAmt'] for d in einvoice['ItemList']])
|
||||
total_item_cgst_value = sum([d['CgstAmt'] for d in einvoice['ItemList']])
|
||||
total_item_sgst_value = sum([d['SgstAmt'] for d in einvoice['ItemList']])
|
||||
total_item_igst_value = sum([d['IgstAmt'] for d in einvoice['ItemList']])
|
||||
total_item_value = sum([d['TotItemVal'] for d in einvoice['ItemList']])
|
||||
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.assertEqual(einvoice['ValDtls']['AssVal'], total_item_ass_value)
|
||||
self.assertEqual(einvoice['ValDtls']['CgstVal'], total_item_cgst_value)
|
||||
self.assertEqual(einvoice['ValDtls']['SgstVal'], total_item_sgst_value)
|
||||
self.assertEqual(einvoice['ValDtls']['IgstVal'], total_item_igst_value)
|
||||
self.assertEqual(einvoice['ValDtls']['TotInvVal'], total_item_value)
|
||||
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_sales_invoice_for_ewaybill():
|
||||
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",
|
||||
@@ -1983,7 +1996,8 @@ def make_sales_invoice_for_ewaybill():
|
||||
})
|
||||
|
||||
address.save()
|
||||
|
||||
|
||||
def make_test_transporter_for_ewaybill():
|
||||
if not frappe.db.exists('Supplier', '_Test Transporter'):
|
||||
frappe.get_doc({
|
||||
"doctype": "Supplier",
|
||||
@@ -1994,12 +2008,17 @@ def make_sales_invoice_for_ewaybill():
|
||||
"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"})
|
||||
filters = {"company": "_Test Company"}
|
||||
)
|
||||
|
||||
if not gst_account:
|
||||
gst_settings.append("gst_accounts", {
|
||||
@@ -2011,7 +2030,7 @@ def make_sales_invoice_for_ewaybill():
|
||||
|
||||
gst_settings.save()
|
||||
|
||||
si = create_sales_invoice(do_not_save =1, rate = '60000')
|
||||
si = create_sales_invoice(do_not_save=1, rate='60000')
|
||||
|
||||
si.distance = 2000
|
||||
si.company_address = "_Test Address for Eway bill-Billing"
|
||||
@@ -2039,27 +2058,6 @@ def make_sales_invoice_for_ewaybill():
|
||||
|
||||
return si
|
||||
|
||||
def test_item_tax_validity(self):
|
||||
item = frappe.get_doc("Item", "_Test Item 2")
|
||||
|
||||
if item.taxes:
|
||||
item.taxes = []
|
||||
item.save()
|
||||
|
||||
item.append("taxes", {
|
||||
"item_tax_template": "_Test Item Tax Template 1",
|
||||
"valid_from": add_days(nowdate(), 1)
|
||||
})
|
||||
|
||||
item.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)
|
||||
|
||||
item.taxes = []
|
||||
item.save()
|
||||
|
||||
def create_sales_invoice(**args):
|
||||
si = frappe.new_doc("Sales Invoice")
|
||||
args = frappe._dict(args)
|
||||
@@ -2076,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:
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
"additional_discount_percentage",
|
||||
"additional_discount_amount",
|
||||
"sb_3",
|
||||
"submit_invoice",
|
||||
"invoices",
|
||||
"accounting_dimensions_section",
|
||||
"dimension_col_break"
|
||||
@@ -202,9 +203,15 @@
|
||||
"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-11-29 22:46:14.879289",
|
||||
"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
|
||||
@@ -468,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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -2018,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)
|
||||
@@ -2057,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) {
|
||||
|
||||
@@ -156,7 +156,7 @@
|
||||
<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(0, 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>
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -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,9 +53,7 @@
|
||||
"fieldname": "depreciation_start_date",
|
||||
"fieldtype": "Date",
|
||||
"in_list_view": 1,
|
||||
"label": "Depreciation Posting Date",
|
||||
"mandatory_depends_on": "eval:parent.doctype == 'Asset'",
|
||||
"reqd": 1
|
||||
"label": "Depreciation Posting Date"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
@@ -84,10 +81,8 @@
|
||||
"label": "Rate of Depreciation"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-30 15:22:29.119868",
|
||||
"modified": "2020-12-30 15:43:03.188256",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Assets",
|
||||
"name": "Asset Finance Book",
|
||||
|
||||
@@ -141,7 +141,7 @@ 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"])
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
24
erpnext/change_log/v12/v12_17_0.md
Normal file
24
erpnext/change_log/v12/v12_17_0.md
Normal file
@@ -0,0 +1,24 @@
|
||||
## ERPNext v12.17.0 Release Notes
|
||||
|
||||
### Features
|
||||
|
||||
- Separated equity tree in COA SKR04 ([#24094](https://github.com/frappe/erpnext/pull/24094))
|
||||
- Display transporter address in sales invoice ([#23731](https://github.com/frappe/erpnext/pull/23731))
|
||||
- Introduced GST E-Invoicing ([#24184](https://github.com/frappe/erpnext/pull/24184))
|
||||
|
||||
### Fixes
|
||||
|
||||
- Fixed Payment Entry multi-currency issue ([#24331](https://github.com/frappe/erpnext/pull/24331))
|
||||
- Fixed an issue where user could have manufactured same serial no multiple times ([#24163](https://github.com/frappe/erpnext/pull/24163))
|
||||
- Back Update from QC based on Batch No ([#24368](https://github.com/frappe/erpnext/pull/24368))
|
||||
- Fixed tax calculation on salary slip for the first month ([#24309](https://github.com/frappe/erpnext/pull/24309)) ([#24272](https://github.com/frappe/erpnext/pull/24272))
|
||||
- Fixed issues related to e-invoicing ([#24366](https://github.com/frappe/erpnext/pull/24366)) ([#24421](https://github.com/frappe/erpnext/pull/24421)) ([#24284](https://github.com/frappe/erpnext/pull/24284))
|
||||
- Added a validation to restrict manual overriding of valuation rate in Stock Entry ([#24221](https://github.com/frappe/erpnext/pull/24221))
|
||||
- Fixed incorrect serial no. in the subcontracted Purchase Receipt ([#24353](https://github.com/frappe/erpnext/pull/24353))
|
||||
- Fixed an issue where Stock Ledger entry was not getting created against Stock Reconciliation ([#24384](https://github.com/frappe/erpnext/pull/24384))
|
||||
- Fixed company wise Valuation Rate for raw material in BOM ([#24367](https://github.com/frappe/erpnext/pull/24367))
|
||||
- Fixed Taxation related issue ([#24159](https://github.com/frappe/erpnext/pull/24159))
|
||||
- Allowed to override the basic rate for the finished good ([#24301](https://github.com/frappe/erpnext/pull/24301))
|
||||
- Fixed Loyalty Program related issues ([#24188](https://github.com/frappe/erpnext/pull/24188))
|
||||
- Fixed an issue where last purchase rate was not getting updated on canceling last voucher ([#24323](https://github.com/frappe/erpnext/pull/24323))
|
||||
- Fixed issue with pricing rule for offline POS ([#24288](https://github.com/frappe/erpnext/pull/24288))
|
||||
37
erpnext/change_log/v12/v12_18_0.md
Normal file
37
erpnext/change_log/v12/v12_18_0.md
Normal file
@@ -0,0 +1,37 @@
|
||||
## ERPNext v12.18.0 Release Notes
|
||||
|
||||
### Enhancements
|
||||
|
||||
- Make patient age translatable ([#24416](https://github.com/frappe/erpnext/pull/24416))
|
||||
- Adding UOM, Item Group via Update Items ([#24479](https://github.com/frappe/erpnext/pull/24479))
|
||||
|
||||
### Fixes
|
||||
|
||||
- Incorrect incoming rate for the sales return ([#24620](https://github.com/frappe/erpnext/pull/24620))
|
||||
- Prorata factor fixes in subscription ([#24638](https://github.com/frappe/erpnext/pull/24638))
|
||||
- QR code image generation for e-invoicing ([#24422](https://github.com/frappe/erpnext/pull/24422))
|
||||
- Validation for disabled warehouse ([#24546](https://github.com/frappe/erpnext/pull/24546))
|
||||
- Update total in words after updating items ([#24592](https://github.com/frappe/erpnext/pull/24592))
|
||||
- Set contact email in RFQ ([#24486](https://github.com/frappe/erpnext/pull/24486))
|
||||
- Plaid client version to support latest API ([#24532](https://github.com/frappe/erpnext/pull/24532))
|
||||
- Dynamic Links for reports ([#24461](https://github.com/frappe/erpnext/pull/24461))
|
||||
- Use supplied year for IRS 1099 forms ([#24425](https://github.com/frappe/erpnext/pull/24425))
|
||||
- Add check for allowing access to european region ([#24393](https://github.com/frappe/erpnext/pull/24393))
|
||||
- Item-wise Sales Register item_name error ([#24484](https://github.com/frappe/erpnext/pull/24484))
|
||||
- Skip e-invoice generation for non-taxable invoices (India) ([#24569](https://github.com/frappe/erpnext/pull/24569))
|
||||
- Issues with packing items ([#24606](https://github.com/frappe/erpnext/pull/24606))
|
||||
- Remove max 5 file attachment limit in task ([#24056](https://github.com/frappe/erpnext/pull/24056))
|
||||
- Calculate discount amount ([#24511](https://github.com/frappe/erpnext/pull/24511))
|
||||
- Validate tax template for tax category ([#24403](https://github.com/frappe/erpnext/pull/24403))
|
||||
- Do not validate gstin for exports (India) ([#24564](https://github.com/frappe/erpnext/pull/24564))
|
||||
- Stock ageing should not take cancelled stock entries. ([#24438](https://github.com/frappe/erpnext/pull/24438))
|
||||
- Discount amount calculation on net total ([#24498](https://github.com/frappe/erpnext/pull/24498))
|
||||
- Fetching of standalone cr/dr notes for reconciliation ([#24576](https://github.com/frappe/erpnext/pull/24576))
|
||||
- Avoid changing Ref. Doctype in Accounting Dimension after creation ([#24579](https://github.com/frappe/erpnext/pull/24579))
|
||||
- Add GST state code for Ladakh (India) ([#24635](https://github.com/frappe/erpnext/pull/24635))
|
||||
- Consolidated Financial Statement report not works if child company accounts not present in the parent company ([#24580](https://github.com/frappe/erpnext/pull/24580))
|
||||
- Missing Asset Id in the Fixed Asset Register Report ([#24391](https://github.com/frappe/erpnext/pull/24391))
|
||||
- e_invoice print format not showing other charges ([#24473](https://github.com/frappe/erpnext/pull/24473))
|
||||
- Fix filters for report IRS 1099 ([#24597](https://github.com/frappe/erpnext/pull/24597))
|
||||
- Stock ledger entry was not created against stock reconciliation ([#24382](https://github.com/frappe/erpnext/pull/24382))
|
||||
- Validate cancellation only if irn generated (India) ([#24609](https://github.com/frappe/erpnext/pull/24609))
|
||||
34
erpnext/change_log/v12/v12_20_0.md
Normal file
34
erpnext/change_log/v12/v12_20_0.md
Normal file
@@ -0,0 +1,34 @@
|
||||
## Version 12.20.0 Release Notes
|
||||
|
||||
|
||||
### Fixes & Enhancements
|
||||
|
||||
- E-invoicing - Validations & tax calculation fixes ([#25315](https://github.com/frappe/erpnext/pull/25315))
|
||||
- Allow zero valuation in stock reconciliation ([#24986](https://github.com/frappe/erpnext/pull/24986))
|
||||
- Remove shipping address GSTIN validation for e-invoice ([#25133](https://github.com/frappe/erpnext/pull/25133))
|
||||
- Allow Item image alt ([#24935](https://github.com/frappe/erpnext/pull/24935))
|
||||
- Added parent task expected end date validation ([#24889](https://github.com/frappe/erpnext/pull/24889))
|
||||
- Incorrect Nil Exempt and Non GST amount in GSTR3B report ([#24919](https://github.com/frappe/erpnext/pull/24919))
|
||||
- Item attributes not editable until refresh ([#24887](https://github.com/frappe/erpnext/pull/24887))
|
||||
- Create property setters for shorter naming series ([#25134](https://github.com/frappe/erpnext/pull/25134))
|
||||
- Serial no refresh issue ([#25130](https://github.com/frappe/erpnext/pull/25130))
|
||||
- Sales Order not saving due type mismatch in promo scheme (#24748) ([#25056](https://github.com/frappe/erpnext/pull/25056))
|
||||
- TDS check getting checked after reload ([#24974](https://github.com/frappe/erpnext/pull/24974))
|
||||
- Round total quantity in job card ([#25246](https://github.com/frappe/erpnext/pull/25246))
|
||||
- santize_for_json is not defined ([#25157](https://github.com/frappe/erpnext/pull/25157))
|
||||
- Apply single transaction threshold on net_total instead of supplier credit amount ([#25208](https://github.com/frappe/erpnext/pull/25208))
|
||||
- Zero amount completed delivery notes being shown in Sales Invoice get items ([#25318](https://github.com/frappe/erpnext/pull/25318))
|
||||
- Error message compensatory leave request ([#25216](https://github.com/frappe/erpnext/pull/25216))
|
||||
- RCM tax calculation ([#25226](https://github.com/frappe/erpnext/pull/25226))
|
||||
- Don't delete mode of payment account details while deleting comp… ([#25218](https://github.com/frappe/erpnext/pull/25218))
|
||||
- Opportunity-quotation mapping order status ([#25002](https://github.com/frappe/erpnext/pull/25002))
|
||||
- Object referencing same memory address issue ([#25165](https://github.com/frappe/erpnext/pull/25165))
|
||||
- Validation msg for TransDocNo e-invoicing ([#25120](https://github.com/frappe/erpnext/pull/25120))
|
||||
- Do not fetch stopped MR in production plan ([#25110](https://github.com/frappe/erpnext/pull/25110))
|
||||
- Incorrect status creating PR from PO after creating PI ([#25203](https://github.com/frappe/erpnext/pull/25203))
|
||||
- Remove gst name validation for purchase Invoice ([#25236](https://github.com/frappe/erpnext/pull/25236))
|
||||
- Assignment Rule Unassign Condition doesn't work ([#24890](https://github.com/frappe/erpnext/pull/24890))
|
||||
- Place of supply for e-invoicing ([#25149](https://github.com/frappe/erpnext/pull/25149))
|
||||
- Serial no trim issue ([#24950](https://github.com/frappe/erpnext/pull/24950))
|
||||
- Commit individual SLE rename for large datasets (v12) ([#25085](https://github.com/frappe/erpnext/pull/25085))
|
||||
- Incorrect batch picked in subcontracted purchase receipt ([#25169](https://github.com/frappe/erpnext/pull/25169))
|
||||
21
erpnext/change_log/v12/v12_21_0.md
Normal file
21
erpnext/change_log/v12/v12_21_0.md
Normal file
@@ -0,0 +1,21 @@
|
||||
## Version 12.21.0 Release Notes
|
||||
|
||||
|
||||
### Fixes & Enhancements
|
||||
|
||||
- Incorrect qty calculated for sub-contracted raw materials in purchase receipt ([#25443](https://github.com/frappe/erpnext/pull/25443))
|
||||
- Update cost center in the item table fetched from POS Profile in v12 ([#25612](https://github.com/frappe/erpnext/pull/25612))
|
||||
- Total stock summary report not working ([#25552](https://github.com/frappe/erpnext/pull/25552))
|
||||
- Timeout error while loading warehouse tree ([#25693](https://github.com/frappe/erpnext/pull/25693))
|
||||
- RCM rounding precision ([#25410](https://github.com/frappe/erpnext/pull/25410))
|
||||
- Change subcontracted item display ([#25426](https://github.com/frappe/erpnext/pull/25426))
|
||||
- Remove invalid changes added due to merge conflict ([#25437](https://github.com/frappe/erpnext/pull/25437))
|
||||
- Add document type field for e-invoicing (Italy) ([#25420](https://github.com/frappe/erpnext/pull/25420))
|
||||
- Issue in project custom status ([#25453](https://github.com/frappe/erpnext/pull/25453))
|
||||
- Employee Separation ([#25504](https://github.com/frappe/erpnext/pull/25504))
|
||||
- State code for Other Territory ([#25422](https://github.com/frappe/erpnext/pull/25422))
|
||||
- Remove invalid changes added due to merge conflict ([#25405](https://github.com/frappe/erpnext/pull/25405))
|
||||
- Check for None in item.schedule_date before setting ([#25589](https://github.com/frappe/erpnext/pull/25589))
|
||||
- Can't multiply sequence by non-int of type 'float' ([#25385](https://github.com/frappe/erpnext/pull/25385))
|
||||
- Filter using purpose, make requested changes ([#25388](https://github.com/frappe/erpnext/pull/25388))
|
||||
- Purchase from registered composition dealer ([#25419](https://github.com/frappe/erpnext/pull/25419))
|
||||
16
erpnext/change_log/v12/v12_22_0.md
Normal file
16
erpnext/change_log/v12/v12_22_0.md
Normal file
@@ -0,0 +1,16 @@
|
||||
## Version 12.22.0 Release Notes
|
||||
|
||||
### Fixes & Enhancements
|
||||
|
||||
- Cost-center wise period closing entry ([#25930](https://github.com/frappe/erpnext/pull/25930))
|
||||
- Wrong round off gl entry posted in case of purchase invoice ([#25952](https://github.com/frappe/erpnext/pull/25952))
|
||||
- Sync shopify customer addresses (#25481) ([#25937](https://github.com/frappe/erpnext/pull/25937))
|
||||
- Plaid NoneType error ([#25662](https://github.com/frappe/erpnext/pull/25662))
|
||||
- Cashlfow mapper not showing data ([#25739](https://github.com/frappe/erpnext/pull/25739))
|
||||
- Update shopify api version (#25600) ([#25939](https://github.com/frappe/erpnext/pull/25939))
|
||||
- Invalid 'depends_on' expression in opportunity ([#25954](https://github.com/frappe/erpnext/pull/25954))
|
||||
- Ignore rounding diff while importing JV using data import ([#25715](https://github.com/frappe/erpnext/pull/25715))
|
||||
- update cost center from POS ([#25972](https://github.com/frappe/erpnext/pull/25972))
|
||||
- update employee field on renaming employee ([#25958](https://github.com/frappe/erpnext/pull/25958))
|
||||
- student invalid password reset link ([#25827](https://github.com/frappe/erpnext/pull/25827))
|
||||
- Backward compatibility for GSTR-1 report ([#25913](https://github.com/frappe/erpnext/pull/25913))
|
||||
20
erpnext/change_log/v12/v12_23_0.md
Normal file
20
erpnext/change_log/v12/v12_23_0.md
Normal file
@@ -0,0 +1,20 @@
|
||||
## Version 12.23.0 Release Notes
|
||||
|
||||
### Fixes & Enhancements
|
||||
- Added Permissions for employee to book an appointment ([#26246](https://github.com/frappe/erpnext/pull/26246))
|
||||
- New check field in subscriptions for (not) submitting invoices (BP #25394) ([#25560](https://github.com/frappe/erpnext/pull/25560))
|
||||
- fix(e-invoicing): allow export invoice even if no taxes applied (#26363) ([#26406](https://github.com/frappe/erpnext/pull/26406))
|
||||
- Omit item discount amount for e-invoicing (#26353) ([#26408](https://github.com/frappe/erpnext/pull/26408))
|
||||
- fix(plaid): cannot reset plaid link for a bank account ([#26282](https://github.com/frappe/erpnext/pull/26282))
|
||||
- Job applicant link issue ([#25935](https://github.com/frappe/erpnext/pull/25935))
|
||||
- LMS progress issue ([#26254](https://github.com/frappe/erpnext/pull/26254))
|
||||
- Half day to be accounted in its leave type ([#26267](https://github.com/frappe/erpnext/pull/26267))
|
||||
- Material request status issue ([#26089](https://github.com/frappe/erpnext/pull/26089))
|
||||
- fix(e-invoicing): service item check ([#26141](https://github.com/frappe/erpnext/pull/26141))
|
||||
- Invoices can alter profit and loss of a closed year ([#26161](https://github.com/frappe/erpnext/pull/26161))
|
||||
- Material request and supplier quotation not linked if supplier quotation created from supplier portal ([#26117](https://github.com/frappe/erpnext/pull/26117))
|
||||
- Update positions in default cashflow mappers ([#26091](https://github.com/frappe/erpnext/pull/26091))
|
||||
- Staffing plan vacancies data type issue ([#25940](https://github.com/frappe/erpnext/pull/25940))
|
||||
- Added company filter while fetching loans ([#26296](https://github.com/frappe/erpnext/pull/26296))
|
||||
- Serial no issue in subcontract purchase receipt ([#26423](https://github.com/frappe/erpnext/pull/26423))
|
||||
- Fixed rounding off ordered percent to 100 in condition ([#26153](https://github.com/frappe/erpnext/pull/26153))
|
||||
13
erpnext/change_log/v12/v12_24_0.md
Normal file
13
erpnext/change_log/v12/v12_24_0.md
Normal file
@@ -0,0 +1,13 @@
|
||||
## Version 12.24.0 Release Notes
|
||||
|
||||
### Fixes & Enhancements
|
||||
- Sales order qty update fails in "Update Items" button ([#26992](https://github.com/frappe/erpnext/pull/26992))
|
||||
- Speed up validate_account_for_perpetual_inventory ([#26730](https://github.com/frappe/erpnext/pull/26730))
|
||||
- Deadlock while doing payment reconciliation ([#26674](https://github.com/frappe/erpnext/pull/26674))
|
||||
- Remove incorrect condition in GLE comparison ([#26713](https://github.com/frappe/erpnext/pull/26713))
|
||||
- Cannot cancel invoice if IRN cancelled on portal ([#26880](https://github.com/frappe/erpnext/pull/26880))
|
||||
- Item name is missing into job card ([#26956](https://github.com/frappe/erpnext/pull/26956))
|
||||
- Removed company filter for Loan Type ([#26463](https://github.com/frappe/erpnext/pull/26463))
|
||||
- Exchange rate revaluation posting date and precision fixes ([#26629](https://github.com/frappe/erpnext/pull/26629))
|
||||
- Fixed clearing issue of payment references on setting cost center ([#26548](https://github.com/frappe/erpnext/pull/26548))
|
||||
- Increase number of supported currency exchanges ([#25722](https://github.com/frappe/erpnext/pull/25722))
|
||||
10
erpnext/change_log/v12/v12_25_0.md
Normal file
10
erpnext/change_log/v12/v12_25_0.md
Normal file
@@ -0,0 +1,10 @@
|
||||
## Version 12.25.0 Release Notes
|
||||
|
||||
### Fixes & Enhancements
|
||||
- Multiple price rules margin. ([#24844](https://github.com/frappe/erpnext/pull/24844))
|
||||
- Document naming rule not working for subscription invoices ([#27394](https://github.com/frappe/erpnext/pull/27394))
|
||||
- Prematurely referenced variable in buying controller for subcontracting ([#27333](https://github.com/frappe/erpnext/pull/27333))
|
||||
- Calculation of gross profit percentage in Gross Profit Report ([#26713](https://github.com/frappe/erpnext/pull/27045))
|
||||
- Price list rate not fetched for return sales invoice fixed ([#26593](https://github.com/frappe/erpnext/pull/26593))
|
||||
- Set production plan to completed even on over production ([#27027](https://github.com/frappe/erpnext/pull/27027))
|
||||
- Add `total_billing_hours` to Sales Invoice ([#26652](https://github.com/frappe/erpnext/pull/26652))
|
||||
8
erpnext/change_log/v12/v12_26_0.md
Normal file
8
erpnext/change_log/v12/v12_26_0.md
Normal file
@@ -0,0 +1,8 @@
|
||||
## ERPNext Version 12.26.0 Release Notes
|
||||
|
||||
### Fixes & Enhancements
|
||||
- Make Gross Profit Report more readable ([#27124](https://github.com/frappe/erpnext/pull/27124))
|
||||
- Set item uom as stock_uom if it isn't set ([#27623](https://github.com/frappe/erpnext/pull/27623))
|
||||
- Adding empty row on new maintenance visit ([#27626](https://github.com/frappe/erpnext/pull/27626))
|
||||
- Employee Leave Balance report should only consider ledgers of transaction type Leave Allocation ([#28017](https://github.com/frappe/erpnext/pull/28017))
|
||||
- Validate if item exists on uploading items in stock reco ([#27538](https://github.com/frappe/erpnext/pull/27538))
|
||||
9
erpnext/change_log/v12/v12_27_0.md
Normal file
9
erpnext/change_log/v12/v12_27_0.md
Normal file
@@ -0,0 +1,9 @@
|
||||
## ERPNext Version 12.27.0 Release Notes
|
||||
|
||||
### Fixes & Enhancements
|
||||
- Always expect signature in webhook requests for WooCommerce ([#28367](https://github.com/frappe/erpnext/pull/28367))
|
||||
- Debit & Credit currency in bank transaction ([#28574](https://github.com/frappe/erpnext/pull/28574))
|
||||
- Incorrect balance in "Warehouse Wise Item Balance and Age" report ([#28583](https://github.com/frappe/erpnext/pull/28583))
|
||||
- Check if gst_category exists while validating GSTIN ([#28065](https://github.com/frappe/erpnext/pull/28065))
|
||||
- Skip empty rows while updating unsaved BOM cost ([#28136](https://github.com/frappe/erpnext/pull/28136))
|
||||
- Remove warehouse filter on Batch field for Material Receipt ([#28195](https://github.com/frappe/erpnext/pull/28195))
|
||||
7
erpnext/change_log/v12/v12_28_0.md
Normal file
7
erpnext/change_log/v12/v12_28_0.md
Normal file
@@ -0,0 +1,7 @@
|
||||
## ERPNext Version 12.28.0 Release Notes
|
||||
|
||||
### Fixes & Enhancements
|
||||
- Set cost center for credit entries while posting Depreciation Entries ([#28908](https://github.com/frappe/erpnext/pull/28908))
|
||||
- Incorrect amount based on Payment Days in timesheet based salary slip ([#28884](https://github.com/frappe/erpnext/pull/28884))
|
||||
- Removed rename feature for the Warehouse document ([#28712](https://github.com/frappe/erpnext/pull/28712))
|
||||
- Actual tax conversion in case of multicurrency invoices ([#28539](https://github.com/frappe/erpnext/pull/28539))
|
||||
5
erpnext/change_log/v12/v12_29_0.md
Normal file
5
erpnext/change_log/v12/v12_29_0.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## ERPNext Version 12.29.0 Release Notes
|
||||
|
||||
### Fixes & Enhancements
|
||||
|
||||
- Display 'Make Depreciation Entry' only for submitted or partially depreciated Assets ([#29291](https://github.com/frappe/erpnext/pull/29291))
|
||||
@@ -1185,43 +1185,28 @@ def add_taxes_from_tax_template(child_item, parent_doc):
|
||||
})
|
||||
tax_row.db_insert()
|
||||
|
||||
def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, trans_item):
|
||||
def set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child_docname, trans_item):
|
||||
"""
|
||||
Returns a Sales Order Item child item containing the default values
|
||||
Returns a Sales/Purchase Order Item child item containing the default values
|
||||
"""
|
||||
p_doc = frappe.get_doc(parent_doctype, parent_doctype_name)
|
||||
child_item = frappe.new_doc('Sales Order Item', p_doc, child_docname)
|
||||
child_item = frappe.new_doc(child_doctype, p_doc, child_docname)
|
||||
item = frappe.get_doc("Item", trans_item.get('item_code'))
|
||||
child_item.item_code = item.item_code
|
||||
child_item.item_name = item.item_name
|
||||
child_item.description = item.description
|
||||
child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date
|
||||
child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0
|
||||
child_item.uom = item.stock_uom
|
||||
set_child_tax_template_and_map(item, child_item, p_doc)
|
||||
add_taxes_from_tax_template(child_item, p_doc)
|
||||
child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True)
|
||||
if not child_item.warehouse:
|
||||
frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.")
|
||||
.format(frappe.bold("default warehouse"), frappe.bold(item.item_code)))
|
||||
return child_item
|
||||
|
||||
|
||||
def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, trans_item):
|
||||
"""
|
||||
Returns a Purchase Order Item child item containing the default values
|
||||
"""
|
||||
p_doc = frappe.get_doc(parent_doctype, parent_doctype_name)
|
||||
child_item = frappe.new_doc('Purchase Order Item', p_doc, child_docname)
|
||||
item = frappe.get_doc("Item", trans_item.get('item_code'))
|
||||
child_item.item_code = item.item_code
|
||||
child_item.item_name = item.item_name
|
||||
child_item.description = item.description
|
||||
child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date
|
||||
child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0
|
||||
child_item.uom = item.stock_uom
|
||||
child_item.base_rate = 1 # Initiallize value will update in parent validation
|
||||
child_item.base_amount = 1 # Initiallize value will update in parent validation
|
||||
for field in ("item_code", "item_name", "description", "item_group"):
|
||||
child_item.update({field: item.get(field)})
|
||||
date_fieldname = "delivery_date" if child_doctype == "Sales Order Item" else "schedule_date"
|
||||
child_item.update({date_fieldname: trans_item.get(date_fieldname) or p_doc.get(date_fieldname)})
|
||||
child_item.uom = trans_item.get("uom") or item.stock_uom
|
||||
conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor"))
|
||||
child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or conversion_factor
|
||||
if child_doctype == "Purchase Order Item":
|
||||
child_item.base_rate = 1 # Initiallize value will update in parent validation
|
||||
child_item.base_amount = 1 # Initiallize value will update in parent validation
|
||||
if child_doctype == "Sales Order Item":
|
||||
child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True)
|
||||
if not child_item.warehouse:
|
||||
frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.")
|
||||
.format(frappe.bold("default warehouse"), frappe.bold(item.item_code)))
|
||||
set_child_tax_template_and_map(item, child_item, p_doc)
|
||||
add_taxes_from_tax_template(child_item, p_doc)
|
||||
return child_item
|
||||
@@ -1285,8 +1270,8 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
||||
)
|
||||
|
||||
def get_new_child_item(item_row):
|
||||
new_child_function = set_sales_order_defaults if parent_doctype == "Sales Order" else set_purchase_order_defaults
|
||||
return new_child_function(parent_doctype, parent_doctype_name, child_docname, item_row)
|
||||
child_doctype = "Sales Order Item" if parent_doctype == "Sales Order" else "Purchase Order Item"
|
||||
return set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child_docname, item_row)
|
||||
|
||||
def validate_quantity(child_item, d):
|
||||
if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty):
|
||||
@@ -1305,6 +1290,11 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
||||
|
||||
for d in data:
|
||||
new_child_flag = False
|
||||
|
||||
if not d.get("item_code"):
|
||||
# ignore empty rows
|
||||
continue
|
||||
|
||||
if not d.get("docname"):
|
||||
new_child_flag = True
|
||||
check_doc_permissions(parent, 'create')
|
||||
@@ -1316,6 +1306,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
||||
prev_rate, new_rate = flt(child_item.get("rate")), flt(d.get("rate"))
|
||||
prev_qty, new_qty = flt(child_item.get("qty")), flt(d.get("qty"))
|
||||
prev_con_fac, new_con_fac = flt(child_item.get("conversion_factor")), flt(d.get("conversion_factor"))
|
||||
prev_uom, new_uom = child_item.get("uom"), d.get("uom")
|
||||
|
||||
if parent_doctype == 'Sales Order':
|
||||
prev_date, new_date = child_item.get("delivery_date"), d.get("delivery_date")
|
||||
@@ -1324,9 +1315,10 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
||||
|
||||
rate_unchanged = prev_rate == new_rate
|
||||
qty_unchanged = prev_qty == new_qty
|
||||
uom_unchanged = prev_uom == new_uom
|
||||
conversion_factor_unchanged = prev_con_fac == new_con_fac
|
||||
date_unchanged = prev_date == new_date if prev_date and new_date else False # in case of delivery note etc
|
||||
if rate_unchanged and qty_unchanged and conversion_factor_unchanged and date_unchanged:
|
||||
date_unchanged = prev_date == getdate(new_date) if prev_date and new_date else False # in case of delivery note etc
|
||||
if rate_unchanged and qty_unchanged and conversion_factor_unchanged and uom_unchanged and date_unchanged:
|
||||
continue
|
||||
|
||||
validate_quantity(child_item, d)
|
||||
@@ -1347,6 +1339,11 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
||||
child_item.conversion_factor = 1
|
||||
else:
|
||||
child_item.conversion_factor = flt(d.get('conversion_factor'), conv_fac_precision)
|
||||
|
||||
if d.get("uom"):
|
||||
child_item.uom = d.get("uom")
|
||||
conversion_factor = flt(get_conversion_factor(child_item.item_code, child_item.uom).get("conversion_factor"))
|
||||
child_item.conversion_factor = flt(d.get('conversion_factor')) or conversion_factor
|
||||
|
||||
if d.get("delivery_date") and parent_doctype == 'Sales Order':
|
||||
child_item.delivery_date = d.get('delivery_date')
|
||||
@@ -1388,6 +1385,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
||||
parent.flags.ignore_validate_update_after_submit = True
|
||||
parent.set_qty_as_per_stock_uom()
|
||||
parent.calculate_taxes_and_totals()
|
||||
parent.set_total_in_words()
|
||||
if parent_doctype == "Sales Order":
|
||||
make_packing_list(parent)
|
||||
parent.set_gross_profit()
|
||||
@@ -1413,6 +1411,8 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
||||
parent.update_receiving_percentage()
|
||||
if parent.is_subcontracted == "Yes":
|
||||
parent.update_reserved_qty_for_subcontract()
|
||||
parent.create_raw_materials_supplied("supplied_items")
|
||||
parent.save()
|
||||
else:
|
||||
parent.update_reserved_qty()
|
||||
parent.update_project()
|
||||
|
||||
@@ -6,6 +6,7 @@ import frappe
|
||||
from frappe import _, msgprint
|
||||
from frappe.utils import flt,cint, cstr, getdate
|
||||
from six import iteritems
|
||||
from collections import OrderedDict
|
||||
from erpnext.accounts.party import get_party_details
|
||||
from erpnext.stock.get_item_details import get_conversion_factor
|
||||
from erpnext.buying.utils import validate_for_items, update_last_purchase_rate
|
||||
@@ -294,9 +295,12 @@ class BuyingController(StockController):
|
||||
for raw_material in transferred_raw_materials + non_stock_items:
|
||||
rm_item_key = (raw_material.rm_item_code, item.item_code, item.purchase_order)
|
||||
raw_material_data = backflushed_raw_materials_map.get(rm_item_key, {})
|
||||
if not raw_material_data and raw_material.get('batch_nos'):
|
||||
backflushed_raw_materials_map.setdefault(rm_item_key, {'consumed_batch': {}})
|
||||
raw_material_data = backflushed_raw_materials_map.get(rm_item_key, {})
|
||||
|
||||
consumed_qty = raw_material_data.get('qty', 0)
|
||||
consumed_serial_nos = raw_material_data.get('serial_nos', '')
|
||||
consumed_serial_nos = raw_material_data.get('serial_no', '')
|
||||
consumed_batch_nos = raw_material_data.get('batch_nos', '')
|
||||
|
||||
transferred_qty = raw_material.qty
|
||||
@@ -325,14 +329,17 @@ class BuyingController(StockController):
|
||||
|
||||
batches_qty = get_batches_with_qty(raw_material.rm_item_code, raw_material.main_item_code,
|
||||
qty, transferred_batch_qty_map, backflushed_batch_qty_map, item.purchase_order)
|
||||
|
||||
for batch_data in batches_qty:
|
||||
qty = batch_data['qty']
|
||||
raw_material.batch_no = batch_data['batch']
|
||||
self.append_raw_material_to_be_backflushed(item, raw_material, qty)
|
||||
if qty > 0:
|
||||
self.append_raw_material_to_be_backflushed(item, raw_material, qty)
|
||||
else:
|
||||
self.append_raw_material_to_be_backflushed(item, raw_material, qty)
|
||||
|
||||
def append_raw_material_to_be_backflushed(self, fg_item_doc, raw_material_data, qty):
|
||||
qty = flt(qty, fg_item_doc.precision('qty'))
|
||||
rm = self.append('supplied_items', {})
|
||||
rm.update(raw_material_data)
|
||||
|
||||
@@ -790,9 +797,10 @@ class BuyingController(StockController):
|
||||
if not self.get("items"):
|
||||
return
|
||||
|
||||
earliest_schedule_date = min([d.schedule_date for d in self.get("items")])
|
||||
if earliest_schedule_date:
|
||||
self.schedule_date = earliest_schedule_date
|
||||
if any(d.schedule_date for d in self.get("items")):
|
||||
# Select earliest schedule_date.
|
||||
self.schedule_date = min(d.schedule_date for d in self.get("items")
|
||||
if d.schedule_date is not None)
|
||||
|
||||
if self.schedule_date:
|
||||
for d in self.get('items'):
|
||||
@@ -973,8 +981,16 @@ def get_non_stock_items(purchase_order, fg_item_code):
|
||||
|
||||
|
||||
def set_serial_nos(raw_material, consumed_serial_nos, qty):
|
||||
serial_nos = set(get_serial_nos(raw_material.serial_nos)) - \
|
||||
set(get_serial_nos(consumed_serial_nos))
|
||||
consumed_serial_nos_list = []
|
||||
|
||||
if consumed_serial_nos and isinstance(consumed_serial_nos, list):
|
||||
for row in consumed_serial_nos:
|
||||
consumed_serial_nos_list.extend(get_serial_nos(row))
|
||||
elif consumed_serial_nos:
|
||||
consumed_serial_nos_list = get_serial_nos(consumed_serial_nos)
|
||||
|
||||
serial_nos = set(get_serial_nos(raw_material.serial_nos)) - set(consumed_serial_nos_list)
|
||||
|
||||
if serial_nos and qty <= len(serial_nos):
|
||||
raw_material.serial_no = '\n'.join(list(serial_nos)[0:frappe.utils.cint(qty)])
|
||||
|
||||
@@ -1009,7 +1025,7 @@ def get_transferred_batch_qty_map(purchase_order, fg_item):
|
||||
for batch_data in transferred_batches:
|
||||
key = ((batch_data.item_code, fg_item)
|
||||
if batch_data.subcontracted_item else (batch_data.item_code, purchase_order))
|
||||
transferred_batch_qty_map.setdefault(key, {})
|
||||
transferred_batch_qty_map.setdefault(key, OrderedDict())
|
||||
transferred_batch_qty_map[key][batch_data.batch_no] = batch_data.qty
|
||||
|
||||
return transferred_batch_qty_map
|
||||
@@ -1062,8 +1078,14 @@ def get_batches_with_qty(item_code, fg_item, required_qty, transferred_batch_qty
|
||||
if available_qty >= required_qty:
|
||||
available_batches.append({'batch': batch, 'qty': required_qty})
|
||||
break
|
||||
else:
|
||||
elif available_qty != 0:
|
||||
available_batches.append({'batch': batch, 'qty': available_qty})
|
||||
required_qty -= available_qty
|
||||
|
||||
for row in available_batches:
|
||||
if backflushed_batches.get(row.get('batch'), 0) > 0:
|
||||
backflushed_batches[row.get('batch')] += row.get('qty')
|
||||
else:
|
||||
backflushed_batches.setdefault(row.get('batch'), row.get('qty'))
|
||||
|
||||
return available_batches
|
||||
|
||||
@@ -320,7 +320,7 @@ def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len,
|
||||
and status not in ("Stopped", "Closed") %(fcond)s
|
||||
and (
|
||||
(`tabDelivery Note`.is_return = 0 and `tabDelivery Note`.per_billed < 100)
|
||||
or `tabDelivery Note`.grand_total = 0
|
||||
or (`tabDelivery Note`.grand_total = 0 and `tabDelivery Note`.per_billed < 100)
|
||||
or (
|
||||
`tabDelivery Note`.is_return = 1
|
||||
and return_against in (select name from `tabDelivery Note` where per_billed < 100)
|
||||
|
||||
@@ -279,6 +279,7 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
||||
target_doc.po_detail = source_doc.po_detail
|
||||
target_doc.pr_detail = source_doc.pr_detail
|
||||
target_doc.purchase_invoice_item = source_doc.name
|
||||
target_doc.price_list_rate = 0
|
||||
|
||||
elif doctype == "Delivery Note":
|
||||
target_doc.against_sales_order = source_doc.against_sales_order
|
||||
@@ -297,6 +298,7 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
||||
target_doc.dn_detail = source_doc.dn_detail
|
||||
target_doc.expense_account = source_doc.expense_account
|
||||
target_doc.sales_invoice_item = source_doc.name
|
||||
target_doc.price_list_rate = 0
|
||||
if default_warehouse_for_sales_return:
|
||||
target_doc.warehouse = default_warehouse_for_sales_return
|
||||
|
||||
|
||||
@@ -310,12 +310,27 @@ class SellingController(StockController):
|
||||
if flt(d.conversion_factor)==0.0:
|
||||
d.conversion_factor = get_conversion_factor(d.item_code, d.uom).get("conversion_factor") or 1.0
|
||||
return_rate = 0
|
||||
if cint(self.is_return) and self.return_against and self.docstatus==1:
|
||||
against_document_no = (d.get("sales_invoice_item")
|
||||
if self.doctype == "Sales Invoice" else d.get("delivery_note_item"))
|
||||
if cint(self.is_return) and self.docstatus==1:
|
||||
if self.return_against:
|
||||
against_document_no = (d.get("sales_invoice_item")
|
||||
if self.doctype == "Sales Invoice" else d.get("delivery_note_item"))
|
||||
|
||||
return_rate = self.get_incoming_rate_for_sales_return(d.item_code,
|
||||
self.return_against, against_document_no)
|
||||
return_rate = self.get_incoming_rate_for_sales_return(d.item_code,
|
||||
self.return_against, against_document_no)
|
||||
else:
|
||||
# For standalone credit note
|
||||
args = frappe._dict({
|
||||
"item_code": d.item_code,
|
||||
"warehouse": d.warehouse,
|
||||
"posting_date": self.posting_date,
|
||||
"posting_time": self.posting_time,
|
||||
"qty": flt(d.qty),
|
||||
"serial_no": d.serial_no,
|
||||
"company": d.company,
|
||||
"allow_zero_valuation": d.allow_zero_valuation
|
||||
})
|
||||
|
||||
return_rate = get_incoming_rate(args)
|
||||
|
||||
# On cancellation or if return entry submission, make stock ledger entry for
|
||||
# target warehouse first, to update serial no values properly
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.utils import flt, comma_or, nowdate, getdate
|
||||
from frappe.utils import flt, comma_or, nowdate, getdate, now
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
|
||||
@@ -307,10 +307,14 @@ class StatusUpdater(Document):
|
||||
target.notify_update()
|
||||
|
||||
def _update_modified(self, args, update_modified):
|
||||
args['update_modified'] = ''
|
||||
if update_modified:
|
||||
args['update_modified'] = ', modified = now(), modified_by = {0}'\
|
||||
.format(frappe.db.escape(frappe.session.user))
|
||||
if not update_modified:
|
||||
args['update_modified'] = ''
|
||||
return
|
||||
|
||||
args['update_modified'] = ', modified = {0}, modified_by = {1}'.format(
|
||||
frappe.db.escape(now()),
|
||||
frappe.db.escape(frappe.session.user)
|
||||
)
|
||||
|
||||
def update_billing_status_for_zero_amount_refdoc(self, ref_dt):
|
||||
ref_fieldname = frappe.scrub(ref_dt)
|
||||
|
||||
@@ -319,12 +319,13 @@ class StockController(AccountsController):
|
||||
return incoming_rate
|
||||
|
||||
def validate_warehouse(self):
|
||||
from erpnext.stock.utils import validate_warehouse_company
|
||||
from erpnext.stock.utils import validate_warehouse_company, validate_disabled_warehouse
|
||||
|
||||
warehouses = list(set([d.warehouse for d in
|
||||
self.get("items") if getattr(d, "warehouse", None)]))
|
||||
|
||||
for w in warehouses:
|
||||
validate_disabled_warehouse(w)
|
||||
validate_warehouse_company(w, self.company)
|
||||
|
||||
def update_billing_percentage(self, update_modified=True):
|
||||
@@ -417,7 +418,7 @@ def compare_existing_and_expected_gle(existing_gle, expected_gle):
|
||||
for e in existing_gle:
|
||||
if entry.account == e.account:
|
||||
account_existed = True
|
||||
if entry.account == e.account and entry.against_account == e.against_account \
|
||||
if entry.account == e.account \
|
||||
and (not entry.cost_center or not e.cost_center or entry.cost_center == e.cost_center) \
|
||||
and (entry.debit != e.debit or entry.credit != e.credit):
|
||||
matched = False
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"modified": "2019-10-14 15:23:54.630731",
|
||||
"modified": "2021-06-28 16:27:53.235714",
|
||||
"modified_by": "Administrator",
|
||||
"module": "CRM",
|
||||
"name": "Appointment",
|
||||
@@ -144,6 +144,18 @@
|
||||
"role": "Sales User",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Employee",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
|
||||
@@ -277,7 +277,6 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:",
|
||||
"fieldname": "territory",
|
||||
"fieldtype": "Link",
|
||||
"label": "Territory",
|
||||
@@ -413,7 +412,7 @@
|
||||
],
|
||||
"icon": "fa fa-info-sign",
|
||||
"idx": 195,
|
||||
"modified": "2020-08-12 23:34:39.665513",
|
||||
"modified": "2021-06-04 10:11:22.831139",
|
||||
"modified_by": "Administrator",
|
||||
"module": "CRM",
|
||||
"name": "Opportunity",
|
||||
|
||||
@@ -244,7 +244,6 @@ def make_quotation(source_name, target_doc=None):
|
||||
"doctype": "Quotation",
|
||||
"field_map": {
|
||||
"opportunity_from": "quotation_to",
|
||||
"opportunity_type": "order_type",
|
||||
"name": "enq_no",
|
||||
}
|
||||
},
|
||||
|
||||
@@ -19,15 +19,50 @@ def set_defaut_value_for_filters(filters):
|
||||
if not filters.get('lead_age'): filters["lead_age"] = 60
|
||||
|
||||
def get_columns():
|
||||
return [
|
||||
_("Lead") + ":Link/Lead:100",
|
||||
_("Name") + "::100",
|
||||
_("Organization") + "::100",
|
||||
_("Reference Document") + "::150",
|
||||
_("Reference Name") + ":Dynamic Link/"+_("Reference Document")+":120",
|
||||
_("Last Communication") + ":Data:200",
|
||||
_("Last Communication Date") + ":Date:180"
|
||||
]
|
||||
columns = [{
|
||||
"label": _("Lead"),
|
||||
"fieldname": "lead",
|
||||
"fieldtype": "Link",
|
||||
"options": "Lead",
|
||||
"width": 130
|
||||
},
|
||||
{
|
||||
"label": _("Name"),
|
||||
"fieldname": "name",
|
||||
"width": 120
|
||||
},
|
||||
{
|
||||
"label": _("Organization"),
|
||||
"fieldname": "organization",
|
||||
"width": 120
|
||||
},
|
||||
{
|
||||
"label": _("Reference Document Type"),
|
||||
"fieldname": "reference_document_type",
|
||||
"fieldtype": "Link",
|
||||
"options": "Doctype",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Reference Name"),
|
||||
"fieldname": "reference_name",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"options": "reference_document_type",
|
||||
"width": 140
|
||||
},
|
||||
{
|
||||
"label": _("Last Communication"),
|
||||
"fieldname": "last_communication",
|
||||
"fieldtype": "Data",
|
||||
"width": 200
|
||||
},
|
||||
{
|
||||
"label": _("Last Communication Date"),
|
||||
"fieldname": "last_communication_date",
|
||||
"fieldtype": "Date",
|
||||
"width": 100
|
||||
}]
|
||||
return columns
|
||||
|
||||
def get_data(filters):
|
||||
lead_details = []
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "question",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"in_list_view": 1,
|
||||
"label": "Question",
|
||||
"reqd": 1
|
||||
@@ -34,7 +34,7 @@
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"modified": "2019-05-30 18:39:21.880974",
|
||||
"modified": "2021-01-28 18:39:21.880974",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Education",
|
||||
"name": "Question",
|
||||
@@ -77,4 +77,4 @@
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,6 @@ class Student(Document):
|
||||
student_user.flags.ignore_permissions = True
|
||||
student_user.add_roles("Student")
|
||||
student_user.save()
|
||||
update_password_link = student_user.reset_password()
|
||||
|
||||
def update_applicant_status(self):
|
||||
"""Updates Student Applicant status to Admitted"""
|
||||
|
||||
@@ -345,11 +345,11 @@ def get_or_create_course_enrollment(course, program):
|
||||
student = get_current_student()
|
||||
course_enrollment = get_enrollment("course", course, student.name)
|
||||
if not course_enrollment:
|
||||
program_enrollment = get_enrollment('program', program, student.name)
|
||||
program_enrollment = get_enrollment('program', program.name, student.name)
|
||||
if not program_enrollment:
|
||||
frappe.throw(_("You are not enrolled in program {0}".format(program)))
|
||||
return
|
||||
return student.enroll_in_course(course_name=course, program_enrollment=get_enrollment('program', program, student.name))
|
||||
return student.enroll_in_course(course_name=course, program_enrollment=get_enrollment('program', program.name, student.name))
|
||||
else:
|
||||
return frappe.get_doc('Course Enrollment', course_enrollment)
|
||||
|
||||
|
||||
@@ -14,8 +14,7 @@ def verify_request():
|
||||
)
|
||||
|
||||
if frappe.request.data and \
|
||||
frappe.get_request_header("X-Wc-Webhook-Signature") and \
|
||||
not sig == bytes(frappe.get_request_header("X-Wc-Webhook-Signature").encode()):
|
||||
not sig == frappe.get_request_header("X-Wc-Webhook-Signature", "").encode():
|
||||
frappe.throw(_("Unverified Webhook Data"))
|
||||
frappe.set_user(woocommerce_settings.creation_user)
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ def call_mws_method(mws_method, *args, **kwargs):
|
||||
return response
|
||||
except Exception as e:
|
||||
delay = math.pow(4, x) * 125
|
||||
frappe.log_error(message=e, title=str(mws_method))
|
||||
frappe.log_error(message=e, title="Method {} failed".format(mws_method.__name__))
|
||||
time.sleep(delay)
|
||||
continue
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user