Compare commits
1052 Commits
skip_enque
...
v12.9.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8abcf42e20 | ||
|
|
485f6cd7a2 | ||
|
|
5447decd9e | ||
|
|
d2491e403b | ||
|
|
7b795734e8 | ||
|
|
b6bbd0efcf | ||
|
|
5ec0289d98 | ||
|
|
c5921c605f | ||
|
|
0c303c4f54 | ||
|
|
746e879187 | ||
|
|
a24362a83f | ||
|
|
69a5a7f11e | ||
|
|
d062a4b6b5 | ||
|
|
ab79a9554b | ||
|
|
f5d8d6e4e6 | ||
|
|
fae4805d3f | ||
|
|
cb74ff870d | ||
|
|
3c84ef3b5e | ||
|
|
2894640d56 | ||
|
|
a45ff8e3ba | ||
|
|
74dd64501c | ||
|
|
bd8b94c9dd | ||
|
|
d1569b9581 | ||
|
|
938cde30e3 | ||
|
|
0550b3537d | ||
|
|
ea6f08cb26 | ||
|
|
96a05f65aa | ||
|
|
e85c3a50cd | ||
|
|
8209507a8f | ||
|
|
ca5384343f | ||
|
|
a4e92cf577 | ||
|
|
632c65cd59 | ||
|
|
70ab59f473 | ||
|
|
c4d6cca9f1 | ||
|
|
795d318fcc | ||
|
|
6dbb02d293 | ||
|
|
ea4c91f51c | ||
|
|
395da09dae | ||
|
|
9b08258955 | ||
|
|
7f2aedb67c | ||
|
|
1045dc49f5 | ||
|
|
a569fed4da | ||
|
|
9af9b438af | ||
|
|
3e321d5c24 | ||
|
|
e2f53cdc83 | ||
|
|
f9834504c8 | ||
|
|
ec19926a97 | ||
|
|
39790f05e7 | ||
|
|
317b53a8b6 | ||
|
|
101612e599 | ||
|
|
838ed77797 | ||
|
|
8ff718249e | ||
|
|
88c2ba54ab | ||
|
|
a52fe009cc | ||
|
|
79b691fe18 | ||
|
|
29f1a219d1 | ||
|
|
73e3ba2c30 | ||
|
|
78f69e448b | ||
|
|
52890341d5 | ||
|
|
725684a0c3 | ||
|
|
dc59831aa9 | ||
|
|
2fa841821a | ||
|
|
3b14810564 | ||
|
|
8ed066ba1f | ||
|
|
f344369068 | ||
|
|
71d9a52a07 | ||
|
|
0af125f6fe | ||
|
|
b432f3358e | ||
|
|
441bb760ee | ||
|
|
f69da75e33 | ||
|
|
12739ec464 | ||
|
|
6a317a8e78 | ||
|
|
cb1cc96210 | ||
|
|
b47f5830f4 | ||
|
|
855373a253 | ||
|
|
c8ed6b1e79 | ||
|
|
00bbc76605 | ||
|
|
0df4f1738c | ||
|
|
12a2da49ee | ||
|
|
65d6efdaee | ||
|
|
f861a3e720 | ||
|
|
767d17ce2f | ||
|
|
0f455060ab | ||
|
|
861f2726a2 | ||
|
|
55640be627 | ||
|
|
d837b6cedc | ||
|
|
27b2988938 | ||
|
|
c739c2d355 | ||
|
|
231f65ff59 | ||
|
|
626585e9f3 | ||
|
|
5afcc9c185 | ||
|
|
41ff2cb4a7 | ||
|
|
664f536e9b | ||
|
|
923fcc7738 | ||
|
|
368afc551d | ||
|
|
3a328b0413 | ||
|
|
970b21075e | ||
|
|
d769a2036b | ||
|
|
56a3b9abc4 | ||
|
|
6844dd75da | ||
|
|
0b6ef55e78 | ||
|
|
9fa952d10e | ||
|
|
6d4f451d0d | ||
|
|
1c03d154ce | ||
|
|
2d430ec077 | ||
|
|
cd0fcfd84c | ||
|
|
43118e3551 | ||
|
|
9bd6d119c4 | ||
|
|
2aa045865f | ||
|
|
ec3a462acd | ||
|
|
a6066009aa | ||
|
|
60b19fe935 | ||
|
|
70b52c37d5 | ||
|
|
178ad9b4d6 | ||
|
|
b4311a41c5 | ||
|
|
c6e0dbb1c4 | ||
|
|
d476eb79b4 | ||
|
|
10ea82001f | ||
|
|
794bb6ebdd | ||
|
|
c3a9513978 | ||
|
|
a4a019f9d9 | ||
|
|
67ed21d443 | ||
|
|
c89d750e5c | ||
|
|
c253f0621d | ||
|
|
3202b0a486 | ||
|
|
4a37ee8908 | ||
|
|
bdbfd2ad0c | ||
|
|
933d1262f2 | ||
|
|
c4752e36eb | ||
|
|
0da919c091 | ||
|
|
12b5d72e70 | ||
|
|
272d2bc0b3 | ||
|
|
55b7904e2f | ||
|
|
ed709b36b4 | ||
|
|
bf1fc47564 | ||
|
|
db6953dc78 | ||
|
|
438e0f5d49 | ||
|
|
5a9476e0d4 | ||
|
|
f77a735469 | ||
|
|
4451b7eda9 | ||
|
|
426f0bc168 | ||
|
|
a8b87ccce0 | ||
|
|
c31fc19f08 | ||
|
|
5e817b2aee | ||
|
|
393857d7de | ||
|
|
0ffff47b64 | ||
|
|
6397590f07 | ||
|
|
2620ac31f9 | ||
|
|
22c4f82fc6 | ||
|
|
ae5414fced | ||
|
|
61584c8601 | ||
|
|
99dfe6c571 | ||
|
|
fd1ab37bc8 | ||
|
|
bd969477b1 | ||
|
|
a816788039 | ||
|
|
c099a410c8 | ||
|
|
e33c44ae33 | ||
|
|
4930233bbe | ||
|
|
e383727394 | ||
|
|
3d2dcd8c59 | ||
|
|
6647f45eb5 | ||
|
|
bd9625d150 | ||
|
|
c832291bd6 | ||
|
|
7c67c38bc5 | ||
|
|
8076deb080 | ||
|
|
dd8566ecde | ||
|
|
fa7d496413 | ||
|
|
3b8ad79445 | ||
|
|
558c3284a0 | ||
|
|
95f7807b78 | ||
|
|
6ece7fe265 | ||
|
|
5aa2241439 | ||
|
|
68d7b77314 | ||
|
|
93e8b5d87e | ||
|
|
6b082b5edb | ||
|
|
093720b870 | ||
|
|
e1697fd9cc | ||
|
|
c2f952045c | ||
|
|
2b5bca4ce6 | ||
|
|
de6f0f7a05 | ||
|
|
1f3584b9a8 | ||
|
|
5bfdf0af4d | ||
|
|
14a50a9403 | ||
|
|
b724fec6c9 | ||
|
|
1d5ea4feee | ||
|
|
7fedff260d | ||
|
|
9fc78e44ea | ||
|
|
be97087d32 | ||
|
|
b0f829e541 | ||
|
|
0455a96dcd | ||
|
|
d771ba2895 | ||
|
|
c8e5b42dba | ||
|
|
424dbef139 | ||
|
|
6256ca81b1 | ||
|
|
fda451f3e3 | ||
|
|
2307385210 | ||
|
|
bd4b5da11b | ||
|
|
7ed7c3237a | ||
|
|
522cf08f67 | ||
|
|
ef7f9c6ecc | ||
|
|
097c643a59 | ||
|
|
dd560d676e | ||
|
|
794fd75ca1 | ||
|
|
015e1c123c | ||
|
|
f49e66e721 | ||
|
|
8fd1e08763 | ||
|
|
c5d108c954 | ||
|
|
f2c43ca81e | ||
|
|
9d6ee99d99 | ||
|
|
0578cf5a73 | ||
|
|
499f9198b9 | ||
|
|
4daab871d5 | ||
|
|
17585710b2 | ||
|
|
56e7887511 | ||
|
|
08c782709a | ||
|
|
fb20982194 | ||
|
|
9453add3dd | ||
|
|
c62cf98d7e | ||
|
|
ea8e302833 | ||
|
|
5d3fd9dc43 | ||
|
|
c9ba147615 | ||
|
|
7ef57533ec | ||
|
|
712abe4cd2 | ||
|
|
1a93977ef7 | ||
|
|
fc078c1d45 | ||
|
|
77532b96b8 | ||
|
|
4171c5ceeb | ||
|
|
fa07e0fb9d | ||
|
|
e80702b6c2 | ||
|
|
7345b8494d | ||
|
|
5817adbb64 | ||
|
|
4de4cb055f | ||
|
|
19c841eb64 | ||
|
|
5f10e0ac26 | ||
|
|
64bb910015 | ||
|
|
6b7848232c | ||
|
|
b9df3793fb | ||
|
|
09f95858c0 | ||
|
|
fcd7548220 | ||
|
|
9989bfe2dc | ||
|
|
4af9ab702f | ||
|
|
b882d7046b | ||
|
|
b33f82d4e8 | ||
|
|
e8bd7c0233 | ||
|
|
cfb00fb887 | ||
|
|
2128dc8d87 | ||
|
|
46ae415923 | ||
|
|
21b34b9607 | ||
|
|
44f0c077ff | ||
|
|
d959463cbc | ||
|
|
e6d02ecd7f | ||
|
|
3b2aa5ead3 | ||
|
|
4770626bbd | ||
|
|
df045e9f6d | ||
|
|
2c045179a3 | ||
|
|
da83af1213 | ||
|
|
c10c920914 | ||
|
|
61fc2d3263 | ||
|
|
a66be0f5c5 | ||
|
|
fd99cc8494 | ||
|
|
cf1f777ae2 | ||
|
|
9ea9ed4256 | ||
|
|
c7b5309dcd | ||
|
|
65eea97e0c | ||
|
|
45d454b14d | ||
|
|
f390b8062c | ||
|
|
90adf076f3 | ||
|
|
2b6942d9ce | ||
|
|
4dca806737 | ||
|
|
bd57cbda03 | ||
|
|
31c8f8e795 | ||
|
|
ccfc005932 | ||
|
|
b4dfc8e1bf | ||
|
|
3379cac956 | ||
|
|
ed48755a6f | ||
|
|
c43e759b87 | ||
|
|
05e9af60bd | ||
|
|
3635b17f22 | ||
|
|
de9220568e | ||
|
|
5d3e211531 | ||
|
|
bde676ef74 | ||
|
|
4879858657 | ||
|
|
773eb6ce68 | ||
|
|
8e1201d31e | ||
|
|
18a04a24b3 | ||
|
|
64f053d583 | ||
|
|
bf401290d1 | ||
|
|
1fc8de32f1 | ||
|
|
09f6199b36 | ||
|
|
b0f000b1c8 | ||
|
|
49b653b444 | ||
|
|
107229de04 | ||
|
|
af33f71562 | ||
|
|
25c26b5904 | ||
|
|
1112bd0f23 | ||
|
|
8c931e4185 | ||
|
|
5771aabc49 | ||
|
|
954c6540fd | ||
|
|
4eea8bd2c0 | ||
|
|
335d35a988 | ||
|
|
2833db559e | ||
|
|
3d667bfe55 | ||
|
|
7841653a03 | ||
|
|
02a9b5a8a5 | ||
|
|
4b1d0d19fb | ||
|
|
8b31e3ec2a | ||
|
|
b2ba5dbd6a | ||
|
|
b2a9413c06 | ||
|
|
1b15734bd8 | ||
|
|
725d6b235f | ||
|
|
87c6b6d12f | ||
|
|
703e20df79 | ||
|
|
ae8a0940f5 | ||
|
|
de423f8183 | ||
|
|
c7bf050e8b | ||
|
|
9d0af22b88 | ||
|
|
073d1c4c13 | ||
|
|
c4115c19e4 | ||
|
|
39b8e150bf | ||
|
|
849ec84852 | ||
|
|
38896efd6d | ||
|
|
b225ffa003 | ||
|
|
c3bb5a97f8 | ||
|
|
cdd589a58a | ||
|
|
bf5c7fc4d3 | ||
|
|
27eba1fc61 | ||
|
|
95b6e8afda | ||
|
|
7c1f2ce4eb | ||
|
|
af64300c18 | ||
|
|
6668544b4c | ||
|
|
5e841b40f3 | ||
|
|
5e79f763b5 | ||
|
|
aebf0e47f3 | ||
|
|
645ef9db87 | ||
|
|
d76ecb960e | ||
|
|
d2da8bd6e2 | ||
|
|
a5fb07e475 | ||
|
|
bcf1897e60 | ||
|
|
044abbace8 | ||
|
|
36a763bdc5 | ||
|
|
2dd488f878 | ||
|
|
34288b0f38 | ||
|
|
d7389b1920 | ||
|
|
42eddbf89b | ||
|
|
8e206f55be | ||
|
|
de9c73c5cd | ||
|
|
4ea7df69b4 | ||
|
|
da6ef63bc4 | ||
|
|
5da9663ae4 | ||
|
|
322660c644 | ||
|
|
2378572e1b | ||
|
|
ab436d4147 | ||
|
|
18cd3a029d | ||
|
|
dba4bd6f26 | ||
|
|
7bc70758fb | ||
|
|
69e0700101 | ||
|
|
dfe4d36e8d | ||
|
|
55ded1379e | ||
|
|
64ed25abfb | ||
|
|
7b70679bf0 | ||
|
|
31fbafe16f | ||
|
|
878e4cb3ce | ||
|
|
73bc29c011 | ||
|
|
44dbd98d90 | ||
|
|
04201028d1 | ||
|
|
8b5693b77f | ||
|
|
76d2bd9633 | ||
|
|
83c7561a84 | ||
|
|
f8cb81c9f9 | ||
|
|
4a59b1d3b5 | ||
|
|
763aab3301 | ||
|
|
0bbc78cdbd | ||
|
|
29fc063764 | ||
|
|
a66da0ddf4 | ||
|
|
4fd447a31d | ||
|
|
122049a9b4 | ||
|
|
6a49ea5262 | ||
|
|
9f709aa64c | ||
|
|
702e0ac29e | ||
|
|
954276ff0e | ||
|
|
0541b2ee27 | ||
|
|
086e5c4dac | ||
|
|
dc6a3ae99c | ||
|
|
2512435a7b | ||
|
|
7d8d351668 | ||
|
|
0126ef78c8 | ||
|
|
489a5d203f | ||
|
|
4716fc5eb8 | ||
|
|
437689371d | ||
|
|
798d532a11 | ||
|
|
c5a3bcca70 | ||
|
|
e4cb523181 | ||
|
|
b80213d65d | ||
|
|
d13e7d00b0 | ||
|
|
f492ba1e2d | ||
|
|
83111a4a2a | ||
|
|
5a482a6685 | ||
|
|
bda11e628b | ||
|
|
b8be73caea | ||
|
|
df151ad6fa | ||
|
|
7e7787c298 | ||
|
|
1dd0c8f54c | ||
|
|
dc04cbf9a9 | ||
|
|
77673f33db | ||
|
|
f4e7de3cef | ||
|
|
9b4ef1059c | ||
|
|
8ba661b8ed | ||
|
|
1125ed8830 | ||
|
|
58351de797 | ||
|
|
9578ead7f9 | ||
|
|
f9ce7c2e18 | ||
|
|
221189ab6e | ||
|
|
8f854c7d93 | ||
|
|
728edacfd4 | ||
|
|
9aa8cabf93 | ||
|
|
cf2ab9b129 | ||
|
|
2313d2d80a | ||
|
|
b03d1327a5 | ||
|
|
8110edbc62 | ||
|
|
d90f658163 | ||
|
|
0b8191d566 | ||
|
|
27f0d13bdd | ||
|
|
c944676bb5 | ||
|
|
ff49a2f0b6 | ||
|
|
8a5587749b | ||
|
|
3c378aaac7 | ||
|
|
72430e91b2 | ||
|
|
6b038bda0d | ||
|
|
01f80b239d | ||
|
|
b9f81d913b | ||
|
|
750985dc23 | ||
|
|
bcb9c28597 | ||
|
|
23891a918d | ||
|
|
4f1a3876ec | ||
|
|
43806f3e68 | ||
|
|
f4405246ed | ||
|
|
8cc507af53 | ||
|
|
6c8bbf9a1c | ||
|
|
ea470609fd | ||
|
|
ad44f52a26 | ||
|
|
1e7cba9dd3 | ||
|
|
87b3dbb214 | ||
|
|
243283eec8 | ||
|
|
b05922026d | ||
|
|
ed8e44b467 | ||
|
|
76e163a889 | ||
|
|
20ab2c1e20 | ||
|
|
3f7ea072c3 | ||
|
|
601db77801 | ||
|
|
93e72fdd21 | ||
|
|
e2bb2593db | ||
|
|
e53e96f01b | ||
|
|
794064db9f | ||
|
|
4c4a1aa56d | ||
|
|
84a5e34319 | ||
|
|
d1bbda6d71 | ||
|
|
8c56f8595e | ||
|
|
c2f6782658 | ||
|
|
7c0398c8e3 | ||
|
|
71e9f23d77 | ||
|
|
7b45ae1f2d | ||
|
|
bf0f9c5cb4 | ||
|
|
3f1682a4f3 | ||
|
|
4dd8f390e2 | ||
|
|
82661aa042 | ||
|
|
210a718a04 | ||
|
|
1d94f6c848 | ||
|
|
d21a9fa917 | ||
|
|
51cbfa6aa6 | ||
|
|
1ed5993ff6 | ||
|
|
e655648b18 | ||
|
|
da7c6b0d99 | ||
|
|
a5b836d3d4 | ||
|
|
8a7ecebc03 | ||
|
|
7cf245895e | ||
|
|
738517c6e8 | ||
|
|
a6cd666b10 | ||
|
|
bc8a881e64 | ||
|
|
224059e7ba | ||
|
|
6f454dce20 | ||
|
|
3528149329 | ||
|
|
6c148ef314 | ||
|
|
7f58f6af39 | ||
|
|
3ddcc2e5c7 | ||
|
|
756f47af04 | ||
|
|
b9e1d9ad7d | ||
|
|
169ba6f9b2 | ||
|
|
7c0e89d785 | ||
|
|
dd8059a3aa | ||
|
|
0dcd5a0f34 | ||
|
|
d2a2837034 | ||
|
|
22a069d4b0 | ||
|
|
281a9fdf07 | ||
|
|
e7a6a4b009 | ||
|
|
5c760175d1 | ||
|
|
11fb93d022 | ||
|
|
43a0011987 | ||
|
|
37886a72ef | ||
|
|
98573fb868 | ||
|
|
4d954f1a5f | ||
|
|
46053f6c8f | ||
|
|
6f3941c9cc | ||
|
|
cd71114c15 | ||
|
|
e2a31f221a | ||
|
|
97dfe2ac32 | ||
|
|
9c63b1d2c9 | ||
|
|
0f807bf7ee | ||
|
|
ee003495f6 | ||
|
|
08f0e9d6f3 | ||
|
|
4d6fb59bfb | ||
|
|
0317576434 | ||
|
|
b607963fba | ||
|
|
d08cff9efc | ||
|
|
2f2cef6c5f | ||
|
|
875e0f5fb2 | ||
|
|
d9ab412032 | ||
|
|
6c4cddccf6 | ||
|
|
69d3c4ff25 | ||
|
|
1f80c8dffe | ||
|
|
12cdf0fb52 | ||
|
|
b6a3a06c4b | ||
|
|
2728590471 | ||
|
|
7766530afc | ||
|
|
d7839eb8ac | ||
|
|
6a734ecd34 | ||
|
|
acd93b3bd1 | ||
|
|
94200b1179 | ||
|
|
a6bf96322b | ||
|
|
27163e60bb | ||
|
|
1041516e8e | ||
|
|
b7bbd827fd | ||
|
|
fda7cbeca4 | ||
|
|
2fc58b3327 | ||
|
|
3fcd575f53 | ||
|
|
0cee807dfb | ||
|
|
a51c56c4b3 | ||
|
|
d2f26e57d2 | ||
|
|
b7790c0394 | ||
|
|
d17e0b5aca | ||
|
|
f08b5e4866 | ||
|
|
78ae5e7721 | ||
|
|
5fb66a3953 | ||
|
|
d952e71d97 | ||
|
|
fe337dfb66 | ||
|
|
45329232b3 | ||
|
|
30ad21b7d8 | ||
|
|
4e3ad9c3f2 | ||
|
|
2a1e9b542e | ||
|
|
8f458feafc | ||
|
|
e116d8f819 | ||
|
|
f3674ccc6c | ||
|
|
6f35a63f2d | ||
|
|
acbc4f648d | ||
|
|
5475b5f562 | ||
|
|
39861c270c | ||
|
|
6eaa542709 | ||
|
|
2a8981929a | ||
|
|
0aa7aa5996 | ||
|
|
a20bd89c20 | ||
|
|
aa7af30382 | ||
|
|
9921d28ea7 | ||
|
|
b26fd04843 | ||
|
|
1b78d20d3c | ||
|
|
b203406d4c | ||
|
|
5a0017c61a | ||
|
|
df15c758f6 | ||
|
|
588a89f957 | ||
|
|
c74343531f | ||
|
|
a257189c77 | ||
|
|
355051bf75 | ||
|
|
ab8aa43ffb | ||
|
|
a6f56bbc3e | ||
|
|
4e2b9395b9 | ||
|
|
ae9159fbd8 | ||
|
|
8e80c17602 | ||
|
|
8903258362 | ||
|
|
1beeb28908 | ||
|
|
2d2aa7d664 | ||
|
|
cd6b60df70 | ||
|
|
b18790f5c2 | ||
|
|
f6d7090f07 | ||
|
|
017d280877 | ||
|
|
031d4092d0 | ||
|
|
0119d15adb | ||
|
|
df6cb3c43b | ||
|
|
1ed737cf64 | ||
|
|
c64e46fe67 | ||
|
|
f579b0e3de | ||
|
|
b63c041aa4 | ||
|
|
22f9a5f09f | ||
|
|
e47875340e | ||
|
|
6f28383531 | ||
|
|
95050702f1 | ||
|
|
ae8f717fe5 | ||
|
|
f95ac99baa | ||
|
|
660b4d1e2f | ||
|
|
5bfebaf1c0 | ||
|
|
980e54c5f4 | ||
|
|
efbfeb2d89 | ||
|
|
0676ed08b1 | ||
|
|
e9946672e0 | ||
|
|
6124f83e6e | ||
|
|
2b83debc72 | ||
|
|
fc4cc9ab42 | ||
|
|
cd19974d7a | ||
|
|
cde48a44cf | ||
|
|
57d33f9ce6 | ||
|
|
90a46acc0d | ||
|
|
60634cc829 | ||
|
|
a7c27596bb | ||
|
|
b4e3146665 | ||
|
|
495ab6c7f2 | ||
|
|
0e396a62b1 | ||
|
|
1343a4b755 | ||
|
|
ec45c096a0 | ||
|
|
c7e523cee4 | ||
|
|
9e436336e2 | ||
|
|
a9205adfbd | ||
|
|
f5bff5f15d | ||
|
|
bbeacb9acf | ||
|
|
7027584391 | ||
|
|
372d4e2e05 | ||
|
|
bdfa2ff33a | ||
|
|
787b31674a | ||
|
|
0eb9f7408e | ||
|
|
5c96415eb5 | ||
|
|
d9f8347fc8 | ||
|
|
2a48fe6ce6 | ||
|
|
3ed574532d | ||
|
|
9f58e59a2c | ||
|
|
23c6d6cf16 | ||
|
|
8e0a9e8748 | ||
|
|
67dbeee7b2 | ||
|
|
b4a0c773ae | ||
|
|
ce598530db | ||
|
|
694f57fbb4 | ||
|
|
0f90b870fb | ||
|
|
bd5b37dbb7 | ||
|
|
7e93e87244 | ||
|
|
02f4aa6db6 | ||
|
|
ade2c36123 | ||
|
|
3ef80f2d2e | ||
|
|
85c6480b41 | ||
|
|
f3c94315c3 | ||
|
|
a4219f1cfe | ||
|
|
831a7ddd6f | ||
|
|
43886ca016 | ||
|
|
61b4244837 | ||
|
|
6871979acc | ||
|
|
bb7cf81d52 | ||
|
|
9826291c52 | ||
|
|
08661250d2 | ||
|
|
814001a90f | ||
|
|
eef73a0d92 | ||
|
|
a1e3202054 | ||
|
|
bee017e17c | ||
|
|
a054508211 | ||
|
|
feaa82a8ea | ||
|
|
a175e06149 | ||
|
|
ba4d9469ff | ||
|
|
54c6f91dde | ||
|
|
e552e958de | ||
|
|
7524ecbc59 | ||
|
|
be2ca5f8cc | ||
|
|
d4d641a07e | ||
|
|
8e5b812a97 | ||
|
|
df94f0565e | ||
|
|
637915f295 | ||
|
|
f2838c4a13 | ||
|
|
fe56c7ce39 | ||
|
|
29ecbde445 | ||
|
|
611f212aa4 | ||
|
|
6fbafa1924 | ||
|
|
875bac8bb9 | ||
|
|
b4b410f1d2 | ||
|
|
eec0ee83e2 | ||
|
|
148951e6f3 | ||
|
|
612e3b77fd | ||
|
|
a60849f6ab | ||
|
|
6f27e97980 | ||
|
|
8d9145aaac | ||
|
|
25a7330db0 | ||
|
|
92c999afcb | ||
|
|
9e3dc25b1c | ||
|
|
2a3cd7d601 | ||
|
|
3ca67ceebb | ||
|
|
245fc49089 | ||
|
|
78199dbf0a | ||
|
|
2ac33c9ce9 | ||
|
|
34cd035d8c | ||
|
|
ba4f565ca3 | ||
|
|
5860427bc5 | ||
|
|
89f42eef49 | ||
|
|
eacbfa15c0 | ||
|
|
5d45c2c184 | ||
|
|
b24b0378ea | ||
|
|
144012a5de | ||
|
|
5fb4027375 | ||
|
|
3fa65f1363 | ||
|
|
79f630661a | ||
|
|
61b9fe6a81 | ||
|
|
f38d9d8f0d | ||
|
|
3a67daa1fd | ||
|
|
813726b415 | ||
|
|
87fe2143be | ||
|
|
c759b06f16 | ||
|
|
c32249a72f | ||
|
|
7cd6022a0b | ||
|
|
67274d01e8 | ||
|
|
19d7e43b90 | ||
|
|
05f4be69f4 | ||
|
|
541881162a | ||
|
|
c8884257b9 | ||
|
|
4674fec320 | ||
|
|
c7f0ab8ed6 | ||
|
|
19c5e031a9 | ||
|
|
8b943af911 | ||
|
|
863eb86a1d | ||
|
|
e5fbebe126 | ||
|
|
45be9fe416 | ||
|
|
1596352585 | ||
|
|
4b893b1d51 | ||
|
|
d85e144944 | ||
|
|
4dfff5a271 | ||
|
|
c2b9598966 | ||
|
|
d16e7c096b | ||
|
|
d5676a87a8 | ||
|
|
73dff8993a | ||
|
|
0ef799dff8 | ||
|
|
e8476ef42a | ||
|
|
5511912251 | ||
|
|
514d3c37b9 | ||
|
|
9fef6e1b68 | ||
|
|
37c51f5913 | ||
|
|
7721954bbb | ||
|
|
7e33d875e0 | ||
|
|
0a50088a0e | ||
|
|
6ba6ba27bd | ||
|
|
611e23c149 | ||
|
|
89affd8d09 | ||
|
|
ab2e52ca07 | ||
|
|
8dfd5535f1 | ||
|
|
dc0ea3fdb0 | ||
|
|
b65b525c44 | ||
|
|
8c45ae73b3 | ||
|
|
4872417f86 | ||
|
|
fed9e2861c | ||
|
|
c50f08a23a | ||
|
|
6e115638a1 | ||
|
|
2d37bbedaa | ||
|
|
00f6f114e7 | ||
|
|
437be1162e | ||
|
|
ca4789c9f7 | ||
|
|
e3b6a14ab1 | ||
|
|
eb7893768b | ||
|
|
6e1ef4f5c2 | ||
|
|
d56bc81daf | ||
|
|
7d7027f47c | ||
|
|
a14f72590d | ||
|
|
7086ecb3b0 | ||
|
|
4b401f0573 | ||
|
|
5beee8af58 | ||
|
|
8508ace50e | ||
|
|
c52ef56875 | ||
|
|
4dfe8ab483 | ||
|
|
ea5e0a9d5b | ||
|
|
30111e6403 | ||
|
|
e5a572a4a8 | ||
|
|
83ed7df8cd | ||
|
|
97e93c6257 | ||
|
|
f7cd8c2702 | ||
|
|
f67b3f8a79 | ||
|
|
8356d214e1 | ||
|
|
64f3577148 | ||
|
|
ff3ff9a3c2 | ||
|
|
03c1396fc7 | ||
|
|
4479b93c1a | ||
|
|
da82edf711 | ||
|
|
7b8bf4f4d4 | ||
|
|
07c915d00b | ||
|
|
75b62cdd9f | ||
|
|
54f2d53522 | ||
|
|
55bdaae3ee | ||
|
|
6bbfce13fe | ||
|
|
0980ebe93e | ||
|
|
ded5a255af | ||
|
|
13c29aeec4 | ||
|
|
cf261d0788 | ||
|
|
3074679d7e | ||
|
|
a037f9d55e | ||
|
|
3530d0d7c9 | ||
|
|
3da9967fa8 | ||
|
|
f276e8d6a1 | ||
|
|
9e0d9aa947 | ||
|
|
df363d7dd6 | ||
|
|
81e2f2a45f | ||
|
|
255361ba92 | ||
|
|
67bcfde3de | ||
|
|
b6351b4ca0 | ||
|
|
c5a8d33374 | ||
|
|
f4589da806 | ||
|
|
c5cd7cd65d | ||
|
|
a5e56dfb6f | ||
|
|
adb35d8703 | ||
|
|
db4f3d98ab | ||
|
|
cd2899f429 | ||
|
|
8f6a6e8d2a | ||
|
|
fd68a682ca | ||
|
|
25fd2743a8 | ||
|
|
826989afda | ||
|
|
10622aee7f | ||
|
|
26e86e7576 | ||
|
|
efa7d5c831 | ||
|
|
55cf2477cd | ||
|
|
5c144c992e | ||
|
|
2fcc51dece | ||
|
|
e76670562c | ||
|
|
e1aa48968e | ||
|
|
2faa8f92be | ||
|
|
82c5a2bf45 | ||
|
|
ed386376dc | ||
|
|
65353e5590 | ||
|
|
aab1fb0d6a | ||
|
|
2dec923740 | ||
|
|
6ea509f7f4 | ||
|
|
dfdd684fbf | ||
|
|
b299026280 | ||
|
|
3d2e61eef3 | ||
|
|
47eda0fa6d | ||
|
|
8ec1d8d5da | ||
|
|
6adf617a72 | ||
|
|
7c7291ed4f | ||
|
|
a169292f6e | ||
|
|
5869757860 | ||
|
|
6fefdb5b18 | ||
|
|
8c08db33d7 | ||
|
|
bc15ea68b8 | ||
|
|
ee28663836 | ||
|
|
c522df8f9d | ||
|
|
d0e39e6fac | ||
|
|
4c4da06a71 | ||
|
|
827ac91e7f | ||
|
|
379b6df361 | ||
|
|
2e461551d6 | ||
|
|
2d30dc583c | ||
|
|
9de6083dbf | ||
|
|
9512e55704 | ||
|
|
9635df4f26 | ||
|
|
7bbeb31015 | ||
|
|
1f4b251f53 | ||
|
|
34dabc715b | ||
|
|
3881ce94ef | ||
|
|
065bbc9cb3 | ||
|
|
139bb92df9 | ||
|
|
15c8caac57 | ||
|
|
7b926f228f | ||
|
|
dab963aac0 | ||
|
|
170c9d0ab8 | ||
|
|
f27a6528db | ||
|
|
b139f2fc0f | ||
|
|
498ebfba9d | ||
|
|
2ebafb39ac | ||
|
|
adc1b9dc0a | ||
|
|
b7012a47a2 | ||
|
|
f1036c6985 | ||
|
|
8f6e989f57 | ||
|
|
9609cce7b7 | ||
|
|
e14961a6c6 | ||
|
|
f47afecc42 | ||
|
|
782f45ae5f | ||
|
|
f9ac8f63cf | ||
|
|
411b8e86b3 | ||
|
|
2b14669b58 | ||
|
|
14f624f587 | ||
|
|
98ddbf05b1 | ||
|
|
67d25028b2 | ||
|
|
328d5920bd | ||
|
|
81d614b6bd | ||
|
|
fdb7cec244 | ||
|
|
3aabeb88b4 | ||
|
|
b7fd9a652d | ||
|
|
63bc8f2d90 | ||
|
|
b6d7fba936 | ||
|
|
38540e85e8 | ||
|
|
b3d97a560f | ||
|
|
39436c6d38 | ||
|
|
59cecb29a0 | ||
|
|
55bf951ff5 | ||
|
|
4f543ce42a | ||
|
|
8931b3d602 | ||
|
|
b13eebe657 | ||
|
|
fcb296aafb | ||
|
|
5109b36689 | ||
|
|
2149d7ca4f | ||
|
|
220a208f4e | ||
|
|
0c8e46fdea | ||
|
|
ee4901f4a0 | ||
|
|
a0c847920f | ||
|
|
86e5ff3cf9 | ||
|
|
5dd7503516 | ||
|
|
5cc0e08a41 | ||
|
|
6b00d60b46 | ||
|
|
fecf5a9a15 | ||
|
|
5b4050a4ff | ||
|
|
4d7862ef4c | ||
|
|
6e41475612 | ||
|
|
803e0ec27c | ||
|
|
b3addff99e | ||
|
|
c6e098881f | ||
|
|
200ceb5352 | ||
|
|
8c50f5c23f | ||
|
|
6351a9395c | ||
|
|
fb2153c0eb | ||
|
|
86600ac8b9 | ||
|
|
b76a04b470 | ||
|
|
bb42fc615e | ||
|
|
80913994da | ||
|
|
92ecdbe0c8 | ||
|
|
089dcaed70 | ||
|
|
03a427a9e7 | ||
|
|
764c2c7f17 | ||
|
|
b4d9b80fed | ||
|
|
215274719e | ||
|
|
c920efc156 | ||
|
|
3b9fe1ae6f | ||
|
|
c76c5e699b | ||
|
|
ce6923ecb6 | ||
|
|
03db85f3e7 | ||
|
|
666fba94e2 | ||
|
|
48a8a40703 | ||
|
|
5646816282 | ||
|
|
f8df3c7af2 | ||
|
|
62d4dfa883 | ||
|
|
b8f9fd023b | ||
|
|
0df3c93737 | ||
|
|
e0e7dcd2f6 | ||
|
|
d5b1baed39 | ||
|
|
800545ff5b | ||
|
|
388a177f75 | ||
|
|
821166c628 | ||
|
|
2b8df06f8e | ||
|
|
4e8e466a98 | ||
|
|
31d4482336 | ||
|
|
5cd8c7c722 | ||
|
|
e14d9b5476 | ||
|
|
6a8ff1bebe | ||
|
|
a41d464198 | ||
|
|
980793bde0 | ||
|
|
b7329eac19 | ||
|
|
9ec5cb2570 | ||
|
|
44296a392d | ||
|
|
9097c7e11c | ||
|
|
0256d7549c | ||
|
|
94d8b99ef9 | ||
|
|
3fe1335f7b | ||
|
|
dc7a4ac8af | ||
|
|
c0ff769214 | ||
|
|
0a527b9f9a | ||
|
|
e03871f9de | ||
|
|
c0286780bd | ||
|
|
9cc484650b | ||
|
|
319f126258 | ||
|
|
234de12836 | ||
|
|
4c19000ed9 | ||
|
|
a1651ca5f2 | ||
|
|
4d042cd81a | ||
|
|
d72fae670a | ||
|
|
d458e25dc5 | ||
|
|
43474a3afa | ||
|
|
7daa2a2085 | ||
|
|
45075d8915 | ||
|
|
1de3040ecb | ||
|
|
af10f659d9 | ||
|
|
6f36691c64 | ||
|
|
dfe629aff7 | ||
|
|
23bf2a6647 | ||
|
|
b69cb8080c | ||
|
|
f23b5ed23b | ||
|
|
0a28387c70 | ||
|
|
caae8c57bc | ||
|
|
44ae135c36 | ||
|
|
47e786ef62 | ||
|
|
f10be395c1 | ||
|
|
ac967d09ec | ||
|
|
d1e8e8652f | ||
|
|
72649c207f | ||
|
|
d06b685fdf | ||
|
|
6411a56cdc | ||
|
|
34b3b04fb0 | ||
|
|
b1a2a16f43 | ||
|
|
f092e68a58 | ||
|
|
6d497ccb4c | ||
|
|
a7b97f7bac | ||
|
|
f40d3bd10f | ||
|
|
1e2be32860 | ||
|
|
6aec9e32d4 | ||
|
|
59cc0e5029 | ||
|
|
851f39cee1 | ||
|
|
6822a30f8c | ||
|
|
495ba1618b | ||
|
|
778d7595aa | ||
|
|
a40dbd0384 | ||
|
|
80dfb9f834 | ||
|
|
dabb303358 | ||
|
|
d16ef54665 | ||
|
|
dc248b9458 | ||
|
|
bf0f26b4a4 | ||
|
|
929fd4ce47 | ||
|
|
81c895b21e | ||
|
|
27a21f80d7 | ||
|
|
aa7085e11c | ||
|
|
6e5363ba48 | ||
|
|
53746636c3 | ||
|
|
485d48c101 | ||
|
|
0e1ef35968 | ||
|
|
35effe9be0 | ||
|
|
648d6e46f3 | ||
|
|
d6d9a3ddd7 | ||
|
|
18f05db19a | ||
|
|
586fecfe73 | ||
|
|
14018b3dea | ||
|
|
1c196f958f | ||
|
|
91f2cfb999 | ||
|
|
c0a0331570 | ||
|
|
4ceba43e43 | ||
|
|
9b64e2e24c | ||
|
|
da5e227ad6 | ||
|
|
4f95e5d092 | ||
|
|
6a8fd0102f | ||
|
|
2b172ec4b4 | ||
|
|
5d2ad7fc38 | ||
|
|
3347473aa1 | ||
|
|
7f951b5595 | ||
|
|
208c69f196 | ||
|
|
32b69bf122 | ||
|
|
b1fac1817c | ||
|
|
6516358a71 | ||
|
|
c6e2087673 | ||
|
|
d8469a7bfa | ||
|
|
cf645aceae | ||
|
|
3dd72e238f | ||
|
|
b74ce74ec9 | ||
|
|
074aaa6005 | ||
|
|
9d5f43f4f0 | ||
|
|
7522aadc6e | ||
|
|
326fdcb454 | ||
|
|
c41addec96 | ||
|
|
defed15528 | ||
|
|
cbc29989fe | ||
|
|
73089470b1 | ||
|
|
3798f8bd25 | ||
|
|
f805a76e79 |
@@ -63,6 +63,7 @@ install:
|
|||||||
- tar -xf /tmp/wkhtmltox.tar.xz -C /tmp
|
- tar -xf /tmp/wkhtmltox.tar.xz -C /tmp
|
||||||
- sudo mv /tmp/wkhtmltox/bin/wkhtmltopdf /usr/local/bin/wkhtmltopdf
|
- sudo mv /tmp/wkhtmltox/bin/wkhtmltopdf /usr/local/bin/wkhtmltopdf
|
||||||
- sudo chmod o+x /usr/local/bin/wkhtmltopdf
|
- sudo chmod o+x /usr/local/bin/wkhtmltopdf
|
||||||
|
- sudo apt-get install libcups2-dev
|
||||||
|
|
||||||
- cd ~/frappe-bench
|
- cd ~/frappe-bench
|
||||||
|
|
||||||
@@ -76,5 +77,6 @@ install:
|
|||||||
- bench --site test_site reinstall --yes
|
- bench --site test_site reinstall --yes
|
||||||
|
|
||||||
after_script:
|
after_script:
|
||||||
|
- pip install coverage==4.5.4
|
||||||
- pip install python-coveralls
|
- pip install python-coveralls
|
||||||
- coveralls -b apps/erpnext -d ../../sites/.coverage
|
- coveralls -b apps/erpnext -d ../../sites/.coverage
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"baseUrl": "http://test_site_ui:8000"
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Using fixtures to represent data",
|
|
||||||
"email": "hello@cypress.io",
|
|
||||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
context('Form', () => {
|
|
||||||
before(() => {
|
|
||||||
cy.login('Administrator', 'qwe');
|
|
||||||
cy.visit('/desk');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('create a new opportunity', () => {
|
|
||||||
cy.visit('/desk#Form/Opportunity/New Opportunity 1');
|
|
||||||
cy.get('.page-title').should('contain', 'Not Saved');
|
|
||||||
cy.fill_field('opportunity_from', 'Customer', 'Select');
|
|
||||||
cy.fill_field('party_name', 'Test Customer', 'Link').blur();
|
|
||||||
cy.get('.primary-action').click();
|
|
||||||
cy.get('.page-title').should('contain', 'Open');
|
|
||||||
cy.get('.form-inner-toolbar button:contains("Lost")').click({ force: true });
|
|
||||||
cy.get('.modal input[data-fieldname="lost_reason"]').as('input');
|
|
||||||
cy.get('@input').focus().type('Higher', { delay: 200 });
|
|
||||||
cy.get('.modal .awesomplete ul')
|
|
||||||
.should('be.visible')
|
|
||||||
.get('li:contains("Higher Price")')
|
|
||||||
.click({ force: true });
|
|
||||||
cy.get('@input').focus().type('No Followup', { delay: 200 });
|
|
||||||
cy.get('.modal .awesomplete ul')
|
|
||||||
.should('be.visible')
|
|
||||||
.get('li:contains("No Followup")')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.fill_field('detailed_reason', 'Test Detailed Reason', 'Text');
|
|
||||||
cy.get('.modal button:contains("Declare Lost")').click({ force: true });
|
|
||||||
cy.get('.page-title').should('contain', 'Lost');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
// ***********************************************************
|
|
||||||
// This example plugins/index.js can be used to load plugins
|
|
||||||
//
|
|
||||||
// You can change the location of this file or turn off loading
|
|
||||||
// the plugins file with the 'pluginsFile' configuration option.
|
|
||||||
//
|
|
||||||
// You can read more here:
|
|
||||||
// https://on.cypress.io/plugins-guide
|
|
||||||
// ***********************************************************
|
|
||||||
|
|
||||||
// This function is called when a project is opened or re-opened (e.g. due to
|
|
||||||
// the project's config changing)
|
|
||||||
|
|
||||||
// module.exports = (on, config) => {
|
|
||||||
// `on` is used to hook into various events Cypress emits
|
|
||||||
// `config` is the resolved Cypress config
|
|
||||||
// }
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
// ***********************************************
|
|
||||||
// This example commands.js shows you how to
|
|
||||||
// create various custom commands and overwrite
|
|
||||||
// existing commands.
|
|
||||||
//
|
|
||||||
// For more comprehensive examples of custom
|
|
||||||
// commands please read more here:
|
|
||||||
// https://on.cypress.io/custom-commands
|
|
||||||
// ***********************************************
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// -- This is a parent command --
|
|
||||||
// Cypress.Commands.add("login", (email, password) => { ... })
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// -- This is a child command --
|
|
||||||
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// -- This is a dual command --
|
|
||||||
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// -- This is will overwrite an existing command --
|
|
||||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
// ***********************************************************
|
|
||||||
// This example support/index.js is processed and
|
|
||||||
// loaded automatically before your test files.
|
|
||||||
//
|
|
||||||
// This is a great place to put global configuration and
|
|
||||||
// behavior that modifies Cypress.
|
|
||||||
//
|
|
||||||
// You can change the location of this file or turn off
|
|
||||||
// automatically serving support files with the
|
|
||||||
// 'supportFile' configuration option.
|
|
||||||
//
|
|
||||||
// You can read more here:
|
|
||||||
// https://on.cypress.io/configuration
|
|
||||||
// ***********************************************************
|
|
||||||
|
|
||||||
// import frappe commands
|
|
||||||
import '../../../frappe/cypress/support/index';
|
|
||||||
// Import commands.js using ES2015 syntax:
|
|
||||||
import './commands';
|
|
||||||
|
|
||||||
// Alternatively you can use CommonJS syntax:
|
|
||||||
// require('./commands')
|
|
||||||
@@ -5,7 +5,7 @@ import frappe
|
|||||||
from erpnext.hooks import regional_overrides
|
from erpnext.hooks import regional_overrides
|
||||||
from frappe.utils import getdate
|
from frappe.utils import getdate
|
||||||
|
|
||||||
__version__ = '12.2.0'
|
__version__ = '12.9.2'
|
||||||
|
|
||||||
def get_default_company(user=None):
|
def get_default_company(user=None):
|
||||||
'''Get default company for user'''
|
'''Get default company for user'''
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe, json
|
import frappe, json
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import add_to_date, date_diff, getdate, nowdate, get_last_day, formatdate
|
from frappe.utils import add_to_date, date_diff, getdate, nowdate, get_last_day, formatdate, get_link_to_form
|
||||||
from erpnext.accounts.report.general_ledger.general_ledger import execute
|
from erpnext.accounts.report.general_ledger.general_ledger import execute
|
||||||
from frappe.core.page.dashboard.dashboard import cache_source, get_from_date_from_timespan
|
from frappe.core.page.dashboard.dashboard import cache_source, get_from_date_from_timespan
|
||||||
from frappe.desk.doctype.dashboard_chart.dashboard_chart import get_period_ending
|
from frappe.desk.doctype.dashboard_chart.dashboard_chart import get_period_ending
|
||||||
@@ -30,8 +30,13 @@ def get(chart_name = None, chart = None, no_cache = None, from_date = None, to_d
|
|||||||
account = filters.get("account")
|
account = filters.get("account")
|
||||||
company = filters.get("company")
|
company = filters.get("company")
|
||||||
|
|
||||||
if not account and chart:
|
if not account and chart_name:
|
||||||
frappe.throw(_("Account is not set for the dashboard chart {0}").format(chart))
|
frappe.throw(_("Account is not set for the dashboard chart {0}")
|
||||||
|
.format(get_link_to_form("Dashboard Chart", chart_name)))
|
||||||
|
|
||||||
|
if not frappe.db.exists("Account", account) and chart_name:
|
||||||
|
frappe.throw(_("Account {0} does not exists in the dashboard chart {1}")
|
||||||
|
.format(account, get_link_to_form("Dashboard Chart", chart_name)))
|
||||||
|
|
||||||
if not to_date:
|
if not to_date:
|
||||||
to_date = nowdate()
|
to_date = nowdate()
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ def book_deferred_income_or_expense(doc, posting_date=None):
|
|||||||
total_days, total_booking_days, account_currency)
|
total_days, total_booking_days, account_currency)
|
||||||
|
|
||||||
make_gl_entries(doc, credit_account, debit_account, against,
|
make_gl_entries(doc, credit_account, debit_account, against,
|
||||||
amount, base_amount, end_date, project, account_currency, item.cost_center, item.name)
|
amount, base_amount, end_date, project, account_currency, item.cost_center, item)
|
||||||
|
|
||||||
if getdate(end_date) < getdate(posting_date) and not last_gl_entry:
|
if getdate(end_date) < getdate(posting_date) and not last_gl_entry:
|
||||||
_book_deferred_revenue_or_expense(item)
|
_book_deferred_revenue_or_expense(item)
|
||||||
@@ -170,7 +170,7 @@ def book_deferred_income_or_expense(doc, posting_date=None):
|
|||||||
_book_deferred_revenue_or_expense(item)
|
_book_deferred_revenue_or_expense(item)
|
||||||
|
|
||||||
def make_gl_entries(doc, credit_account, debit_account, against,
|
def make_gl_entries(doc, credit_account, debit_account, against,
|
||||||
amount, base_amount, posting_date, project, account_currency, cost_center, voucher_detail_no):
|
amount, base_amount, posting_date, project, account_currency, cost_center, item):
|
||||||
# GL Entry for crediting the amount in the deferred expense
|
# GL Entry for crediting the amount in the deferred expense
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries
|
from erpnext.accounts.general_ledger import make_gl_entries
|
||||||
|
|
||||||
@@ -184,10 +184,10 @@ def make_gl_entries(doc, credit_account, debit_account, against,
|
|||||||
"credit": base_amount,
|
"credit": base_amount,
|
||||||
"credit_in_account_currency": amount,
|
"credit_in_account_currency": amount,
|
||||||
"cost_center": cost_center,
|
"cost_center": cost_center,
|
||||||
"voucher_detail_no": voucher_detail_no,
|
"voucher_detail_no": item.name,
|
||||||
'posting_date': posting_date,
|
'posting_date': posting_date,
|
||||||
'project': project
|
'project': project
|
||||||
}, account_currency)
|
}, account_currency, item=item)
|
||||||
)
|
)
|
||||||
# GL Entry to debit the amount from the expense
|
# GL Entry to debit the amount from the expense
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
@@ -197,10 +197,10 @@ def make_gl_entries(doc, credit_account, debit_account, against,
|
|||||||
"debit": base_amount,
|
"debit": base_amount,
|
||||||
"debit_in_account_currency": amount,
|
"debit_in_account_currency": amount,
|
||||||
"cost_center": cost_center,
|
"cost_center": cost_center,
|
||||||
"voucher_detail_no": voucher_detail_no,
|
"voucher_detail_no": item.name,
|
||||||
'posting_date': posting_date,
|
'posting_date': posting_date,
|
||||||
'project': project
|
'project': project
|
||||||
}, account_currency)
|
}, account_currency, item=item)
|
||||||
)
|
)
|
||||||
|
|
||||||
if gl_entries:
|
if gl_entries:
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"allow_copy": 1,
|
"allow_copy": 1,
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"creation": "2013-01-30 12:49:46",
|
"creation": "2013-01-30 12:49:46",
|
||||||
@@ -196,10 +197,13 @@
|
|||||||
],
|
],
|
||||||
"icon": "fa fa-money",
|
"icon": "fa fa-money",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"modified": "2019-10-10 19:10:02.967554",
|
"is_tree": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2020-03-18 18:26:03.992861",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Account",
|
"name": "Account",
|
||||||
|
"nsm_parent_field": "parent_account",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -89,34 +89,35 @@ class Account(NestedSet):
|
|||||||
throw(_("Root cannot be edited."), RootNotEditable)
|
throw(_("Root cannot be edited."), RootNotEditable)
|
||||||
|
|
||||||
if not self.parent_account and not self.is_group:
|
if not self.parent_account and not self.is_group:
|
||||||
frappe.throw(_("Root Account must be a group"))
|
frappe.throw(_("The root account {0} must be a group").format(frappe.bold(self.name)))
|
||||||
|
|
||||||
def validate_root_company_and_sync_account_to_children(self):
|
def validate_root_company_and_sync_account_to_children(self):
|
||||||
# ignore validation while creating new compnay or while syncing to child companies
|
# ignore validation while creating new compnay or while syncing to child companies
|
||||||
if frappe.local.flags.ignore_root_company_validation or self.flags.ignore_root_company_validation:
|
if frappe.local.flags.ignore_root_company_validation or self.flags.ignore_root_company_validation:
|
||||||
return
|
return
|
||||||
|
|
||||||
ancestors = get_root_company(self.company)
|
ancestors = get_root_company(self.company)
|
||||||
if ancestors:
|
if ancestors:
|
||||||
if frappe.get_value("Company", self.company, "allow_account_creation_against_child_company"):
|
if frappe.get_value("Company", self.company, "allow_account_creation_against_child_company"):
|
||||||
return
|
return
|
||||||
|
|
||||||
if not frappe.db.get_value("Account",
|
if not frappe.db.get_value("Account",
|
||||||
{'account_name': self.account_name, 'company': ancestors[0]}, 'name'):
|
{'account_name': self.account_name, 'company': ancestors[0]}, 'name'):
|
||||||
frappe.throw(_("Please add the account to root level Company - %s" % ancestors[0]))
|
frappe.throw(_("Please add the account to root level Company - %s" % ancestors[0]))
|
||||||
else:
|
elif self.parent_account:
|
||||||
descendants = get_descendants_of('Company', self.company)
|
descendants = get_descendants_of('Company', self.company)
|
||||||
if not descendants: return
|
if not descendants: return
|
||||||
|
|
||||||
parent_acc_name_map = {}
|
parent_acc_name_map = {}
|
||||||
parent_acc_name = frappe.db.get_value('Account', self.parent_account, "account_name")
|
parent_acc_name, parent_acc_number = frappe.db.get_value('Account', self.parent_account, \
|
||||||
for d in frappe.db.get_values('Account',
|
["account_name", "account_number"])
|
||||||
{"company": ["in", descendants], "account_name": parent_acc_name},
|
filters = {
|
||||||
["company", "name"], as_dict=True):
|
"company": ["in", descendants],
|
||||||
|
"account_name": parent_acc_name,
|
||||||
|
}
|
||||||
|
if parent_acc_number:
|
||||||
|
filters["account_number"] = parent_acc_number
|
||||||
|
|
||||||
|
for d in frappe.db.get_values('Account', filters=filters, fieldname=["company", "name"], as_dict=True):
|
||||||
parent_acc_name_map[d["company"]] = d["name"]
|
parent_acc_name_map[d["company"]] = d["name"]
|
||||||
|
|
||||||
if not parent_acc_name_map: return
|
if not parent_acc_name_map: return
|
||||||
|
|
||||||
self.create_account_for_child_company(parent_acc_name_map, descendants, parent_acc_name)
|
self.create_account_for_child_company(parent_acc_name_map, descendants, parent_acc_name)
|
||||||
|
|
||||||
def validate_group_or_ledger(self):
|
def validate_group_or_ledger(self):
|
||||||
@@ -174,7 +175,6 @@ class Account(NestedSet):
|
|||||||
filters["account_number"] = self.account_number
|
filters["account_number"] = self.account_number
|
||||||
|
|
||||||
child_account = frappe.db.get_value("Account", filters, 'name')
|
child_account = frappe.db.get_value("Account", filters, 'name')
|
||||||
|
|
||||||
if not child_account:
|
if not child_account:
|
||||||
doc = frappe.copy_doc(self)
|
doc = frappe.copy_doc(self)
|
||||||
doc.flags.ignore_root_company_validation = True
|
doc.flags.ignore_root_company_validation = True
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
frappe.provide("frappe.treeview_settings")
|
frappe.provide("frappe.treeview_settings")
|
||||||
|
|
||||||
frappe.treeview_settings["Account"] = {
|
frappe.treeview_settings["Account"] = {
|
||||||
breadcrumbs: "Accounts",
|
breadcrumb: "Accounts",
|
||||||
title: __("Chart Of Accounts"),
|
title: __("Chart Of Accounts"),
|
||||||
get_tree_root: false,
|
get_tree_root: false,
|
||||||
filters: [
|
filters: [
|
||||||
|
|||||||
@@ -406,11 +406,11 @@
|
|||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Bewertungskorrektur zu Forderungen aus Lieferungen und Leistungen": {
|
"Bewertungskorrektur zu Forderungen aus Lieferungen und Leistungen": {
|
||||||
"account_number": "9960"
|
"account_number": "9960"
|
||||||
},
|
},
|
||||||
"Debitoren": {
|
"Debitoren": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"account_number": "10000"
|
"account_number": "10000"
|
||||||
},
|
},
|
||||||
"Forderungen aus Lieferungen und Leistungen": {
|
"Forderungen aus Lieferungen und Leistungen": {
|
||||||
"account_number": "1200",
|
"account_number": "1200",
|
||||||
"account_type": "Receivable"
|
"account_type": "Receivable"
|
||||||
@@ -663,16 +663,22 @@
|
|||||||
"account_number": "1400"
|
"account_number": "1400"
|
||||||
},
|
},
|
||||||
"Abziehbare Vorsteuer 7 %": {
|
"Abziehbare Vorsteuer 7 %": {
|
||||||
"account_number": "1401"
|
"account_number": "1401",
|
||||||
|
"account_type": "Tax",
|
||||||
|
"tax_rate": 7.0
|
||||||
},
|
},
|
||||||
"Abziehbare Vorsteuer aus innergem. Erwerb": {
|
"Abziehbare Vorsteuer aus innergem. Erwerb": {
|
||||||
"account_number": "1402"
|
"account_number": "1402"
|
||||||
},
|
},
|
||||||
"Abziehbare Vorsteuer aus innergem. Erwerb 19%": {
|
"Abziehbare Vorsteuer aus innergem. Erwerb 19%": {
|
||||||
"account_number": "1404"
|
"account_number": "1404",
|
||||||
|
"account_type": "Tax",
|
||||||
|
"tax_rate": 19.0
|
||||||
},
|
},
|
||||||
"Abziehbare Vorsteuer 19 %": {
|
"Abziehbare Vorsteuer 19 %": {
|
||||||
"account_number": "1406"
|
"account_number": "1406",
|
||||||
|
"account_type": "Tax",
|
||||||
|
"tax_rate": 19.0
|
||||||
},
|
},
|
||||||
"Abziehbare Vorsteuer nach \u00a7 13b UStG 19 %": {
|
"Abziehbare Vorsteuer nach \u00a7 13b UStG 19 %": {
|
||||||
"account_number": "1407"
|
"account_number": "1407"
|
||||||
@@ -1197,15 +1203,15 @@
|
|||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Bewertungskorrektur zu Verb. aus Lieferungen und Leistungen": {
|
"Bewertungskorrektur zu Verb. aus Lieferungen und Leistungen": {
|
||||||
"account_number": "9964"
|
"account_number": "9964"
|
||||||
},
|
},
|
||||||
"Kreditoren": {
|
"Kreditoren": {
|
||||||
"account_number": "70000",
|
"account_number": "70000",
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Wareneingangs-Verrechnungskonto" : {
|
"Wareneingangs-Verrechnungskonto" : {
|
||||||
"account_number": "70001",
|
"account_number": "70001",
|
||||||
"account_type": "Stock Received But Not Billed"
|
"account_type": "Stock Received But Not Billed"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Verb. aus Lieferungen und Leistungen": {
|
"Verb. aus Lieferungen und Leistungen": {
|
||||||
"account_number": "3300",
|
"account_number": "3300",
|
||||||
"account_type": "Payable"
|
"account_type": "Payable"
|
||||||
@@ -1488,17 +1494,21 @@
|
|||||||
},
|
},
|
||||||
"Umsatzsteuer 7 %": {
|
"Umsatzsteuer 7 %": {
|
||||||
"account_number": "3801",
|
"account_number": "3801",
|
||||||
"account_type": "Tax"
|
"account_type": "Tax",
|
||||||
|
"tax_rate": 7.0
|
||||||
},
|
},
|
||||||
"Umsatzsteuer aus innergem. Erwerb": {
|
"Umsatzsteuer aus innergem. Erwerb": {
|
||||||
"account_number": "3802"
|
"account_number": "3802"
|
||||||
},
|
},
|
||||||
"Umsatzsteuer aus innergem. Erwerb 19 %": {
|
"Umsatzsteuer aus innergem. Erwerb 19 %": {
|
||||||
"account_number": "3804"
|
"account_number": "3804",
|
||||||
|
"account_type": "Tax",
|
||||||
|
"tax_rate": 19.0
|
||||||
},
|
},
|
||||||
"Umsatzsteuer 19 %": {
|
"Umsatzsteuer 19 %": {
|
||||||
"account_number": "3806",
|
"account_number": "3806",
|
||||||
"account_type": "Tax"
|
"account_type": "Tax",
|
||||||
|
"tax_rate": 19.0
|
||||||
},
|
},
|
||||||
"Umsatzsteuer aus im Inland steuerpfl. EU-Lieferungen": {
|
"Umsatzsteuer aus im Inland steuerpfl. EU-Lieferungen": {
|
||||||
"account_number": "3807"
|
"account_number": "3807"
|
||||||
@@ -2295,49 +2305,49 @@
|
|||||||
},
|
},
|
||||||
"6 - sonstige betriebliche Ertr\u00e4ge": {
|
"6 - sonstige betriebliche Ertr\u00e4ge": {
|
||||||
"root_type": "Income",
|
"root_type": "Income",
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Erhaltene Boni (Gruppe)": {
|
"Erhaltene Boni (Gruppe)": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Erhaltene Boni 7 % Vorsteuer": {
|
"Erhaltene Boni 7 % Vorsteuer": {
|
||||||
"account_number": "5750"
|
"account_number": "5750"
|
||||||
},
|
},
|
||||||
"Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe": {
|
"Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe": {
|
||||||
"account_number": "5753"
|
"account_number": "5753"
|
||||||
},
|
},
|
||||||
"Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe 7% Vorsteuer": {
|
"Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe 7% Vorsteuer": {
|
||||||
"account_number": "5754"
|
"account_number": "5754"
|
||||||
},
|
},
|
||||||
"Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe 19% Vorsteuer": {
|
"Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe 19% Vorsteuer": {
|
||||||
"account_number": "5755"
|
"account_number": "5755"
|
||||||
},
|
},
|
||||||
"Erhaltene Boni 19 % Vorsteuer": {
|
"Erhaltene Boni 19 % Vorsteuer": {
|
||||||
"account_number": "5760"
|
"account_number": "5760"
|
||||||
},
|
},
|
||||||
"Erhaltene Boni": {
|
"Erhaltene Boni": {
|
||||||
"account_number": "5769"
|
"account_number": "5769"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Erhaltene Rabatte (Gruppe)": {
|
"Erhaltene Rabatte (Gruppe)": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Erhaltene Rabatte": {
|
"Erhaltene Rabatte": {
|
||||||
"account_number": "5770"
|
"account_number": "5770"
|
||||||
},
|
},
|
||||||
"Erhaltene Rabatte 7 % Vorsteuer": {
|
"Erhaltene Rabatte 7 % Vorsteuer": {
|
||||||
"account_number": "5780"
|
"account_number": "5780"
|
||||||
},
|
},
|
||||||
"Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe": {
|
"Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe": {
|
||||||
"account_number": "5783"
|
"account_number": "5783"
|
||||||
},
|
},
|
||||||
"Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe 7% Vorsteuer": {
|
"Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe 7% Vorsteuer": {
|
||||||
"account_number": "5784"
|
"account_number": "5784"
|
||||||
},
|
},
|
||||||
"Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe 19% Vorsteuer": {
|
"Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe 19% Vorsteuer": {
|
||||||
"account_number": "5785"
|
"account_number": "5785"
|
||||||
},
|
},
|
||||||
"Erhaltene Rabatte 19 % Vorsteuer": {
|
"Erhaltene Rabatte 19 % Vorsteuer": {
|
||||||
"account_number": "5790"
|
"account_number": "5790"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Andere aktivierte Eigenleistungen": {
|
"Andere aktivierte Eigenleistungen": {
|
||||||
"account_number": "4820"
|
"account_number": "4820"
|
||||||
},
|
},
|
||||||
@@ -2407,29 +2417,26 @@
|
|||||||
"Erl\u00f6se aus Verk\u00e4ufen Sachanlageverm\u00f6gen (bei Buchgewinn)": {
|
"Erl\u00f6se aus Verk\u00e4ufen Sachanlageverm\u00f6gen (bei Buchgewinn)": {
|
||||||
"account_number": "4849"
|
"account_number": "4849"
|
||||||
},
|
},
|
||||||
"Erl\u00f6se aus Verk\u00e4ufen immaterieller VG (bei Buchgewinn) (Gruppe)": {
|
"Erl\u00f6se aus Verk\u00e4ufen immaterieller VG (bei Buchgewinn)": {
|
||||||
"is_group": 1,
|
"account_number": "4850"
|
||||||
"Erl\u00f6se aus Verk\u00e4ufen immaterieller VG (bei Buchgewinn)": {
|
},
|
||||||
"account_number": "4850"
|
"Erl\u00f6se aus Verk\u00e4ufen Finanzanlagen (bei Buchgewinn)": {
|
||||||
},
|
"account_number": "4851"
|
||||||
"Erl\u00f6se aus Verk\u00e4ufen Finanzanlagen (bei Buchgewinn)": {
|
},
|
||||||
"account_number": "4851"
|
"Erl\u00f6se aus Verk\u00e4ufen Finanzanlagen (inl\u00e4ndische Kap.Ges., bei Buchgewinn)": {
|
||||||
},
|
"account_number": "4852"
|
||||||
"Erl\u00f6se aus Verk\u00e4ufen Finanzanlagen (inl\u00e4ndische Kap.Ges., bei Buchgewinn)": {
|
},
|
||||||
"account_number": "4852"
|
"Anlagenabg\u00e4nge Sachanlagen (Restbuchwert bei Buchvergewinn)": {
|
||||||
},
|
"account_number": "4855"
|
||||||
"Anlagenabg\u00e4nge Sachanlagen (Restbuchwert bei Buchvergewinn)": {
|
},
|
||||||
"account_number": "4855"
|
"Anlagenabg\u00e4nge immaterielle VG (Restbuchwert bei Buchgewinn)": {
|
||||||
},
|
"account_number": "4856"
|
||||||
"Anlagenabg\u00e4nge immaterielle VG (Restbuchwert bei Buchgewinn)": {
|
},
|
||||||
"account_number": "4856"
|
"Anlagenabg\u00e4nge Finanzanlagen (Restbuchwert bei Buchgewinn)": {
|
||||||
},
|
"account_number": "4857"
|
||||||
"Anlagenabg\u00e4nge Finanzanlagen (Restbuchwert bei Buchgewinn)": {
|
},
|
||||||
"account_number": "4857"
|
"Anlagenabg\u00e4nge Finanzanlagen (inl\u00e4ndische Kap.Ges., Restbuchwert bei Buchgewinn)": {
|
||||||
},
|
"account_number": "4858"
|
||||||
"Anlagenabg\u00e4nge Finanzanlagen (inl\u00e4ndische Kap.Ges., Restbuchwert bei Buchgewinn)": {
|
|
||||||
"account_number": "4858"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"Ertr\u00e4ge aus Zuschreibungen des Sachanlageverm\u00f6gens": {
|
"Ertr\u00e4ge aus Zuschreibungen des Sachanlageverm\u00f6gens": {
|
||||||
"account_number": "4910",
|
"account_number": "4910",
|
||||||
@@ -2552,20 +2559,17 @@
|
|||||||
"Entnahme von Gegenst\u00e4nden ohne USt": {
|
"Entnahme von Gegenst\u00e4nden ohne USt": {
|
||||||
"account_number": "4605"
|
"account_number": "4605"
|
||||||
},
|
},
|
||||||
"Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens 7 % USt (Gruppe)": {
|
"Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens 7 % USt": {
|
||||||
"is_group": 1,
|
"account_number": "4630"
|
||||||
"Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens 7 % USt": {
|
},
|
||||||
"account_number": "4630"
|
"Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens ohne USt": {
|
||||||
},
|
"account_number": "4637"
|
||||||
"Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens ohne USt": {
|
},
|
||||||
"account_number": "4637"
|
"Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternnehmens ohne USt (Telefon-Nutzung)": {
|
||||||
},
|
"account_number": "4638"
|
||||||
"Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternnehmens ohne USt (Telefon-Nutzung)": {
|
},
|
||||||
"account_number": "4638"
|
"Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens ohne USt (Kfz-Nutzung)": {
|
||||||
},
|
"account_number": "4639"
|
||||||
"Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens ohne USt (Kfz-Nutzung)": {
|
|
||||||
"account_number": "4639"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens 19 % USt (Gruppe)": {
|
"Verwendung von Gegenst\u00e4nden f. Zwecke au\u00dferhalb des Unternehmens 19 % USt (Gruppe)": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
@@ -2603,14 +2607,11 @@
|
|||||||
"Unentgeltliche Zuwendung von Gegenst\u00e4nden ohne USt": {
|
"Unentgeltliche Zuwendung von Gegenst\u00e4nden ohne USt": {
|
||||||
"account_number": "4689"
|
"account_number": "4689"
|
||||||
},
|
},
|
||||||
"Nicht steuerbare Ums\u00e4tze (Innenums\u00e4tze) (Gruppe)": {
|
"Nicht steuerbare Ums\u00e4tze (Innenums\u00e4tze)": {
|
||||||
"is_group": 1,
|
"account_number": "4690"
|
||||||
"Nicht steuerbare Ums\u00e4tze (Innenums\u00e4tze)": {
|
},
|
||||||
"account_number": "4690"
|
"Umsatzsteuerverg\u00fctungen, z.B. nach \u00a7 24 UStG": {
|
||||||
},
|
"account_number": "4695"
|
||||||
"Umsatzsteuerverg\u00fctungen, z.B. nach \u00a7 24 UStG": {
|
|
||||||
"account_number": "4695"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"Au\u00dferordentliche Ertr\u00e4ge (Gruppe)": {
|
"Au\u00dferordentliche Ertr\u00e4ge (Gruppe)": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
@@ -2620,48 +2621,42 @@
|
|||||||
"Au\u00dferordentliche Ertr\u00e4ge finanzwirksam": {
|
"Au\u00dferordentliche Ertr\u00e4ge finanzwirksam": {
|
||||||
"account_number": "7401"
|
"account_number": "7401"
|
||||||
},
|
},
|
||||||
"Au\u00dferordentliche Ertr\u00e4ge nicht finanzwirksam (Gruppe)": {
|
"Au\u00dferordentliche Ertr\u00e4ge nicht finanzwirksam": {
|
||||||
"is_group": 1,
|
"account_number": "7450"
|
||||||
"Au\u00dferordentliche Ertr\u00e4ge nicht finanzwirksam": {
|
|
||||||
"account_number": "7450"
|
|
||||||
},
|
|
||||||
"Ertr\u00e4ge durch Verschmelzung und Umwandlung": {
|
|
||||||
"account_number": "7451"
|
|
||||||
},
|
|
||||||
"Ertr\u00e4ge durch den Verkauf von bedeutenden Beteiligungen": {
|
|
||||||
"account_number": "7452"
|
|
||||||
},
|
|
||||||
"Ert\u00e4ge durch den Verkauf von bedeutenden Grundst\u00fccken": {
|
|
||||||
"account_number": "7453"
|
|
||||||
},
|
|
||||||
"Gewinn aus der Ver\u00e4u\u00dferung oder der Aufgabe von Gesch\u00e4ftsaktivit\u00e4ten nach Steuern": {
|
|
||||||
"account_number": "7454"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"Au\u00dferordentliche Ertr\u00e4ge aus der Anwendung von \u00dcbergangsvorschriften (Gruppe)": {
|
"Ertr\u00e4ge durch Verschmelzung und Umwandlung": {
|
||||||
"is_group": 1,
|
"account_number": "7451"
|
||||||
"Au\u00dferordentliche Ertr\u00e4ge aus der Anwendung von \u00dcbergangsvorschriften": {
|
},
|
||||||
"account_number": "7460"
|
"Ertr\u00e4ge durch den Verkauf von bedeutenden Beteiligungen": {
|
||||||
},
|
"account_number": "7452"
|
||||||
"Au\u00dferordentliche Ertr\u00e4ge: Zuschreibung f. Sachanlageverm\u00f6gen": {
|
},
|
||||||
"account_number": "7461"
|
"Ert\u00e4ge durch den Verkauf von bedeutenden Grundst\u00fccken": {
|
||||||
},
|
"account_number": "7453"
|
||||||
"Au\u00dferordentliche Ertr\u00e4ge: Zuschreibung f. Finanzanlageverm\u00f6gen": {
|
},
|
||||||
"account_number": "7462"
|
"Gewinn aus der Ver\u00e4u\u00dferung oder der Aufgabe von Gesch\u00e4ftsaktivit\u00e4ten nach Steuern": {
|
||||||
},
|
"account_number": "7454"
|
||||||
"Au\u00dferordentliche Ertr\u00e4ge: Wertpapiere im Umlaufverm\u00f6gen": {
|
},
|
||||||
"account_number": "7463"
|
"Au\u00dferordentliche Ertr\u00e4ge aus der Anwendung von \u00dcbergangsvorschriften": {
|
||||||
},
|
"account_number": "7460"
|
||||||
"Au\u00dferordentliche Ertr\u00e4ge: latente Steuern": {
|
},
|
||||||
"account_number": "7464"
|
"Au\u00dferordentliche Ertr\u00e4ge: Zuschreibung f. Sachanlageverm\u00f6gen": {
|
||||||
}
|
"account_number": "7461"
|
||||||
|
},
|
||||||
|
"Au\u00dferordentliche Ertr\u00e4ge: Zuschreibung f. Finanzanlageverm\u00f6gen": {
|
||||||
|
"account_number": "7462"
|
||||||
|
},
|
||||||
|
"Au\u00dferordentliche Ertr\u00e4ge: Wertpapiere im Umlaufverm\u00f6gen": {
|
||||||
|
"account_number": "7463"
|
||||||
|
},
|
||||||
|
"Au\u00dferordentliche Ertr\u00e4ge: latente Steuern": {
|
||||||
|
"account_number": "7464"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"7 - sonstige betriebliche Aufwendungen": {
|
"7 - sonstige betriebliche Aufwendungen": {
|
||||||
"root_type": "Expense",
|
"root_type": "Expense",
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Erl\u00f6sschm\u00e4lerungen (Gruppe)": {
|
"Erl\u00f6sschm\u00e4lerungen (Gruppe)": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Erl\u00f6sschm\u00e4lerungen": {
|
"Erl\u00f6sschm\u00e4lerungen": {
|
||||||
"account_number": "4700"
|
"account_number": "4700"
|
||||||
@@ -2692,40 +2687,43 @@
|
|||||||
},
|
},
|
||||||
"Erl\u00f6sschm\u00e4lerungen aus im Inland steuerpfl. EU-Lieferungen 16 % USt": {
|
"Erl\u00f6sschm\u00e4lerungen aus im Inland steuerpfl. EU-Lieferungen 16 % USt": {
|
||||||
"account_number": "4729"
|
"account_number": "4729"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Gew\u00e4hrte Skonti (Gruppe)": {
|
||||||
|
"is_group": 1,
|
||||||
|
"Gew. Skonti": {
|
||||||
|
"account_number": "4730"
|
||||||
},
|
},
|
||||||
"Gew\u00e4hrte Skonti (Gruppe)": {
|
"Gew. Skonti 7 % USt": {
|
||||||
"is_group": 1,
|
"account_number": "4731"
|
||||||
"Gew. Skonti": {
|
|
||||||
"account_number": "4730"
|
|
||||||
},
|
|
||||||
"Gew. Skonti 7 % USt": {
|
|
||||||
"account_number": "4731"
|
|
||||||
},
|
|
||||||
"Gew. Skonti 19 % USt": {
|
|
||||||
"account_number": "4736"
|
|
||||||
},
|
|
||||||
"Gew. Skonti aus Lieferungen von Mobilfunkger./Schaltkr., f. die der Leistungsempf. die Ust. schuldet": {
|
|
||||||
"account_number": "4738"
|
|
||||||
},
|
|
||||||
"Gew. Skonti aus Leistungen, f. die der Leistungsempf. die Umsatzsteuer nach \u00a7 13b UStG schuldet": {
|
|
||||||
"account_number": "4741"
|
|
||||||
},
|
|
||||||
"Gew. Skonti aus Erl\u00f6sen aus im anderen EU-Land steuerpfl. Leistungen, f. die der Leistungsempf. die Ust. schuldet": {
|
|
||||||
"account_number": "4742"
|
|
||||||
},
|
|
||||||
"Gew. Skonti aus steuerfreien innergem. Lieferungen \u00a7 4 Nr. 1b UStG": {
|
|
||||||
"account_number": "4743"
|
|
||||||
},
|
|
||||||
"Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen": {
|
|
||||||
"account_number": "4745"
|
|
||||||
},
|
|
||||||
"Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen 7% USt": {
|
|
||||||
"account_number": "4746"
|
|
||||||
},
|
|
||||||
"Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen 19% USt": {
|
|
||||||
"account_number": "4748"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
"Gew. Skonti 19 % USt": {
|
||||||
|
"account_number": "4736"
|
||||||
|
},
|
||||||
|
"Gew. Skonti aus Lieferungen von Mobilfunkger./Schaltkr., f. die der Leistungsempf. die Ust. schuldet": {
|
||||||
|
"account_number": "4738"
|
||||||
|
},
|
||||||
|
"Gew. Skonti aus Leistungen, f. die der Leistungsempf. die Umsatzsteuer nach \u00a7 13b UStG schuldet": {
|
||||||
|
"account_number": "4741"
|
||||||
|
},
|
||||||
|
"Gew. Skonti aus Erl\u00f6sen aus im anderen EU-Land steuerpfl. Leistungen, f. die der Leistungsempf. die Ust. schuldet": {
|
||||||
|
"account_number": "4742"
|
||||||
|
},
|
||||||
|
"Gew. Skonti aus steuerfreien innergem. Lieferungen \u00a7 4 Nr. 1b UStG": {
|
||||||
|
"account_number": "4743"
|
||||||
|
},
|
||||||
|
"Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen": {
|
||||||
|
"account_number": "4745"
|
||||||
|
},
|
||||||
|
"Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen 7% USt": {
|
||||||
|
"account_number": "4746"
|
||||||
|
},
|
||||||
|
"Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen 19% USt": {
|
||||||
|
"account_number": "4748"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Gew\u00e4hrte Boni (Gruppe)": {
|
||||||
|
"is_group": 1,
|
||||||
"Gew\u00e4hrte Boni 7 % USt": {
|
"Gew\u00e4hrte Boni 7 % USt": {
|
||||||
"account_number": "4750"
|
"account_number": "4750"
|
||||||
},
|
},
|
||||||
@@ -2744,7 +2742,7 @@
|
|||||||
"Gew\u00e4hrte Rabatte 19 % USt": {
|
"Gew\u00e4hrte Rabatte 19 % USt": {
|
||||||
"account_number": "4790"
|
"account_number": "4790"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Sonstige betriebliche Aufwendungen": {
|
"Sonstige betriebliche Aufwendungen": {
|
||||||
"account_number": "6300"
|
"account_number": "6300"
|
||||||
},
|
},
|
||||||
@@ -2838,103 +2836,79 @@
|
|||||||
"account_number": "6398"
|
"account_number": "6398"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Versicherungen (Gruppe)": {
|
"Versicherungen": {
|
||||||
"is_group": 1,
|
"account_number": "6400"
|
||||||
"Versicherungen": {
|
},
|
||||||
"account_number": "6400"
|
"Versicherungen f. Geb\u00e4ude, die zum Betriebsverm\u00f6gen geh\u00f6ren": {
|
||||||
},
|
"account_number": "6405"
|
||||||
"Versicherungen f. Geb\u00e4ude, die zum Betriebsverm\u00f6gen geh\u00f6ren": {
|
},
|
||||||
"account_number": "6405"
|
"Netto-Pr\u00e4mie f. R\u00fcckdeckung k\u00fcnftiger Versorgungsleistungen": {
|
||||||
},
|
"account_number": "6410"
|
||||||
"Netto-Pr\u00e4mie f. R\u00fcckdeckung k\u00fcnftiger Versorgungsleistungen": {
|
},
|
||||||
"account_number": "6410"
|
"Beitr\u00e4ge": {
|
||||||
},
|
"account_number": "6420"
|
||||||
"Beitr\u00e4ge": {
|
},
|
||||||
"account_number": "6420"
|
"Sonstige Abgaben": {
|
||||||
},
|
"account_number": "6430"
|
||||||
"Sonstige Abgaben": {
|
},
|
||||||
"account_number": "6430"
|
"Steuerlich abzugsf\u00e4hige Versp\u00e4tungszuschl\u00e4ge und Zwangsgelder": {
|
||||||
},
|
"account_number": "6436"
|
||||||
"Steuerlich abzugsf\u00e4hige Versp\u00e4tungszuschl\u00e4ge und Zwangsgelder": {
|
},
|
||||||
"account_number": "6436"
|
"Steuerlich nicht abzugsf\u00e4hige Versp\u00e4tungszuschl\u00e4ge und Zwangsgelder": {
|
||||||
},
|
"account_number": "6437"
|
||||||
"Steuerlich nicht abzugsf\u00e4hige Versp\u00e4tungszuschl\u00e4ge und Zwangsgelder": {
|
},
|
||||||
"account_number": "6437"
|
"Ausgleichsabgabe i. S. d. Schwerbehindertengesetzes": {
|
||||||
},
|
"account_number": "6440"
|
||||||
"Ausgleichsabgabe i. S. d. Schwerbehindertengesetzes": {
|
},
|
||||||
"account_number": "6440"
|
"Reparaturen und Instandhaltung von Bauten": {
|
||||||
},
|
"account_number": "6450"
|
||||||
"Reparaturen und Instandhaltung von Bauten": {
|
},
|
||||||
"account_number": "6450"
|
"Reparaturen und Instandhaltung von technischenAnlagen und Maschinen": {
|
||||||
},
|
"account_number": "6460"
|
||||||
"Reparaturen und Instandhaltung von technischenAnlagen und Maschinen": {
|
},
|
||||||
"account_number": "6460"
|
"Reparaturen und Instandhaltung von anderen Anlagen und Betriebs- und Gesch\u00e4ftsausstattung": {
|
||||||
},
|
"account_number": "6470"
|
||||||
"Reparaturen und Instandhaltung von anderen Anlagen und Betriebs- und Gesch\u00e4ftsausstattung": {
|
},
|
||||||
"account_number": "6470"
|
"Zuf\u00fchrung zu Aufwandsr\u00fcckstellungen": {
|
||||||
},
|
"account_number": "6475"
|
||||||
"Zuf\u00fchrung zu Aufwandsr\u00fcckstellungen": {
|
},
|
||||||
"account_number": "6475"
|
"Reparaturen und Instandhaltung von anderen Anlagen": {
|
||||||
},
|
"account_number": "6485"
|
||||||
"Reparaturen und Instandhaltung von anderen Anlagen": {
|
},
|
||||||
"account_number": "6485"
|
"Sonstige Reparaturen und Instandhaltungen": {
|
||||||
},
|
"account_number": "6490"
|
||||||
"Sonstige Reparaturen und Instandhaltungen": {
|
},
|
||||||
"account_number": "6490"
|
"Wartungskosten f. Hard- und Software": {
|
||||||
},
|
"account_number": "6495"
|
||||||
"Wartungskosten f. Hard- und Software": {
|
},
|
||||||
"account_number": "6495"
|
"Mietleasing (bewegliche Wirtschaftsg\u00fcter)": {
|
||||||
},
|
"account_number": "6498"
|
||||||
"Mietleasing (bewegliche Wirtschaftsg\u00fcter)": {
|
|
||||||
"account_number": "6498"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"Fahrzeugkosten (Gruppe)": {
|
"Fahrzeugkosten (Gruppe)": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Fahrzeugkosten": {
|
"Fahrzeugkosten": {
|
||||||
"account_number": "6500"
|
"account_number": "6500"
|
||||||
},
|
},
|
||||||
"Kfz-Versicherungen (Gruppe)": {
|
"Kfz-Versicherungen": {
|
||||||
"is_group": 1,
|
"account_number": "6520"
|
||||||
"Kfz-Versicherungen": {
|
|
||||||
"account_number": "6520"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"Laufende Kfz-Betriebskosten (Gruppe)": {
|
"Laufende Kfz-Betriebskosten": {
|
||||||
"is_group": 1,
|
"account_number": "6530"
|
||||||
"Laufende Kfz-Betriebskosten": {
|
|
||||||
"account_number": "6530"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"Kfz-Reparaturen (Gruppe)": {
|
"Kfz-Reparaturen": {
|
||||||
"is_group": 1,
|
"account_number": "6540"
|
||||||
"Kfz-Reparaturen": {
|
|
||||||
"account_number": "6540"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"Garagenmiete (Gruppe)": {
|
"Garagenmiete": {
|
||||||
"is_group": 1,
|
"account_number": "6550"
|
||||||
"Garagenmiete": {
|
|
||||||
"account_number": "6550"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"Mietleasing Kfz (Gruppe)": {
|
"Mietleasing Kfz": {
|
||||||
"is_group": 1,
|
"account_number": "6560"
|
||||||
"Mietleasing Kfz": {
|
|
||||||
"account_number": "6560"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"Sonstige Kfz-Kosten (Gruppe)": {
|
"Sonstige Kfz-Kosten": {
|
||||||
"is_group": 1,
|
"account_number": "6570"
|
||||||
"Sonstige Kfz-Kosten": {
|
|
||||||
"account_number": "6570"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"Mautgeb\u00fchren (Gruppe)": {
|
"Mautgeb\u00fchren": {
|
||||||
"is_group": 1,
|
"account_number": "6580"
|
||||||
"Mautgeb\u00fchren": {
|
|
||||||
"account_number": "6580"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"Kfz-Kosten f. betrieblich genutzte zum Privatverm\u00f6gen geh\u00f6rende Kraftfahrzeuge": {
|
"Kfz-Kosten f. betrieblich genutzte zum Privatverm\u00f6gen geh\u00f6rende Kraftfahrzeuge": {
|
||||||
"account_number": "6590"
|
"account_number": "6590"
|
||||||
@@ -2996,20 +2970,23 @@
|
|||||||
"Nicht abzugsf\u00e4hige Betriebsausgaben aus Werbe- und Repr\u00e4sentationskosten": {
|
"Nicht abzugsf\u00e4hige Betriebsausgaben aus Werbe- und Repr\u00e4sentationskosten": {
|
||||||
"account_number": "6645"
|
"account_number": "6645"
|
||||||
},
|
},
|
||||||
"Reisekosten Arbeitnehmer": {
|
"Reisekosten Arbeitnehmer (Gruppe)": {
|
||||||
"account_number": "6650"
|
"is_group": 1,
|
||||||
},
|
"Reisekosten Arbeitnehmer": {
|
||||||
"Reisekosten Arbeitnehmer \u00dcbernachtungsaufwand": {
|
"account_number": "6650"
|
||||||
"account_number": "6660"
|
},
|
||||||
},
|
"Reisekosten Arbeitnehmer \u00dcbernachtungsaufwand": {
|
||||||
"Reisekosten Arbeitnehmer Fahrtkosten": {
|
"account_number": "6660"
|
||||||
"account_number": "6663"
|
},
|
||||||
},
|
"Reisekosten Arbeitnehmer Fahrtkosten": {
|
||||||
"Reisekosten Arbeitnehmer Verpflegungsmehraufwand": {
|
"account_number": "6663"
|
||||||
"account_number": "6664"
|
},
|
||||||
},
|
"Reisekosten Arbeitnehmer Verpflegungsmehraufwand": {
|
||||||
"Kilometergelderstattung Arbeitnehmer": {
|
"account_number": "6664"
|
||||||
"account_number": "6668"
|
},
|
||||||
|
"Kilometergelderstattung Arbeitnehmer": {
|
||||||
|
"account_number": "6668"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"Reisekosten Unternehmer (Gruppe)": {
|
"Reisekosten Unternehmer (Gruppe)": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ class TestAccount(unittest.TestCase):
|
|||||||
acc.account_name = "Accumulated Depreciation"
|
acc.account_name = "Accumulated Depreciation"
|
||||||
acc.parent_account = "Fixed Assets - _TC"
|
acc.parent_account = "Fixed Assets - _TC"
|
||||||
acc.company = "_Test Company"
|
acc.company = "_Test Company"
|
||||||
|
acc.account_type = "Accumulated Depreciation"
|
||||||
acc.insert()
|
acc.insert()
|
||||||
|
|
||||||
doc = frappe.get_doc("Account", "Securities and Deposits - _TC")
|
doc = frappe.get_doc("Account", "Securities and Deposits - _TC")
|
||||||
@@ -149,7 +150,7 @@ def _make_test_records(verbose):
|
|||||||
|
|
||||||
# fixed asset depreciation
|
# fixed asset depreciation
|
||||||
["_Test Fixed Asset", "Current Assets", 0, "Fixed Asset", None],
|
["_Test Fixed Asset", "Current Assets", 0, "Fixed Asset", None],
|
||||||
["_Test Accumulated Depreciations", "Current Assets", 0, None, None],
|
["_Test Accumulated Depreciations", "Current Assets", 0, "Accumulated Depreciation", None],
|
||||||
["_Test Depreciations", "Expenses", 0, None, None],
|
["_Test Depreciations", "Expenses", 0, None, None],
|
||||||
["_Test Gain/Loss on Asset Disposal", "Expenses", 0, None, None],
|
["_Test Gain/Loss on Asset Disposal", "Expenses", 0, None, None],
|
||||||
|
|
||||||
|
|||||||
@@ -48,12 +48,6 @@ frappe.ui.form.on('Accounting Dimension', {
|
|||||||
frm.set_value('label', frm.doc.document_type);
|
frm.set_value('label', frm.doc.document_type);
|
||||||
frm.set_value('fieldname', frappe.model.scrub(frm.doc.document_type));
|
frm.set_value('fieldname', frappe.model.scrub(frm.doc.document_type));
|
||||||
|
|
||||||
if (frm.is_new()){
|
|
||||||
let row = frappe.model.add_child(frm.doc, "Accounting Dimension Detail", "dimension_defaults");
|
|
||||||
row.reference_document = frm.doc.document_type;
|
|
||||||
frm.refresh_fields("dimension_defaults");
|
|
||||||
}
|
|
||||||
|
|
||||||
frappe.db.get_value('Accounting Dimension', {'document_type': frm.doc.document_type}, 'document_type', (r) => {
|
frappe.db.get_value('Accounting Dimension', {'document_type': frm.doc.document_type}, 'document_type', (r) => {
|
||||||
if (r && r.document_type) {
|
if (r && r.document_type) {
|
||||||
frm.set_df_property('document_type', 'description', "Document type is already set as dimension");
|
frm.set_df_property('document_type', 'description', "Document type is already set as dimension");
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"autoname": "field:label",
|
"autoname": "field:label",
|
||||||
"creation": "2019-05-04 18:13:37.002352",
|
"creation": "2019-05-04 18:13:37.002352",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@@ -46,7 +47,8 @@
|
|||||||
"options": "Accounting Dimension Detail"
|
"options": "Accounting Dimension Detail"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2019-07-17 16:49:31.134385",
|
"links": [],
|
||||||
|
"modified": "2020-03-22 20:34:39.805728",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Accounting Dimension",
|
"name": "Accounting Dimension",
|
||||||
@@ -63,9 +65,20 @@
|
|||||||
"role": "System Manager",
|
"role": "System Manager",
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "Accounts Manager",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 1,
|
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "ASC",
|
"sort_order": "ASC",
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
|
|||||||
@@ -162,9 +162,9 @@ def toggle_disabling(doc):
|
|||||||
|
|
||||||
def get_doctypes_with_dimensions():
|
def get_doctypes_with_dimensions():
|
||||||
doclist = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "Asset",
|
doclist = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "Asset",
|
||||||
"Expense Claim", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note", "Sales Invoice Item", "Purchase Invoice Item",
|
"Expense Claim", "Expense Claim Detail", "Expense Taxes and Charges", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note",
|
||||||
"Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item", "Purchase Receipt Item",
|
"Sales Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item",
|
||||||
"Stock Entry Detail", "Payment Entry Deduction", "Sales Taxes and Charges", "Purchase Taxes and Charges", "Shipping Rule",
|
"Purchase Receipt Item", "Stock Entry Detail", "Payment Entry Deduction", "Sales Taxes and Charges", "Purchase Taxes and Charges", "Shipping Rule",
|
||||||
"Landed Cost Item", "Asset Value Adjustment", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation",
|
"Landed Cost Item", "Asset Value Adjustment", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation",
|
||||||
"Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item", "Subscription",
|
"Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item", "Subscription",
|
||||||
"Subscription Plan"]
|
"Subscription Plan"]
|
||||||
@@ -172,7 +172,7 @@ def get_doctypes_with_dimensions():
|
|||||||
return doclist
|
return doclist
|
||||||
|
|
||||||
def get_accounting_dimensions(as_list=True):
|
def get_accounting_dimensions(as_list=True):
|
||||||
accounting_dimensions = frappe.get_all("Accounting Dimension", fields=["label", "fieldname", "disabled"])
|
accounting_dimensions = frappe.get_all("Accounting Dimension", fields=["label", "fieldname", "disabled", "document_type"])
|
||||||
|
|
||||||
if as_list:
|
if as_list:
|
||||||
return [d.fieldname for d in accounting_dimensions]
|
return [d.fieldname for d in accounting_dimensions]
|
||||||
@@ -186,6 +186,18 @@ def get_checks_for_pl_and_bs_accounts():
|
|||||||
|
|
||||||
return dimensions
|
return dimensions
|
||||||
|
|
||||||
|
def get_dimension_with_children(doctype, dimension):
|
||||||
|
|
||||||
|
if isinstance(dimension, list):
|
||||||
|
dimension = dimension[0]
|
||||||
|
|
||||||
|
all_dimensions = []
|
||||||
|
lft, rgt = frappe.db.get_value(doctype, dimension, ["lft", "rgt"])
|
||||||
|
children = frappe.get_all(doctype, filters={"lft": [">=", lft], "rgt": ["<=", rgt]}, order_by="lft")
|
||||||
|
all_dimensions += [c.name for c in children]
|
||||||
|
|
||||||
|
return all_dimensions
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_dimension_filters():
|
def get_dimension_filters():
|
||||||
dimension_filters = frappe.db.sql("""
|
dimension_filters = frappe.db.sql("""
|
||||||
@@ -194,12 +206,13 @@ def get_dimension_filters():
|
|||||||
WHERE disabled = 0
|
WHERE disabled = 0
|
||||||
""", as_dict=1)
|
""", as_dict=1)
|
||||||
|
|
||||||
default_dimensions = frappe.db.sql("""SELECT parent, company, default_dimension
|
default_dimensions = frappe.db.sql("""SELECT p.fieldname, c.company, c.default_dimension
|
||||||
FROM `tabAccounting Dimension Detail`""", as_dict=1)
|
FROM `tabAccounting Dimension Detail` c, `tabAccounting Dimension` p
|
||||||
|
WHERE c.parent = p.name""", as_dict=1)
|
||||||
|
|
||||||
default_dimensions_map = {}
|
default_dimensions_map = {}
|
||||||
for dimension in default_dimensions:
|
for dimension in default_dimensions:
|
||||||
default_dimensions_map.setdefault(dimension['company'], {})
|
default_dimensions_map.setdefault(dimension.company, {})
|
||||||
default_dimensions_map[dimension['company']][dimension['parent']] = dimension['default_dimension']
|
default_dimensions_map[dimension.company][dimension.fieldname] = dimension.default_dimension
|
||||||
|
|
||||||
return dimension_filters, default_dimensions_map
|
return dimension_filters, default_dimensions_map
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ class AccountingPeriod(Document):
|
|||||||
|
|
||||||
def get_doctypes_for_closing(self):
|
def get_doctypes_for_closing(self):
|
||||||
docs_for_closing = []
|
docs_for_closing = []
|
||||||
doctypes = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Payroll Entry", "Bank Reconciliation",
|
doctypes = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Payroll Entry", \
|
||||||
"Asset", "Purchase Order", "Sales Order", "Leave Application", "Leave Allocation", "Stock Entry"]
|
"Bank Reconciliation", "Asset", "Stock Entry"]
|
||||||
closed_doctypes = [{"document_type": doctype, "closed": 1} for doctype in doctypes]
|
closed_doctypes = [{"document_type": doctype, "closed": 1} for doctype in doctypes]
|
||||||
for closed_doctype in closed_doctypes:
|
for closed_doctype in closed_doctypes:
|
||||||
docs_for_closing.append(closed_doctype)
|
docs_for_closing.append(closed_doctype)
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
frappe.ui.form.on('Accounts Settings', {
|
||||||
|
refresh: function(frm) {
|
||||||
|
frm.set_df_property("acc_frozen_upto", "label", "Books Closed Through");
|
||||||
|
frm.set_df_property("frozen_accounts_modifier", "label", "Role Allowed to Close Books & Make Changes to Closed Periods");
|
||||||
|
frm.set_df_property("credit_controller", "label", "Credit Manager");
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"allow_rename": 1,
|
"allow_rename": 1,
|
||||||
"creation": "2017-05-29 21:35:13.136357",
|
"creation": "2017-05-29 21:35:13.136357",
|
||||||
@@ -82,7 +83,7 @@
|
|||||||
"default": "0",
|
"default": "0",
|
||||||
"fieldname": "is_default",
|
"fieldname": "is_default",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Is the Default Account"
|
"label": "Is Default Account"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
@@ -211,7 +212,8 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2019-10-02 01:34:12.417601",
|
"links": [],
|
||||||
|
"modified": "2020-01-29 20:42:26.458316",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Bank Account",
|
"name": "Bank Account",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe, json
|
import frappe, json
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
from frappe.desk.search import sanitize_searchfield
|
||||||
|
|
||||||
class BankGuarantee(Document):
|
class BankGuarantee(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
@@ -22,5 +23,8 @@ class BankGuarantee(Document):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_vouchar_detials(column_list, doctype, docname):
|
def get_vouchar_detials(column_list, doctype, docname):
|
||||||
|
column_list = json.loads(column_list)
|
||||||
|
for col in column_list:
|
||||||
|
sanitize_searchfield(col)
|
||||||
return frappe.db.sql(''' select {columns} from `tab{doctype}` where name=%s'''
|
return frappe.db.sql(''' select {columns} from `tab{doctype}` where name=%s'''
|
||||||
.format(columns=", ".join(json.loads(column_list)), doctype=doctype), docname, as_dict=1)[0]
|
.format(columns=", ".join(json.loads(column_list)), doctype=doctype), docname, as_dict=1)[0]
|
||||||
|
|||||||
@@ -3,16 +3,16 @@
|
|||||||
|
|
||||||
frappe.ui.form.on("Bank Reconciliation", {
|
frappe.ui.form.on("Bank Reconciliation", {
|
||||||
setup: function(frm) {
|
setup: function(frm) {
|
||||||
frm.add_fetch("bank_account", "account_currency", "account_currency");
|
frm.add_fetch("account", "account_currency", "account_currency");
|
||||||
},
|
},
|
||||||
|
|
||||||
onload: function(frm) {
|
onload: function(frm) {
|
||||||
|
|
||||||
let default_bank_account = frappe.defaults.get_user_default("Company")?
|
let default_bank_account = frappe.defaults.get_user_default("Company")?
|
||||||
locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]: "";
|
locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]: "";
|
||||||
frm.set_value("bank_account", default_bank_account);
|
frm.set_value("account", default_bank_account);
|
||||||
|
|
||||||
frm.set_query("bank_account", function() {
|
frm.set_query("account", function() {
|
||||||
return {
|
return {
|
||||||
"filters": {
|
"filters": {
|
||||||
"account_type": ["in",["Bank","Cash"]],
|
"account_type": ["in",["Bank","Cash"]],
|
||||||
|
|||||||
@@ -19,10 +19,9 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"description": "Select account head of the bank where cheque was deposited.",
|
"fetch_from": "bank_account.account",
|
||||||
"fetch_from": "bank_account_no.account",
|
|
||||||
"fetch_if_empty": 1,
|
"fetch_if_empty": 1,
|
||||||
"fieldname": "bank_account",
|
"fieldname": "account",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
@@ -31,7 +30,7 @@
|
|||||||
"in_global_search": 0,
|
"in_global_search": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
"in_standard_filter": 0,
|
||||||
"label": "Bank Account",
|
"label": "Account",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"options": "Account",
|
"options": "Account",
|
||||||
@@ -164,7 +163,6 @@
|
|||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
@@ -183,8 +181,9 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"description": "Select the Bank Account to reconcile.",
|
||||||
"fetch_if_empty": 0,
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "bank_account_no",
|
"fieldname": "bank_account",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
@@ -193,12 +192,11 @@
|
|||||||
"in_global_search": 0,
|
"in_global_search": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"in_standard_filter": 0,
|
"in_standard_filter": 0,
|
||||||
"label": "Bank Account No",
|
"label": "Bank Account",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"options": "Bank Account",
|
"options": "Bank Account",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
@@ -450,7 +448,7 @@
|
|||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": 0,
|
"menu_index": 0,
|
||||||
"modified": "2019-04-09 18:41:06.110453",
|
"modified": "2020-01-22 00:00:00.000000",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Bank Reconciliation",
|
"name": "Bank Reconciliation",
|
||||||
@@ -483,4 +481,4 @@
|
|||||||
"track_changes": 0,
|
"track_changes": 0,
|
||||||
"track_seen": 0,
|
"track_seen": 0,
|
||||||
"track_views": 0
|
"track_views": 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,17 +13,15 @@ form_grid_templates = {
|
|||||||
|
|
||||||
class BankReconciliation(Document):
|
class BankReconciliation(Document):
|
||||||
def get_payment_entries(self):
|
def get_payment_entries(self):
|
||||||
if not (self.bank_account and self.from_date and self.to_date):
|
if not (self.from_date and self.to_date):
|
||||||
msgprint(_("Bank Account, From Date and To Date are Mandatory"))
|
frappe.throw(_("From Date and To Date are Mandatory"))
|
||||||
return
|
|
||||||
|
if not self.account:
|
||||||
|
frappe.throw(_("Account is mandatory to get payment entries"))
|
||||||
|
|
||||||
condition = ""
|
condition = ""
|
||||||
if not self.include_reconciled_entries:
|
if not self.include_reconciled_entries:
|
||||||
condition = " and (clearance_date is null or clearance_date='0000-00-00')"
|
condition = "and (clearance_date IS NULL or clearance_date='0000-00-00')"
|
||||||
|
|
||||||
account_cond = ""
|
|
||||||
if self.bank_account_no:
|
|
||||||
account_cond = " and t2.bank_account_no = {0}".format(frappe.db.escape(self.bank_account_no))
|
|
||||||
|
|
||||||
journal_entries = frappe.db.sql("""
|
journal_entries = frappe.db.sql("""
|
||||||
select
|
select
|
||||||
@@ -34,15 +32,15 @@ class BankReconciliation(Document):
|
|||||||
from
|
from
|
||||||
`tabJournal Entry` t1, `tabJournal Entry Account` t2
|
`tabJournal Entry` t1, `tabJournal Entry Account` t2
|
||||||
where
|
where
|
||||||
t2.parent = t1.name and t2.account = %s and t1.docstatus=1
|
t2.parent = t1.name and t2.account = %(account)s and t1.docstatus=1
|
||||||
and t1.posting_date >= %s and t1.posting_date <= %s
|
and t1.posting_date >= %(from)s and t1.posting_date <= %(to)s
|
||||||
and ifnull(t1.is_opening, 'No') = 'No' {0} {1}
|
and ifnull(t1.is_opening, 'No') = 'No' {condition}
|
||||||
group by t2.account, t1.name
|
group by t2.account, t1.name
|
||||||
order by t1.posting_date ASC, t1.name DESC
|
order by t1.posting_date ASC, t1.name DESC
|
||||||
""".format(condition, account_cond), (self.bank_account, self.from_date, self.to_date), as_dict=1)
|
""".format(condition=condition), {"account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1)
|
||||||
|
|
||||||
if self.bank_account_no:
|
if self.bank_account:
|
||||||
condition = " and bank_account = %(bank_account_no)s"
|
condition += 'and bank_account = %(bank_account)s'
|
||||||
|
|
||||||
payment_entries = frappe.db.sql("""
|
payment_entries = frappe.db.sql("""
|
||||||
select
|
select
|
||||||
@@ -55,12 +53,12 @@ class BankReconciliation(Document):
|
|||||||
from `tabPayment Entry`
|
from `tabPayment Entry`
|
||||||
where
|
where
|
||||||
(paid_from=%(account)s or paid_to=%(account)s) and docstatus=1
|
(paid_from=%(account)s or paid_to=%(account)s) and docstatus=1
|
||||||
and posting_date >= %(from)s and posting_date <= %(to)s {0}
|
and posting_date >= %(from)s and posting_date <= %(to)s
|
||||||
|
{condition}
|
||||||
order by
|
order by
|
||||||
posting_date ASC, name DESC
|
posting_date ASC, name DESC
|
||||||
""".format(condition),
|
""".format(condition=condition), {"account": self.account, "from":self.from_date,
|
||||||
{"account":self.bank_account, "from":self.from_date,
|
"to": self.to_date, "bank_account": self.bank_account}, as_dict=1)
|
||||||
"to":self.to_date, "bank_account_no": self.bank_account_no}, as_dict=1)
|
|
||||||
|
|
||||||
pos_entries = []
|
pos_entries = []
|
||||||
if self.include_pos_transactions:
|
if self.include_pos_transactions:
|
||||||
@@ -72,11 +70,10 @@ class BankReconciliation(Document):
|
|||||||
from `tabSales Invoice Payment` sip, `tabSales Invoice` si, `tabAccount` account
|
from `tabSales Invoice Payment` sip, `tabSales Invoice` si, `tabAccount` account
|
||||||
where
|
where
|
||||||
sip.account=%(account)s and si.docstatus=1 and sip.parent = si.name
|
sip.account=%(account)s and si.docstatus=1 and sip.parent = si.name
|
||||||
and account.name = sip.account and si.posting_date >= %(from)s and si.posting_date <= %(to)s {0}
|
and account.name = sip.account and si.posting_date >= %(from)s and si.posting_date <= %(to)s
|
||||||
order by
|
order by
|
||||||
si.posting_date ASC, si.name DESC
|
si.posting_date ASC, si.name DESC
|
||||||
""".format(condition),
|
""", {"account":self.account, "from":self.from_date, "to":self.to_date}, as_dict=1)
|
||||||
{"account":self.bank_account, "from":self.from_date, "to":self.to_date}, as_dict=1)
|
|
||||||
|
|
||||||
entries = sorted(list(payment_entries)+list(journal_entries+list(pos_entries)),
|
entries = sorted(list(payment_entries)+list(journal_entries+list(pos_entries)),
|
||||||
key=lambda k: k['posting_date'] or getdate(nowdate()))
|
key=lambda k: k['posting_date'] or getdate(nowdate()))
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ def upload_bank_statement():
|
|||||||
with open(frappe.uploaded_file, "rb") as upfile:
|
with open(frappe.uploaded_file, "rb") as upfile:
|
||||||
fcontent = upfile.read()
|
fcontent = upfile.read()
|
||||||
else:
|
else:
|
||||||
from frappe.utils.file_manager import get_uploaded_content
|
fcontent = frappe.local.uploaded_file
|
||||||
fname, fcontent = get_uploaded_content()
|
fname = frappe.local.uploaded_filename
|
||||||
|
|
||||||
if frappe.safe_encode(fname).lower().endswith("csv".encode('utf-8')):
|
if frappe.safe_encode(fname).lower().endswith("csv".encode('utf-8')):
|
||||||
from frappe.utils.csvutils import read_csv_content
|
from frappe.utils.csvutils import read_csv_content
|
||||||
|
|||||||
@@ -110,6 +110,15 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"translatable": 0,
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.docstatus==1",
|
||||||
|
"fieldname": "clearance_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"label": "Clearance Date",
|
||||||
|
"no_copy": 1,
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"has_web_view": 0,
|
||||||
@@ -122,7 +131,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2018-12-06 10:57:02.635141",
|
"modified": "2020-01-22 00:00:00.000000",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Bank Transaction Payments",
|
"name": "Bank Transaction Payments",
|
||||||
@@ -138,4 +147,4 @@
|
|||||||
"track_changes": 1,
|
"track_changes": 1,
|
||||||
"track_seen": 0,
|
"track_seen": 0,
|
||||||
"track_views": 0
|
"track_views": 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from frappe.utils import flt, getdate, add_months, get_last_day, fmt_money, nowd
|
|||||||
from frappe.model.naming import make_autoname
|
from frappe.model.naming import make_autoname
|
||||||
from erpnext.accounts.utils import get_fiscal_year
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
|
||||||
|
|
||||||
class BudgetError(frappe.ValidationError): pass
|
class BudgetError(frappe.ValidationError): pass
|
||||||
class DuplicateBudgetError(frappe.ValidationError): pass
|
class DuplicateBudgetError(frappe.ValidationError): pass
|
||||||
@@ -98,30 +99,32 @@ def validate_expense_against_budget(args):
|
|||||||
if not (args.get('account') and args.get('cost_center')) and args.item_code:
|
if not (args.get('account') and args.get('cost_center')) and args.item_code:
|
||||||
args.cost_center, args.account = get_item_details(args)
|
args.cost_center, args.account = get_item_details(args)
|
||||||
|
|
||||||
if not (args.cost_center or args.project) and not args.account:
|
if not args.account:
|
||||||
return
|
return
|
||||||
|
|
||||||
for budget_against in ['project', 'cost_center']:
|
for budget_against in ['project', 'cost_center'] + get_accounting_dimensions():
|
||||||
if (args.get(budget_against) and args.account
|
if (args.get(budget_against) and args.account
|
||||||
and frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"})):
|
and frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"})):
|
||||||
|
|
||||||
if args.project and budget_against == 'project':
|
doctype = frappe.unscrub(budget_against)
|
||||||
condition = "and b.project=%s" % frappe.db.escape(args.project)
|
|
||||||
args.budget_against_field = "Project"
|
|
||||||
|
|
||||||
elif args.cost_center and budget_against == 'cost_center':
|
if frappe.get_cached_value('DocType', doctype, 'is_tree'):
|
||||||
cc_lft, cc_rgt = frappe.db.get_value("Cost Center", args.cost_center, ["lft", "rgt"])
|
lft, rgt = frappe.db.get_value(doctype, args.get(budget_against), ["lft", "rgt"])
|
||||||
condition = """and exists(select name from `tabCost Center`
|
condition = """and exists(select name from `tab%s`
|
||||||
where lft<=%s and rgt>=%s and name=b.cost_center)""" % (cc_lft, cc_rgt)
|
where lft<=%s and rgt>=%s and name=b.%s)""" % (doctype, lft, rgt, budget_against) #nosec
|
||||||
args.budget_against_field = "Cost Center"
|
args.is_tree = True
|
||||||
|
else:
|
||||||
|
condition = "and b.%s=%s" % (budget_against, frappe.db.escape(args.get(budget_against)))
|
||||||
|
args.is_tree = False
|
||||||
|
|
||||||
args.budget_against = args.get(budget_against)
|
args.budget_against_field = budget_against
|
||||||
|
args.budget_against_doctype = doctype
|
||||||
|
|
||||||
budget_records = frappe.db.sql("""
|
budget_records = frappe.db.sql("""
|
||||||
select
|
select
|
||||||
b.{budget_against_field} as budget_against, ba.budget_amount, b.monthly_distribution,
|
b.{budget_against_field} as budget_against, ba.budget_amount, b.monthly_distribution,
|
||||||
ifnull(b.applicable_on_material_request, 0) as for_material_request,
|
ifnull(b.applicable_on_material_request, 0) as for_material_request,
|
||||||
ifnull(applicable_on_purchase_order,0) as for_purchase_order,
|
ifnull(applicable_on_purchase_order, 0) as for_purchase_order,
|
||||||
ifnull(applicable_on_booking_actual_expenses,0) as for_actual_expenses,
|
ifnull(applicable_on_booking_actual_expenses,0) as for_actual_expenses,
|
||||||
b.action_if_annual_budget_exceeded, b.action_if_accumulated_monthly_budget_exceeded,
|
b.action_if_annual_budget_exceeded, b.action_if_accumulated_monthly_budget_exceeded,
|
||||||
b.action_if_annual_budget_exceeded_on_mr, b.action_if_accumulated_monthly_budget_exceeded_on_mr,
|
b.action_if_annual_budget_exceeded_on_mr, b.action_if_accumulated_monthly_budget_exceeded_on_mr,
|
||||||
@@ -132,9 +135,7 @@ def validate_expense_against_budget(args):
|
|||||||
b.name=ba.parent and b.fiscal_year=%s
|
b.name=ba.parent and b.fiscal_year=%s
|
||||||
and ba.account=%s and b.docstatus=1
|
and ba.account=%s and b.docstatus=1
|
||||||
{condition}
|
{condition}
|
||||||
""".format(condition=condition,
|
""".format(condition=condition, budget_against_field=budget_against), (args.fiscal_year, args.account), as_dict=True) #nosec
|
||||||
budget_against_field=frappe.scrub(args.get("budget_against_field"))),
|
|
||||||
(args.fiscal_year, args.account), as_dict=True)
|
|
||||||
|
|
||||||
if budget_records:
|
if budget_records:
|
||||||
validate_budget_records(args, budget_records)
|
validate_budget_records(args, budget_records)
|
||||||
@@ -210,10 +211,10 @@ def get_requested_amount(args, budget):
|
|||||||
item_code = args.get('item_code')
|
item_code = args.get('item_code')
|
||||||
condition = get_other_condition(args, budget, 'Material Request')
|
condition = get_other_condition(args, budget, 'Material Request')
|
||||||
|
|
||||||
data = frappe.db.sql(""" select ifnull((sum(mri.stock_qty - mri.ordered_qty) * rate), 0) as amount
|
data = frappe.db.sql(""" select ifnull((sum(child.stock_qty - child.ordered_qty) * rate), 0) as amount
|
||||||
from `tabMaterial Request Item` mri, `tabMaterial Request` mr where mr.name = mri.parent and
|
from `tabMaterial Request Item` child, `tabMaterial Request` parent where parent.name = child.parent and
|
||||||
mri.item_code = %s and mr.docstatus = 1 and mri.stock_qty > mri.ordered_qty and {0} and
|
child.item_code = %s and parent.docstatus = 1 and child.stock_qty > child.ordered_qty and {0} and
|
||||||
mr.material_request_type = 'Purchase' and mr.status != 'Stopped'""".format(condition), item_code, as_list=1)
|
parent.material_request_type = 'Purchase' and parent.status != 'Stopped'""".format(condition), item_code, as_list=1)
|
||||||
|
|
||||||
return data[0][0] if data else 0
|
return data[0][0] if data else 0
|
||||||
|
|
||||||
@@ -221,45 +222,55 @@ def get_ordered_amount(args, budget):
|
|||||||
item_code = args.get('item_code')
|
item_code = args.get('item_code')
|
||||||
condition = get_other_condition(args, budget, 'Purchase Order')
|
condition = get_other_condition(args, budget, 'Purchase Order')
|
||||||
|
|
||||||
data = frappe.db.sql(""" select ifnull(sum(poi.amount - poi.billed_amt), 0) as amount
|
data = frappe.db.sql(""" select ifnull(sum(child.amount - child.billed_amt), 0) as amount
|
||||||
from `tabPurchase Order Item` poi, `tabPurchase Order` po where
|
from `tabPurchase Order Item` child, `tabPurchase Order` parent where
|
||||||
po.name = poi.parent and poi.item_code = %s and po.docstatus = 1 and poi.amount > poi.billed_amt
|
parent.name = child.parent and child.item_code = %s and parent.docstatus = 1 and child.amount > child.billed_amt
|
||||||
and po.status != 'Closed' and {0}""".format(condition), item_code, as_list=1)
|
and parent.status != 'Closed' and {0}""".format(condition), item_code, as_list=1)
|
||||||
|
|
||||||
return data[0][0] if data else 0
|
return data[0][0] if data else 0
|
||||||
|
|
||||||
def get_other_condition(args, budget, for_doc):
|
def get_other_condition(args, budget, for_doc):
|
||||||
condition = "expense_account = '%s'" % (args.expense_account)
|
condition = "expense_account = '%s'" % (args.expense_account)
|
||||||
budget_against_field = frappe.scrub(args.get("budget_against_field"))
|
budget_against_field = args.get("budget_against_field")
|
||||||
|
|
||||||
if budget_against_field and args.get(budget_against_field):
|
if budget_against_field and args.get(budget_against_field):
|
||||||
condition += " and %s = '%s'" %(budget_against_field, args.get(budget_against_field))
|
condition += " and child.%s = '%s'" % (budget_against_field, args.get(budget_against_field))
|
||||||
|
|
||||||
if args.get('fiscal_year'):
|
if args.get('fiscal_year'):
|
||||||
date_field = 'schedule_date' if for_doc == 'Material Request' else 'transaction_date'
|
date_field = 'schedule_date' if for_doc == 'Material Request' else 'transaction_date'
|
||||||
start_date, end_date = frappe.db.get_value('Fiscal Year', args.get('fiscal_year'),
|
start_date, end_date = frappe.db.get_value('Fiscal Year', args.get('fiscal_year'),
|
||||||
['year_start_date', 'year_end_date'])
|
['year_start_date', 'year_end_date'])
|
||||||
|
|
||||||
alias = 'mr' if for_doc == 'Material Request' else 'po'
|
condition += """ and parent.%s
|
||||||
condition += """ and %s.%s
|
between '%s' and '%s' """ %(date_field, start_date, end_date)
|
||||||
between '%s' and '%s' """ %(alias, date_field, start_date, end_date)
|
|
||||||
|
|
||||||
return condition
|
return condition
|
||||||
|
|
||||||
def get_actual_expense(args):
|
def get_actual_expense(args):
|
||||||
|
if not args.budget_against_doctype:
|
||||||
|
args.budget_against_doctype = frappe.unscrub(args.budget_against_field)
|
||||||
|
|
||||||
|
budget_against_field = args.get('budget_against_field')
|
||||||
condition1 = " and gle.posting_date <= %(month_end_date)s" \
|
condition1 = " and gle.posting_date <= %(month_end_date)s" \
|
||||||
if args.get("month_end_date") else ""
|
if args.get("month_end_date") else ""
|
||||||
if args.budget_against_field == "Cost Center":
|
|
||||||
lft_rgt = frappe.db.get_value(args.budget_against_field,
|
if args.is_tree:
|
||||||
args.budget_against, ["lft", "rgt"], as_dict=1)
|
lft_rgt = frappe.db.get_value(args.budget_against_doctype,
|
||||||
|
args.get(budget_against_field), ["lft", "rgt"], as_dict=1)
|
||||||
|
|
||||||
args.update(lft_rgt)
|
args.update(lft_rgt)
|
||||||
condition2 = """and exists(select name from `tabCost Center`
|
|
||||||
where lft>=%(lft)s and rgt<=%(rgt)s and name=gle.cost_center)"""
|
|
||||||
|
|
||||||
elif args.budget_against_field == "Project":
|
condition2 = """and exists(select name from `tab{doctype}`
|
||||||
condition2 = "and exists(select name from `tabProject` where name=gle.project and gle.project = %(budget_against)s)"
|
where lft>=%(lft)s and rgt<=%(rgt)s
|
||||||
|
and name=gle.{budget_against_field})""".format(doctype=args.budget_against_doctype, #nosec
|
||||||
|
budget_against_field=budget_against_field)
|
||||||
|
else:
|
||||||
|
condition2 = """and exists(select name from `tab{doctype}`
|
||||||
|
where name=gle.{budget_against} and
|
||||||
|
gle.{budget_against} = %({budget_against})s)""".format(doctype=args.budget_against_doctype,
|
||||||
|
budget_against = budget_against_field)
|
||||||
|
|
||||||
return flt(frappe.db.sql("""
|
amount = flt(frappe.db.sql("""
|
||||||
select sum(gle.debit) - sum(gle.credit)
|
select sum(gle.debit) - sum(gle.credit)
|
||||||
from `tabGL Entry` gle
|
from `tabGL Entry` gle
|
||||||
where gle.account=%(account)s
|
where gle.account=%(account)s
|
||||||
@@ -268,7 +279,9 @@ def get_actual_expense(args):
|
|||||||
and gle.company=%(company)s
|
and gle.company=%(company)s
|
||||||
and gle.docstatus=1
|
and gle.docstatus=1
|
||||||
{condition2}
|
{condition2}
|
||||||
""".format(condition1=condition1, condition2=condition2), (args))[0][0])
|
""".format(condition1=condition1, condition2=condition2), (args))[0][0]) #nosec
|
||||||
|
|
||||||
|
return amount
|
||||||
|
|
||||||
def get_accumulated_monthly_budget(monthly_distribution, posting_date, fiscal_year, annual_budget):
|
def get_accumulated_monthly_budget(monthly_distribution, posting_date, fiscal_year, annual_budget):
|
||||||
distribution = {}
|
distribution = {}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journ
|
|||||||
|
|
||||||
class TestBudget(unittest.TestCase):
|
class TestBudget(unittest.TestCase):
|
||||||
def test_monthly_budget_crossed_ignore(self):
|
def test_monthly_budget_crossed_ignore(self):
|
||||||
set_total_expense_zero("2013-02-28", "Cost Center")
|
set_total_expense_zero("2013-02-28", "cost_center")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Cost Center")
|
budget = make_budget(budget_against="Cost Center")
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
budget.cancel()
|
budget.cancel()
|
||||||
|
|
||||||
def test_monthly_budget_crossed_stop1(self):
|
def test_monthly_budget_crossed_stop1(self):
|
||||||
set_total_expense_zero("2013-02-28", "Cost Center")
|
set_total_expense_zero("2013-02-28", "cost_center")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Cost Center")
|
budget = make_budget(budget_against="Cost Center")
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
budget.cancel()
|
budget.cancel()
|
||||||
|
|
||||||
def test_exception_approver_role(self):
|
def test_exception_approver_role(self):
|
||||||
set_total_expense_zero("2013-02-28", "Cost Center")
|
set_total_expense_zero("2013-02-28", "cost_center")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Cost Center")
|
budget = make_budget(budget_against="Cost Center")
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
budget.cancel()
|
budget.cancel()
|
||||||
|
|
||||||
def test_monthly_budget_crossed_stop2(self):
|
def test_monthly_budget_crossed_stop2(self):
|
||||||
set_total_expense_zero("2013-02-28", "Project")
|
set_total_expense_zero("2013-02-28", "project")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Project")
|
budget = make_budget(budget_against="Project")
|
||||||
|
|
||||||
@@ -129,7 +129,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
budget.cancel()
|
budget.cancel()
|
||||||
|
|
||||||
def test_yearly_budget_crossed_stop1(self):
|
def test_yearly_budget_crossed_stop1(self):
|
||||||
set_total_expense_zero("2013-02-28", "Cost Center")
|
set_total_expense_zero("2013-02-28", "cost_center")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Cost Center")
|
budget = make_budget(budget_against="Cost Center")
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
budget.cancel()
|
budget.cancel()
|
||||||
|
|
||||||
def test_yearly_budget_crossed_stop2(self):
|
def test_yearly_budget_crossed_stop2(self):
|
||||||
set_total_expense_zero("2013-02-28", "Project")
|
set_total_expense_zero("2013-02-28", "project")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Project")
|
budget = make_budget(budget_against="Project")
|
||||||
|
|
||||||
@@ -153,7 +153,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
budget.cancel()
|
budget.cancel()
|
||||||
|
|
||||||
def test_monthly_budget_on_cancellation1(self):
|
def test_monthly_budget_on_cancellation1(self):
|
||||||
set_total_expense_zero("2013-02-28", "Cost Center")
|
set_total_expense_zero("2013-02-28", "cost_center")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Cost Center")
|
budget = make_budget(budget_against="Cost Center")
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
budget.cancel()
|
budget.cancel()
|
||||||
|
|
||||||
def test_monthly_budget_on_cancellation2(self):
|
def test_monthly_budget_on_cancellation2(self):
|
||||||
set_total_expense_zero("2013-02-28", "Project")
|
set_total_expense_zero("2013-02-28", "project")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Project")
|
budget = make_budget(budget_against="Project")
|
||||||
|
|
||||||
@@ -201,8 +201,8 @@ class TestBudget(unittest.TestCase):
|
|||||||
budget.cancel()
|
budget.cancel()
|
||||||
|
|
||||||
def test_monthly_budget_against_group_cost_center(self):
|
def test_monthly_budget_against_group_cost_center(self):
|
||||||
set_total_expense_zero("2013-02-28", "Cost Center")
|
set_total_expense_zero("2013-02-28", "cost_center")
|
||||||
set_total_expense_zero("2013-02-28", "Cost Center", "_Test Cost Center 2 - _TC")
|
set_total_expense_zero("2013-02-28", "cost_center", "_Test Cost Center 2 - _TC")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Cost Center", cost_center="_Test Company - _TC")
|
budget = make_budget(budget_against="Cost Center", cost_center="_Test Company - _TC")
|
||||||
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||||
@@ -241,25 +241,30 @@ class TestBudget(unittest.TestCase):
|
|||||||
|
|
||||||
|
|
||||||
def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None):
|
def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None):
|
||||||
if budget_against_field == "Project":
|
if budget_against_field == "project":
|
||||||
budget_against = "_Test Project"
|
budget_against = "_Test Project"
|
||||||
else:
|
else:
|
||||||
budget_against = budget_against_CC or "_Test Cost Center - _TC"
|
budget_against = budget_against_CC or "_Test Cost Center - _TC"
|
||||||
existing_expense = get_actual_expense(frappe._dict({
|
|
||||||
|
args = frappe._dict({
|
||||||
"account": "_Test Account Cost for Goods Sold - _TC",
|
"account": "_Test Account Cost for Goods Sold - _TC",
|
||||||
"cost_center": "_Test Cost Center - _TC",
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
"monthly_end_date": posting_date,
|
"monthly_end_date": posting_date,
|
||||||
"company": "_Test Company",
|
"company": "_Test Company",
|
||||||
"fiscal_year": "_Test Fiscal Year 2013",
|
"fiscal_year": "_Test Fiscal Year 2013",
|
||||||
"budget_against_field": budget_against_field,
|
"budget_against_field": budget_against_field,
|
||||||
"budget_against": budget_against
|
})
|
||||||
}))
|
|
||||||
|
if not args.get(budget_against_field):
|
||||||
|
args[budget_against_field] = budget_against
|
||||||
|
|
||||||
|
existing_expense = get_actual_expense(args)
|
||||||
|
|
||||||
if existing_expense:
|
if existing_expense:
|
||||||
if budget_against_field == "Cost Center":
|
if budget_against_field == "cost_center":
|
||||||
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
||||||
"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True)
|
"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True)
|
||||||
elif budget_against_field == "Project":
|
elif budget_against_field == "project":
|
||||||
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
||||||
"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True, project="_Test Project", posting_date="2013-02-28")
|
"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True, project="_Test Project", posting_date="2013-02-28")
|
||||||
|
|
||||||
|
|||||||
@@ -32,10 +32,12 @@ frappe.ui.form.on('C-Form Invoice Detail', {
|
|||||||
invoice_no(frm, cdt, cdn) {
|
invoice_no(frm, cdt, cdn) {
|
||||||
let d = frappe.get_doc(cdt, cdn);
|
let d = frappe.get_doc(cdt, cdn);
|
||||||
|
|
||||||
frm.call('get_invoice_details', {
|
if (d.invoice_no) {
|
||||||
invoice_no: d.invoice_no
|
frm.call('get_invoice_details', {
|
||||||
}).then(r => {
|
invoice_no: d.invoice_no
|
||||||
frappe.model.set_value(cdt, cdn, r.message);
|
}).then(r => {
|
||||||
});
|
frappe.model.set_value(cdt, cdn, r.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,17 +17,60 @@ frappe.ui.form.on('Chart of Accounts Importer', {
|
|||||||
if (frm.page && frm.page.show_import_button) {
|
if (frm.page && frm.page.show_import_button) {
|
||||||
create_import_button(frm);
|
create_import_button(frm);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// show download template button when company is properly selected
|
download_template: function(frm) {
|
||||||
if(frm.doc.company) {
|
var d = new frappe.ui.Dialog({
|
||||||
// download the csv template file
|
title: __("Download Template"),
|
||||||
frm.add_custom_button(__("Download template"), function () {
|
fields: [
|
||||||
let get_template_url = 'erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.download_template';
|
{
|
||||||
open_url_post(frappe.request.url, { cmd: get_template_url, doctype: frm.doc.doctype });
|
label : "File Type",
|
||||||
});
|
fieldname: "file_type",
|
||||||
} else {
|
fieldtype: "Select",
|
||||||
frm.set_value("import_file", "");
|
reqd: 1,
|
||||||
}
|
options: ["Excel", "CSV"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Template Type",
|
||||||
|
fieldname: "template_type",
|
||||||
|
fieldtype: "Select",
|
||||||
|
reqd: 1,
|
||||||
|
options: ["Sample Template", "Blank Template"],
|
||||||
|
change: () => {
|
||||||
|
let template_type = d.get_value('template_type');
|
||||||
|
|
||||||
|
if (template_type === "Sample Template") {
|
||||||
|
d.set_df_property('template_type', 'description',
|
||||||
|
`The Sample Template contains all the required accounts pre filled in the template.
|
||||||
|
You can add more accounts or change existing accounts in the template as per your choice.`);
|
||||||
|
} else {
|
||||||
|
d.set_df_property('template_type', 'description',
|
||||||
|
`The Blank Template contains just the account type and root type required to build the Chart
|
||||||
|
of Accounts. Please enter the account names and add more rows as per your requirement.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
primary_action: function() {
|
||||||
|
var data = d.get_values();
|
||||||
|
|
||||||
|
if (!data.template_type) {
|
||||||
|
frappe.throw(__('Please select <b>Template Type</b> to download template'));
|
||||||
|
}
|
||||||
|
|
||||||
|
open_url_post(
|
||||||
|
'/api/method/erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.download_template',
|
||||||
|
{
|
||||||
|
file_type: data.file_type,
|
||||||
|
template_type: data.template_type
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
d.hide();
|
||||||
|
},
|
||||||
|
primary_action_label: __('Download')
|
||||||
|
});
|
||||||
|
d.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
import_file: function (frm) {
|
import_file: function (frm) {
|
||||||
@@ -41,21 +84,24 @@ frappe.ui.form.on('Chart of Accounts Importer', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
company: function (frm) {
|
company: function (frm) {
|
||||||
// validate that no Gl Entry record for the company exists.
|
if (frm.doc.company) {
|
||||||
frappe.call({
|
// validate that no Gl Entry record for the company exists.
|
||||||
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.validate_company",
|
frappe.call({
|
||||||
args: {
|
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.validate_company",
|
||||||
company: frm.doc.company
|
args: {
|
||||||
},
|
company: frm.doc.company
|
||||||
callback: function(r) {
|
},
|
||||||
if(r.message===false) {
|
callback: function(r) {
|
||||||
frm.set_value("company", "");
|
if(r.message===false) {
|
||||||
frappe.throw(__("Transactions against the company already exist! "));
|
frm.set_value("company", "");
|
||||||
} else {
|
frappe.throw(__(`Transactions against the company already exist!
|
||||||
frm.trigger("refresh");
|
Chart Of accounts can be imported for company with no transactions`));
|
||||||
|
} else {
|
||||||
|
frm.trigger("refresh");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -77,7 +123,7 @@ var validate_csv_data = function(frm) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var create_import_button = function(frm) {
|
var create_import_button = function(frm) {
|
||||||
frm.page.set_primary_action(__("Start Import"), function () {
|
frm.page.set_primary_action(__("Import"), function () {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.import_coa",
|
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.import_coa",
|
||||||
args: {
|
args: {
|
||||||
@@ -118,7 +164,8 @@ var generate_tree_preview = function(frm) {
|
|||||||
args: {
|
args: {
|
||||||
file_name: frm.doc.import_file,
|
file_name: frm.doc.import_file,
|
||||||
parent: parent,
|
parent: parent,
|
||||||
doctype: 'Chart of Accounts Importer'
|
doctype: 'Chart of Accounts Importer',
|
||||||
|
file_type: frm.doc.file_type
|
||||||
},
|
},
|
||||||
onclick: function(node) {
|
onclick: function(node) {
|
||||||
parent = node.value;
|
parent = node.value;
|
||||||
|
|||||||
@@ -1,226 +1,71 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 1,
|
"actions": [],
|
||||||
"allow_events_in_timeline": 0,
|
"allow_copy": 1,
|
||||||
"allow_guest_to_view": 0,
|
"creation": "2019-02-01 12:24:34.761380",
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"beta": 0,
|
|
||||||
"creation": "2019-02-01 12:24:34.761380",
|
|
||||||
"custom": 0,
|
|
||||||
"description": "Import Chart of Accounts from a csv file",
|
"description": "Import Chart of Accounts from a csv file",
|
||||||
"docstatus": 0,
|
"doctype": "DocType",
|
||||||
"doctype": "DocType",
|
"document_type": "Other",
|
||||||
"document_type": "Other",
|
"editable_grid": 1,
|
||||||
"editable_grid": 1,
|
"engine": "InnoDB",
|
||||||
"engine": "InnoDB",
|
"field_order": [
|
||||||
|
"company",
|
||||||
|
"download_template",
|
||||||
|
"import_file",
|
||||||
|
"chart_preview",
|
||||||
|
"chart_tree"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "company",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Link",
|
||||||
"allow_on_submit": 0,
|
"in_list_view": 1,
|
||||||
"bold": 0,
|
"label": "Company",
|
||||||
"collapsible": 0,
|
"options": "Company"
|
||||||
"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": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Company",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Company",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"depends_on": "company",
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "import_file_section",
|
|
||||||
"fieldtype": "Section 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,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"depends_on": "",
|
|
||||||
"fieldname": "import_file",
|
"fieldname": "import_file",
|
||||||
"fieldtype": "Attach",
|
"fieldtype": "Attach",
|
||||||
"hidden": 0,
|
"label": "Attach custom Chart of Accounts file"
|
||||||
"ignore_user_permissions": 0,
|
},
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Attach custom Chart of Accounts file",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 1,
|
"collapsible": 1,
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "chart_preview",
|
"fieldname": "chart_preview",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"label": "Chart Preview"
|
||||||
"ignore_user_permissions": 0,
|
},
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Chart Preview",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "chart_tree",
|
"fieldname": "chart_tree",
|
||||||
"fieldtype": "HTML",
|
"fieldtype": "HTML",
|
||||||
"hidden": 0,
|
"label": "Chart Tree"
|
||||||
"ignore_user_permissions": 0,
|
},
|
||||||
"ignore_xss_filter": 0,
|
{
|
||||||
"in_filter": 0,
|
"depends_on": "company",
|
||||||
"in_global_search": 0,
|
"fieldname": "download_template",
|
||||||
"in_list_view": 0,
|
"fieldtype": "Button",
|
||||||
"in_standard_filter": 0,
|
"label": "Download Template"
|
||||||
"label": "Chart Tree",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"hide_toolbar": 1,
|
||||||
"hide_heading": 1,
|
"in_create": 1,
|
||||||
"hide_toolbar": 1,
|
"issingle": 1,
|
||||||
"idx": 0,
|
"links": [],
|
||||||
"image_view": 0,
|
"modified": "2020-02-28 08:49:11.422846",
|
||||||
"in_create": 1,
|
"modified_by": "Administrator",
|
||||||
"is_submittable": 0,
|
"module": "Accounts",
|
||||||
"issingle": 1,
|
"name": "Chart of Accounts Importer",
|
||||||
"istable": 0,
|
"owner": "Administrator",
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2019-02-04 23:10:30.136807",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "Accounts",
|
|
||||||
"name": "Chart of Accounts Importer",
|
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"cancel": 0,
|
"read": 1,
|
||||||
"create": 1,
|
"role": "Accounts Manager",
|
||||||
"delete": 0,
|
"share": 1,
|
||||||
"email": 0,
|
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 0,
|
|
||||||
"read": 1,
|
|
||||||
"report": 0,
|
|
||||||
"role": "Accounts Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"read_only_onload": 0,
|
"sort_field": "modified",
|
||||||
"show_name_in_global_search": 0,
|
"sort_order": "DESC"
|
||||||
"sort_field": "",
|
|
||||||
"sort_order": "DESC",
|
|
||||||
"track_changes": 0,
|
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
||||||
@@ -4,18 +4,28 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
import frappe, csv
|
import frappe, csv, os
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import cstr
|
from frappe.utils import cstr, cint
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils.csvutils import UnicodeWriter
|
from frappe.utils.csvutils import UnicodeWriter
|
||||||
from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts, build_tree_from_json
|
from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts, build_tree_from_json
|
||||||
|
from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file, read_xls_file_from_attached_file
|
||||||
|
|
||||||
class ChartofAccountsImporter(Document):
|
class ChartofAccountsImporter(Document):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def validate_company(company):
|
def validate_company(company):
|
||||||
|
parent_company, allow_account_creation_against_child_company = frappe.db.get_value('Company',
|
||||||
|
{'name': company}, ['parent_company',
|
||||||
|
'allow_account_creation_against_child_company'])
|
||||||
|
|
||||||
|
if parent_company and (not allow_account_creation_against_child_company):
|
||||||
|
frappe.throw(_("""{0} is a child company. Please import accounts against parent company
|
||||||
|
or enable {1} in company master""").format(frappe.bold(company),
|
||||||
|
frappe.bold('Allow Account Creation Against Child Company')), title='Wrong Company')
|
||||||
|
|
||||||
if frappe.db.get_all('GL Entry', {"company": company}, "name", limit=1):
|
if frappe.db.get_all('GL Entry', {"company": company}, "name", limit=1):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -25,42 +35,85 @@ def import_coa(file_name, company):
|
|||||||
unset_existing_data(company)
|
unset_existing_data(company)
|
||||||
|
|
||||||
# create accounts
|
# create accounts
|
||||||
forest = build_forest(generate_data_from_csv(file_name))
|
file_doc, extension = get_file(file_name)
|
||||||
|
|
||||||
|
if extension == 'csv':
|
||||||
|
data = generate_data_from_csv(file_doc)
|
||||||
|
else:
|
||||||
|
data = generate_data_from_excel(file_doc, extension)
|
||||||
|
|
||||||
|
forest = build_forest(data)
|
||||||
create_charts(company, custom_chart=forest)
|
create_charts(company, custom_chart=forest)
|
||||||
|
|
||||||
# trigger on_update for company to reset default accounts
|
# trigger on_update for company to reset default accounts
|
||||||
set_default_accounts(company)
|
set_default_accounts(company)
|
||||||
|
|
||||||
def generate_data_from_csv(file_name, as_dict=False):
|
def get_file(file_name):
|
||||||
''' read csv file and return the generated nested tree '''
|
file_doc = frappe.get_doc("File", {"file_url": file_name})
|
||||||
if not file_name.endswith('.csv'):
|
parts = file_doc.get_extension()
|
||||||
frappe.throw("Only CSV files can be used to for importing data. Please check the file format you are trying to upload")
|
extension = parts[1]
|
||||||
|
extension = extension.lstrip(".")
|
||||||
|
|
||||||
|
if extension not in ('csv', 'xlsx', 'xls'):
|
||||||
|
frappe.throw("Only CSV and Excel files can be used to for importing data. Please check the file format you are trying to upload")
|
||||||
|
|
||||||
|
return file_doc, extension
|
||||||
|
|
||||||
|
def generate_data_from_csv(file_doc, as_dict=False):
|
||||||
|
''' read csv file and return the generated nested tree '''
|
||||||
|
|
||||||
file_doc = frappe.get_doc('File', {"file_url": file_name})
|
|
||||||
file_path = file_doc.get_full_path()
|
file_path = file_doc.get_full_path()
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
with open(file_path, 'r') as in_file:
|
with open(file_path, 'r') as in_file:
|
||||||
csv_reader = list(csv.reader(in_file))
|
csv_reader = list(csv.reader(in_file))
|
||||||
headers = csv_reader[1][1:]
|
headers = csv_reader[0]
|
||||||
del csv_reader[0:2] # delete top row and headers row
|
del csv_reader[0] # delete top row and headers row
|
||||||
|
|
||||||
for row in csv_reader:
|
for row in csv_reader:
|
||||||
if as_dict:
|
if as_dict:
|
||||||
data.append({frappe.scrub(header): row[index+1] for index, header in enumerate(headers)})
|
data.append({frappe.scrub(header): row[index] for index, header in enumerate(headers)})
|
||||||
else:
|
else:
|
||||||
if not row[2]: row[2] = row[1]
|
if not row[1]: row[1] = row[0]
|
||||||
data.append(row[1:])
|
data.append(row)
|
||||||
|
|
||||||
# convert csv data
|
# convert csv data
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def generate_data_from_excel(file_doc, extension, as_dict=False):
|
||||||
|
content = file_doc.get_content()
|
||||||
|
|
||||||
|
if extension == "xlsx":
|
||||||
|
rows = read_xlsx_file_from_attached_file(fcontent=content)
|
||||||
|
elif extension == "xls":
|
||||||
|
rows = read_xls_file_from_attached_file(content)
|
||||||
|
|
||||||
|
data = []
|
||||||
|
headers = rows[0]
|
||||||
|
del rows[0]
|
||||||
|
|
||||||
|
for row in rows:
|
||||||
|
if as_dict:
|
||||||
|
data.append({frappe.scrub(header): row[index] for index, header in enumerate(headers)})
|
||||||
|
else:
|
||||||
|
if not row[1]: row[1] = row[0]
|
||||||
|
data.append(row)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_coa(doctype, parent, is_root=False, file_name=None):
|
def get_coa(doctype, parent, is_root=False, file_name=None):
|
||||||
''' called by tree view (to fetch node's children) '''
|
''' called by tree view (to fetch node's children) '''
|
||||||
|
|
||||||
|
file_doc, extension = get_file(file_name)
|
||||||
parent = None if parent==_('All Accounts') else parent
|
parent = None if parent==_('All Accounts') else parent
|
||||||
forest = build_forest(generate_data_from_csv(file_name))
|
|
||||||
|
if extension == 'csv':
|
||||||
|
data = generate_data_from_csv(file_doc)
|
||||||
|
else:
|
||||||
|
data = generate_data_from_excel(file_doc, extension)
|
||||||
|
|
||||||
|
forest = build_forest(data)
|
||||||
accounts = build_tree_from_json("", chart_data=forest) # returns alist of dict in a tree render-able form
|
accounts = build_tree_from_json("", chart_data=forest) # returns alist of dict in a tree render-able form
|
||||||
|
|
||||||
# filter out to show data for the selected node only
|
# filter out to show data for the selected node only
|
||||||
@@ -91,12 +144,19 @@ def build_forest(data):
|
|||||||
|
|
||||||
# returns the path of any node in list format
|
# returns the path of any node in list format
|
||||||
def return_parent(data, child):
|
def return_parent(data, child):
|
||||||
|
from frappe import _
|
||||||
|
|
||||||
for row in data:
|
for row in data:
|
||||||
account_name, parent_account = row[0:2]
|
account_name, parent_account = row[0:2]
|
||||||
if parent_account == account_name == child:
|
if parent_account == account_name == child:
|
||||||
return [parent_account]
|
return [parent_account]
|
||||||
elif account_name == child:
|
elif account_name == child:
|
||||||
return [child] + return_parent(data, parent_account)
|
parent_account_list = return_parent(data, parent_account)
|
||||||
|
if not parent_account_list:
|
||||||
|
frappe.throw(_("The parent account {0} does not exists in the uploaded template").format(
|
||||||
|
frappe.bold(parent_account)))
|
||||||
|
|
||||||
|
return [child] + parent_account_list
|
||||||
|
|
||||||
charts_map, paths = {}, []
|
charts_map, paths = {}, []
|
||||||
|
|
||||||
@@ -104,13 +164,13 @@ def build_forest(data):
|
|||||||
error_messages = []
|
error_messages = []
|
||||||
|
|
||||||
for i in data:
|
for i in data:
|
||||||
account_name, _, account_number, is_group, account_type, root_type = i
|
account_name, dummy, account_number, is_group, account_type, root_type = i
|
||||||
|
|
||||||
if not account_name:
|
if not account_name:
|
||||||
error_messages.append("Row {0}: Please enter Account Name".format(line_no))
|
error_messages.append("Row {0}: Please enter Account Name".format(line_no))
|
||||||
|
|
||||||
charts_map[account_name] = {}
|
charts_map[account_name] = {}
|
||||||
if is_group == 1: charts_map[account_name]["is_group"] = is_group
|
if cint(is_group) == 1: charts_map[account_name]["is_group"] = is_group
|
||||||
if account_type: charts_map[account_name]["account_type"] = account_type
|
if account_type: charts_map[account_name]["account_type"] = account_type
|
||||||
if root_type: charts_map[account_name]["root_type"] = root_type
|
if root_type: charts_map[account_name]["root_type"] = root_type
|
||||||
if account_number: charts_map[account_name]["account_number"] = account_number
|
if account_number: charts_map[account_name]["account_number"] = account_number
|
||||||
@@ -128,24 +188,94 @@ def build_forest(data):
|
|||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
def build_response_as_excel(writer):
|
||||||
|
filename = frappe.generate_hash("", 10)
|
||||||
|
with open(filename, 'wb') as f:
|
||||||
|
f.write(cstr(writer.getvalue()).encode('utf-8'))
|
||||||
|
f = open(filename)
|
||||||
|
reader = csv.reader(f)
|
||||||
|
|
||||||
|
from frappe.utils.xlsxutils import make_xlsx
|
||||||
|
xlsx_file = make_xlsx(reader, "Chart Of Accounts Importer Template")
|
||||||
|
|
||||||
|
f.close()
|
||||||
|
os.remove(filename)
|
||||||
|
|
||||||
|
# write out response as a xlsx type
|
||||||
|
frappe.response['filename'] = 'coa_importer_template.xlsx'
|
||||||
|
frappe.response['filecontent'] = xlsx_file.getvalue()
|
||||||
|
frappe.response['type'] = 'binary'
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def download_template():
|
def download_template(file_type, template_type):
|
||||||
data = frappe._dict(frappe.local.form_dict)
|
data = frappe._dict(frappe.local.form_dict)
|
||||||
|
|
||||||
|
writer = get_template(template_type)
|
||||||
|
|
||||||
|
if file_type == 'CSV':
|
||||||
|
# download csv file
|
||||||
|
frappe.response['result'] = cstr(writer.getvalue())
|
||||||
|
frappe.response['type'] = 'csv'
|
||||||
|
frappe.response['doctype'] = 'Chart of Accounts Importer'
|
||||||
|
else:
|
||||||
|
build_response_as_excel(writer)
|
||||||
|
|
||||||
|
def get_template(template_type):
|
||||||
|
|
||||||
fields = ["Account Name", "Parent Account", "Account Number", "Is Group", "Account Type", "Root Type"]
|
fields = ["Account Name", "Parent Account", "Account Number", "Is Group", "Account Type", "Root Type"]
|
||||||
writer = UnicodeWriter()
|
writer = UnicodeWriter()
|
||||||
|
writer.writerow(fields)
|
||||||
|
|
||||||
writer.writerow([_('Chart of Accounts Template')])
|
if template_type == 'Blank Template':
|
||||||
writer.writerow([_("Column Labels : ")] + fields)
|
for root_type in get_root_types():
|
||||||
writer.writerow([_("Start entering data from here : ")])
|
writer.writerow(['', '', '', 1, '', root_type])
|
||||||
|
|
||||||
|
for account in get_mandatory_group_accounts():
|
||||||
|
writer.writerow(['', '', '', 1, account, "Asset"])
|
||||||
|
|
||||||
|
for account_type in get_mandatory_account_types():
|
||||||
|
writer.writerow(['', '', '', 0, account_type.get('account_type'), account_type.get('root_type')])
|
||||||
|
else:
|
||||||
|
writer = get_sample_template(writer)
|
||||||
|
|
||||||
|
return writer
|
||||||
|
|
||||||
|
def get_sample_template(writer):
|
||||||
|
template = [
|
||||||
|
["Application Of Funds(Assets)", "", "", 1, "", "Asset"],
|
||||||
|
["Sources Of Funds(Liabilities)", "", "", 1, "", "Liability"],
|
||||||
|
["Equity", "", "", 1, "", "Equity"],
|
||||||
|
["Expenses", "", "", 1, "", "Expense"],
|
||||||
|
["Income", "", "", 1, "", "Income"],
|
||||||
|
["Bank Accounts", "Application Of Funds(Assets)", "", 1, "Bank", "Asset"],
|
||||||
|
["Cash In Hand", "Application Of Funds(Assets)", "", 1, "Cash", "Asset"],
|
||||||
|
["Stock Assets", "Application Of Funds(Assets)", "", 1, "Stock", "Asset"],
|
||||||
|
["Cost Of Goods Sold", "Expenses", "", 0, "Cost of Goods Sold", "Expense"],
|
||||||
|
["Asset Depreciation", "Expenses", "", 0, "Depreciation", "Expense"],
|
||||||
|
["Fixed Assets", "Application Of Funds(Assets)", "", 0, "Fixed Asset", "Asset"],
|
||||||
|
["Accounts Payable", "Sources Of Funds(Liabilities)", "", 0, "Payable", "Liability"],
|
||||||
|
["Accounts Receivable", "Application Of Funds(Assets)", "", 1, "Receivable", "Asset"],
|
||||||
|
["Stock Expenses", "Expenses", "", 0, "Stock Adjustment", "Expense"],
|
||||||
|
["Sample Bank", "Bank Accounts", "", 0, "Bank", "Asset"],
|
||||||
|
["Cash", "Cash In Hand", "", 0, "Cash", "Asset"],
|
||||||
|
["Stores", "Stock Assets", "", 0, "Stock", "Asset"],
|
||||||
|
]
|
||||||
|
|
||||||
|
for row in template:
|
||||||
|
writer.writerow(row)
|
||||||
|
|
||||||
|
return writer
|
||||||
|
|
||||||
# download csv file
|
|
||||||
frappe.response['result'] = cstr(writer.getvalue())
|
|
||||||
frappe.response['type'] = 'csv'
|
|
||||||
frappe.response['doctype'] = data.get('doctype')
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def validate_accounts(file_name):
|
def validate_accounts(file_name):
|
||||||
accounts = generate_data_from_csv(file_name, as_dict=True)
|
|
||||||
|
file_doc, extension = get_file(file_name)
|
||||||
|
|
||||||
|
if extension == 'csv':
|
||||||
|
accounts = generate_data_from_csv(file_doc, as_dict=True)
|
||||||
|
else:
|
||||||
|
accounts = generate_data_from_excel(file_doc, extension, as_dict=True)
|
||||||
|
|
||||||
accounts_dict = {}
|
accounts_dict = {}
|
||||||
for account in accounts:
|
for account in accounts:
|
||||||
@@ -170,12 +300,38 @@ def validate_root(accounts):
|
|||||||
for account in roots:
|
for account in roots:
|
||||||
if not account.get("root_type") and account.get("account_name"):
|
if not account.get("root_type") and account.get("account_name"):
|
||||||
error_messages.append("Please enter Root Type for account- {0}".format(account.get("account_name")))
|
error_messages.append("Please enter Root Type for account- {0}".format(account.get("account_name")))
|
||||||
elif account.get("root_type") not in ("Asset", "Liability", "Expense", "Income", "Equity") and account.get("account_name"):
|
elif account.get("root_type") not in get_root_types() and account.get("account_name"):
|
||||||
error_messages.append("Root Type for {0} must be one of the Asset, Liability, Income, Expense and Equity".format(account.get("account_name")))
|
error_messages.append("Root Type for {0} must be one of the Asset, Liability, Income, Expense and Equity".format(account.get("account_name")))
|
||||||
|
|
||||||
if error_messages:
|
if error_messages:
|
||||||
return "<br>".join(error_messages)
|
return "<br>".join(error_messages)
|
||||||
|
|
||||||
|
def get_root_types():
|
||||||
|
return ('Asset', 'Liability', 'Expense', 'Income', 'Equity')
|
||||||
|
|
||||||
|
def get_report_type(root_type):
|
||||||
|
if root_type in ('Asset', 'Liability', 'Equity'):
|
||||||
|
return 'Balance Sheet'
|
||||||
|
else:
|
||||||
|
return 'Profit and Loss'
|
||||||
|
|
||||||
|
def get_mandatory_group_accounts():
|
||||||
|
return ('Bank', 'Cash', 'Stock')
|
||||||
|
|
||||||
|
def get_mandatory_account_types():
|
||||||
|
return [
|
||||||
|
{'account_type': 'Cost of Goods Sold', 'root_type': 'Expense'},
|
||||||
|
{'account_type': 'Depreciation', 'root_type': 'Expense'},
|
||||||
|
{'account_type': 'Fixed Asset', 'root_type': 'Asset'},
|
||||||
|
{'account_type': 'Payable', 'root_type': 'Liability'},
|
||||||
|
{'account_type': 'Receivable', 'root_type': 'Asset'},
|
||||||
|
{'account_type': 'Stock Adjustment', 'root_type': 'Expense'},
|
||||||
|
{'account_type': 'Bank', 'root_type': 'Asset'},
|
||||||
|
{'account_type': 'Cash', 'root_type': 'Asset'},
|
||||||
|
{'account_type': 'Stock', 'root_type': 'Asset'}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def validate_account_types(accounts):
|
def validate_account_types(accounts):
|
||||||
account_types_for_ledger = ["Cost of Goods Sold", "Depreciation", "Fixed Asset", "Payable", "Receivable", "Stock Adjustment"]
|
account_types_for_ledger = ["Cost of Goods Sold", "Depreciation", "Fixed Asset", "Payable", "Receivable", "Stock Adjustment"]
|
||||||
account_types = [accounts[d]["account_type"] for d in accounts if not accounts[d]['is_group'] == 1]
|
account_types = [accounts[d]["account_type"] for d in accounts if not accounts[d]['is_group'] == 1]
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ frappe.ui.form.on('Cost Center', {
|
|||||||
},
|
},
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
if (!frm.is_new()) {
|
if (!frm.is_new()) {
|
||||||
frm.add_custom_button(__('Update Cost Center Number'), function () {
|
frm.add_custom_button(__('Update Cost Center Name / Number'), function () {
|
||||||
frm.trigger("update_cost_center_number");
|
frm.trigger("update_cost_center_number");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -47,35 +47,45 @@ frappe.ui.form.on('Cost Center', {
|
|||||||
},
|
},
|
||||||
update_cost_center_number: function(frm) {
|
update_cost_center_number: function(frm) {
|
||||||
var d = new frappe.ui.Dialog({
|
var d = new frappe.ui.Dialog({
|
||||||
title: __('Update Cost Center Number'),
|
title: __('Update Cost Center Name / Number'),
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
"label": 'Cost Center Number',
|
"label": "Cost Center Name",
|
||||||
|
"fieldname": "cost_center_name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"reqd": 1,
|
||||||
|
"default": frm.doc.cost_center_name
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Cost Center Number",
|
||||||
"fieldname": "cost_center_number",
|
"fieldname": "cost_center_number",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"reqd": 1
|
"reqd": 1,
|
||||||
|
"default": frm.doc.cost_center_number
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
primary_action: function() {
|
primary_action: function() {
|
||||||
var data = d.get_values();
|
var data = d.get_values();
|
||||||
if(data.cost_center_number === frm.doc.cost_center_number) {
|
if(data.cost_center_name === frm.doc.cost_center_name && data.cost_center_number === frm.doc.cost_center_number) {
|
||||||
d.hide();
|
d.hide();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
frappe.dom.freeze();
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.accounts.utils.update_number_field",
|
method: "erpnext.accounts.utils.update_cost_center",
|
||||||
args: {
|
args: {
|
||||||
doctype_name: frm.doc.doctype,
|
docname: frm.doc.name,
|
||||||
name: frm.doc.name,
|
cost_center_name: data.cost_center_name,
|
||||||
field_name: d.fields[0].fieldname,
|
cost_center_number: data.cost_center_number,
|
||||||
number_value: data.cost_center_number,
|
|
||||||
company: frm.doc.company
|
company: frm.doc.company
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
|
frappe.dom.unfreeze();
|
||||||
if(!r.exc) {
|
if(!r.exc) {
|
||||||
if(r.message) {
|
if(r.message) {
|
||||||
frappe.set_route("Form", "Cost Center", r.message);
|
frappe.set_route("Form", "Cost Center", r.message);
|
||||||
} else {
|
} else {
|
||||||
|
me.frm.set_value("cost_center_name", data.cost_center_name);
|
||||||
me.frm.set_value("cost_center_number", data.cost_center_number);
|
me.frm.set_value("cost_center_number", data.cost_center_number);
|
||||||
}
|
}
|
||||||
d.hide();
|
d.hide();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"allow_copy": 1,
|
"allow_copy": 1,
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"allow_rename": 1,
|
|
||||||
"creation": "2013-01-23 19:57:17",
|
"creation": "2013-01-23 19:57:17",
|
||||||
"description": "Track separate Income and Expense for product verticals or divisions.",
|
"description": "Track separate Income and Expense for product verticals or divisions.",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@@ -123,10 +123,13 @@
|
|||||||
],
|
],
|
||||||
"icon": "fa fa-money",
|
"icon": "fa fa-money",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"modified": "2019-09-16 14:44:17.103548",
|
"is_tree": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2020-04-29 16:09:30.025214",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Cost Center",
|
"name": "Cost Center",
|
||||||
|
"nsm_parent_field": "parent_cost_center",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
@@ -162,7 +165,6 @@
|
|||||||
"role": "Purchase User"
|
"role": "Purchase User"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 1,
|
|
||||||
"search_fields": "parent_cost_center, is_group",
|
"search_fields": "parent_cost_center, is_group",
|
||||||
"show_name_in_global_search": 1,
|
"show_name_in_global_search": 1,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
frappe.treeview_settings["Cost Center"] = {
|
frappe.treeview_settings["Cost Center"] = {
|
||||||
breadcrumbs: "Accounts",
|
breadcrumb: "Accounts",
|
||||||
get_tree_root: false,
|
get_tree_root: false,
|
||||||
filters: [{
|
filters: [{
|
||||||
fieldname: "company",
|
fieldname: "company",
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Invoice",
|
"label": "Invoice",
|
||||||
"options": "Sales Invoice",
|
"options": "Sales Invoice",
|
||||||
"reqd": 1
|
"reqd": 1,
|
||||||
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fetch_from": "sales_invoice.customer",
|
"fetch_from": "sales_invoice.customer",
|
||||||
@@ -60,7 +61,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2019-09-26 11:05:36.016772",
|
"modified": "2020-02-20 16:16:20.724620",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Discounted Invoice",
|
"name": "Discounted Invoice",
|
||||||
|
|||||||
@@ -7,4 +7,4 @@ from __future__ import unicode_literals
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class DiscountedInvoice(Document):
|
class DiscountedInvoice(Document):
|
||||||
pass
|
pass
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -29,7 +29,6 @@ class GLEntry(Document):
|
|||||||
self.validate_and_set_fiscal_year()
|
self.validate_and_set_fiscal_year()
|
||||||
self.pl_must_have_cost_center()
|
self.pl_must_have_cost_center()
|
||||||
self.validate_cost_center()
|
self.validate_cost_center()
|
||||||
self.validate_dimensions_for_pl_and_bs()
|
|
||||||
|
|
||||||
if not self.flags.from_repost:
|
if not self.flags.from_repost:
|
||||||
self.check_pl_account()
|
self.check_pl_account()
|
||||||
@@ -39,6 +38,7 @@ class GLEntry(Document):
|
|||||||
def on_update_with_args(self, adv_adj, update_outstanding = 'Yes', from_repost=False):
|
def on_update_with_args(self, adv_adj, update_outstanding = 'Yes', from_repost=False):
|
||||||
if not from_repost:
|
if not from_repost:
|
||||||
self.validate_account_details(adv_adj)
|
self.validate_account_details(adv_adj)
|
||||||
|
self.validate_dimensions_for_pl_and_bs()
|
||||||
check_freezing_date(self.posting_date, adv_adj)
|
check_freezing_date(self.posting_date, adv_adj)
|
||||||
|
|
||||||
validate_frozen_account(self.account, adv_adj)
|
validate_frozen_account(self.account, adv_adj)
|
||||||
@@ -115,8 +115,8 @@ class GLEntry(Document):
|
|||||||
from tabAccount where name=%s""", self.account, as_dict=1)[0]
|
from tabAccount where name=%s""", self.account, as_dict=1)[0]
|
||||||
|
|
||||||
if ret.is_group==1:
|
if ret.is_group==1:
|
||||||
frappe.throw(_("{0} {1}: Account {2} cannot be a Group")
|
frappe.throw(_('''{0} {1}: Account {2} is a Group Account and group accounts cannot be used in
|
||||||
.format(self.voucher_type, self.voucher_no, self.account))
|
transactions''').format(self.voucher_type, self.voucher_no, self.account))
|
||||||
|
|
||||||
if ret.docstatus==2:
|
if ret.docstatus==2:
|
||||||
frappe.throw(_("{0} {1}: Account {2} is inactive")
|
frappe.throw(_("{0} {1}: Account {2} is inactive")
|
||||||
@@ -232,10 +232,13 @@ def update_outstanding_amt(account, party_type, party, against_voucher_type, aga
|
|||||||
if bal < 0 and not on_cancel:
|
if bal < 0 and not on_cancel:
|
||||||
frappe.throw(_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal)))
|
frappe.throw(_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal)))
|
||||||
|
|
||||||
# Update outstanding amt on against voucher
|
|
||||||
if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]:
|
if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]:
|
||||||
ref_doc = frappe.get_doc(against_voucher_type, against_voucher)
|
ref_doc = frappe.get_doc(against_voucher_type, against_voucher)
|
||||||
ref_doc.db_set('outstanding_amount', bal)
|
|
||||||
|
# Didn't use db_set for optimisation purpose
|
||||||
|
ref_doc.outstanding_amount = bal
|
||||||
|
frappe.db.set_value(against_voucher_type, against_voucher, 'outstanding_amount', bal)
|
||||||
|
|
||||||
ref_doc.set_status(update=True)
|
ref_doc.set_status(update=True)
|
||||||
|
|
||||||
def validate_frozen_account(account, adv_adj=None):
|
def validate_frozen_account(account, adv_adj=None):
|
||||||
@@ -274,6 +277,9 @@ def update_against_account(voucher_type, voucher_no):
|
|||||||
if d.against != new_against:
|
if d.against != new_against:
|
||||||
frappe.db.set_value("GL Entry", d.name, "against", new_against)
|
frappe.db.set_value("GL Entry", d.name, "against", new_against)
|
||||||
|
|
||||||
|
def on_doctype_update():
|
||||||
|
frappe.db.add_index("GL Entry", ["against_voucher_type", "against_voucher"])
|
||||||
|
frappe.db.add_index("GL Entry", ["voucher_type", "voucher_no"])
|
||||||
|
|
||||||
def rename_gle_sle_docs():
|
def rename_gle_sle_docs():
|
||||||
for doctype in ["GL Entry", "Stock Ledger Entry"]:
|
for doctype in ["GL Entry", "Stock Ledger Entry"]:
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from frappe import _
|
|||||||
from frappe.utils import flt, getdate, nowdate, add_days
|
from frappe.utils import flt, getdate, nowdate, add_days
|
||||||
from erpnext.controllers.accounts_controller import AccountsController
|
from erpnext.controllers.accounts_controller import AccountsController
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries
|
from erpnext.accounts.general_ledger import make_gl_entries
|
||||||
|
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
|
||||||
|
|
||||||
class InvoiceDiscounting(AccountsController):
|
class InvoiceDiscounting(AccountsController):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
@@ -81,10 +82,15 @@ class InvoiceDiscounting(AccountsController):
|
|||||||
def make_gl_entries(self):
|
def make_gl_entries(self):
|
||||||
company_currency = frappe.get_cached_value('Company', self.company, "default_currency")
|
company_currency = frappe.get_cached_value('Company', self.company, "default_currency")
|
||||||
|
|
||||||
|
|
||||||
gl_entries = []
|
gl_entries = []
|
||||||
|
invoice_fields = ["debit_to", "party_account_currency", "conversion_rate", "cost_center"]
|
||||||
|
accounting_dimensions = get_accounting_dimensions()
|
||||||
|
|
||||||
|
invoice_fields.extend(accounting_dimensions)
|
||||||
|
|
||||||
for d in self.invoices:
|
for d in self.invoices:
|
||||||
inv = frappe.db.get_value("Sales Invoice", d.sales_invoice,
|
inv = frappe.db.get_value("Sales Invoice", d.sales_invoice, invoice_fields, as_dict=1)
|
||||||
["debit_to", "party_account_currency", "conversion_rate", "cost_center"], as_dict=1)
|
|
||||||
|
|
||||||
if d.outstanding_amount:
|
if d.outstanding_amount:
|
||||||
outstanding_in_company_currency = flt(d.outstanding_amount * inv.conversion_rate,
|
outstanding_in_company_currency = flt(d.outstanding_amount * inv.conversion_rate,
|
||||||
@@ -102,7 +108,7 @@ class InvoiceDiscounting(AccountsController):
|
|||||||
"cost_center": inv.cost_center,
|
"cost_center": inv.cost_center,
|
||||||
"against_voucher": d.sales_invoice,
|
"against_voucher": d.sales_invoice,
|
||||||
"against_voucher_type": "Sales Invoice"
|
"against_voucher_type": "Sales Invoice"
|
||||||
}, inv.party_account_currency))
|
}, inv.party_account_currency, item=inv))
|
||||||
|
|
||||||
gl_entries.append(self.get_gl_dict({
|
gl_entries.append(self.get_gl_dict({
|
||||||
"account": self.accounts_receivable_credit,
|
"account": self.accounts_receivable_credit,
|
||||||
@@ -115,7 +121,7 @@ class InvoiceDiscounting(AccountsController):
|
|||||||
"cost_center": inv.cost_center,
|
"cost_center": inv.cost_center,
|
||||||
"against_voucher": d.sales_invoice,
|
"against_voucher": d.sales_invoice,
|
||||||
"against_voucher_type": "Sales Invoice"
|
"against_voucher_type": "Sales Invoice"
|
||||||
}, ar_credit_account_currency))
|
}, ar_credit_account_currency, item=inv))
|
||||||
|
|
||||||
make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding='No')
|
make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding='No')
|
||||||
|
|
||||||
|
|||||||
@@ -190,7 +190,6 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
|||||||
if(jvd.reference_type==="Employee Advance") {
|
if(jvd.reference_type==="Employee Advance") {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
'status': ['=', 'Unpaid'],
|
|
||||||
'docstatus': 1
|
'docstatus': 1
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,6 @@ from erpnext.controllers.accounts_controller import AccountsController
|
|||||||
from erpnext.accounts.utils import get_balance_on, get_account_currency
|
from erpnext.accounts.utils import get_balance_on, get_account_currency
|
||||||
from erpnext.accounts.party import get_party_account
|
from erpnext.accounts.party import get_party_account
|
||||||
from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
|
from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
|
||||||
from erpnext.hr.doctype.loan.loan import update_disbursement_status, update_total_amount_paid
|
|
||||||
from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import get_party_account_based_on_invoice_discounting
|
from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import get_party_account_based_on_invoice_discounting
|
||||||
|
|
||||||
from six import string_types, iteritems
|
from six import string_types, iteritems
|
||||||
@@ -457,11 +456,12 @@ class JournalEntry(AccountsController):
|
|||||||
def set_print_format_fields(self):
|
def set_print_format_fields(self):
|
||||||
bank_amount = party_amount = total_amount = 0.0
|
bank_amount = party_amount = total_amount = 0.0
|
||||||
currency = bank_account_currency = party_account_currency = pay_to_recd_from= None
|
currency = bank_account_currency = party_account_currency = pay_to_recd_from= None
|
||||||
|
party_type = None
|
||||||
for d in self.get('accounts'):
|
for d in self.get('accounts'):
|
||||||
if d.party_type in ['Customer', 'Supplier'] and d.party:
|
if d.party_type in ['Customer', 'Supplier'] and d.party:
|
||||||
|
party_type = d.party_type
|
||||||
if not pay_to_recd_from:
|
if not pay_to_recd_from:
|
||||||
pay_to_recd_from = frappe.db.get_value(d.party_type, d.party,
|
pay_to_recd_from = d.party
|
||||||
"customer_name" if d.party_type=="Customer" else "supplier_name")
|
|
||||||
|
|
||||||
if pay_to_recd_from and pay_to_recd_from == d.party:
|
if pay_to_recd_from and pay_to_recd_from == d.party:
|
||||||
party_amount += (d.debit_in_account_currency or d.credit_in_account_currency)
|
party_amount += (d.debit_in_account_currency or d.credit_in_account_currency)
|
||||||
@@ -471,8 +471,9 @@ class JournalEntry(AccountsController):
|
|||||||
bank_amount += (d.debit_in_account_currency or d.credit_in_account_currency)
|
bank_amount += (d.debit_in_account_currency or d.credit_in_account_currency)
|
||||||
bank_account_currency = d.account_currency
|
bank_account_currency = d.account_currency
|
||||||
|
|
||||||
if pay_to_recd_from:
|
if party_type and pay_to_recd_from:
|
||||||
self.pay_to_recd_from = pay_to_recd_from
|
self.pay_to_recd_from = frappe.db.get_value(party_type, pay_to_recd_from,
|
||||||
|
"customer_name" if party_type=="Customer" else "supplier_name")
|
||||||
if bank_amount:
|
if bank_amount:
|
||||||
total_amount = bank_amount
|
total_amount = bank_amount
|
||||||
currency = bank_account_currency
|
currency = bank_account_currency
|
||||||
@@ -560,20 +561,20 @@ class JournalEntry(AccountsController):
|
|||||||
|
|
||||||
if self.write_off_based_on == 'Accounts Receivable':
|
if self.write_off_based_on == 'Accounts Receivable':
|
||||||
jd1.party_type = "Customer"
|
jd1.party_type = "Customer"
|
||||||
jd1.credit = flt(d.outstanding_amount, self.precision("credit", "accounts"))
|
jd1.credit_in_account_currency = flt(d.outstanding_amount, self.precision("credit", "accounts"))
|
||||||
jd1.reference_type = "Sales Invoice"
|
jd1.reference_type = "Sales Invoice"
|
||||||
jd1.reference_name = cstr(d.name)
|
jd1.reference_name = cstr(d.name)
|
||||||
elif self.write_off_based_on == 'Accounts Payable':
|
elif self.write_off_based_on == 'Accounts Payable':
|
||||||
jd1.party_type = "Supplier"
|
jd1.party_type = "Supplier"
|
||||||
jd1.debit = flt(d.outstanding_amount, self.precision("debit", "accounts"))
|
jd1.debit_in_account_currency = flt(d.outstanding_amount, self.precision("debit", "accounts"))
|
||||||
jd1.reference_type = "Purchase Invoice"
|
jd1.reference_type = "Purchase Invoice"
|
||||||
jd1.reference_name = cstr(d.name)
|
jd1.reference_name = cstr(d.name)
|
||||||
|
|
||||||
jd2 = self.append('accounts', {})
|
jd2 = self.append('accounts', {})
|
||||||
if self.write_off_based_on == 'Accounts Receivable':
|
if self.write_off_based_on == 'Accounts Receivable':
|
||||||
jd2.debit = total
|
jd2.debit_in_account_currency = total
|
||||||
elif self.write_off_based_on == 'Accounts Payable':
|
elif self.write_off_based_on == 'Accounts Payable':
|
||||||
jd2.credit = total
|
jd2.credit_in_account_currency = total
|
||||||
|
|
||||||
self.validate_total_debit_and_credit()
|
self.validate_total_debit_and_credit()
|
||||||
|
|
||||||
@@ -606,8 +607,8 @@ class JournalEntry(AccountsController):
|
|||||||
for d in self.accounts:
|
for d in self.accounts:
|
||||||
if d.reference_type=="Loan" and flt(d.debit) > 0:
|
if d.reference_type=="Loan" and flt(d.debit) > 0:
|
||||||
doc = frappe.get_doc("Loan", d.reference_name)
|
doc = frappe.get_doc("Loan", d.reference_name)
|
||||||
update_disbursement_status(doc)
|
doc.update_total_amount_paid()
|
||||||
update_total_amount_paid(doc)
|
doc.set_status()
|
||||||
|
|
||||||
def validate_expense_claim(self):
|
def validate_expense_claim(self):
|
||||||
for d in self.accounts:
|
for d in self.accounts:
|
||||||
@@ -968,7 +969,7 @@ def get_exchange_rate(posting_date, account=None, account_currency=None, company
|
|||||||
|
|
||||||
# The date used to retreive the exchange rate here is the date passed
|
# The date used to retreive the exchange rate here is the date passed
|
||||||
# in as an argument to this function.
|
# in as an argument to this function.
|
||||||
elif (not exchange_rate or exchange_rate==1) and account_currency and posting_date:
|
elif (not exchange_rate or flt(exchange_rate)==1) and account_currency and posting_date:
|
||||||
exchange_rate = get_exchange_rate(account_currency, company_currency, posting_date)
|
exchange_rate = get_exchange_rate(account_currency, company_currency, posting_date)
|
||||||
else:
|
else:
|
||||||
exchange_rate = 1
|
exchange_rate = 1
|
||||||
|
|||||||
@@ -90,7 +90,6 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "Customer",
|
|
||||||
"fieldname": "party_type",
|
"fieldname": "party_type",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
@@ -272,7 +271,7 @@
|
|||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2019-10-02 12:23:21.693443",
|
"modified": "2020-01-13 12:41:33.968025",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Journal Entry Account",
|
"name": "Journal Entry Account",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ class ModeofPayment(Document):
|
|||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_accounts()
|
self.validate_accounts()
|
||||||
self.validate_repeating_companies()
|
self.validate_repeating_companies()
|
||||||
|
self.validate_pos_mode_of_payment()
|
||||||
|
|
||||||
def validate_repeating_companies(self):
|
def validate_repeating_companies(self):
|
||||||
"""Error when Same Company is entered multiple times in accounts"""
|
"""Error when Same Company is entered multiple times in accounts"""
|
||||||
@@ -27,3 +28,15 @@ class ModeofPayment(Document):
|
|||||||
if frappe.db.get_value("Account", entry.default_account, "company") != entry.company:
|
if frappe.db.get_value("Account", entry.default_account, "company") != entry.company:
|
||||||
frappe.throw(_("Account {0} does not match with Company {1} in Mode of Account: {2}")
|
frappe.throw(_("Account {0} does not match with Company {1} in Mode of Account: {2}")
|
||||||
.format(entry.default_account, entry.company, self.name))
|
.format(entry.default_account, entry.company, self.name))
|
||||||
|
|
||||||
|
def validate_pos_mode_of_payment(self):
|
||||||
|
if not self.enabled:
|
||||||
|
pos_profiles = frappe.db.sql("""SELECT sip.parent FROM `tabSales Invoice Payment` sip
|
||||||
|
WHERE sip.parenttype = 'POS Profile' and sip.mode_of_payment = %s""", (self.name))
|
||||||
|
pos_profiles = list(map(lambda x: x[0], pos_profiles))
|
||||||
|
|
||||||
|
if pos_profiles:
|
||||||
|
message = "POS Profile " + frappe.bold(", ".join(pos_profiles)) + " contains \
|
||||||
|
Mode of Payment " + frappe.bold(str(self.name)) + ". Please remove them to disable this mode."
|
||||||
|
frappe.throw(_(message), title="Not Allowed")
|
||||||
|
|
||||||
|
|||||||
@@ -104,6 +104,21 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frm.set_query('payment_term', 'references', function(frm, cdt, cdn) {
|
||||||
|
const child = locals[cdt][cdn];
|
||||||
|
if (in_list(['Purchase Invoice', 'Sales Invoice'], child.reference_doctype) && child.reference_name) {
|
||||||
|
let payment_term_list = frappe.get_list('Payment Schedule', {'parent': child.reference_name});
|
||||||
|
|
||||||
|
payment_term_list = payment_term_list.map(pt => pt.payment_term);
|
||||||
|
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
'name': ['in', payment_term_list]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
frm.set_query("reference_name", "references", function(doc, cdt, cdn) {
|
frm.set_query("reference_name", "references", function(doc, cdt, cdn) {
|
||||||
const child = locals[cdt][cdn];
|
const child = locals[cdt][cdn];
|
||||||
const filters = {"docstatus": 1, "company": doc.company};
|
const filters = {"docstatus": 1, "company": doc.company};
|
||||||
@@ -154,8 +169,11 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
|
|
||||||
frm.toggle_display("base_paid_amount", frm.doc.paid_from_account_currency != company_currency);
|
frm.toggle_display("base_paid_amount", frm.doc.paid_from_account_currency != company_currency);
|
||||||
|
|
||||||
frm.toggle_display("base_received_amount", (frm.doc.paid_to_account_currency != company_currency &&
|
frm.toggle_display("base_received_amount", (
|
||||||
frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency));
|
frm.doc.paid_to_account_currency != company_currency &&
|
||||||
|
frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency
|
||||||
|
&& frm.doc.base_paid_amount != frm.doc.base_received_amount
|
||||||
|
));
|
||||||
|
|
||||||
frm.toggle_display("received_amount", (frm.doc.payment_type=="Internal Transfer" ||
|
frm.toggle_display("received_amount", (frm.doc.payment_type=="Internal Transfer" ||
|
||||||
frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency))
|
frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency))
|
||||||
@@ -269,7 +287,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
frm.set_value("contact_email", "");
|
frm.set_value("contact_email", "");
|
||||||
frm.set_value("contact_person", "");
|
frm.set_value("contact_person", "");
|
||||||
}
|
}
|
||||||
if(frm.doc.payment_type && frm.doc.party_type && frm.doc.party) {
|
if(frm.doc.payment_type && frm.doc.party_type && frm.doc.party && frm.doc.company) {
|
||||||
if(!frm.doc.posting_date) {
|
if(!frm.doc.posting_date) {
|
||||||
frappe.msgprint(__("Please select Posting Date before selecting Party"))
|
frappe.msgprint(__("Please select Posting Date before selecting Party"))
|
||||||
frm.set_value("party", "");
|
frm.set_value("party", "");
|
||||||
@@ -486,6 +504,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
paid_amount: function(frm) {
|
paid_amount: function(frm) {
|
||||||
frm.set_value("base_paid_amount", flt(frm.doc.paid_amount) * flt(frm.doc.source_exchange_rate));
|
frm.set_value("base_paid_amount", flt(frm.doc.paid_amount) * flt(frm.doc.source_exchange_rate));
|
||||||
frm.trigger("reset_received_amount");
|
frm.trigger("reset_received_amount");
|
||||||
|
frm.events.hide_unhide_fields(frm);
|
||||||
},
|
},
|
||||||
|
|
||||||
received_amount: function(frm) {
|
received_amount: function(frm) {
|
||||||
@@ -509,6 +528,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
frm.events.set_unallocated_amount(frm);
|
frm.events.set_unallocated_amount(frm);
|
||||||
|
|
||||||
frm.set_paid_amount_based_on_received_amount = false;
|
frm.set_paid_amount_based_on_received_amount = false;
|
||||||
|
frm.events.hide_unhide_fields(frm);
|
||||||
},
|
},
|
||||||
|
|
||||||
reset_received_amount: function(frm) {
|
reset_received_amount: function(frm) {
|
||||||
@@ -652,14 +672,16 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Student")
|
(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Student")
|
||||||
) {
|
) {
|
||||||
if(total_positive_outstanding > total_negative_outstanding)
|
if(total_positive_outstanding > total_negative_outstanding)
|
||||||
frm.set_value("paid_amount",
|
if (!frm.doc.paid_amount)
|
||||||
total_positive_outstanding - total_negative_outstanding);
|
frm.set_value("paid_amount",
|
||||||
|
total_positive_outstanding - total_negative_outstanding);
|
||||||
} else if (
|
} else if (
|
||||||
total_negative_outstanding &&
|
total_negative_outstanding &&
|
||||||
total_positive_outstanding < total_negative_outstanding
|
total_positive_outstanding < total_negative_outstanding
|
||||||
) {
|
) {
|
||||||
frm.set_value("received_amount",
|
if (!frm.doc.received_amount)
|
||||||
total_negative_outstanding - total_positive_outstanding);
|
frm.set_value("received_amount",
|
||||||
|
total_negative_outstanding - total_positive_outstanding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1011,4 +1033,4 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -60,6 +60,7 @@ class PaymentEntry(AccountsController):
|
|||||||
self.set_remarks()
|
self.set_remarks()
|
||||||
self.validate_duplicate_entry()
|
self.validate_duplicate_entry()
|
||||||
self.validate_allocated_amount()
|
self.validate_allocated_amount()
|
||||||
|
self.validate_paid_invoices()
|
||||||
self.ensure_supplier_is_not_blocked()
|
self.ensure_supplier_is_not_blocked()
|
||||||
self.set_status()
|
self.set_status()
|
||||||
|
|
||||||
@@ -71,9 +72,9 @@ class PaymentEntry(AccountsController):
|
|||||||
self.update_outstanding_amounts()
|
self.update_outstanding_amounts()
|
||||||
self.update_advance_paid()
|
self.update_advance_paid()
|
||||||
self.update_expense_claim()
|
self.update_expense_claim()
|
||||||
|
self.update_payment_schedule()
|
||||||
self.set_status()
|
self.set_status()
|
||||||
|
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.setup_party_account_field()
|
self.setup_party_account_field()
|
||||||
self.make_gl_entries(cancel=1)
|
self.make_gl_entries(cancel=1)
|
||||||
@@ -81,18 +82,24 @@ class PaymentEntry(AccountsController):
|
|||||||
self.update_advance_paid()
|
self.update_advance_paid()
|
||||||
self.update_expense_claim()
|
self.update_expense_claim()
|
||||||
self.delink_advance_entry_references()
|
self.delink_advance_entry_references()
|
||||||
|
self.update_payment_schedule(cancel=1)
|
||||||
|
self.set_payment_req_status()
|
||||||
self.set_status()
|
self.set_status()
|
||||||
|
|
||||||
|
def set_payment_req_status(self):
|
||||||
|
from erpnext.accounts.doctype.payment_request.payment_request import update_payment_req_status
|
||||||
|
update_payment_req_status(self, None)
|
||||||
|
|
||||||
def update_outstanding_amounts(self):
|
def update_outstanding_amounts(self):
|
||||||
self.set_missing_ref_details(force=True)
|
self.set_missing_ref_details(force=True)
|
||||||
|
|
||||||
def validate_duplicate_entry(self):
|
def validate_duplicate_entry(self):
|
||||||
reference_names = []
|
reference_names = []
|
||||||
for d in self.get("references"):
|
for d in self.get("references"):
|
||||||
if (d.reference_doctype, d.reference_name) in reference_names:
|
if (d.reference_doctype, d.reference_name, d.payment_term) in reference_names:
|
||||||
frappe.throw(_("Row #{0}: Duplicate entry in References {1} {2}")
|
frappe.throw(_("Row #{0}: Duplicate entry in References {1} {2}")
|
||||||
.format(d.idx, d.reference_doctype, d.reference_name))
|
.format(d.idx, d.reference_doctype, d.reference_name))
|
||||||
reference_names.append((d.reference_doctype, d.reference_name))
|
reference_names.append((d.reference_doctype, d.reference_name, d.payment_term))
|
||||||
|
|
||||||
def set_bank_account_data(self):
|
def set_bank_account_data(self):
|
||||||
if self.bank_account:
|
if self.bank_account:
|
||||||
@@ -102,7 +109,9 @@ class PaymentEntry(AccountsController):
|
|||||||
|
|
||||||
self.bank = bank_data.bank
|
self.bank = bank_data.bank
|
||||||
self.bank_account_no = bank_data.bank_account_no
|
self.bank_account_no = bank_data.bank_account_no
|
||||||
self.set(field, bank_data.account)
|
|
||||||
|
if not self.get(field):
|
||||||
|
self.set(field, bank_data.account)
|
||||||
|
|
||||||
def validate_allocated_amount(self):
|
def validate_allocated_amount(self):
|
||||||
for d in self.get("references"):
|
for d in self.get("references"):
|
||||||
@@ -257,6 +266,25 @@ class PaymentEntry(AccountsController):
|
|||||||
frappe.throw(_("{0} {1} must be submitted")
|
frappe.throw(_("{0} {1} must be submitted")
|
||||||
.format(d.reference_doctype, d.reference_name))
|
.format(d.reference_doctype, d.reference_name))
|
||||||
|
|
||||||
|
def validate_paid_invoices(self):
|
||||||
|
no_oustanding_refs = {}
|
||||||
|
|
||||||
|
for d in self.get("references"):
|
||||||
|
if not d.allocated_amount:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Fees"):
|
||||||
|
outstanding_amount, is_return = frappe.get_cached_value(d.reference_doctype, d.reference_name, ["outstanding_amount", "is_return"])
|
||||||
|
if outstanding_amount <= 0 and not is_return:
|
||||||
|
no_oustanding_refs.setdefault(d.reference_doctype, []).append(d)
|
||||||
|
|
||||||
|
for k, v in no_oustanding_refs.items():
|
||||||
|
frappe.msgprint(_("{} - {} now have {} as they had no outstanding amount left before submitting the Payment Entry.<br><br>\
|
||||||
|
If this is undesirable please cancel the corresponding Payment Entry.")
|
||||||
|
.format(k, frappe.bold(", ".join([d.reference_name for d in v])), frappe.bold("negative outstanding amount")),
|
||||||
|
title=_("Warning"), indicator="orange")
|
||||||
|
|
||||||
|
|
||||||
def validate_journal_entry(self):
|
def validate_journal_entry(self):
|
||||||
for d in self.get("references"):
|
for d in self.get("references"):
|
||||||
if d.allocated_amount and d.reference_doctype == "Journal Entry":
|
if d.allocated_amount and d.reference_doctype == "Journal Entry":
|
||||||
@@ -278,6 +306,36 @@ class PaymentEntry(AccountsController):
|
|||||||
frappe.throw(_("Against Journal Entry {0} does not have any unmatched {1} entry")
|
frappe.throw(_("Against Journal Entry {0} does not have any unmatched {1} entry")
|
||||||
.format(d.reference_name, dr_or_cr))
|
.format(d.reference_name, dr_or_cr))
|
||||||
|
|
||||||
|
def update_payment_schedule(self, cancel=0):
|
||||||
|
invoice_payment_amount_map = {}
|
||||||
|
invoice_paid_amount_map = {}
|
||||||
|
|
||||||
|
for reference in self.get('references'):
|
||||||
|
if reference.payment_term and reference.reference_name:
|
||||||
|
key = (reference.payment_term, reference.reference_name)
|
||||||
|
invoice_payment_amount_map.setdefault(key, 0.0)
|
||||||
|
invoice_payment_amount_map[key] += reference.allocated_amount
|
||||||
|
|
||||||
|
if not invoice_paid_amount_map.get(reference.reference_name):
|
||||||
|
payment_schedule = frappe.get_all('Payment Schedule', filters={'parent': reference.reference_name},
|
||||||
|
fields=['paid_amount', 'payment_amount', 'payment_term'])
|
||||||
|
for term in payment_schedule:
|
||||||
|
invoice_key = (term.payment_term, reference.reference_name)
|
||||||
|
invoice_paid_amount_map.setdefault(invoice_key, {})
|
||||||
|
invoice_paid_amount_map[invoice_key]['outstanding'] = term.payment_amount - term.paid_amount
|
||||||
|
|
||||||
|
for key, amount in iteritems(invoice_payment_amount_map):
|
||||||
|
if cancel:
|
||||||
|
frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` - %s
|
||||||
|
WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0]))
|
||||||
|
else:
|
||||||
|
outstanding = invoice_paid_amount_map.get(key)['outstanding']
|
||||||
|
if amount > outstanding:
|
||||||
|
frappe.throw(_('Cannot allocate more than {0} against payment term {1}').format(outstanding, key[0]))
|
||||||
|
|
||||||
|
frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` + %s
|
||||||
|
WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0]))
|
||||||
|
|
||||||
def set_status(self):
|
def set_status(self):
|
||||||
if self.docstatus == 2:
|
if self.docstatus == 2:
|
||||||
self.status = 'Cancelled'
|
self.status = 'Cancelled'
|
||||||
@@ -390,8 +448,6 @@ class PaymentEntry(AccountsController):
|
|||||||
frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction"))
|
frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction"))
|
||||||
|
|
||||||
def set_remarks(self):
|
def set_remarks(self):
|
||||||
if self.remarks: return
|
|
||||||
|
|
||||||
if self.payment_type=="Internal Transfer":
|
if self.payment_type=="Internal Transfer":
|
||||||
remarks = [_("Amount {0} {1} transferred from {2} to {3}")
|
remarks = [_("Amount {0} {1} transferred from {2} to {3}")
|
||||||
.format(self.paid_from_account_currency, self.paid_amount, self.paid_from, self.paid_to)]
|
.format(self.paid_from_account_currency, self.paid_amount, self.paid_from, self.paid_to)]
|
||||||
@@ -445,7 +501,7 @@ class PaymentEntry(AccountsController):
|
|||||||
"against": against_account,
|
"against": against_account,
|
||||||
"account_currency": self.party_account_currency,
|
"account_currency": self.party_account_currency,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
})
|
}, item=self)
|
||||||
|
|
||||||
dr_or_cr = "credit" if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit"
|
dr_or_cr = "credit" if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit"
|
||||||
|
|
||||||
@@ -489,7 +545,7 @@ class PaymentEntry(AccountsController):
|
|||||||
"credit_in_account_currency": self.paid_amount,
|
"credit_in_account_currency": self.paid_amount,
|
||||||
"credit": self.base_paid_amount,
|
"credit": self.base_paid_amount,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
})
|
}, item=self)
|
||||||
)
|
)
|
||||||
if self.payment_type in ("Receive", "Internal Transfer"):
|
if self.payment_type in ("Receive", "Internal Transfer"):
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
@@ -500,7 +556,7 @@ class PaymentEntry(AccountsController):
|
|||||||
"debit_in_account_currency": self.received_amount,
|
"debit_in_account_currency": self.received_amount,
|
||||||
"debit": self.base_received_amount,
|
"debit": self.base_received_amount,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
})
|
}, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
def add_deductions_gl_entries(self, gl_entries):
|
def add_deductions_gl_entries(self, gl_entries):
|
||||||
@@ -911,7 +967,10 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
|||||||
else:
|
else:
|
||||||
party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company)
|
party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company)
|
||||||
|
|
||||||
party_account_currency = doc.get("party_account_currency") or get_account_currency(party_account)
|
if dt not in ("Sales Invoice", "Purchase Invoice"):
|
||||||
|
party_account_currency = get_account_currency(party_account)
|
||||||
|
else:
|
||||||
|
party_account_currency = doc.get("party_account_currency") or get_account_currency(party_account)
|
||||||
|
|
||||||
# payment type
|
# payment type
|
||||||
if (dt == "Sales Order" or (dt in ("Sales Invoice", "Fees") and doc.outstanding_amount > 0)) \
|
if (dt == "Sales Order" or (dt in ("Sales Invoice", "Fees") and doc.outstanding_amount > 0)) \
|
||||||
@@ -1002,15 +1061,22 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
|||||||
if doc.doctype == "Purchase Invoice" and doc.invoice_is_blocked():
|
if doc.doctype == "Purchase Invoice" and doc.invoice_is_blocked():
|
||||||
frappe.msgprint(_('{0} is on hold till {1}'.format(doc.name, doc.release_date)))
|
frappe.msgprint(_('{0} is on hold till {1}'.format(doc.name, doc.release_date)))
|
||||||
else:
|
else:
|
||||||
pe.append("references", {
|
if (doc.doctype in ('Sales Invoice', 'Purchase Invoice')
|
||||||
'reference_doctype': dt,
|
and frappe.get_value('Payment Terms Template',
|
||||||
'reference_name': dn,
|
{'name': doc.payment_terms_template}, 'allocate_payment_based_on_payment_terms')):
|
||||||
"bill_no": doc.get("bill_no"),
|
|
||||||
"due_date": doc.get("due_date"),
|
for reference in get_reference_as_per_payment_terms(doc.payment_schedule, dt, dn, doc, grand_total, outstanding_amount):
|
||||||
'total_amount': grand_total,
|
pe.append('references', reference)
|
||||||
'outstanding_amount': outstanding_amount,
|
else:
|
||||||
'allocated_amount': outstanding_amount
|
pe.append("references", {
|
||||||
})
|
'reference_doctype': dt,
|
||||||
|
'reference_name': dn,
|
||||||
|
"bill_no": doc.get("bill_no"),
|
||||||
|
"due_date": doc.get("due_date"),
|
||||||
|
'total_amount': grand_total,
|
||||||
|
'outstanding_amount': outstanding_amount,
|
||||||
|
'allocated_amount': outstanding_amount
|
||||||
|
})
|
||||||
|
|
||||||
pe.setup_party_account_field()
|
pe.setup_party_account_field()
|
||||||
pe.set_missing_values()
|
pe.set_missing_values()
|
||||||
@@ -1019,6 +1085,22 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
|||||||
pe.set_amounts()
|
pe.set_amounts()
|
||||||
return pe
|
return pe
|
||||||
|
|
||||||
|
def get_reference_as_per_payment_terms(payment_schedule, dt, dn, doc, grand_total, outstanding_amount):
|
||||||
|
references = []
|
||||||
|
for payment_term in payment_schedule:
|
||||||
|
references.append({
|
||||||
|
'reference_doctype': dt,
|
||||||
|
'reference_name': dn,
|
||||||
|
'bill_no': doc.get('bill_no'),
|
||||||
|
'due_date': doc.get('due_date'),
|
||||||
|
'total_amount': grand_total,
|
||||||
|
'outstanding_amount': outstanding_amount,
|
||||||
|
'payment_term': payment_term.payment_term,
|
||||||
|
'allocated_amount': flt(payment_term.payment_amount - payment_term.paid_amount,
|
||||||
|
payment_term.precision('payment_amount'))
|
||||||
|
})
|
||||||
|
|
||||||
|
return references
|
||||||
|
|
||||||
def get_paid_amount(dt, dn, party_type, party, account, due_date):
|
def get_paid_amount(dt, dn, party_type, party, account, due_date):
|
||||||
if party_type=="Customer":
|
if party_type=="Customer":
|
||||||
|
|||||||
12
erpnext/accounts/doctype/payment_entry/payment_entry_list.js
Normal file
12
erpnext/accounts/doctype/payment_entry/payment_entry_list.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
frappe.listview_settings['Payment Entry'] = {
|
||||||
|
|
||||||
|
onload: function(listview) {
|
||||||
|
listview.page.fields_dict.party_type.get_query = function() {
|
||||||
|
return {
|
||||||
|
"filters": {
|
||||||
|
"name": ["in", Object.keys(frappe.boot.party_account_types)],
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -149,6 +149,73 @@ class TestPaymentEntry(unittest.TestCase):
|
|||||||
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", pi.name, "outstanding_amount"))
|
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", pi.name, "outstanding_amount"))
|
||||||
self.assertEqual(outstanding_amount, 0)
|
self.assertEqual(outstanding_amount, 0)
|
||||||
|
|
||||||
|
def test_payment_entry_against_payment_terms(self):
|
||||||
|
si = create_sales_invoice(do_not_save=1, qty=1, rate=200)
|
||||||
|
create_payment_terms_template()
|
||||||
|
si.payment_terms_template = 'Test Receivable Template'
|
||||||
|
|
||||||
|
si.append('taxes', {
|
||||||
|
"charge_type": "On Net Total",
|
||||||
|
"account_head": "_Test Account Service Tax - _TC",
|
||||||
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
|
"description": "Service Tax",
|
||||||
|
"rate": 18
|
||||||
|
})
|
||||||
|
si.save()
|
||||||
|
|
||||||
|
si.submit()
|
||||||
|
|
||||||
|
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Cash - _TC")
|
||||||
|
pe.submit()
|
||||||
|
si.load_from_db()
|
||||||
|
|
||||||
|
self.assertEqual(pe.references[0].payment_term, 'Basic Amount Receivable')
|
||||||
|
self.assertEqual(pe.references[1].payment_term, 'Tax Receivable')
|
||||||
|
self.assertEqual(si.payment_schedule[0].paid_amount, 200.0)
|
||||||
|
self.assertEqual(si.payment_schedule[1].paid_amount, 36.0)
|
||||||
|
|
||||||
|
def test_payment_against_sales_invoice_to_check_status(self):
|
||||||
|
si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
|
||||||
|
currency="USD", conversion_rate=50)
|
||||||
|
|
||||||
|
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC")
|
||||||
|
pe.reference_no = "1"
|
||||||
|
pe.reference_date = "2016-01-01"
|
||||||
|
pe.target_exchange_rate = 50
|
||||||
|
pe.insert()
|
||||||
|
pe.submit()
|
||||||
|
|
||||||
|
outstanding_amount, status = frappe.db.get_value("Sales Invoice", si.name, ["outstanding_amount", "status"])
|
||||||
|
self.assertEqual(flt(outstanding_amount), 0)
|
||||||
|
self.assertEqual(status, 'Paid')
|
||||||
|
|
||||||
|
pe.cancel()
|
||||||
|
|
||||||
|
outstanding_amount, status = frappe.db.get_value("Sales Invoice", si.name, ["outstanding_amount", "status"])
|
||||||
|
self.assertEqual(flt(outstanding_amount), 100)
|
||||||
|
self.assertEqual(status, 'Unpaid')
|
||||||
|
|
||||||
|
def test_payment_against_purchase_invoice_to_check_status(self):
|
||||||
|
pi = make_purchase_invoice(supplier="_Test Supplier USD", debit_to="_Test Payable USD - _TC",
|
||||||
|
currency="USD", conversion_rate=50)
|
||||||
|
|
||||||
|
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank USD - _TC")
|
||||||
|
pe.reference_no = "1"
|
||||||
|
pe.reference_date = "2016-01-01"
|
||||||
|
pe.source_exchange_rate = 50
|
||||||
|
pe.insert()
|
||||||
|
pe.submit()
|
||||||
|
|
||||||
|
outstanding_amount, status = frappe.db.get_value("Purchase Invoice", pi.name, ["outstanding_amount", "status"])
|
||||||
|
self.assertEqual(flt(outstanding_amount), 0)
|
||||||
|
self.assertEqual(status, 'Paid')
|
||||||
|
|
||||||
|
pe.cancel()
|
||||||
|
|
||||||
|
outstanding_amount, status = frappe.db.get_value("Purchase Invoice", pi.name, ["outstanding_amount", "status"])
|
||||||
|
self.assertEqual(flt(outstanding_amount), 250)
|
||||||
|
self.assertEqual(status, 'Unpaid')
|
||||||
|
|
||||||
def test_payment_entry_against_ec(self):
|
def test_payment_entry_against_ec(self):
|
||||||
|
|
||||||
payable = frappe.get_cached_value('Company', "_Test Company", 'default_payable_account')
|
payable = frappe.get_cached_value('Company', "_Test Company", 'default_payable_account')
|
||||||
@@ -567,3 +634,37 @@ class TestPaymentEntry(unittest.TestCase):
|
|||||||
|
|
||||||
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
|
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
|
||||||
accounts_settings.save()
|
accounts_settings.save()
|
||||||
|
|
||||||
|
def create_payment_terms_template():
|
||||||
|
|
||||||
|
create_payment_term('Basic Amount Receivable')
|
||||||
|
create_payment_term('Tax Receivable')
|
||||||
|
|
||||||
|
if not frappe.db.exists('Payment Terms Template', 'Test Receivable Template'):
|
||||||
|
payment_term_template = frappe.get_doc({
|
||||||
|
'doctype': 'Payment Terms Template',
|
||||||
|
'template_name': 'Test Receivable Template',
|
||||||
|
'allocate_payment_based_on_payment_terms': 1,
|
||||||
|
'terms': [{
|
||||||
|
'doctype': 'Payment Terms Template Detail',
|
||||||
|
'payment_term': 'Basic Amount Receivable',
|
||||||
|
'invoice_portion': 84.746,
|
||||||
|
'credit_days_based_on': 'Day(s) after invoice date',
|
||||||
|
'credit_days': 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'doctype': 'Payment Terms Template Detail',
|
||||||
|
'payment_term': 'Tax Receivable',
|
||||||
|
'invoice_portion': 15.254,
|
||||||
|
'credit_days_based_on': 'Day(s) after invoice date',
|
||||||
|
'credit_days': 2
|
||||||
|
}]
|
||||||
|
}).insert()
|
||||||
|
|
||||||
|
|
||||||
|
def create_payment_term(name):
|
||||||
|
if not frappe.db.exists('Payment Term', name):
|
||||||
|
frappe.get_doc({
|
||||||
|
'doctype': 'Payment Term',
|
||||||
|
'payment_term_name': name
|
||||||
|
}).insert()
|
||||||
|
|||||||
@@ -1,343 +1,107 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_events_in_timeline": 0,
|
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"beta": 0,
|
|
||||||
"creation": "2016-06-01 16:55:32.196722",
|
"creation": "2016-06-01 16:55:32.196722",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "",
|
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"reference_doctype",
|
||||||
|
"reference_name",
|
||||||
|
"due_date",
|
||||||
|
"bill_no",
|
||||||
|
"payment_term",
|
||||||
|
"column_break_4",
|
||||||
|
"total_amount",
|
||||||
|
"outstanding_amount",
|
||||||
|
"allocated_amount",
|
||||||
|
"exchange_rate"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 2,
|
"columns": 2,
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "reference_doctype",
|
"fieldname": "reference_doctype",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Type",
|
"label": "Type",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "DocType",
|
"options": "DocType",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"precision": "",
|
|
||||||
"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
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 2,
|
"columns": 2,
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "reference_name",
|
"fieldname": "reference_name",
|
||||||
"fieldtype": "Dynamic Link",
|
"fieldtype": "Dynamic Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Name",
|
"label": "Name",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "reference_doctype",
|
"options": "reference_doctype",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"precision": "",
|
|
||||||
"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
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "due_date",
|
"fieldname": "due_date",
|
||||||
"fieldtype": "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": "Due Date",
|
"label": "Due Date",
|
||||||
"length": 0,
|
"read_only": 1
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"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
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"depends_on": "",
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "bill_no",
|
"fieldname": "bill_no",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"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": "Supplier Invoice No",
|
"label": "Supplier Invoice No",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"permlevel": 0,
|
"read_only": 1
|
||||||
"precision": "",
|
|
||||||
"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
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "column_break_4",
|
"fieldname": "column_break_4",
|
||||||
"fieldtype": "Column Break",
|
"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,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 2,
|
"columns": 2,
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "total_amount",
|
"fieldname": "total_amount",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Total Amount",
|
"label": "Total Amount",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_hide_if_no_value": 0,
|
"read_only": 1
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 2,
|
"columns": 2,
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "outstanding_amount",
|
"fieldname": "outstanding_amount",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Outstanding",
|
"label": "Outstanding",
|
||||||
"length": 0,
|
"read_only": 1
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"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
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 2,
|
"columns": 2,
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "allocated_amount",
|
"fieldname": "allocated_amount",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
"label": "Allocated"
|
||||||
"label": "Allocated",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"depends_on": "eval:(doc.reference_doctype=='Purchase Invoice')",
|
"depends_on": "eval:(doc.reference_doctype=='Purchase Invoice')",
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "exchange_rate",
|
"fieldname": "exchange_rate",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"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": "Exchange Rate",
|
"label": "Exchange Rate",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_hide_if_no_value": 0,
|
"read_only": 1
|
||||||
"read_only": 1,
|
},
|
||||||
"remember_last_selected_value": 0,
|
{
|
||||||
"report_hide": 0,
|
"fieldname": "payment_term",
|
||||||
"reqd": 0,
|
"fieldtype": "Link",
|
||||||
"search_index": 0,
|
"label": "Payment Term",
|
||||||
"set_only_once": 0,
|
"options": "Payment Term"
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"idx": 0,
|
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"links": [],
|
||||||
"modified": "2019-05-01 13:24:56.586677",
|
"modified": "2020-03-13 12:07:19.362539",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Entry Reference",
|
"name": "Payment Entry Reference",
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"track_changes": 1,
|
"track_changes": 1
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
||||||
@@ -15,11 +15,11 @@ frappe.ui.form.on('Payment Order', {
|
|||||||
if (frm.doc.docstatus == 0) {
|
if (frm.doc.docstatus == 0) {
|
||||||
frm.add_custom_button(__('Payment Request'), function() {
|
frm.add_custom_button(__('Payment Request'), function() {
|
||||||
frm.trigger("get_from_payment_request");
|
frm.trigger("get_from_payment_request");
|
||||||
}, __("Get from"));
|
}, __("Get Payments from"));
|
||||||
|
|
||||||
frm.add_custom_button(__('Payment Entry'), function() {
|
frm.add_custom_button(__('Payment Entry'), function() {
|
||||||
frm.trigger("get_from_payment_entry");
|
frm.trigger("get_from_payment_entry");
|
||||||
}, __("Get from"));
|
}, __("Get Payments from"));
|
||||||
|
|
||||||
frm.trigger('remove_button');
|
frm.trigger('remove_button');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,6 @@
|
|||||||
"fieldtype": "Section Break"
|
"fieldtype": "Section Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 1,
|
|
||||||
"fieldname": "references",
|
"fieldname": "references",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"label": "Payment Order Reference",
|
"label": "Payment Order Reference",
|
||||||
@@ -108,7 +107,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2019-05-14 17:12:24.912666",
|
"modified": "2020-04-06 18:00:56.022642",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Order",
|
"name": "Payment Order",
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ def make_journal_entry(doc, supplier, mode_of_payment=None):
|
|||||||
paid_amt += d.amount
|
paid_amt += d.amount
|
||||||
|
|
||||||
je.append('accounts', {
|
je.append('accounts', {
|
||||||
'account': doc.references[0].account,
|
'account': doc.account,
|
||||||
'credit_in_account_currency': paid_amt
|
'credit_in_account_currency': paid_amt
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ class PaymentReconciliation(Document):
|
|||||||
|
|
||||||
if self.party_type in ["Customer", "Supplier"]:
|
if self.party_type in ["Customer", "Supplier"]:
|
||||||
dr_or_cr_notes = self.get_dr_or_cr_notes()
|
dr_or_cr_notes = self.get_dr_or_cr_notes()
|
||||||
|
else:
|
||||||
|
dr_or_cr_notes = []
|
||||||
|
|
||||||
self.add_payment_entries(payment_entries + journal_entries + dr_or_cr_notes)
|
self.add_payment_entries(payment_entries + journal_entries + dr_or_cr_notes)
|
||||||
|
|
||||||
@@ -90,6 +92,7 @@ class PaymentReconciliation(Document):
|
|||||||
FROM `tab{doc}`, `tabGL Entry`
|
FROM `tab{doc}`, `tabGL Entry`
|
||||||
WHERE
|
WHERE
|
||||||
(`tab{doc}`.name = `tabGL Entry`.against_voucher or `tab{doc}`.name = `tabGL Entry`.voucher_no)
|
(`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 `tab{doc}`.is_return = 1 and `tab{doc}`.return_against IS NULL
|
||||||
and `tabGL Entry`.against_voucher_type = %(voucher_type)s
|
and `tabGL Entry`.against_voucher_type = %(voucher_type)s
|
||||||
and `tab{doc}`.docstatus = 1 and `tabGL Entry`.party = %(party)s
|
and `tab{doc}`.docstatus = 1 and `tabGL Entry`.party = %(party)s
|
||||||
@@ -97,12 +100,17 @@ class PaymentReconciliation(Document):
|
|||||||
GROUP BY `tab{doc}`.name
|
GROUP BY `tab{doc}`.name
|
||||||
Having
|
Having
|
||||||
amount > 0
|
amount > 0
|
||||||
""".format(doc=voucher_type, dr_or_cr=dr_or_cr, reconciled_dr_or_cr=reconciled_dr_or_cr), {
|
""".format(
|
||||||
'party': self.party,
|
doc=voucher_type,
|
||||||
'party_type': self.party_type,
|
dr_or_cr=dr_or_cr,
|
||||||
'voucher_type': voucher_type,
|
reconciled_dr_or_cr=reconciled_dr_or_cr,
|
||||||
'account': self.receivable_payable_account
|
party_type_field=frappe.scrub(self.party_type)),
|
||||||
}, as_dict=1)
|
{
|
||||||
|
'party': self.party,
|
||||||
|
'party_type': self.party_type,
|
||||||
|
'voucher_type': voucher_type,
|
||||||
|
'account': self.receivable_payable_account
|
||||||
|
}, as_dict=1)
|
||||||
|
|
||||||
def add_payment_entries(self, entries):
|
def add_payment_entries(self, entries):
|
||||||
self.set('payments', [])
|
self.set('payments', [])
|
||||||
|
|||||||
@@ -2,6 +2,16 @@ cur_frm.add_fetch("payment_gateway_account", "payment_account", "payment_account
|
|||||||
cur_frm.add_fetch("payment_gateway_account", "payment_gateway", "payment_gateway")
|
cur_frm.add_fetch("payment_gateway_account", "payment_gateway", "payment_gateway")
|
||||||
cur_frm.add_fetch("payment_gateway_account", "message", "message")
|
cur_frm.add_fetch("payment_gateway_account", "message", "message")
|
||||||
|
|
||||||
|
frappe.ui.form.on("Payment Request", {
|
||||||
|
setup: function(frm) {
|
||||||
|
frm.set_query("party_type", function() {
|
||||||
|
return {
|
||||||
|
query: "erpnext.setup.doctype.party_type.party_type.get_party_type",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
frappe.ui.form.on("Payment Request", "onload", function(frm, dt, dn){
|
frappe.ui.form.on("Payment Request", "onload", function(frm, dt, dn){
|
||||||
if (frm.doc.reference_doctype) {
|
if (frm.doc.reference_doctype) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -66,8 +66,10 @@ class PaymentRequest(Document):
|
|||||||
if self.payment_request_type == 'Outward':
|
if self.payment_request_type == 'Outward':
|
||||||
self.db_set('status', 'Initiated')
|
self.db_set('status', 'Initiated')
|
||||||
return
|
return
|
||||||
|
elif self.payment_request_type == 'Inward':
|
||||||
|
self.db_set('status', 'Requested')
|
||||||
|
|
||||||
send_mail = self.payment_gateway_validation()
|
send_mail = self.payment_gateway_validation() if self.payment_gateway else None
|
||||||
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
|
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
|
||||||
|
|
||||||
if (hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart") \
|
if (hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart") \
|
||||||
@@ -88,6 +90,7 @@ class PaymentRequest(Document):
|
|||||||
if (hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart"):
|
if (hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart"):
|
||||||
from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice
|
from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice
|
||||||
si = make_sales_invoice(self.reference_name, ignore_permissions=True)
|
si = make_sales_invoice(self.reference_name, ignore_permissions=True)
|
||||||
|
si.allocate_advances_automatically = True
|
||||||
si = si.insert(ignore_permissions=True)
|
si = si.insert(ignore_permissions=True)
|
||||||
si.submit()
|
si.submit()
|
||||||
|
|
||||||
@@ -126,12 +129,12 @@ class PaymentRequest(Document):
|
|||||||
|
|
||||||
return controller.get_payment_url(**{
|
return controller.get_payment_url(**{
|
||||||
"amount": flt(self.grand_total, self.precision("grand_total")),
|
"amount": flt(self.grand_total, self.precision("grand_total")),
|
||||||
"title": data.company.encode("utf-8"),
|
"title": frappe.as_unicode(data.company),
|
||||||
"description": self.subject.encode("utf-8"),
|
"description": frappe.as_unicode(self.subject),
|
||||||
"reference_doctype": "Payment Request",
|
"reference_doctype": "Payment Request",
|
||||||
"reference_docname": self.name,
|
"reference_docname": self.name,
|
||||||
"payer_email": self.email_to or frappe.session.user,
|
"payer_email": self.email_to or frappe.session.user,
|
||||||
"payer_name": frappe.safe_encode(data.customer_name),
|
"payer_name": frappe.as_unicode(data.customer_name),
|
||||||
"order_id": self.name,
|
"order_id": self.name,
|
||||||
"currency": self.currency
|
"currency": self.currency
|
||||||
})
|
})
|
||||||
@@ -317,13 +320,13 @@ def make_payment_request(**args):
|
|||||||
"payment_request_type": args.get("payment_request_type"),
|
"payment_request_type": args.get("payment_request_type"),
|
||||||
"currency": ref_doc.currency,
|
"currency": ref_doc.currency,
|
||||||
"grand_total": grand_total,
|
"grand_total": grand_total,
|
||||||
"email_to": args.recipient_id or "",
|
"email_to": args.recipient_id or ref_doc.owner,
|
||||||
"subject": _("Payment Request for {0}").format(args.dn),
|
"subject": _("Payment Request for {0}").format(args.dn),
|
||||||
"message": gateway_account.get("message") or get_dummy_message(ref_doc),
|
"message": gateway_account.get("message") or get_dummy_message(ref_doc),
|
||||||
"reference_doctype": args.dt,
|
"reference_doctype": args.dt,
|
||||||
"reference_name": args.dn,
|
"reference_name": args.dn,
|
||||||
"party_type": args.get("party_type"),
|
"party_type": args.get("party_type") or "Customer",
|
||||||
"party": args.get("party"),
|
"party": args.get("party") or ref_doc.get("customer"),
|
||||||
"bank_account": bank_account
|
"bank_account": bank_account
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -350,13 +353,13 @@ def get_amount(ref_doc):
|
|||||||
if dt in ["Sales Order", "Purchase Order"]:
|
if dt in ["Sales Order", "Purchase Order"]:
|
||||||
grand_total = flt(ref_doc.grand_total) - flt(ref_doc.advance_paid)
|
grand_total = flt(ref_doc.grand_total) - flt(ref_doc.advance_paid)
|
||||||
|
|
||||||
if dt in ["Sales Invoice", "Purchase Invoice"]:
|
elif dt in ["Sales Invoice", "Purchase Invoice"]:
|
||||||
if ref_doc.party_account_currency == ref_doc.currency:
|
if ref_doc.party_account_currency == ref_doc.currency:
|
||||||
grand_total = flt(ref_doc.outstanding_amount)
|
grand_total = flt(ref_doc.outstanding_amount)
|
||||||
else:
|
else:
|
||||||
grand_total = flt(ref_doc.outstanding_amount) / ref_doc.conversion_rate
|
grand_total = flt(ref_doc.outstanding_amount) / ref_doc.conversion_rate
|
||||||
|
|
||||||
if dt == "Fees":
|
elif dt == "Fees":
|
||||||
grand_total = ref_doc.outstanding_amount
|
grand_total = ref_doc.outstanding_amount
|
||||||
|
|
||||||
if grand_total > 0 :
|
if grand_total > 0 :
|
||||||
@@ -373,6 +376,7 @@ def get_existing_payment_request_amount(ref_dt, ref_dn):
|
|||||||
reference_doctype = %s
|
reference_doctype = %s
|
||||||
and reference_name = %s
|
and reference_name = %s
|
||||||
and docstatus = 1
|
and docstatus = 1
|
||||||
|
and status != 'Paid'
|
||||||
""", (ref_dt, ref_dn))
|
""", (ref_dt, ref_dn))
|
||||||
return flt(existing_payment_request_amount[0][0]) if existing_payment_request_amount else 0
|
return flt(existing_payment_request_amount[0][0]) if existing_payment_request_amount else 0
|
||||||
|
|
||||||
@@ -414,17 +418,31 @@ def make_payment_entry(docname):
|
|||||||
doc = frappe.get_doc("Payment Request", docname)
|
doc = frappe.get_doc("Payment Request", docname)
|
||||||
return doc.create_payment_entry(submit=False).as_dict()
|
return doc.create_payment_entry(submit=False).as_dict()
|
||||||
|
|
||||||
def make_status_as_paid(doc, method):
|
def update_payment_req_status(doc, method):
|
||||||
|
from erpnext.accounts.doctype.payment_entry.payment_entry import get_reference_details
|
||||||
|
|
||||||
for ref in doc.references:
|
for ref in doc.references:
|
||||||
payment_request_name = frappe.db.get_value("Payment Request",
|
payment_request_name = frappe.db.get_value("Payment Request",
|
||||||
{"reference_doctype": ref.reference_doctype, "reference_name": ref.reference_name,
|
{"reference_doctype": ref.reference_doctype, "reference_name": ref.reference_name,
|
||||||
"docstatus": 1})
|
"docstatus": 1})
|
||||||
|
|
||||||
if payment_request_name:
|
if payment_request_name:
|
||||||
doc = frappe.get_doc("Payment Request", payment_request_name)
|
ref_details = get_reference_details(ref.reference_doctype, ref.reference_name, doc.party_account_currency)
|
||||||
if doc.status != "Paid":
|
pay_req_doc = frappe.get_doc('Payment Request', payment_request_name)
|
||||||
doc.db_set('status', 'Paid')
|
status = pay_req_doc.status
|
||||||
frappe.db.commit()
|
|
||||||
|
if status != "Paid" and not ref_details.outstanding_amount:
|
||||||
|
status = 'Paid'
|
||||||
|
elif status != "Partially Paid" and ref_details.outstanding_amount != ref_details.total_amount:
|
||||||
|
status = 'Partially Paid'
|
||||||
|
elif ref_details.outstanding_amount == ref_details.total_amount:
|
||||||
|
if pay_req_doc.payment_request_type == 'Outward':
|
||||||
|
status = 'Initiated'
|
||||||
|
elif pay_req_doc.payment_request_type == 'Inward':
|
||||||
|
status = 'Requested'
|
||||||
|
|
||||||
|
pay_req_doc.db_set('status', status)
|
||||||
|
frappe.db.commit()
|
||||||
|
|
||||||
def get_dummy_message(doc):
|
def get_dummy_message(doc):
|
||||||
return frappe.render_template("""{% if doc.contact_person -%}
|
return frappe.render_template("""{% if doc.contact_person -%}
|
||||||
@@ -474,4 +492,4 @@ def make_payment_order(source_name, target_doc=None):
|
|||||||
}
|
}
|
||||||
}, target_doc, set_missing_values)
|
}, target_doc, set_missing_values)
|
||||||
|
|
||||||
return doclist
|
return doclist
|
||||||
|
|||||||
@@ -4,14 +4,20 @@ frappe.listview_settings['Payment Request'] = {
|
|||||||
if(doc.status == "Draft") {
|
if(doc.status == "Draft") {
|
||||||
return [__("Draft"), "darkgrey", "status,=,Draft"];
|
return [__("Draft"), "darkgrey", "status,=,Draft"];
|
||||||
}
|
}
|
||||||
|
if(doc.status == "Requested") {
|
||||||
|
return [__("Requested"), "green", "status,=,Requested"];
|
||||||
|
}
|
||||||
else if(doc.status == "Initiated") {
|
else if(doc.status == "Initiated") {
|
||||||
return [__("Initiated"), "green", "status,=,Initiated"];
|
return [__("Initiated"), "green", "status,=,Initiated"];
|
||||||
}
|
}
|
||||||
|
else if(doc.status == "Partially Paid") {
|
||||||
|
return [__("Partially Paid"), "orange", "status,=,Partially Paid"];
|
||||||
|
}
|
||||||
else if(doc.status == "Paid") {
|
else if(doc.status == "Paid") {
|
||||||
return [__("Paid"), "blue", "status,=,Paid"];
|
return [__("Paid"), "blue", "status,=,Paid"];
|
||||||
}
|
}
|
||||||
else if(doc.status == "Cancelled") {
|
else if(doc.status == "Cancelled") {
|
||||||
return [__("Cancelled"), "orange", "status,=,Cancelled"];
|
return [__("Cancelled"), "red", "status,=,Cancelled"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,6 +101,23 @@ class TestPaymentRequest(unittest.TestCase):
|
|||||||
self.assertEqual(expected_gle[gle.account][2], gle.credit)
|
self.assertEqual(expected_gle[gle.account][2], gle.credit)
|
||||||
self.assertEqual(expected_gle[gle.account][3], gle.against_voucher)
|
self.assertEqual(expected_gle[gle.account][3], gle.against_voucher)
|
||||||
|
|
||||||
|
def test_status(self):
|
||||||
|
si_usd = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
|
||||||
|
currency="USD", conversion_rate=50)
|
||||||
|
|
||||||
|
pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com",
|
||||||
|
mute_email=1, payment_gateway="_Test Gateway - USD", submit_doc=1, return_doc=1)
|
||||||
|
|
||||||
|
pe = pr.create_payment_entry()
|
||||||
|
pr.load_from_db()
|
||||||
|
|
||||||
|
self.assertEqual(pr.status, 'Paid')
|
||||||
|
|
||||||
|
pe.cancel()
|
||||||
|
pr.load_from_db()
|
||||||
|
|
||||||
|
self.assertEqual(pr.status, 'Requested')
|
||||||
|
|
||||||
def test_multiple_payment_entries_against_sales_order(self):
|
def test_multiple_payment_entries_against_sales_order(self):
|
||||||
# Make Sales Order, grand_total = 1000
|
# Make Sales Order, grand_total = 1000
|
||||||
so = make_sales_order()
|
so = make_sales_order()
|
||||||
|
|||||||
@@ -1,243 +1,82 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_guest_to_view": 0,
|
"creation": "2017-08-10 15:38:00.080575",
|
||||||
"allow_import": 0,
|
"doctype": "DocType",
|
||||||
"allow_rename": 0,
|
"editable_grid": 1,
|
||||||
"autoname": "",
|
"engine": "InnoDB",
|
||||||
"beta": 0,
|
"field_order": [
|
||||||
"creation": "2017-08-10 15:38:00.080575",
|
"payment_term",
|
||||||
"custom": 0,
|
"description",
|
||||||
"docstatus": 0,
|
"due_date",
|
||||||
"doctype": "DocType",
|
"invoice_portion",
|
||||||
"document_type": "",
|
"payment_amount",
|
||||||
"editable_grid": 1,
|
"mode_of_payment",
|
||||||
"engine": "InnoDB",
|
"paid_amount"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"columns": 2,
|
||||||
"allow_in_quick_entry": 0,
|
"fieldname": "payment_term",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Link",
|
||||||
"bold": 0,
|
"in_list_view": 1,
|
||||||
"collapsible": 0,
|
"label": "Payment Term",
|
||||||
"columns": 2,
|
"options": "Payment Term",
|
||||||
"fieldname": "payment_term",
|
"print_hide": 1
|
||||||
"fieldtype": "Link",
|
},
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Payment Term",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Payment Term",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 1,
|
|
||||||
"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
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"columns": 2,
|
||||||
"allow_in_quick_entry": 0,
|
"fieldname": "description",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Small Text",
|
||||||
"bold": 0,
|
"in_list_view": 1,
|
||||||
"collapsible": 0,
|
"label": "Description"
|
||||||
"columns": 2,
|
},
|
||||||
"fetch_from": "",
|
|
||||||
"fieldname": "description",
|
|
||||||
"fieldtype": "Small Text",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Description",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"columns": 2,
|
||||||
"allow_in_quick_entry": 0,
|
"fieldname": "due_date",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Date",
|
||||||
"bold": 0,
|
"in_list_view": 1,
|
||||||
"collapsible": 0,
|
"label": "Due Date",
|
||||||
"columns": 2,
|
"reqd": 1
|
||||||
"fieldname": "due_date",
|
},
|
||||||
"fieldtype": "Date",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Due Date",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"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
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"columns": 2,
|
||||||
"allow_in_quick_entry": 0,
|
"fieldname": "invoice_portion",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Percent",
|
||||||
"bold": 0,
|
"in_list_view": 1,
|
||||||
"collapsible": 0,
|
"label": "Invoice Portion",
|
||||||
"columns": 2,
|
"print_hide": 1
|
||||||
"fetch_from": "",
|
},
|
||||||
"fieldname": "invoice_portion",
|
|
||||||
"fieldtype": "Percent",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Invoice Portion",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 1,
|
|
||||||
"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
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"columns": 2,
|
||||||
"allow_in_quick_entry": 0,
|
"fieldname": "payment_amount",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Currency",
|
||||||
"bold": 0,
|
"in_list_view": 1,
|
||||||
"collapsible": 0,
|
"label": "Payment Amount",
|
||||||
"columns": 2,
|
"options": "currency",
|
||||||
"fieldname": "payment_amount",
|
"reqd": 1
|
||||||
"fieldtype": "Currency",
|
},
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Payment Amount",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "currency",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"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
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "mode_of_payment",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Link",
|
||||||
"allow_on_submit": 0,
|
"label": "Mode of Payment",
|
||||||
"bold": 0,
|
"options": "Mode of Payment"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
{
|
||||||
"fieldname": "mode_of_payment",
|
"fieldname": "paid_amount",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
"label": "Paid Amount"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Mode of Payment",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Mode of Payment",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"istable": 1,
|
||||||
"hide_heading": 0,
|
"links": [],
|
||||||
"hide_toolbar": 0,
|
"modified": "2020-03-13 17:58:24.729526",
|
||||||
"idx": 0,
|
"modified_by": "Administrator",
|
||||||
"image_view": 0,
|
"module": "Accounts",
|
||||||
"in_create": 0,
|
"name": "Payment Schedule",
|
||||||
"is_submittable": 0,
|
"owner": "Administrator",
|
||||||
"issingle": 0,
|
"permissions": [],
|
||||||
"istable": 1,
|
"quick_entry": 1,
|
||||||
"max_attachments": 0,
|
"sort_field": "modified",
|
||||||
"modified": "2018-09-06 17:35:44.580209",
|
"sort_order": "DESC",
|
||||||
"modified_by": "Administrator",
|
"track_changes": 1
|
||||||
"module": "Accounts",
|
|
||||||
"name": "Payment Schedule",
|
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [],
|
|
||||||
"quick_entry": 1,
|
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
|
||||||
"sort_order": "DESC",
|
|
||||||
"track_changes": 1,
|
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
||||||
@@ -1,164 +1,84 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_guest_to_view": 0,
|
"allow_import": 1,
|
||||||
"allow_import": 1,
|
"allow_rename": 1,
|
||||||
"allow_rename": 1,
|
"autoname": "field:template_name",
|
||||||
"autoname": "field:template_name",
|
"creation": "2017-08-10 15:34:28.058054",
|
||||||
"beta": 0,
|
"doctype": "DocType",
|
||||||
"creation": "2017-08-10 15:34:28.058054",
|
"editable_grid": 1,
|
||||||
"custom": 0,
|
"engine": "InnoDB",
|
||||||
"docstatus": 0,
|
"field_order": [
|
||||||
"doctype": "DocType",
|
"template_name",
|
||||||
"document_type": "",
|
"allocate_payment_based_on_payment_terms",
|
||||||
"editable_grid": 1,
|
"terms"
|
||||||
"engine": "InnoDB",
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "template_name",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Data",
|
||||||
"bold": 0,
|
"label": "Template Name",
|
||||||
"collapsible": 0,
|
"unique": 1
|
||||||
"columns": 0,
|
},
|
||||||
"fieldname": "template_name",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"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": "Template Name",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "terms",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Table",
|
||||||
"bold": 0,
|
"label": "Payment Terms",
|
||||||
"collapsible": 0,
|
"options": "Payment Terms Template Detail",
|
||||||
"columns": 0,
|
"reqd": 1
|
||||||
"fieldname": "terms",
|
},
|
||||||
"fieldtype": "Table",
|
{
|
||||||
"hidden": 0,
|
"default": "0",
|
||||||
"ignore_user_permissions": 0,
|
"description": "If this checkbox is checked, paid amount will be splitted and allocated as per the amounts in payment schedule against each payment term",
|
||||||
"ignore_xss_filter": 0,
|
"fieldname": "allocate_payment_based_on_payment_terms",
|
||||||
"in_filter": 0,
|
"fieldtype": "Check",
|
||||||
"in_global_search": 0,
|
"label": "Allocate Payment Based On Payment Terms"
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Payment Terms",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Payment Terms Template Detail",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"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,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"links": [],
|
||||||
"hide_heading": 0,
|
"modified": "2020-04-01 15:35:18.112619",
|
||||||
"hide_toolbar": 0,
|
"modified_by": "Administrator",
|
||||||
"idx": 0,
|
"module": "Accounts",
|
||||||
"image_view": 0,
|
"name": "Payment Terms Template",
|
||||||
"in_create": 0,
|
"owner": "Administrator",
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 0,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2018-01-24 11:13:31.158613",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "Accounts",
|
|
||||||
"name": "Payment Terms Template",
|
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"apply_user_permissions": 0,
|
"delete": 1,
|
||||||
"cancel": 0,
|
"email": 1,
|
||||||
"create": 1,
|
"export": 1,
|
||||||
"delete": 1,
|
"print": 1,
|
||||||
"email": 1,
|
"read": 1,
|
||||||
"export": 1,
|
"report": 1,
|
||||||
"if_owner": 0,
|
"role": "System Manager",
|
||||||
"import": 0,
|
"share": 1,
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "System Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"apply_user_permissions": 0,
|
"delete": 1,
|
||||||
"cancel": 0,
|
"email": 1,
|
||||||
"create": 1,
|
"export": 1,
|
||||||
"delete": 1,
|
"print": 1,
|
||||||
"email": 1,
|
"read": 1,
|
||||||
"export": 1,
|
"report": 1,
|
||||||
"if_owner": 0,
|
"role": "Accounts User",
|
||||||
"import": 0,
|
"share": 1,
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Accounts User",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"apply_user_permissions": 0,
|
"delete": 1,
|
||||||
"cancel": 0,
|
"email": 1,
|
||||||
"create": 1,
|
"export": 1,
|
||||||
"delete": 1,
|
"print": 1,
|
||||||
"email": 1,
|
"read": 1,
|
||||||
"export": 1,
|
"report": 1,
|
||||||
"if_owner": 0,
|
"role": "Accounts Manager",
|
||||||
"import": 0,
|
"share": 1,
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Accounts Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
"sort_field": "modified",
|
||||||
"read_only": 0,
|
"sort_order": "DESC",
|
||||||
"read_only_onload": 0,
|
"track_changes": 1
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
|
||||||
"sort_order": "DESC",
|
|
||||||
"track_changes": 1,
|
|
||||||
"track_seen": 0
|
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,8 @@ from frappe.utils import flt
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
from erpnext.accounts.utils import get_account_currency
|
from erpnext.accounts.utils import get_account_currency
|
||||||
from erpnext.controllers.accounts_controller import AccountsController
|
from erpnext.controllers.accounts_controller import AccountsController
|
||||||
|
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (get_accounting_dimensions,
|
||||||
|
get_dimension_filters)
|
||||||
|
|
||||||
class PeriodClosingVoucher(AccountsController):
|
class PeriodClosingVoucher(AccountsController):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
@@ -49,7 +51,15 @@ class PeriodClosingVoucher(AccountsController):
|
|||||||
def make_gl_entries(self):
|
def make_gl_entries(self):
|
||||||
gl_entries = []
|
gl_entries = []
|
||||||
net_pl_balance = 0
|
net_pl_balance = 0
|
||||||
pl_accounts = self.get_pl_balances()
|
dimension_fields = ['t1.cost_center']
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
for acc in pl_accounts:
|
for acc in pl_accounts:
|
||||||
if flt(acc.balance_in_company_currency):
|
if flt(acc.balance_in_company_currency):
|
||||||
@@ -65,34 +75,41 @@ class PeriodClosingVoucher(AccountsController):
|
|||||||
if flt(acc.balance_in_account_currency) > 0 else 0,
|
if flt(acc.balance_in_account_currency) > 0 else 0,
|
||||||
"credit": abs(flt(acc.balance_in_company_currency)) \
|
"credit": abs(flt(acc.balance_in_company_currency)) \
|
||||||
if flt(acc.balance_in_company_currency) > 0 else 0
|
if flt(acc.balance_in_company_currency) > 0 else 0
|
||||||
}))
|
}, item=acc))
|
||||||
|
|
||||||
net_pl_balance += flt(acc.balance_in_company_currency)
|
net_pl_balance += flt(acc.balance_in_company_currency)
|
||||||
|
|
||||||
if net_pl_balance:
|
if net_pl_balance:
|
||||||
cost_center = frappe.db.get_value("Company", self.company, "cost_center")
|
cost_center = frappe.db.get_value("Company", self.company, "cost_center")
|
||||||
gl_entries.append(self.get_gl_dict({
|
gl_entry = self.get_gl_dict({
|
||||||
"account": self.closing_account_head,
|
"account": self.closing_account_head,
|
||||||
"debit_in_account_currency": abs(net_pl_balance) if net_pl_balance > 0 else 0,
|
"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,
|
"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_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,
|
"credit": abs(net_pl_balance) if net_pl_balance < 0 else 0,
|
||||||
"cost_center": cost_center
|
"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)
|
||||||
|
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries
|
from erpnext.accounts.general_ledger import make_gl_entries
|
||||||
make_gl_entries(gl_entries)
|
make_gl_entries(gl_entries)
|
||||||
|
|
||||||
def get_pl_balances(self):
|
def get_pl_balances(self, dimension_fields):
|
||||||
"""Get balance for pl accounts"""
|
"""Get balance for pl accounts"""
|
||||||
return frappe.db.sql("""
|
return frappe.db.sql("""
|
||||||
select
|
select
|
||||||
t1.account, t1.cost_center, t2.account_currency,
|
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_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) - sum(t1.credit) as balance_in_company_currency
|
||||||
from `tabGL Entry` t1, `tabAccount` t2
|
from `tabGL Entry` t1, `tabAccount` t2
|
||||||
where t1.account = t2.name and t2.report_type = 'Profit and Loss'
|
where t1.account = t2.name and t2.report_type = 'Profit and Loss'
|
||||||
and t2.docstatus < 2 and t2.company = %s
|
and t2.docstatus < 2 and t2.company = %s
|
||||||
and t1.posting_date between %s and %s
|
and t1.posting_date between %s and %s
|
||||||
group by t1.account, t1.cost_center
|
group by t1.account, {dimension_fields}
|
||||||
""", (self.company, self.get("year_start_date"), self.posting_date), as_dict=1)
|
""".format(dimension_fields = ', '.join(dimension_fields)), (self.company, self.get("year_start_date"), self.posting_date), as_dict=1)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"autoname": "Prompt",
|
"autoname": "Prompt",
|
||||||
"creation": "2013-05-24 12:15:51",
|
"creation": "2013-05-24 12:15:51",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"disabled",
|
"disabled",
|
||||||
"section_break_2",
|
"section_break_2",
|
||||||
@@ -50,6 +51,7 @@
|
|||||||
"income_account",
|
"income_account",
|
||||||
"expense_account",
|
"expense_account",
|
||||||
"taxes_and_charges",
|
"taxes_and_charges",
|
||||||
|
"tax_category",
|
||||||
"apply_discount_on",
|
"apply_discount_on",
|
||||||
"accounting_dimensions_section",
|
"accounting_dimensions_section",
|
||||||
"cost_center",
|
"cost_center",
|
||||||
@@ -381,11 +383,17 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "dimension_col_break",
|
"fieldname": "dimension_col_break",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "tax_category",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Tax Category",
|
||||||
|
"options": "Tax Category"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "icon-cog",
|
"icon": "icon-cog",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"modified": "2019-05-25 22:56:30.352693",
|
"modified": "2020-01-24 15:52:03.797701",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "POS Profile",
|
"name": "POS Profile",
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ class TestPOSProfile(unittest.TestCase):
|
|||||||
pos_profile = get_pos_profile("_Test Company") or {}
|
pos_profile = get_pos_profile("_Test Company") or {}
|
||||||
if pos_profile:
|
if pos_profile:
|
||||||
doc = frappe.get_doc("POS Profile", pos_profile.get("name"))
|
doc = frappe.get_doc("POS Profile", pos_profile.get("name"))
|
||||||
doc.append('item_groups', {'item_group': '_Test Item Group'})
|
doc.set('item_groups', [{'item_group': '_Test Item Group'}])
|
||||||
doc.append('customer_groups', {'customer_group': '_Test Customer Group'})
|
doc.set('customer_groups', [{'customer_group': '_Test Customer Group'}])
|
||||||
doc.save()
|
doc.save()
|
||||||
items = get_items_list(doc, doc.company)
|
items = get_items_list(doc, doc.company)
|
||||||
customers = get_customers_list(doc)
|
customers = get_customers_list(doc)
|
||||||
|
|
||||||
products_count = frappe.db.sql(""" select count(name) from tabItem where item_group = '_Test Item Group'""", as_list=1)
|
products_count = frappe.db.sql(""" select count(name) from tabItem where item_group = '_Test Item Group'""", as_list=1)
|
||||||
customers_count = frappe.db.sql(""" select count(name) from tabCustomer where customer_group = '_Test Customer Group'""")
|
customers_count = frappe.db.sql(""" select count(name) from tabCustomer where customer_group = '_Test Customer Group'""")
|
||||||
|
|
||||||
|
|||||||
@@ -389,8 +389,7 @@
|
|||||||
"fieldname": "rate_or_discount",
|
"fieldname": "rate_or_discount",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Rate or Discount",
|
"label": "Rate or Discount",
|
||||||
"options": "\nRate\nDiscount Percentage\nDiscount Amount",
|
"options": "\nRate\nDiscount Percentage\nDiscount Amount"
|
||||||
"reqd": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "Grand Total",
|
"default": "Grand Total",
|
||||||
@@ -439,19 +438,20 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"depends_on": "eval:!doc.mixed_conditions",
|
"depends_on": "eval:!doc.mixed_conditions && doc.apply_on != 'Transaction'",
|
||||||
"fieldname": "same_item",
|
"fieldname": "same_item",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Same Item"
|
"label": "Same Item"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:!doc.same_item || doc.mixed_conditions",
|
"depends_on": "eval:(!doc.same_item || doc.apply_on == 'Transaction') || doc.mixed_conditions",
|
||||||
"fieldname": "free_item",
|
"fieldname": "free_item",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Free Item",
|
"label": "Free Item",
|
||||||
"options": "Item"
|
"options": "Item"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"default": "0",
|
||||||
"fieldname": "free_qty",
|
"fieldname": "free_qty",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "Qty"
|
"label": "Qty"
|
||||||
@@ -554,7 +554,7 @@
|
|||||||
],
|
],
|
||||||
"icon": "fa fa-gift",
|
"icon": "fa fa-gift",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"modified": "2019-10-15 12:39:40.399792",
|
"modified": "2019-12-18 17:29:22.957077",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Pricing Rule",
|
"name": "Pricing Rule",
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ class PricingRule(Document):
|
|||||||
|
|
||||||
def validate_duplicate_apply_on(self):
|
def validate_duplicate_apply_on(self):
|
||||||
field = apply_on_dict.get(self.apply_on)
|
field = apply_on_dict.get(self.apply_on)
|
||||||
|
if not field:
|
||||||
|
return False
|
||||||
|
|
||||||
values = [d.get(frappe.scrub(self.apply_on)) for d in self.get(field)]
|
values = [d.get(frappe.scrub(self.apply_on)) for d in self.get(field)]
|
||||||
|
|
||||||
if len(values) != len(set(values)):
|
if len(values) != len(set(values)):
|
||||||
@@ -48,6 +51,9 @@ class PricingRule(Document):
|
|||||||
if tocheck and not self.get(tocheck):
|
if tocheck and not self.get(tocheck):
|
||||||
throw(_("{0} is required").format(self.meta.get_label(tocheck)), frappe.MandatoryError)
|
throw(_("{0} is required").format(self.meta.get_label(tocheck)), frappe.MandatoryError)
|
||||||
|
|
||||||
|
if self.price_or_product_discount == 'Price' and not self.rate_or_discount:
|
||||||
|
throw(_("Rate or Discount is required for the price discount."), frappe.MandatoryError)
|
||||||
|
|
||||||
def validate_applicable_for_selling_or_buying(self):
|
def validate_applicable_for_selling_or_buying(self):
|
||||||
if not self.selling and not self.buying:
|
if not self.selling and not self.buying:
|
||||||
throw(_("Atleast one of the Selling or Buying must be selected"))
|
throw(_("Atleast one of the Selling or Buying must be selected"))
|
||||||
@@ -97,7 +103,7 @@ class PricingRule(Document):
|
|||||||
self.same_item = 1
|
self.same_item = 1
|
||||||
|
|
||||||
def validate_max_discount(self):
|
def validate_max_discount(self):
|
||||||
if self.rate_or_discount == "Discount Percentage" and self.items:
|
if self.rate_or_discount == "Discount Percentage" and self.get("items"):
|
||||||
for d in self.items:
|
for d in self.items:
|
||||||
max_discount = frappe.get_cached_value("Item", d.item_code, "max_discount")
|
max_discount = frappe.get_cached_value("Item", d.item_code, "max_discount")
|
||||||
if max_discount and flt(self.discount_percentage) > flt(max_discount):
|
if max_discount and flt(self.discount_percentage) > flt(max_discount):
|
||||||
@@ -183,7 +189,7 @@ def get_serial_no_for_item(args):
|
|||||||
|
|
||||||
def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=False):
|
def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=False):
|
||||||
from erpnext.accounts.doctype.pricing_rule.utils import (get_pricing_rules,
|
from erpnext.accounts.doctype.pricing_rule.utils import (get_pricing_rules,
|
||||||
get_applied_pricing_rules, get_pricing_rule_items)
|
get_applied_pricing_rules, get_pricing_rule_items, get_product_discount_rule)
|
||||||
|
|
||||||
if isinstance(doc, string_types):
|
if isinstance(doc, string_types):
|
||||||
doc = json.loads(doc)
|
doc = json.loads(doc)
|
||||||
@@ -235,6 +241,7 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa
|
|||||||
if pricing_rule.mixed_conditions or pricing_rule.apply_rule_on_other:
|
if pricing_rule.mixed_conditions or pricing_rule.apply_rule_on_other:
|
||||||
item_details.update({
|
item_details.update({
|
||||||
'apply_rule_on_other_items': json.dumps(pricing_rule.apply_rule_on_other_items),
|
'apply_rule_on_other_items': json.dumps(pricing_rule.apply_rule_on_other_items),
|
||||||
|
'price_or_product_discount': pricing_rule.price_or_product_discount,
|
||||||
'apply_rule_on': (frappe.scrub(pricing_rule.apply_rule_on_other)
|
'apply_rule_on': (frappe.scrub(pricing_rule.apply_rule_on_other)
|
||||||
if pricing_rule.apply_rule_on_other else frappe.scrub(pricing_rule.get('apply_on')))
|
if pricing_rule.apply_rule_on_other else frappe.scrub(pricing_rule.get('apply_on')))
|
||||||
})
|
})
|
||||||
@@ -242,9 +249,11 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa
|
|||||||
if pricing_rule.coupon_code_based==1 and args.coupon_code==None:
|
if pricing_rule.coupon_code_based==1 and args.coupon_code==None:
|
||||||
return item_details
|
return item_details
|
||||||
|
|
||||||
if (not pricing_rule.validate_applied_rule and
|
if not pricing_rule.validate_applied_rule:
|
||||||
pricing_rule.price_or_product_discount == "Price"):
|
if pricing_rule.price_or_product_discount == "Price":
|
||||||
apply_price_discount_pricing_rule(pricing_rule, item_details, args)
|
apply_price_discount_rule(pricing_rule, item_details, args)
|
||||||
|
else:
|
||||||
|
get_product_discount_rule(pricing_rule, item_details, args, doc)
|
||||||
|
|
||||||
item_details.has_pricing_rule = 1
|
item_details.has_pricing_rule = 1
|
||||||
|
|
||||||
@@ -294,7 +303,7 @@ def get_pricing_rule_details(args, pricing_rule):
|
|||||||
'child_docname': args.get('child_docname')
|
'child_docname': args.get('child_docname')
|
||||||
})
|
})
|
||||||
|
|
||||||
def apply_price_discount_pricing_rule(pricing_rule, item_details, args):
|
def apply_price_discount_rule(pricing_rule, item_details, args):
|
||||||
item_details.pricing_rule_for = pricing_rule.rate_or_discount
|
item_details.pricing_rule_for = pricing_rule.rate_or_discount
|
||||||
|
|
||||||
if ((pricing_rule.margin_type == 'Amount' and pricing_rule.currency == args.currency)
|
if ((pricing_rule.margin_type == 'Amount' and pricing_rule.currency == args.currency)
|
||||||
@@ -414,4 +423,4 @@ def get_item_uoms(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
|
|
||||||
return frappe.get_all('UOM Conversion Detail',
|
return frappe.get_all('UOM Conversion Detail',
|
||||||
filters = {'parent': ('in', items), 'uom': ("like", "{0}%".format(txt))},
|
filters = {'parent': ('in', items), 'uom': ("like", "{0}%".format(txt))},
|
||||||
fields = ["distinct uom"], as_list=1)
|
fields = ["distinct uom"], as_list=1)
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_orde
|
|||||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||||
from erpnext.stock.get_item_details import get_item_details
|
from erpnext.stock.get_item_details import get_item_details
|
||||||
from frappe import MandatoryError
|
from frappe import MandatoryError
|
||||||
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
|
from erpnext.healthcare.doctype.lab_test_template.lab_test_template import make_item_price
|
||||||
|
|
||||||
class TestPricingRule(unittest.TestCase):
|
class TestPricingRule(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@@ -145,6 +147,52 @@ class TestPricingRule(unittest.TestCase):
|
|||||||
self.assertEquals(details.get("margin_type"), "Percentage")
|
self.assertEquals(details.get("margin_type"), "Percentage")
|
||||||
self.assertEquals(details.get("margin_rate_or_amount"), 10)
|
self.assertEquals(details.get("margin_rate_or_amount"), 10)
|
||||||
|
|
||||||
|
def test_mixed_conditions_for_item_group(self):
|
||||||
|
for item in ["Mixed Cond Item 1", "Mixed Cond Item 2"]:
|
||||||
|
make_item(item, {"item_group": "Products"})
|
||||||
|
make_item_price(item, "_Test Price List", 100)
|
||||||
|
|
||||||
|
test_record = {
|
||||||
|
"doctype": "Pricing Rule",
|
||||||
|
"title": "_Test Pricing Rule for Item Group",
|
||||||
|
"apply_on": "Item Group",
|
||||||
|
"item_groups": [
|
||||||
|
{
|
||||||
|
"item_group": "Products",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"item_group": "Seed",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"selling": 1,
|
||||||
|
"mixed_conditions": 1,
|
||||||
|
"currency": "USD",
|
||||||
|
"rate_or_discount": "Discount Percentage",
|
||||||
|
"discount_percentage": 10,
|
||||||
|
"applicable_for": "Customer Group",
|
||||||
|
"customer_group": "All Customer Groups",
|
||||||
|
"company": "_Test Company"
|
||||||
|
}
|
||||||
|
frappe.get_doc(test_record.copy()).insert()
|
||||||
|
|
||||||
|
args = frappe._dict({
|
||||||
|
"item_code": "Mixed Cond Item 1",
|
||||||
|
"item_group": "Products",
|
||||||
|
"company": "_Test Company",
|
||||||
|
"price_list": "_Test Price List",
|
||||||
|
"currency": "_Test Currency",
|
||||||
|
"doctype": "Sales Order",
|
||||||
|
"conversion_rate": 1,
|
||||||
|
"price_list_currency": "_Test Currency",
|
||||||
|
"plc_conversion_rate": 1,
|
||||||
|
"order_type": "Sales",
|
||||||
|
"customer": "_Test Customer",
|
||||||
|
"customer_group": "_Test Customer Group",
|
||||||
|
"name": None
|
||||||
|
})
|
||||||
|
details = get_item_details(args)
|
||||||
|
self.assertEquals(details.get("discount_percentage"), 10)
|
||||||
|
|
||||||
def test_pricing_rule_for_variants(self):
|
def test_pricing_rule_for_variants(self):
|
||||||
from erpnext.stock.get_item_details import get_item_details
|
from erpnext.stock.get_item_details import get_item_details
|
||||||
from frappe import MandatoryError
|
from frappe import MandatoryError
|
||||||
@@ -278,6 +326,66 @@ class TestPricingRule(unittest.TestCase):
|
|||||||
self.assertEquals(item.discount_amount, 110)
|
self.assertEquals(item.discount_amount, 110)
|
||||||
self.assertEquals(item.rate, 990)
|
self.assertEquals(item.rate, 990)
|
||||||
|
|
||||||
|
def test_pricing_rule_for_product_discount_on_same_item(self):
|
||||||
|
frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
|
||||||
|
test_record = {
|
||||||
|
"doctype": "Pricing Rule",
|
||||||
|
"title": "_Test Pricing Rule",
|
||||||
|
"apply_on": "Item Code",
|
||||||
|
"currency": "USD",
|
||||||
|
"items": [{
|
||||||
|
"item_code": "_Test Item",
|
||||||
|
}],
|
||||||
|
"selling": 1,
|
||||||
|
"rate_or_discount": "Discount Percentage",
|
||||||
|
"rate": 0,
|
||||||
|
"min_qty": 0,
|
||||||
|
"max_qty": 7,
|
||||||
|
"discount_percentage": 17.5,
|
||||||
|
"price_or_product_discount": "Product",
|
||||||
|
"same_item": 1,
|
||||||
|
"free_qty": 1,
|
||||||
|
"company": "_Test Company"
|
||||||
|
}
|
||||||
|
frappe.get_doc(test_record.copy()).insert()
|
||||||
|
|
||||||
|
# With pricing rule
|
||||||
|
so = make_sales_order(item_code="_Test Item", qty=1)
|
||||||
|
so.load_from_db()
|
||||||
|
self.assertEqual(so.items[1].is_free_item, 1)
|
||||||
|
self.assertEqual(so.items[1].item_code, "_Test Item")
|
||||||
|
|
||||||
|
|
||||||
|
def test_pricing_rule_for_product_discount_on_different_item(self):
|
||||||
|
frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
|
||||||
|
test_record = {
|
||||||
|
"doctype": "Pricing Rule",
|
||||||
|
"title": "_Test Pricing Rule",
|
||||||
|
"apply_on": "Item Code",
|
||||||
|
"currency": "USD",
|
||||||
|
"items": [{
|
||||||
|
"item_code": "_Test Item",
|
||||||
|
}],
|
||||||
|
"selling": 1,
|
||||||
|
"rate_or_discount": "Discount Percentage",
|
||||||
|
"rate": 0,
|
||||||
|
"min_qty": 0,
|
||||||
|
"max_qty": 7,
|
||||||
|
"discount_percentage": 17.5,
|
||||||
|
"price_or_product_discount": "Product",
|
||||||
|
"same_item": 0,
|
||||||
|
"free_item": "_Test Item 2",
|
||||||
|
"free_qty": 1,
|
||||||
|
"company": "_Test Company"
|
||||||
|
}
|
||||||
|
frappe.get_doc(test_record.copy()).insert()
|
||||||
|
|
||||||
|
# With pricing rule
|
||||||
|
so = make_sales_order(item_code="_Test Item", qty=1)
|
||||||
|
so.load_from_db()
|
||||||
|
self.assertEqual(so.items[1].is_free_item, 1)
|
||||||
|
self.assertEqual(so.items[1].item_code, "_Test Item 2")
|
||||||
|
|
||||||
def make_pricing_rule(**args):
|
def make_pricing_rule(**args):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
|
||||||
|
|||||||
@@ -4,13 +4,19 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe, copy, json
|
|
||||||
from frappe import throw, _
|
import copy
|
||||||
|
import json
|
||||||
|
|
||||||
from six import string_types
|
from six import string_types
|
||||||
from frappe.utils import flt, cint, get_datetime
|
|
||||||
|
import frappe
|
||||||
from erpnext.setup.doctype.item_group.item_group import get_child_item_groups
|
from erpnext.setup.doctype.item_group.item_group import get_child_item_groups
|
||||||
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
|
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
|
||||||
from erpnext.stock.get_item_details import get_conversion_factor
|
from erpnext.stock.get_item_details import get_conversion_factor
|
||||||
|
from frappe import _, throw
|
||||||
|
from frappe.utils import cint, flt, get_datetime, get_link_to_form, getdate, today
|
||||||
|
|
||||||
|
|
||||||
class MultiplePricingRuleConflict(frappe.ValidationError): pass
|
class MultiplePricingRuleConflict(frappe.ValidationError): pass
|
||||||
|
|
||||||
@@ -178,7 +184,8 @@ def filter_pricing_rules(args, pricing_rules, doc=None):
|
|||||||
|
|
||||||
if pricing_rules[0].mixed_conditions and doc:
|
if pricing_rules[0].mixed_conditions and doc:
|
||||||
stock_qty, amount, items = get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args)
|
stock_qty, amount, items = get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args)
|
||||||
pricing_rules[0].apply_rule_on_other_items = items
|
for pricing_rule_args in pricing_rules:
|
||||||
|
pricing_rule_args.apply_rule_on_other_items = items
|
||||||
|
|
||||||
elif pricing_rules[0].is_cumulative:
|
elif pricing_rules[0].is_cumulative:
|
||||||
items = [args.get(frappe.scrub(pr_doc.get('apply_on')))]
|
items = [args.get(frappe.scrub(pr_doc.get('apply_on')))]
|
||||||
@@ -245,7 +252,7 @@ def filter_pricing_rules(args, pricing_rules, doc=None):
|
|||||||
|
|
||||||
def validate_quantity_and_amount_for_suggestion(args, qty, amount, item_code, transaction_type):
|
def validate_quantity_and_amount_for_suggestion(args, qty, amount, item_code, transaction_type):
|
||||||
fieldname, msg = '', ''
|
fieldname, msg = '', ''
|
||||||
type_of_transaction = 'purcahse' if transaction_type == "buying" else "sale"
|
type_of_transaction = 'purchase' if transaction_type == 'buying' else 'sale'
|
||||||
|
|
||||||
for field, value in {'min_qty': qty, 'min_amt': amount}.items():
|
for field, value in {'min_qty': qty, 'min_amt': amount}.items():
|
||||||
if (args.get(field) and value < args.get(field)
|
if (args.get(field) and value < args.get(field)
|
||||||
@@ -284,7 +291,7 @@ def filter_pricing_rules_for_qty_amount(qty, rate, pricing_rules, args=None):
|
|||||||
status = True
|
status = True
|
||||||
|
|
||||||
# if user has created item price against the transaction UOM
|
# if user has created item price against the transaction UOM
|
||||||
if rule.get("uom") == args.get("uom"):
|
if args and rule.get("uom") == args.get("uom"):
|
||||||
conversion_factor = 1.0
|
conversion_factor = 1.0
|
||||||
|
|
||||||
if status and (flt(rate) >= (flt(rule.min_amt) * conversion_factor)
|
if status and (flt(rate) >= (flt(rule.min_amt) * conversion_factor)
|
||||||
@@ -329,9 +336,9 @@ def get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args):
|
|||||||
if pr_doc.mixed_conditions:
|
if pr_doc.mixed_conditions:
|
||||||
amt = args.get('qty') * args.get("price_list_rate")
|
amt = args.get('qty') * args.get("price_list_rate")
|
||||||
if args.get("item_code") != row.get("item_code"):
|
if args.get("item_code") != row.get("item_code"):
|
||||||
amt = row.get('qty') * row.get("price_list_rate")
|
amt = flt(row.get('qty')) * flt(row.get("price_list_rate") or args.get("rate"))
|
||||||
|
|
||||||
sum_qty += row.get("stock_qty") or args.get("stock_qty")
|
sum_qty += flt(row.get("stock_qty")) or flt(args.get("stock_qty")) or flt(args.get("qty"))
|
||||||
sum_amt += amt
|
sum_amt += amt
|
||||||
|
|
||||||
if pr_doc.is_cumulative:
|
if pr_doc.is_cumulative:
|
||||||
@@ -408,7 +415,8 @@ def apply_pricing_rule_on_transaction(doc):
|
|||||||
conditions = get_other_conditions(conditions, values, doc)
|
conditions = get_other_conditions(conditions, values, doc)
|
||||||
|
|
||||||
pricing_rules = frappe.db.sql(""" Select `tabPricing Rule`.* from `tabPricing Rule`
|
pricing_rules = frappe.db.sql(""" Select `tabPricing Rule`.* from `tabPricing Rule`
|
||||||
where {conditions} """.format(conditions = conditions), values, as_dict=1)
|
where {conditions} and `tabPricing Rule`.disable = 0
|
||||||
|
""".format(conditions = conditions), values, as_dict=1)
|
||||||
|
|
||||||
if pricing_rules:
|
if pricing_rules:
|
||||||
pricing_rules = filter_pricing_rules_for_qty_amount(doc.total_qty,
|
pricing_rules = filter_pricing_rules_for_qty_amount(doc.total_qty,
|
||||||
@@ -420,39 +428,66 @@ def apply_pricing_rule_on_transaction(doc):
|
|||||||
doc.set('apply_discount_on', d.apply_discount_on)
|
doc.set('apply_discount_on', d.apply_discount_on)
|
||||||
|
|
||||||
for field in ['additional_discount_percentage', 'discount_amount']:
|
for field in ['additional_discount_percentage', 'discount_amount']:
|
||||||
if not d.get(field): continue
|
|
||||||
|
|
||||||
pr_field = ('discount_percentage'
|
pr_field = ('discount_percentage'
|
||||||
if field == 'additional_discount_percentage' else field)
|
if field == 'additional_discount_percentage' else field)
|
||||||
|
|
||||||
|
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) < d.get(pr_field):
|
||||||
frappe.msgprint(_("User has not applied rule on the invoice {0}")
|
frappe.msgprint(_("User has not applied rule on the invoice {0}")
|
||||||
.format(doc.name))
|
.format(doc.name))
|
||||||
else:
|
else:
|
||||||
doc.set(field, d.get(pr_field))
|
doc.set(field, d.get(pr_field))
|
||||||
|
|
||||||
|
doc.calculate_taxes_and_totals()
|
||||||
elif d.price_or_product_discount == 'Product':
|
elif d.price_or_product_discount == 'Product':
|
||||||
apply_pricing_rule_for_free_items(doc, d)
|
item_details = frappe._dict({'parenttype': doc.doctype})
|
||||||
|
get_product_discount_rule(d, item_details, doc=doc)
|
||||||
|
apply_pricing_rule_for_free_items(doc, item_details.free_item_data)
|
||||||
|
doc.set_missing_values()
|
||||||
|
|
||||||
def get_applied_pricing_rules(item_row):
|
def get_applied_pricing_rules(item_row):
|
||||||
return (item_row.get("pricing_rules").split(',')
|
return (item_row.get("pricing_rules").split(',')
|
||||||
if item_row.get("pricing_rules") else [])
|
if item_row.get("pricing_rules") else [])
|
||||||
|
|
||||||
def apply_pricing_rule_for_free_items(doc, pricing_rule):
|
def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
|
||||||
if pricing_rule.get('free_item'):
|
free_item = pricing_rule.free_item
|
||||||
|
if pricing_rule.same_item:
|
||||||
|
free_item = item_details.item_code or args.item_code
|
||||||
|
|
||||||
|
if not free_item:
|
||||||
|
frappe.throw(_("Free item not set in the pricing rule {0}")
|
||||||
|
.format(get_link_to_form("Pricing Rule", pricing_rule.name)))
|
||||||
|
|
||||||
|
item_details.free_item_data = {
|
||||||
|
'item_code': free_item,
|
||||||
|
'qty': pricing_rule.free_qty or 1,
|
||||||
|
'rate': pricing_rule.free_item_rate or 0,
|
||||||
|
'price_list_rate': pricing_rule.free_item_rate or 0,
|
||||||
|
'is_free_item': 1
|
||||||
|
}
|
||||||
|
|
||||||
|
item_data = frappe.get_cached_value('Item', free_item, ['item_name',
|
||||||
|
'description', 'stock_uom'], as_dict=1)
|
||||||
|
|
||||||
|
item_details.free_item_data.update(item_data)
|
||||||
|
item_details.free_item_data['uom'] = pricing_rule.free_item_uom or item_data.stock_uom
|
||||||
|
item_details.free_item_data['conversion_factor'] = get_conversion_factor(free_item,
|
||||||
|
item_details.free_item_data['uom']).get("conversion_factor", 1)
|
||||||
|
|
||||||
|
if item_details.get("parenttype") == 'Purchase Order':
|
||||||
|
item_details.free_item_data['schedule_date'] = doc.schedule_date if doc else today()
|
||||||
|
|
||||||
|
if item_details.get("parenttype") == 'Sales Order':
|
||||||
|
item_details.free_item_data['delivery_date'] = doc.delivery_date if doc else today()
|
||||||
|
|
||||||
|
def apply_pricing_rule_for_free_items(doc, pricing_rule_args, set_missing_values=False):
|
||||||
|
if pricing_rule_args.get('item_code'):
|
||||||
items = [d.item_code for d in doc.items
|
items = [d.item_code for d in doc.items
|
||||||
if d.item_code == (d.item_code
|
if d.item_code == (pricing_rule_args.get("item_code")) and d.is_free_item]
|
||||||
if pricing_rule.get('same_item') else pricing_rule.get('free_item')) and d.is_free_item]
|
|
||||||
|
|
||||||
if not items:
|
if not items:
|
||||||
doc.append('items', {
|
doc.append('items', pricing_rule_args)
|
||||||
'item_code': pricing_rule.get('free_item'),
|
|
||||||
'qty': pricing_rule.get('free_qty'),
|
|
||||||
'uom': pricing_rule.get('free_item_uom'),
|
|
||||||
'rate': pricing_rule.get('free_item_rate') or 0,
|
|
||||||
'is_free_item': 1
|
|
||||||
})
|
|
||||||
|
|
||||||
doc.set_missing_values()
|
|
||||||
|
|
||||||
def get_pricing_rule_items(pr_doc):
|
def get_pricing_rule_items(pr_doc):
|
||||||
apply_on_data = []
|
apply_on_data = []
|
||||||
@@ -462,29 +497,27 @@ def get_pricing_rule_items(pr_doc):
|
|||||||
|
|
||||||
for d in pr_doc.get(pricing_rule_apply_on):
|
for d in pr_doc.get(pricing_rule_apply_on):
|
||||||
if apply_on == 'item_group':
|
if apply_on == 'item_group':
|
||||||
get_child_item_groups(d.get(apply_on))
|
apply_on_data.extend(get_child_item_groups(d.get(apply_on)))
|
||||||
else:
|
else:
|
||||||
apply_on_data.append(d.get(apply_on))
|
apply_on_data.append(d.get(apply_on))
|
||||||
|
|
||||||
if pr_doc.apply_rule_on_other:
|
if pr_doc.apply_rule_on_other:
|
||||||
apply_on = frappe.scrub(pr_doc.apply_rule_on_other)
|
apply_on = frappe.scrub(pr_doc.apply_rule_on_other)
|
||||||
apply_on_data.append(pr_doc.get(apply_on))
|
apply_on_data.append(pr_doc.get("other_" + apply_on))
|
||||||
|
|
||||||
return list(set(apply_on_data))
|
return list(set(apply_on_data))
|
||||||
|
|
||||||
def validate_coupon_code(coupon_name):
|
def validate_coupon_code(coupon_name):
|
||||||
from frappe.utils import today,getdate
|
coupon = frappe.get_doc("Coupon Code", coupon_name)
|
||||||
coupon=frappe.get_doc("Coupon Code",coupon_name)
|
|
||||||
if coupon.valid_from:
|
if coupon.valid_from:
|
||||||
if coupon.valid_from > getdate(today()) :
|
if coupon.valid_from > getdate(today()):
|
||||||
frappe.throw(_("Sorry,coupon code validity has not started"))
|
frappe.throw(_("Sorry, this coupon code's validity has not started"))
|
||||||
elif coupon.valid_upto:
|
elif coupon.valid_upto:
|
||||||
if coupon.valid_upto < getdate(today()) :
|
if coupon.valid_upto < getdate(today()):
|
||||||
frappe.throw(_("Sorry,coupon code validity has expired"))
|
frappe.throw(_("Sorry, this coupon code's validity has expired"))
|
||||||
elif coupon.used>=coupon.maximum_use:
|
elif coupon.used >= coupon.maximum_use:
|
||||||
frappe.throw(_("Sorry,coupon code are exhausted"))
|
frappe.throw(_("Sorry, this coupon code is no longer valid"))
|
||||||
else:
|
|
||||||
return
|
|
||||||
|
|
||||||
def update_coupon_code_count(coupon_name,transaction_type):
|
def update_coupon_code_count(coupon_name,transaction_type):
|
||||||
coupon=frappe.get_doc("Coupon Code",coupon_name)
|
coupon=frappe.get_doc("Coupon Code",coupon_name)
|
||||||
|
|||||||
@@ -167,8 +167,16 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
|||||||
make_comment_dialog_and_block_invoice: function(){
|
make_comment_dialog_and_block_invoice: function(){
|
||||||
const me = this;
|
const me = this;
|
||||||
|
|
||||||
const title = __('Add Comment');
|
const title = __('Block Invoice');
|
||||||
const fields = [
|
const fields = [
|
||||||
|
{
|
||||||
|
fieldname: 'release_date',
|
||||||
|
read_only: 0,
|
||||||
|
fieldtype:'Date',
|
||||||
|
label: __('Release Date'),
|
||||||
|
default: me.frm.doc.release_date,
|
||||||
|
reqd: 1
|
||||||
|
},
|
||||||
{
|
{
|
||||||
fieldname: 'hold_comment',
|
fieldname: 'hold_comment',
|
||||||
read_only: 0,
|
read_only: 0,
|
||||||
@@ -187,7 +195,11 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
|||||||
const dialog_data = me.dialog.get_values();
|
const dialog_data = me.dialog.get_values();
|
||||||
frappe.call({
|
frappe.call({
|
||||||
'method': 'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.block_invoice',
|
'method': 'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.block_invoice',
|
||||||
'args': {'name': me.frm.doc.name, 'hold_comment': dialog_data.hold_comment},
|
'args': {
|
||||||
|
'name': me.frm.doc.name,
|
||||||
|
'hold_comment': dialog_data.hold_comment,
|
||||||
|
'release_date': dialog_data.release_date
|
||||||
|
},
|
||||||
'callback': (r) => me.frm.reload_doc()
|
'callback': (r) => me.frm.reload_doc()
|
||||||
});
|
});
|
||||||
me.dialog.hide();
|
me.dialog.hide();
|
||||||
@@ -249,12 +261,25 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
|||||||
price_list: this.frm.doc.buying_price_list
|
price_list: this.frm.doc.buying_price_list
|
||||||
}, function() {
|
}, function() {
|
||||||
me.apply_pricing_rule();
|
me.apply_pricing_rule();
|
||||||
|
|
||||||
me.frm.doc.apply_tds = me.frm.supplier_tds ? 1 : 0;
|
me.frm.doc.apply_tds = me.frm.supplier_tds ? 1 : 0;
|
||||||
|
me.frm.doc.tax_withholding_category = me.frm.supplier_tds;
|
||||||
me.frm.set_df_property("apply_tds", "read_only", me.frm.supplier_tds ? 0 : 1);
|
me.frm.set_df_property("apply_tds", "read_only", me.frm.supplier_tds ? 0 : 1);
|
||||||
|
me.frm.set_df_property("tax_withholding_category", "hidden", me.frm.supplier_tds ? 0 : 1);
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
apply_tds: function(frm) {
|
||||||
|
var me = this;
|
||||||
|
|
||||||
|
if (!me.frm.doc.apply_tds) {
|
||||||
|
me.frm.set_value("tax_withholding_category", '');
|
||||||
|
me.frm.set_df_property("tax_withholding_category", "hidden", 1);
|
||||||
|
} else {
|
||||||
|
me.frm.set_value("tax_withholding_category", me.frm.supplier_tds);
|
||||||
|
me.frm.set_df_property("tax_withholding_category", "hidden", 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
credit_to: function() {
|
credit_to: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
if(this.frm.doc.credit_to) {
|
if(this.frm.doc.credit_to) {
|
||||||
@@ -330,23 +355,6 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
|||||||
frm: cur_frm
|
frm: cur_frm
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
item_code: function(frm, cdt, cdn) {
|
|
||||||
var row = locals[cdt][cdn];
|
|
||||||
if(row.item_code) {
|
|
||||||
frappe.call({
|
|
||||||
method: "erpnext.assets.doctype.asset_category.asset_category.get_asset_category_account",
|
|
||||||
args: {
|
|
||||||
"item": row.item_code,
|
|
||||||
"fieldname": "fixed_asset_account",
|
|
||||||
"company": frm.doc.company
|
|
||||||
},
|
|
||||||
callback: function(r, rt) {
|
|
||||||
frappe.model.set_value(cdt, cdn, "expense_account", r.message);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
cur_frm.script_manager.make(erpnext.accounts.PurchaseInvoice);
|
cur_frm.script_manager.make(erpnext.accounts.PurchaseInvoice);
|
||||||
@@ -399,21 +407,11 @@ cur_frm.fields_dict['items'].grid.get_field("item_code").get_query = function(do
|
|||||||
|
|
||||||
cur_frm.fields_dict['credit_to'].get_query = function(doc) {
|
cur_frm.fields_dict['credit_to'].get_query = function(doc) {
|
||||||
// filter on Account
|
// filter on Account
|
||||||
if (doc.supplier) {
|
return {
|
||||||
return {
|
filters: {
|
||||||
filters: {
|
'account_type': 'Payable',
|
||||||
'account_type': 'Payable',
|
'is_group': 0,
|
||||||
'is_group': 0,
|
'company': doc.company
|
||||||
'company': doc.company
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
filters: {
|
|
||||||
'report_type': 'Balance Sheet',
|
|
||||||
'is_group': 0,
|
|
||||||
'company': doc.company
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"autoname": "naming_series:",
|
"autoname": "naming_series:",
|
||||||
"creation": "2013-05-21 16:16:39",
|
"creation": "2013-05-21 16:16:39",
|
||||||
@@ -15,6 +16,7 @@
|
|||||||
"is_paid",
|
"is_paid",
|
||||||
"is_return",
|
"is_return",
|
||||||
"apply_tds",
|
"apply_tds",
|
||||||
|
"tax_withholding_category",
|
||||||
"column_break1",
|
"column_break1",
|
||||||
"company",
|
"company",
|
||||||
"posting_date",
|
"posting_date",
|
||||||
@@ -72,9 +74,9 @@
|
|||||||
"base_total",
|
"base_total",
|
||||||
"base_net_total",
|
"base_net_total",
|
||||||
"column_break_28",
|
"column_break_28",
|
||||||
|
"total_net_weight",
|
||||||
"total",
|
"total",
|
||||||
"net_total",
|
"net_total",
|
||||||
"total_net_weight",
|
|
||||||
"taxes_section",
|
"taxes_section",
|
||||||
"tax_category",
|
"tax_category",
|
||||||
"column_break_49",
|
"column_break_49",
|
||||||
@@ -417,6 +419,7 @@
|
|||||||
"fieldname": "contact_email",
|
"fieldname": "contact_email",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"label": "Contact Email",
|
"label": "Contact Email",
|
||||||
|
"options": "Email",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
@@ -705,7 +708,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "other_charges_calculation",
|
"fieldname": "other_charges_calculation",
|
||||||
"fieldtype": "Text",
|
"fieldtype": "Long Text",
|
||||||
"label": "Taxes and Charges Calculation",
|
"label": "Taxes and Charges Calculation",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldtype": "HTML",
|
"oldfieldtype": "HTML",
|
||||||
@@ -1282,12 +1285,21 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "dimension_col_break",
|
"fieldname": "dimension_col_break",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "tax_withholding_category",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 1,
|
||||||
|
"label": "Tax Withholding Category",
|
||||||
|
"options": "Tax Withholding Category",
|
||||||
|
"print_hide": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
"idx": 204,
|
"idx": 204,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2019-09-17 22:31:42.666601",
|
"links": [],
|
||||||
|
"modified": "2020-04-18 13:05:25.199832",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Invoice",
|
"name": "Purchase Invoice",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe, erpnext
|
import frappe, erpnext
|
||||||
from frappe.utils import cint, cstr, formatdate, flt, getdate, nowdate
|
from frappe.utils import cint, cstr, formatdate, flt, getdate, nowdate, get_link_to_form
|
||||||
from frappe import _, throw
|
from frappe import _, throw
|
||||||
import frappe.defaults
|
import frappe.defaults
|
||||||
|
|
||||||
@@ -146,10 +146,14 @@ class PurchaseInvoice(BuyingController):
|
|||||||
["account_type", "report_type", "account_currency"], as_dict=True)
|
["account_type", "report_type", "account_currency"], as_dict=True)
|
||||||
|
|
||||||
if account.report_type != "Balance Sheet":
|
if account.report_type != "Balance Sheet":
|
||||||
frappe.throw(_("Credit To account must be a Balance Sheet account"))
|
frappe.throw(_("Please ensure {} account is a Balance Sheet account. \
|
||||||
|
You can change the parent account to a Balance Sheet account or select a different account.")
|
||||||
|
.format(frappe.bold("Credit To")), title=_("Invalid Account"))
|
||||||
|
|
||||||
if self.supplier and account.account_type != "Payable":
|
if self.supplier and account.account_type != "Payable":
|
||||||
frappe.throw(_("Credit To account must be a Payable account"))
|
frappe.throw(_("Please ensure {} account is a Payable account. \
|
||||||
|
Change the account type to Payable or select a different account.")
|
||||||
|
.format(frappe.bold("Credit To")), title=_("Invalid Account"))
|
||||||
|
|
||||||
self.party_account_currency = account.account_currency
|
self.party_account_currency = account.account_currency
|
||||||
|
|
||||||
@@ -248,23 +252,37 @@ class PurchaseInvoice(BuyingController):
|
|||||||
def set_against_expense_account(self):
|
def set_against_expense_account(self):
|
||||||
against_accounts = []
|
against_accounts = []
|
||||||
for item in self.get("items"):
|
for item in self.get("items"):
|
||||||
if item.expense_account not in against_accounts:
|
if item.expense_account and (item.expense_account not in against_accounts):
|
||||||
against_accounts.append(item.expense_account)
|
against_accounts.append(item.expense_account)
|
||||||
|
|
||||||
self.against_expense_account = ",".join(against_accounts)
|
self.against_expense_account = ",".join(against_accounts)
|
||||||
|
|
||||||
def po_required(self):
|
def po_required(self):
|
||||||
if frappe.db.get_value("Buying Settings", None, "po_required") == 'Yes':
|
if frappe.db.get_value("Buying Settings", None, "po_required") == 'Yes':
|
||||||
|
|
||||||
|
if frappe.get_value('Supplier', self.supplier, 'allow_purchase_invoice_creation_without_purchase_order'):
|
||||||
|
return
|
||||||
|
|
||||||
for d in self.get('items'):
|
for d in self.get('items'):
|
||||||
if not d.purchase_order:
|
if not d.purchase_order:
|
||||||
throw(_("As per the Buying Settings if Purchase Order Required == 'YES', then for creating Purchase Invoice, user need to create Purchase Order first for item {0}").format(d.item_code))
|
throw(_("""Purchase Order Required for item {0}
|
||||||
|
To submit the invoice without purchase order please set
|
||||||
|
{1} as {2} in {3}""").format(frappe.bold(d.item_code), frappe.bold(_('Purchase Order Required')),
|
||||||
|
frappe.bold('No'), get_link_to_form('Buying Settings', 'Buying Settings', 'Buying Settings')))
|
||||||
|
|
||||||
def pr_required(self):
|
def pr_required(self):
|
||||||
stock_items = self.get_stock_items()
|
stock_items = self.get_stock_items()
|
||||||
if frappe.db.get_value("Buying Settings", None, "pr_required") == 'Yes':
|
if frappe.db.get_value("Buying Settings", None, "pr_required") == 'Yes':
|
||||||
|
|
||||||
|
if frappe.get_value('Supplier', self.supplier, 'allow_purchase_invoice_creation_without_purchase_receipt'):
|
||||||
|
return
|
||||||
|
|
||||||
for d in self.get('items'):
|
for d in self.get('items'):
|
||||||
if not d.purchase_receipt and d.item_code in stock_items:
|
if not d.purchase_receipt and d.item_code in stock_items:
|
||||||
throw(_("As per the Buying Settings if Purchase Reciept Required == 'YES', then for creating Purchase Invoice, user need to create Purchase Receipt first for item {0}").format(d.item_code))
|
throw(_("""Purchase Receipt Required for item {0}
|
||||||
|
To submit the invoice without purchase receipt please set
|
||||||
|
{1} as {2} in {3}""").format(frappe.bold(d.item_code), frappe.bold(_('Purchase Receipt Required')),
|
||||||
|
frappe.bold('No'), get_link_to_form('Buying Settings', 'Buying Settings', 'Buying Settings')))
|
||||||
|
|
||||||
def validate_write_off_account(self):
|
def validate_write_off_account(self):
|
||||||
if self.write_off_amount and not self.write_off_account:
|
if self.write_off_amount and not self.write_off_account:
|
||||||
@@ -368,7 +386,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
update_outstanding_amt(self.credit_to, "Supplier", self.supplier,
|
update_outstanding_amt(self.credit_to, "Supplier", self.supplier,
|
||||||
self.doctype, self.return_against if cint(self.is_return) and self.return_against else self.name)
|
self.doctype, self.return_against if cint(self.is_return) and self.return_against else self.name)
|
||||||
|
|
||||||
if repost_future_gle and cint(self.update_stock) and self.auto_accounting_for_stock:
|
if (repost_future_gle or self.flags.repost_future_gle) and cint(self.update_stock) and self.auto_accounting_for_stock:
|
||||||
from erpnext.controllers.stock_controller import update_gl_entries_after
|
from erpnext.controllers.stock_controller import update_gl_entries_after
|
||||||
items, warehouses = self.get_items_and_warehouses()
|
items, warehouses = self.get_items_and_warehouses()
|
||||||
update_gl_entries_after(self.posting_date, self.posting_time,
|
update_gl_entries_after(self.posting_date, self.posting_time,
|
||||||
@@ -434,7 +452,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
||||||
"against_voucher_type": self.doctype,
|
"against_voucher_type": self.doctype,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
}, self.party_account_currency)
|
}, self.party_account_currency, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
def make_item_gl_entries(self, gl_entries):
|
def make_item_gl_entries(self, gl_entries):
|
||||||
@@ -784,7 +802,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
||||||
"against_voucher_type": self.doctype,
|
"against_voucher_type": self.doctype,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
}, self.party_account_currency)
|
}, self.party_account_currency, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
@@ -795,7 +813,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
"credit_in_account_currency": self.base_paid_amount \
|
"credit_in_account_currency": self.base_paid_amount \
|
||||||
if bank_account_currency==self.company_currency else self.paid_amount,
|
if bank_account_currency==self.company_currency else self.paid_amount,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
}, bank_account_currency)
|
}, bank_account_currency, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
def make_write_off_gl_entry(self, gl_entries):
|
def make_write_off_gl_entry(self, gl_entries):
|
||||||
@@ -816,7 +834,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
||||||
"against_voucher_type": self.doctype,
|
"against_voucher_type": self.doctype,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
}, self.party_account_currency)
|
}, self.party_account_currency, item=self)
|
||||||
)
|
)
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
self.get_gl_dict({
|
self.get_gl_dict({
|
||||||
@@ -826,11 +844,15 @@ class PurchaseInvoice(BuyingController):
|
|||||||
"credit_in_account_currency": self.base_write_off_amount \
|
"credit_in_account_currency": self.base_write_off_amount \
|
||||||
if write_off_account_currency==self.company_currency else self.write_off_amount,
|
if write_off_account_currency==self.company_currency else self.write_off_amount,
|
||||||
"cost_center": self.cost_center or self.write_off_cost_center
|
"cost_center": self.cost_center or self.write_off_cost_center
|
||||||
})
|
}, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
def make_gle_for_rounding_adjustment(self, gl_entries):
|
def make_gle_for_rounding_adjustment(self, gl_entries):
|
||||||
if self.rounding_adjustment:
|
# if rounding adjustment in small and conversion rate is also small then
|
||||||
|
# base_rounding_adjustment may become zero due to small precision
|
||||||
|
# eg: rounding_adjustment = 0.01 and exchange rate = 0.05 and precision of base_rounding_adjustment is 2
|
||||||
|
# then base_rounding_adjustment becomes zero and error is thrown in GL Entry
|
||||||
|
if self.rounding_adjustment and self.base_rounding_adjustment:
|
||||||
round_off_account, round_off_cost_center = \
|
round_off_account, round_off_cost_center = \
|
||||||
get_round_off_account_and_cost_center(self.company)
|
get_round_off_account_and_cost_center(self.company)
|
||||||
|
|
||||||
@@ -841,8 +863,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
"debit_in_account_currency": self.rounding_adjustment,
|
"debit_in_account_currency": self.rounding_adjustment,
|
||||||
"debit": self.base_rounding_adjustment,
|
"debit": self.base_rounding_adjustment,
|
||||||
"cost_center": self.cost_center or round_off_cost_center,
|
"cost_center": self.cost_center or round_off_cost_center,
|
||||||
}
|
}, item=self))
|
||||||
))
|
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
super(PurchaseInvoice, self).on_cancel()
|
super(PurchaseInvoice, self).on_cancel()
|
||||||
@@ -862,6 +883,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
# because updating ordered qty in bin depends upon updated ordered qty in PO
|
# because updating ordered qty in bin depends upon updated ordered qty in PO
|
||||||
if self.update_stock == 1:
|
if self.update_stock == 1:
|
||||||
self.update_stock_ledger()
|
self.update_stock_ledger()
|
||||||
|
self.delete_auto_created_batches()
|
||||||
|
|
||||||
self.make_gl_entries_on_cancel()
|
self.make_gl_entries_on_cancel()
|
||||||
self.update_project()
|
self.update_project()
|
||||||
@@ -923,9 +945,10 @@ class PurchaseInvoice(BuyingController):
|
|||||||
def on_recurring(self, reference_doc, auto_repeat_doc):
|
def on_recurring(self, reference_doc, auto_repeat_doc):
|
||||||
self.due_date = None
|
self.due_date = None
|
||||||
|
|
||||||
def block_invoice(self, hold_comment=None):
|
def block_invoice(self, hold_comment=None, release_date=None):
|
||||||
self.db_set('on_hold', 1)
|
self.db_set('on_hold', 1)
|
||||||
self.db_set('hold_comment', cstr(hold_comment))
|
self.db_set('hold_comment', cstr(hold_comment))
|
||||||
|
self.db_set('release_date', release_date)
|
||||||
|
|
||||||
def unblock_invoice(self):
|
def unblock_invoice(self):
|
||||||
self.db_set('on_hold', 0)
|
self.db_set('on_hold', 0)
|
||||||
@@ -935,7 +958,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
if not self.apply_tds:
|
if not self.apply_tds:
|
||||||
return
|
return
|
||||||
|
|
||||||
tax_withholding_details = get_party_tax_withholding_details(self)
|
tax_withholding_details = get_party_tax_withholding_details(self, self.tax_withholding_category)
|
||||||
|
|
||||||
if not tax_withholding_details:
|
if not tax_withholding_details:
|
||||||
return
|
return
|
||||||
@@ -957,6 +980,40 @@ class PurchaseInvoice(BuyingController):
|
|||||||
|
|
||||||
# calculate totals again after applying TDS
|
# calculate totals again after applying TDS
|
||||||
self.calculate_taxes_and_totals()
|
self.calculate_taxes_and_totals()
|
||||||
|
|
||||||
|
def set_status(self, update=False, status=None, update_modified=True):
|
||||||
|
if self.is_new():
|
||||||
|
if self.get('amended_from'):
|
||||||
|
self.status = 'Draft'
|
||||||
|
return
|
||||||
|
|
||||||
|
precision = self.precision("outstanding_amount")
|
||||||
|
outstanding_amount = flt(self.outstanding_amount, precision)
|
||||||
|
due_date = getdate(self.due_date)
|
||||||
|
nowdate = getdate()
|
||||||
|
|
||||||
|
if not status:
|
||||||
|
if self.docstatus == 2:
|
||||||
|
status = "Cancelled"
|
||||||
|
elif self.docstatus == 1:
|
||||||
|
if outstanding_amount > 0 and due_date < nowdate:
|
||||||
|
self.status = "Overdue"
|
||||||
|
elif outstanding_amount > 0 and due_date >= nowdate:
|
||||||
|
self.status = "Unpaid"
|
||||||
|
#Check if outstanding amount is 0 due to debit note issued against invoice
|
||||||
|
elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
|
||||||
|
self.status = "Debit Note Issued"
|
||||||
|
elif self.is_return == 1:
|
||||||
|
self.status = "Return"
|
||||||
|
elif outstanding_amount<=0:
|
||||||
|
self.status = "Paid"
|
||||||
|
else:
|
||||||
|
self.status = "Submitted"
|
||||||
|
else:
|
||||||
|
self.status = "Draft"
|
||||||
|
|
||||||
|
if update:
|
||||||
|
self.db_set('status', self.status, update_modified = update_modified)
|
||||||
|
|
||||||
def get_list_context(context=None):
|
def get_list_context(context=None):
|
||||||
from erpnext.controllers.website_list_for_contact import get_list_context
|
from erpnext.controllers.website_list_for_contact import get_list_context
|
||||||
@@ -1009,12 +1066,15 @@ def unblock_invoice(name):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def block_invoice(name, hold_comment):
|
def block_invoice(name, release_date, hold_comment=None):
|
||||||
if frappe.db.exists('Purchase Invoice', name):
|
if frappe.db.exists('Purchase Invoice', name):
|
||||||
pi = frappe.get_doc('Purchase Invoice', name)
|
pi = frappe.get_doc('Purchase Invoice', name)
|
||||||
pi.block_invoice(hold_comment)
|
pi.block_invoice(hold_comment, release_date)
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_inter_company_sales_invoice(source_name, target_doc=None):
|
def make_inter_company_sales_invoice(source_name, target_doc=None):
|
||||||
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction
|
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction
|
||||||
return make_inter_company_transaction("Purchase Invoice", source_name, target_doc)
|
return make_inter_company_transaction("Purchase Invoice", source_name, target_doc)
|
||||||
|
|
||||||
|
def on_doctype_update():
|
||||||
|
frappe.db.add_index("Purchase Invoice", ["supplier", "is_return", "return_against"])
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{% include "erpnext/regional/india/taxes.js" %}
|
||||||
|
|
||||||
|
erpnext.setup_auto_gst_taxation('Purchase Invoice');
|
||||||
@@ -86,6 +86,8 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
pe.submit()
|
pe.submit()
|
||||||
|
|
||||||
pi_doc = frappe.get_doc('Purchase Invoice', pi_doc.name)
|
pi_doc = frappe.get_doc('Purchase Invoice', pi_doc.name)
|
||||||
|
pi_doc.load_from_db()
|
||||||
|
self.assertTrue(pi_doc.status, "Paid")
|
||||||
|
|
||||||
self.assertRaises(frappe.LinkExistsError, pi_doc.cancel)
|
self.assertRaises(frappe.LinkExistsError, pi_doc.cancel)
|
||||||
unlink_payment_on_cancel_of_invoice()
|
unlink_payment_on_cancel_of_invoice()
|
||||||
@@ -203,7 +205,9 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
pi.insert()
|
pi.insert()
|
||||||
pi.submit()
|
pi.submit()
|
||||||
|
pi.load_from_db()
|
||||||
|
|
||||||
|
self.assertTrue(pi.status, "Unpaid")
|
||||||
self.check_gle_for_pi(pi.name)
|
self.check_gle_for_pi(pi.name)
|
||||||
|
|
||||||
def check_gle_for_pi(self, pi):
|
def check_gle_for_pi(self, pi):
|
||||||
@@ -234,6 +238,9 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
pi = frappe.copy_doc(test_records[0])
|
pi = frappe.copy_doc(test_records[0])
|
||||||
pi.insert()
|
pi.insert()
|
||||||
|
pi.load_from_db()
|
||||||
|
|
||||||
|
self.assertTrue(pi.status, "Draft")
|
||||||
pi.naming_series = 'TEST-'
|
pi.naming_series = 'TEST-'
|
||||||
|
|
||||||
self.assertRaises(frappe.CannotChangeConstantError, pi.save)
|
self.assertRaises(frappe.CannotChangeConstantError, pi.save)
|
||||||
@@ -248,6 +255,8 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
pi.get("taxes").pop(1)
|
pi.get("taxes").pop(1)
|
||||||
pi.insert()
|
pi.insert()
|
||||||
pi.submit()
|
pi.submit()
|
||||||
|
pi.load_from_db()
|
||||||
|
self.assertTrue(pi.status, "Unpaid")
|
||||||
|
|
||||||
gl_entries = frappe.db.sql("""select account, debit, credit
|
gl_entries = frappe.db.sql("""select account, debit, credit
|
||||||
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
|
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
|
||||||
@@ -599,6 +608,11 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
# return entry
|
# return entry
|
||||||
pi1 = make_purchase_invoice(is_return=1, return_against=pi.name, qty=-2, rate=50, update_stock=1)
|
pi1 = make_purchase_invoice(is_return=1, return_against=pi.name, qty=-2, rate=50, update_stock=1)
|
||||||
|
|
||||||
|
pi.load_from_db()
|
||||||
|
self.assertTrue(pi.status, "Debit Note Issued")
|
||||||
|
pi1.load_from_db()
|
||||||
|
self.assertTrue(pi1.status, "Return")
|
||||||
|
|
||||||
actual_qty_2 = get_qty_after_transaction()
|
actual_qty_2 = get_qty_after_transaction()
|
||||||
self.assertEqual(actual_qty_1 - 2, actual_qty_2)
|
self.assertEqual(actual_qty_1 - 2, actual_qty_2)
|
||||||
|
|
||||||
@@ -771,6 +785,8 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import get_outstanding_amount
|
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import get_outstanding_amount
|
||||||
|
|
||||||
pi = make_purchase_invoice(item_code = "_Test Item", qty = (5 * -1), rate=500, is_return = 1)
|
pi = make_purchase_invoice(item_code = "_Test Item", qty = (5 * -1), rate=500, is_return = 1)
|
||||||
|
pi.load_from_db()
|
||||||
|
self.assertTrue(pi.status, "Return")
|
||||||
|
|
||||||
outstanding_amount = get_outstanding_amount(pi.doctype,
|
outstanding_amount = get_outstanding_amount(pi.doctype,
|
||||||
pi.name, "Creditors - _TC", pi.supplier, "Supplier")
|
pi.name, "Creditors - _TC", pi.supplier, "Supplier")
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"autoname": "hash",
|
"autoname": "hash",
|
||||||
"creation": "2013-05-22 12:43:10",
|
"creation": "2013-05-22 12:43:10",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@@ -197,7 +198,6 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "UOM",
|
"label": "UOM",
|
||||||
"options": "UOM",
|
"options": "UOM",
|
||||||
"print_hide": 1,
|
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -507,7 +507,8 @@
|
|||||||
"depends_on": "enable_deferred_expense",
|
"depends_on": "enable_deferred_expense",
|
||||||
"fieldname": "service_stop_date",
|
"fieldname": "service_stop_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "Service Stop Date"
|
"label": "Service Stop Date",
|
||||||
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
@@ -523,13 +524,15 @@
|
|||||||
"depends_on": "enable_deferred_expense",
|
"depends_on": "enable_deferred_expense",
|
||||||
"fieldname": "service_start_date",
|
"fieldname": "service_start_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "Service Start Date"
|
"label": "Service Start Date",
|
||||||
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "enable_deferred_expense",
|
"depends_on": "enable_deferred_expense",
|
||||||
"fieldname": "service_end_date",
|
"fieldname": "service_end_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "Service End Date"
|
"label": "Service End Date",
|
||||||
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "reference",
|
"fieldname": "reference",
|
||||||
@@ -750,14 +753,13 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "manufacturer_part_no",
|
"fieldname": "manufacturer_part_no",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Manufacturer Part Number",
|
"label": "Manufacturer Part Number"
|
||||||
"read_only": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "is_fixed_asset",
|
"depends_on": "is_fixed_asset",
|
||||||
"fetch_from": "item_code.asset_category",
|
"fetch_from": "item_code.asset_category",
|
||||||
"fieldname": "asset_category",
|
"fieldname": "asset_category",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Link",
|
||||||
"in_preview": 1,
|
"in_preview": 1,
|
||||||
"label": "Asset Category",
|
"label": "Asset Category",
|
||||||
"options": "Asset Category",
|
"options": "Asset Category",
|
||||||
@@ -766,7 +768,8 @@
|
|||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2019-11-21 16:27:52.043744",
|
"links": [],
|
||||||
|
"modified": "2020-04-07 18:34:35.104178",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Invoice Item",
|
"name": "Purchase Invoice Item",
|
||||||
|
|||||||
@@ -1,300 +1,108 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"allow_import": 1,
|
||||||
"allow_import": 1,
|
"allow_rename": 1,
|
||||||
"allow_rename": 1,
|
"creation": "2013-01-10 16:34:08",
|
||||||
"autoname": "field:title",
|
"description": "Standard tax template that can be applied to all Purchase Transactions. This template can contain list of tax heads and also other expense heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Consider Tax or Charge for: In this section you can specify if the tax / charge is only for valuation (not a part of total) or only for total (does not add value to the item) or for both.\n10. Add or Deduct: Whether you want to add or deduct the tax.",
|
||||||
"beta": 0,
|
"doctype": "DocType",
|
||||||
"creation": "2013-01-10 16:34:08",
|
"document_type": "Setup",
|
||||||
"custom": 0,
|
"field_order": [
|
||||||
"description": "Standard tax template that can be applied to all Purchase Transactions. This template can contain list of tax heads and also other expense heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Consider Tax or Charge for: In this section you can specify if the tax / charge is only for valuation (not a part of total) or only for total (does not add value to the item) or for both.\n10. Add or Deduct: Whether you want to add or deduct the tax.",
|
"title",
|
||||||
"docstatus": 0,
|
"is_default",
|
||||||
"doctype": "DocType",
|
"disabled",
|
||||||
"document_type": "Setup",
|
"column_break4",
|
||||||
"editable_grid": 0,
|
"company",
|
||||||
|
"tax_category",
|
||||||
|
"section_break6",
|
||||||
|
"taxes"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "title",
|
||||||
"bold": 0,
|
"fieldtype": "Data",
|
||||||
"collapsible": 0,
|
"label": "Title",
|
||||||
"columns": 0,
|
"no_copy": 1,
|
||||||
"fieldname": "title",
|
"oldfieldname": "title",
|
||||||
"fieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
"hidden": 0,
|
"reqd": 1
|
||||||
"ignore_user_permissions": 0,
|
},
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 1,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Title",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
|
||||||
"oldfieldname": "title",
|
|
||||||
"oldfieldtype": "Data",
|
|
||||||
"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,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"default": "0",
|
||||||
"bold": 0,
|
"fieldname": "is_default",
|
||||||
"collapsible": 0,
|
"fieldtype": "Check",
|
||||||
"columns": 0,
|
"in_list_view": 1,
|
||||||
"fieldname": "is_default",
|
"label": "Default"
|
||||||
"fieldtype": "Check",
|
},
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Default",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"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,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"default": "0",
|
||||||
"bold": 0,
|
"fieldname": "disabled",
|
||||||
"collapsible": 0,
|
"fieldtype": "Check",
|
||||||
"columns": 0,
|
"in_list_view": 1,
|
||||||
"fieldname": "disabled",
|
"label": "Disabled"
|
||||||
"fieldtype": "Check",
|
},
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Disabled",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "column_break4",
|
||||||
"bold": 0,
|
"fieldtype": "Column Break"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "column_break4",
|
|
||||||
"fieldtype": "Column Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "company",
|
||||||
"bold": 0,
|
"fieldtype": "Link",
|
||||||
"collapsible": 0,
|
"in_list_view": 1,
|
||||||
"columns": 0,
|
"in_standard_filter": 1,
|
||||||
"fieldname": "company",
|
"label": "Company",
|
||||||
"fieldtype": "Link",
|
"options": "Company",
|
||||||
"hidden": 0,
|
"remember_last_selected_value": 1,
|
||||||
"ignore_user_permissions": 0,
|
"reqd": 1
|
||||||
"ignore_xss_filter": 0,
|
},
|
||||||
"in_filter": 1,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 1,
|
|
||||||
"label": "Company",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Company",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 1,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "section_break6",
|
||||||
"bold": 0,
|
"fieldtype": "Section Break"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "section_break6",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "taxes",
|
||||||
"bold": 0,
|
"fieldtype": "Table",
|
||||||
"collapsible": 0,
|
"label": "Purchase Taxes and Charges",
|
||||||
"columns": 0,
|
"oldfieldname": "purchase_tax_details",
|
||||||
"fieldname": "taxes",
|
"oldfieldtype": "Table",
|
||||||
"fieldtype": "Table",
|
"options": "Purchase Taxes and Charges"
|
||||||
"hidden": 0,
|
},
|
||||||
"ignore_user_permissions": 0,
|
{
|
||||||
"ignore_xss_filter": 0,
|
"fieldname": "tax_category",
|
||||||
"in_filter": 0,
|
"fieldtype": "Link",
|
||||||
"in_list_view": 0,
|
"label": "Tax Category",
|
||||||
"in_standard_filter": 0,
|
"options": "Tax Category"
|
||||||
"label": "Purchase Taxes and Charges",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "purchase_tax_details",
|
|
||||||
"oldfieldtype": "Table",
|
|
||||||
"options": "Purchase Taxes and Charges",
|
|
||||||
"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,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hide_heading": 0,
|
"icon": "fa fa-money",
|
||||||
"hide_toolbar": 0,
|
"idx": 1,
|
||||||
"icon": "fa fa-money",
|
"modified": "2019-11-25 13:05:26.220275",
|
||||||
"idx": 1,
|
"modified_by": "Administrator",
|
||||||
"image_view": 0,
|
"module": "Accounts",
|
||||||
"in_create": 0,
|
"name": "Purchase Taxes and Charges Template",
|
||||||
|
"owner": "wasim@webnotestech.com",
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 0,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2016-11-07 05:18:44.095798",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "Accounts",
|
|
||||||
"name": "Purchase Taxes and Charges Template",
|
|
||||||
"owner": "wasim@webnotestech.com",
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"email": 1,
|
||||||
"apply_user_permissions": 0,
|
"print": 1,
|
||||||
"cancel": 0,
|
"read": 1,
|
||||||
"create": 0,
|
"report": 1,
|
||||||
"delete": 0,
|
"role": "Purchase Manager"
|
||||||
"email": 1,
|
},
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"is_custom": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Purchase Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 0,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"apply_user_permissions": 0,
|
"delete": 1,
|
||||||
"cancel": 0,
|
"email": 1,
|
||||||
"create": 1,
|
"print": 1,
|
||||||
"delete": 1,
|
"read": 1,
|
||||||
"email": 1,
|
"report": 1,
|
||||||
"export": 0,
|
"role": "Purchase Master Manager",
|
||||||
"if_owner": 0,
|
"share": 1,
|
||||||
"import": 0,
|
|
||||||
"is_custom": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Purchase Master Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"read": 1,
|
||||||
"apply_user_permissions": 0,
|
"role": "Purchase User"
|
||||||
"cancel": 0,
|
|
||||||
"create": 0,
|
|
||||||
"delete": 0,
|
|
||||||
"email": 0,
|
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"is_custom": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 0,
|
|
||||||
"read": 1,
|
|
||||||
"report": 0,
|
|
||||||
"role": "Purchase User",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 0,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
"sort_order": "DESC",
|
||||||
"read_only": 0,
|
"track_changes": 1
|
||||||
"read_only_onload": 0,
|
|
||||||
"sort_order": "DESC",
|
|
||||||
"track_seen": 0
|
|
||||||
}
|
}
|
||||||
@@ -152,8 +152,11 @@ def update_multi_mode_option(doc, pos_profile):
|
|||||||
|
|
||||||
|
|
||||||
def get_mode_of_payment(doc):
|
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, \
|
return frappe.db.sql("""
|
||||||
`tabMode of Payment` mp where mpa.parent = mp.name and mpa.company = %(company)s""", {'company': doc.company}, as_dict=1)
|
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)
|
||||||
|
|
||||||
|
|
||||||
def update_tax_table(doc):
|
def update_tax_table(doc):
|
||||||
@@ -177,14 +180,16 @@ def get_items_list(pos_profile, company):
|
|||||||
i.name, i.item_code, i.item_name, i.description, i.item_group, i.has_batch_no,
|
i.name, i.item_code, i.item_name, i.description, i.item_group, i.has_batch_no,
|
||||||
i.has_serial_no, i.is_stock_item, i.brand, i.stock_uom, i.image,
|
i.has_serial_no, i.is_stock_item, i.brand, i.stock_uom, i.image,
|
||||||
id.expense_account, id.selling_cost_center, id.default_warehouse,
|
id.expense_account, id.selling_cost_center, id.default_warehouse,
|
||||||
i.sales_uom, c.conversion_factor
|
i.sales_uom, c.conversion_factor, it.item_tax_template, it.valid_from
|
||||||
from
|
from
|
||||||
`tabItem` i
|
`tabItem` i
|
||||||
left join `tabItem Default` id on id.parent = i.name and id.company = %s
|
left join `tabItem Default` id on id.parent = i.name and id.company = %s
|
||||||
|
left join `tabItem Tax` it on it.parent = i.name
|
||||||
left join `tabUOM Conversion Detail` c on i.name = c.parent and i.sales_uom = c.uom
|
left join `tabUOM Conversion Detail` c on i.name = c.parent and i.sales_uom = c.uom
|
||||||
where
|
where
|
||||||
i.disabled = 0 and i.has_variants = 0 and i.is_sales_item = 1
|
i.disabled = 0 and i.has_variants = 0 and i.is_sales_item = 1
|
||||||
{cond}
|
{cond}
|
||||||
|
group by i.item_code
|
||||||
""".format(cond=cond), tuple([company] + args_list), as_dict=1)
|
""".format(cond=cond), tuple([company] + args_list), as_dict=1)
|
||||||
|
|
||||||
|
|
||||||
@@ -204,7 +209,7 @@ def get_customers_list(pos_profile={}):
|
|||||||
if pos_profile.get('customer_groups'):
|
if pos_profile.get('customer_groups'):
|
||||||
# Get customers based on the customer groups defined in the POS profile
|
# Get customers based on the customer groups defined in the POS profile
|
||||||
for d in pos_profile.get('customer_groups'):
|
for d in pos_profile.get('customer_groups'):
|
||||||
customer_groups.extend([d.name for d in get_child_nodes('Customer Group', d.customer_group)])
|
customer_groups.extend([d.get('name') for d in get_child_nodes('Customer Group', d.get('customer_group'))])
|
||||||
cond = "customer_group in (%s)" % (', '.join(['%s'] * len(customer_groups)))
|
cond = "customer_group in (%s)" % (', '.join(['%s'] * len(customer_groups)))
|
||||||
|
|
||||||
return frappe.db.sql(""" select name, customer_name, customer_group,
|
return frappe.db.sql(""" select name, customer_name, customer_group,
|
||||||
@@ -384,7 +389,9 @@ def get_pricing_rule_data(doc):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_invoice(doc_list={}, email_queue_list={}, customers_list={}):
|
def make_invoice(pos_profile, doc_list={}, email_queue_list={}, customers_list={}):
|
||||||
|
import json
|
||||||
|
|
||||||
if isinstance(doc_list, string_types):
|
if isinstance(doc_list, string_types):
|
||||||
doc_list = json.loads(doc_list)
|
doc_list = json.loads(doc_list)
|
||||||
|
|
||||||
@@ -418,7 +425,11 @@ def make_invoice(doc_list={}, email_queue_list={}, customers_list={}):
|
|||||||
name_list.append(name)
|
name_list.append(name)
|
||||||
|
|
||||||
email_queue = make_email_queue(email_queue_list)
|
email_queue = make_email_queue(email_queue_list)
|
||||||
customers = get_customers_list()
|
|
||||||
|
if isinstance(pos_profile, string_types):
|
||||||
|
pos_profile = json.loads(pos_profile)
|
||||||
|
|
||||||
|
customers = get_customers_list(pos_profile)
|
||||||
return {
|
return {
|
||||||
'invoice': name_list,
|
'invoice': name_list,
|
||||||
'email_queue': email_queue,
|
'email_queue': email_queue,
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
{% include "erpnext/regional/india/taxes.js" %}
|
||||||
|
|
||||||
|
erpnext.setup_auto_gst_taxation('Sales Invoice');
|
||||||
|
|
||||||
frappe.ui.form.on("Sales Invoice", {
|
frappe.ui.form.on("Sales Invoice", {
|
||||||
setup: function(frm) {
|
setup: function(frm) {
|
||||||
frm.set_query('transporter', function() {
|
frm.set_query('transporter', function() {
|
||||||
@@ -21,18 +25,29 @@ frappe.ui.form.on("Sales Invoice", {
|
|||||||
if(frm.doc.docstatus == 1 && !frm.is_dirty()
|
if(frm.doc.docstatus == 1 && !frm.is_dirty()
|
||||||
&& !frm.doc.is_return && !frm.doc.ewaybill) {
|
&& !frm.doc.is_return && !frm.doc.ewaybill) {
|
||||||
|
|
||||||
frm.add_custom_button('e-Way Bill JSON', () => {
|
frm.add_custom_button('E-Way Bill JSON', () => {
|
||||||
var w = window.open(
|
frappe.call({
|
||||||
frappe.urllib.get_full_url(
|
method: 'erpnext.regional.india.utils.generate_ewb_json',
|
||||||
"/api/method/erpnext.regional.india.utils.generate_ewb_json?"
|
args: {
|
||||||
+ "dt=" + encodeURIComponent(frm.doc.doctype)
|
'dt': frm.doc.doctype,
|
||||||
+ "&dn=" + encodeURIComponent(frm.doc.name)
|
'dn': [frm.doc.name]
|
||||||
)
|
},
|
||||||
);
|
callback: function(r) {
|
||||||
if (!w) {
|
if (r.message) {
|
||||||
frappe.msgprint(__("Please enable pop-ups")); return;
|
const args = {
|
||||||
}
|
cmd: 'erpnext.regional.india.utils.download_ewb_json',
|
||||||
}, __("Make"));
|
data: r.message,
|
||||||
|
docname: frm.doc.name
|
||||||
|
};
|
||||||
|
open_url_post(frappe.request.url, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}, __("Create"));
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,17 +16,23 @@ frappe.listview_settings['Sales Invoice'].onload = function (doclist) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var w = window.open(
|
frappe.call({
|
||||||
frappe.urllib.get_full_url(
|
method: 'erpnext.regional.india.utils.generate_ewb_json',
|
||||||
"/api/method/erpnext.regional.india.utils.generate_ewb_json?"
|
args: {
|
||||||
+ "dt=" + encodeURIComponent(doclist.doctype)
|
'dt': doclist.doctype,
|
||||||
+ "&dn=" + encodeURIComponent(docnames)
|
'dn': docnames
|
||||||
)
|
},
|
||||||
);
|
callback: function(r) {
|
||||||
if (!w) {
|
if (r.message) {
|
||||||
frappe.msgprint(__("Please enable pop-ups")); return;
|
const args = {
|
||||||
}
|
cmd: 'erpnext.regional.india.utils.download_ewb_json',
|
||||||
|
data: r.message,
|
||||||
|
docname: docnames
|
||||||
|
};
|
||||||
|
open_url_post(frappe.request.url, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
doclist.page.add_actions_menu_item(__('Generate e-Way Bill JSON'), action, false);
|
doclist.page.add_actions_menu_item(__('Generate e-Way Bill JSON'), action, false);
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
|||||||
me.frm.script_manager.trigger("is_pos");
|
me.frm.script_manager.trigger("is_pos");
|
||||||
me.frm.refresh_fields();
|
me.frm.refresh_fields();
|
||||||
}
|
}
|
||||||
|
erpnext.queries.setup_warehouse_query(this.frm);
|
||||||
},
|
},
|
||||||
|
|
||||||
refresh: function(doc, dt, dn) {
|
refresh: function(doc, dt, dn) {
|
||||||
@@ -556,22 +557,11 @@ cur_frm.cscript.cost_center = function(doc, cdt, cdn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cur_frm.set_query("debit_to", function(doc) {
|
cur_frm.set_query("debit_to", function(doc) {
|
||||||
// filter on Account
|
return {
|
||||||
if (doc.customer) {
|
filters: {
|
||||||
return {
|
'account_type': 'Receivable',
|
||||||
filters: {
|
'is_group': 0,
|
||||||
'account_type': 'Receivable',
|
'company': doc.company
|
||||||
'is_group': 0,
|
|
||||||
'company': doc.company
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
filters: {
|
|
||||||
'report_type': 'Balance Sheet',
|
|
||||||
'is_group': 0,
|
|
||||||
'company': doc.company
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -597,7 +587,9 @@ frappe.ui.form.on('Sales Invoice', {
|
|||||||
frm.set_query("account_for_change_amount", function() {
|
frm.set_query("account_for_change_amount", function() {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
account_type: ['in', ["Cash", "Bank"]]
|
account_type: ['in', ["Cash", "Bank"]],
|
||||||
|
company: frm.doc.company,
|
||||||
|
is_group: 0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -678,7 +670,8 @@ frappe.ui.form.on('Sales Invoice', {
|
|||||||
frm.fields_dict["loyalty_redemption_account"].get_query = function() {
|
frm.fields_dict["loyalty_redemption_account"].get_query = function() {
|
||||||
return {
|
return {
|
||||||
filters:{
|
filters:{
|
||||||
"company": frm.doc.company
|
"company": frm.doc.company,
|
||||||
|
"is_group": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -687,7 +680,8 @@ frappe.ui.form.on('Sales Invoice', {
|
|||||||
frm.fields_dict["loyalty_redemption_cost_center"].get_query = function() {
|
frm.fields_dict["loyalty_redemption_cost_center"].get_query = function() {
|
||||||
return {
|
return {
|
||||||
filters:{
|
filters:{
|
||||||
"company": frm.doc.company
|
"company": frm.doc.company,
|
||||||
|
"is_group": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -697,8 +691,8 @@ frappe.ui.form.on('Sales Invoice', {
|
|||||||
if (frm.doc.company)
|
if (frm.doc.company)
|
||||||
{
|
{
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method:"frappe.contacts.doctype.address.address.get_default_address",
|
method:"erpnext.setup.doctype.company.company.get_default_company_address",
|
||||||
args:{ doctype:'Company',name:frm.doc.company},
|
args:{name:frm.doc.company, existing_address: frm.doc.company_address},
|
||||||
callback: function(r){
|
callback: function(r){
|
||||||
if (r.message){
|
if (r.message){
|
||||||
frm.set_value("company_address",r.message)
|
frm.set_value("company_address",r.message)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"autoname": "naming_series:",
|
"autoname": "naming_series:",
|
||||||
"creation": "2013-05-24 19:29:05",
|
"creation": "2013-05-24 19:29:05",
|
||||||
@@ -74,9 +75,9 @@
|
|||||||
"base_total",
|
"base_total",
|
||||||
"base_net_total",
|
"base_net_total",
|
||||||
"column_break_32",
|
"column_break_32",
|
||||||
|
"total_net_weight",
|
||||||
"total",
|
"total",
|
||||||
"net_total",
|
"net_total",
|
||||||
"total_net_weight",
|
|
||||||
"taxes_section",
|
"taxes_section",
|
||||||
"taxes_and_charges",
|
"taxes_and_charges",
|
||||||
"column_break_38",
|
"column_break_38",
|
||||||
@@ -148,9 +149,9 @@
|
|||||||
"edit_printing_settings",
|
"edit_printing_settings",
|
||||||
"letter_head",
|
"letter_head",
|
||||||
"group_same_items",
|
"group_same_items",
|
||||||
"language",
|
|
||||||
"column_break_84",
|
|
||||||
"select_print_heading",
|
"select_print_heading",
|
||||||
|
"column_break_84",
|
||||||
|
"language",
|
||||||
"more_information",
|
"more_information",
|
||||||
"inter_company_invoice_reference",
|
"inter_company_invoice_reference",
|
||||||
"customer_group",
|
"customer_group",
|
||||||
@@ -372,7 +373,8 @@
|
|||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Sales Invoice",
|
"options": "Sales Invoice",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1,
|
||||||
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_21",
|
"fieldname": "column_break_21",
|
||||||
@@ -395,7 +397,7 @@
|
|||||||
{
|
{
|
||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
"fieldname": "po_no",
|
"fieldname": "po_no",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Small Text",
|
||||||
"label": "Customer's Purchase Order",
|
"label": "Customer's Purchase Order",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"print_hide": 1
|
"print_hide": 1
|
||||||
@@ -774,7 +776,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "other_charges_calculation",
|
"fieldname": "other_charges_calculation",
|
||||||
"fieldtype": "Text",
|
"fieldtype": "Long Text",
|
||||||
"label": "Taxes and Charges Calculation",
|
"label": "Taxes and Charges Calculation",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldtype": "HTML",
|
"oldfieldtype": "HTML",
|
||||||
@@ -1567,7 +1569,8 @@
|
|||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
"idx": 181,
|
"idx": 181,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2019-10-05 21:39:49.235990",
|
"links": [],
|
||||||
|
"modified": "2020-05-19 17:00:57.208696",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice",
|
"name": "Sales Invoice",
|
||||||
|
|||||||
@@ -413,6 +413,9 @@ class SalesInvoice(SellingController):
|
|||||||
if pos:
|
if pos:
|
||||||
self.allow_print_before_pay = pos.allow_print_before_pay
|
self.allow_print_before_pay = pos.allow_print_before_pay
|
||||||
|
|
||||||
|
if not for_validate:
|
||||||
|
self.tax_category = pos.get("tax_category")
|
||||||
|
|
||||||
if not for_validate and not self.customer:
|
if not for_validate and not self.customer:
|
||||||
self.customer = pos.customer
|
self.customer = pos.customer
|
||||||
|
|
||||||
@@ -426,13 +429,18 @@ class SalesInvoice(SellingController):
|
|||||||
if (not for_validate) or (for_validate and not self.get(fieldname)):
|
if (not for_validate) or (for_validate and not self.get(fieldname)):
|
||||||
self.set(fieldname, pos.get(fieldname))
|
self.set(fieldname, pos.get(fieldname))
|
||||||
|
|
||||||
customer_price_list = frappe.get_value("Customer", self.customer, 'default_price_list')
|
|
||||||
|
|
||||||
if pos.get("company_address"):
|
if pos.get("company_address"):
|
||||||
self.company_address = pos.get("company_address")
|
self.company_address = pos.get("company_address")
|
||||||
|
|
||||||
if not customer_price_list:
|
if self.customer:
|
||||||
self.set('selling_price_list', pos.get('selling_price_list'))
|
customer_price_list, customer_group = frappe.get_value("Customer", self.customer, ['default_price_list', 'customer_group'])
|
||||||
|
customer_group_price_list = frappe.get_value("Customer Group", customer_group, 'default_price_list')
|
||||||
|
selling_price_list = customer_price_list or customer_group_price_list or pos.get('selling_price_list')
|
||||||
|
else:
|
||||||
|
selling_price_list = pos.get('selling_price_list')
|
||||||
|
|
||||||
|
if selling_price_list:
|
||||||
|
self.set('selling_price_list', selling_price_list)
|
||||||
|
|
||||||
if not for_validate:
|
if not for_validate:
|
||||||
self.update_stock = cint(pos.get("update_stock"))
|
self.update_stock = cint(pos.get("update_stock"))
|
||||||
@@ -463,13 +471,17 @@ class SalesInvoice(SellingController):
|
|||||||
["account_type", "report_type", "account_currency"], as_dict=True)
|
["account_type", "report_type", "account_currency"], as_dict=True)
|
||||||
|
|
||||||
if not account:
|
if not account:
|
||||||
frappe.throw(_("Debit To is required"))
|
frappe.throw(_("Debit To is required"), title=_("Account Missing"))
|
||||||
|
|
||||||
if account.report_type != "Balance Sheet":
|
if account.report_type != "Balance Sheet":
|
||||||
frappe.throw(_("Debit To account must be a Balance Sheet account"))
|
frappe.throw(_("Please ensure {} account is a Balance Sheet account. \
|
||||||
|
You can change the parent account to a Balance Sheet account or select a different account.")
|
||||||
|
.format(frappe.bold("Debit To")), title=_("Invalid Account"))
|
||||||
|
|
||||||
if self.customer and account.account_type != "Receivable":
|
if self.customer and account.account_type != "Receivable":
|
||||||
frappe.throw(_("Debit To account must be a Receivable account"))
|
frappe.throw(_("Please ensure {} account is a Receivable account. \
|
||||||
|
Change the account type to Receivable or select a different account.")
|
||||||
|
.format(frappe.bold("Debit To")), title=_("Invalid Account"))
|
||||||
|
|
||||||
self.party_account_currency = account.account_currency
|
self.party_account_currency = account.account_currency
|
||||||
|
|
||||||
@@ -531,14 +543,18 @@ class SalesInvoice(SellingController):
|
|||||||
"""check in manage account if sales order / delivery note required or not."""
|
"""check in manage account if sales order / delivery note required or not."""
|
||||||
if self.is_return:
|
if self.is_return:
|
||||||
return
|
return
|
||||||
dic = {'Sales Order':['so_required', 'is_pos'],'Delivery Note':['dn_required', 'update_stock']}
|
|
||||||
for i in dic:
|
prev_doc_field_map = {'Sales Order': ['so_required', 'is_pos'],'Delivery Note': ['dn_required', 'update_stock']}
|
||||||
if frappe.db.get_single_value('Selling Settings', dic[i][0]) == 'Yes':
|
for key, value in iteritems(prev_doc_field_map):
|
||||||
|
if frappe.db.get_single_value('Selling Settings', value[0]) == 'Yes':
|
||||||
|
|
||||||
|
if frappe.get_value('Customer', self.customer, value[0]):
|
||||||
|
continue
|
||||||
|
|
||||||
for d in self.get('items'):
|
for d in self.get('items'):
|
||||||
is_stock_item = frappe.get_cached_value('Item', d.item_code, 'is_stock_item')
|
is_stock_item = frappe.get_cached_value('Item', d.item_code, 'is_stock_item')
|
||||||
if (d.item_code and is_stock_item == 1\
|
if (d.item_code and is_stock_item ==1 and not d.get(key.lower().replace(' ', '_')) and not self.get(value[1])):
|
||||||
and not d.get(i.lower().replace(' ','_')) and not self.get(dic[i][1])):
|
msgprint(_("{0} is mandatory for Item {1}").format(key, d.item_code), raise_exception=1)
|
||||||
msgprint(_("{0} is mandatory for Item {1}").format(i,d.item_code), raise_exception=1)
|
|
||||||
|
|
||||||
|
|
||||||
def validate_proj_cust(self):
|
def validate_proj_cust(self):
|
||||||
@@ -714,7 +730,7 @@ class SalesInvoice(SellingController):
|
|||||||
update_outstanding_amt(self.debit_to, "Customer", self.customer,
|
update_outstanding_amt(self.debit_to, "Customer", self.customer,
|
||||||
self.doctype, self.return_against if cint(self.is_return) and self.return_against else self.name)
|
self.doctype, self.return_against if cint(self.is_return) and self.return_against else self.name)
|
||||||
|
|
||||||
if repost_future_gle and cint(self.update_stock) \
|
if (repost_future_gle or self.flags.repost_future_gle) and cint(self.update_stock) \
|
||||||
and cint(auto_accounting_for_stock):
|
and cint(auto_accounting_for_stock):
|
||||||
items, warehouses = self.get_items_and_warehouses()
|
items, warehouses = self.get_items_and_warehouses()
|
||||||
update_gl_entries_after(self.posting_date, self.posting_time,
|
update_gl_entries_after(self.posting_date, self.posting_time,
|
||||||
@@ -769,7 +785,7 @@ class SalesInvoice(SellingController):
|
|||||||
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
||||||
"against_voucher_type": self.doctype,
|
"against_voucher_type": self.doctype,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
}, self.party_account_currency)
|
}, self.party_account_currency, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
def make_tax_gl_entries(self, gl_entries):
|
def make_tax_gl_entries(self, gl_entries):
|
||||||
@@ -786,7 +802,7 @@ class SalesInvoice(SellingController):
|
|||||||
tax.precision("base_tax_amount_after_discount_amount")) if account_currency==self.company_currency else
|
tax.precision("base_tax_amount_after_discount_amount")) if account_currency==self.company_currency else
|
||||||
flt(tax.tax_amount_after_discount_amount, tax.precision("tax_amount_after_discount_amount"))),
|
flt(tax.tax_amount_after_discount_amount, tax.precision("tax_amount_after_discount_amount"))),
|
||||||
"cost_center": tax.cost_center
|
"cost_center": tax.cost_center
|
||||||
}, account_currency)
|
}, account_currency, item=tax)
|
||||||
)
|
)
|
||||||
|
|
||||||
def make_item_gl_entries(self, gl_entries):
|
def make_item_gl_entries(self, gl_entries):
|
||||||
@@ -806,7 +822,7 @@ class SalesInvoice(SellingController):
|
|||||||
|
|
||||||
for gle in fixed_asset_gl_entries:
|
for gle in fixed_asset_gl_entries:
|
||||||
gle["against"] = self.customer
|
gle["against"] = self.customer
|
||||||
gl_entries.append(self.get_gl_dict(gle))
|
gl_entries.append(self.get_gl_dict(gle, item=item))
|
||||||
|
|
||||||
asset.db_set("disposal_date", self.posting_date)
|
asset.db_set("disposal_date", self.posting_date)
|
||||||
asset.set_status("Sold" if self.docstatus==1 else None)
|
asset.set_status("Sold" if self.docstatus==1 else None)
|
||||||
@@ -844,7 +860,7 @@ class SalesInvoice(SellingController):
|
|||||||
"against_voucher": self.return_against if cint(self.is_return) else self.name,
|
"against_voucher": self.return_against if cint(self.is_return) else self.name,
|
||||||
"against_voucher_type": self.doctype,
|
"against_voucher_type": self.doctype,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
})
|
}, item=self)
|
||||||
)
|
)
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
self.get_gl_dict({
|
self.get_gl_dict({
|
||||||
@@ -853,7 +869,7 @@ class SalesInvoice(SellingController):
|
|||||||
"against": self.customer,
|
"against": self.customer,
|
||||||
"debit": self.loyalty_amount,
|
"debit": self.loyalty_amount,
|
||||||
"remark": "Loyalty Points redeemed by the customer"
|
"remark": "Loyalty Points redeemed by the customer"
|
||||||
})
|
}, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
def make_pos_gl_entries(self, gl_entries):
|
def make_pos_gl_entries(self, gl_entries):
|
||||||
@@ -874,7 +890,7 @@ class SalesInvoice(SellingController):
|
|||||||
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
||||||
"against_voucher_type": self.doctype,
|
"against_voucher_type": self.doctype,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
}, self.party_account_currency)
|
}, self.party_account_currency, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
payment_mode_account_currency = get_account_currency(payment_mode.account)
|
payment_mode_account_currency = get_account_currency(payment_mode.account)
|
||||||
@@ -887,7 +903,7 @@ class SalesInvoice(SellingController):
|
|||||||
if payment_mode_account_currency==self.company_currency \
|
if payment_mode_account_currency==self.company_currency \
|
||||||
else payment_mode.amount,
|
else payment_mode.amount,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
}, payment_mode_account_currency)
|
}, payment_mode_account_currency, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
def make_gle_for_change_amount(self, gl_entries):
|
def make_gle_for_change_amount(self, gl_entries):
|
||||||
@@ -905,7 +921,7 @@ class SalesInvoice(SellingController):
|
|||||||
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
||||||
"against_voucher_type": self.doctype,
|
"against_voucher_type": self.doctype,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
}, self.party_account_currency)
|
}, self.party_account_currency, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
@@ -914,7 +930,7 @@ class SalesInvoice(SellingController):
|
|||||||
"against": self.customer,
|
"against": self.customer,
|
||||||
"credit": self.base_change_amount,
|
"credit": self.base_change_amount,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
})
|
}, item=self)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
frappe.throw(_("Select change amount account"), title="Mandatory Field")
|
frappe.throw(_("Select change amount account"), title="Mandatory Field")
|
||||||
@@ -938,7 +954,7 @@ class SalesInvoice(SellingController):
|
|||||||
"against_voucher": self.return_against if cint(self.is_return) else self.name,
|
"against_voucher": self.return_against if cint(self.is_return) else self.name,
|
||||||
"against_voucher_type": self.doctype,
|
"against_voucher_type": self.doctype,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
}, self.party_account_currency)
|
}, self.party_account_currency, item=self)
|
||||||
)
|
)
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
self.get_gl_dict({
|
self.get_gl_dict({
|
||||||
@@ -949,11 +965,11 @@ class SalesInvoice(SellingController):
|
|||||||
self.precision("base_write_off_amount")) if write_off_account_currency==self.company_currency
|
self.precision("base_write_off_amount")) if write_off_account_currency==self.company_currency
|
||||||
else flt(self.write_off_amount, self.precision("write_off_amount"))),
|
else flt(self.write_off_amount, self.precision("write_off_amount"))),
|
||||||
"cost_center": self.cost_center or self.write_off_cost_center or default_cost_center
|
"cost_center": self.cost_center or self.write_off_cost_center or default_cost_center
|
||||||
}, write_off_account_currency)
|
}, write_off_account_currency, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
def make_gle_for_rounding_adjustment(self, gl_entries):
|
def make_gle_for_rounding_adjustment(self, gl_entries):
|
||||||
if flt(self.rounding_adjustment, self.precision("rounding_adjustment")):
|
if flt(self.rounding_adjustment, self.precision("rounding_adjustment")) and self.base_rounding_adjustment:
|
||||||
round_off_account, round_off_cost_center = \
|
round_off_account, round_off_cost_center = \
|
||||||
get_round_off_account_and_cost_center(self.company)
|
get_round_off_account_and_cost_center(self.company)
|
||||||
|
|
||||||
@@ -966,8 +982,7 @@ class SalesInvoice(SellingController):
|
|||||||
"credit": flt(self.base_rounding_adjustment,
|
"credit": flt(self.base_rounding_adjustment,
|
||||||
self.precision("base_rounding_adjustment")),
|
self.precision("base_rounding_adjustment")),
|
||||||
"cost_center": self.cost_center or round_off_cost_center,
|
"cost_center": self.cost_center or round_off_cost_center,
|
||||||
}
|
}, item=self))
|
||||||
))
|
|
||||||
|
|
||||||
def update_billing_status_in_dn(self, update_modified=True):
|
def update_billing_status_in_dn(self, update_modified=True):
|
||||||
updated_delivery_notes = []
|
updated_delivery_notes = []
|
||||||
@@ -1048,13 +1063,18 @@ class SalesInvoice(SellingController):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
for serial_no in item.serial_no.split("\n"):
|
for serial_no in item.serial_no.split("\n"):
|
||||||
sales_invoice, item_code = frappe.db.get_value("Serial No", serial_no,
|
serial_no_details = frappe.db.get_value("Serial No", serial_no,
|
||||||
["sales_invoice", "item_code"])
|
["sales_invoice", "item_code"], as_dict=1)
|
||||||
if sales_invoice and item_code == item.item_code and self.name != sales_invoice:
|
|
||||||
sales_invoice_company = frappe.db.get_value("Sales Invoice", sales_invoice, "company")
|
if not serial_no_details:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if serial_no_details.sales_invoice and serial_no_details.item_code == item.item_code \
|
||||||
|
and self.name != serial_no_details.sales_invoice:
|
||||||
|
sales_invoice_company = frappe.db.get_value("Sales Invoice", serial_no_details.sales_invoice, "company")
|
||||||
if sales_invoice_company == self.company:
|
if sales_invoice_company == self.company:
|
||||||
frappe.throw(_("Serial Number: {0} is already referenced in Sales Invoice: {1}"
|
frappe.throw(_("Serial Number: {0} is already referenced in Sales Invoice: {1}"
|
||||||
.format(serial_no, sales_invoice)))
|
.format(serial_no, serial_no_details.sales_invoice)))
|
||||||
|
|
||||||
def update_project(self):
|
def update_project(self):
|
||||||
if self.project:
|
if self.project:
|
||||||
@@ -1203,48 +1223,39 @@ class SalesInvoice(SellingController):
|
|||||||
|
|
||||||
self.set_missing_values(for_validate = True)
|
self.set_missing_values(for_validate = True)
|
||||||
|
|
||||||
def get_discounting_status(self):
|
|
||||||
status = None
|
|
||||||
if self.is_discounted:
|
|
||||||
invoice_discounting_list = frappe.db.sql("""
|
|
||||||
select status
|
|
||||||
from `tabInvoice Discounting` id, `tabDiscounted Invoice` d
|
|
||||||
where
|
|
||||||
id.name = d.parent
|
|
||||||
and d.sales_invoice=%s
|
|
||||||
and id.docstatus=1
|
|
||||||
and status in ('Disbursed', 'Settled')
|
|
||||||
""", self.name)
|
|
||||||
for d in invoice_discounting_list:
|
|
||||||
status = d[0]
|
|
||||||
if status == "Disbursed":
|
|
||||||
break
|
|
||||||
return status
|
|
||||||
|
|
||||||
def set_status(self, update=False, status=None, update_modified=True):
|
def set_status(self, update=False, status=None, update_modified=True):
|
||||||
if self.is_new():
|
if self.is_new():
|
||||||
if self.get('amended_from'):
|
if self.get('amended_from'):
|
||||||
self.status = 'Draft'
|
self.status = 'Draft'
|
||||||
return
|
return
|
||||||
|
|
||||||
|
precision = self.precision("outstanding_amount")
|
||||||
|
outstanding_amount = flt(self.outstanding_amount, precision)
|
||||||
|
due_date = getdate(self.due_date)
|
||||||
|
nowdate = getdate()
|
||||||
|
|
||||||
|
discounting_status = None
|
||||||
|
if self.is_discounted:
|
||||||
|
discountng_status = get_discounting_status(self.name)
|
||||||
|
|
||||||
if not status:
|
if not status:
|
||||||
if self.docstatus == 2:
|
if self.docstatus == 2:
|
||||||
status = "Cancelled"
|
status = "Cancelled"
|
||||||
elif self.docstatus == 1:
|
elif self.docstatus == 1:
|
||||||
if flt(self.outstanding_amount) > 0 and getdate(self.due_date) < getdate(nowdate()) and self.is_discounted and self.get_discounting_status()=='Disbursed':
|
if outstanding_amount > 0 and due_date < nowdate and self.is_discounted and discountng_status=='Disbursed':
|
||||||
self.status = "Overdue and Discounted"
|
self.status = "Overdue and Discounted"
|
||||||
elif flt(self.outstanding_amount) > 0 and getdate(self.due_date) < getdate(nowdate()):
|
elif outstanding_amount > 0 and due_date < nowdate:
|
||||||
self.status = "Overdue"
|
self.status = "Overdue"
|
||||||
elif flt(self.outstanding_amount) > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.is_discounted and self.get_discounting_status()=='Disbursed':
|
elif outstanding_amount > 0 and due_date >= nowdate and self.is_discounted and discountng_status=='Disbursed':
|
||||||
self.status = "Unpaid and Discounted"
|
self.status = "Unpaid and Discounted"
|
||||||
elif flt(self.outstanding_amount) > 0 and getdate(self.due_date) >= getdate(nowdate()):
|
elif outstanding_amount > 0 and due_date >= nowdate:
|
||||||
self.status = "Unpaid"
|
self.status = "Unpaid"
|
||||||
#Check if outstanding amount is 0 due to credit note issued against invoice
|
#Check if outstanding amount is 0 due to credit note issued against invoice
|
||||||
elif flt(self.outstanding_amount) <= 0 and self.is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
|
elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
|
||||||
self.status = "Credit Note Issued"
|
self.status = "Credit Note Issued"
|
||||||
elif self.is_return == 1:
|
elif self.is_return == 1:
|
||||||
self.status = "Return"
|
self.status = "Return"
|
||||||
elif flt(self.outstanding_amount)<=0:
|
elif outstanding_amount<=0:
|
||||||
self.status = "Paid"
|
self.status = "Paid"
|
||||||
else:
|
else:
|
||||||
self.status = "Submitted"
|
self.status = "Submitted"
|
||||||
@@ -1254,6 +1265,26 @@ class SalesInvoice(SellingController):
|
|||||||
if update:
|
if update:
|
||||||
self.db_set('status', self.status, update_modified = update_modified)
|
self.db_set('status', self.status, update_modified = update_modified)
|
||||||
|
|
||||||
|
def get_discounting_status(sales_invoice):
|
||||||
|
status = None
|
||||||
|
|
||||||
|
invoice_discounting_list = frappe.db.sql("""
|
||||||
|
select status
|
||||||
|
from `tabInvoice Discounting` id, `tabDiscounted Invoice` d
|
||||||
|
where
|
||||||
|
id.name = d.parent
|
||||||
|
and d.sales_invoice=%s
|
||||||
|
and id.docstatus=1
|
||||||
|
and status in ('Disbursed', 'Settled')
|
||||||
|
""", sales_invoice)
|
||||||
|
|
||||||
|
for d in invoice_discounting_list:
|
||||||
|
status = d[0]
|
||||||
|
if status == "Disbursed":
|
||||||
|
break
|
||||||
|
|
||||||
|
return status
|
||||||
|
|
||||||
def validate_inter_company_party(doctype, party, company, inter_company_reference):
|
def validate_inter_company_party(doctype, party, company, inter_company_reference):
|
||||||
if not party:
|
if not party:
|
||||||
return
|
return
|
||||||
@@ -1421,6 +1452,21 @@ def get_inter_company_details(doc, doctype):
|
|||||||
"company": company
|
"company": company
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_internal_party(parties, link_doctype, doc):
|
||||||
|
if len(parties) == 1:
|
||||||
|
party = parties[0].name
|
||||||
|
else:
|
||||||
|
# If more than one Internal Supplier/Customer, get supplier/customer on basis of address
|
||||||
|
if doc.get('company_address') or doc.get('shipping_address'):
|
||||||
|
party = frappe.db.get_value("Dynamic Link", {"parent": doc.get('company_address') or doc.get('shipping_address'),
|
||||||
|
"parenttype": "Address", "link_doctype": link_doctype}, "link_name")
|
||||||
|
|
||||||
|
if not party:
|
||||||
|
party = parties[0].name
|
||||||
|
else:
|
||||||
|
party = parties[0].name
|
||||||
|
|
||||||
|
return party
|
||||||
|
|
||||||
def validate_inter_company_transaction(doc, doctype):
|
def validate_inter_company_transaction(doc, doctype):
|
||||||
|
|
||||||
@@ -1509,6 +1555,9 @@ def get_loyalty_programs(customer):
|
|||||||
else:
|
else:
|
||||||
return lp_details
|
return lp_details
|
||||||
|
|
||||||
|
def on_doctype_update():
|
||||||
|
frappe.db.add_index("Sales Invoice", ["customer", "is_return", "return_against"])
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def create_invoice_discounting(source_name, target_doc=None):
|
def create_invoice_discounting(source_name, target_doc=None):
|
||||||
invoice = frappe.get_doc("Sales Invoice", source_name)
|
invoice = frappe.get_doc("Sales Invoice", source_name)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
import unittest, copy, time
|
import unittest, copy, time
|
||||||
from frappe.utils import nowdate, flt, getdate, cint
|
from frappe.utils import nowdate, flt, getdate, cint, add_days
|
||||||
from frappe.model.dynamic_links import get_dynamic_link_map
|
from frappe.model.dynamic_links import get_dynamic_link_map
|
||||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction
|
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction
|
||||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice
|
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice
|
||||||
@@ -728,7 +728,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
def test_make_pos_invoice(self):
|
def test_make_pos_invoice(self):
|
||||||
from erpnext.accounts.doctype.sales_invoice.pos import make_invoice
|
from erpnext.accounts.doctype.sales_invoice.pos import make_invoice
|
||||||
|
|
||||||
make_pos_profile()
|
pos_profile = make_pos_profile()
|
||||||
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",supplier_warehouse= "Work In Progress - TCP1", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
|
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",supplier_warehouse= "Work In Progress - TCP1", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
|
||||||
pos = create_sales_invoice(company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1", do_not_save=True)
|
pos = create_sales_invoice(company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1", do_not_save=True)
|
||||||
|
|
||||||
@@ -744,7 +744,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
pos.append("taxes", tax)
|
pos.append("taxes", tax)
|
||||||
|
|
||||||
invoice_data = [{'09052016142': pos}]
|
invoice_data = [{'09052016142': pos}]
|
||||||
si = make_invoice(invoice_data).get('invoice')
|
si = make_invoice(pos_profile, invoice_data).get('invoice')
|
||||||
self.assertEqual(si[0], '09052016142')
|
self.assertEqual(si[0], '09052016142')
|
||||||
|
|
||||||
sales_invoice = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': '09052016142', 'docstatus': 1})
|
sales_invoice = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': '09052016142', 'docstatus': 1})
|
||||||
@@ -762,7 +762,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
if allow_negative_stock:
|
if allow_negative_stock:
|
||||||
frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 0)
|
frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 0)
|
||||||
|
|
||||||
make_pos_profile()
|
pos_profile = make_pos_profile()
|
||||||
timestamp = cint(time.time())
|
timestamp = cint(time.time())
|
||||||
|
|
||||||
item = make_item("_Test POS Item")
|
item = make_item("_Test POS Item")
|
||||||
@@ -776,7 +776,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
{'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 330}]
|
{'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 330}]
|
||||||
|
|
||||||
invoice_data = [{timestamp: pos}]
|
invoice_data = [{timestamp: pos}]
|
||||||
si = make_invoice(invoice_data).get('invoice')
|
si = make_invoice(pos_profile, invoice_data).get('invoice')
|
||||||
self.assertEqual(si[0], timestamp)
|
self.assertEqual(si[0], timestamp)
|
||||||
|
|
||||||
sales_invoice = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': timestamp})
|
sales_invoice = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': timestamp})
|
||||||
@@ -785,7 +785,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
timestamp = cint(time.time())
|
timestamp = cint(time.time())
|
||||||
pos["offline_pos_name"] = timestamp
|
pos["offline_pos_name"] = timestamp
|
||||||
invoice_data = [{timestamp: pos}]
|
invoice_data = [{timestamp: pos}]
|
||||||
si1 = make_invoice(invoice_data).get('invoice')
|
si1 = make_invoice(pos_profile, invoice_data).get('invoice')
|
||||||
self.assertEqual(si1[0], timestamp)
|
self.assertEqual(si1[0], timestamp)
|
||||||
|
|
||||||
sales_invoice1 = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': timestamp})
|
sales_invoice1 = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': timestamp})
|
||||||
@@ -1834,7 +1834,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
si.submit()
|
si.submit()
|
||||||
|
|
||||||
data = get_ewb_data("Sales Invoice", si.name)
|
data = get_ewb_data("Sales Invoice", [si.name])
|
||||||
|
|
||||||
self.assertEqual(data['version'], '1.0.1118')
|
self.assertEqual(data['version'], '1.0.1118')
|
||||||
self.assertEqual(data['billLists'][0]['fromGstin'], '27AAECE4835E1ZR')
|
self.assertEqual(data['billLists'][0]['fromGstin'], '27AAECE4835E1ZR')
|
||||||
@@ -1847,6 +1847,26 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
self.assertEqual(data['billLists'][0]['vehicleNo'], 'KA12KA1234')
|
self.assertEqual(data['billLists'][0]['vehicleNo'], 'KA12KA1234')
|
||||||
self.assertEqual(data['billLists'][0]['itemList'][0]['taxableAmount'], 60000)
|
self.assertEqual(data['billLists'][0]['itemList'][0]['taxableAmount'], 60000)
|
||||||
|
|
||||||
|
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):
|
def create_sales_invoice(**args):
|
||||||
si = frappe.new_doc("Sales Invoice")
|
si = frappe.new_doc("Sales Invoice")
|
||||||
@@ -1870,7 +1890,7 @@ def create_sales_invoice(**args):
|
|||||||
"gst_hsn_code": "999800",
|
"gst_hsn_code": "999800",
|
||||||
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
||||||
"qty": args.qty or 1,
|
"qty": args.qty or 1,
|
||||||
"rate": args.rate or 100,
|
"rate": args.rate if args.get("rate") is not None else 100,
|
||||||
"income_account": args.income_account or "Sales - _TC",
|
"income_account": args.income_account or "Sales - _TC",
|
||||||
"expense_account": args.expense_account or "Cost of Goods Sold - _TC",
|
"expense_account": args.expense_account or "Cost of Goods Sold - _TC",
|
||||||
"cost_center": args.cost_center or "_Test Cost Center - _TC",
|
"cost_center": args.cost_center or "_Test Cost Center - _TC",
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"autoname": "hash",
|
"autoname": "hash",
|
||||||
"creation": "2013-06-04 11:02:19",
|
"creation": "2013-06-04 11:02:19",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@@ -484,7 +485,8 @@
|
|||||||
"depends_on": "enable_deferred_revenue",
|
"depends_on": "enable_deferred_revenue",
|
||||||
"fieldname": "service_stop_date",
|
"fieldname": "service_stop_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "Service Stop Date"
|
"label": "Service Stop Date",
|
||||||
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
@@ -500,13 +502,15 @@
|
|||||||
"depends_on": "enable_deferred_revenue",
|
"depends_on": "enable_deferred_revenue",
|
||||||
"fieldname": "service_start_date",
|
"fieldname": "service_start_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "Service Start Date"
|
"label": "Service Start Date",
|
||||||
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "enable_deferred_revenue",
|
"depends_on": "enable_deferred_revenue",
|
||||||
"fieldname": "service_end_date",
|
"fieldname": "service_end_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "Service End Date"
|
"label": "Service End Date",
|
||||||
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"collapsible": 1,
|
"collapsible": 1,
|
||||||
@@ -783,7 +787,8 @@
|
|||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2019-07-16 16:36:46.527606",
|
"links": [],
|
||||||
|
"modified": "2019-12-04 12:22:38.517710",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice Item",
|
"name": "Sales Invoice Item",
|
||||||
|
|||||||
@@ -1,299 +1,119 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"allow_import": 1,
|
||||||
"allow_import": 1,
|
"allow_rename": 1,
|
||||||
"allow_rename": 1,
|
"creation": "2013-01-10 16:34:09",
|
||||||
"autoname": "field:title",
|
"description": "Standard tax template that can be applied to all Sales Transactions. This template can contain list of tax heads and also other expense / income heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Is this Tax included in Basic Rate?: If you check this, it means that this tax will not be shown below the item table, but will be included in the Basic Rate in your main item table. This is useful where you want give a flat price (inclusive of all taxes) price to customers.",
|
||||||
"beta": 0,
|
"doctype": "DocType",
|
||||||
"creation": "2013-01-10 16:34:09",
|
"document_type": "Setup",
|
||||||
"custom": 0,
|
"engine": "InnoDB",
|
||||||
"description": "Standard tax template that can be applied to all Sales Transactions. This template can contain list of tax heads and also other expense / income heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Is this Tax included in Basic Rate?: If you check this, it means that this tax will not be shown below the item table, but will be included in the Basic Rate in your main item table. This is useful where you want give a flat price (inclusive of all taxes) price to customers.",
|
"field_order": [
|
||||||
"docstatus": 0,
|
"title",
|
||||||
"doctype": "DocType",
|
"is_default",
|
||||||
"document_type": "Setup",
|
"disabled",
|
||||||
"editable_grid": 0,
|
"column_break_3",
|
||||||
|
"company",
|
||||||
|
"tax_category",
|
||||||
|
"section_break_5",
|
||||||
|
"taxes"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "title",
|
||||||
"bold": 0,
|
"fieldtype": "Data",
|
||||||
"collapsible": 0,
|
"label": "Title",
|
||||||
"columns": 0,
|
"no_copy": 1,
|
||||||
"fieldname": "title",
|
"oldfieldname": "title",
|
||||||
"fieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
"hidden": 0,
|
"reqd": 1
|
||||||
"ignore_user_permissions": 0,
|
},
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 1,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Title",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
|
||||||
"oldfieldname": "title",
|
|
||||||
"oldfieldtype": "Data",
|
|
||||||
"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,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"default": "0",
|
||||||
"bold": 0,
|
"fieldname": "is_default",
|
||||||
"collapsible": 0,
|
"fieldtype": "Check",
|
||||||
"columns": 0,
|
"in_list_view": 1,
|
||||||
"fieldname": "is_default",
|
"label": "Default"
|
||||||
"fieldtype": "Check",
|
},
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Default",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"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,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"default": "0",
|
||||||
"bold": 0,
|
"fieldname": "disabled",
|
||||||
"collapsible": 0,
|
"fieldtype": "Check",
|
||||||
"columns": 0,
|
"label": "Disabled"
|
||||||
"fieldname": "disabled",
|
},
|
||||||
"fieldtype": "Check",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Disabled",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "column_break_3",
|
||||||
"bold": 0,
|
"fieldtype": "Column Break"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "column_break_3",
|
|
||||||
"fieldtype": "Column Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"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,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "company",
|
||||||
"bold": 0,
|
"fieldtype": "Link",
|
||||||
"collapsible": 0,
|
"in_list_view": 1,
|
||||||
"columns": 0,
|
"in_standard_filter": 1,
|
||||||
"fieldname": "company",
|
"label": "Company",
|
||||||
"fieldtype": "Link",
|
"oldfieldname": "company",
|
||||||
"hidden": 0,
|
"oldfieldtype": "Link",
|
||||||
"ignore_user_permissions": 0,
|
"options": "Company",
|
||||||
"ignore_xss_filter": 0,
|
"remember_last_selected_value": 1,
|
||||||
"in_filter": 1,
|
"reqd": 1
|
||||||
"in_list_view": 1,
|
},
|
||||||
"in_standard_filter": 1,
|
|
||||||
"label": "Company",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "company",
|
|
||||||
"oldfieldtype": "Link",
|
|
||||||
"options": "Company",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 1,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "section_break_5",
|
||||||
"bold": 0,
|
"fieldtype": "Section Break"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "section_break_5",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"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,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"description": "* Will be calculated in the transaction.",
|
||||||
"bold": 0,
|
"fieldname": "taxes",
|
||||||
"collapsible": 0,
|
"fieldtype": "Table",
|
||||||
"columns": 0,
|
"label": "Sales Taxes and Charges",
|
||||||
"description": "* Will be calculated in the transaction.",
|
"oldfieldname": "other_charges",
|
||||||
"fieldname": "taxes",
|
"oldfieldtype": "Table",
|
||||||
"fieldtype": "Table",
|
"options": "Sales Taxes and Charges"
|
||||||
"hidden": 0,
|
},
|
||||||
"ignore_user_permissions": 0,
|
{
|
||||||
"ignore_xss_filter": 0,
|
"fieldname": "tax_category",
|
||||||
"in_filter": 0,
|
"fieldtype": "Link",
|
||||||
"in_list_view": 0,
|
"label": "Tax Category",
|
||||||
"in_standard_filter": 0,
|
"options": "Tax Category"
|
||||||
"label": "Sales Taxes and Charges",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "other_charges",
|
|
||||||
"oldfieldtype": "Table",
|
|
||||||
"options": "Sales Taxes and Charges",
|
|
||||||
"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,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hide_heading": 0,
|
"icon": "fa fa-money",
|
||||||
"hide_toolbar": 0,
|
"idx": 1,
|
||||||
"icon": "fa fa-money",
|
"modified": "2019-11-25 13:06:03.279099",
|
||||||
"idx": 1,
|
"modified_by": "Administrator",
|
||||||
"image_view": 0,
|
"module": "Accounts",
|
||||||
"in_create": 0,
|
"name": "Sales Taxes and Charges Template",
|
||||||
|
"owner": "Administrator",
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 0,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2016-11-07 05:18:41.743257",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "Accounts",
|
|
||||||
"name": "Sales Taxes and Charges Template",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"email": 1,
|
||||||
"apply_user_permissions": 1,
|
"print": 1,
|
||||||
"cancel": 0,
|
"read": 1,
|
||||||
"create": 0,
|
"report": 1,
|
||||||
"delete": 0,
|
"role": "Sales User"
|
||||||
"email": 1,
|
},
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"is_custom": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Sales User",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 0,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"apply_user_permissions": 0,
|
"delete": 1,
|
||||||
"cancel": 0,
|
"email": 1,
|
||||||
"create": 1,
|
"print": 1,
|
||||||
"delete": 1,
|
"read": 1,
|
||||||
"email": 1,
|
"report": 1,
|
||||||
"export": 0,
|
"role": "Accounts Manager",
|
||||||
"if_owner": 0,
|
"share": 1,
|
||||||
"import": 0,
|
|
||||||
"is_custom": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Accounts Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"apply_user_permissions": 0,
|
"delete": 1,
|
||||||
"cancel": 0,
|
"email": 1,
|
||||||
"create": 1,
|
"print": 1,
|
||||||
"delete": 1,
|
"read": 1,
|
||||||
"email": 1,
|
"report": 1,
|
||||||
"export": 0,
|
"role": "Sales Master Manager",
|
||||||
"if_owner": 0,
|
"share": 1,
|
||||||
"import": 0,
|
|
||||||
"is_custom": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Sales Master Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
"sort_field": "modified",
|
||||||
"read_only": 0,
|
"sort_order": "ASC",
|
||||||
"read_only_onload": 0,
|
"track_changes": 1
|
||||||
"sort_order": "ASC",
|
|
||||||
"track_seen": 0
|
|
||||||
}
|
}
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2019-11-07 13:31:17.999744",
|
"modified": "2019-12-20 14:48:01.990600",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Share Transfer",
|
"name": "Share Transfer",
|
||||||
@@ -196,6 +196,7 @@
|
|||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 1,
|
"amend": 1,
|
||||||
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
@@ -221,6 +222,7 @@
|
|||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
@@ -230,6 +232,7 @@
|
|||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Accounts Manager",
|
"role": "Accounts Manager",
|
||||||
"share": 1,
|
"share": 1,
|
||||||
|
"submit": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ class ShippingRule(Document):
|
|||||||
|
|
||||||
def get_shipping_amount_from_rules(self, value):
|
def get_shipping_amount_from_rules(self, value):
|
||||||
for condition in self.get("conditions"):
|
for condition in self.get("conditions"):
|
||||||
if not condition.to_value or (flt(condition.from_value) <= value <= flt(condition.to_value)):
|
if not condition.to_value or (flt(condition.from_value) <= flt(value) <= flt(condition.to_value)):
|
||||||
return condition.shipping_amount
|
return condition.shipping_amount
|
||||||
|
|
||||||
return 0.0
|
return 0.0
|
||||||
@@ -82,7 +82,7 @@ class ShippingRule(Document):
|
|||||||
if not shipping_country:
|
if not shipping_country:
|
||||||
frappe.throw(_('Shipping Address does not have country, which is required for this Shipping Rule'))
|
frappe.throw(_('Shipping Address does not have country, which is required for this Shipping Rule'))
|
||||||
if shipping_country not in [d.country for d in self.countries]:
|
if shipping_country not in [d.country for d in self.countries]:
|
||||||
frappe.throw(_('Shipping rule not applicable for country {0}'.format(shipping_country)))
|
frappe.throw(_('Shipping rule not applicable for country {0} in Shipping Address').format(shipping_country))
|
||||||
|
|
||||||
def add_shipping_rule_to_tax_table(self, doc, shipping_amount):
|
def add_shipping_rule_to_tax_table(self, doc, shipping_amount):
|
||||||
shipping_charge = {
|
shipping_charge = {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"autoname": "ACC-SUB-.YYYY.-.#####",
|
"autoname": "ACC-SUB-.YYYY.-.#####",
|
||||||
"creation": "2017-07-18 17:50:43.967266",
|
"creation": "2017-07-18 17:50:43.967266",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@@ -155,7 +156,7 @@
|
|||||||
"fieldname": "apply_additional_discount",
|
"fieldname": "apply_additional_discount",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Apply Additional Discount On",
|
"label": "Apply Additional Discount On",
|
||||||
"options": "\nGrand Total\nNet total"
|
"options": "\nGrand Total\nNet Total"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "cb_2",
|
"fieldname": "cb_2",
|
||||||
@@ -196,7 +197,8 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2019-07-25 18:45:38.579579",
|
"links": [],
|
||||||
|
"modified": "2020-01-27 14:37:32.845173",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Subscription",
|
"name": "Subscription",
|
||||||
|
|||||||
@@ -280,7 +280,7 @@ class Subscription(Document):
|
|||||||
|
|
||||||
if self.additional_discount_percentage or self.additional_discount_amount:
|
if self.additional_discount_percentage or self.additional_discount_amount:
|
||||||
discount_on = self.apply_additional_discount
|
discount_on = self.apply_additional_discount
|
||||||
invoice.apply_additional_discount = discount_on if discount_on else 'Grand Total'
|
invoice.apply_discount_on = discount_on if discount_on else 'Grand Total'
|
||||||
|
|
||||||
# Subscription period
|
# Subscription period
|
||||||
invoice.from_date = self.current_invoice_start
|
invoice.from_date = self.current_invoice_start
|
||||||
@@ -338,6 +338,16 @@ class Subscription(Document):
|
|||||||
|
|
||||||
# Check invoice dates and make sure it doesn't have outstanding invoices
|
# Check invoice dates and make sure it doesn't have outstanding invoices
|
||||||
return getdate(nowdate()) >= getdate(self.current_invoice_start) and not self.has_outstanding_invoice()
|
return getdate(nowdate()) >= getdate(self.current_invoice_start) and not self.has_outstanding_invoice()
|
||||||
|
|
||||||
|
def is_current_invoice_paid(self):
|
||||||
|
if self.is_new_subscription():
|
||||||
|
return False
|
||||||
|
|
||||||
|
last_invoice = frappe.get_doc('Sales Invoice', self.invoices[-1].invoice)
|
||||||
|
if getdate(last_invoice.posting_date) == getdate(self.current_invoice_start) and last_invoice.status == 'Paid':
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def process_for_active(self):
|
def process_for_active(self):
|
||||||
"""
|
"""
|
||||||
@@ -348,7 +358,7 @@ class Subscription(Document):
|
|||||||
2. Change the `Subscription` status to 'Past Due Date'
|
2. Change the `Subscription` status to 'Past Due Date'
|
||||||
3. Change the `Subscription` status to 'Cancelled'
|
3. Change the `Subscription` status to 'Cancelled'
|
||||||
"""
|
"""
|
||||||
if self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice():
|
if not self.is_current_invoice_paid() and (self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice()):
|
||||||
self.generate_invoice()
|
self.generate_invoice()
|
||||||
if self.current_invoice_is_past_due():
|
if self.current_invoice_is_past_due():
|
||||||
self.status = 'Past Due Date'
|
self.status = 'Past Due Date'
|
||||||
|
|||||||
@@ -1,134 +1,134 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"allow_copy": 0,
|
||||||
"allow_events_in_timeline": 0,
|
"allow_events_in_timeline": 0,
|
||||||
"allow_guest_to_view": 0,
|
"allow_guest_to_view": 0,
|
||||||
"allow_import": 0,
|
"allow_import": 0,
|
||||||
"allow_rename": 0,
|
"allow_rename": 0,
|
||||||
"autoname": "field:title",
|
"autoname": "field:title",
|
||||||
"beta": 0,
|
"beta": 0,
|
||||||
"creation": "2018-11-22 23:38:39.668804",
|
"creation": "2018-11-22 23:38:39.668804",
|
||||||
"custom": 0,
|
"custom": 0,
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "",
|
"document_type": "",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
"allow_in_quick_entry": 0,
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"fieldname": "title",
|
"fieldname": "title",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_global_search": 0,
|
"in_global_search": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"in_standard_filter": 0,
|
"in_standard_filter": 0,
|
||||||
"label": "Title",
|
"label": "Title",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"remember_last_selected_value": 0,
|
"remember_last_selected_value": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"translatable": 0,
|
"translatable": 0,
|
||||||
"unique": 1
|
"unique": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"has_web_view": 0,
|
||||||
"hide_heading": 0,
|
"hide_heading": 0,
|
||||||
"hide_toolbar": 0,
|
"hide_toolbar": 0,
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"image_view": 0,
|
"image_view": 0,
|
||||||
"in_create": 0,
|
"in_create": 0,
|
||||||
"is_submittable": 0,
|
"is_submittable": 0,
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2018-11-22 23:38:39.668804",
|
"modified": "2020-01-15 17:14:28.951793",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Tax Category",
|
"name": "Tax Category",
|
||||||
"name_case": "",
|
"name_case": "",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"amend": 0,
|
||||||
"cancel": 0,
|
"cancel": 0,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
"if_owner": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "System Manager",
|
"role": "System Manager",
|
||||||
"set_user_permissions": 0,
|
"set_user_permissions": 0,
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
"submit": 0,
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"amend": 0,
|
||||||
"cancel": 0,
|
"cancel": 0,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
"if_owner": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Accounts Manager",
|
"role": "Accounts Manager",
|
||||||
"set_user_permissions": 0,
|
"set_user_permissions": 0,
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
"submit": 0,
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"amend": 0,
|
||||||
"cancel": 0,
|
"cancel": 0,
|
||||||
"create": 0,
|
"create": 0,
|
||||||
"delete": 0,
|
"delete": 0,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
"if_owner": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Accounts User",
|
"role": "Accounts User",
|
||||||
"set_user_permissions": 0,
|
"set_user_permissions": 0,
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
"submit": 0,
|
||||||
"write": 0
|
"write": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"read_only_onload": 0,
|
"read_only_onload": 0,
|
||||||
"show_name_in_global_search": 0,
|
"show_name_in_global_search": 0,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"track_changes": 1,
|
"track_changes": 1,
|
||||||
"track_seen": 0,
|
"track_seen": 0,
|
||||||
"track_views": 0
|
"track_views": 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,23 +6,42 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import flt
|
from frappe.utils import flt, getdate
|
||||||
from erpnext.accounts.utils import get_fiscal_year
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
|
|
||||||
class TaxWithholdingCategory(Document):
|
class TaxWithholdingCategory(Document):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get_party_tax_withholding_details(ref_doc):
|
def get_party_tax_withholding_details(ref_doc, tax_withholding_category=None):
|
||||||
tax_withholding_category = frappe.db.get_value('Supplier', ref_doc.supplier, 'tax_withholding_category')
|
|
||||||
|
pan_no = ''
|
||||||
|
suppliers = []
|
||||||
|
|
||||||
|
if not tax_withholding_category:
|
||||||
|
tax_withholding_category, pan_no = frappe.db.get_value('Supplier', ref_doc.supplier, ['tax_withholding_category', 'pan'])
|
||||||
|
|
||||||
if not tax_withholding_category:
|
if not tax_withholding_category:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if not pan_no:
|
||||||
|
pan_no = frappe.db.get_value('Supplier', ref_doc.supplier, 'pan')
|
||||||
|
|
||||||
|
# Get others suppliers with the same PAN No
|
||||||
|
if pan_no:
|
||||||
|
suppliers = [d.name for d in frappe.get_all('Supplier', fields=['name'], filters={'pan': pan_no})]
|
||||||
|
|
||||||
|
if not suppliers:
|
||||||
|
suppliers.append(ref_doc.supplier)
|
||||||
|
|
||||||
fy = get_fiscal_year(ref_doc.posting_date, company=ref_doc.company)
|
fy = get_fiscal_year(ref_doc.posting_date, company=ref_doc.company)
|
||||||
tax_details = get_tax_withholding_details(tax_withholding_category, fy[0], ref_doc.company)
|
tax_details = get_tax_withholding_details(tax_withholding_category, fy[0], ref_doc.company)
|
||||||
if not tax_details:
|
if not tax_details:
|
||||||
frappe.throw(_('Please set associated account in Tax Withholding Category {0} against Company {1}')
|
frappe.throw(_('Please set associated account in Tax Withholding Category {0} against Company {1}')
|
||||||
.format(tax_withholding_category, ref_doc.company))
|
.format(tax_withholding_category, ref_doc.company))
|
||||||
tds_amount = get_tds_amount(ref_doc, tax_details, fy)
|
|
||||||
|
tds_amount = get_tds_amount(suppliers, ref_doc.net_total, ref_doc.company,
|
||||||
|
tax_details, fy, ref_doc.posting_date, pan_no)
|
||||||
|
|
||||||
tax_row = get_tax_row(tax_details, tds_amount)
|
tax_row = get_tax_row(tax_details, tds_amount)
|
||||||
|
|
||||||
return tax_row
|
return tax_row
|
||||||
@@ -51,6 +70,7 @@ def get_tax_withholding_rates(tax_withholding, fiscal_year):
|
|||||||
frappe.throw(_("No Tax Withholding data found for the current Fiscal Year."))
|
frappe.throw(_("No Tax Withholding data found for the current Fiscal Year."))
|
||||||
|
|
||||||
def get_tax_row(tax_details, tds_amount):
|
def get_tax_row(tax_details, tds_amount):
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"category": "Total",
|
"category": "Total",
|
||||||
"add_deduct_tax": "Deduct",
|
"add_deduct_tax": "Deduct",
|
||||||
@@ -60,25 +80,36 @@ def get_tax_row(tax_details, tds_amount):
|
|||||||
"tax_amount": tds_amount
|
"tax_amount": tds_amount
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_tds_amount(ref_doc, tax_details, fiscal_year_details):
|
def get_tds_amount(suppliers, net_total, company, tax_details, fiscal_year_details, posting_date, pan_no=None):
|
||||||
fiscal_year, year_start_date, year_end_date = fiscal_year_details
|
fiscal_year, year_start_date, year_end_date = fiscal_year_details
|
||||||
tds_amount = 0
|
tds_amount = 0
|
||||||
tds_deducted = 0
|
tds_deducted = 0
|
||||||
|
|
||||||
def _get_tds(amount):
|
def _get_tds(amount, rate):
|
||||||
if amount <= 0:
|
if amount <= 0:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
return amount * tax_details.rate / 100
|
return amount * rate / 100
|
||||||
|
|
||||||
|
ldc_name = frappe.db.get_value('Lower Deduction Certificate',
|
||||||
|
{
|
||||||
|
'pan_no': pan_no,
|
||||||
|
'fiscal_year': fiscal_year
|
||||||
|
}, 'name')
|
||||||
|
ldc = ''
|
||||||
|
|
||||||
|
if ldc_name:
|
||||||
|
ldc = frappe.get_doc('Lower Deduction Certificate', ldc_name)
|
||||||
|
|
||||||
entries = frappe.db.sql("""
|
entries = frappe.db.sql("""
|
||||||
select voucher_no, credit
|
select voucher_no, credit
|
||||||
from `tabGL Entry`
|
from `tabGL Entry`
|
||||||
where party=%s and fiscal_year=%s and credit > 0
|
where company = %s and
|
||||||
""", (ref_doc.supplier, fiscal_year), as_dict=1)
|
party in %s and fiscal_year=%s and credit > 0
|
||||||
|
""", (company, tuple(suppliers), fiscal_year), as_dict=1)
|
||||||
|
|
||||||
vouchers = [d.voucher_no for d in entries]
|
vouchers = [d.voucher_no for d in entries]
|
||||||
advance_vouchers = get_advance_vouchers(ref_doc.supplier, fiscal_year)
|
advance_vouchers = get_advance_vouchers(suppliers, fiscal_year=fiscal_year, company=company)
|
||||||
|
|
||||||
tds_vouchers = vouchers + advance_vouchers
|
tds_vouchers = vouchers + advance_vouchers
|
||||||
|
|
||||||
@@ -93,7 +124,20 @@ def get_tds_amount(ref_doc, tax_details, fiscal_year_details):
|
|||||||
tds_deducted = tds_deducted[0][0] if tds_deducted and tds_deducted[0][0] else 0
|
tds_deducted = tds_deducted[0][0] if tds_deducted and tds_deducted[0][0] else 0
|
||||||
|
|
||||||
if tds_deducted:
|
if tds_deducted:
|
||||||
tds_amount = _get_tds(ref_doc.net_total)
|
if ldc:
|
||||||
|
limit_consumed = frappe.db.get_value('Purchase Invoice',
|
||||||
|
{
|
||||||
|
'supplier': ('in', suppliers),
|
||||||
|
'apply_tds': 1,
|
||||||
|
'docstatus': 1
|
||||||
|
}, 'sum(net_total)')
|
||||||
|
|
||||||
|
if ldc and is_valid_certificate(ldc.valid_from, ldc.valid_upto, posting_date, limit_consumed, net_total,
|
||||||
|
ldc.certificate_limit):
|
||||||
|
|
||||||
|
tds_amount = get_ltds_amount(net_total, limit_consumed, ldc.certificate_limit, ldc.rate, tax_details)
|
||||||
|
else:
|
||||||
|
tds_amount = _get_tds(net_total, tax_details.rate)
|
||||||
else:
|
else:
|
||||||
supplier_credit_amount = frappe.get_all('Purchase Invoice Item',
|
supplier_credit_amount = frappe.get_all('Purchase Invoice Item',
|
||||||
fields = ['sum(net_amount)'],
|
fields = ['sum(net_amount)'],
|
||||||
@@ -106,43 +150,79 @@ def get_tds_amount(ref_doc, tax_details, fiscal_year_details):
|
|||||||
fields = ['sum(credit_in_account_currency)'],
|
fields = ['sum(credit_in_account_currency)'],
|
||||||
filters = {
|
filters = {
|
||||||
'parent': ('in', vouchers), 'docstatus': 1,
|
'parent': ('in', vouchers), 'docstatus': 1,
|
||||||
'party': ref_doc.supplier,
|
'party': ('in', suppliers),
|
||||||
'reference_type': ('not in', ['Purchase Invoice'])
|
'reference_type': ('not in', ['Purchase Invoice'])
|
||||||
}, as_list=1)
|
}, as_list=1)
|
||||||
|
|
||||||
supplier_credit_amount += (jv_supplier_credit_amt[0][0]
|
supplier_credit_amount += (jv_supplier_credit_amt[0][0]
|
||||||
if jv_supplier_credit_amt and jv_supplier_credit_amt[0][0] else 0)
|
if jv_supplier_credit_amt and jv_supplier_credit_amt[0][0] else 0)
|
||||||
|
|
||||||
supplier_credit_amount += ref_doc.net_total
|
supplier_credit_amount += net_total
|
||||||
|
|
||||||
debit_note_amount = get_debit_note_amount(ref_doc.supplier, year_start_date, year_end_date)
|
debit_note_amount = get_debit_note_amount(suppliers, year_start_date, year_end_date)
|
||||||
supplier_credit_amount -= debit_note_amount
|
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 supplier_credit_amount >= tax_details.threshold)
|
||||||
or (tax_details.get('cumulative_threshold', 0) and supplier_credit_amount >= tax_details.cumulative_threshold)):
|
or (tax_details.get('cumulative_threshold', 0) and supplier_credit_amount >= tax_details.cumulative_threshold)):
|
||||||
tds_amount = _get_tds(supplier_credit_amount)
|
|
||||||
|
if ldc and is_valid_certificate(ldc.valid_from, ldc.valid_upto, posting_date, tds_deducted, net_total,
|
||||||
|
ldc.certificate_limit):
|
||||||
|
tds_amount = get_ltds_amount(supplier_credit_amount, 0, ldc.certificate_limit, ldc.rate,
|
||||||
|
tax_details)
|
||||||
|
else:
|
||||||
|
tds_amount = _get_tds(supplier_credit_amount, tax_details.rate)
|
||||||
|
|
||||||
return tds_amount
|
return tds_amount
|
||||||
|
|
||||||
def get_advance_vouchers(supplier, fiscal_year=None, company=None, from_date=None, to_date=None):
|
def get_advance_vouchers(suppliers, fiscal_year=None, company=None, from_date=None, to_date=None):
|
||||||
condition = "fiscal_year=%s" % fiscal_year
|
condition = "fiscal_year=%s" % fiscal_year
|
||||||
|
|
||||||
|
if company:
|
||||||
|
condition += "and company =%s" % (company)
|
||||||
if from_date and to_date:
|
if from_date and to_date:
|
||||||
condition = "company=%s and posting_date between %s and %s" % (company, from_date, to_date)
|
condition += "and posting_date between %s and %s" % (company, from_date, to_date)
|
||||||
|
|
||||||
|
## Appending the same supplier again if length of suppliers list is 1
|
||||||
|
## since tuple of single element list contains None, For example ('Test Supplier 1', )
|
||||||
|
## and the below query fails
|
||||||
|
if len(suppliers) == 1:
|
||||||
|
suppliers.append(suppliers[0])
|
||||||
|
|
||||||
return frappe.db.sql_list("""
|
return frappe.db.sql_list("""
|
||||||
select distinct voucher_no
|
select distinct voucher_no
|
||||||
from `tabGL Entry`
|
from `tabGL Entry`
|
||||||
where party=%s and %s and debit > 0
|
where party in %s and %s and debit > 0
|
||||||
""", (supplier, condition)) or []
|
""", (tuple(suppliers), condition)) or []
|
||||||
|
|
||||||
def get_debit_note_amount(supplier, year_start_date, year_end_date, company=None):
|
def get_debit_note_amount(suppliers, year_start_date, year_end_date, company=None):
|
||||||
condition = ""
|
condition = "and 1=1"
|
||||||
if company:
|
if company:
|
||||||
condition = " and company=%s " % company
|
condition = " and company=%s " % company
|
||||||
|
|
||||||
|
if len(suppliers) == 1:
|
||||||
|
suppliers.append(suppliers[0])
|
||||||
|
|
||||||
return flt(frappe.db.sql("""
|
return flt(frappe.db.sql("""
|
||||||
select abs(sum(net_total))
|
select abs(sum(net_total))
|
||||||
from `tabPurchase Invoice`
|
from `tabPurchase Invoice`
|
||||||
where supplier=%s %s and is_return=1 and docstatus=1
|
where supplier in %s and is_return=1 and docstatus=1
|
||||||
and posting_date between %s and %s
|
and posting_date between %s and %s %s
|
||||||
""", (supplier, condition, year_start_date, year_end_date)))
|
""", (tuple(suppliers), year_start_date, year_end_date, condition)))
|
||||||
|
|
||||||
|
def get_ltds_amount(current_amount, deducted_amount, certificate_limit, rate, tax_details):
|
||||||
|
if current_amount < (certificate_limit - deducted_amount):
|
||||||
|
return current_amount * rate/100
|
||||||
|
else:
|
||||||
|
ltds_amount = (certificate_limit - deducted_amount)
|
||||||
|
tds_amount = current_amount - ltds_amount
|
||||||
|
|
||||||
|
return ltds_amount * rate/100 + tds_amount * tax_details.rate/100
|
||||||
|
|
||||||
|
def is_valid_certificate(valid_from, valid_upto, posting_date, deducted_amount, current_amount, certificate_limit):
|
||||||
|
valid = False
|
||||||
|
|
||||||
|
if ((getdate(valid_from) <= getdate(posting_date) <= getdate(valid_upto)) and
|
||||||
|
certificate_limit > deducted_amount):
|
||||||
|
valid = True
|
||||||
|
|
||||||
|
return valid
|
||||||
@@ -90,8 +90,12 @@ def merge_similar_entries(gl_map):
|
|||||||
else:
|
else:
|
||||||
merged_gl_map.append(entry)
|
merged_gl_map.append(entry)
|
||||||
|
|
||||||
|
company = gl_map[0].company if gl_map else erpnext.get_default_company()
|
||||||
|
company_currency = erpnext.get_company_currency(company)
|
||||||
|
precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), company_currency)
|
||||||
|
|
||||||
# filter zero debit and credit entries
|
# filter zero debit and credit entries
|
||||||
merged_gl_map = filter(lambda x: flt(x.debit, 9)!=0 or flt(x.credit, 9)!=0, merged_gl_map)
|
merged_gl_map = filter(lambda x: flt(x.debit, precision)!=0 or flt(x.credit, precision)!=0, merged_gl_map)
|
||||||
merged_gl_map = list(merged_gl_map)
|
merged_gl_map = list(merged_gl_map)
|
||||||
|
|
||||||
return merged_gl_map
|
return merged_gl_map
|
||||||
@@ -132,12 +136,14 @@ def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
|
|||||||
|
|
||||||
|
|
||||||
def make_entry(args, adv_adj, update_outstanding, from_repost=False):
|
def make_entry(args, adv_adj, update_outstanding, from_repost=False):
|
||||||
args.update({"doctype": "GL Entry"})
|
gle = frappe.new_doc("GL Entry")
|
||||||
gle = frappe.get_doc(args)
|
gle.update(args)
|
||||||
gle.flags.ignore_permissions = 1
|
gle.flags.ignore_permissions = 1
|
||||||
gle.flags.from_repost = from_repost
|
gle.flags.from_repost = from_repost
|
||||||
gle.insert()
|
gle.validate()
|
||||||
|
gle.db_insert()
|
||||||
gle.run_method("on_update_with_args", adv_adj, update_outstanding, from_repost)
|
gle.run_method("on_update_with_args", adv_adj, update_outstanding, from_repost)
|
||||||
|
gle.flags.ignore_validate = True
|
||||||
gle.submit()
|
gle.submit()
|
||||||
|
|
||||||
def validate_account_for_perpetual_inventory(gl_map):
|
def validate_account_for_perpetual_inventory(gl_map):
|
||||||
@@ -162,33 +168,34 @@ def validate_account_for_perpetual_inventory(gl_map):
|
|||||||
frappe.throw(_("Account: {0} can only be updated via Stock Transactions")
|
frappe.throw(_("Account: {0} can only be updated via Stock Transactions")
|
||||||
.format(account), StockAccountInvalidTransaction)
|
.format(account), StockAccountInvalidTransaction)
|
||||||
|
|
||||||
elif account_bal != stock_bal:
|
# This has been comment for a temporary, will add this code again on release of immutable ledger
|
||||||
precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"),
|
# elif account_bal != stock_bal:
|
||||||
currency=frappe.get_cached_value('Company', gl_map[0].company, "default_currency"))
|
# precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"),
|
||||||
|
# currency=frappe.get_cached_value('Company', gl_map[0].company, "default_currency"))
|
||||||
|
|
||||||
diff = flt(stock_bal - account_bal, precision)
|
# diff = flt(stock_bal - account_bal, precision)
|
||||||
error_reason = _("Stock Value ({0}) and Account Balance ({1}) are out of sync for account {2} and it's linked warehouses.").format(
|
# error_reason = _("Stock Value ({0}) and Account Balance ({1}) are out of sync for account {2} and it's linked warehouses.").format(
|
||||||
stock_bal, account_bal, frappe.bold(account))
|
# stock_bal, account_bal, frappe.bold(account))
|
||||||
error_resolution = _("Please create adjustment Journal Entry for amount {0} ").format(frappe.bold(diff))
|
# error_resolution = _("Please create adjustment Journal Entry for amount {0} ").format(frappe.bold(diff))
|
||||||
stock_adjustment_account = frappe.db.get_value("Company",gl_map[0].company,"stock_adjustment_account")
|
# stock_adjustment_account = frappe.db.get_value("Company",gl_map[0].company,"stock_adjustment_account")
|
||||||
|
|
||||||
db_or_cr_warehouse_account =('credit_in_account_currency' if diff < 0 else 'debit_in_account_currency')
|
# db_or_cr_warehouse_account =('credit_in_account_currency' if diff < 0 else 'debit_in_account_currency')
|
||||||
db_or_cr_stock_adjustment_account = ('debit_in_account_currency' if diff < 0 else 'credit_in_account_currency')
|
# db_or_cr_stock_adjustment_account = ('debit_in_account_currency' if diff < 0 else 'credit_in_account_currency')
|
||||||
|
|
||||||
journal_entry_args = {
|
# journal_entry_args = {
|
||||||
'accounts':[
|
# 'accounts':[
|
||||||
{'account': account, db_or_cr_warehouse_account : abs(diff)},
|
# {'account': account, db_or_cr_warehouse_account : abs(diff)},
|
||||||
{'account': stock_adjustment_account, db_or_cr_stock_adjustment_account : abs(diff) }]
|
# {'account': stock_adjustment_account, db_or_cr_stock_adjustment_account : abs(diff) }]
|
||||||
}
|
# }
|
||||||
|
|
||||||
frappe.msgprint(msg="""{0}<br></br>{1}<br></br>""".format(error_reason, error_resolution),
|
# frappe.msgprint(msg="""{0}<br></br>{1}<br></br>""".format(error_reason, error_resolution),
|
||||||
raise_exception=StockValueAndAccountBalanceOutOfSync,
|
# raise_exception=StockValueAndAccountBalanceOutOfSync,
|
||||||
title=_('Values Out Of Sync'),
|
# title=_('Values Out Of Sync'),
|
||||||
primary_action={
|
# primary_action={
|
||||||
'label': _('Make Journal Entry'),
|
# 'label': _('Make Journal Entry'),
|
||||||
'client_action': 'erpnext.route_to_adjustment_jv',
|
# 'client_action': 'erpnext.route_to_adjustment_jv',
|
||||||
'args': journal_entry_args
|
# 'args': journal_entry_args
|
||||||
})
|
# })
|
||||||
|
|
||||||
def validate_cwip_accounts(gl_map):
|
def validate_cwip_accounts(gl_map):
|
||||||
cwip_enabled = any([cint(ac.enable_cwip_accounting) for ac in frappe.db.get_all("Asset Category","enable_cwip_accounting")])
|
cwip_enabled = any([cint(ac.enable_cwip_accounting) for ac in frappe.db.get_all("Asset Category","enable_cwip_accounting")])
|
||||||
|
|||||||
@@ -1458,7 +1458,40 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
|||||||
this.child.batch_no = this.item_batch_no[this.child.item_code];
|
this.child.batch_no = this.item_batch_no[this.child.item_code];
|
||||||
this.child.serial_no = (this.item_serial_no[this.child.item_code]
|
this.child.serial_no = (this.item_serial_no[this.child.item_code]
|
||||||
? this.item_serial_no[this.child.item_code][0] : '');
|
? this.item_serial_no[this.child.item_code][0] : '');
|
||||||
this.child.item_tax_rate = JSON.stringify(this.tax_data[this.child.item_code]);
|
|
||||||
|
const tax_template_is_valid = true;
|
||||||
|
if (this.items && this.items[0].valid_from) {
|
||||||
|
tax_template_is_valid = frappe.datetime.get_diff(frappe.datetime.now_date(),
|
||||||
|
this.items[0].valid_from) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.child.item_tax_template = tax_template_is_valid ? this.items[0].item_tax_template : '';
|
||||||
|
this.child.item_tax_rate = JSON.stringify(this.tax_data[this.child.item_tax_template]);
|
||||||
|
|
||||||
|
if (this.child.item_tax_rate) {
|
||||||
|
this.add_taxes_from_item_tax_template(this.child.item_tax_rate);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
add_taxes_from_item_tax_template: function(item_tax_map) {
|
||||||
|
let me = this;
|
||||||
|
|
||||||
|
if(item_tax_map && cint(frappe.defaults.get_default("add_taxes_from_item_tax_template"))) {
|
||||||
|
if(typeof (item_tax_map) == "string") {
|
||||||
|
item_tax_map = JSON.parse(item_tax_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
$.each(item_tax_map, function(tax, rate) {
|
||||||
|
let found = (me.frm.doc.taxes || []).find(d => d.account_head === tax);
|
||||||
|
if(!found) {
|
||||||
|
let child = frappe.model.add_child(me.frm.doc, "taxes");
|
||||||
|
child.charge_type = "On Net Total";
|
||||||
|
child.account_head = tax;
|
||||||
|
child.description = String(tax);
|
||||||
|
child.rate = rate;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
update_paid_amount_status: function (update_paid_amount) {
|
update_paid_amount_status: function (update_paid_amount) {
|
||||||
@@ -1769,6 +1802,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
|||||||
method: "erpnext.accounts.doctype.sales_invoice.pos.make_invoice",
|
method: "erpnext.accounts.doctype.sales_invoice.pos.make_invoice",
|
||||||
freeze: true,
|
freeze: true,
|
||||||
args: {
|
args: {
|
||||||
|
pos_profile: me.pos_profile_data,
|
||||||
doc_list: me.si_docs,
|
doc_list: me.si_docs,
|
||||||
email_queue_list: me.email_queue_list,
|
email_queue_list: me.email_queue_list,
|
||||||
customers_list: me.customers_list
|
customers_list: me.customers_list
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class DuplicatePartyAccountError(frappe.ValidationError): pass
|
|||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_party_details(party=None, account=None, party_type="Customer", company=None, posting_date=None,
|
def get_party_details(party=None, account=None, party_type="Customer", company=None, posting_date=None,
|
||||||
bill_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False, fetch_payment_terms_template=True,
|
bill_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False, fetch_payment_terms_template=True,
|
||||||
party_address=None, shipping_address=None, pos_profile=None):
|
party_address=None, company_address=None, shipping_address=None, pos_profile=None):
|
||||||
|
|
||||||
if not party:
|
if not party:
|
||||||
return {}
|
return {}
|
||||||
@@ -31,14 +31,14 @@ def get_party_details(party=None, account=None, party_type="Customer", company=N
|
|||||||
frappe.throw(_("{0}: {1} does not exists").format(party_type, party))
|
frappe.throw(_("{0}: {1} does not exists").format(party_type, party))
|
||||||
return _get_party_details(party, account, party_type,
|
return _get_party_details(party, account, party_type,
|
||||||
company, posting_date, bill_date, price_list, currency, doctype, ignore_permissions,
|
company, posting_date, bill_date, price_list, currency, doctype, ignore_permissions,
|
||||||
fetch_payment_terms_template, party_address, shipping_address, pos_profile)
|
fetch_payment_terms_template, party_address, company_address, shipping_address, pos_profile)
|
||||||
|
|
||||||
def _get_party_details(party=None, account=None, party_type="Customer", company=None, posting_date=None,
|
def _get_party_details(party=None, account=None, party_type="Customer", company=None, posting_date=None,
|
||||||
bill_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False,
|
bill_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False,
|
||||||
fetch_payment_terms_template=True, party_address=None, shipping_address=None, pos_profile=None):
|
fetch_payment_terms_template=True, party_address=None, company_address=None,shipping_address=None, pos_profile=None):
|
||||||
|
|
||||||
out = frappe._dict(set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype))
|
party_details = frappe._dict(set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype))
|
||||||
party = out[party_type.lower()]
|
party = party_details[party_type.lower()]
|
||||||
|
|
||||||
if not ignore_permissions and not frappe.has_permission(party_type, "read", party):
|
if not ignore_permissions and not frappe.has_permission(party_type, "read", party):
|
||||||
frappe.throw(_("Not permitted for {0}").format(party), frappe.PermissionError)
|
frappe.throw(_("Not permitted for {0}").format(party), frappe.PermissionError)
|
||||||
@@ -46,76 +46,81 @@ def _get_party_details(party=None, account=None, party_type="Customer", company=
|
|||||||
party = frappe.get_doc(party_type, party)
|
party = frappe.get_doc(party_type, party)
|
||||||
currency = party.default_currency if party.get("default_currency") else get_company_currency(company)
|
currency = party.default_currency if party.get("default_currency") else get_company_currency(company)
|
||||||
|
|
||||||
party_address, shipping_address = set_address_details(out, party, party_type, doctype, company, party_address, shipping_address)
|
party_address, shipping_address = set_address_details(party_details, party, party_type, doctype, company, party_address, company_address, shipping_address)
|
||||||
set_contact_details(out, party, party_type)
|
set_contact_details(party_details, party, party_type)
|
||||||
set_other_values(out, party, party_type)
|
set_other_values(party_details, party, party_type)
|
||||||
set_price_list(out, party, party_type, price_list, pos_profile)
|
set_price_list(party_details, party, party_type, price_list, pos_profile)
|
||||||
|
|
||||||
out["tax_category"] = get_address_tax_category(party.get("tax_category"),
|
party_details["tax_category"] = get_address_tax_category(party.get("tax_category"),
|
||||||
party_address, shipping_address if party_type != "Supplier" else party_address)
|
party_address, shipping_address if party_type != "Supplier" else party_address)
|
||||||
out["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company,
|
|
||||||
customer_group=out.customer_group, supplier_group=out.supplier_group, tax_category=out.tax_category,
|
if not party_details.get("taxes_and_charges"):
|
||||||
billing_address=party_address, shipping_address=shipping_address)
|
party_details["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company,
|
||||||
|
customer_group=party_details.customer_group, supplier_group=party_details.supplier_group, tax_category=party_details.tax_category,
|
||||||
|
billing_address=party_address, shipping_address=shipping_address)
|
||||||
|
|
||||||
if fetch_payment_terms_template:
|
if fetch_payment_terms_template:
|
||||||
out["payment_terms_template"] = get_pyt_term_template(party.name, party_type, company)
|
party_details["payment_terms_template"] = get_pyt_term_template(party.name, party_type, company)
|
||||||
|
|
||||||
if not out.get("currency"):
|
if not party_details.get("currency"):
|
||||||
out["currency"] = currency
|
party_details["currency"] = currency
|
||||||
|
|
||||||
# sales team
|
# sales team
|
||||||
if party_type=="Customer":
|
if party_type=="Customer":
|
||||||
out["sales_team"] = [{
|
party_details["sales_team"] = [{
|
||||||
"sales_person": d.sales_person,
|
"sales_person": d.sales_person,
|
||||||
"allocated_percentage": d.allocated_percentage or None
|
"allocated_percentage": d.allocated_percentage or None
|
||||||
} for d in party.get("sales_team")]
|
} for d in party.get("sales_team")]
|
||||||
|
|
||||||
# supplier tax withholding category
|
# supplier tax withholding category
|
||||||
if party_type == "Supplier" and party:
|
if party_type == "Supplier" and party:
|
||||||
out["supplier_tds"] = frappe.get_value(party_type, party.name, "tax_withholding_category")
|
party_details["supplier_tds"] = frappe.get_value(party_type, party.name, "tax_withholding_category")
|
||||||
|
|
||||||
return out
|
return party_details
|
||||||
|
|
||||||
def set_address_details(out, party, party_type, doctype=None, company=None, party_address=None, shipping_address=None):
|
def set_address_details(party_details, party, party_type, doctype=None, company=None, party_address=None, company_address=None, shipping_address=None):
|
||||||
billing_address_field = "customer_address" if party_type == "Lead" \
|
billing_address_field = "customer_address" if party_type == "Lead" \
|
||||||
else party_type.lower() + "_address"
|
else party_type.lower() + "_address"
|
||||||
out[billing_address_field] = party_address or get_default_address(party_type, party.name)
|
party_details[billing_address_field] = party_address or get_default_address(party_type, party.name)
|
||||||
if doctype:
|
if doctype:
|
||||||
out.update(get_fetch_values(doctype, billing_address_field, out[billing_address_field]))
|
party_details.update(get_fetch_values(doctype, billing_address_field, party_details[billing_address_field]))
|
||||||
# address display
|
# address display
|
||||||
out.address_display = get_address_display(out[billing_address_field])
|
party_details.address_display = get_address_display(party_details[billing_address_field])
|
||||||
# shipping address
|
# shipping address
|
||||||
if party_type in ["Customer", "Lead"]:
|
if party_type in ["Customer", "Lead"]:
|
||||||
out.shipping_address_name = shipping_address or get_party_shipping_address(party_type, party.name)
|
party_details.shipping_address_name = shipping_address or get_party_shipping_address(party_type, party.name)
|
||||||
out.shipping_address = get_address_display(out["shipping_address_name"])
|
party_details.shipping_address = get_address_display(party_details["shipping_address_name"])
|
||||||
if doctype:
|
if doctype:
|
||||||
out.update(get_fetch_values(doctype, 'shipping_address_name', out.shipping_address_name))
|
party_details.update(get_fetch_values(doctype, 'shipping_address_name', party_details.shipping_address_name))
|
||||||
|
|
||||||
if doctype and doctype in ['Delivery Note', 'Sales Invoice']:
|
if company_address:
|
||||||
out.update(get_company_address(company))
|
party_details.update({'company_address': company_address})
|
||||||
if out.company_address:
|
else:
|
||||||
out.update(get_fetch_values(doctype, 'company_address', out.company_address))
|
party_details.update(get_company_address(company))
|
||||||
get_regional_address_details(out, doctype, company)
|
|
||||||
|
|
||||||
elif doctype and doctype == "Purchase Invoice":
|
if doctype and doctype in ['Delivery Note', 'Sales Invoice', 'Sales Order']:
|
||||||
out.update(get_company_address(company))
|
if party_details.company_address:
|
||||||
if out.company_address:
|
party_details.update(get_fetch_values(doctype, 'company_address', party_details.company_address))
|
||||||
out["shipping_address"] = shipping_address or out["company_address"]
|
get_regional_address_details(party_details, doctype, company)
|
||||||
out.shipping_address_display = get_address_display(out["shipping_address"])
|
|
||||||
out.update(get_fetch_values(doctype, 'shipping_address', out.shipping_address))
|
|
||||||
get_regional_address_details(out, doctype, company)
|
|
||||||
|
|
||||||
return out.get(billing_address_field), out.shipping_address_name
|
elif doctype and doctype in ["Purchase Invoice", "Purchase Order", "Purchase Receipt"]:
|
||||||
|
if party_details.company_address:
|
||||||
|
party_details["shipping_address"] = shipping_address or party_details["company_address"]
|
||||||
|
party_details.shipping_address_display = get_address_display(party_details["shipping_address"])
|
||||||
|
party_details.update(get_fetch_values(doctype, 'shipping_address', party_details.shipping_address))
|
||||||
|
get_regional_address_details(party_details, doctype, company)
|
||||||
|
|
||||||
|
return party_details.get(billing_address_field), party_details.shipping_address_name
|
||||||
|
|
||||||
@erpnext.allow_regional
|
@erpnext.allow_regional
|
||||||
def get_regional_address_details(out, doctype, company):
|
def get_regional_address_details(party_details, doctype, company):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def set_contact_details(out, party, party_type):
|
def set_contact_details(party_details, party, party_type):
|
||||||
out.contact_person = get_default_contact(party_type, party.name)
|
party_details.contact_person = get_default_contact(party_type, party.name)
|
||||||
|
|
||||||
if not out.contact_person:
|
if not party_details.contact_person:
|
||||||
out.update({
|
party_details.update({
|
||||||
"contact_person": None,
|
"contact_person": None,
|
||||||
"contact_display": None,
|
"contact_display": None,
|
||||||
"contact_email": None,
|
"contact_email": None,
|
||||||
@@ -125,22 +130,22 @@ def set_contact_details(out, party, party_type):
|
|||||||
"contact_department": None
|
"contact_department": None
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
out.update(get_contact_details(out.contact_person))
|
party_details.update(get_contact_details(party_details.contact_person))
|
||||||
|
|
||||||
def set_other_values(out, party, party_type):
|
def set_other_values(party_details, party, party_type):
|
||||||
# copy
|
# copy
|
||||||
if party_type=="Customer":
|
if party_type=="Customer":
|
||||||
to_copy = ["customer_name", "customer_group", "territory", "language"]
|
to_copy = ["customer_name", "customer_group", "territory", "language"]
|
||||||
else:
|
else:
|
||||||
to_copy = ["supplier_name", "supplier_group", "language"]
|
to_copy = ["supplier_name", "supplier_group", "language"]
|
||||||
for f in to_copy:
|
for f in to_copy:
|
||||||
out[f] = party.get(f)
|
party_details[f] = party.get(f)
|
||||||
|
|
||||||
# fields prepended with default in Customer doctype
|
# fields prepended with default in Customer doctype
|
||||||
for f in ['currency'] \
|
for f in ['currency'] \
|
||||||
+ (['sales_partner', 'commission_rate'] if party_type=="Customer" else []):
|
+ (['sales_partner', 'commission_rate'] if party_type=="Customer" else []):
|
||||||
if party.get("default_" + f):
|
if party.get("default_" + f):
|
||||||
out[f] = party.get("default_" + f)
|
party_details[f] = party.get("default_" + f)
|
||||||
|
|
||||||
def get_default_price_list(party):
|
def get_default_price_list(party):
|
||||||
"""Return default price list for party (Document object)"""
|
"""Return default price list for party (Document object)"""
|
||||||
@@ -155,11 +160,12 @@ def get_default_price_list(party):
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def set_price_list(out, party, party_type, given_price_list, pos=None):
|
def set_price_list(party_details, party, party_type, given_price_list, pos=None):
|
||||||
# price list
|
# price list
|
||||||
price_list = get_permitted_documents('Price List')
|
price_list = get_permitted_documents('Price List')
|
||||||
|
|
||||||
if price_list:
|
# if there is only one permitted document based on user permissions, set it
|
||||||
|
if price_list and len(price_list) == 1:
|
||||||
price_list = price_list[0]
|
price_list = price_list[0]
|
||||||
elif pos and party_type == 'Customer':
|
elif pos and party_type == 'Customer':
|
||||||
customer_price_list = frappe.get_value('Customer', party.name, 'default_price_list')
|
customer_price_list = frappe.get_value('Customer', party.name, 'default_price_list')
|
||||||
@@ -173,9 +179,9 @@ def set_price_list(out, party, party_type, given_price_list, pos=None):
|
|||||||
price_list = get_default_price_list(party) or given_price_list
|
price_list = get_default_price_list(party) or given_price_list
|
||||||
|
|
||||||
if price_list:
|
if price_list:
|
||||||
out.price_list_currency = frappe.db.get_value("Price List", price_list, "currency", cache=True)
|
party_details.price_list_currency = frappe.db.get_value("Price List", price_list, "currency", cache=True)
|
||||||
|
|
||||||
out["selling_price_list" if party.doctype=="Customer" else "buying_price_list"] = price_list
|
party_details["selling_price_list" if party.doctype=="Customer" else "buying_price_list"] = price_list
|
||||||
|
|
||||||
|
|
||||||
def set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype):
|
def set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype):
|
||||||
@@ -462,23 +468,25 @@ def get_timeline_data(doctype, name):
|
|||||||
from frappe.desk.form.load import get_communication_data
|
from frappe.desk.form.load import get_communication_data
|
||||||
|
|
||||||
out = {}
|
out = {}
|
||||||
fields = 'date(creation), count(name)'
|
fields = 'creation, count(*)'
|
||||||
after = add_years(None, -1).strftime('%Y-%m-%d')
|
after = add_years(None, -1).strftime('%Y-%m-%d')
|
||||||
group_by='group by date(creation)'
|
group_by='group by Date(creation)'
|
||||||
|
|
||||||
data = get_communication_data(doctype, name, after=after, group_by='group by date(creation)',
|
data = get_communication_data(doctype, name, after=after, group_by='group by creation',
|
||||||
fields='date(C.creation) as creation, count(C.name)',as_dict=False)
|
fields='C.creation as creation, count(C.name)',as_dict=False)
|
||||||
|
|
||||||
# fetch and append data from Activity Log
|
# fetch and append data from Activity Log
|
||||||
data += frappe.db.sql("""select {fields}
|
data += frappe.db.sql("""select {fields}
|
||||||
from `tabActivity Log`
|
from `tabActivity Log`
|
||||||
where (reference_doctype="{doctype}" and reference_name="{name}")
|
where (reference_doctype=%(doctype)s and reference_name=%(name)s)
|
||||||
or (timeline_doctype in ("{doctype}") and timeline_name="{name}")
|
or (timeline_doctype in (%(doctype)s) and timeline_name=%(name)s)
|
||||||
or (reference_doctype in ("Quotation", "Opportunity") and timeline_name="{name}")
|
or (reference_doctype in ("Quotation", "Opportunity") and timeline_name=%(name)s)
|
||||||
and status!='Success' and creation > {after}
|
and status!='Success' and creation > {after}
|
||||||
{group_by} order by creation desc
|
{group_by} order by creation desc
|
||||||
""".format(doctype=frappe.db.escape(doctype), name=frappe.db.escape(name), fields=fields,
|
""".format(fields=fields, group_by=group_by, after=after), {
|
||||||
group_by=group_by, after=after), as_dict=False)
|
"doctype": doctype,
|
||||||
|
"name": name
|
||||||
|
}, as_dict=False)
|
||||||
|
|
||||||
timeline_items = dict(data)
|
timeline_items = dict(data)
|
||||||
|
|
||||||
@@ -597,10 +605,12 @@ def get_party_shipping_address(doctype, name):
|
|||||||
else:
|
else:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
def get_partywise_advanced_payment_amount(party_type, posting_date = None):
|
def get_partywise_advanced_payment_amount(party_type, posting_date = None, company=None):
|
||||||
cond = "1=1"
|
cond = "1=1"
|
||||||
if posting_date:
|
if posting_date:
|
||||||
cond = "posting_date <= '{0}'".format(posting_date)
|
cond = "posting_date <= '{0}'".format(posting_date)
|
||||||
|
if company:
|
||||||
|
cond += "and company = '{0}'".format(company)
|
||||||
|
|
||||||
data = frappe.db.sql(""" SELECT party, sum({0}) as amount
|
data = frappe.db.sql(""" SELECT party, sum({0}) as amount
|
||||||
FROM `tabGL Entry`
|
FROM `tabGL Entry`
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="right" colspan="3" ><strong>Total (debit) </strong></td>
|
<td class="right" colspan="3" ><strong>Total (debit) </strong></td>
|
||||||
<td class="left" >{{ gl | sum(attribute='debit') }}</td>
|
<td class="left" >{{ frappe.format((gl | sum(attribute="debit")), {fieldtype: "Currency"}) }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="top-bottom" colspan="5"><strong>Credit</strong></td>
|
<td class="top-bottom" colspan="5"><strong>Credit</strong></td>
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="right" colspan="3"><strong>Total (credit) </strong></td>
|
<td class="right" colspan="3"><strong>Total (credit) </strong></td>
|
||||||
<td class="left" >{{ gl | sum(attribute='credit') }}</td>
|
<td class="left" >{{ frappe.format((gl | sum(attribute="credit")), {fieldtype: "Currency"}) }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -100,6 +100,16 @@ frappe.query_reports["Accounts Payable"] = {
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Supplier Group"
|
"options": "Supplier Group"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "group_by_party",
|
||||||
|
"label": __("Group By Supplier"),
|
||||||
|
"fieldtype": "Check"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"based_on_payment_terms",
|
||||||
|
"label": __("Based On Payment Terms"),
|
||||||
|
"fieldtype": "Check",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname":"tax_id",
|
"fieldname":"tax_id",
|
||||||
"label": __("Tax Id"),
|
"label": __("Tax Id"),
|
||||||
@@ -107,6 +117,16 @@ frappe.query_reports["Accounts Payable"] = {
|
|||||||
"hidden": 1
|
"hidden": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
"formatter": function(value, row, column, data, default_formatter) {
|
||||||
|
value = default_formatter(value, row, column, data);
|
||||||
|
if (data && data.bold) {
|
||||||
|
value = value.bold();
|
||||||
|
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
|
||||||
onload: function(report) {
|
onload: function(report) {
|
||||||
report.page.add_inner_button(__("Accounts Payable Summary"), function() {
|
report.page.add_inner_button(__("Accounts Payable Summary"), function() {
|
||||||
var filters = report.get_values();
|
var filters = report.get_values();
|
||||||
|
|||||||
@@ -88,6 +88,11 @@ frappe.query_reports["Accounts Payable Summary"] = {
|
|||||||
"label": __("Supplier Group"),
|
"label": __("Supplier Group"),
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Supplier Group"
|
"options": "Supplier Group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"based_on_payment_terms",
|
||||||
|
"label": __("Based On Payment Terms"),
|
||||||
|
"fieldtype": "Check",
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@@ -218,15 +218,15 @@
|
|||||||
<td></td>
|
<td></td>
|
||||||
<td style="text-align: right"><b>{%= __("Total") %}</b></td>
|
<td style="text-align: right"><b>{%= __("Total") %}</b></td>
|
||||||
<td style="text-align: right">
|
<td style="text-align: right">
|
||||||
{%= format_currency(data[i]["invoiced"], data[i]["currency"] ) %}</td>
|
{%= format_currency(data[i]["invoiced"], data[0]["currency"] ) %}</td>
|
||||||
|
|
||||||
{% if(!filters.show_future_payments) { %}
|
{% if(!filters.show_future_payments) { %}
|
||||||
<td style="text-align: right">
|
<td style="text-align: right">
|
||||||
{%= format_currency(data[i]["paid"], data[i]["currency"]) %}</td>
|
{%= format_currency(data[i]["paid"], data[0]["currency"]) %}</td>
|
||||||
<td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %} </td>
|
<td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[0]["currency"]) %} </td>
|
||||||
{% } %}
|
{% } %}
|
||||||
<td style="text-align: right">
|
<td style="text-align: right">
|
||||||
{%= format_currency(data[i]["outstanding"], data[i]["currency"]) %}</td>
|
{%= format_currency(data[i]["outstanding"], data[0]["currency"]) %}</td>
|
||||||
|
|
||||||
{% if(filters.show_future_payments) { %}
|
{% if(filters.show_future_payments) { %}
|
||||||
{% if(report.report_name === "Accounts Receivable") { %}
|
{% if(report.report_name === "Accounts Receivable") { %}
|
||||||
@@ -234,8 +234,8 @@
|
|||||||
{%= data[i]["po_no"] %}</td>
|
{%= data[i]["po_no"] %}</td>
|
||||||
{% } %}
|
{% } %}
|
||||||
<td style="text-align: right">{%= data[i]["future_ref"] %}</td>
|
<td style="text-align: right">{%= data[i]["future_ref"] %}</td>
|
||||||
<td style="text-align: right">{%= format_currency(data[i]["future_amount"], data[i]["currency"]) %}</td>
|
<td style="text-align: right">{%= format_currency(data[i]["future_amount"], data[0]["currency"]) %}</td>
|
||||||
<td style="text-align: right">{%= format_currency(data[i]["remaining_balance"], data[i]["currency"]) %}</td>
|
<td style="text-align: right">{%= format_currency(data[i]["remaining_balance"], data[0]["currency"]) %}</td>
|
||||||
{% } %}
|
{% } %}
|
||||||
{% } %}
|
{% } %}
|
||||||
{% } else { %}
|
{% } else { %}
|
||||||
@@ -256,10 +256,10 @@
|
|||||||
{% } else { %}
|
{% } else { %}
|
||||||
<td><b>{%= __("Total") %}</b></td>
|
<td><b>{%= __("Total") %}</b></td>
|
||||||
{% } %}
|
{% } %}
|
||||||
<td style="text-align: right">{%= format_currency(data[i]["invoiced"], data[i]["currency"]) %}</td>
|
<td style="text-align: right">{%= format_currency(data[i]["invoiced"], data[0]["currency"]) %}</td>
|
||||||
<td style="text-align: right">{%= format_currency(data[i]["paid"], data[i]["currency"]) %}</td>
|
<td style="text-align: right">{%= format_currency(data[i]["paid"], data[0]["currency"]) %}</td>
|
||||||
<td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %}</td>
|
<td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[0]["currency"]) %}</td>
|
||||||
<td style="text-align: right">{%= format_currency(data[i]["outstanding"], data[i]["currency"]) %}</td>
|
<td style="text-align: right">{%= format_currency(data[i]["outstanding"], data[0]["currency"]) %}</td>
|
||||||
{% } %}
|
{% } %}
|
||||||
{% } %}
|
{% } %}
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ frappe.query_reports["Accounts Receivable"] = {
|
|||||||
frappe.query_report.set_filter_value('payment_terms', value["payment_terms"]);
|
frappe.query_report.set_filter_value('payment_terms', value["payment_terms"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
frappe.db.get_value('Customer Credit Limit', {'parent': customer, 'company': company},
|
frappe.db.get_value('Customer Credit Limit', {'parent': customer, 'company': company},
|
||||||
["credit_limit"], function(value) {
|
["credit_limit"], function(value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
frappe.query_report.set_filter_value('credit_limit', value["credit_limit"]);
|
frappe.query_report.set_filter_value('credit_limit', value["credit_limit"]);
|
||||||
@@ -131,6 +131,11 @@ frappe.query_reports["Accounts Receivable"] = {
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Sales Person"
|
"options": "Sales Person"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "group_by_party",
|
||||||
|
"label": __("Group By Customer"),
|
||||||
|
"fieldtype": "Check"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname":"based_on_payment_terms",
|
"fieldname":"based_on_payment_terms",
|
||||||
"label": __("Based On Payment Terms"),
|
"label": __("Based On Payment Terms"),
|
||||||
@@ -177,6 +182,15 @@ frappe.query_reports["Accounts Receivable"] = {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
"formatter": function(value, row, column, data, default_formatter) {
|
||||||
|
value = default_formatter(value, row, column, data);
|
||||||
|
if (data && data.bold) {
|
||||||
|
value = value.bold();
|
||||||
|
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
|
||||||
onload: function(report) {
|
onload: function(report) {
|
||||||
report.page.add_inner_button(__("Accounts Receivable Summary"), function() {
|
report.page.add_inner_button(__("Accounts Receivable Summary"), function() {
|
||||||
var filters = report.get_values();
|
var filters = report.get_values();
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from frappe import _, scrub
|
|||||||
from frappe.utils import getdate, nowdate, flt, cint, formatdate, cstr, now, time_diff_in_seconds
|
from frappe.utils import getdate, nowdate, flt, cint, formatdate, cstr, now, time_diff_in_seconds
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from erpnext.accounts.utils import get_currency_precision
|
from erpnext.accounts.utils import get_currency_precision
|
||||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
|
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children
|
||||||
|
|
||||||
# This report gives a summary of all Outstanding Invoices considering the following
|
# This report gives a summary of all Outstanding Invoices considering the following
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ class ReceivablePayableReport(object):
|
|||||||
self.get_columns()
|
self.get_columns()
|
||||||
self.get_data()
|
self.get_data()
|
||||||
self.get_chart_data()
|
self.get_chart_data()
|
||||||
return self.columns, self.data, None, self.chart
|
return self.columns, self.data, None, self.chart, None, self.skip_total_row
|
||||||
|
|
||||||
def set_defaults(self):
|
def set_defaults(self):
|
||||||
if not self.filters.get("company"):
|
if not self.filters.get("company"):
|
||||||
@@ -57,9 +57,16 @@ class ReceivablePayableReport(object):
|
|||||||
self.party_type = self.filters.party_type
|
self.party_type = self.filters.party_type
|
||||||
self.party_details = {}
|
self.party_details = {}
|
||||||
self.invoices = set()
|
self.invoices = set()
|
||||||
|
self.skip_total_row = 0
|
||||||
|
|
||||||
|
if self.filters.get('group_by_party'):
|
||||||
|
self.previous_party=''
|
||||||
|
self.total_row_map = {}
|
||||||
|
self.skip_total_row = 1
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
self.get_gl_entries()
|
self.get_gl_entries()
|
||||||
|
self.get_sales_invoices_or_customers_based_on_sales_person()
|
||||||
self.voucher_balance = OrderedDict()
|
self.voucher_balance = OrderedDict()
|
||||||
self.init_voucher_balance() # invoiced, paid, credit_note, outstanding
|
self.init_voucher_balance() # invoiced, paid, credit_note, outstanding
|
||||||
|
|
||||||
@@ -101,14 +108,40 @@ class ReceivablePayableReport(object):
|
|||||||
)
|
)
|
||||||
self.get_invoices(gle)
|
self.get_invoices(gle)
|
||||||
|
|
||||||
|
if self.filters.get('group_by_party'):
|
||||||
|
self.init_subtotal_row(gle.party)
|
||||||
|
|
||||||
|
if self.filters.get('group_by_party'):
|
||||||
|
self.init_subtotal_row('Total')
|
||||||
|
|
||||||
def get_invoices(self, gle):
|
def get_invoices(self, gle):
|
||||||
if gle.voucher_type in ('Sales Invoice', 'Purchase Invoice'):
|
if gle.voucher_type in ('Sales Invoice', 'Purchase Invoice'):
|
||||||
self.invoices.add(gle.voucher_no)
|
if self.filters.get("sales_person"):
|
||||||
|
if gle.voucher_no in self.sales_person_records.get("Sales Invoice", []) \
|
||||||
|
or gle.party in self.sales_person_records.get("Customer", []):
|
||||||
|
self.invoices.add(gle.voucher_no)
|
||||||
|
else:
|
||||||
|
self.invoices.add(gle.voucher_no)
|
||||||
|
|
||||||
|
def init_subtotal_row(self, party):
|
||||||
|
if not self.total_row_map.get(party):
|
||||||
|
self.total_row_map.setdefault(party, {
|
||||||
|
'party': party,
|
||||||
|
'bold': 1
|
||||||
|
})
|
||||||
|
|
||||||
|
for field in self.get_currency_fields():
|
||||||
|
self.total_row_map[party][field] = 0.0
|
||||||
|
|
||||||
|
def get_currency_fields(self):
|
||||||
|
return ['invoiced', 'paid', 'credit_note', 'outstanding', 'range1',
|
||||||
|
'range2', 'range3', 'range4', 'range5']
|
||||||
|
|
||||||
def update_voucher_balance(self, gle):
|
def update_voucher_balance(self, gle):
|
||||||
# get the row where this balance needs to be updated
|
# get the row where this balance needs to be updated
|
||||||
# if its a payment, it will return the linked invoice or will be considered as advance
|
# if its a payment, it will return the linked invoice or will be considered as advance
|
||||||
row = self.get_voucher_balance(gle)
|
row = self.get_voucher_balance(gle)
|
||||||
|
if not row: return
|
||||||
# gle_balance will be the total "debit - credit" for receivable type reports and
|
# gle_balance will be the total "debit - credit" for receivable type reports and
|
||||||
# and vice-versa for payable type reports
|
# and vice-versa for payable type reports
|
||||||
gle_balance = self.get_gle_balance(gle)
|
gle_balance = self.get_gle_balance(gle)
|
||||||
@@ -128,9 +161,26 @@ class ReceivablePayableReport(object):
|
|||||||
# advance / unlinked payment or other adjustment
|
# advance / unlinked payment or other adjustment
|
||||||
row.paid -= gle_balance
|
row.paid -= gle_balance
|
||||||
|
|
||||||
def get_voucher_balance(self, gle):
|
def update_sub_total_row(self, row, party):
|
||||||
voucher_balance = None
|
total_row = self.total_row_map.get(party)
|
||||||
|
|
||||||
|
for field in self.get_currency_fields():
|
||||||
|
total_row[field] += row.get(field, 0.0)
|
||||||
|
|
||||||
|
def append_subtotal_row(self, party):
|
||||||
|
sub_total_row = self.total_row_map.get(party)
|
||||||
|
self.data.append(sub_total_row)
|
||||||
|
self.data.append({})
|
||||||
|
self.update_sub_total_row(sub_total_row, 'Total')
|
||||||
|
|
||||||
|
def get_voucher_balance(self, gle):
|
||||||
|
if self.filters.get("sales_person"):
|
||||||
|
against_voucher = gle.against_voucher or gle.voucher_no
|
||||||
|
if not (gle.party in self.sales_person_records.get("Customer", []) or \
|
||||||
|
against_voucher in self.sales_person_records.get("Sales Invoice", [])):
|
||||||
|
return
|
||||||
|
|
||||||
|
voucher_balance = None
|
||||||
if gle.against_voucher:
|
if gle.against_voucher:
|
||||||
# find invoice
|
# find invoice
|
||||||
against_voucher = gle.against_voucher
|
against_voucher = gle.against_voucher
|
||||||
@@ -159,7 +209,7 @@ class ReceivablePayableReport(object):
|
|||||||
row.outstanding = flt(row.invoiced - row.paid - row.credit_note, self.currency_precision)
|
row.outstanding = flt(row.invoiced - row.paid - row.credit_note, self.currency_precision)
|
||||||
row.invoice_grand_total = row.invoiced
|
row.invoice_grand_total = row.invoiced
|
||||||
|
|
||||||
if abs(row.outstanding) > 0.1/10 ** self.currency_precision:
|
if abs(row.outstanding) > 1.0/10 ** self.currency_precision:
|
||||||
# non-zero oustanding, we must consider this row
|
# non-zero oustanding, we must consider this row
|
||||||
|
|
||||||
if self.is_invoice(row) and self.filters.based_on_payment_terms:
|
if self.is_invoice(row) and self.filters.based_on_payment_terms:
|
||||||
@@ -180,11 +230,22 @@ class ReceivablePayableReport(object):
|
|||||||
else:
|
else:
|
||||||
self.append_row(row)
|
self.append_row(row)
|
||||||
|
|
||||||
|
if self.filters.get('group_by_party'):
|
||||||
|
self.append_subtotal_row(self.previous_party)
|
||||||
|
self.data.append(self.total_row_map.get('Total'))
|
||||||
|
|
||||||
def append_row(self, row):
|
def append_row(self, row):
|
||||||
self.allocate_future_payments(row)
|
self.allocate_future_payments(row)
|
||||||
self.set_invoice_details(row)
|
self.set_invoice_details(row)
|
||||||
self.set_party_details(row)
|
self.set_party_details(row)
|
||||||
self.set_ageing(row)
|
self.set_ageing(row)
|
||||||
|
|
||||||
|
if self.filters.get('group_by_party'):
|
||||||
|
self.update_sub_total_row(row, row.party)
|
||||||
|
if self.previous_party and (self.previous_party != row.party):
|
||||||
|
self.append_subtotal_row(self.previous_party)
|
||||||
|
self.previous_party = row.party
|
||||||
|
|
||||||
self.data.append(row)
|
self.data.append(row)
|
||||||
|
|
||||||
def set_invoice_details(self, row):
|
def set_invoice_details(self, row):
|
||||||
@@ -273,7 +334,7 @@ class ReceivablePayableReport(object):
|
|||||||
|
|
||||||
def set_party_details(self, row):
|
def set_party_details(self, row):
|
||||||
# customer / supplier name
|
# customer / supplier name
|
||||||
party_details = self.get_party_details(row.party)
|
party_details = self.get_party_details(row.party) or {}
|
||||||
row.update(party_details)
|
row.update(party_details)
|
||||||
if self.filters.get(scrub(self.filters.party_type)):
|
if self.filters.get(scrub(self.filters.party_type)):
|
||||||
row.currency = row.account_currency
|
row.currency = row.account_currency
|
||||||
@@ -283,26 +344,28 @@ class ReceivablePayableReport(object):
|
|||||||
def allocate_outstanding_based_on_payment_terms(self, row):
|
def allocate_outstanding_based_on_payment_terms(self, row):
|
||||||
self.get_payment_terms(row)
|
self.get_payment_terms(row)
|
||||||
for term in row.payment_terms:
|
for term in row.payment_terms:
|
||||||
term.outstanding = term.invoiced
|
|
||||||
|
|
||||||
# update "paid" and "oustanding" for this term
|
# update "paid" and "oustanding" for this term
|
||||||
self.allocate_closing_to_term(row, term, 'paid')
|
if not term.paid:
|
||||||
|
self.allocate_closing_to_term(row, term, 'paid')
|
||||||
|
|
||||||
# update "credit_note" and "oustanding" for this term
|
# update "credit_note" and "oustanding" for this term
|
||||||
if term.outstanding:
|
if term.outstanding:
|
||||||
self.allocate_closing_to_term(row, term, 'credit_note')
|
self.allocate_closing_to_term(row, term, 'credit_note')
|
||||||
|
|
||||||
|
row.payment_terms = sorted(row.payment_terms, key=lambda x: x['due_date'])
|
||||||
|
|
||||||
def get_payment_terms(self, row):
|
def get_payment_terms(self, row):
|
||||||
# build payment_terms for row
|
# build payment_terms for row
|
||||||
payment_terms_details = frappe.db.sql("""
|
payment_terms_details = frappe.db.sql("""
|
||||||
select
|
select
|
||||||
si.name, si.party_account_currency, si.currency, si.conversion_rate,
|
si.name, si.party_account_currency, si.currency, si.conversion_rate,
|
||||||
ps.due_date, ps.payment_amount, ps.description
|
ps.due_date, ps.payment_amount, ps.description, ps.paid_amount
|
||||||
from `tab{0}` si, `tabPayment Schedule` ps
|
from `tab{0}` si, `tabPayment Schedule` ps
|
||||||
where
|
where
|
||||||
si.name = ps.parent and
|
si.name = ps.parent and
|
||||||
si.name = %s
|
si.name = %s
|
||||||
order by ps.due_date
|
order by ps.paid_amount desc, due_date
|
||||||
""".format(row.voucher_type), row.voucher_no, as_dict = 1)
|
""".format(row.voucher_type), row.voucher_no, as_dict = 1)
|
||||||
|
|
||||||
|
|
||||||
@@ -318,7 +381,7 @@ class ReceivablePayableReport(object):
|
|||||||
self.append_payment_term(row, d, term)
|
self.append_payment_term(row, d, term)
|
||||||
|
|
||||||
def append_payment_term(self, row, d, term):
|
def append_payment_term(self, row, d, term):
|
||||||
if self.filters.get("customer") and d.currency == d.party_account_currency:
|
if (self.filters.get("customer") or self.filters.get("supplier")) and d.currency == d.party_account_currency:
|
||||||
invoiced = d.payment_amount
|
invoiced = d.payment_amount
|
||||||
else:
|
else:
|
||||||
invoiced = flt(flt(d.payment_amount) * flt(d.conversion_rate), self.currency_precision)
|
invoiced = flt(flt(d.payment_amount) * flt(d.conversion_rate), self.currency_precision)
|
||||||
@@ -328,11 +391,14 @@ class ReceivablePayableReport(object):
|
|||||||
"invoiced": invoiced,
|
"invoiced": invoiced,
|
||||||
"invoice_grand_total": row.invoiced,
|
"invoice_grand_total": row.invoiced,
|
||||||
"payment_term": d.description,
|
"payment_term": d.description,
|
||||||
"paid": 0.0,
|
"paid": d.paid_amount,
|
||||||
"credit_note": 0.0,
|
"credit_note": 0.0,
|
||||||
"outstanding": 0.0
|
"outstanding": invoiced - d.paid_amount
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
if d.paid_amount:
|
||||||
|
row['paid'] -= d.paid_amount
|
||||||
|
|
||||||
def allocate_closing_to_term(self, row, term, key):
|
def allocate_closing_to_term(self, row, term, key):
|
||||||
if row[key]:
|
if row[key]:
|
||||||
if row[key] > term.outstanding:
|
if row[key] > term.outstanding:
|
||||||
@@ -491,6 +557,7 @@ class ReceivablePayableReport(object):
|
|||||||
# get all the GL entries filtered by the given filters
|
# get all the GL entries filtered by the given filters
|
||||||
|
|
||||||
conditions, values = self.prepare_conditions()
|
conditions, values = self.prepare_conditions()
|
||||||
|
order_by = self.get_order_by_condition()
|
||||||
|
|
||||||
if self.filters.get(scrub(self.party_type)):
|
if self.filters.get(scrub(self.party_type)):
|
||||||
select_fields = "debit_in_account_currency as debit, credit_in_account_currency as credit"
|
select_fields = "debit_in_account_currency as debit, credit_in_account_currency as credit"
|
||||||
@@ -508,9 +575,24 @@ class ReceivablePayableReport(object):
|
|||||||
and party_type=%s
|
and party_type=%s
|
||||||
and (party is not null and party != '')
|
and (party is not null and party != '')
|
||||||
and posting_date <= %s
|
and posting_date <= %s
|
||||||
{1}
|
{1} {2}"""
|
||||||
order by posting_date, party"""
|
.format(select_fields, conditions, order_by), values, as_dict=True)
|
||||||
.format(select_fields, conditions), values, as_dict=True)
|
|
||||||
|
def get_sales_invoices_or_customers_based_on_sales_person(self):
|
||||||
|
if self.filters.get("sales_person"):
|
||||||
|
lft, rgt = frappe.db.get_value("Sales Person",
|
||||||
|
self.filters.get("sales_person"), ["lft", "rgt"])
|
||||||
|
|
||||||
|
records = frappe.db.sql("""
|
||||||
|
select distinct parent, parenttype
|
||||||
|
from `tabSales Team` steam
|
||||||
|
where parenttype in ('Customer', 'Sales Invoice')
|
||||||
|
and exists(select name from `tabSales Person` where lft >= %s and rgt <= %s and name = steam.sales_person)
|
||||||
|
""", (lft, rgt), as_dict=1)
|
||||||
|
|
||||||
|
self.sales_person_records = frappe._dict()
|
||||||
|
for d in records:
|
||||||
|
self.sales_person_records.setdefault(d.parenttype, set()).add(d.parent)
|
||||||
|
|
||||||
def prepare_conditions(self):
|
def prepare_conditions(self):
|
||||||
conditions = [""]
|
conditions = [""]
|
||||||
@@ -526,9 +608,14 @@ class ReceivablePayableReport(object):
|
|||||||
self.add_supplier_filters(conditions, values)
|
self.add_supplier_filters(conditions, values)
|
||||||
|
|
||||||
self.add_accounting_dimensions_filters(conditions, values)
|
self.add_accounting_dimensions_filters(conditions, values)
|
||||||
|
|
||||||
return " and ".join(conditions), values
|
return " and ".join(conditions), values
|
||||||
|
|
||||||
|
def get_order_by_condition(self):
|
||||||
|
if self.filters.get('group_by_party'):
|
||||||
|
return "order by party, posting_date"
|
||||||
|
else:
|
||||||
|
return "order by posting_date, party"
|
||||||
|
|
||||||
def add_common_filters(self, conditions, values, party_type_field):
|
def add_common_filters(self, conditions, values, party_type_field):
|
||||||
if self.filters.company:
|
if self.filters.company:
|
||||||
conditions.append("company=%s")
|
conditions.append("company=%s")
|
||||||
@@ -564,16 +651,6 @@ class ReceivablePayableReport(object):
|
|||||||
conditions.append("party in (select name from tabCustomer where default_sales_partner=%s)")
|
conditions.append("party in (select name from tabCustomer where default_sales_partner=%s)")
|
||||||
values.append(self.filters.get("sales_partner"))
|
values.append(self.filters.get("sales_partner"))
|
||||||
|
|
||||||
if self.filters.get("sales_person"):
|
|
||||||
lft, rgt = frappe.db.get_value("Sales Person",
|
|
||||||
self.filters.get("sales_person"), ["lft", "rgt"])
|
|
||||||
|
|
||||||
conditions.append("""exists(select name from `tabSales Team` steam where
|
|
||||||
steam.sales_person in (select name from `tabSales Person` where lft >= {0} and rgt <= {1})
|
|
||||||
and ((steam.parent = voucher_no and steam.parenttype = voucher_type)
|
|
||||||
or (steam.parent = against_voucher and steam.parenttype = against_voucher_type)
|
|
||||||
or (steam.parent = party and steam.parenttype = 'Customer')))""".format(lft, rgt))
|
|
||||||
|
|
||||||
def add_supplier_filters(self, conditions, values):
|
def add_supplier_filters(self, conditions, values):
|
||||||
if self.filters.get("supplier_group"):
|
if self.filters.get("supplier_group"):
|
||||||
conditions.append("""party in (select name from tabSupplier
|
conditions.append("""party in (select name from tabSupplier
|
||||||
@@ -593,13 +670,16 @@ class ReceivablePayableReport(object):
|
|||||||
doctype=doctype, lft=lft, rgt=rgt, key=key)
|
doctype=doctype, lft=lft, rgt=rgt, key=key)
|
||||||
|
|
||||||
def add_accounting_dimensions_filters(self, conditions, values):
|
def add_accounting_dimensions_filters(self, conditions, values):
|
||||||
accounting_dimensions = get_accounting_dimensions()
|
accounting_dimensions = get_accounting_dimensions(as_list=False)
|
||||||
|
|
||||||
if accounting_dimensions:
|
if accounting_dimensions:
|
||||||
for dimension in accounting_dimensions:
|
for dimension in accounting_dimensions:
|
||||||
if self.filters.get(dimension):
|
if self.filters.get(dimension.fieldname):
|
||||||
conditions.append("{0} = %s".format(dimension))
|
if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'):
|
||||||
values.append(self.filters.get(dimension))
|
self.filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type,
|
||||||
|
self.filters.get(dimension.fieldname))
|
||||||
|
conditions.append("{0} in %s".format(dimension.fieldname))
|
||||||
|
values.append(tuple(self.filters.get(dimension.fieldname)))
|
||||||
|
|
||||||
def get_gle_balance(self, gle):
|
def get_gle_balance(self, gle):
|
||||||
# get the balance of the GL (debit - credit) or reverse balance based on report type
|
# get the balance of the GL (debit - credit) or reverse balance based on report type
|
||||||
@@ -718,11 +798,13 @@ class ReceivablePayableReport(object):
|
|||||||
def get_chart_data(self):
|
def get_chart_data(self):
|
||||||
rows = []
|
rows = []
|
||||||
for row in self.data:
|
for row in self.data:
|
||||||
values = [row.range1, row.range2, row.range3, row.range4, row.range5]
|
row = frappe._dict(row)
|
||||||
precision = cint(frappe.db.get_default("float_precision")) or 2
|
if not cint(row.bold):
|
||||||
rows.append({
|
values = [row.range1, row.range2, row.range3, row.range4, row.range5]
|
||||||
'values': [flt(val, precision) for val in values]
|
precision = cint(frappe.db.get_default("float_precision")) or 2
|
||||||
})
|
rows.append({
|
||||||
|
'values': [flt(val, precision) for val in values]
|
||||||
|
})
|
||||||
|
|
||||||
self.chart = {
|
self.chart = {
|
||||||
"data": {
|
"data": {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user