mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-18 12:39:18 +00:00
Compare commits
868 Commits
v14.43.0
...
revert-328
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c5336a3c8 | ||
|
|
a0d9e1bd57 | ||
|
|
91fad1935e | ||
|
|
9f5d613c78 | ||
|
|
a1a992b0dd | ||
|
|
8c13f70fc5 | ||
|
|
ab1722d78e | ||
|
|
490b0e3cdf | ||
|
|
a191ce6200 | ||
|
|
22598a09de | ||
|
|
b06345af46 | ||
|
|
a2260a3dc2 | ||
|
|
6b93b3f12a | ||
|
|
4b9921782b | ||
|
|
7df2921d38 | ||
|
|
42a59d5c17 | ||
|
|
4e9a63423f | ||
|
|
d03979eef9 | ||
|
|
2c8b0b17a7 | ||
|
|
e4d16c31da | ||
|
|
8ae9c1d192 | ||
|
|
f4920d6475 | ||
|
|
9d5c8d9d6b | ||
|
|
06a42caa5b | ||
|
|
e559fea8ff | ||
|
|
8f65677cb4 | ||
|
|
362ec7b673 | ||
|
|
51521fc19f | ||
|
|
e9306f3c75 | ||
|
|
e7fa2e08ad | ||
|
|
9730cd0aec | ||
|
|
c16553626f | ||
|
|
1283c5b7a5 | ||
|
|
a007f1da9d | ||
|
|
7e1742956c | ||
|
|
57038c3969 | ||
|
|
fb7ee301b5 | ||
|
|
ff51f3b95e | ||
|
|
5f1b226362 | ||
|
|
7278387879 | ||
|
|
438f2ac1a8 | ||
|
|
030ef6da25 | ||
|
|
4082149f0e | ||
|
|
c8619284bc | ||
|
|
47248251e2 | ||
|
|
a863f59cf0 | ||
|
|
84ab100d86 | ||
|
|
55d6773be6 | ||
|
|
7b5cf6978e | ||
|
|
0448c0fa36 | ||
|
|
b5d64bc7c5 | ||
|
|
2b65b22aa2 | ||
|
|
1d52084b05 | ||
|
|
5e8a22be24 | ||
|
|
637c08d189 | ||
|
|
404662e794 | ||
|
|
d6ab2b3b87 | ||
|
|
734db5b722 | ||
|
|
bf4b012cec | ||
|
|
f8d2e276a5 | ||
|
|
e29f756146 | ||
|
|
5fe55176ec | ||
|
|
30daeb90a8 | ||
|
|
eb4820b68b | ||
|
|
8355c1092c | ||
|
|
5e7a69e649 | ||
|
|
4efc947f14 | ||
|
|
8859e6f1bb | ||
|
|
4d8da4420e | ||
|
|
70c9b8dc50 | ||
|
|
c447dfaa9c | ||
|
|
9209ec59c2 | ||
|
|
d2b6490bca | ||
|
|
8d30ebb12b | ||
|
|
0394080898 | ||
|
|
75983ce809 | ||
|
|
e820629f83 | ||
|
|
cc8d540943 | ||
|
|
90fbab4b1e | ||
|
|
5665d1afa0 | ||
|
|
be0f6b3e6e | ||
|
|
0966867c08 | ||
|
|
4ff06a9d79 | ||
|
|
b5221a5c74 | ||
|
|
324bfa9fde | ||
|
|
760c26e9c0 | ||
|
|
761e9df1bf | ||
|
|
611d827e0b | ||
|
|
54072ec9cd | ||
|
|
4a4abf9b64 | ||
|
|
3ca9d53d1b | ||
|
|
f4779df9b3 | ||
|
|
428971f127 | ||
|
|
181df2fe63 | ||
|
|
1328a45f2a | ||
|
|
cbf82872fb | ||
|
|
159b511d16 | ||
|
|
441f082fcb | ||
|
|
f6a61b999f | ||
|
|
ad0dd693ac | ||
|
|
153675e52a | ||
|
|
e2ad785422 | ||
|
|
0caa35a251 | ||
|
|
e5b19e3f70 | ||
|
|
7a5a500d29 | ||
|
|
b0fc568c80 | ||
|
|
12456f9850 | ||
|
|
f5478c871c | ||
|
|
8581b7820d | ||
|
|
c95f05a3dc | ||
|
|
cd5836243d | ||
|
|
c3dc1c272b | ||
|
|
25a2c8ab82 | ||
|
|
721ac6b847 | ||
|
|
1a0a8ac7e2 | ||
|
|
81d791eea0 | ||
|
|
e1f9ba78e5 | ||
|
|
ea387937d0 | ||
|
|
2ca0cf6fc4 | ||
|
|
be4593c1d9 | ||
|
|
aa6e8c9ec0 | ||
|
|
4aff2a32ad | ||
|
|
579afed460 | ||
|
|
a6a280669c | ||
|
|
3f2728e3f7 | ||
|
|
e0a705afe7 | ||
|
|
62ae329be3 | ||
|
|
31f95da947 | ||
|
|
a4f7079270 | ||
|
|
5b74161195 | ||
|
|
f7c9258770 | ||
|
|
8ea6983734 | ||
|
|
ddd1b4be3f | ||
|
|
05d2c7f9ae | ||
|
|
1d83fb20d6 | ||
|
|
9c57f3d339 | ||
|
|
6b0178e9fb | ||
|
|
790b371e47 | ||
|
|
cc6dfd91cf | ||
|
|
672fbd3849 | ||
|
|
935f31eff9 | ||
|
|
65e855bfff | ||
|
|
9fb3fb4c83 | ||
|
|
4487065b67 | ||
|
|
1033d34964 | ||
|
|
5b150abdcb | ||
|
|
b98d25dafb | ||
|
|
06e8e28531 | ||
|
|
92f37ca111 | ||
|
|
3a420ec808 | ||
|
|
45ededbed5 | ||
|
|
f322c608cf | ||
|
|
a612a666f3 | ||
|
|
4e26d42d17 | ||
|
|
54c2ffc36b | ||
|
|
16a8bcc77d | ||
|
|
15ebf4a0cf | ||
|
|
cca36cab70 | ||
|
|
49343e9f68 | ||
|
|
b6ade62211 | ||
|
|
323e45374f | ||
|
|
aee4fe9f7b | ||
|
|
50652be6e3 | ||
|
|
92f2d9f99f | ||
|
|
25eac80ae6 | ||
|
|
9ac1c4bc4f | ||
|
|
c3275c3f3c | ||
|
|
d9eda45b0b | ||
|
|
b9d497c61c | ||
|
|
fed39a53cb | ||
|
|
4765f937ea | ||
|
|
aadb6b1772 | ||
|
|
fb41bdd700 | ||
|
|
c71f805667 | ||
|
|
db0f2c0b10 | ||
|
|
09fa2298ba | ||
|
|
e8c0157017 | ||
|
|
a04c44fe34 | ||
|
|
334b158f0b | ||
|
|
16959c0a8c | ||
|
|
591291caee | ||
|
|
de20dfe459 | ||
|
|
8f0e63cd27 | ||
|
|
a5a73ba857 | ||
|
|
e7caa48e2f | ||
|
|
6063c4e3c0 | ||
|
|
4cd65027c4 | ||
|
|
f9f78c1086 | ||
|
|
4c7fa9482d | ||
|
|
9c529c61bb | ||
|
|
b7b53b5857 | ||
|
|
2012bdf4bd | ||
|
|
48ed6381b3 | ||
|
|
083a78135c | ||
|
|
c1e608d9ef | ||
|
|
71a0ae2e59 | ||
|
|
a671652ab2 | ||
|
|
a963618b08 | ||
|
|
397e3b1ade | ||
|
|
a18a715bb4 | ||
|
|
49ee873655 | ||
|
|
183662c0e2 | ||
|
|
5f84993bae | ||
|
|
46d148defd | ||
|
|
267e7c3a90 | ||
|
|
1a980123a2 | ||
|
|
ed98015a56 | ||
|
|
1105e52031 | ||
|
|
48808aeb8a | ||
|
|
e59b147a62 | ||
|
|
77d509eb9a | ||
|
|
ef0cb17faf | ||
|
|
26c00f8dd3 | ||
|
|
40bd121593 | ||
|
|
cde785f1bb | ||
|
|
feaa2dbba8 | ||
|
|
eedf7e44a2 | ||
|
|
51d8e9dc5e | ||
|
|
2a50a0ce69 | ||
|
|
11207c4e56 | ||
|
|
b88e850d55 | ||
|
|
adeb1f92c8 | ||
|
|
9cfe527492 | ||
|
|
1ca472cc8a | ||
|
|
b4d008b743 | ||
|
|
ae54610b2a | ||
|
|
873502c95d | ||
|
|
796f2d3c09 | ||
|
|
c52b41d311 | ||
|
|
4ad3002861 | ||
|
|
6e55b419a6 | ||
|
|
8f60f0a0cf | ||
|
|
4d5ef721f7 | ||
|
|
ad278b2007 | ||
|
|
de4c0528f9 | ||
|
|
23f0bb45b0 | ||
|
|
430492152f | ||
|
|
4a35a224e2 | ||
|
|
21d09c5bf2 | ||
|
|
8b2165e0d1 | ||
|
|
d100e68fd0 | ||
|
|
f844097f8e | ||
|
|
96b4211ea1 | ||
|
|
8f42e7f703 | ||
|
|
f08c42e920 | ||
|
|
ae8a63827d | ||
|
|
7184330acd | ||
|
|
43b80683eb | ||
|
|
42e4c37f15 | ||
|
|
32117c030a | ||
|
|
9df9915600 | ||
|
|
faadf78332 | ||
|
|
aab325eca5 | ||
|
|
9b50221bf0 | ||
|
|
2665d7d293 | ||
|
|
98b695984b | ||
|
|
8b16ba7f3a | ||
|
|
d794b834fd | ||
|
|
e626107d3d | ||
|
|
582e1bf10c | ||
|
|
ced8d2a537 | ||
|
|
cda0baa7fe | ||
|
|
0f358e3ca1 | ||
|
|
2481574a28 | ||
|
|
6381e75fa5 | ||
|
|
484b115b09 | ||
|
|
309fc6db43 | ||
|
|
dc3fe85921 | ||
|
|
30da6ab2c1 | ||
|
|
682fb171cb | ||
|
|
177c9c93e8 | ||
|
|
40b3d8ec1b | ||
|
|
fc795275f2 | ||
|
|
5f3796f6ea | ||
|
|
3c1bd90d0d | ||
|
|
9127b96033 | ||
|
|
50e9698932 | ||
|
|
fd49503ba2 | ||
|
|
2d0217abd5 | ||
|
|
f561d8f689 | ||
|
|
56a4a77398 | ||
|
|
edc93ab6aa | ||
|
|
e543dca6a0 | ||
|
|
d6c28aa64d | ||
|
|
bb7897caab | ||
|
|
610d18cc25 | ||
|
|
7dfd741ced | ||
|
|
e583d17062 | ||
|
|
692462d7c8 | ||
|
|
43c2bd1351 | ||
|
|
ab137472b0 | ||
|
|
eb819368aa | ||
|
|
9aa5e20ef7 | ||
|
|
98bf8e1304 | ||
|
|
3266e54e33 | ||
|
|
683a47f7a1 | ||
|
|
65992304bc | ||
|
|
49601558c6 | ||
|
|
1c05c004cd | ||
|
|
c8d2181498 | ||
|
|
59edf64951 | ||
|
|
0af385d73f | ||
|
|
d2755c0106 | ||
|
|
43037d893d | ||
|
|
3426d91140 | ||
|
|
ef60c533a0 | ||
|
|
7da32c7db3 | ||
|
|
6ce3ce758c | ||
|
|
16aef53470 | ||
|
|
a161048a9d | ||
|
|
38b34f2a45 | ||
|
|
df2a0e265b | ||
|
|
dc20b21fb5 | ||
|
|
b31c3bd35d | ||
|
|
633c2a289e | ||
|
|
e71c417f7e | ||
|
|
5389a35798 | ||
|
|
aaf3c2b329 | ||
|
|
35f836c4b7 | ||
|
|
3d9263bf86 | ||
|
|
2172c5034a | ||
|
|
10a25603ac | ||
|
|
7546562139 | ||
|
|
f6613e1e4c | ||
|
|
9e2bd10d03 | ||
|
|
dddbc130db | ||
|
|
4b908ebcd6 | ||
|
|
b99b2b75ca | ||
|
|
6a2edab6eb | ||
|
|
ba754825b9 | ||
|
|
e4d7d8c42d | ||
|
|
2d30b36cca | ||
|
|
65194efa97 | ||
|
|
143f905838 | ||
|
|
a14c5dbcc4 | ||
|
|
be1ebd348b | ||
|
|
537d953f4c | ||
|
|
0f033bf8f6 | ||
|
|
d806e32030 | ||
|
|
4b828dd276 | ||
|
|
60befcd8a8 | ||
|
|
4992e4a2b8 | ||
|
|
fcc1272d42 | ||
|
|
2657ece2cd | ||
|
|
ce20b05ed0 | ||
|
|
e78a706994 | ||
|
|
ce5a792e34 | ||
|
|
73a9791401 | ||
|
|
d59ed24e6c | ||
|
|
a14b9c7bac | ||
|
|
a5b3f8cae9 | ||
|
|
17201facf1 | ||
|
|
781d160c68 | ||
|
|
abf5b6be3e | ||
|
|
f0f2413932 | ||
|
|
8103856a41 | ||
|
|
7c759b193c | ||
|
|
d7c3b7633a | ||
|
|
8376fbc982 | ||
|
|
d3c073dc25 | ||
|
|
c18f13a45b | ||
|
|
07c4a74838 | ||
|
|
8d1db0ea3d | ||
|
|
0e4017cbe5 | ||
|
|
0235901614 | ||
|
|
fbdd1f39e7 | ||
|
|
777ed10e21 | ||
|
|
aaabba9b1e | ||
|
|
39707757a6 | ||
|
|
74505a116f | ||
|
|
518ab93e03 | ||
|
|
da43a5e371 | ||
|
|
c35adcf5a1 | ||
|
|
7d84cca431 | ||
|
|
e758a753f8 | ||
|
|
ccf2952b76 | ||
|
|
e295d0c091 | ||
|
|
ba02209f1d | ||
|
|
1c5ae80029 | ||
|
|
8c23b19da1 | ||
|
|
0b1727cf79 | ||
|
|
69efd2ee24 | ||
|
|
91055151ce | ||
|
|
73e5a7d671 | ||
|
|
601880f1ce | ||
|
|
28772bc9a4 | ||
|
|
04f9e7fa73 | ||
|
|
47bd4be71b | ||
|
|
1c560a967c | ||
|
|
62c5b28690 | ||
|
|
c9275f156e | ||
|
|
e71b25107d | ||
|
|
793295cfe9 | ||
|
|
128c4bb7cb | ||
|
|
f95ed7dca2 | ||
|
|
1f4e4db50d | ||
|
|
c7d808a560 | ||
|
|
ca2958a999 | ||
|
|
dc8d49260c | ||
|
|
eedaf9cd26 | ||
|
|
fa2290657a | ||
|
|
cceb29c005 | ||
|
|
a79d074436 | ||
|
|
429303bbb7 | ||
|
|
fe891aa488 | ||
|
|
9245d3b5cd | ||
|
|
bf7a51791a | ||
|
|
6d0842465b | ||
|
|
679b5ed551 | ||
|
|
b1edd911f9 | ||
|
|
774097bd10 | ||
|
|
2ddee50f27 | ||
|
|
3466461eb3 | ||
|
|
76c6ccab5d | ||
|
|
eadcd8e614 | ||
|
|
28952eda87 | ||
|
|
990514ae6d | ||
|
|
f1169fb213 | ||
|
|
f04ae7a112 | ||
|
|
ffef659782 | ||
|
|
10b7592d14 | ||
|
|
3656f7d06f | ||
|
|
469a97ddd2 | ||
|
|
0b3b4c99e6 | ||
|
|
b734f9d237 | ||
|
|
fd889fd29a | ||
|
|
b3c4305fa2 | ||
|
|
369a343fb2 | ||
|
|
bff3cd9068 | ||
|
|
08443c6421 | ||
|
|
84ab5d45fd | ||
|
|
d4242de699 | ||
|
|
f4bf9c672f | ||
|
|
9743add40e | ||
|
|
a145d1065a | ||
|
|
ac7409bbf3 | ||
|
|
0618f606b2 | ||
|
|
a8dd3c3a7c | ||
|
|
1725672168 | ||
|
|
f9f68005a5 | ||
|
|
e63e017d20 | ||
|
|
80080a3d7b | ||
|
|
be623ce8e8 | ||
|
|
0439e41a44 | ||
|
|
dde8b2afff | ||
|
|
f42a8e4e03 | ||
|
|
c1e3498a2a | ||
|
|
d5359bbdc2 | ||
|
|
0c869251ce | ||
|
|
d850b0adb1 | ||
|
|
e392ea1104 | ||
|
|
dbe4fdc73d | ||
|
|
4c8617e1bb | ||
|
|
82a2f31ada | ||
|
|
cf72931816 | ||
|
|
c6a7de0e54 | ||
|
|
14e2d31619 | ||
|
|
f95de1fd4d | ||
|
|
cbfe28286a | ||
|
|
6145013f30 | ||
|
|
e39e088f18 | ||
|
|
1f6205e1ea | ||
|
|
376febb9d1 | ||
|
|
c760ca2323 | ||
|
|
05392e0918 | ||
|
|
c917d716f7 | ||
|
|
a4a86ee23f | ||
|
|
9049db41ae | ||
|
|
21095502b9 | ||
|
|
ce80b9fa0e | ||
|
|
6e47fd54a0 | ||
|
|
106ee1bf4d | ||
|
|
6a67cc96d0 | ||
|
|
499ce5139c | ||
|
|
728ef46048 | ||
|
|
e683cccf35 | ||
|
|
e312d17eae | ||
|
|
f41d1500b0 | ||
|
|
a80e8726e7 | ||
|
|
afb323b01c | ||
|
|
2647018870 | ||
|
|
9a1fa53791 | ||
|
|
d84fd6c925 | ||
|
|
9b38a372fb | ||
|
|
389fa180c8 | ||
|
|
539f8550c7 | ||
|
|
8b21d27f04 | ||
|
|
6919f389aa | ||
|
|
b33fb26b84 | ||
|
|
b93331e844 | ||
|
|
22299d2382 | ||
|
|
bb59346651 | ||
|
|
bc3ab45af2 | ||
|
|
1c1f991d2f | ||
|
|
2c9d9577e3 | ||
|
|
c4919cf5ec | ||
|
|
4efb8b142c | ||
|
|
729193aca8 | ||
|
|
8417b9b99c | ||
|
|
d7e699ea1a | ||
|
|
caf23e6b8e | ||
|
|
07f87b9147 | ||
|
|
86c9ce9c20 | ||
|
|
5aff9e999e | ||
|
|
1c0f1a2831 | ||
|
|
56f9c1b6f7 | ||
|
|
122d5f2729 | ||
|
|
d3a6881737 | ||
|
|
2235f31ffe | ||
|
|
0048f58500 | ||
|
|
92e03e2c22 | ||
|
|
6e65f01ede | ||
|
|
19e64eb247 | ||
|
|
70f6484d9d | ||
|
|
000c538d65 | ||
|
|
390ce5719d | ||
|
|
8fd7c04920 | ||
|
|
4f2486a67a | ||
|
|
d4b01f6ffd | ||
|
|
ecb5fff2ec | ||
|
|
ef7d7e7d71 | ||
|
|
2186a89781 | ||
|
|
dad40b8d51 | ||
|
|
3dc754cac2 | ||
|
|
f0a78aa559 | ||
|
|
3d356763d0 | ||
|
|
49538e81de | ||
|
|
1f4932966a | ||
|
|
9aa1f84d45 | ||
|
|
94199b7867 | ||
|
|
aa49ec815a | ||
|
|
9decebe6e1 | ||
|
|
377576f131 | ||
|
|
a4db9abcb4 | ||
|
|
3a9c08e7c9 | ||
|
|
083309c056 | ||
|
|
efc9553561 | ||
|
|
fac82cf69b | ||
|
|
785eaf8e8f | ||
|
|
b944849bd4 | ||
|
|
b747d9d05e | ||
|
|
b90875575c | ||
|
|
110840aa98 | ||
|
|
77fdc37cb7 | ||
|
|
3b5284ec2b | ||
|
|
2f97370b8e | ||
|
|
3c01bf3a12 | ||
|
|
32e75ff808 | ||
|
|
613c8158a8 | ||
|
|
93e134aab0 | ||
|
|
43ebfa7982 | ||
|
|
8c5b420aea | ||
|
|
bd6af7c613 | ||
|
|
5bd5dd7262 | ||
|
|
f5bd3fa952 | ||
|
|
5be7d42dfd | ||
|
|
f4b64686ae | ||
|
|
36d0906ea2 | ||
|
|
487d825ede | ||
|
|
f0ae77b23b | ||
|
|
0a6462e627 | ||
|
|
be0d9d8c13 | ||
|
|
b8cf3b4c77 | ||
|
|
38d00f407e | ||
|
|
e4ef6c9645 | ||
|
|
0286f2da78 | ||
|
|
a1fcabee0e | ||
|
|
9c96cd6090 | ||
|
|
43a3400221 | ||
|
|
5a8b28c194 | ||
|
|
11ac20e5ee | ||
|
|
786891c600 | ||
|
|
0d732609f0 | ||
|
|
c5c6a69269 | ||
|
|
73f6c5fe35 | ||
|
|
5fa3450a9c | ||
|
|
ff5cad1cd6 | ||
|
|
29db084dc3 | ||
|
|
af21a11e1e | ||
|
|
cb763938dc | ||
|
|
c0da948a4e | ||
|
|
3457105504 | ||
|
|
a5b5885933 | ||
|
|
cbf973d90f | ||
|
|
97977cdb4b | ||
|
|
87160c8d2f | ||
|
|
f2209045f8 | ||
|
|
b6184ce471 | ||
|
|
444fda5d82 | ||
|
|
b6d87ae25b | ||
|
|
bf1fa014f4 | ||
|
|
7b878ea3d8 | ||
|
|
3fb1595a4e | ||
|
|
246c1a9380 | ||
|
|
8043f4fc10 | ||
|
|
e24a4b18c4 | ||
|
|
58d430fe3e | ||
|
|
dc985e0e83 | ||
|
|
f2b7c9ee66 | ||
|
|
fffc245922 | ||
|
|
f8bee0e75f | ||
|
|
eb01f9729d | ||
|
|
51c37aeee3 | ||
|
|
e00ece7a78 | ||
|
|
62163ab3d3 | ||
|
|
2ecd2a3c44 | ||
|
|
4f1cc41b86 | ||
|
|
d4071575a4 | ||
|
|
2336bcfe20 | ||
|
|
b3b1df7184 | ||
|
|
d3cd3bc5ef | ||
|
|
e1a98c1ff7 | ||
|
|
a8fd92ddc1 | ||
|
|
8f787c08a2 | ||
|
|
7a968a5f0d | ||
|
|
56192daabf | ||
|
|
ce0676ac70 | ||
|
|
e2b4ae13fa | ||
|
|
a30f38481d | ||
|
|
12d99b3292 | ||
|
|
6b94b5334c | ||
|
|
ada1ab3509 | ||
|
|
a86023eb09 | ||
|
|
5245928648 | ||
|
|
0aeb7c6484 | ||
|
|
4adc372f9a | ||
|
|
4576c1ebc3 | ||
|
|
e4a1cf0cd2 | ||
|
|
5558191a2a | ||
|
|
d4c4dddfc3 | ||
|
|
70313df531 | ||
|
|
723fa9eebc | ||
|
|
1dce3c98d8 | ||
|
|
fefe95052d | ||
|
|
2e314a20f1 | ||
|
|
9a3dcb9ad1 | ||
|
|
30909a9b79 | ||
|
|
0f655e4430 | ||
|
|
659d007bf0 | ||
|
|
b49caf170c | ||
|
|
33d0e09497 | ||
|
|
6da45144d0 | ||
|
|
a03b4ce213 | ||
|
|
9d453c91f1 | ||
|
|
61110dbfe4 | ||
|
|
3585daab95 | ||
|
|
aea7188304 | ||
|
|
ccb2889cac | ||
|
|
72d5366e96 | ||
|
|
448c5ff3dc | ||
|
|
7dc8ab4069 | ||
|
|
f1c4aea7b5 | ||
|
|
6bfd193b0d | ||
|
|
741b5b4978 | ||
|
|
f19049e643 | ||
|
|
01c2e4d2cf | ||
|
|
ef7def8f1d | ||
|
|
5b02adbd33 | ||
|
|
9d1be48bd2 | ||
|
|
aab2c9c682 | ||
|
|
b4a102d119 | ||
|
|
a6fbb80b94 | ||
|
|
008542b715 | ||
|
|
8efd305afd | ||
|
|
506b289b2a | ||
|
|
2f00413864 | ||
|
|
c6380a25d6 | ||
|
|
5ab5811770 | ||
|
|
75fcab04b1 | ||
|
|
4b13452022 | ||
|
|
2a100abef1 | ||
|
|
3abd00f3bb | ||
|
|
fb54277484 | ||
|
|
74c2458bdb | ||
|
|
a349b58306 | ||
|
|
4a7add2169 | ||
|
|
987ac513c8 | ||
|
|
57257a1795 | ||
|
|
3c055f94e1 | ||
|
|
118b0c0f86 | ||
|
|
10d94ed539 | ||
|
|
86395c6adb | ||
|
|
acb88e5c57 | ||
|
|
78d1b83d11 | ||
|
|
1a61d4e8a8 | ||
|
|
ad8d0efa29 | ||
|
|
237299948a | ||
|
|
0efc6a9abe | ||
|
|
92b0f9cd7e | ||
|
|
875ff15109 | ||
|
|
58e553151e | ||
|
|
7919513c8a | ||
|
|
e424ad5ff2 | ||
|
|
64f8010a25 | ||
|
|
1c385541fa | ||
|
|
08f2e4edc3 | ||
|
|
6e8395cccd | ||
|
|
68907ca783 | ||
|
|
75396c02d2 | ||
|
|
069cb10d6f | ||
|
|
2e9f531e2c | ||
|
|
2085626390 | ||
|
|
b05fdb28ff | ||
|
|
30039e8e62 | ||
|
|
4a38ce659d | ||
|
|
8f51ccd002 | ||
|
|
a76d3827ec | ||
|
|
eefc9b7172 | ||
|
|
ffa3071d36 | ||
|
|
9e0e308a44 | ||
|
|
73f4d5931d | ||
|
|
2d41704424 | ||
|
|
25072e5d32 | ||
|
|
d522f13d55 | ||
|
|
3a6b095ed4 | ||
|
|
5782c4469a | ||
|
|
6881b68ed7 | ||
|
|
9dbaaa33f5 | ||
|
|
69ffef8c0e | ||
|
|
61c143cb82 | ||
|
|
318da16b99 | ||
|
|
af5cbc881f | ||
|
|
7da39c3ff3 | ||
|
|
ac57101833 | ||
|
|
87ca23736d | ||
|
|
6cd7ef9cc3 | ||
|
|
c42fef541a | ||
|
|
5ac27100a5 | ||
|
|
bd4b4ddd8b | ||
|
|
d19b664ba9 | ||
|
|
ac66538651 | ||
|
|
9d02fbadb4 | ||
|
|
902797d0f0 | ||
|
|
6aa8fd0f7b | ||
|
|
8566832dd5 | ||
|
|
9e43c9cff3 | ||
|
|
b27f3ab327 | ||
|
|
dae112eed2 | ||
|
|
9ab10def49 | ||
|
|
c1f6dd46d1 | ||
|
|
5fd468d9ec | ||
|
|
e9b0c7177f | ||
|
|
915102a400 | ||
|
|
5187a9a5ad | ||
|
|
9b626d06fc | ||
|
|
1af22e5312 | ||
|
|
77906ea4ab | ||
|
|
40bf1a50fd | ||
|
|
91d6454f87 | ||
|
|
e5b04d54ff | ||
|
|
ee889afd4c | ||
|
|
299da5d596 | ||
|
|
1f6f2747d4 | ||
|
|
264f98af14 | ||
|
|
122f1c0ced | ||
|
|
f9a7b31b5b | ||
|
|
36f5883dda | ||
|
|
0e26df331c | ||
|
|
b4a2eb2e65 | ||
|
|
fdd167cac1 | ||
|
|
fe73d55f70 | ||
|
|
277ef04b60 | ||
|
|
a956e20f29 | ||
|
|
9fd0c25c9f | ||
|
|
1cb7ae16ab | ||
|
|
2effbb55ae | ||
|
|
bf5c43322a | ||
|
|
e888639c7e | ||
|
|
8cb7567fd3 | ||
|
|
42de9ca49e | ||
|
|
ae3dce0cbd | ||
|
|
f4673941e0 | ||
|
|
3b15966cc9 | ||
|
|
520306dc87 | ||
|
|
588ca68171 | ||
|
|
3b51874da5 | ||
|
|
f92f3e0208 | ||
|
|
f8c11847bb | ||
|
|
1d28ea5458 | ||
|
|
dd719099bc | ||
|
|
c247cf728c | ||
|
|
addd7347d8 | ||
|
|
756fe4b375 | ||
|
|
aafb735283 | ||
|
|
256b4245d5 | ||
|
|
2fc6833684 | ||
|
|
7e88eb549f | ||
|
|
ea82fe5bc2 | ||
|
|
eabd3135f0 | ||
|
|
d7ed4093d8 | ||
|
|
72a7ed5b58 | ||
|
|
86bdddd1b8 | ||
|
|
1a6508972e | ||
|
|
8704ca783d | ||
|
|
d38778e400 | ||
|
|
f1a612245c | ||
|
|
967dd398e7 | ||
|
|
2d04e71412 | ||
|
|
396667b702 | ||
|
|
ea84c157e0 | ||
|
|
0b39a0123e | ||
|
|
dd08045f28 | ||
|
|
795c94384a | ||
|
|
313625c349 | ||
|
|
538cd6fdcf | ||
|
|
5fd0770372 | ||
|
|
3b4c0a3fc0 | ||
|
|
3ef551872a | ||
|
|
27891ecb77 | ||
|
|
0047e18a9b | ||
|
|
e5e88bb9f1 | ||
|
|
4ff1cba522 | ||
|
|
79ac50d0f7 | ||
|
|
72869ed197 | ||
|
|
5018472840 | ||
|
|
33762dbbac | ||
|
|
eb25eddc22 | ||
|
|
909945c0ac | ||
|
|
bb40e38451 | ||
|
|
7ecd67605f | ||
|
|
08d7c48dc7 | ||
|
|
32b30bc5de | ||
|
|
a2252c9236 | ||
|
|
e93a1cc02e | ||
|
|
6b510546ae | ||
|
|
ddd24ea8c8 | ||
|
|
534d7ce64b | ||
|
|
5c4cc5ae5b | ||
|
|
9ef8d5c5c3 | ||
|
|
b85dbdc3c1 | ||
|
|
e5a68b2dcb | ||
|
|
d05082987f | ||
|
|
a3625b3817 | ||
|
|
03002f7431 | ||
|
|
80f508c4b1 | ||
|
|
a272d73dd9 | ||
|
|
452584c4bd | ||
|
|
c95b986414 | ||
|
|
af0a353b79 | ||
|
|
ef312b8fc4 | ||
|
|
5f1562c5b2 | ||
|
|
91762097a5 | ||
|
|
17b9bfd249 | ||
|
|
ea88451875 | ||
|
|
9c580dde39 | ||
|
|
9baa222976 | ||
|
|
0ef9c03f05 | ||
|
|
bb00d38dd7 | ||
|
|
06aead0470 | ||
|
|
cdb1800087 | ||
|
|
13f8edd43f | ||
|
|
85d1a237ce | ||
|
|
dc3c27fd1b | ||
|
|
2d9da22721 | ||
|
|
86a6293e62 | ||
|
|
c311b8ea4f | ||
|
|
e832944dfe | ||
|
|
003cfe2717 | ||
|
|
132b517584 | ||
|
|
d173e06e69 | ||
|
|
9ae0380a96 | ||
|
|
8873ef7b67 | ||
|
|
dc24a657fd | ||
|
|
8c54be7e99 | ||
|
|
7a5d75b68d | ||
|
|
3b9bc8e4ef | ||
|
|
702b5c32c1 | ||
|
|
6c748966e7 |
32
.github/workflows/initiate_release.yml
vendored
Normal file
32
.github/workflows/initiate_release.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# This workflow is agnostic to branches. Only maintain on develop branch.
|
||||
# To add/remove versions just modify the matrix.
|
||||
|
||||
name: Create weekly release pull requests
|
||||
on:
|
||||
schedule:
|
||||
# 9:30 UTC => 3 PM IST Tuesday
|
||||
- cron: "30 9 * * 2"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version: ["13", "14"]
|
||||
|
||||
steps:
|
||||
- uses: octokit/request-action@v2.x
|
||||
with:
|
||||
route: POST /repos/{owner}/{repo}/pulls
|
||||
owner: frappe
|
||||
repo: erpnext
|
||||
title: |-
|
||||
"chore: release v${{ matrix.version }}"
|
||||
body: "Automated weekly release."
|
||||
base: version-${{ matrix.version }}
|
||||
head: version-${{ matrix.version }}-hotfix
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
2
.github/workflows/patch.yml
vendored
2
.github/workflows/patch.yml
vendored
@@ -11,7 +11,7 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: patch-develop-${{ github.event.number }}
|
||||
group: patch-develop-${{ github.event_name }}-${{ github.event.number || github.event_name == 'workflow_dispatch' && github.run_id || '' }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
|
||||
6
.github/workflows/server-tests-mariadb.yml
vendored
6
.github/workflows/server-tests-mariadb.yml
vendored
@@ -27,7 +27,7 @@ on:
|
||||
type: string
|
||||
|
||||
concurrency:
|
||||
group: server-mariadb-develop-${{ github.event.number }}
|
||||
group: server-mariadb-develop-${{ github.event_name }}-${{ github.event.number || github.event_name == 'workflow_dispatch' && github.run_id || '' }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
@@ -59,7 +59,7 @@ jobs:
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.10'
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Check for valid Python & Merge Conflicts
|
||||
run: |
|
||||
@@ -120,7 +120,7 @@ jobs:
|
||||
FRAPPE_BRANCH: ${{ github.event.inputs.branch }}
|
||||
|
||||
- name: Run Tests
|
||||
run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --app erpnext --use-orchestrator --with-coverage
|
||||
run: 'cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --app erpnext --with-coverage --total-builds 4 --build-number ${{ matrix.container }}'
|
||||
env:
|
||||
TYPE: server
|
||||
CI_BUILD_ID: ${{ github.run_id }}
|
||||
|
||||
2
.github/workflows/server-tests-postgres.yml
vendored
2
.github/workflows/server-tests-postgres.yml
vendored
@@ -9,7 +9,7 @@ on:
|
||||
types: [opened, labelled, synchronize, reopened]
|
||||
|
||||
concurrency:
|
||||
group: server-postgres-develop-${{ github.event.number }}
|
||||
group: server-postgres-develop-${{ github.event_name }}-${{ github.event.number || github.event_name == 'workflow_dispatch' && github.run_id || '' }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -9,6 +9,7 @@ pull_request_rules:
|
||||
- author!=nabinhait
|
||||
- author!=ankush
|
||||
- author!=deepeshgarg007
|
||||
- author!=frappe-pr-bot
|
||||
- author!=mergify[bot]
|
||||
|
||||
- or:
|
||||
|
||||
24
CODEOWNERS
24
CODEOWNERS
@@ -11,22 +11,18 @@ erpnext/selling @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
|
||||
erpnext/support/ @nextchamp-saqib @deepeshgarg007
|
||||
pos* @nextchamp-saqib
|
||||
|
||||
erpnext/buying/ @marination @rohitwaghchaure @s-aga-r
|
||||
erpnext/e_commerce/ @marination
|
||||
erpnext/maintenance/ @marination @rohitwaghchaure @s-aga-r
|
||||
erpnext/manufacturing/ @marination @rohitwaghchaure @s-aga-r
|
||||
erpnext/portal/ @marination
|
||||
erpnext/quality_management/ @marination @rohitwaghchaure @s-aga-r
|
||||
erpnext/shopping_cart/ @marination
|
||||
erpnext/stock/ @marination @rohitwaghchaure @s-aga-r
|
||||
erpnext/buying/ @rohitwaghchaure @s-aga-r
|
||||
erpnext/maintenance/ @rohitwaghchaure @s-aga-r
|
||||
erpnext/manufacturing/ @rohitwaghchaure @s-aga-r
|
||||
erpnext/quality_management/ @rohitwaghchaure @s-aga-r
|
||||
erpnext/stock/ @rohitwaghchaure @s-aga-r
|
||||
|
||||
erpnext/crm/ @NagariaHussain
|
||||
erpnext/education/ @rutwikhdev
|
||||
erpnext/crm/ @NagariaHussain
|
||||
erpnext/education/ @rutwikhdev
|
||||
erpnext/projects/ @ruchamahabal
|
||||
|
||||
erpnext/controllers/ @deepeshgarg007 @nextchamp-saqib @rohitwaghchaure @marination
|
||||
erpnext/patches/ @deepeshgarg007 @nextchamp-saqib @marination
|
||||
erpnext/public/ @nextchamp-saqib @marination
|
||||
erpnext/controllers/ @deepeshgarg007 @nextchamp-saqib @rohitwaghchaure
|
||||
erpnext/patches/ @deepeshgarg007 @nextchamp-saqib
|
||||
|
||||
.github/ @ankush
|
||||
pyproject.toml @gavindsouza @ankush
|
||||
pyproject.toml @ankush
|
||||
|
||||
@@ -82,6 +82,8 @@ GNU/General Public License (see [license.txt](license.txt))
|
||||
|
||||
The ERPNext code is licensed as GNU General Public License (v3) and the Documentation is licensed as Creative Commons (CC-BY-SA-3.0) and the copyright is owned by Frappe Technologies Pvt Ltd (Frappe) and Contributors.
|
||||
|
||||
By contributing to ERPNext, you agree that your contributions will be licensed under its GNU General Public License (v3).
|
||||
|
||||
## Logo and Trademark Policy
|
||||
|
||||
Please read our [Logo and Trademark Policy](TRADEMARK_POLICY.md).
|
||||
|
||||
@@ -37,7 +37,7 @@ class Account(NestedSet):
|
||||
def autoname(self):
|
||||
from erpnext.accounts.utils import get_autoname_with_number
|
||||
|
||||
self.name = get_autoname_with_number(self.account_number, self.account_name, None, self.company)
|
||||
self.name = get_autoname_with_number(self.account_number, self.account_name, self.company)
|
||||
|
||||
def validate(self):
|
||||
from erpnext.accounts.utils import validate_field_number
|
||||
|
||||
@@ -2,397 +2,438 @@
|
||||
"country_code": "at",
|
||||
"name": "Austria - Chart of Accounts",
|
||||
"tree": {
|
||||
"Summe Abschreibungen und Aufwendungen": {
|
||||
"7010 bis 7080 Abschreibungen auf das Anlageverm\u00f6gen (ausgenommen Finanzanlagen)": {},
|
||||
"7100 bis 7190 Sonstige Steuern": {
|
||||
"account_type": "Tax"
|
||||
},
|
||||
"7200 bis 7290 Instandhaltung u. Reinigung durh Dritte, Entsorgung, Beleuchtung": {},
|
||||
"7300 bis 7310 Transporte durch Dritte": {},
|
||||
"7320 bis 7330 Kfz - Aufwand": {},
|
||||
"7340 bis 7350 Reise- und Fahraufwand": {},
|
||||
"7360 bis 7370 Tag- und N\u00e4chtigungsgelder": {},
|
||||
"7380 bis 7390 Nachrichtenaufwand": {},
|
||||
"7400 bis 7430 Miet- und Pachtaufwand": {},
|
||||
"7440 bis 7470 Leasingaufwand": {},
|
||||
"7480 bis 7490 Lizenzaufwand": {},
|
||||
"7500 bis 7530 Aufwand f\u00fcr beigestelltes Personal": {},
|
||||
"7540 bis 7570 Provisionen an Dritte": {},
|
||||
"7580 bis 7590 Aufsichtsratsverg\u00fctungen": {},
|
||||
"7610 bis 7620 Druckerzeugnisse und Vervielf\u00e4ltigungen": {},
|
||||
"7650 bis 7680 Werbung und Repr\u00e4sentationen": {},
|
||||
"7700 bis 7740 Versicherungen": {},
|
||||
"7750 bis 7760 Beratungs- und Pr\u00fcfungsaufwand": {},
|
||||
"7800 bis 7810 Schadensf\u00e4lle": {},
|
||||
"7840 bis 7880 Verschiedene betriebliche Aufwendungen": {},
|
||||
"7910 bis 7950 Aufwandsstellenrechung der Hersteller": {},
|
||||
"Abschreibungen auf aktivierte Aufwendungen f\u00fcr das Ingangs. u. Erweitern des Betriebes": {},
|
||||
"Abschreibungen vom Umlaufverm\u00f6gen, soweit diese die im Unternehmen \u00fcblichen Abschreibungen \u00fcbersteigen": {},
|
||||
"Aufwandsstellenrechnung": {},
|
||||
"Aus- und Fortbildung": {},
|
||||
"Buchwert abgegangener Anlagen, ausgenommen Finanzanlagen": {},
|
||||
"B\u00fcromaterial und Drucksorten": {},
|
||||
"Fachliteratur und Zeitungen ": {},
|
||||
"Herstellungskosten der zur Erzielung der Umsatzerl\u00f6se erbrachten Leistungen": {},
|
||||
"Mitgliedsbeitr\u00e4ge": {},
|
||||
"Skontoertr\u00e4ge auf sonstige betriebliche Aufwendungen": {},
|
||||
"Sonstige betrieblichen Aufwendungen": {},
|
||||
"Spenden und Trinkgelder": {},
|
||||
"Spesen des Geldverkehrs": {},
|
||||
"Verluste aus dem Abgang vom Anlageverm\u00f6gen, ausgenommen Finanzanlagen": {},
|
||||
"Vertriebskosten": {},
|
||||
"Verwaltungskosten": {},
|
||||
"root_type": "Expense"
|
||||
},
|
||||
"Summe Betriebliche Ertr\u00e4ge": {
|
||||
"4400 bis 4490 Erl\u00f6sschm\u00e4lerungen": {},
|
||||
"4500 bis 4570 Ver\u00e4nderungen des Bestandes an fertigen und unfertigen Erzeugn. sowie an noch nicht abrechenbaren Leistungen": {},
|
||||
"4580 bis 4590 andere aktivierte Eigenleistungen": {},
|
||||
"4600 bis 4620 Erl\u00f6se aus dem Abgang vom Anlageverm\u00f6gen, ausgen. Finanzanlagen": {},
|
||||
"4630 bis 4650 Ertr\u00e4ge aus dem Abgang vom Anlageverm\u00f6gen, ausgen. Finanzanlagen": {},
|
||||
"4660 bis 4670 Ertr\u00e4ge aus der Zuschreibung zum Anlageverm\u00f6gen, ausgen. Finanzanlagen": {},
|
||||
"4700 bis 4790 Ertr\u00e4ge aus der Aufl\u00f6sung von R\u00fcckstellungen": {},
|
||||
"4800 bis 4990 \u00dcbrige betriebliche Ertr\u00e4ge": {},
|
||||
"Erl\u00f6se 0 % Ausfuhrlieferungen/Drittl\u00e4nder": {},
|
||||
"Erl\u00f6se 10 %": {},
|
||||
"Erl\u00f6se 20 %": {},
|
||||
"Erl\u00f6se aus im Inland stpfl. EG Lieferungen 10 % USt": {},
|
||||
"Erl\u00f6se aus im Inland stpfl. EG Lieferungen 20 % USt": {},
|
||||
"Erl\u00f6se i.g. Lieferungen (stfr)": {},
|
||||
"root_type": "Income"
|
||||
},
|
||||
"Summe Eigenkapital R\u00fccklagen Abschlusskonten": {
|
||||
"9000 bis 9180 Gezeichnetes bzw. gewidmetes Kapital": {
|
||||
"account_type": "Equity"
|
||||
},
|
||||
"9200 bis 9290 Kapitalr\u00fccklagen": {
|
||||
"account_type": "Equity"
|
||||
},
|
||||
"9300 bis 9380 Gewinnr\u00fccklagen": {
|
||||
"account_type": "Equity"
|
||||
},
|
||||
"9400 bis 9590 Bewertungsreserven uns sonst. unversteuerte R\u00fccklagen": {
|
||||
"account_type": "Equity"
|
||||
},
|
||||
"9600 bis 9690 Privat und Verrechnungskonten bei Einzelunternehmen und Personengesellschaften": {},
|
||||
"9700 bis 9790 Einlagen stiller Gesellschafter ": {},
|
||||
"9900 bis 9999 Evidenzkonten": {},
|
||||
"Bilanzgewinn (-verlust )": {
|
||||
"account_type": "Equity"
|
||||
},
|
||||
"Er\u00f6ffnungsbilanz": {},
|
||||
"Gewinn- und Verlustrechnung": {},
|
||||
"Schlussbilanz": {},
|
||||
"nicht eingeforderte ausstehende Einlagen": {
|
||||
"account_type": "Equity"
|
||||
},
|
||||
"root_type": "Equity"
|
||||
},
|
||||
"Summe Finanzertr\u00e4ge und Aufwendungen": {
|
||||
"8000 bis 8040 Ertr\u00e4ge aus Beteiligungen": {},
|
||||
"8050 bis 8090 Ertr\u00e4ge aus anderen Wertpapieren und Ausleihungen des Finanzanlageverm\u00f6gens": {},
|
||||
"8100 bis 8130 Sonstige Zinsen und \u00e4hnliche Ertr\u00e4ge": {},
|
||||
"8220 bis 8250 Aufwendungen aus Beteiligungen": {},
|
||||
"8260 bis 8270 Aufwendungen aus sonst. Fiananzanlagen und aus Wertpapieren des Umlaufverm\u00f6gens": {},
|
||||
"8280 bis 8340 Zinsen und \u00e4hnliche Aufwendungem": {},
|
||||
"8400 bis 8440 Au\u00dferordentliche Ertr\u00e4ge": {},
|
||||
"8450 bis 8490 Au\u00dferordentliche Aufwendungen": {},
|
||||
"8500 bis 8590 Steuern vom Einkommen und vom Ertrag": {
|
||||
"account_type": "Tax"
|
||||
},
|
||||
"8600 bis 8690 Aufl\u00f6sung unversteuerten R\u00fccklagen": {},
|
||||
"8700 bis 8740 Aufl\u00f6sung von Kapitalr\u00fccklagen": {},
|
||||
"8750 bis 8790 Aufl\u00f6sung von Gewinnr\u00fccklagen": {},
|
||||
"8800 bis 8890 Zuweisung von unversteuerten R\u00fccklagen": {},
|
||||
"Buchwert abgegangener Beteiligungen": {},
|
||||
"Buchwert abgegangener Wertpapiere des Umlaufverm\u00f6gens": {},
|
||||
"Buchwert abgegangener sonstiger Finanzanlagen": {},
|
||||
"Erl\u00f6se aus dem Abgang von Beteiligungen": {},
|
||||
"Erl\u00f6se aus dem Abgang von Wertpapieren des Umlaufverm\u00f6gens": {},
|
||||
"Erl\u00f6se aus dem Abgang von sonstigen Finanzanlagen": {},
|
||||
"Ertr\u00e4ge aus dem Abgang von und der Zuschreibung zu Finanzanlagen": {},
|
||||
"Ertr\u00e4ge aus dem Abgang von und der Zuschreibung zu Wertpapieren des Umlaufverm\u00f6gens": {},
|
||||
"Gewinabfuhr bzw. Verlust\u00fcberrechnung aus Ergebnisabf\u00fchrungsvertr\u00e4gen": {},
|
||||
"nicht ausgenutzte Lieferantenskonti": {},
|
||||
"root_type": "Income"
|
||||
},
|
||||
"Summe Fremdkapital": {
|
||||
"3020 bis 3030 Steuerr\u00fcckstellungen": {},
|
||||
"3040 bis 3090 Sonstige R\u00fcckstellungen": {},
|
||||
"3110 bis 3170 Verbindlichkeiten gegen\u00fcber Kredidinstituten": {},
|
||||
"3180 bis 3190 Verbindlichkeiten gegen\u00fcber Finanzinstituten": {},
|
||||
"3380 bis 3390 Verbindlichkeiten aus der Annahme gezogener Wechsel u. d. Ausstellungen eigener Wechsel": {
|
||||
"Klasse 0 Aktiva: Anlageverm\u00f6gen": {
|
||||
"0100 Konzessionen ": {"account_type": "Fixed Asset"},
|
||||
"0110 Patentrechte und Lizenzen ": {"account_type": "Fixed Asset"},
|
||||
"0120 Datenverarbeitungsprogramme ": {"account_type": "Fixed Asset"},
|
||||
"0130 Marken, Warenzeichen und Musterschutzrechte, sonstige Urheberrechte ": {"account_type": "Fixed Asset"},
|
||||
"0140 Pacht- und Mietrechte ": {"account_type": "Fixed Asset"},
|
||||
"0150 Bezugs- und ähnliche Rechte ": {"account_type": "Fixed Asset"},
|
||||
"0160 Geschäfts-/Firmenwert ": {"account_type": "Fixed Asset"},
|
||||
"0170 Umgründungsmehrwert ": {"account_type": "Fixed Asset"},
|
||||
"0180 Geleistete Anzahlungen auf immaterielle Vermögensgegenstände": {"account_type": "Fixed Asset"},
|
||||
"0190 Kumulierte Abschreibungen zu immateriellen Vermögensgegenständen ": {"account_type": "Fixed Asset"},
|
||||
"0200 Unbebaute Grundstücke, soweit nicht landwirtschaftlich genutzt ": {"account_type": "Fixed Asset"},
|
||||
"0210 Bebaute Grundstücke (Grundwert) ": {"account_type": "Fixed Asset"},
|
||||
"0220 Landwirtschaftlich genutzte Grundstücke ": {"account_type": "Fixed Asset"},
|
||||
"0230 Grundstücksgleiche Rechte ": {"account_type": "Fixed Asset"},
|
||||
"0300 Betriebs- und Geschäftsgebäude auf eigenem Grund ": {"account_type": "Fixed Asset"},
|
||||
"0310 Wohn- und Sozialgebäude auf eigenem Grund ": {"account_type": "Fixed Asset"},
|
||||
"0320 Betriebs- und Geschäftsgebäude auf fremdem Grund ": {"account_type": "Fixed Asset"},
|
||||
"0330 Wohn- und Sozialgebäude auf fremdem Grund ": {"account_type": "Fixed Asset"},
|
||||
"0340 Grundstückseinrichtungen auf eigenem Grund ": {"account_type": "Fixed Asset"},
|
||||
"0350 Grundstückseinrichtungen auf fremdem Grund ": {"account_type": "Fixed Asset"},
|
||||
"0360 Bauliche Investitionen in fremden (gepachteten) Betriebs- und Geschäftsgebäuden": {"account_type": "Fixed Asset"},
|
||||
"0370 Bauliche Investitionen in fremden (gepachteten) Wohn- und Sozialgebäuden": {"account_type": "Fixed Asset"},
|
||||
"0390 Kumulierte Abschreibungen zu Grundstücken ": {"account_type": "Fixed Asset"},
|
||||
"0400 Maschinen und Geräte ": {"account_type": "Fixed Asset"},
|
||||
"0500 Maschinenwerkzeuge ": {"account_type": "Fixed Asset"},
|
||||
"0510 Allgemeine Werkzeuge und Handwerkzeuge ": {"account_type": "Fixed Asset"},
|
||||
"0520 Prototypen, Formen, Modelle ": {"account_type": "Fixed Asset"},
|
||||
"0530 Andere Erzeugungshilfsmittel (auch Softwarewerkzeuge)": {"account_type": "Fixed Asset"},
|
||||
"0540 Hebezeuge und Montageanlagen ": {"account_type": "Fixed Asset"},
|
||||
"0550 Geringwertige Vermögensgegenstände, soweit im Erzeugungsprozess ": {"account_type": "Fixed Asset"},
|
||||
"0560 Festwerte technische Anlagen und Maschinen ": {"account_type": "Fixed Asset"},
|
||||
"0590 Kumulierte Abschreibungen zu technischen Anlagen und Maschinen ": {"account_type": "Fixed Asset"},
|
||||
"0600 Betriebs- und Geschäftsausstattung, soweit nicht gesondert angeführt ": {"account_type": "Fixed Asset"},
|
||||
"0610 Andere Anlagen, soweit nicht gesondert angeführt ": {"account_type": "Fixed Asset"},
|
||||
"0620 Büromaschinen, EDV-Anlagen ": {"account_type": "Fixed Asset"},
|
||||
"0630 PKW und Kombis ": {"account_type": "Fixed Asset"},
|
||||
"0640 LKW ": {"account_type": "Fixed Asset"},
|
||||
"0650 Andere Beförderungsmittel ": {"account_type": "Fixed Asset"},
|
||||
"0660 Gebinde ": {"account_type": "Fixed Asset"},
|
||||
"0670 Geringwertige Vermögensgegenstände, soweit nicht im Erzeugungssprozess verwendet": {"account_type": "Fixed Asset"},
|
||||
"0680 Festwerte außer technische Anlagen und Maschinen ": {"account_type": "Fixed Asset"},
|
||||
"0690 Kumulierte Abschreibungen zu anderen Anlagen, Betriebs- und Geschäftsausstattung": {"account_type": "Fixed Asset"},
|
||||
"0700 Geleistete Anzahlungen auf Sachanlagen ": {"account_type": "Fixed Asset"},
|
||||
"0710 Anlagen in Bau ": {"account_type": "Fixed Asset"},
|
||||
"0790 Kumulierte Abschreibungen zu geleisteten Anzahlungen auf Sachanlagen ": {"account_type": "Fixed Asset"},
|
||||
"0800 Anteile an verbundenen Unternehmen ": {"account_type": "Fixed Asset"},
|
||||
"0810 Beteiligungen an Gemeinschaftsunternehmen ": {"account_type": "Fixed Asset"},
|
||||
"0820 Beteiligungen an angeschlossenen (assoziierten) Unternehmen ": {"account_type": "Fixed Asset"},
|
||||
"0830 Eigene Anteile, Anteile an herrschenden oder mit Mehrheit beteiligten ": {"account_type": "Fixed Asset"},
|
||||
"0840 Sonstige Beteiligungen ": {"account_type": "Fixed Asset"},
|
||||
"0850 Ausleihungen an verbundene Unternehmen ": {"account_type": "Fixed Asset"},
|
||||
"0860 Ausleihungen an Unternehmen mit Beteiligungsverhältnis": {"account_type": "Fixed Asset"},
|
||||
"0870 Ausleihungen an Gesellschafter ": {"account_type": "Fixed Asset"},
|
||||
"0880 Sonstige Ausleihungen ": {"account_type": "Fixed Asset"},
|
||||
"0890 Anteile an Kapitalgesellschaften ohne Beteiligungscharakter ": {"account_type": "Fixed Asset"},
|
||||
"0900 Anteile an Personengesellschaften ohne Beteiligungscharakter ": {"account_type": "Fixed Asset"},
|
||||
"0910 Genossenschaftsanteile ohne Beteiligungscharakter ": {"account_type": "Fixed Asset"},
|
||||
"0920 Anteile an Investmentfonds ": {"account_type": "Fixed Asset"},
|
||||
"0930 Festverzinsliche Wertpapiere des Anlagevermögens ": {"account_type": "Fixed Asset"},
|
||||
"0980 Geleistete Anzahlungen auf Finanzanlagen ": {"account_type": "Fixed Asset"},
|
||||
"0990 Kumulierte Abschreibungen zu Finanzanlagen ": {"account_type": "Fixed Asset"},
|
||||
"root_type": "Asset"
|
||||
},
|
||||
"Klasse 1 Aktiva: Vorr\u00e4te": {
|
||||
"1000 Bezugsverrechnung": {"account_type": "Stock"},
|
||||
"1100 Rohstoffe": {"account_type": "Stock"},
|
||||
"1200 Bezogene Teile": {"account_type": "Stock"},
|
||||
"1300 Hilfsstoffe": {"account_type": "Stock"},
|
||||
"1350 Betriebsstoffe": {"account_type": "Stock"},
|
||||
"1360 Vorrat Energietraeger": {"account_type": "Stock"},
|
||||
"1400 Unfertige Erzeugnisse": {"account_type": "Stock"},
|
||||
"1500 Fertige Erzeugnisse": {"account_type": "Stock"},
|
||||
"1600 Handelswarenvorrat": {"account_type": "Stock Received But Not Billed"},
|
||||
"1700 Noch nicht abrechenbare Leistungen": {"account_type": "Stock"},
|
||||
"1900 Wertberichtigungen": {"account_type": "Stock"},
|
||||
"1800 Geleistete Anzahlungen": {"account_type": "Stock"},
|
||||
"1900 Wertberichtigungen": {"account_type": "Stock"},
|
||||
"root_type": "Asset"
|
||||
},
|
||||
"Klasse 3 Passiva: Verbindlichkeiten": {
|
||||
"3000 Allgemeine Verbindlichkeiten (Schuld)": {"account_type": "Payable"},
|
||||
"3010 R\u00fcckstellungen f\u00fcr Pensionen": {"account_type": "Payable"},
|
||||
"3020 Steuerr\u00fcckstellungen": {"account_type": "Tax"},
|
||||
"3041 Sonstige R\u00fcckstellungen": {"account_type": "Payable"},
|
||||
"3110 Verbindlichkeiten gegen\u00fcber Bank": {"account_type": "Payable"},
|
||||
"3150 Verbindlichkeiten Darlehen": {"account_type": "Payable"},
|
||||
"3185 Verbindlichkeiten Kreditkarte": {"account_type": "Payable"},
|
||||
"3380 Verbindlichkeiten aus der Annahme gezogener Wechsel u. d. Ausstellungen eigener Wechsel": {
|
||||
"account_type": "Payable"
|
||||
},
|
||||
"3400 bis 3470 Verbindlichkeiten gegen\u00fc. verb. Untern., Verbindl. gegen\u00fc. Untern., mit denen eine Beteiligungsverh\u00e4lnis besteht": {},
|
||||
"3600 bis 3690 Verbindlichkeiten im Rahmen der sozialen Sicherheit": {},
|
||||
"3700 bis 3890 \u00dcbrige sonstige Verbindlichkeiten": {},
|
||||
"3900 bis 3990 Passive Rechnungsabgrenzungsposten": {},
|
||||
"Anleihen (einschlie\u00dflich konvertibler)": {},
|
||||
"Erhaltene Anzahlungenauf Bestellungen": {},
|
||||
"R\u00fcckstellungen f\u00fcr Abfertigung": {},
|
||||
"R\u00fcckstellungen f\u00fcr Pensionen": {},
|
||||
"USt. \u00a719 /art (reverse charge)": {
|
||||
"3400 Verbindlichkeiten gegen\u00fc. verb. Untern., Verbindl. gegen\u00fc. Untern., mit denen eine Beteiligungsverh\u00e4lnis besteht": {},
|
||||
"3460 Verbindlichkeiten gegenueber Gesellschaftern": {"account_type": "Payable"},
|
||||
"3470 Einlagen stiller Gesellschafter": {"account_type": "Payable"},
|
||||
"3585 Verbindlichkeiten Lohnsteuer": {"account_type": "Tax"},
|
||||
"3590 Verbindlichkeiten Kommunalabgaben": {"account_type": "Tax"},
|
||||
"3595 Verbindlichkeiten Dienstgeberbeitrag": {"account_type": "Tax"},
|
||||
"3600 Verbindlichkeiten Sozialversicherung": {"account_type": "Payable"},
|
||||
"3640 Verbindlichkeiten Loehne und Gehaelter": {"account_type": "Payable"},
|
||||
"3700 Sonstige Verbindlichkeiten": {"account_type": "Payable"},
|
||||
"3900 Passive Rechnungsabgrenzungsposten": {"account_type": "Payable"},
|
||||
"3100 Anleihen (einschlie\u00dflich konvertibler)": {"account_type": "Payable"},
|
||||
"3200 Erhaltene Anzahlungen auf Bestellungen": {"account_type": "Payable"},
|
||||
"3040 R\u00fcckstellungen f\u00fcr Abfertigung": {"account_type": "Payable"},
|
||||
|
||||
"3530 USt. \u00a719 (reverse charge)": {
|
||||
"account_type": "Tax"
|
||||
},
|
||||
"Umsatzsteuer": {},
|
||||
"Umsatzsteuer Zahllast": {
|
||||
"3500 Verbindlichkeiten aus Umsatzsteuer": {"account_type": "Tax"},
|
||||
"3580 Umsatzsteuer Zahllast": {
|
||||
"account_type": "Tax"
|
||||
},
|
||||
"Umsatzsteuer aus i.g. Erwerb 10%": {
|
||||
"3510 Umsatzsteuer Inland 20%": {
|
||||
"account_type": "Tax"
|
||||
},
|
||||
"Umsatzsteuer aus i.g. Erwerb 20%": {
|
||||
"3515 Umsatzsteuer Inland 10%": {
|
||||
"account_type": "Tax"
|
||||
},
|
||||
"3520 Umsatzsteuer aus i.g. Erwerb 20%": {
|
||||
"account_type": "Tax"
|
||||
},
|
||||
"Umsatzsteuer aus i.g. Lieferungen 10%": {
|
||||
"3525 Umsatzsteuer aus i.g. Erwerb 10%": {
|
||||
"account_type": "Tax"
|
||||
},
|
||||
"Umsatzsteuer aus i.g. Lieferungen 20%": {
|
||||
"account_type": "Tax"
|
||||
},
|
||||
"Umsatzsteuer-Evidenzkonto f\u00fcr erhaltene Anzahlungen auf Bestellungen": {},
|
||||
"Verbindlichkeiten aus Lieferungen u. Leistungen EU": {
|
||||
},
|
||||
"3560 Umsatzsteuer-Evidenzkonto f\u00fcr erhaltene Anzahlungen auf Bestellungen": {},
|
||||
"3360 Verbindlichkeiten aus Lieferungen u. Leistungen EU": {
|
||||
"account_type": "Payable"
|
||||
},
|
||||
"Verbindlichkeiten aus Lieferungen u. Leistungen Inland": {
|
||||
"3000 Verbindlichkeiten aus Lieferungen u. Leistungen Inland": {
|
||||
"account_type": "Payable"
|
||||
},
|
||||
"Verbindlichkeiten aus Lieferungen u. Leistungen sonst. Ausland": {
|
||||
"3370 Verbindlichkeiten aus Lieferungen u. Leistungen sonst. Ausland": {
|
||||
"account_type": "Payable"
|
||||
},
|
||||
"Verbindlichkeiten gegen\u00fcber Gesellschaften": {},
|
||||
"Verrechnung Finanzamt": {
|
||||
"3400 Verbindlichkeiten gegen\u00fcber verbundenen Unternehmen": {},
|
||||
"3570 Verrechnung Finanzamt": {
|
||||
"account_type": "Tax"
|
||||
},
|
||||
"root_type": "Liability"
|
||||
},
|
||||
"Summe Kontoklasse 0 Anlageverm\u00f6gen": {
|
||||
"44 bis 49 Sonstige Maschinen und maschinelle Anlagen": {},
|
||||
"920 bis 930 Festverzinsliche Wertpapiere des Anlageverm\u00f6gens": {},
|
||||
"940 bis 970 Sonstige Finanzanlagen, Wertrechte": {},
|
||||
"Allgemeine Werkzeuge und Handwerkzeuge": {},
|
||||
"Andere Bef\u00f6rderungsmittel": {},
|
||||
"Andere Betriebs- und Gesch\u00e4ftsausstattung": {},
|
||||
"Andere Erzeugungshilfsmittel": {},
|
||||
"Anlagen im Bau": {},
|
||||
"Anteile an Investmentfonds": {},
|
||||
"Anteile an Kapitalgesellschaften ohne Beteiligungscharakter": {},
|
||||
"Anteile an Personengesellschaften ohne Beteiligungscharakter": {},
|
||||
"Anteile an verbundenen Unternehmen": {},
|
||||
"Antriebsmaschinen": {},
|
||||
"Aufwendungen f\u00fcs das Ingangssetzen u. Erweitern eines Betriebes": {},
|
||||
"Ausleihungen an verbundene Unternehmen": {},
|
||||
"Ausleihungen an verbundene Unternehmen, mit denen ein Beteiligungsverh\u00e4lnis besteht": {},
|
||||
"Bauliche Investitionen in fremden (gepachteten) Betriebs- und Gesch\u00e4ftsgeb\u00e4uden": {},
|
||||
"Bauliche Investitionen in fremden (gepachteten) Wohn- und Sozialgeb\u00e4uden": {},
|
||||
"Bebaute Grundst\u00fccke (Grundwert)": {},
|
||||
"Beheizungs- und Beleuchtungsanlagen": {},
|
||||
"Beteiligungen an Gemeinschaftunternehmen": {},
|
||||
"Beteiligungen an angeschlossenen (assoziierten) Unternehmen": {},
|
||||
"Betriebs- und Gesch\u00e4ftsgeb\u00e4ude auf eigenem Grund": {},
|
||||
"Betriebs- und Gesch\u00e4ftsgeb\u00e4ude auf fremdem Grund": {},
|
||||
"B\u00fcromaschinen, EDV - Anlagen": {},
|
||||
"Datenverarbeitungsprogramme": {},
|
||||
"Energieversorgungsanlagen": {},
|
||||
"Fertigungsmaschinen": {},
|
||||
"Gebinde": {},
|
||||
"Geleistete Anzahlungen": {},
|
||||
"Genossenschaften ohne Beteiligungscharakter": {},
|
||||
"Geringwertige Verm\u00f6gensgegenst\u00e4nde, soweit im Erzeugerprozess verwendet": {},
|
||||
"Geringwertige Verm\u00f6gensgegenst\u00e4nde, soweit nicht im Erzeugungsprozess verwendet": {},
|
||||
"Gesch\u00e4fts(Firmen)wert": {},
|
||||
"Grundst\u00fcckseinrichtunten auf eigenem Grund": {},
|
||||
"Grundst\u00fcckseinrichtunten auf fremdem Grund": {},
|
||||
"Grundst\u00fccksgleiche Rechte": {},
|
||||
"Hebezeuge und Montageanlagen": {},
|
||||
"Konzessionen": {},
|
||||
"Kumulierte Abschreibungen": {},
|
||||
"LKW": {},
|
||||
"Marken, Warenzeichen und Musterschutzrechte": {},
|
||||
"Maschinenwerkzeuge": {},
|
||||
"Nachrichten- und Kontrollanlagen": {},
|
||||
"PKW": {},
|
||||
"Pacht- und Mietrechte": {},
|
||||
"Patentrechte und Lizenzen": {},
|
||||
"Sonstige Ausleihungen": {},
|
||||
"Sonstige Beteiligungen": {},
|
||||
"Transportanlagen": {},
|
||||
"Unbebaute Grundst\u00fccke": {},
|
||||
"Vorrichtungen, Formen und Modelle": {},
|
||||
"Wohn- und Sozialgeb\u00e4ude auf eigenem Grund": {},
|
||||
"Wohn- und Sozialgeb\u00e4ude auf fremdem Grund": {},
|
||||
},
|
||||
"Klasse 2 Aktiva: Umlaufverm\u00f6gen, Rechnungsabgrenzungen": {
|
||||
"2030 Forderungen aus Lieferungen und Leistungen Inland (0% USt, umsatzsteuerfrei)": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2010 Forderungen aus Lieferungen und Leistungen Inland (10% USt, umsatzsteuerfrei)": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2000 Forderungen aus Lieferungen und Leistungen Inland (20% USt, umsatzsteuerfrei)": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2040 Forderungen aus Lieferungen und Leistungen Inland (sonstiger USt-Satz)": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2100 Forderungen aus Lieferungen und Leistungen EU": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2150 Forderungen aus Lieferungen und Leistungen Ausland (Nicht-EU)": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2200 Forderungen gegen\u00fcber verbundenen Unternehmen": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2250 Forderungen gegen\u00fcber Unternehmen, mit denen ein Beteiligungsverh\u00e4ltnis besteht": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2300 Sonstige Forderungen und Verm\u00f6gensgegenst\u00e4nde": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2630 Sonstige Wertpapiere": {
|
||||
"account_type": "Stock"
|
||||
},
|
||||
"2750 Kassenbest\u00e4nde in Fremdw\u00e4hrung": {
|
||||
"account_type": "Cash"
|
||||
},
|
||||
"2900 Aktive Rechnungsabrenzungsposten": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2600 Anteile an verbundenen Unternehmen": {
|
||||
"account_type": "Equity"
|
||||
},
|
||||
"2680 Besitzwechsel ohne Forderungen": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2950 Aktiviertes Disagio": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2610 Eigene Anteile und Wertpapiere an mit Mehrheit beteiligten Unternehmen": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2570 Einfuhrumsatzsteuer (bezahlt)": {"account_type": "Tax"},
|
||||
|
||||
"2460 Eingeforderte aber noch nicht eingezahlte Einlagen": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2180 Einzelwertberichtigungen zu Forderungen aus Lief. und Leist. Ausland": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2130 Einzelwertberichtigungen zu Forderungen aus Lief. und Leist. EU": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2080 Einzelwertberichtigungen zu Forderungen aus Lief. und Leist. Inland ": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2270 Einzelwertberichtigungen zu Forderungen gegen\u00fcber Unternehmen mit denen ein Beteiligungsverh\u00e4ltnis besteht": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2230 Einzelwertberichtigungen zu Forderungen gegen\u00fcber verbundenen Unternehmen": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2470 Einzelwertberichtigungen zu sonstigen Forderungen und Verm\u00f6gensgegenst\u00e4nden": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2700 Kassenbestand": {
|
||||
"account_type": "Cash"
|
||||
},
|
||||
"2190 Pauschalwertberichtigungen zu Forderungen aus Lief. und Leist. sonstiges Ausland": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2130 Pauschalwertberichtigungen zu Forderungen aus Lief. und Leist. EU": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2100 Pauschalwertberichtigungen zu Forderungen aus Lief. und Leist. Inland ": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2280 Pauschalwertberichtigungen zu Forderungen gegen\u00fcber Unternehmen mit denen ein Beteiligungsverh\u00e4ltnis besteht": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2240 Pauschalwertberichtigungen zu Forderungen gegen\u00fcber verbundenen Unternehmen": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2480 Pauschalwertberichtigungen zu sonstigen Forderungen und Verm\u00f6gensgegenst\u00e4nden": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2740 Postwertzeichen": {
|
||||
"account_type": "Cash"
|
||||
},
|
||||
"2780 Schecks in Euro": {
|
||||
"account_type": "Cash"
|
||||
},
|
||||
"2800 Guthaben bei Bank": {
|
||||
"account_type": "Bank"
|
||||
},
|
||||
"2801 Guthaben bei Bank - Sparkonto": {
|
||||
"account_type": "Bank"
|
||||
},
|
||||
"2810 Guthaben bei Paypal": {
|
||||
"account_type": "Bank"
|
||||
},
|
||||
"2930 Mietvorauszahlungen": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2980 Abgrenzung latenter Steuern": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2500 Vorsteuer": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2510 Vorsteuer Inland 10%": {
|
||||
"account_type": "Tax"
|
||||
},
|
||||
"2895 Schwebende Geldbewegugen": {
|
||||
"account_type": "Bank"
|
||||
},
|
||||
"2513 Vorsteuer Inland 5%": {
|
||||
"account_type": "Tax"
|
||||
},
|
||||
"2515 Vorsteuer Inland 20%": {
|
||||
"account_type": "Tax"
|
||||
},
|
||||
"2520 Vorsteuer aus innergemeinschaftlichem Erwerb 10%": {
|
||||
"account_type": "Tax"
|
||||
},
|
||||
"2525 Vorsteuer aus innergemeinschaftlichem Erwerb 20%": {
|
||||
"account_type": "Tax"
|
||||
},
|
||||
"2530 Vorsteuer \u00a719/Art 19 ( reverse charge ) ": {
|
||||
"account_type": "Tax"
|
||||
},
|
||||
"2690 Wertberichtigungen zu Wertpapieren und Anteilen": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"root_type": "Asset"
|
||||
},
|
||||
"Summe Personalaufwand": {
|
||||
"6000 bis 6190 L\u00f6hne": {},
|
||||
"6200 bis 6390 Geh\u00e4lter": {},
|
||||
"6400 bis 6440 Aufwendungen f\u00fcr Abfertigungen": {},
|
||||
"6450 bis 6490 Aufwendungen f\u00fcr Altersversorgung": {},
|
||||
"6500 bis 6550 Gesetzlicher Sozialaufwand Arbeiter": {},
|
||||
"6560 bis 6590 Gesetzlicher Sozialaufwand Angestellte": {},
|
||||
"6600 bis 6650 Lohnabh\u00e4ngige Abgaben und Pflichtbeitr\u00e4gte": {},
|
||||
"6660 bis 6690 Gehaltsabh\u00e4ngige Abgaben und Pflichtbeitr\u00e4gte": {},
|
||||
"6700 bis 6890 Sonstige Sozialaufwendungen": {},
|
||||
"Aufwandsstellenrechnung": {},
|
||||
"Klasse 4: Betriebliche Erträge": {
|
||||
"4000 Erlöse 20 %": {"account_type": "Income Account"},
|
||||
"4020 Erl\u00f6se 0 % steuerbefreit": {"account_type": "Income Account"},
|
||||
"4010 Erl\u00f6se 10 %": {"account_type": "Income Account"},
|
||||
"4030 Erl\u00f6se 13 %": {"account_type": "Income Account"},
|
||||
"4040 Erl\u00f6se 0 % innergemeinschaftliche Lieferungen": {"account_type": "Income Account"},
|
||||
"4400 Erl\u00f6sreduktion 0 % steuerbefreit": {"account_type": "Expense Account"},
|
||||
"4410 Erl\u00f6sreduktion 10 %": {"account_type": "Expense Account"},
|
||||
"4420 Erl\u00f6sreduktion 20 %": {"account_type": "Expense Account"},
|
||||
"4430 Erl\u00f6sreduktion 13 %": {"account_type": "Expense Account"},
|
||||
"4440 Erl\u00f6sreduktion 0 % innergemeinschaftliche Lieferungen": {"account_type": "Expense Account"},
|
||||
"4500 Ver\u00e4nderungen des Bestandes an fertigen und unfertigen Erzeugn. sowie an noch nicht abrechenbaren Leistungen": {"account_type": "Income Account"},
|
||||
"4580 Aktivierte Eigenleistungen": {"account_type": "Income Account"},
|
||||
"4600 Erl\u00f6se aus dem Abgang vom Anlageverm\u00f6gen, ausgen. Finanzanlagen": {"account_type": "Income Account"},
|
||||
"4630 Ertr\u00e4ge aus dem Abgang vom Anlageverm\u00f6gen, ausgen. Finanzanlagen": {"account_type": "Income Account"},
|
||||
"4660 Ertr\u00e4ge aus der Zuschreibung zum Anlageverm\u00f6gen, ausgen. Finanzanlagen": {"account_type": "Income Account"},
|
||||
"4700 Ertr\u00e4ge aus der Aufl\u00f6sung von R\u00fcckstellungen": {"account_type": "Income Account"},
|
||||
"4800 \u00dcbrige betriebliche Ertr\u00e4ge": {"account_type": "Income Account"},
|
||||
"root_type": "Income"
|
||||
},
|
||||
"Klasse 5: Aufwand f\u00fcr Material und Leistungen": {
|
||||
"5000 Einkauf Partnerleistungen": {"account_type": "Cost of Goods Sold"},
|
||||
"5100 Verbrauch an Rohstoffen": {"account_type": "Cost of Goods Sold"},
|
||||
"5200 Verbrauch von bezogenen Fertig- und Einzelteilen": {"account_type": "Cost of Goods Sold"},
|
||||
"5300 Verbrauch von Hilfsstoffen": {"account_type": "Cost of Goods Sold"},
|
||||
"5340 Verbrauch Verpackungsmaterial": {"account_type": "Cost of Goods Sold"},
|
||||
"5470 Verbrauch von Kleinmaterial": {"account_type": "Cost of Goods Sold"},
|
||||
"5450 Verbrauch von Reinigungsmaterial": {"account_type": "Cost of Goods Sold"},
|
||||
"5400 Verbrauch von Betriebsstoffen": {"account_type": "Cost of Goods Sold"},
|
||||
"5500 Verbrauch von Werkzeugen und anderen Erzeugungshilfsmittel": {"account_type": "Cost of Goods Sold"},
|
||||
"5600 Verbrauch von Brenn- und Treibstoffen, Energie und Wasser": {"account_type": "Cost of Goods Sold"},
|
||||
"5700 Bearbeitung durch Dritte": {"account_type": "Cost of Goods Sold"},
|
||||
"5900 Aufwandsstellenrechnung Material": {"account_type": "Cost of Goods Sold"},
|
||||
"5820 Skontoertr\u00e4ge (20% USt.)": {"account_type": "Income Account"},
|
||||
"5810 Skontoertr\u00e4ge (10% USt.)": {"account_type": "Income Account"},
|
||||
"5010 Handelswareneinkauf 10 %": {"account_type": "Cost of Goods Sold"},
|
||||
"5020 Handelswareneinkauf 20 %": {"account_type": "Cost of Goods Sold"},
|
||||
"5040 Handelswareneinkauf innergemeinschaftlicher Erwerb 10 % VSt/10 % USt": {"account_type": "Cost of Goods Sold"},
|
||||
"5050 Handelswareneinkauf innergemeinschaftlicher Erwerb 20 % VSt/20 % USt": {"account_type": "Cost of Goods Sold"},
|
||||
"5070 Handelswareneinkauf innergemeinschaftlicher Erwerb ohne Vorsteuerabzug und 10 % USt": {"account_type": "Cost of Goods Sold"},
|
||||
"5080 Handelswareneinkauf innergemeinschaftlicher Erwerb ohne Vorsteuerabzug und 20 % USt": {"account_type": "Cost of Goods Sold"},
|
||||
"root_type": "Expense"
|
||||
},
|
||||
"Summe Umlaufverm\u00f6gen": {
|
||||
"2000 bis 2007 Forderungen aus Lief. und Leist. Inland": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2100 bis 2120 Forderungen aus Lief. und Leist. EU": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2150 bis 2170 Forderungen aus Lief. und Leist. Ausland": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2200 bis 2220 Forderungen gegen\u00fcber verbundenen Unternehmen": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2250 bis 2270 Forderungen gegen\u00fcber Unternehmen, mit denen ein Beteiligungsverh\u00e4ltnis besteht": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2300 bis 2460 Sonstige Forderungen und Verm\u00f6gensgegenst\u00e4nde": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2630 bis 2670 Sonstige Wertpapiere": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"2750 bis 2770 Kassenbest\u00e4nde in Fremdw\u00e4hrung": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Aktive Rechnungsabrenzungsposten": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Anteile an verbundenen Unternehmen": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Bank / Guthaben bei Kreditinstituten": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Besitzwechsel ...": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Disagio": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Eigene Anteile (Wertpapiere)": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Einfuhrumsatzsteuer (bezahlt)": {},
|
||||
"Eingeforderte aber noch nicht eingezahlte Einlagen": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Einzelwertberichtigungen zu Forderungen aus Lief. und Leist. Ausland": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Einzelwertberichtigungen zu Forderungen aus Lief. und Leist. EU": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Einzelwertberichtigungen zu Forderungen aus Lief. und Leist. Inland ": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Einzelwertberichtigungen zu Forderungen gegen\u00fcber Unternehmen mit denen ein Beteiligungsverh\u00e4ltnis besteht": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Einzelwertberichtigungen zu Forderungen gegen\u00fcber verbundenen Unternehmen": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Einzelwertberichtigungen zu sonstigen Forderungen und Verm\u00f6gensgegenst\u00e4nden": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Kassenbestand": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Pauschalwertberichtigungen zu Forderungen aus Lief. und Leist. Ausland": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Pauschalwertberichtigungen zu Forderungen aus Lief. und Leist. EU": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Pauschalwertberichtigungen zu Forderungen aus Lief. und Leist. Inland ": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Pauschalwertberichtigungen zu Forderungen gegen\u00fcber Unternehmen mit denen ein Beteiligungsverh\u00e4ltnis besteht": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Pauschalwertberichtigungen zu Forderungen gegen\u00fcber verbundenen Unternehmen": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Pauschalwertberichtigungen zu sonstigen Forderungen und Verm\u00f6gensgegenst\u00e4nden": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Postwertzeichen": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Schecks in Inlandsw\u00e4hrung": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Sonstige Anteile": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Stempelmarken": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Steuerabgrenzung": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Unterschiedsbetrag gem. Abschnitt XII Pensionskassengesetz": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Unterschiedsbetrag zur gebotenen Pensionsr\u00fcckstellung": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Vorsteuer": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Vorsteuer aus ig. Erwerb 10%": {
|
||||
"account_type": "Tax"
|
||||
},
|
||||
"Vorsteuer aus ig. Erwerb 20%": {
|
||||
"account_type": "Tax"
|
||||
},
|
||||
"Vorsteuer \u00a719/Art 19 ( reverse charge ) ": {
|
||||
"account_type": "Tax"
|
||||
},
|
||||
"Wertberichtigungen": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"root_type": "Asset"
|
||||
},
|
||||
"Summe Vorr\u00e4te": {
|
||||
"1000 bis 1090 Bezugsverrechnung": {},
|
||||
"1100 bis 1190 Rohstoffe": {},
|
||||
"1200 bis 1290 Bezogene Teile": {},
|
||||
"1300 bis 1340 Hilfsstoffe": {},
|
||||
"1350 bis 1390 Betriebsstoffe": {},
|
||||
"1400 bis 1490 Unfertige Erzeugniss": {},
|
||||
"1500 bis 1590 Fertige Erzeugniss": {},
|
||||
"1600 bis 1690 Waren": {},
|
||||
"1700 bis 1790 Noch nicht abgerechenbare Leistungen": {},
|
||||
"1900 bis 1990 Wertberichtigungen": {},
|
||||
"geleistete Anzahlungen": {},
|
||||
"root_type": "Asset"
|
||||
},
|
||||
"Summe Wareneinsatz": {
|
||||
"5100 bis 5190 Verbrauch an Rohstoffen": {},
|
||||
"5200 bis 5290 Verbrauch von bezogenen Fertig- und Einzelteilen": {},
|
||||
"5300 bis 5390 Verbrauch von Hilfsstoffen": {},
|
||||
"5400 bis 5490 Verbrauch von Betriebsstoffen": {},
|
||||
"5500 bis 5590 Verbrauch von Werkzeugen und anderen Erzeugungshilfsmittel": {},
|
||||
"5600 bis 5690 Verbrauch von Brenn- und Treibstoffen, Energie und Wasser": {},
|
||||
"5700 bis 5790 Sonstige bezogene Herstellungsleistungen": {},
|
||||
"Aufwandsstellenrechnung": {},
|
||||
"Skontoertr\u00e4ge auf Materialaufwand": {},
|
||||
"Skontoertr\u00e4ge auf sonstige bezogene Herstellungsleistungen": {},
|
||||
"Wareneinkauf 10 %": {},
|
||||
"Wareneinkauf 20 %": {},
|
||||
"Wareneinkauf igErwerb 10 % VSt/10 % USt": {},
|
||||
"Wareneinkauf igErwerb 20 % VSt/20 % USt": {},
|
||||
"Wareneinkauf igErwerb ohne Vorsteuerabzug und 10 % USt": {},
|
||||
"Wareneinkauf igErwerb ohne Vorsteuerabzug und 20 % USt": {},
|
||||
"Klasse 6: Personalaufwand": {
|
||||
"6000 L\u00f6hne": {"account_type": "Payable"},
|
||||
"6200 Geh\u00e4lter": {"account_type": "Payable"},
|
||||
"6400 Aufwendungen f\u00fcr Abfertigungen": {"account_type": "Payable"},
|
||||
"6450 Aufwendungen f\u00fcr Altersversorgung": {"account_type": "Payable"},
|
||||
"6500 Gesetzlicher Sozialaufwand Arbeiter": {"account_type": "Payable"},
|
||||
"6560 Gesetzlicher Sozialaufwand Angestellte": {"account_type": "Payable"},
|
||||
"6600 Lohnabh\u00e4ngige Abgaben und Pflichtbeitr\u00e4gte": {"account_type": "Payable"},
|
||||
"6660 Gehaltsabh\u00e4ngige Abgaben und Pflichtbeitr\u00e4gte": {"account_type": "Payable"},
|
||||
"6700 Sonstige Sozialaufwendungen": {"account_type": "Payable"},
|
||||
"6900 Aufwandsstellenrechnung Personal": {"account_type": "Payable"},
|
||||
"root_type": "Expense"
|
||||
},
|
||||
"Klasse 7: Abschreibungen und sonstige betriebliche Aufwendungen": {
|
||||
"7010 Abschreibungen auf das Anlageverm\u00f6gen (ausgenommen Finanzanlagen)": {"account_type": "Depreciation"},
|
||||
"7100 Sonstige Steuern und Geb\u00fchren": {"account_type": "Tax"},
|
||||
"7200 Instandhaltung u. Reinigung durch Dritte, Entsorgung, Energie": {"account_type": "Expense Account"},
|
||||
"7300 Transporte durch Dritte": {"account_type": "Expense Account"},
|
||||
"7310 Fahrrad - Aufwand": {"account_type": "Expense Account"},
|
||||
"7320 Kfz - Aufwand": {"account_type": "Expense Account"},
|
||||
"7330 LKW - Aufwand": {"account_type": "Expense Account"},
|
||||
"7340 Lastenrad - Aufwand": {"account_type": "Expense Account"},
|
||||
"7350 Reise- und Fahraufwand": {"account_type": "Expense Account"},
|
||||
"7360 Tag- und N\u00e4chtigungsgelder": {"account_type": "Expense Account"},
|
||||
"7380 Nachrichtenaufwand": {"account_type": "Expense Account"},
|
||||
"7400 Miet- und Pachtaufwand": {"account_type": "Expense Account"},
|
||||
"7440 Leasingaufwand": {"account_type": "Expense Account"},
|
||||
"7480 Lizenzaufwand": {"account_type": "Expense Account"},
|
||||
"7500 Aufwand f\u00fcr beigestelltes Personal": {"account_type": "Expense Account"},
|
||||
"7540 Provisionen an Dritte": {"account_type": "Expense Account"},
|
||||
"7580 Aufsichtsratsverg\u00fctungen": {"account_type": "Expense Account"},
|
||||
"7610 Druckerzeugnisse und Vervielf\u00e4ltigungen": {"account_type": "Expense Account"},
|
||||
"7650 Werbung und Repr\u00e4sentationen": {"account_type": "Expense Account"},
|
||||
"7700 Versicherungen": {"account_type": "Expense Account"},
|
||||
"7750 Beratungs- und Pr\u00fcfungsaufwand": {"account_type": "Expense Account"},
|
||||
"7800 Forderungsverluste und Schadensf\u00e4lle": {"account_type": "Expense Account"},
|
||||
"7840 Verschiedene betriebliche Aufwendungen": {"account_type": "Expense Account"},
|
||||
"7910 Aufwandsstellenrechung der Hersteller": {"account_type": "Expense Account"},
|
||||
"7060 Sofortabschreibungen geringwertig": {"account_type": "Expense Account"},
|
||||
"7070 Abschreibungen vom Umlaufverm\u00f6gen, soweit diese die im Unternehmen \u00fcblichen Abschreibungen \u00fcbersteigen": {"account_type": "Depreciation"},
|
||||
"7900 Aufwandsstellenrechnung": {"account_type": "Expense Account"},
|
||||
"7770 Aus- und Fortbildung": {"account_type": "Expense Account"},
|
||||
"7820 Buchwert abgegangener Anlagen, ausgenommen Finanzanlagen": {"account_type": "Expense Account"},
|
||||
"7600 B\u00fcromaterial und Drucksorten": {"account_type": "Expense Account"},
|
||||
"7630 Fachliteratur und Zeitungen ": {"account_type": "Expense Account"},
|
||||
"7960 Herstellungskosten der zur Erzielung der Umsatzerl\u00f6se erbrachten Leistungen": {"account_type": "Expense Account"},
|
||||
"7780 Mitgliedsbeitr\u00e4ge": {"account_type": "Expense Account"},
|
||||
"7880 Skontoertr\u00e4ge auf sonstige betriebliche Aufwendungen": {"account_type": "Expense Account"},
|
||||
"7990 Sonstige betrieblichen Aufwendungen": {"account_type": "Expense Account"},
|
||||
"7680 Spenden und Trinkgelder": {"account_type": "Expense Account"},
|
||||
"7790 Spesen des Geldverkehrs": {"account_type": "Expense Account"},
|
||||
"7830 Verluste aus dem Abgang vom Anlageverm\u00f6gen, ausgenommen Finanzanlagen": {"account_type": "Expense Account"},
|
||||
"7970 Vertriebskosten": {"account_type": "Expense Account"},
|
||||
"7980 Verwaltungskosten": {"account_type": "Expense Account"},
|
||||
"root_type": "Expense"
|
||||
},
|
||||
"Klasse 8: Finanz- und ausserordentliche Ertr\u00e4ge und Aufwendungen": {
|
||||
"8000 Ertr\u00e4ge aus Beteiligungen": {"account_type": "Income Account"},
|
||||
"8050 Ertr\u00e4ge aus anderen Wertpapieren und Ausleihungen des Finanzanlageverm\u00f6gens": {"account_type": "Income Account"},
|
||||
"8100 Zinsen aus Bankguthaben": {"account_type": "Income Account"},
|
||||
"8110 Zinsen aus gewaehrten Darlehen": {"account_type": "Income Account"},
|
||||
"8130 Verzugszinsenertraege": {"account_type": "Income Account"},
|
||||
"8220 Aufwendungen aus Beteiligungen": {"account_type": "Expense Account"},
|
||||
"8260 Aufwendungen aus sonst. Fiananzanlagen und aus Wertpapieren des Umlaufverm\u00f6gens": {},
|
||||
"8280 Zinsen und \u00e4hnliche Aufwendungem": {"account_type": "Expense Account"},
|
||||
"8400 Au\u00dferordentliche Ertr\u00e4ge": {"account_type": "Income Account"},
|
||||
"8450 Au\u00dferordentliche Aufwendungen": {"account_type": "Expense Account"},
|
||||
"8500 Steuern vom Einkommen und vom Ertrag": {
|
||||
"account_type": "Tax"
|
||||
},
|
||||
"8600 Aufl\u00f6sung unversteuerten R\u00fccklagen": {"account_type": "Income Account"},
|
||||
"8700 Aufl\u00f6sung von Kapitalr\u00fccklagen": {"account_type": "Income Account"},
|
||||
"8750 Aufl\u00f6sung von Gewinnr\u00fccklagen": {"account_type": "Income Account"},
|
||||
"8800 Zuweisung zu unversteuerten R\u00fccklagen": {"account_type": "Expense Account"},
|
||||
"8900 Zuweisung zu Gewinnr\u00fccklagen": {"account_type": "Expense Account"},
|
||||
"8100 Buchwert abgegangener Beteiligungen": {"account_type": "Expense Account"},
|
||||
"8130 Buchwert abgegangener Wertpapiere des Umlaufverm\u00f6gens": {"account_type": "Expense Account"},
|
||||
"8120 Buchwert abgegangener sonstiger Finanzanlagen": {"account_type": "Expense Account"},
|
||||
"8990 Gewinnabfuhr bzw. Verlust\u00fcberrechnung aus Ergebnisabf\u00fchrungsvertr\u00e4gen": {"account_type": "Expense Account"},
|
||||
"8350 nicht ausgenutzte Lieferantenskonti": {"account_type": "Expense Account"},
|
||||
"root_type": "Income"
|
||||
},
|
||||
"Klasse 9 Passiva: Eigenkapital, R\u00fccklagen, stille Einlagen, Abschlusskonten": {
|
||||
"9000 Gezeichnetes bzw. gewidmetes Kapital": {
|
||||
"account_type": "Equity"
|
||||
},
|
||||
"9200 Kapitalr\u00fccklagen": {
|
||||
"account_type": "Equity"
|
||||
},
|
||||
"9300 Gewinnr\u00fccklagen": {
|
||||
"account_type": "Equity"
|
||||
},
|
||||
"9400 Bewertungsreserven uns sonst. unversteuerte R\u00fccklagen": {
|
||||
"account_type": "Equity"
|
||||
},
|
||||
"9600 Private Entnahmen": {"account_type": "Equity"},
|
||||
"9610 Privatsteuern": {"account_type": "Equity"},
|
||||
"9700 Einlagen stiller Gesellschafter ": {"account_type": "Equity"},
|
||||
"9900 Evidenzkonto": {"account_type": "Equity"},
|
||||
"9800 Er\u00f6ffnungsbilanzkonto (EBK)": {"account_type": "Equity"},
|
||||
"9880 Jahresergebnis laut Gewinn- und Verlustrechnung (G+V)": {"account_type": "Equity"},
|
||||
"9850 Schlussbilanzkonto (SBK)": {"account_type": "Round Off"},
|
||||
"9190 nicht eingeforderte ausstehende Einlagen und berechtigte Entnahmen von Gesellschaftern": {
|
||||
"account_type": "Equity"
|
||||
},
|
||||
"root_type": "Equity"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,10 +3,6 @@
|
||||
|
||||
frappe.ui.form.on('Accounting Dimension Filter', {
|
||||
refresh: function(frm, cdt, cdn) {
|
||||
if (frm.doc.accounting_dimension) {
|
||||
frm.set_df_property('dimensions', 'label', frm.doc.accounting_dimension, cdn, 'dimension_value');
|
||||
}
|
||||
|
||||
let help_content =
|
||||
`<table class="table table-bordered" style="background-color: var(--scrollbar-track-color);">
|
||||
<tr><td>
|
||||
@@ -68,6 +64,7 @@ frappe.ui.form.on('Accounting Dimension Filter', {
|
||||
frm.clear_table("dimensions");
|
||||
let row = frm.add_child("dimensions");
|
||||
row.accounting_dimension = frm.doc.accounting_dimension;
|
||||
frm.fields_dict["dimensions"].grid.update_docfield_property("dimension_value", "label", frm.doc.accounting_dimension);
|
||||
frm.refresh_field("dimensions");
|
||||
frm.trigger('setup_filters');
|
||||
},
|
||||
|
||||
@@ -4,6 +4,23 @@
|
||||
frappe.ui.form.on("Bank Clearance", {
|
||||
setup: function(frm) {
|
||||
frm.add_fetch("account", "account_currency", "account_currency");
|
||||
|
||||
frm.set_query("account", function() {
|
||||
return {
|
||||
"filters": {
|
||||
"account_type": ["in",["Bank","Cash"]],
|
||||
"is_group": 0,
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query("bank_account", function () {
|
||||
return {
|
||||
filters: {
|
||||
'is_company_account': 1
|
||||
},
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
onload: function(frm) {
|
||||
@@ -12,14 +29,7 @@ frappe.ui.form.on("Bank Clearance", {
|
||||
locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]: "";
|
||||
frm.set_value("account", default_bank_account);
|
||||
|
||||
frm.set_query("account", function() {
|
||||
return {
|
||||
"filters": {
|
||||
"account_type": ["in",["Bank","Cash"]],
|
||||
"is_group": 0
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
frm.set_value("from_date", frappe.datetime.month_start());
|
||||
frm.set_value("to_date", frappe.datetime.month_end());
|
||||
|
||||
@@ -99,7 +99,7 @@ class BankClearance(Document):
|
||||
.where(loan_disbursement.clearance_date.isnull())
|
||||
.where(loan_disbursement.disbursement_account.isin([self.bank_account, self.account]))
|
||||
.orderby(loan_disbursement.disbursement_date)
|
||||
.orderby(loan_disbursement.name, frappe.qb.desc)
|
||||
.orderby(loan_disbursement.name, order=frappe.qb.desc)
|
||||
).run(as_dict=1)
|
||||
|
||||
loan_repayment = frappe.qb.DocType("Loan Repayment")
|
||||
@@ -126,7 +126,9 @@ class BankClearance(Document):
|
||||
if frappe.db.has_column("Loan Repayment", "repay_from_salary"):
|
||||
query = query.where((loan_repayment.repay_from_salary == 0))
|
||||
|
||||
query = query.orderby(loan_repayment.posting_date).orderby(loan_repayment.name, frappe.qb.desc)
|
||||
query = query.orderby(loan_repayment.posting_date).orderby(
|
||||
loan_repayment.name, order=frappe.qb.desc
|
||||
)
|
||||
|
||||
loan_repayments = query.run(as_dict=True)
|
||||
|
||||
|
||||
@@ -43,20 +43,13 @@ frappe.ui.form.on('Bank Guarantee', {
|
||||
|
||||
reference_docname: function(frm) {
|
||||
if (frm.doc.reference_docname && frm.doc.reference_doctype) {
|
||||
let fields_to_fetch = ["grand_total"];
|
||||
let party_field = frm.doc.reference_doctype == "Sales Order" ? "customer" : "supplier";
|
||||
|
||||
if (frm.doc.reference_doctype == "Sales Order") {
|
||||
fields_to_fetch.push("project");
|
||||
}
|
||||
|
||||
fields_to_fetch.push(party_field);
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.bank_guarantee.bank_guarantee.get_vouchar_detials",
|
||||
method: "erpnext.accounts.doctype.bank_guarantee.bank_guarantee.get_voucher_details",
|
||||
args: {
|
||||
"column_list": fields_to_fetch,
|
||||
"doctype": frm.doc.reference_doctype,
|
||||
"docname": frm.doc.reference_docname
|
||||
"bank_guarantee_type": frm.doc.bg_type,
|
||||
"reference_name": frm.doc.reference_docname
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
|
||||
@@ -2,11 +2,8 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
import json
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.desk.search import sanitize_searchfield
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
@@ -25,14 +22,18 @@ class BankGuarantee(Document):
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
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""".format(
|
||||
columns=", ".join(column_list), doctype=doctype
|
||||
),
|
||||
docname,
|
||||
as_dict=1,
|
||||
)[0]
|
||||
def get_voucher_details(bank_guarantee_type: str, reference_name: str):
|
||||
if not isinstance(reference_name, str):
|
||||
raise TypeError("reference_name must be a string")
|
||||
|
||||
fields_to_fetch = ["grand_total"]
|
||||
|
||||
if bank_guarantee_type == "Receiving":
|
||||
doctype = "Sales Order"
|
||||
fields_to_fetch.append("customer")
|
||||
fields_to_fetch.append("project")
|
||||
else:
|
||||
doctype = "Purchase Order"
|
||||
fields_to_fetch.append("supplier")
|
||||
|
||||
return frappe.db.get_value(doctype, reference_name, fields_to_fetch, as_dict=True)
|
||||
|
||||
@@ -12,6 +12,9 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
||||
},
|
||||
};
|
||||
});
|
||||
let no_bank_transactions_text =
|
||||
`<div class="text-muted text-center">${__("No Matching Bank Transactions Found")}</div>`
|
||||
set_field_options("no_bank_transactions", no_bank_transactions_text);
|
||||
},
|
||||
|
||||
onload: function (frm) {
|
||||
|
||||
@@ -81,8 +81,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "no_bank_transactions",
|
||||
"fieldtype": "HTML",
|
||||
"options": "<div class=\"text-muted text-center\">No Matching Bank Transactions Found</div>"
|
||||
"fieldtype": "HTML"
|
||||
}
|
||||
],
|
||||
"hide_toolbar": 1,
|
||||
@@ -109,4 +108,4 @@
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ frappe.ui.form.on("Bank Statement Import", {
|
||||
|
||||
if (frm.doc.status.includes("Success")) {
|
||||
frm.add_custom_button(
|
||||
__("Go to {0} List", [frm.doc.reference_doctype]),
|
||||
__("Go to {0} List", [__(frm.doc.reference_doctype)]),
|
||||
() => frappe.set_route("List", frm.doc.reference_doctype)
|
||||
);
|
||||
}
|
||||
@@ -141,7 +141,7 @@ frappe.ui.form.on("Bank Statement Import", {
|
||||
},
|
||||
|
||||
show_import_status(frm) {
|
||||
let import_log = JSON.parse(frm.doc.import_log || "[]");
|
||||
let import_log = JSON.parse(frm.doc.statement_import_log || "[]");
|
||||
let successful_records = import_log.filter((log) => log.success);
|
||||
let failed_records = import_log.filter((log) => !log.success);
|
||||
if (successful_records.length === 0) return;
|
||||
@@ -309,7 +309,7 @@ frappe.ui.form.on("Bank Statement Import", {
|
||||
// method: 'frappe.core.doctype.data_import.data_import.get_preview_from_template',
|
||||
|
||||
show_import_preview(frm, preview_data) {
|
||||
let import_log = JSON.parse(frm.doc.import_log || "[]");
|
||||
let import_log = JSON.parse(frm.doc.statement_import_log || "[]");
|
||||
|
||||
if (
|
||||
frm.import_preview &&
|
||||
@@ -439,7 +439,7 @@ frappe.ui.form.on("Bank Statement Import", {
|
||||
},
|
||||
|
||||
show_import_log(frm) {
|
||||
let import_log = JSON.parse(frm.doc.import_log || "[]");
|
||||
let import_log = JSON.parse(frm.doc.statement_import_log || "[]");
|
||||
let logs = import_log;
|
||||
frm.toggle_display("import_log", false);
|
||||
frm.toggle_display("import_log_section", logs.length > 0);
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"section_import_preview",
|
||||
"import_preview",
|
||||
"import_log_section",
|
||||
"import_log",
|
||||
"statement_import_log",
|
||||
"show_failed_logs",
|
||||
"import_log_preview",
|
||||
"reference_doctype",
|
||||
@@ -90,12 +90,6 @@
|
||||
"options": "JSON",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "import_log",
|
||||
"fieldtype": "Code",
|
||||
"label": "Import Log",
|
||||
"options": "JSON"
|
||||
},
|
||||
{
|
||||
"fieldname": "import_log_section",
|
||||
"fieldtype": "Section Break",
|
||||
@@ -198,11 +192,17 @@
|
||||
{
|
||||
"fieldname": "column_break_4",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "statement_import_log",
|
||||
"fieldtype": "Code",
|
||||
"label": "Statement Import Log",
|
||||
"options": "JSON"
|
||||
}
|
||||
],
|
||||
"hide_toolbar": 1,
|
||||
"links": [],
|
||||
"modified": "2021-05-12 14:17:37.777246",
|
||||
"modified": "2022-09-07 11:11:40.293317",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Bank Statement Import",
|
||||
|
||||
@@ -53,15 +53,13 @@ class BankStatementImport(DataImport):
|
||||
if "Bank Account" not in json.dumps(preview["columns"]):
|
||||
frappe.throw(_("Please add the Bank Account column"))
|
||||
|
||||
from frappe.core.page.background_jobs.background_jobs import get_info
|
||||
from frappe.utils.background_jobs import is_job_queued
|
||||
from frappe.utils.scheduler import is_scheduler_inactive
|
||||
|
||||
if is_scheduler_inactive() and not frappe.flags.in_test:
|
||||
frappe.throw(_("Scheduler is inactive. Cannot import data."), title=_("Scheduler Inactive"))
|
||||
|
||||
enqueued_jobs = [d.get("job_name") for d in get_info()]
|
||||
|
||||
if self.name not in enqueued_jobs:
|
||||
if not is_job_queued(self.name):
|
||||
enqueue(
|
||||
start_import,
|
||||
queue="default",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2016-05-16 11:42:29.632528",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
@@ -9,6 +10,7 @@
|
||||
"budget_against",
|
||||
"company",
|
||||
"cost_center",
|
||||
"naming_series",
|
||||
"project",
|
||||
"fiscal_year",
|
||||
"column_break_3",
|
||||
@@ -190,15 +192,26 @@
|
||||
"label": "Budget Accounts",
|
||||
"options": "Budget Account",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "naming_series",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Series",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
"set_only_once": 1
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-06 15:13:54.055854",
|
||||
"modified": "2022-10-10 22:14:36.361509",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Budget",
|
||||
"naming_rule": "By \"Naming Series\" field",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
@@ -220,5 +233,6 @@
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -5,7 +5,6 @@
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.model.naming import make_autoname
|
||||
from frappe.utils import add_months, flt, fmt_money, get_last_day, getdate
|
||||
|
||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
|
||||
@@ -23,11 +22,6 @@ class DuplicateBudgetError(frappe.ValidationError):
|
||||
|
||||
|
||||
class Budget(Document):
|
||||
def autoname(self):
|
||||
self.name = make_autoname(
|
||||
self.get(frappe.scrub(self.budget_against)) + "/" + self.fiscal_year + "/.###"
|
||||
)
|
||||
|
||||
def validate(self):
|
||||
if not self.get(frappe.scrub(self.budget_against)):
|
||||
frappe.throw(_("{0} is mandatory").format(self.budget_against))
|
||||
@@ -109,8 +103,11 @@ class Budget(Document):
|
||||
):
|
||||
self.applicable_on_booking_actual_expenses = 1
|
||||
|
||||
def before_naming(self):
|
||||
self.naming_series = f"{{{frappe.scrub(self.budget_against)}}}./.{self.fiscal_year}/.###"
|
||||
|
||||
def validate_expense_against_budget(args):
|
||||
|
||||
def validate_expense_against_budget(args, expense_amount=0):
|
||||
args = frappe._dict(args)
|
||||
|
||||
if args.get("company") and not args.fiscal_year:
|
||||
@@ -178,13 +175,13 @@ def validate_expense_against_budget(args):
|
||||
) # nosec
|
||||
|
||||
if budget_records:
|
||||
validate_budget_records(args, budget_records)
|
||||
validate_budget_records(args, budget_records, expense_amount)
|
||||
|
||||
|
||||
def validate_budget_records(args, budget_records):
|
||||
def validate_budget_records(args, budget_records, expense_amount):
|
||||
for budget in budget_records:
|
||||
if flt(budget.budget_amount):
|
||||
amount = get_amount(args, budget)
|
||||
amount = expense_amount or get_amount(args, budget)
|
||||
yearly_action, monthly_action = get_actions(args, budget)
|
||||
|
||||
if monthly_action in ["Stop", "Warn"]:
|
||||
|
||||
@@ -334,6 +334,39 @@ class TestBudget(unittest.TestCase):
|
||||
budget.cancel()
|
||||
jv.cancel()
|
||||
|
||||
def test_monthly_budget_against_main_cost_center(self):
|
||||
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
|
||||
from erpnext.accounts.doctype.cost_center_allocation.test_cost_center_allocation import (
|
||||
create_cost_center_allocation,
|
||||
)
|
||||
|
||||
cost_centers = [
|
||||
"Main Budget Cost Center 1",
|
||||
"Sub Budget Cost Center 1",
|
||||
"Sub Budget Cost Center 2",
|
||||
]
|
||||
|
||||
for cc in cost_centers:
|
||||
create_cost_center(cost_center_name=cc, company="_Test Company")
|
||||
|
||||
create_cost_center_allocation(
|
||||
"_Test Company",
|
||||
"Main Budget Cost Center 1 - _TC",
|
||||
{"Sub Budget Cost Center 1 - _TC": 60, "Sub Budget Cost Center 2 - _TC": 40},
|
||||
)
|
||||
|
||||
make_budget(budget_against="Cost Center", cost_center="Main Budget Cost Center 1 - _TC")
|
||||
|
||||
jv = make_journal_entry(
|
||||
"_Test Account Cost for Goods Sold - _TC",
|
||||
"_Test Bank - _TC",
|
||||
400000,
|
||||
"Main Budget Cost Center 1 - _TC",
|
||||
posting_date=nowdate(),
|
||||
)
|
||||
|
||||
self.assertRaises(BudgetError, jv.submit)
|
||||
|
||||
|
||||
def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None):
|
||||
if budget_against_field == "project":
|
||||
|
||||
@@ -52,7 +52,7 @@ def validate_company(company):
|
||||
if parent_company and (not allow_account_creation_against_child_company):
|
||||
msg = _("{} is a child company.").format(frappe.bold(company)) + " "
|
||||
msg += _("Please import accounts against parent company or enable {} in company master.").format(
|
||||
frappe.bold("Allow Account Creation Against Child Company")
|
||||
frappe.bold(_("Allow Account Creation Against Child Company"))
|
||||
)
|
||||
frappe.throw(msg, title=_("Wrong Company"))
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ class CostCenter(NestedSet):
|
||||
from erpnext.accounts.utils import get_autoname_with_number
|
||||
|
||||
self.name = get_autoname_with_number(
|
||||
self.cost_center_number, self.cost_center_name, None, self.company
|
||||
self.cost_center_number, self.cost_center_name, self.company
|
||||
)
|
||||
|
||||
def validate(self):
|
||||
|
||||
@@ -366,7 +366,7 @@ def update_outstanding_amt(
|
||||
if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]:
|
||||
ref_doc = frappe.get_doc(against_voucher_type, against_voucher)
|
||||
|
||||
# Didn't use db_set for optimisation purpose
|
||||
# Didn't use db_set for optimization purpose
|
||||
ref_doc.outstanding_amount = bal
|
||||
frappe.db.set_value(against_voucher_type, against_voucher, "outstanding_amount", bal)
|
||||
|
||||
|
||||
@@ -173,8 +173,8 @@ frappe.ui.form.on("Journal Entry", {
|
||||
var update_jv_details = function(doc, r) {
|
||||
$.each(r, function(i, d) {
|
||||
var row = frappe.model.add_child(doc, "Journal Entry Account", "accounts");
|
||||
row.account = d.account;
|
||||
row.balance = d.balance;
|
||||
frappe.model.set_value(row.doctype, row.name, "account", d.account)
|
||||
frappe.model.set_value(row.doctype, row.name, "balance", d.balance)
|
||||
});
|
||||
refresh_field("accounts");
|
||||
}
|
||||
@@ -312,8 +312,7 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro
|
||||
}
|
||||
}
|
||||
|
||||
get_outstanding(doctype, docname, company, child, due_date) {
|
||||
var me = this;
|
||||
get_outstanding(doctype, docname, company, child) {
|
||||
var args = {
|
||||
"doctype": doctype,
|
||||
"docname": docname,
|
||||
|
||||
@@ -184,7 +184,9 @@ class JournalEntry(AccountsController):
|
||||
}
|
||||
)
|
||||
|
||||
tax_withholding_details = get_party_tax_withholding_details(inv, self.tax_withholding_category)
|
||||
tax_withholding_details, advance_taxes, voucher_wise_amount = get_party_tax_withholding_details(
|
||||
inv, self.tax_withholding_category
|
||||
)
|
||||
|
||||
if not tax_withholding_details:
|
||||
return
|
||||
@@ -1208,6 +1210,7 @@ def get_outstanding(args):
|
||||
args = json.loads(args)
|
||||
|
||||
company_currency = erpnext.get_company_currency(args.get("company"))
|
||||
due_date = None
|
||||
|
||||
if args.get("doctype") == "Journal Entry":
|
||||
condition = " and party=%(party)s" if args.get("party") else ""
|
||||
@@ -1232,10 +1235,12 @@ def get_outstanding(args):
|
||||
invoice = frappe.db.get_value(
|
||||
args["doctype"],
|
||||
args["docname"],
|
||||
["outstanding_amount", "conversion_rate", scrub(party_type)],
|
||||
["outstanding_amount", "conversion_rate", scrub(party_type), "due_date"],
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
due_date = invoice.get("due_date")
|
||||
|
||||
exchange_rate = (
|
||||
invoice.conversion_rate if (args.get("account_currency") != company_currency) else 1
|
||||
)
|
||||
@@ -1258,6 +1263,7 @@ def get_outstanding(args):
|
||||
"exchange_rate": exchange_rate,
|
||||
"party_type": party_type,
|
||||
"party": invoice.get(scrub(party_type)),
|
||||
"reference_due_date": due_date,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -202,6 +202,7 @@
|
||||
"fieldname": "reference_type",
|
||||
"fieldtype": "Select",
|
||||
"label": "Reference Type",
|
||||
"no_copy": 1,
|
||||
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting\nFees\nFull and Final Statement"
|
||||
},
|
||||
{
|
||||
@@ -209,13 +210,15 @@
|
||||
"fieldtype": "Dynamic Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Reference Name",
|
||||
"no_copy": 1,
|
||||
"options": "reference_type"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.reference_type&&!in_list(doc.reference_type, ['Expense Claim', 'Asset', 'Employee Loan', 'Employee Advance'])",
|
||||
"fieldname": "reference_due_date",
|
||||
"fieldtype": "Select",
|
||||
"label": "Reference Due Date"
|
||||
"fieldtype": "Date",
|
||||
"label": "Reference Due Date",
|
||||
"no_copy": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "project",
|
||||
@@ -274,19 +277,22 @@
|
||||
"fieldname": "reference_detail_no",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Reference Detail No"
|
||||
"label": "Reference Detail No",
|
||||
"no_copy": 1
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-08-30 21:27:32.200299",
|
||||
"modified": "2022-10-26 20:03:10.906259",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Journal Entry Account",
|
||||
"naming_rule": "Random",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Journal Entry Template", {
|
||||
setup: function(frm) {
|
||||
refresh: function(frm) {
|
||||
frappe.model.set_default_values(frm.doc);
|
||||
|
||||
frm.set_query("account" ,"accounts", function(){
|
||||
|
||||
@@ -4,22 +4,20 @@
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils.background_jobs import is_job_queued
|
||||
|
||||
from erpnext.accounts.doctype.account.account import merge_account
|
||||
|
||||
|
||||
class LedgerMerge(Document):
|
||||
def start_merge(self):
|
||||
from frappe.core.page.background_jobs.background_jobs import get_info
|
||||
from frappe.utils.background_jobs import enqueue
|
||||
from frappe.utils.scheduler import is_scheduler_inactive
|
||||
|
||||
if is_scheduler_inactive() and not frappe.flags.in_test:
|
||||
frappe.throw(_("Scheduler is inactive. Cannot merge accounts."), title=_("Scheduler Inactive"))
|
||||
|
||||
enqueued_jobs = [d.get("job_name") for d in get_info()]
|
||||
|
||||
if self.name not in enqueued_jobs:
|
||||
if not is_job_queued(self.name):
|
||||
enqueue(
|
||||
start_merge,
|
||||
queue="default",
|
||||
|
||||
@@ -22,13 +22,13 @@ frappe.ui.form.on('Opening Invoice Creation Tool', {
|
||||
}
|
||||
if (data.user != frappe.session.user) return;
|
||||
if (data.count == data.total) {
|
||||
setTimeout((title) => {
|
||||
setTimeout(() => {
|
||||
frm.doc.import_in_progress = false;
|
||||
frm.clear_table("invoices");
|
||||
frm.refresh_fields();
|
||||
frm.page.clear_indicator();
|
||||
frm.dashboard.hide_progress(title);
|
||||
frappe.msgprint(__("Opening {0} Invoice created", [frm.doc.invoice_type]));
|
||||
frm.dashboard.hide_progress();
|
||||
frappe.msgprint(__("Opening {0} Invoices created", [frm.doc.invoice_type]));
|
||||
}, 1500, data.title);
|
||||
return;
|
||||
}
|
||||
@@ -51,13 +51,6 @@ frappe.ui.form.on('Opening Invoice Creation Tool', {
|
||||
method: "make_invoices",
|
||||
freeze: 1,
|
||||
freeze_message: __("Creating {0} Invoice", [frm.doc.invoice_type]),
|
||||
callback: function(r) {
|
||||
if (r.message.length == 1) {
|
||||
frappe.msgprint(__("{0} Invoice created successfully.", [frm.doc.invoice_type]));
|
||||
} else if (r.message.length < 50) {
|
||||
frappe.msgprint(__("{0} Invoices created successfully.", [frm.doc.invoice_type]));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import frappe
|
||||
from frappe import _, scrub
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import flt, nowdate
|
||||
from frappe.utils.background_jobs import enqueue
|
||||
from frappe.utils.background_jobs import enqueue, is_job_queued
|
||||
|
||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
|
||||
get_accounting_dimensions,
|
||||
@@ -207,14 +207,12 @@ class OpeningInvoiceCreationTool(Document):
|
||||
if len(invoices) < 50:
|
||||
return start_import(invoices)
|
||||
else:
|
||||
from frappe.core.page.background_jobs.background_jobs import get_info
|
||||
from frappe.utils.scheduler import is_scheduler_inactive
|
||||
|
||||
if is_scheduler_inactive() and not frappe.flags.in_test:
|
||||
frappe.throw(_("Scheduler is inactive. Cannot import data."), title=_("Scheduler Inactive"))
|
||||
|
||||
enqueued_jobs = [d.get("job_name") for d in get_info()]
|
||||
if self.name not in enqueued_jobs:
|
||||
if not is_job_queued(self.name):
|
||||
enqueue(
|
||||
start_import,
|
||||
queue="default",
|
||||
@@ -257,8 +255,6 @@ def start_import(invoices):
|
||||
|
||||
|
||||
def publish(index, total, doctype):
|
||||
if total < 50:
|
||||
return
|
||||
frappe.publish_realtime(
|
||||
"opening_invoice_creation_progress",
|
||||
dict(
|
||||
|
||||
@@ -1091,7 +1091,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
|
||||
$.each(tax_fields, function(i, fieldname) { tax[fieldname] = 0.0; });
|
||||
|
||||
frm.doc.paid_amount_after_tax = frm.doc.paid_amount;
|
||||
frm.doc.paid_amount_after_tax = frm.doc.base_paid_amount;
|
||||
});
|
||||
},
|
||||
|
||||
@@ -1182,7 +1182,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
}
|
||||
|
||||
cumulated_tax_fraction += tax.tax_fraction_for_current_item;
|
||||
frm.doc.paid_amount_after_tax = flt(frm.doc.paid_amount/(1+cumulated_tax_fraction))
|
||||
frm.doc.paid_amount_after_tax = flt(frm.doc.base_paid_amount/(1+cumulated_tax_fraction))
|
||||
});
|
||||
},
|
||||
|
||||
@@ -1214,6 +1214,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
frm.doc.total_taxes_and_charges = 0.0;
|
||||
frm.doc.base_total_taxes_and_charges = 0.0;
|
||||
|
||||
let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
|
||||
let actual_tax_dict = {};
|
||||
|
||||
// maintain actual tax rate based on idx
|
||||
@@ -1234,8 +1235,8 @@ frappe.ui.form.on('Payment Entry', {
|
||||
}
|
||||
}
|
||||
|
||||
tax.tax_amount = current_tax_amount;
|
||||
tax.base_tax_amount = tax.tax_amount * frm.doc.source_exchange_rate;
|
||||
// tax accounts are only in company currency
|
||||
tax.base_tax_amount = current_tax_amount;
|
||||
current_tax_amount *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0;
|
||||
|
||||
if(i==0) {
|
||||
@@ -1244,9 +1245,29 @@ frappe.ui.form.on('Payment Entry', {
|
||||
tax.total = flt(frm.doc["taxes"][i-1].total + current_tax_amount, precision("total", tax));
|
||||
}
|
||||
|
||||
tax.base_total = tax.total * frm.doc.source_exchange_rate;
|
||||
frm.doc.total_taxes_and_charges += current_tax_amount;
|
||||
frm.doc.base_total_taxes_and_charges += current_tax_amount * frm.doc.source_exchange_rate;
|
||||
// tac accounts are only in company currency
|
||||
tax.base_total = tax.total
|
||||
|
||||
// calculate total taxes and base total taxes
|
||||
if(frm.doc.payment_type == "Pay") {
|
||||
// tax accounts only have company currency
|
||||
if(tax.currency != frm.doc.paid_to_account_currency) {
|
||||
//total_taxes_and_charges has the target currency. so using target conversion rate
|
||||
frm.doc.total_taxes_and_charges += flt(current_tax_amount / frm.doc.target_exchange_rate);
|
||||
|
||||
} else {
|
||||
frm.doc.total_taxes_and_charges += current_tax_amount;
|
||||
}
|
||||
} else if(frm.doc.payment_type == "Receive") {
|
||||
if(tax.currency != frm.doc.paid_from_account_currency) {
|
||||
//total_taxes_and_charges has the target currency. so using source conversion rate
|
||||
frm.doc.total_taxes_and_charges += flt(current_tax_amount / frm.doc.source_exchange_rate);
|
||||
} else {
|
||||
frm.doc.total_taxes_and_charges += current_tax_amount;
|
||||
}
|
||||
}
|
||||
|
||||
frm.doc.base_total_taxes_and_charges += tax.base_tax_amount;
|
||||
|
||||
frm.refresh_field('taxes');
|
||||
frm.refresh_field('total_taxes_and_charges');
|
||||
|
||||
@@ -181,7 +181,11 @@ class PaymentEntry(AccountsController):
|
||||
frappe.throw(_("Party is mandatory"))
|
||||
|
||||
_party_name = "title" if self.party_type == "Shareholder" else self.party_type.lower() + "_name"
|
||||
self.party_name = frappe.db.get_value(self.party_type, self.party, _party_name)
|
||||
|
||||
if frappe.db.has_column(self.party_type, _party_name):
|
||||
self.party_name = frappe.db.get_value(self.party_type, self.party, _party_name)
|
||||
else:
|
||||
self.party_name = frappe.db.get_value(self.party_type, self.party, "name")
|
||||
|
||||
if self.party:
|
||||
if not self.party_balance:
|
||||
@@ -295,6 +299,9 @@ class PaymentEntry(AccountsController):
|
||||
def validate_reference_documents(self):
|
||||
valid_reference_doctypes = self.get_valid_reference_doctypes()
|
||||
|
||||
if not valid_reference_doctypes:
|
||||
return
|
||||
|
||||
for d in self.get("references"):
|
||||
if not d.allocated_amount:
|
||||
continue
|
||||
@@ -362,7 +369,7 @@ class PaymentEntry(AccountsController):
|
||||
if not d.allocated_amount:
|
||||
continue
|
||||
|
||||
if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Fees"):
|
||||
if d.reference_doctype in ("Sales Invoice", "Purchase Invoice"):
|
||||
outstanding_amount, is_return = frappe.get_cached_value(
|
||||
d.reference_doctype, d.reference_name, ["outstanding_amount", "is_return"]
|
||||
)
|
||||
@@ -933,6 +940,13 @@ class PaymentEntry(AccountsController):
|
||||
)
|
||||
|
||||
if not d.included_in_paid_amount:
|
||||
if get_account_currency(payment_account) != self.company_currency:
|
||||
if self.payment_type == "Receive":
|
||||
exchange_rate = self.target_exchange_rate
|
||||
elif self.payment_type in ["Pay", "Internal Transfer"]:
|
||||
exchange_rate = self.source_exchange_rate
|
||||
base_tax_amount = flt((tax_amount / exchange_rate), self.precision("paid_amount"))
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
@@ -981,7 +995,9 @@ class PaymentEntry(AccountsController):
|
||||
if self.payment_type in ("Receive", "Pay") and self.party:
|
||||
for d in self.get("references"):
|
||||
if d.allocated_amount and d.reference_doctype in frappe.get_hooks("advance_payment_doctypes"):
|
||||
frappe.get_doc(d.reference_doctype, d.reference_name).set_total_advance_paid()
|
||||
frappe.get_doc(
|
||||
d.reference_doctype, d.reference_name, for_update=True
|
||||
).set_total_advance_paid()
|
||||
|
||||
def on_recurring(self, reference_doc, auto_repeat_doc):
|
||||
self.reference_no = reference_doc.name
|
||||
@@ -1026,7 +1042,7 @@ class PaymentEntry(AccountsController):
|
||||
for fieldname in tax_fields:
|
||||
tax.set(fieldname, 0.0)
|
||||
|
||||
self.paid_amount_after_tax = self.paid_amount
|
||||
self.paid_amount_after_tax = self.base_paid_amount
|
||||
|
||||
def determine_exclusive_rate(self):
|
||||
if not any(cint(tax.included_in_paid_amount) for tax in self.get("taxes")):
|
||||
@@ -1045,7 +1061,7 @@ class PaymentEntry(AccountsController):
|
||||
|
||||
cumulated_tax_fraction += tax.tax_fraction_for_current_item
|
||||
|
||||
self.paid_amount_after_tax = flt(self.paid_amount / (1 + cumulated_tax_fraction))
|
||||
self.paid_amount_after_tax = flt(self.base_paid_amount / (1 + cumulated_tax_fraction))
|
||||
|
||||
def calculate_taxes(self):
|
||||
self.total_taxes_and_charges = 0.0
|
||||
@@ -1068,7 +1084,7 @@ class PaymentEntry(AccountsController):
|
||||
current_tax_amount += actual_tax_dict[tax.idx]
|
||||
|
||||
tax.tax_amount = current_tax_amount
|
||||
tax.base_tax_amount = tax.tax_amount * self.source_exchange_rate
|
||||
tax.base_tax_amount = current_tax_amount
|
||||
|
||||
if tax.add_deduct_tax == "Deduct":
|
||||
current_tax_amount *= -1.0
|
||||
@@ -1082,14 +1098,20 @@ class PaymentEntry(AccountsController):
|
||||
self.get("taxes")[i - 1].total + current_tax_amount, self.precision("total", tax)
|
||||
)
|
||||
|
||||
tax.base_total = tax.total * self.source_exchange_rate
|
||||
tax.base_total = tax.total
|
||||
|
||||
if self.payment_type == "Pay":
|
||||
self.base_total_taxes_and_charges += flt(current_tax_amount / self.source_exchange_rate)
|
||||
self.total_taxes_and_charges += flt(current_tax_amount / self.target_exchange_rate)
|
||||
else:
|
||||
self.base_total_taxes_and_charges += flt(current_tax_amount / self.target_exchange_rate)
|
||||
self.total_taxes_and_charges += flt(current_tax_amount / self.source_exchange_rate)
|
||||
if tax.currency != self.paid_to_account_currency:
|
||||
self.total_taxes_and_charges += flt(current_tax_amount / self.target_exchange_rate)
|
||||
else:
|
||||
self.total_taxes_and_charges += current_tax_amount
|
||||
elif self.payment_type == "Receive":
|
||||
if tax.currency != self.paid_from_account_currency:
|
||||
self.total_taxes_and_charges += flt(current_tax_amount / self.source_exchange_rate)
|
||||
else:
|
||||
self.total_taxes_and_charges += current_tax_amount
|
||||
|
||||
self.base_total_taxes_and_charges += tax.base_tax_amount
|
||||
|
||||
if self.get("taxes"):
|
||||
self.paid_amount_after_tax = self.get("taxes")[-1].base_total
|
||||
@@ -1184,6 +1206,7 @@ def get_outstanding_reference_documents(args):
|
||||
|
||||
ple = qb.DocType("Payment Ledger Entry")
|
||||
common_filter = []
|
||||
posting_and_due_date = []
|
||||
|
||||
# confirm that Supplier is not blocked
|
||||
if args.get("party_type") == "Supplier":
|
||||
@@ -1200,7 +1223,7 @@ def get_outstanding_reference_documents(args):
|
||||
party_account_currency = get_account_currency(args.get("party_account"))
|
||||
company_currency = frappe.get_cached_value("Company", args.get("company"), "default_currency")
|
||||
|
||||
# Get positive outstanding sales /purchase invoices/ Fees
|
||||
# Get positive outstanding sales /purchase invoices
|
||||
condition = ""
|
||||
if args.get("voucher_type") and args.get("voucher_no"):
|
||||
condition = " and voucher_type={0} and voucher_no={1}".format(
|
||||
@@ -1224,7 +1247,7 @@ def get_outstanding_reference_documents(args):
|
||||
condition += " and {0} between '{1}' and '{2}'".format(
|
||||
fieldname, args.get(date_fields[0]), args.get(date_fields[1])
|
||||
)
|
||||
common_filter.append(ple[fieldname][args.get(date_fields[0]) : args.get(date_fields[1])])
|
||||
posting_and_due_date.append(ple[fieldname][args.get(date_fields[0]) : args.get(date_fields[1])])
|
||||
|
||||
if args.get("company"):
|
||||
condition += " and company = {0}".format(frappe.db.escape(args.get("company")))
|
||||
@@ -1235,6 +1258,7 @@ def get_outstanding_reference_documents(args):
|
||||
args.get("party"),
|
||||
args.get("party_account"),
|
||||
common_filter=common_filter,
|
||||
posting_date=posting_and_due_date,
|
||||
min_outstanding=args.get("outstanding_amt_greater_than"),
|
||||
max_outstanding=args.get("outstanding_amt_less_than"),
|
||||
)
|
||||
@@ -1595,10 +1619,11 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
|
||||
elif reference_doctype != "Journal Entry":
|
||||
if not total_amount:
|
||||
if party_account_currency == company_currency:
|
||||
total_amount = ref_doc.base_grand_total
|
||||
# for handling cases that don't have multi-currency (base field)
|
||||
total_amount = ref_doc.get("grand_total") or ref_doc.get("base_grand_total")
|
||||
exchange_rate = 1
|
||||
else:
|
||||
total_amount = ref_doc.grand_total
|
||||
total_amount = ref_doc.get("grand_total")
|
||||
if not exchange_rate:
|
||||
# Get the exchange rate from the original ref doc
|
||||
# or get it based on the posting date of the ref doc.
|
||||
@@ -1609,7 +1634,7 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
|
||||
if reference_doctype in ("Sales Invoice", "Purchase Invoice"):
|
||||
outstanding_amount = ref_doc.get("outstanding_amount")
|
||||
else:
|
||||
outstanding_amount = flt(total_amount) - flt(ref_doc.advance_paid)
|
||||
outstanding_amount = flt(total_amount) - flt(ref_doc.get("advance_paid"))
|
||||
|
||||
else:
|
||||
# Get the exchange rate based on the posting date of the ref doc.
|
||||
@@ -1627,16 +1652,23 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=None):
|
||||
def get_payment_entry(
|
||||
dt, dn, party_amount=None, bank_account=None, bank_amount=None, party_type=None, payment_type=None
|
||||
):
|
||||
reference_doc = None
|
||||
doc = frappe.get_doc(dt, dn)
|
||||
if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) > 0:
|
||||
frappe.throw(_("Can only make payment against unbilled {0}").format(dt))
|
||||
|
||||
party_type = set_party_type(dt)
|
||||
if not party_type:
|
||||
party_type = set_party_type(dt)
|
||||
|
||||
party_account = set_party_account(dt, dn, doc, party_type)
|
||||
party_account_currency = set_party_account_currency(dt, party_account, doc)
|
||||
payment_type = set_payment_type(dt, doc)
|
||||
|
||||
if not payment_type:
|
||||
payment_type = set_payment_type(dt, doc)
|
||||
|
||||
grand_total, outstanding_amount = set_grand_total_and_outstanding_amount(
|
||||
party_amount, dt, party_account_currency, doc
|
||||
)
|
||||
@@ -1786,8 +1818,6 @@ def set_party_account(dt, dn, doc, party_type):
|
||||
party_account = get_party_account_based_on_invoice_discounting(dn) or doc.debit_to
|
||||
elif dt == "Purchase Invoice":
|
||||
party_account = doc.credit_to
|
||||
elif dt == "Fees":
|
||||
party_account = doc.receivable_account
|
||||
else:
|
||||
party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company)
|
||||
return party_account
|
||||
@@ -1803,8 +1833,7 @@ def set_party_account_currency(dt, party_account, doc):
|
||||
|
||||
def set_payment_type(dt, doc):
|
||||
if (
|
||||
dt == "Sales Order"
|
||||
or (dt in ("Sales Invoice", "Fees", "Dunning") and doc.outstanding_amount > 0)
|
||||
dt == "Sales Order" or (dt in ("Sales Invoice", "Dunning") and doc.outstanding_amount > 0)
|
||||
) or (dt == "Purchase Invoice" and doc.outstanding_amount < 0):
|
||||
payment_type = "Receive"
|
||||
else:
|
||||
@@ -1822,18 +1851,15 @@ def set_grand_total_and_outstanding_amount(party_amount, dt, party_account_curre
|
||||
else:
|
||||
grand_total = doc.rounded_total or doc.grand_total
|
||||
outstanding_amount = doc.outstanding_amount
|
||||
elif dt == "Fees":
|
||||
grand_total = doc.grand_total
|
||||
outstanding_amount = doc.outstanding_amount
|
||||
elif dt == "Dunning":
|
||||
grand_total = doc.grand_total
|
||||
outstanding_amount = doc.grand_total
|
||||
else:
|
||||
if party_account_currency == doc.company_currency:
|
||||
grand_total = flt(doc.get("base_rounded_total") or doc.base_grand_total)
|
||||
grand_total = flt(doc.get("base_rounded_total") or doc.get("base_grand_total"))
|
||||
else:
|
||||
grand_total = flt(doc.get("rounded_total") or doc.grand_total)
|
||||
outstanding_amount = grand_total - flt(doc.advance_paid)
|
||||
grand_total = flt(doc.get("rounded_total") or doc.get("grand_total"))
|
||||
outstanding_amount = doc.get("outstanding_amount") or (grand_total - flt(doc.advance_paid))
|
||||
return grand_total, outstanding_amount
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe import qb
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
from frappe.utils import flt, nowdate
|
||||
|
||||
@@ -722,6 +723,46 @@ class TestPaymentEntry(FrappeTestCase):
|
||||
flt(payment_entry.total_taxes_and_charges, 2), flt(10 / payment_entry.target_exchange_rate, 2)
|
||||
)
|
||||
|
||||
def test_gl_of_multi_currency_payment_with_taxes(self):
|
||||
payment_entry = create_payment_entry(
|
||||
party="_Test Supplier USD", paid_to="_Test Payable USD - _TC", save=True
|
||||
)
|
||||
payment_entry.append(
|
||||
"taxes",
|
||||
{
|
||||
"account_head": "_Test Account Service Tax - _TC",
|
||||
"charge_type": "Actual",
|
||||
"tax_amount": 100,
|
||||
"add_deduct_tax": "Add",
|
||||
"description": "Test",
|
||||
},
|
||||
)
|
||||
payment_entry.target_exchange_rate = 80
|
||||
payment_entry.received_amount = 12.5
|
||||
payment_entry = payment_entry.submit()
|
||||
gle = qb.DocType("GL Entry")
|
||||
gl_entries = (
|
||||
qb.from_(gle)
|
||||
.select(
|
||||
gle.account,
|
||||
gle.debit,
|
||||
gle.credit,
|
||||
gle.debit_in_account_currency,
|
||||
gle.credit_in_account_currency,
|
||||
)
|
||||
.orderby(gle.account)
|
||||
.where(gle.voucher_no == payment_entry.name)
|
||||
.run()
|
||||
)
|
||||
|
||||
expected_gl_entries = (
|
||||
("_Test Account Service Tax - _TC", 100.0, 0.0, 100.0, 0.0),
|
||||
("_Test Bank - _TC", 0.0, 1100.0, 0.0, 1100.0),
|
||||
("_Test Payable USD - _TC", 1000.0, 0.0, 12.5, 0),
|
||||
)
|
||||
|
||||
self.assertEqual(gl_entries, expected_gl_entries)
|
||||
|
||||
def test_payment_entry_against_onhold_purchase_invoice(self):
|
||||
pi = make_purchase_invoice()
|
||||
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
"amount",
|
||||
"account_currency",
|
||||
"amount_in_account_currency",
|
||||
"delinked"
|
||||
"delinked",
|
||||
"remarks"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -136,12 +137,17 @@
|
||||
"fieldtype": "Link",
|
||||
"label": "Finance Book",
|
||||
"options": "Finance Book"
|
||||
},
|
||||
{
|
||||
"fieldname": "remarks",
|
||||
"fieldtype": "Text",
|
||||
"label": "Remarks"
|
||||
}
|
||||
],
|
||||
"in_create": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2022-07-11 09:13:54.379168",
|
||||
"modified": "2022-08-22 15:32:56.629430",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Ledger Entry",
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
|
||||
import frappe
|
||||
from frappe import qb
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
from frappe.tests.utils import FrappeTestCase, change_settings
|
||||
from frappe.utils import nowdate
|
||||
|
||||
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||
from erpnext.stock.doctype.item.test_item import create_item
|
||||
|
||||
|
||||
@@ -127,6 +128,25 @@ class TestPaymentLedgerEntry(FrappeTestCase):
|
||||
payment.posting_date = posting_date
|
||||
return payment
|
||||
|
||||
def create_sales_order(
|
||||
self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False
|
||||
):
|
||||
so = make_sales_order(
|
||||
company=self.company,
|
||||
transaction_date=posting_date,
|
||||
customer=self.customer,
|
||||
item_code=self.item,
|
||||
cost_center=self.cost_center,
|
||||
warehouse=self.warehouse,
|
||||
debit_to=self.debit_to,
|
||||
currency="INR",
|
||||
qty=qty,
|
||||
rate=100,
|
||||
do_not_save=do_not_save,
|
||||
do_not_submit=do_not_submit,
|
||||
)
|
||||
return so
|
||||
|
||||
def clear_old_entries(self):
|
||||
doctype_list = [
|
||||
"GL Entry",
|
||||
@@ -406,3 +426,89 @@ class TestPaymentLedgerEntry(FrappeTestCase):
|
||||
]
|
||||
self.assertEqual(pl_entries_for_crnote[0], expected_values[0])
|
||||
self.assertEqual(pl_entries_for_crnote[1], expected_values[1])
|
||||
|
||||
@change_settings(
|
||||
"Accounts Settings",
|
||||
{"unlink_payment_on_cancellation_of_invoice": 1, "delete_linked_ledger_entries": 1},
|
||||
)
|
||||
def test_multi_payment_unlink_on_invoice_cancellation(self):
|
||||
transaction_date = nowdate()
|
||||
amount = 100
|
||||
si = self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date)
|
||||
|
||||
for amt in [40, 40, 20]:
|
||||
# payment 1
|
||||
pe = get_payment_entry(si.doctype, si.name)
|
||||
pe.paid_amount = amt
|
||||
pe.get("references")[0].allocated_amount = amt
|
||||
pe = pe.save().submit()
|
||||
|
||||
si.reload()
|
||||
si.cancel()
|
||||
|
||||
entries = frappe.db.get_list(
|
||||
"Payment Ledger Entry",
|
||||
filters={"against_voucher_type": si.doctype, "against_voucher_no": si.name, "delinked": 0},
|
||||
)
|
||||
self.assertEqual(entries, [])
|
||||
|
||||
# with references removed, deletion should be possible
|
||||
si.delete()
|
||||
self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, si.doctype, si.name)
|
||||
|
||||
@change_settings(
|
||||
"Accounts Settings",
|
||||
{"unlink_payment_on_cancellation_of_invoice": 1, "delete_linked_ledger_entries": 1},
|
||||
)
|
||||
def test_multi_je_unlink_on_invoice_cancellation(self):
|
||||
transaction_date = nowdate()
|
||||
amount = 100
|
||||
si = self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date)
|
||||
|
||||
# multiple JE's against invoice
|
||||
for amt in [40, 40, 20]:
|
||||
je1 = self.create_journal_entry(
|
||||
self.income_account, self.debit_to, amt, posting_date=transaction_date
|
||||
)
|
||||
je1.get("accounts")[1].party_type = "Customer"
|
||||
je1.get("accounts")[1].party = self.customer
|
||||
je1.get("accounts")[1].reference_type = si.doctype
|
||||
je1.get("accounts")[1].reference_name = si.name
|
||||
je1 = je1.save().submit()
|
||||
|
||||
si.reload()
|
||||
si.cancel()
|
||||
|
||||
entries = frappe.db.get_list(
|
||||
"Payment Ledger Entry",
|
||||
filters={"against_voucher_type": si.doctype, "against_voucher_no": si.name, "delinked": 0},
|
||||
)
|
||||
self.assertEqual(entries, [])
|
||||
|
||||
# with references removed, deletion should be possible
|
||||
si.delete()
|
||||
self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, si.doctype, si.name)
|
||||
|
||||
@change_settings(
|
||||
"Accounts Settings",
|
||||
{"unlink_payment_on_cancellation_of_invoice": 1, "delete_linked_ledger_entries": 1},
|
||||
)
|
||||
def test_advance_payment_unlink_on_order_cancellation(self):
|
||||
transaction_date = nowdate()
|
||||
amount = 100
|
||||
so = self.create_sales_order(qty=1, rate=amount, posting_date=transaction_date).save().submit()
|
||||
|
||||
pe = get_payment_entry(so.doctype, so.name).save().submit()
|
||||
|
||||
so.reload()
|
||||
so.cancel()
|
||||
|
||||
entries = frappe.db.get_list(
|
||||
"Payment Ledger Entry",
|
||||
filters={"against_voucher_type": so.doctype, "against_voucher_no": so.name, "delinked": 0},
|
||||
)
|
||||
self.assertEqual(entries, [])
|
||||
|
||||
# with references removed, deletion should be possible
|
||||
so.delete()
|
||||
self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, so.doctype, so.name)
|
||||
|
||||
@@ -14,6 +14,7 @@ from erpnext.accounts.utils import (
|
||||
QueryPaymentLedger,
|
||||
get_outstanding_invoices,
|
||||
reconcile_against_document,
|
||||
update_reference_in_payment_entry,
|
||||
)
|
||||
from erpnext.controllers.accounts_controller import get_advance_payment_entries
|
||||
|
||||
@@ -22,6 +23,7 @@ class PaymentReconciliation(Document):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PaymentReconciliation, self).__init__(*args, **kwargs)
|
||||
self.common_filter_conditions = []
|
||||
self.ple_posting_date_filter = []
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_unreconciled_entries(self):
|
||||
@@ -150,6 +152,7 @@ class PaymentReconciliation(Document):
|
||||
return_outstanding = ple_query.get_voucher_outstandings(
|
||||
vouchers=return_invoices,
|
||||
common_filter=self.common_filter_conditions,
|
||||
posting_date=self.ple_posting_date_filter,
|
||||
min_outstanding=-(self.minimum_payment_amount) if self.minimum_payment_amount else None,
|
||||
max_outstanding=-(self.maximum_payment_amount) if self.maximum_payment_amount else None,
|
||||
get_payments=True,
|
||||
@@ -187,6 +190,7 @@ class PaymentReconciliation(Document):
|
||||
self.party,
|
||||
self.receivable_payable_account,
|
||||
common_filter=self.common_filter_conditions,
|
||||
posting_date=self.ple_posting_date_filter,
|
||||
min_outstanding=self.minimum_invoice_amount if self.minimum_invoice_amount else None,
|
||||
max_outstanding=self.maximum_invoice_amount if self.maximum_invoice_amount else None,
|
||||
)
|
||||
@@ -209,6 +213,23 @@ class PaymentReconciliation(Document):
|
||||
inv.currency = entry.get("currency")
|
||||
inv.outstanding_amount = flt(entry.get("outstanding_amount"))
|
||||
|
||||
def get_difference_amount(self, allocated_entry):
|
||||
if allocated_entry.get("reference_type") != "Payment Entry":
|
||||
return
|
||||
|
||||
dr_or_cr = (
|
||||
"credit_in_account_currency"
|
||||
if erpnext.get_party_account_type(self.party_type) == "Receivable"
|
||||
else "debit_in_account_currency"
|
||||
)
|
||||
|
||||
row = self.get_payment_details(allocated_entry, dr_or_cr)
|
||||
|
||||
doc = frappe.get_doc(allocated_entry.reference_type, allocated_entry.reference_name)
|
||||
update_reference_in_payment_entry(row, doc, do_not_save=True)
|
||||
|
||||
return doc.difference_amount
|
||||
|
||||
@frappe.whitelist()
|
||||
def allocate_entries(self, args):
|
||||
self.validate_entries()
|
||||
@@ -224,12 +245,16 @@ class PaymentReconciliation(Document):
|
||||
res = self.get_allocated_entry(pay, inv, pay["amount"])
|
||||
inv["outstanding_amount"] = flt(inv.get("outstanding_amount")) - flt(pay.get("amount"))
|
||||
pay["amount"] = 0
|
||||
|
||||
res.difference_amount = self.get_difference_amount(res)
|
||||
|
||||
if pay.get("amount") == 0:
|
||||
entries.append(res)
|
||||
break
|
||||
elif inv.get("outstanding_amount") == 0:
|
||||
entries.append(res)
|
||||
continue
|
||||
|
||||
else:
|
||||
break
|
||||
|
||||
@@ -350,6 +375,7 @@ class PaymentReconciliation(Document):
|
||||
|
||||
def build_qb_filter_conditions(self, get_invoices=False, get_return_invoices=False):
|
||||
self.common_filter_conditions.clear()
|
||||
self.ple_posting_date_filter.clear()
|
||||
ple = qb.DocType("Payment Ledger Entry")
|
||||
|
||||
self.common_filter_conditions.append(ple.company == self.company)
|
||||
@@ -359,15 +385,15 @@ class PaymentReconciliation(Document):
|
||||
|
||||
if get_invoices:
|
||||
if self.from_invoice_date:
|
||||
self.common_filter_conditions.append(ple.posting_date.gte(self.from_invoice_date))
|
||||
self.ple_posting_date_filter.append(ple.posting_date.gte(self.from_invoice_date))
|
||||
if self.to_invoice_date:
|
||||
self.common_filter_conditions.append(ple.posting_date.lte(self.to_invoice_date))
|
||||
self.ple_posting_date_filter.append(ple.posting_date.lte(self.to_invoice_date))
|
||||
|
||||
elif get_return_invoices:
|
||||
if self.from_payment_date:
|
||||
self.common_filter_conditions.append(ple.posting_date.gte(self.from_payment_date))
|
||||
self.ple_posting_date_filter.append(ple.posting_date.gte(self.from_payment_date))
|
||||
if self.to_payment_date:
|
||||
self.common_filter_conditions.append(ple.posting_date.lte(self.to_payment_date))
|
||||
self.ple_posting_date_filter.append(ple.posting_date.lte(self.to_payment_date))
|
||||
|
||||
def get_conditions(self, get_payments=False):
|
||||
condition = " and company = '{0}' ".format(self.company)
|
||||
|
||||
@@ -283,6 +283,41 @@ class TestPaymentReconciliation(FrappeTestCase):
|
||||
self.assertEqual(len(pr.get("invoices")), 2)
|
||||
self.assertEqual(len(pr.get("payments")), 2)
|
||||
|
||||
def test_filter_posting_date_case2(self):
|
||||
"""
|
||||
Posting date should not affect outstanding amount calculation
|
||||
"""
|
||||
|
||||
from_date = add_days(nowdate(), -30)
|
||||
to_date = nowdate()
|
||||
self.create_payment_entry(amount=25, posting_date=from_date).submit()
|
||||
self.create_sales_invoice(rate=25, qty=1, posting_date=to_date)
|
||||
|
||||
pr = self.create_payment_reconciliation()
|
||||
pr.from_invoice_date = pr.from_payment_date = from_date
|
||||
pr.to_invoice_date = pr.to_payment_date = to_date
|
||||
pr.get_unreconciled_entries()
|
||||
|
||||
self.assertEqual(len(pr.invoices), 1)
|
||||
self.assertEqual(len(pr.payments), 1)
|
||||
|
||||
invoices = [x.as_dict() for x in pr.invoices]
|
||||
payments = [x.as_dict() for x in pr.payments]
|
||||
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
|
||||
pr.reconcile()
|
||||
|
||||
pr.get_unreconciled_entries()
|
||||
|
||||
self.assertEqual(len(pr.invoices), 0)
|
||||
self.assertEqual(len(pr.payments), 0)
|
||||
|
||||
pr.from_invoice_date = pr.from_payment_date = to_date
|
||||
pr.to_invoice_date = pr.to_payment_date = to_date
|
||||
|
||||
pr.get_unreconciled_entries()
|
||||
|
||||
self.assertEqual(len(pr.invoices), 0)
|
||||
|
||||
def test_filter_invoice_limit(self):
|
||||
# check filter condition - invoice limit
|
||||
transaction_date = nowdate()
|
||||
|
||||
@@ -186,8 +186,10 @@
|
||||
{
|
||||
"fetch_from": "bank_account.bank",
|
||||
"fieldname": "bank",
|
||||
"fieldtype": "Read Only",
|
||||
"label": "Bank"
|
||||
"fieldtype": "Link",
|
||||
"label": "Bank",
|
||||
"options": "Bank",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "bank_account.bank_account_no",
|
||||
@@ -366,10 +368,11 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-09-18 12:24:14.178853",
|
||||
"modified": "2022-09-30 16:19:43.680025",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Request",
|
||||
"naming_rule": "By \"Naming Series\" field",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
@@ -401,5 +404,6 @@
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
@@ -39,6 +39,7 @@
|
||||
{
|
||||
"columns": 2,
|
||||
"fetch_from": "payment_term.description",
|
||||
"fetch_if_empty": 1,
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Small Text",
|
||||
"in_list_view": 1,
|
||||
@@ -159,7 +160,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-04-28 05:41:35.084233",
|
||||
"modified": "2022-09-16 13:57:06.382859",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Schedule",
|
||||
@@ -168,5 +169,6 @@
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -36,6 +36,15 @@ frappe.ui.form.on('POS Closing Entry', {
|
||||
});
|
||||
|
||||
set_html_data(frm);
|
||||
|
||||
if (frm.doc.docstatus == 1) {
|
||||
if (!frm.doc.posting_date) {
|
||||
frm.set_value("posting_date", frappe.datetime.nowdate());
|
||||
}
|
||||
if (!frm.doc.posting_time) {
|
||||
frm.set_value("posting_time", frappe.datetime.now_time());
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"period_end_date",
|
||||
"column_break_3",
|
||||
"posting_date",
|
||||
"posting_time",
|
||||
"pos_opening_entry",
|
||||
"status",
|
||||
"section_break_5",
|
||||
@@ -51,7 +52,6 @@
|
||||
"fieldtype": "Datetime",
|
||||
"in_list_view": 1,
|
||||
"label": "Period End Date",
|
||||
"read_only": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
@@ -219,6 +219,13 @@
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Error",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "posting_time",
|
||||
"fieldtype": "Time",
|
||||
"label": "Posting Time",
|
||||
"no_copy": 1,
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"is_submittable": 1,
|
||||
@@ -228,10 +235,11 @@
|
||||
"link_fieldname": "pos_closing_entry"
|
||||
}
|
||||
],
|
||||
"modified": "2021-10-20 16:19:25.340565",
|
||||
"modified": "2022-08-01 11:37:14.991228",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "POS Closing Entry",
|
||||
"naming_rule": "Expression (old style)",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
@@ -278,5 +286,6 @@
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -15,6 +15,9 @@ from erpnext.controllers.status_updater import StatusUpdater
|
||||
|
||||
class POSClosingEntry(StatusUpdater):
|
||||
def validate(self):
|
||||
self.posting_date = self.posting_date or frappe.utils.nowdate()
|
||||
self.posting_time = self.posting_time or frappe.utils.nowtime()
|
||||
|
||||
if frappe.db.get_value("POS Opening Entry", self.pos_opening_entry, "status") != "Open":
|
||||
frappe.throw(_("Selected POS Opening Entry should be open."), title=_("Invalid Opening Entry"))
|
||||
|
||||
|
||||
@@ -343,7 +343,8 @@
|
||||
"no_copy": 1,
|
||||
"options": "POS Invoice",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
"read_only": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
@@ -1553,7 +1554,7 @@
|
||||
"icon": "fa fa-file-text",
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-03-22 13:00:24.166684",
|
||||
"modified": "2022-09-30 03:49:50.455199",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "POS Invoice",
|
||||
|
||||
@@ -239,14 +239,14 @@ class POSInvoice(SalesInvoice):
|
||||
frappe.bold(d.warehouse),
|
||||
frappe.bold(d.qty),
|
||||
)
|
||||
if flt(available_stock) <= 0:
|
||||
if is_stock_item and flt(available_stock) <= 0:
|
||||
frappe.throw(
|
||||
_("Row #{}: Item Code: {} is not available under warehouse {}.").format(
|
||||
d.idx, item_code, warehouse
|
||||
),
|
||||
title=_("Item Unavailable"),
|
||||
)
|
||||
elif flt(available_stock) < flt(d.qty):
|
||||
elif is_stock_item and flt(available_stock) < flt(d.qty):
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{}: Stock quantity not enough for Item Code: {} under warehouse {}. Available quantity {}."
|
||||
@@ -632,11 +632,12 @@ def get_stock_availability(item_code, warehouse):
|
||||
pos_sales_qty = get_pos_reserved_qty(item_code, warehouse)
|
||||
return bin_qty - pos_sales_qty, is_stock_item
|
||||
else:
|
||||
is_stock_item = False
|
||||
is_stock_item = True
|
||||
if frappe.db.exists("Product Bundle", item_code):
|
||||
return get_bundle_availability(item_code, warehouse), is_stock_item
|
||||
else:
|
||||
# Is a service item
|
||||
is_stock_item = False
|
||||
# Is a service item or non_stock item
|
||||
return 0, is_stock_item
|
||||
|
||||
|
||||
@@ -650,7 +651,9 @@ def get_bundle_availability(bundle_item_code, warehouse):
|
||||
available_qty = item_bin_qty - item_pos_reserved_qty
|
||||
|
||||
max_available_bundles = available_qty / item.qty
|
||||
if bundle_bin_qty > max_available_bundles:
|
||||
if bundle_bin_qty > max_available_bundles and frappe.get_value(
|
||||
"Item", item.item_code, "is_stock_item"
|
||||
):
|
||||
bundle_bin_qty = max_available_bundles
|
||||
|
||||
pos_sales_qty = get_pos_reserved_qty(bundle_item_code, warehouse)
|
||||
|
||||
@@ -495,6 +495,67 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
|
||||
self.assertRaises(frappe.ValidationError, pos.submit)
|
||||
|
||||
def test_value_error_on_serial_no_validation(self):
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
|
||||
|
||||
se = make_serialized_item(
|
||||
company="_Test Company",
|
||||
target_warehouse="Stores - _TC",
|
||||
cost_center="Main - _TC",
|
||||
expense_account="Cost of Goods Sold - _TC",
|
||||
)
|
||||
serial_nos = se.get("items")[0].serial_no
|
||||
|
||||
# make a pos invoice
|
||||
pos = create_pos_invoice(
|
||||
company="_Test Company",
|
||||
debit_to="Debtors - _TC",
|
||||
account_for_change_amount="Cash - _TC",
|
||||
warehouse="Stores - _TC",
|
||||
income_account="Sales - _TC",
|
||||
expense_account="Cost of Goods Sold - _TC",
|
||||
cost_center="Main - _TC",
|
||||
item=se.get("items")[0].item_code,
|
||||
rate=1000,
|
||||
qty=1,
|
||||
do_not_save=1,
|
||||
)
|
||||
pos.get("items")[0].has_serial_no = 1
|
||||
pos.get("items")[0].serial_no = serial_nos.split("\n")[0]
|
||||
pos.set("payments", [])
|
||||
pos.append(
|
||||
"payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 1000, "default": 1}
|
||||
)
|
||||
pos = pos.save().submit()
|
||||
|
||||
# make a return
|
||||
pos_return = make_sales_return(pos.name)
|
||||
pos_return.paid_amount = pos_return.grand_total
|
||||
pos_return.save()
|
||||
pos_return.submit()
|
||||
|
||||
# set docstatus to 2 for pos to trigger this issue
|
||||
frappe.db.set_value("POS Invoice", pos.name, "docstatus", 2)
|
||||
|
||||
pos2 = create_pos_invoice(
|
||||
company="_Test Company",
|
||||
debit_to="Debtors - _TC",
|
||||
account_for_change_amount="Cash - _TC",
|
||||
warehouse="Stores - _TC",
|
||||
income_account="Sales - _TC",
|
||||
expense_account="Cost of Goods Sold - _TC",
|
||||
cost_center="Main - _TC",
|
||||
item=se.get("items")[0].item_code,
|
||||
rate=1000,
|
||||
qty=1,
|
||||
do_not_save=1,
|
||||
)
|
||||
|
||||
pos2.get("items")[0].has_serial_no = 1
|
||||
pos2.get("items")[0].serial_no = serial_nos.split("\n")[0]
|
||||
# Value error should not be triggered on validation
|
||||
pos2.save()
|
||||
|
||||
def test_loyalty_points(self):
|
||||
from erpnext.accounts.doctype.loyalty_program.loyalty_program import (
|
||||
get_loyalty_program_details_with_points,
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"barcode",
|
||||
"has_item_scanned",
|
||||
"item_code",
|
||||
"col_break1",
|
||||
"item_name",
|
||||
@@ -808,11 +809,19 @@
|
||||
"fieldtype": "Check",
|
||||
"label": "Grant Commission",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "barcode",
|
||||
"fieldname": "has_item_scanned",
|
||||
"fieldtype": "Check",
|
||||
"label": "Has Item Scanned",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-10-05 12:23:47.506290",
|
||||
"modified": "2022-11-02 12:52:39.125295",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "POS Invoice Item",
|
||||
@@ -820,5 +829,6 @@
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"posting_date",
|
||||
"posting_time",
|
||||
"merge_invoices_based_on",
|
||||
"column_break_3",
|
||||
"pos_closing_entry",
|
||||
@@ -105,12 +106,19 @@
|
||||
"label": "Customer Group",
|
||||
"mandatory_depends_on": "eval:doc.merge_invoices_based_on == 'Customer Group'",
|
||||
"options": "Customer Group"
|
||||
},
|
||||
{
|
||||
"fieldname": "posting_time",
|
||||
"fieldtype": "Time",
|
||||
"label": "Posting Time",
|
||||
"no_copy": 1,
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-09-14 11:17:19.001142",
|
||||
"modified": "2022-08-01 11:36:42.456429",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "POS Invoice Merge Log",
|
||||
@@ -173,5 +181,6 @@
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -6,11 +6,10 @@ import json
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.core.page.background_jobs.background_jobs import get_info
|
||||
from frappe.model.document import Document
|
||||
from frappe.model.mapper import map_child_doc, map_doc
|
||||
from frappe.utils import cint, flt, getdate, nowdate
|
||||
from frappe.utils.background_jobs import enqueue
|
||||
from frappe.utils import cint, flt, get_time, getdate, nowdate, nowtime
|
||||
from frappe.utils.background_jobs import enqueue, is_job_queued
|
||||
from frappe.utils.scheduler import is_scheduler_inactive
|
||||
|
||||
|
||||
@@ -99,6 +98,7 @@ class POSInvoiceMergeLog(Document):
|
||||
sales_invoice.is_consolidated = 1
|
||||
sales_invoice.set_posting_time = 1
|
||||
sales_invoice.posting_date = getdate(self.posting_date)
|
||||
sales_invoice.posting_time = get_time(self.posting_time)
|
||||
sales_invoice.save()
|
||||
sales_invoice.submit()
|
||||
|
||||
@@ -115,6 +115,7 @@ class POSInvoiceMergeLog(Document):
|
||||
credit_note.is_consolidated = 1
|
||||
credit_note.set_posting_time = 1
|
||||
credit_note.posting_date = getdate(self.posting_date)
|
||||
credit_note.posting_time = get_time(self.posting_time)
|
||||
# TODO: return could be against multiple sales invoice which could also have been consolidated?
|
||||
# credit_note.return_against = self.consolidated_invoice
|
||||
credit_note.save()
|
||||
@@ -402,6 +403,9 @@ def create_merge_logs(invoice_by_customer, closing_entry=None):
|
||||
merge_log.posting_date = (
|
||||
getdate(closing_entry.get("posting_date")) if closing_entry else nowdate()
|
||||
)
|
||||
merge_log.posting_time = (
|
||||
get_time(closing_entry.get("posting_time")) if closing_entry else nowtime()
|
||||
)
|
||||
merge_log.customer = customer
|
||||
merge_log.pos_closing_entry = closing_entry.get("name") if closing_entry else None
|
||||
|
||||
@@ -462,7 +466,7 @@ def enqueue_job(job, **kwargs):
|
||||
closing_entry = kwargs.get("closing_entry") or {}
|
||||
|
||||
job_name = closing_entry.get("name")
|
||||
if not job_already_enqueued(job_name):
|
||||
if not is_job_queued(job_name):
|
||||
enqueue(
|
||||
job,
|
||||
**kwargs,
|
||||
@@ -486,12 +490,6 @@ def check_scheduler_status():
|
||||
frappe.throw(_("Scheduler is inactive. Cannot enqueue job."), title=_("Scheduler Inactive"))
|
||||
|
||||
|
||||
def job_already_enqueued(job_name):
|
||||
enqueued_jobs = [d.get("job_name") for d in get_info()]
|
||||
if job_name in enqueued_jobs:
|
||||
return True
|
||||
|
||||
|
||||
def safe_load_json(message):
|
||||
try:
|
||||
json_message = json.loads(message).get("message")
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
"currency",
|
||||
"write_off_account",
|
||||
"write_off_cost_center",
|
||||
"write_off_limit",
|
||||
"account_for_change_amount",
|
||||
"disable_rounded_total",
|
||||
"column_break_23",
|
||||
@@ -360,6 +361,14 @@
|
||||
"fieldtype": "Check",
|
||||
"label": "Validate Stock on Save"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"description": "Auto write off precision loss while consolidation",
|
||||
"fieldname": "write_off_limit",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Write Off Limit",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "If enabled, the consolidated invoices will have rounded total disabled",
|
||||
@@ -393,7 +402,7 @@
|
||||
"link_fieldname": "pos_profile"
|
||||
}
|
||||
],
|
||||
"modified": "2022-07-21 11:16:46.911173",
|
||||
"modified": "2022-08-10 12:57:06.241439",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "POS Profile",
|
||||
|
||||
@@ -52,7 +52,10 @@
|
||||
"free_item_rate",
|
||||
"column_break_42",
|
||||
"free_item_uom",
|
||||
"round_free_qty",
|
||||
"is_recursive",
|
||||
"recurse_for",
|
||||
"apply_recursion_over",
|
||||
"section_break_23",
|
||||
"valid_from",
|
||||
"valid_upto",
|
||||
@@ -176,7 +179,7 @@
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"depends_on": "eval:doc.apply_on != 'Transaction'",
|
||||
"depends_on": "eval:doc.apply_on != 'Transaction' && !doc.mixed_conditions",
|
||||
"fieldname": "section_break_18",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Discount on Other Item"
|
||||
@@ -297,12 +300,12 @@
|
||||
{
|
||||
"fieldname": "min_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Min Qty"
|
||||
"label": "Min Qty (As Per Stock UOM)"
|
||||
},
|
||||
{
|
||||
"fieldname": "max_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Max Qty"
|
||||
"label": "Max Qty (As Per Stock UOM)"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_21",
|
||||
@@ -481,7 +484,7 @@
|
||||
"description": "System will notify to increase or decrease quantity or amount ",
|
||||
"fieldname": "threshold_percentage",
|
||||
"fieldtype": "Percent",
|
||||
"label": "Threshold for Suggestion"
|
||||
"label": "Threshold for Suggestion (In Percentage)"
|
||||
},
|
||||
{
|
||||
"description": "Higher the number, higher the priority",
|
||||
@@ -578,15 +581,38 @@
|
||||
"fieldtype": "Select",
|
||||
"label": "Naming Series",
|
||||
"options": "PRLE-.####"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "round_free_qty",
|
||||
"fieldtype": "Check",
|
||||
"label": "Round Free Qty"
|
||||
},
|
||||
{
|
||||
"depends_on": "is_recursive",
|
||||
"description": "Give free item for every N quantity",
|
||||
"fieldname": "recurse_for",
|
||||
"fieldtype": "Float",
|
||||
"label": "Recurse Every (As Per Transaction UOM)",
|
||||
"mandatory_depends_on": "is_recursive"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "is_recursive",
|
||||
"description": "Qty for which recursion isn't applicable.",
|
||||
"fieldname": "apply_recursion_over",
|
||||
"fieldtype": "Float",
|
||||
"label": "Apply Recursion Over (As Per Transaction UOM)"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-gift",
|
||||
"idx": 1,
|
||||
"links": [],
|
||||
"modified": "2021-08-06 15:10:04.219321",
|
||||
"modified": "2022-10-13 19:05:35.056304",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Pricing Rule",
|
||||
"naming_rule": "By \"Naming Series\" field",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
@@ -642,5 +668,6 @@
|
||||
"show_name_in_global_search": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"title_field": "title"
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ class PricingRule(Document):
|
||||
self.validate_applicable_for_selling_or_buying()
|
||||
self.validate_min_max_amt()
|
||||
self.validate_min_max_qty()
|
||||
self.validate_recursion()
|
||||
self.cleanup_fields_value()
|
||||
self.validate_rate_or_discount()
|
||||
self.validate_max_discount()
|
||||
@@ -109,6 +110,18 @@ class PricingRule(Document):
|
||||
if self.min_amt and self.max_amt and flt(self.min_amt) > flt(self.max_amt):
|
||||
throw(_("Min Amt can not be greater than Max Amt"))
|
||||
|
||||
def validate_recursion(self):
|
||||
if self.price_or_product_discount != "Product":
|
||||
return
|
||||
if self.free_item or self.same_item:
|
||||
if flt(self.recurse_for) <= 0:
|
||||
self.recurse_for = 1
|
||||
if self.is_recursive:
|
||||
if flt(self.apply_recursion_over) > flt(self.min_qty):
|
||||
throw(_("Min Qty should be greater than Recurse Over Qty"))
|
||||
if flt(self.apply_recursion_over) < 0:
|
||||
throw(_("Recurse Over Qty cannot be less than 0"))
|
||||
|
||||
def cleanup_fields_value(self):
|
||||
for logic_field in ["apply_on", "applicable_for", "rate_or_discount"]:
|
||||
fieldname = frappe.scrub(self.get(logic_field) or "")
|
||||
@@ -268,6 +281,18 @@ def get_serial_no_for_item(args):
|
||||
return item_details
|
||||
|
||||
|
||||
def update_pricing_rule_uom(pricing_rule, args):
|
||||
child_doc = {"Item Code": "items", "Item Group": "item_groups", "Brand": "brands"}.get(
|
||||
pricing_rule.apply_on
|
||||
)
|
||||
|
||||
apply_on_field = frappe.scrub(pricing_rule.apply_on)
|
||||
|
||||
for row in pricing_rule.get(child_doc):
|
||||
if row.get(apply_on_field) == args.get(apply_on_field):
|
||||
pricing_rule.uom = row.uom
|
||||
|
||||
|
||||
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_applied_pricing_rules,
|
||||
@@ -324,7 +349,8 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa
|
||||
|
||||
if isinstance(pricing_rule, str):
|
||||
pricing_rule = frappe.get_cached_doc("Pricing Rule", pricing_rule)
|
||||
pricing_rule.apply_rule_on_other_items = get_pricing_rule_items(pricing_rule)
|
||||
update_pricing_rule_uom(pricing_rule, args)
|
||||
pricing_rule.apply_rule_on_other_items = get_pricing_rule_items(pricing_rule) or []
|
||||
|
||||
if pricing_rule.get("suggestion"):
|
||||
continue
|
||||
@@ -337,7 +363,6 @@ 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:
|
||||
item_details.update(
|
||||
{
|
||||
"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)
|
||||
@@ -347,6 +372,9 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa
|
||||
}
|
||||
)
|
||||
|
||||
if pricing_rule.apply_rule_on_other_items:
|
||||
item_details["apply_rule_on_other_items"] = json.dumps(pricing_rule.apply_rule_on_other_items)
|
||||
|
||||
if pricing_rule.coupon_code_based == 1 and args.coupon_code == None:
|
||||
return item_details
|
||||
|
||||
@@ -438,12 +466,15 @@ def apply_price_discount_rule(pricing_rule, item_details, args):
|
||||
if pricing_rule.currency == args.currency:
|
||||
pricing_rule_rate = pricing_rule.rate
|
||||
|
||||
# TODO https://github.com/frappe/erpnext/pull/23636 solve this in some other way.
|
||||
if pricing_rule_rate:
|
||||
is_blank_uom = pricing_rule.get("uom") != args.get("uom")
|
||||
# Override already set price list rate (from item price)
|
||||
# if pricing_rule_rate > 0
|
||||
item_details.update(
|
||||
{
|
||||
"price_list_rate": pricing_rule_rate * args.get("conversion_factor", 1),
|
||||
"price_list_rate": pricing_rule_rate
|
||||
* (args.get("conversion_factor", 1) if is_blank_uom else 1),
|
||||
}
|
||||
)
|
||||
item_details.update({"discount_percentage": 0.0})
|
||||
@@ -492,7 +523,7 @@ def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None, ra
|
||||
)
|
||||
|
||||
if pricing_rule.get("mixed_conditions") or pricing_rule.get("apply_rule_on_other"):
|
||||
items = get_pricing_rule_items(pricing_rule)
|
||||
items = get_pricing_rule_items(pricing_rule, other_items=True)
|
||||
item_details.apply_on = (
|
||||
frappe.scrub(pricing_rule.apply_rule_on_other)
|
||||
if pricing_rule.apply_rule_on_other
|
||||
|
||||
@@ -595,6 +595,247 @@ class TestPricingRule(unittest.TestCase):
|
||||
frappe.get_doc("Item Price", {"item_code": "Water Flask"}).delete()
|
||||
item.delete()
|
||||
|
||||
def test_item_price_with_blank_uom_pricing_rule(self):
|
||||
properties = {
|
||||
"item_code": "Item Blank UOM",
|
||||
"stock_uom": "Nos",
|
||||
"sales_uom": "Box",
|
||||
"uoms": [dict(uom="Box", conversion_factor=10)],
|
||||
}
|
||||
item = make_item(properties=properties)
|
||||
|
||||
make_item_price("Item Blank UOM", "_Test Price List", 100)
|
||||
|
||||
pricing_rule_record = {
|
||||
"doctype": "Pricing Rule",
|
||||
"title": "_Test Item Blank UOM Rule",
|
||||
"apply_on": "Item Code",
|
||||
"items": [
|
||||
{
|
||||
"item_code": "Item Blank UOM",
|
||||
}
|
||||
],
|
||||
"selling": 1,
|
||||
"currency": "INR",
|
||||
"rate_or_discount": "Rate",
|
||||
"rate": 101,
|
||||
"company": "_Test Company",
|
||||
}
|
||||
rule = frappe.get_doc(pricing_rule_record)
|
||||
rule.insert()
|
||||
|
||||
si = create_sales_invoice(
|
||||
do_not_save=True, item_code="Item Blank UOM", uom="Box", conversion_factor=10
|
||||
)
|
||||
si.selling_price_list = "_Test Price List"
|
||||
si.save()
|
||||
|
||||
# If UOM is blank consider it as stock UOM and apply pricing_rule on all UOM.
|
||||
# rate is 101, Selling UOM is Box that have conversion_factor of 10 so 101 * 10 = 1010
|
||||
self.assertEqual(si.items[0].price_list_rate, 1010)
|
||||
self.assertEqual(si.items[0].rate, 1010)
|
||||
|
||||
si.delete()
|
||||
|
||||
si = create_sales_invoice(do_not_save=True, item_code="Item Blank UOM", uom="Nos")
|
||||
si.selling_price_list = "_Test Price List"
|
||||
si.save()
|
||||
|
||||
# UOM is blank so consider it as stock UOM and apply pricing_rule on all UOM.
|
||||
# rate is 101, Selling UOM is Nos that have conversion_factor of 1 so 101 * 1 = 101
|
||||
self.assertEqual(si.items[0].price_list_rate, 101)
|
||||
self.assertEqual(si.items[0].rate, 101)
|
||||
|
||||
si.delete()
|
||||
rule.delete()
|
||||
frappe.get_doc("Item Price", {"item_code": "Item Blank UOM"}).delete()
|
||||
|
||||
item.delete()
|
||||
|
||||
def test_item_price_with_selling_uom_pricing_rule(self):
|
||||
properties = {
|
||||
"item_code": "Item UOM other than Stock",
|
||||
"stock_uom": "Nos",
|
||||
"sales_uom": "Box",
|
||||
"uoms": [dict(uom="Box", conversion_factor=10)],
|
||||
}
|
||||
item = make_item(properties=properties)
|
||||
|
||||
make_item_price("Item UOM other than Stock", "_Test Price List", 100)
|
||||
|
||||
pricing_rule_record = {
|
||||
"doctype": "Pricing Rule",
|
||||
"title": "_Test Item UOM other than Stock Rule",
|
||||
"apply_on": "Item Code",
|
||||
"items": [
|
||||
{
|
||||
"item_code": "Item UOM other than Stock",
|
||||
"uom": "Box",
|
||||
}
|
||||
],
|
||||
"selling": 1,
|
||||
"currency": "INR",
|
||||
"rate_or_discount": "Rate",
|
||||
"rate": 101,
|
||||
"company": "_Test Company",
|
||||
}
|
||||
rule = frappe.get_doc(pricing_rule_record)
|
||||
rule.insert()
|
||||
|
||||
si = create_sales_invoice(
|
||||
do_not_save=True, item_code="Item UOM other than Stock", uom="Box", conversion_factor=10
|
||||
)
|
||||
si.selling_price_list = "_Test Price List"
|
||||
si.save()
|
||||
|
||||
# UOM is Box so apply pricing_rule only on Box UOM.
|
||||
# Selling UOM is Box and as both UOM are same no need to multiply by conversion_factor.
|
||||
self.assertEqual(si.items[0].price_list_rate, 101)
|
||||
self.assertEqual(si.items[0].rate, 101)
|
||||
|
||||
si.delete()
|
||||
|
||||
si = create_sales_invoice(do_not_save=True, item_code="Item UOM other than Stock", uom="Nos")
|
||||
si.selling_price_list = "_Test Price List"
|
||||
si.save()
|
||||
|
||||
# UOM is Box so pricing_rule won't apply as selling_uom is Nos.
|
||||
# As Pricing Rule is not applied price of 100 will be fetched from Item Price List.
|
||||
self.assertEqual(si.items[0].price_list_rate, 100)
|
||||
self.assertEqual(si.items[0].rate, 100)
|
||||
|
||||
si.delete()
|
||||
rule.delete()
|
||||
frappe.get_doc("Item Price", {"item_code": "Item UOM other than Stock"}).delete()
|
||||
|
||||
item.delete()
|
||||
|
||||
def test_item_group_price_with_blank_uom_pricing_rule(self):
|
||||
group = frappe.get_doc(doctype="Item Group", item_group_name="_Test Pricing Rule Item Group")
|
||||
group.save()
|
||||
properties = {
|
||||
"item_code": "Item with Group Blank UOM",
|
||||
"item_group": "_Test Pricing Rule Item Group",
|
||||
"stock_uom": "Nos",
|
||||
"sales_uom": "Box",
|
||||
"uoms": [dict(uom="Box", conversion_factor=10)],
|
||||
}
|
||||
item = make_item(properties=properties)
|
||||
|
||||
make_item_price("Item with Group Blank UOM", "_Test Price List", 100)
|
||||
|
||||
pricing_rule_record = {
|
||||
"doctype": "Pricing Rule",
|
||||
"title": "_Test Item with Group Blank UOM Rule",
|
||||
"apply_on": "Item Group",
|
||||
"item_groups": [
|
||||
{
|
||||
"item_group": "_Test Pricing Rule Item Group",
|
||||
}
|
||||
],
|
||||
"selling": 1,
|
||||
"currency": "INR",
|
||||
"rate_or_discount": "Rate",
|
||||
"rate": 101,
|
||||
"company": "_Test Company",
|
||||
}
|
||||
rule = frappe.get_doc(pricing_rule_record)
|
||||
rule.insert()
|
||||
|
||||
si = create_sales_invoice(
|
||||
do_not_save=True, item_code="Item with Group Blank UOM", uom="Box", conversion_factor=10
|
||||
)
|
||||
si.selling_price_list = "_Test Price List"
|
||||
si.save()
|
||||
|
||||
# If UOM is blank consider it as stock UOM and apply pricing_rule on all UOM.
|
||||
# rate is 101, Selling UOM is Box that have conversion_factor of 10 so 101 * 10 = 1010
|
||||
self.assertEqual(si.items[0].price_list_rate, 1010)
|
||||
self.assertEqual(si.items[0].rate, 1010)
|
||||
|
||||
si.delete()
|
||||
|
||||
si = create_sales_invoice(do_not_save=True, item_code="Item with Group Blank UOM", uom="Nos")
|
||||
si.selling_price_list = "_Test Price List"
|
||||
si.save()
|
||||
|
||||
# UOM is blank so consider it as stock UOM and apply pricing_rule on all UOM.
|
||||
# rate is 101, Selling UOM is Nos that have conversion_factor of 1 so 101 * 1 = 101
|
||||
self.assertEqual(si.items[0].price_list_rate, 101)
|
||||
self.assertEqual(si.items[0].rate, 101)
|
||||
|
||||
si.delete()
|
||||
rule.delete()
|
||||
frappe.get_doc("Item Price", {"item_code": "Item with Group Blank UOM"}).delete()
|
||||
item.delete()
|
||||
group.delete()
|
||||
|
||||
def test_item_group_price_with_selling_uom_pricing_rule(self):
|
||||
group = frappe.get_doc(doctype="Item Group", item_group_name="_Test Pricing Rule Item Group UOM")
|
||||
group.save()
|
||||
properties = {
|
||||
"item_code": "Item with Group UOM other than Stock",
|
||||
"item_group": "_Test Pricing Rule Item Group UOM",
|
||||
"stock_uom": "Nos",
|
||||
"sales_uom": "Box",
|
||||
"uoms": [dict(uom="Box", conversion_factor=10)],
|
||||
}
|
||||
item = make_item(properties=properties)
|
||||
|
||||
make_item_price("Item with Group UOM other than Stock", "_Test Price List", 100)
|
||||
|
||||
pricing_rule_record = {
|
||||
"doctype": "Pricing Rule",
|
||||
"title": "_Test Item with Group UOM other than Stock Rule",
|
||||
"apply_on": "Item Group",
|
||||
"item_groups": [
|
||||
{
|
||||
"item_group": "_Test Pricing Rule Item Group UOM",
|
||||
"uom": "Box",
|
||||
}
|
||||
],
|
||||
"selling": 1,
|
||||
"currency": "INR",
|
||||
"rate_or_discount": "Rate",
|
||||
"rate": 101,
|
||||
"company": "_Test Company",
|
||||
}
|
||||
rule = frappe.get_doc(pricing_rule_record)
|
||||
rule.insert()
|
||||
|
||||
si = create_sales_invoice(
|
||||
do_not_save=True,
|
||||
item_code="Item with Group UOM other than Stock",
|
||||
uom="Box",
|
||||
conversion_factor=10,
|
||||
)
|
||||
si.selling_price_list = "_Test Price List"
|
||||
si.save()
|
||||
|
||||
# UOM is Box so apply pricing_rule only on Box UOM.
|
||||
# Selling UOM is Box and as both UOM are same no need to multiply by conversion_factor.
|
||||
self.assertEqual(si.items[0].price_list_rate, 101)
|
||||
self.assertEqual(si.items[0].rate, 101)
|
||||
|
||||
si.delete()
|
||||
|
||||
si = create_sales_invoice(
|
||||
do_not_save=True, item_code="Item with Group UOM other than Stock", uom="Nos"
|
||||
)
|
||||
si.selling_price_list = "_Test Price List"
|
||||
si.save()
|
||||
|
||||
# UOM is Box so pricing_rule won't apply as selling_uom is Nos.
|
||||
# As Pricing Rule is not applied price of 100 will be fetched from Item Price List.
|
||||
self.assertEqual(si.items[0].price_list_rate, 100)
|
||||
self.assertEqual(si.items[0].rate, 100)
|
||||
|
||||
si.delete()
|
||||
rule.delete()
|
||||
frappe.get_doc("Item Price", {"item_code": "Item with Group UOM other than Stock"}).delete()
|
||||
item.delete()
|
||||
group.delete()
|
||||
|
||||
def test_pricing_rule_for_different_currency(self):
|
||||
make_item("Test Sanitizer Item")
|
||||
|
||||
@@ -766,6 +1007,107 @@ class TestPricingRule(unittest.TestCase):
|
||||
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule with Min Qty - 1")
|
||||
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule with Min Qty - 2")
|
||||
|
||||
def test_pricing_rule_for_other_items_cond_with_amount(self):
|
||||
item = make_item("Water Flask New")
|
||||
other_item = make_item("Other Water Flask New")
|
||||
make_item_price(item.name, "_Test Price List", 100)
|
||||
make_item_price(other_item.name, "_Test Price List", 100)
|
||||
|
||||
pricing_rule_record = {
|
||||
"doctype": "Pricing Rule",
|
||||
"title": "_Test Water Flask Rule",
|
||||
"apply_on": "Item Code",
|
||||
"apply_rule_on_other": "Item Code",
|
||||
"price_or_product_discount": "Price",
|
||||
"rate_or_discount": "Discount Percentage",
|
||||
"other_item_code": other_item.name,
|
||||
"items": [
|
||||
{
|
||||
"item_code": item.name,
|
||||
}
|
||||
],
|
||||
"selling": 1,
|
||||
"currency": "INR",
|
||||
"min_amt": 200,
|
||||
"discount_percentage": 10,
|
||||
"company": "_Test Company",
|
||||
}
|
||||
rule = frappe.get_doc(pricing_rule_record)
|
||||
rule.insert()
|
||||
|
||||
si = create_sales_invoice(do_not_save=True, item_code=item.name)
|
||||
si.append(
|
||||
"items",
|
||||
{
|
||||
"item_code": other_item.name,
|
||||
"item_name": other_item.item_name,
|
||||
"description": other_item.description,
|
||||
"stock_uom": other_item.stock_uom,
|
||||
"uom": other_item.stock_uom,
|
||||
"cost_center": si.items[0].cost_center,
|
||||
"expense_account": si.items[0].expense_account,
|
||||
"warehouse": si.items[0].warehouse,
|
||||
"conversion_factor": 1,
|
||||
"qty": 1,
|
||||
},
|
||||
)
|
||||
si.selling_price_list = "_Test Price List"
|
||||
si.save()
|
||||
|
||||
self.assertEqual(si.items[0].discount_percentage, 0)
|
||||
self.assertEqual(si.items[1].discount_percentage, 0)
|
||||
|
||||
si.items[0].qty = 2
|
||||
si.save()
|
||||
|
||||
self.assertEqual(si.items[0].discount_percentage, 0)
|
||||
self.assertEqual(si.items[0].stock_qty, 2)
|
||||
self.assertEqual(si.items[0].amount, 200)
|
||||
self.assertEqual(si.items[0].price_list_rate, 100)
|
||||
self.assertEqual(si.items[1].discount_percentage, 10)
|
||||
|
||||
si.delete()
|
||||
rule.delete()
|
||||
|
||||
def test_pricing_rule_for_product_free_item_rounded_qty_and_recursion(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": 0,
|
||||
"min_qty": 3,
|
||||
"max_qty": 7,
|
||||
"price_or_product_discount": "Product",
|
||||
"same_item": 1,
|
||||
"free_qty": 1,
|
||||
"round_free_qty": 1,
|
||||
"is_recursive": 1,
|
||||
"recurse_for": 2,
|
||||
"company": "_Test Company",
|
||||
}
|
||||
frappe.get_doc(test_record.copy()).insert()
|
||||
|
||||
# With pricing rule
|
||||
so = make_sales_order(item_code="_Test Item", qty=5)
|
||||
so.load_from_db()
|
||||
self.assertEqual(so.items[1].is_free_item, 1)
|
||||
self.assertEqual(so.items[1].item_code, "_Test Item")
|
||||
self.assertEqual(so.items[1].qty, 2)
|
||||
|
||||
so = make_sales_order(item_code="_Test Item", qty=7)
|
||||
so.load_from_db()
|
||||
self.assertEqual(so.items[1].is_free_item, 1)
|
||||
self.assertEqual(so.items[1].item_code, "_Test Item")
|
||||
self.assertEqual(so.items[1].qty, 4)
|
||||
|
||||
|
||||
test_dependencies = ["Campaign"]
|
||||
|
||||
|
||||
@@ -111,6 +111,12 @@ def _get_pricing_rules(apply_on, args, values):
|
||||
)
|
||||
|
||||
if apply_on_field == "item_code":
|
||||
if args.get("uom", None):
|
||||
item_conditions += (
|
||||
" and ({child_doc}.uom='{item_uom}' or IFNULL({child_doc}.uom, '')='')".format(
|
||||
child_doc=child_doc, item_uom=args.get("uom")
|
||||
)
|
||||
)
|
||||
if "variant_of" not in args:
|
||||
args.variant_of = frappe.get_cached_value("Item", args.item_code, "variant_of")
|
||||
|
||||
@@ -121,6 +127,12 @@ def _get_pricing_rules(apply_on, args, values):
|
||||
values["variant_of"] = args.variant_of
|
||||
elif apply_on_field == "item_group":
|
||||
item_conditions = _get_tree_conditions(args, "Item Group", child_doc, False)
|
||||
if args.get("uom", None):
|
||||
item_conditions += (
|
||||
" and ({child_doc}.uom='{item_uom}' or IFNULL({child_doc}.uom, '')='')".format(
|
||||
child_doc=child_doc, item_uom=args.get("uom")
|
||||
)
|
||||
)
|
||||
|
||||
conditions += get_other_conditions(conditions, values, args)
|
||||
warehouse_conditions = _get_tree_conditions(args, "Warehouse", "`tabPricing Rule`")
|
||||
@@ -252,12 +264,6 @@ def filter_pricing_rules(args, pricing_rules, doc=None):
|
||||
stock_qty = flt(args.get("stock_qty"))
|
||||
amount = flt(args.get("price_list_rate")) * flt(args.get("qty"))
|
||||
|
||||
if pricing_rules[0].apply_rule_on_other:
|
||||
field = frappe.scrub(pricing_rules[0].apply_rule_on_other)
|
||||
|
||||
if field and pricing_rules[0].get("other_" + field) != args.get(field):
|
||||
return
|
||||
|
||||
pr_doc = frappe.get_cached_doc("Pricing Rule", pricing_rules[0].name)
|
||||
|
||||
if pricing_rules[0].mixed_conditions and doc:
|
||||
@@ -274,7 +280,7 @@ def filter_pricing_rules(args, pricing_rules, doc=None):
|
||||
amount += data[1]
|
||||
|
||||
if pricing_rules[0].apply_rule_on_other and not pricing_rules[0].mixed_conditions and doc:
|
||||
pricing_rules = get_qty_and_rate_for_other_item(doc, pr_doc, pricing_rules) or []
|
||||
pricing_rules = get_qty_and_rate_for_other_item(doc, pr_doc, pricing_rules, args) or []
|
||||
else:
|
||||
pricing_rules = filter_pricing_rules_for_qty_amount(stock_qty, amount, pricing_rules, args)
|
||||
|
||||
@@ -352,16 +358,14 @@ def validate_quantity_and_amount_for_suggestion(args, qty, amount, item_code, tr
|
||||
if fieldname:
|
||||
msg = _(
|
||||
"If you {0} {1} quantities of the item {2}, the scheme {3} will be applied on the item."
|
||||
).format(
|
||||
type_of_transaction, args.get(fieldname), bold(item_code), bold(args.rule_description)
|
||||
)
|
||||
).format(type_of_transaction, args.get(fieldname), bold(item_code), bold(args.title))
|
||||
|
||||
if fieldname in ["min_amt", "max_amt"]:
|
||||
msg = _("If you {0} {1} worth item {2}, the scheme {3} will be applied on the item.").format(
|
||||
type_of_transaction,
|
||||
fmt_money(args.get(fieldname), currency=args.get("currency")),
|
||||
bold(item_code),
|
||||
bold(args.rule_description),
|
||||
bold(args.title),
|
||||
)
|
||||
|
||||
frappe.msgprint(msg)
|
||||
@@ -454,17 +458,29 @@ def get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args):
|
||||
return sum_qty, sum_amt, items
|
||||
|
||||
|
||||
def get_qty_and_rate_for_other_item(doc, pr_doc, pricing_rules):
|
||||
items = get_pricing_rule_items(pr_doc)
|
||||
def get_qty_and_rate_for_other_item(doc, pr_doc, pricing_rules, row_item):
|
||||
other_items = get_pricing_rule_items(pr_doc, other_items=True)
|
||||
pricing_rule_apply_on = apply_on_table.get(pr_doc.get("apply_on"))
|
||||
apply_on = frappe.scrub(pr_doc.get("apply_on"))
|
||||
|
||||
items = []
|
||||
for d in pr_doc.get(pricing_rule_apply_on):
|
||||
if apply_on == "item_group":
|
||||
items.extend(get_child_item_groups(d.get(apply_on)))
|
||||
else:
|
||||
items.append(d.get(apply_on))
|
||||
|
||||
for row in doc.items:
|
||||
if row.get(frappe.scrub(pr_doc.apply_rule_on_other)) in items:
|
||||
pricing_rules = filter_pricing_rules_for_qty_amount(
|
||||
row.get("stock_qty"), row.get("amount"), pricing_rules, row
|
||||
)
|
||||
if row.get(apply_on) in items:
|
||||
if not row.get("qty"):
|
||||
continue
|
||||
|
||||
stock_qty = row.get("qty") * (row.get("conversion_factor") or 1.0)
|
||||
amount = stock_qty * (row.get("price_list_rate") or row.get("rate"))
|
||||
pricing_rules = filter_pricing_rules_for_qty_amount(stock_qty, amount, pricing_rules, row)
|
||||
|
||||
if pricing_rules and pricing_rules[0]:
|
||||
pricing_rules[0].apply_rule_on_other_items = items
|
||||
pricing_rules[0].apply_rule_on_other_items = other_items
|
||||
return pricing_rules
|
||||
|
||||
|
||||
@@ -617,9 +633,13 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
|
||||
|
||||
qty = pricing_rule.free_qty or 1
|
||||
if pricing_rule.is_recursive:
|
||||
transaction_qty = args.get("qty") if args else doc.total_qty
|
||||
transaction_qty = (
|
||||
args.get("qty") if args else doc.total_qty
|
||||
) - pricing_rule.apply_recursion_over
|
||||
if transaction_qty:
|
||||
qty = flt(transaction_qty) * qty
|
||||
qty = flt(transaction_qty) * qty / pricing_rule.recurse_for
|
||||
if pricing_rule.round_free_qty:
|
||||
qty = round(qty)
|
||||
|
||||
free_item_data_args = {
|
||||
"item_code": free_item,
|
||||
@@ -658,21 +678,21 @@ def apply_pricing_rule_for_free_items(doc, pricing_rule_args, set_missing_values
|
||||
doc.append("items", args)
|
||||
|
||||
|
||||
def get_pricing_rule_items(pr_doc):
|
||||
def get_pricing_rule_items(pr_doc, other_items=False) -> list:
|
||||
apply_on_data = []
|
||||
apply_on = frappe.scrub(pr_doc.get("apply_on"))
|
||||
|
||||
pricing_rule_apply_on = apply_on_table.get(pr_doc.get("apply_on"))
|
||||
|
||||
for d in pr_doc.get(pricing_rule_apply_on):
|
||||
if apply_on == "item_group":
|
||||
apply_on_data.extend(get_child_item_groups(d.get(apply_on)))
|
||||
else:
|
||||
apply_on_data.append(d.get(apply_on))
|
||||
|
||||
if pr_doc.apply_rule_on_other:
|
||||
if pr_doc.apply_rule_on_other and other_items:
|
||||
apply_on = frappe.scrub(pr_doc.apply_rule_on_other)
|
||||
apply_on_data.append(pr_doc.get("other_" + apply_on))
|
||||
else:
|
||||
for d in pr_doc.get(pricing_rule_apply_on):
|
||||
if apply_on == "item_group":
|
||||
apply_on_data.extend(get_child_item_groups(d.get(apply_on)))
|
||||
else:
|
||||
apply_on_data.append(d.get(apply_on))
|
||||
|
||||
return list(set(apply_on_data))
|
||||
|
||||
|
||||
@@ -34,4 +34,4 @@ class ProcessDeferredAccounting(Document):
|
||||
filters={"against_voucher_type": self.doctype, "against_voucher": self.name},
|
||||
)
|
||||
|
||||
make_gl_entries(gl_entries=gl_entries, cancel=1)
|
||||
make_gl_entries(gl_map=gl_entries, cancel=1)
|
||||
|
||||
@@ -57,3 +57,16 @@ class TestProcessDeferredAccounting(unittest.TestCase):
|
||||
]
|
||||
|
||||
check_gl_entries(self, si.name, expected_gle, "2019-01-10")
|
||||
|
||||
def test_pda_submission_and_cancellation(self):
|
||||
pda = frappe.get_doc(
|
||||
dict(
|
||||
doctype="Process Deferred Accounting",
|
||||
posting_date="2019-01-01",
|
||||
start_date="2019-01-01",
|
||||
end_date="2019-01-31",
|
||||
type="Income",
|
||||
)
|
||||
)
|
||||
pda.submit()
|
||||
pda.cancel()
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<table class="table table-bordered" style="font-size: 10px">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 12%">{{ _("Date") }}</th>
|
||||
|
||||
@@ -9,6 +9,7 @@ frappe.ui.form.on('Process Statement Of Accounts', {
|
||||
refresh: function(frm){
|
||||
if(!frm.doc.__islocal) {
|
||||
frm.add_custom_button(__('Send Emails'), function(){
|
||||
if (frm.is_dirty()) frappe.throw(__("Please save before proceeding."))
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_emails",
|
||||
args: {
|
||||
@@ -25,7 +26,8 @@ frappe.ui.form.on('Process Statement Of Accounts', {
|
||||
});
|
||||
});
|
||||
frm.add_custom_button(__('Download'), function(){
|
||||
var url = frappe.urllib.get_full_url(
|
||||
if (frm.is_dirty()) frappe.throw(__("Please save before proceeding."))
|
||||
let url = frappe.urllib.get_full_url(
|
||||
'/api/method/erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.download_statements?'
|
||||
+ 'document_name='+encodeURIComponent(frm.doc.name))
|
||||
$.ajax({
|
||||
|
||||
@@ -23,10 +23,12 @@
|
||||
"fetch_customers",
|
||||
"column_break_6",
|
||||
"primary_mandatory",
|
||||
"show_net_values_in_party_account",
|
||||
"column_break_17",
|
||||
"customers",
|
||||
"preferences",
|
||||
"orientation",
|
||||
"include_break",
|
||||
"include_ageing",
|
||||
"ageing_based_on",
|
||||
"section_break_14",
|
||||
@@ -284,10 +286,22 @@
|
||||
"fieldtype": "Link",
|
||||
"label": "Terms and Conditions",
|
||||
"options": "Terms and Conditions"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "include_break",
|
||||
"fieldtype": "Check",
|
||||
"label": "Page Break After Each SoA"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "show_net_values_in_party_account",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show Net Values in Party Account"
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2021-09-06 21:00:45.732505",
|
||||
"modified": "2022-11-10 17:44:17.165991",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Process Statement Of Accounts",
|
||||
@@ -321,5 +335,6 @@
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import copy
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.desk.reportview import get_match_cond
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import add_days, add_months, format_date, getdate, today
|
||||
from frappe.utils.jinja import validate_template
|
||||
@@ -94,6 +95,7 @@ def get_report_pdf(doc, consolidated=True):
|
||||
"show_opening_entries": 0,
|
||||
"include_default_book_entries": 0,
|
||||
"tax_id": tax_id if tax_id else None,
|
||||
"show_net_values_in_party_account": doc.show_net_values_in_party_account,
|
||||
}
|
||||
)
|
||||
col, res = get_soa(filters)
|
||||
@@ -128,7 +130,8 @@ def get_report_pdf(doc, consolidated=True):
|
||||
if not bool(statement_dict):
|
||||
return False
|
||||
elif consolidated:
|
||||
result = "".join(list(statement_dict.values()))
|
||||
delimiter = '<div style="page-break-before: always;"></div>' if doc.include_break else ""
|
||||
result = delimiter.join(list(statement_dict.values()))
|
||||
return get_pdf(result, {"orientation": doc.orientation})
|
||||
else:
|
||||
for customer, statement_html in statement_dict.items():
|
||||
@@ -240,8 +243,6 @@ def fetch_customers(customer_collection, collection_name, primary_mandatory):
|
||||
if int(primary_mandatory):
|
||||
if primary_email == "":
|
||||
continue
|
||||
elif (billing_email == "") and (primary_email == ""):
|
||||
continue
|
||||
|
||||
customer_list.append(
|
||||
{"name": customer.name, "primary_email": primary_email, "billing_email": billing_email}
|
||||
@@ -273,8 +274,12 @@ def get_customer_emails(customer_name, primary_mandatory, billing_and_primary=Tr
|
||||
link.link_doctype='Customer'
|
||||
and link.link_name=%s
|
||||
and contact.is_billing_contact=1
|
||||
{mcond}
|
||||
ORDER BY
|
||||
contact.creation desc""",
|
||||
contact.creation desc
|
||||
""".format(
|
||||
mcond=get_match_cond("Contact")
|
||||
),
|
||||
customer_name,
|
||||
)
|
||||
|
||||
@@ -313,6 +318,8 @@ def send_emails(document_name, from_scheduler=False):
|
||||
attachments = [{"fname": customer + ".pdf", "fcontent": report_pdf}]
|
||||
|
||||
recipients, cc = get_recipients_and_cc(customer, doc)
|
||||
if not recipients:
|
||||
continue
|
||||
context = get_context(customer, doc)
|
||||
subject = frappe.render_template(doc.subject, context)
|
||||
message = frappe.render_template(doc.body, context)
|
||||
|
||||
@@ -34,8 +34,8 @@ pricing_rule_fields = [
|
||||
other_fields = [
|
||||
"min_qty",
|
||||
"max_qty",
|
||||
"min_amt",
|
||||
"max_amt",
|
||||
"min_amount",
|
||||
"max_amount",
|
||||
"priority",
|
||||
"warehouse",
|
||||
"threshold_percentage",
|
||||
@@ -246,7 +246,11 @@ def prepare_pricing_rule(
|
||||
def set_args(args, pr, doc, child_doc, discount_fields, child_doc_fields):
|
||||
pr.update(args)
|
||||
for field in other_fields + discount_fields:
|
||||
pr.set(field, child_doc_fields.get(field))
|
||||
target_field = field
|
||||
if target_field in ["min_amount", "max_amount"]:
|
||||
target_field = "min_amt" if field == "min_amount" else "max_amt"
|
||||
|
||||
pr.set(target_field, child_doc_fields.get(field))
|
||||
|
||||
pr.promotional_scheme_id = child_doc_fields.name
|
||||
pr.promotional_scheme = doc.name
|
||||
|
||||
@@ -90,6 +90,23 @@ class TestPromotionalScheme(unittest.TestCase):
|
||||
price_rules = frappe.get_all("Pricing Rule", filters={"promotional_scheme": ps.name})
|
||||
self.assertEqual(price_rules, [])
|
||||
|
||||
def test_min_max_amount_configuration(self):
|
||||
ps = make_promotional_scheme()
|
||||
ps.price_discount_slabs[0].min_amount = 10
|
||||
ps.price_discount_slabs[0].max_amount = 1000
|
||||
ps.save()
|
||||
|
||||
price_rules_data = frappe.db.get_value(
|
||||
"Pricing Rule", {"promotional_scheme": ps.name}, ["min_amt", "max_amt"], as_dict=1
|
||||
)
|
||||
|
||||
self.assertEqual(price_rules_data.min_amt, 10)
|
||||
self.assertEqual(price_rules_data.max_amt, 1000)
|
||||
|
||||
frappe.delete_doc("Promotional Scheme", ps.name)
|
||||
price_rules = frappe.get_all("Pricing Rule", filters={"promotional_scheme": ps.name})
|
||||
self.assertEqual(price_rules, [])
|
||||
|
||||
|
||||
def make_promotional_scheme(**args):
|
||||
args = frappe._dict(args)
|
||||
|
||||
@@ -31,7 +31,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
|
||||
super.onload();
|
||||
|
||||
// Ignore linked advances
|
||||
this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry'];
|
||||
this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry', 'Purchase Invoice'];
|
||||
|
||||
if(!this.frm.doc.__islocal) {
|
||||
// show credit_to in print format
|
||||
@@ -569,6 +569,10 @@ frappe.ui.form.on("Purchase Invoice", {
|
||||
erpnext.queries.setup_queries(frm, "Warehouse", function() {
|
||||
return erpnext.queries.warehouse(frm.doc);
|
||||
});
|
||||
|
||||
if (frm.is_new()) {
|
||||
frm.clear_table("tax_withheld_vouchers");
|
||||
}
|
||||
},
|
||||
|
||||
is_subcontracted: function(frm) {
|
||||
|
||||
@@ -12,39 +12,23 @@
|
||||
"supplier",
|
||||
"supplier_name",
|
||||
"tax_id",
|
||||
"due_date",
|
||||
"tax_withholding_category",
|
||||
"column_break1",
|
||||
"company",
|
||||
"column_break_6",
|
||||
"posting_date",
|
||||
"posting_time",
|
||||
"set_posting_time",
|
||||
"due_date",
|
||||
"column_break1",
|
||||
"is_paid",
|
||||
"is_return",
|
||||
"return_against",
|
||||
"apply_tds",
|
||||
"tax_withholding_category",
|
||||
"amended_from",
|
||||
"accounting_dimensions_section",
|
||||
"cost_center",
|
||||
"dimension_col_break",
|
||||
"project",
|
||||
"supplier_invoice_details",
|
||||
"bill_no",
|
||||
"column_break_15",
|
||||
"bill_date",
|
||||
"returns",
|
||||
"return_against",
|
||||
"section_addresses",
|
||||
"supplier_address",
|
||||
"address_display",
|
||||
"contact_person",
|
||||
"contact_display",
|
||||
"contact_mobile",
|
||||
"contact_email",
|
||||
"col_break_address",
|
||||
"shipping_address",
|
||||
"shipping_address_display",
|
||||
"billing_address",
|
||||
"billing_address_display",
|
||||
"currency_and_price_list",
|
||||
"currency",
|
||||
"conversion_rate",
|
||||
@@ -54,37 +38,35 @@
|
||||
"plc_conversion_rate",
|
||||
"ignore_pricing_rule",
|
||||
"sec_warehouse",
|
||||
"set_warehouse",
|
||||
"rejected_warehouse",
|
||||
"col_break_warehouse",
|
||||
"set_from_warehouse",
|
||||
"supplier_warehouse",
|
||||
"is_subcontracted",
|
||||
"items_section",
|
||||
"update_stock",
|
||||
"scan_barcode",
|
||||
"col_break_warehouse",
|
||||
"update_stock",
|
||||
"set_warehouse",
|
||||
"set_from_warehouse",
|
||||
"is_subcontracted",
|
||||
"rejected_warehouse",
|
||||
"supplier_warehouse",
|
||||
"items_section",
|
||||
"items",
|
||||
"pricing_rule_details",
|
||||
"pricing_rules",
|
||||
"raw_materials_supplied",
|
||||
"supplied_items",
|
||||
"section_break_26",
|
||||
"total_qty",
|
||||
"total_net_weight",
|
||||
"column_break_50",
|
||||
"base_total",
|
||||
"base_net_total",
|
||||
"column_break_28",
|
||||
"total_net_weight",
|
||||
"total",
|
||||
"net_total",
|
||||
"tax_withholding_net_total",
|
||||
"base_tax_withholding_net_total",
|
||||
"taxes_section",
|
||||
"taxes_and_charges",
|
||||
"column_break_58",
|
||||
"tax_category",
|
||||
"column_break_49",
|
||||
"shipping_rule",
|
||||
"section_break_51",
|
||||
"taxes_and_charges",
|
||||
"taxes",
|
||||
"sec_tax_breakup",
|
||||
"other_charges_calculation",
|
||||
"totals",
|
||||
"base_taxes_and_charges_added",
|
||||
"base_taxes_and_charges_deducted",
|
||||
@@ -93,13 +75,6 @@
|
||||
"taxes_and_charges_added",
|
||||
"taxes_and_charges_deducted",
|
||||
"total_taxes_and_charges",
|
||||
"section_break_44",
|
||||
"apply_discount_on",
|
||||
"base_discount_amount",
|
||||
"additional_discount_account",
|
||||
"column_break_46",
|
||||
"additional_discount_percentage",
|
||||
"discount_amount",
|
||||
"section_break_49",
|
||||
"base_grand_total",
|
||||
"base_rounding_adjustment",
|
||||
@@ -113,24 +88,57 @@
|
||||
"total_advance",
|
||||
"outstanding_amount",
|
||||
"disable_rounded_total",
|
||||
"section_break_44",
|
||||
"apply_discount_on",
|
||||
"base_discount_amount",
|
||||
"column_break_46",
|
||||
"additional_discount_percentage",
|
||||
"discount_amount",
|
||||
"tax_withheld_vouchers_section",
|
||||
"tax_withheld_vouchers",
|
||||
"sec_tax_breakup",
|
||||
"other_charges_calculation",
|
||||
"pricing_rule_details",
|
||||
"pricing_rules",
|
||||
"raw_materials_supplied",
|
||||
"supplied_items",
|
||||
"payments_tab",
|
||||
"payments_section",
|
||||
"mode_of_payment",
|
||||
"cash_bank_account",
|
||||
"base_paid_amount",
|
||||
"clearance_date",
|
||||
"col_br_payments",
|
||||
"cash_bank_account",
|
||||
"paid_amount",
|
||||
"base_paid_amount",
|
||||
"advances_section",
|
||||
"allocate_advances_automatically",
|
||||
"get_advances",
|
||||
"advances",
|
||||
"advance_tax",
|
||||
"write_off",
|
||||
"write_off_amount",
|
||||
"base_write_off_amount",
|
||||
"column_break_61",
|
||||
"write_off_account",
|
||||
"write_off_cost_center",
|
||||
"advances_section",
|
||||
"allocate_advances_automatically",
|
||||
"get_advances",
|
||||
"advances",
|
||||
"advance_tax",
|
||||
"address_and_contact_tab",
|
||||
"section_addresses",
|
||||
"supplier_address",
|
||||
"address_display",
|
||||
"col_break_address",
|
||||
"contact_person",
|
||||
"contact_display",
|
||||
"contact_mobile",
|
||||
"contact_email",
|
||||
"company_shipping_address_section",
|
||||
"shipping_address",
|
||||
"column_break_126",
|
||||
"shipping_address_display",
|
||||
"company_billing_address_section",
|
||||
"billing_address",
|
||||
"column_break_130",
|
||||
"billing_address_display",
|
||||
"terms_tab",
|
||||
"payment_schedule_section",
|
||||
"payment_terms_template",
|
||||
"ignore_default_payment_terms_template",
|
||||
@@ -138,23 +146,15 @@
|
||||
"terms_section_break",
|
||||
"tc_name",
|
||||
"terms",
|
||||
"printing_settings",
|
||||
"letter_head",
|
||||
"select_print_heading",
|
||||
"column_break_112",
|
||||
"group_same_items",
|
||||
"language",
|
||||
"sb_14",
|
||||
"on_hold",
|
||||
"release_date",
|
||||
"cb_17",
|
||||
"hold_comment",
|
||||
"more_info",
|
||||
"more_info_tab",
|
||||
"status_section",
|
||||
"status",
|
||||
"inter_company_invoice_reference",
|
||||
"represents_company",
|
||||
"column_break_147",
|
||||
"is_internal_supplier",
|
||||
"column_break_177",
|
||||
"per_received",
|
||||
"supplier_invoice_details",
|
||||
"bill_no",
|
||||
"column_break_15",
|
||||
"bill_date",
|
||||
"accounting_details_section",
|
||||
"credit_to",
|
||||
"party_account_currency",
|
||||
@@ -162,15 +162,32 @@
|
||||
"against_expense_account",
|
||||
"column_break_63",
|
||||
"unrealized_profit_loss_account",
|
||||
"remarks",
|
||||
"subscription_section",
|
||||
"from_date",
|
||||
"to_date",
|
||||
"column_break_114",
|
||||
"auto_repeat",
|
||||
"update_auto_repeat_reference",
|
||||
"per_received",
|
||||
"is_old_subcontracting_flow"
|
||||
"column_break_114",
|
||||
"from_date",
|
||||
"to_date",
|
||||
"printing_settings",
|
||||
"letter_head",
|
||||
"group_same_items",
|
||||
"column_break_112",
|
||||
"select_print_heading",
|
||||
"language",
|
||||
"sb_14",
|
||||
"on_hold",
|
||||
"release_date",
|
||||
"cb_17",
|
||||
"hold_comment",
|
||||
"additional_info_section",
|
||||
"is_internal_supplier",
|
||||
"represents_company",
|
||||
"column_break_147",
|
||||
"inter_company_invoice_reference",
|
||||
"is_old_subcontracting_flow",
|
||||
"remarks",
|
||||
"connections_tab",
|
||||
"column_break_38"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -353,7 +370,7 @@
|
||||
"collapsible_depends_on": "bill_no",
|
||||
"fieldname": "supplier_invoice_details",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Supplier Invoice Details"
|
||||
"label": "Supplier Invoice"
|
||||
},
|
||||
{
|
||||
"fieldname": "bill_no",
|
||||
@@ -376,12 +393,6 @@
|
||||
"oldfieldtype": "Date",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "return_against",
|
||||
"fieldname": "returns",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Returns"
|
||||
},
|
||||
{
|
||||
"depends_on": "return_against",
|
||||
"fieldname": "return_against",
|
||||
@@ -393,10 +404,9 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "section_addresses",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Address and Contact"
|
||||
"label": "Supplier Address"
|
||||
},
|
||||
{
|
||||
"fieldname": "supplier_address",
|
||||
@@ -512,17 +522,17 @@
|
||||
"fieldname": "ignore_pricing_rule",
|
||||
"fieldtype": "Check",
|
||||
"label": "Ignore Pricing Rule",
|
||||
"no_copy": 1,
|
||||
"permlevel": 1,
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "sec_warehouse",
|
||||
"fieldtype": "Section Break"
|
||||
"fieldtype": "Section Break",
|
||||
"hide_border": 1,
|
||||
"label": "Items"
|
||||
},
|
||||
{
|
||||
"depends_on": "update_stock",
|
||||
"description": "Sets 'Accepted Warehouse' in each row of the items table.",
|
||||
"fieldname": "set_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Set Accepted Warehouse",
|
||||
@@ -531,7 +541,6 @@
|
||||
},
|
||||
{
|
||||
"depends_on": "update_stock",
|
||||
"description": "Warehouse where you are maintaining stock of rejected items",
|
||||
"fieldname": "rejected_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Rejected Warehouse",
|
||||
@@ -554,6 +563,7 @@
|
||||
{
|
||||
"fieldname": "items_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_border": 1,
|
||||
"oldfieldtype": "Section Break",
|
||||
"options": "fa fa-shopping-cart"
|
||||
},
|
||||
@@ -581,6 +591,7 @@
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "pricing_rule_details",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Pricing Rules"
|
||||
@@ -593,6 +604,7 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "supplied_items",
|
||||
"fieldname": "raw_materials_supplied",
|
||||
"fieldtype": "Section Break",
|
||||
@@ -656,6 +668,7 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "total_net_weight",
|
||||
"fieldname": "total_net_weight",
|
||||
"fieldtype": "Float",
|
||||
"label": "Total Net Weight",
|
||||
@@ -665,6 +678,8 @@
|
||||
{
|
||||
"fieldname": "taxes_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_border": 1,
|
||||
"label": "Taxes and Charges",
|
||||
"oldfieldtype": "Section Break",
|
||||
"options": "fa fa-money"
|
||||
},
|
||||
@@ -688,7 +703,8 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_51",
|
||||
"fieldtype": "Section Break"
|
||||
"fieldtype": "Section Break",
|
||||
"hide_border": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "taxes_and_charges",
|
||||
@@ -792,7 +808,6 @@
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "discount_amount",
|
||||
"fieldname": "section_break_44",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Additional Discount"
|
||||
@@ -832,7 +847,8 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_49",
|
||||
"fieldtype": "Section Break"
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Totals"
|
||||
},
|
||||
{
|
||||
"fieldname": "base_grand_total",
|
||||
@@ -1003,8 +1019,6 @@
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "write_off_amount",
|
||||
"depends_on": "grand_total",
|
||||
"fieldname": "write_off",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Write Off"
|
||||
@@ -1081,7 +1095,6 @@
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "eval:(!doc.is_return)",
|
||||
"fieldname": "payment_schedule_section",
|
||||
"fieldtype": "Section Break",
|
||||
@@ -1102,8 +1115,6 @@
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "terms",
|
||||
"fieldname": "terms_section_break",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Terms and Conditions",
|
||||
@@ -1119,13 +1130,13 @@
|
||||
{
|
||||
"fieldname": "terms",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Terms and Conditions1"
|
||||
"label": "Terms and Conditions"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "printing_settings",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Printing Settings"
|
||||
"label": "Print Settings"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
@@ -1166,15 +1177,6 @@
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "more_info",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "More Information",
|
||||
"oldfieldtype": "Section Break",
|
||||
"options": "fa fa-file-text",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fetch_from": "supplier.is_internal_supplier",
|
||||
@@ -1260,7 +1262,7 @@
|
||||
"collapsible": 1,
|
||||
"fieldname": "subscription_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Subscription Section",
|
||||
"label": "Subscription",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
@@ -1339,7 +1341,7 @@
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_internal_supplier",
|
||||
"description": "Unrealized Profit / Loss account for intra-company transfers",
|
||||
"description": "Unrealized Profit/Loss account for intra-company transfers",
|
||||
"fieldname": "unrealized_profit_loss_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Unrealized Profit / Loss Account",
|
||||
@@ -1356,7 +1358,6 @@
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.update_stock && doc.is_internal_supplier",
|
||||
"description": "Sets 'From Warehouse' in each row of the items table.",
|
||||
"fieldname": "set_from_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Set From Warehouse",
|
||||
@@ -1367,7 +1368,7 @@
|
||||
"width": "50px"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_subcontracted",
|
||||
"depends_on": "eval:doc.is_subcontracted",
|
||||
"fieldname": "supplier_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Supplier Warehouse",
|
||||
@@ -1386,12 +1387,6 @@
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "additional_discount_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Additional Discount Account",
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "ignore_default_payment_terms_template",
|
||||
@@ -1426,13 +1421,126 @@
|
||||
"hidden": 1,
|
||||
"label": "Is Old Subcontracting Flow",
|
||||
"read_only": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "tax_withholding_net_total",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 1,
|
||||
"label": "Tax Withholding Net Total",
|
||||
"no_copy": 1,
|
||||
"options": "currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "base_tax_withholding_net_total",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 1,
|
||||
"label": "Base Tax Withholding Net Total",
|
||||
"no_copy": 1,
|
||||
"options": "currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"collapsible_depends_on": "tax_withheld_vouchers",
|
||||
"fieldname": "tax_withheld_vouchers_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Tax Withheld Vouchers"
|
||||
},
|
||||
{
|
||||
"fieldname": "tax_withheld_vouchers",
|
||||
"fieldtype": "Table",
|
||||
"label": "Tax Withheld Vouchers",
|
||||
"no_copy": 1,
|
||||
"options": "Tax Withheld Vouchers",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "payments_tab",
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "Payments"
|
||||
},
|
||||
{
|
||||
"fieldname": "address_and_contact_tab",
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "Address & Contact"
|
||||
},
|
||||
{
|
||||
"fieldname": "terms_tab",
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "Terms"
|
||||
},
|
||||
{
|
||||
"fieldname": "more_info_tab",
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "More Info"
|
||||
},
|
||||
{
|
||||
"fieldname": "connections_tab",
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "Connections",
|
||||
"show_dashboard": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_6",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_38",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_50",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_58",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "company_shipping_address_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Company Shipping Address"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_126",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "company_billing_address_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Company Billing Address"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_130",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "status_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Status"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_177",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "additional_info_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Additional Info",
|
||||
"oldfieldtype": "Section Break",
|
||||
"options": "fa fa-file-text",
|
||||
"print_hide": 1
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-file-text",
|
||||
"idx": 204,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-06-15 15:40:58.527065",
|
||||
"modified": "2022-11-04 01:02:44.544878",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice",
|
||||
@@ -1492,6 +1600,7 @@
|
||||
"show_name_in_global_search": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"timeline_field": "supplier",
|
||||
"title_field": "title",
|
||||
"track_changes": 1
|
||||
|
||||
@@ -71,6 +71,9 @@ class PurchaseInvoice(BuyingController):
|
||||
supplier_tds = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category")
|
||||
self.set_onload("supplier_tds", supplier_tds)
|
||||
|
||||
if self.is_new():
|
||||
self.set("tax_withheld_vouchers", [])
|
||||
|
||||
def before_save(self):
|
||||
if not self.on_hold:
|
||||
self.release_date = ""
|
||||
@@ -575,7 +578,6 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
self.make_supplier_gl_entry(gl_entries)
|
||||
self.make_item_gl_entries(gl_entries)
|
||||
self.make_discount_gl_entries(gl_entries)
|
||||
|
||||
if self.check_asset_cwip_enabled():
|
||||
self.get_asset_gl_entry(gl_entries)
|
||||
@@ -670,9 +672,6 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
exchange_rate_map, net_rate_map = get_purchase_document_details(self)
|
||||
|
||||
enable_discount_accounting = cint(
|
||||
frappe.db.get_single_value("Buying Settings", "enable_discount_accounting")
|
||||
)
|
||||
provisional_accounting_for_non_stock_items = cint(
|
||||
frappe.db.get_value(
|
||||
"Company", self.company, "enable_provisional_accounting_for_non_stock_items"
|
||||
@@ -709,6 +708,10 @@ class PurchaseInvoice(BuyingController):
|
||||
)
|
||||
)
|
||||
|
||||
credit_amount = item.base_net_amount
|
||||
if self.is_internal_supplier and item.valuation_rate:
|
||||
credit_amount = flt(item.valuation_rate * item.stock_qty)
|
||||
|
||||
# Intentionally passed negative debit amount to avoid incorrect GL Entry validation
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
@@ -718,7 +721,7 @@ class PurchaseInvoice(BuyingController):
|
||||
"cost_center": item.cost_center,
|
||||
"project": item.project or self.project,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"debit": -1 * flt(item.base_net_amount, item.precision("base_net_amount")),
|
||||
"debit": -1 * flt(credit_amount, item.precision("base_net_amount")),
|
||||
},
|
||||
warehouse_account[item.from_warehouse]["account_currency"],
|
||||
item=item,
|
||||
@@ -807,7 +810,7 @@ class PurchaseInvoice(BuyingController):
|
||||
)
|
||||
|
||||
if not item.is_fixed_asset:
|
||||
dummy, amount = self.get_amount_and_base_amount(item, enable_discount_accounting)
|
||||
dummy, amount = self.get_amount_and_base_amount(item, None)
|
||||
else:
|
||||
amount = flt(item.base_net_amount + item.item_tax_amount, item.precision("base_net_amount"))
|
||||
|
||||
@@ -1160,12 +1163,9 @@ class PurchaseInvoice(BuyingController):
|
||||
def make_tax_gl_entries(self, gl_entries):
|
||||
# tax table gl entries
|
||||
valuation_tax = {}
|
||||
enable_discount_accounting = cint(
|
||||
frappe.db.get_single_value("Buying Settings", "enable_discount_accounting")
|
||||
)
|
||||
|
||||
for tax in self.get("taxes"):
|
||||
amount, base_amount = self.get_tax_amounts(tax, enable_discount_accounting)
|
||||
amount, base_amount = self.get_tax_amounts(tax, None)
|
||||
if tax.category in ("Total", "Valuation and Total") and flt(base_amount):
|
||||
account_currency = get_account_currency(tax.account_head)
|
||||
|
||||
@@ -1250,15 +1250,6 @@ class PurchaseInvoice(BuyingController):
|
||||
)
|
||||
)
|
||||
|
||||
@property
|
||||
def enable_discount_accounting(self):
|
||||
if not hasattr(self, "_enable_discount_accounting"):
|
||||
self._enable_discount_accounting = cint(
|
||||
frappe.db.get_single_value("Buying Settings", "enable_discount_accounting")
|
||||
)
|
||||
|
||||
return self._enable_discount_accounting
|
||||
|
||||
def make_internal_transfer_gl_entries(self, gl_entries):
|
||||
if self.is_internal_transfer() and flt(self.base_total_taxes_and_charges):
|
||||
account_currency = get_account_currency(self.unrealized_profit_loss_account)
|
||||
@@ -1419,7 +1410,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.repost_future_sle_and_gle()
|
||||
|
||||
self.update_project()
|
||||
frappe.db.set(self, "status", "Cancelled")
|
||||
self.db_set("status", "Cancelled")
|
||||
|
||||
unlink_inter_company_doc(self.doctype, self.name, self.inter_company_invoice_reference)
|
||||
self.ignore_linked_doctypes = (
|
||||
@@ -1427,6 +1418,7 @@ class PurchaseInvoice(BuyingController):
|
||||
"Stock Ledger Entry",
|
||||
"Repost Item Valuation",
|
||||
"Payment Ledger Entry",
|
||||
"Tax Withheld Vouchers",
|
||||
)
|
||||
self.update_advance_tax_references(cancel=1)
|
||||
|
||||
@@ -1471,6 +1463,7 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
def update_billing_status_in_pr(self, update_modified=True):
|
||||
updated_pr = []
|
||||
po_details = []
|
||||
for d in self.get("items"):
|
||||
if d.pr_detail:
|
||||
billed_amt = frappe.db.sql(
|
||||
@@ -1488,7 +1481,10 @@ class PurchaseInvoice(BuyingController):
|
||||
)
|
||||
updated_pr.append(d.purchase_receipt)
|
||||
elif d.po_detail:
|
||||
updated_pr += update_billed_amount_based_on_po(d.po_detail, update_modified)
|
||||
po_details.append(d.po_detail)
|
||||
|
||||
if po_details:
|
||||
updated_pr += update_billed_amount_based_on_po(po_details, update_modified)
|
||||
|
||||
for pr in set(updated_pr):
|
||||
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billing_percentage
|
||||
@@ -1520,7 +1516,7 @@ class PurchaseInvoice(BuyingController):
|
||||
if not self.tax_withholding_category:
|
||||
return
|
||||
|
||||
tax_withholding_details, advance_taxes = get_party_tax_withholding_details(
|
||||
tax_withholding_details, advance_taxes, voucher_wise_amount = get_party_tax_withholding_details(
|
||||
self, self.tax_withholding_category
|
||||
)
|
||||
|
||||
@@ -1549,6 +1545,19 @@ class PurchaseInvoice(BuyingController):
|
||||
for d in to_remove:
|
||||
self.remove(d)
|
||||
|
||||
## Add pending vouchers on which tax was withheld
|
||||
self.set("tax_withheld_vouchers", [])
|
||||
|
||||
for voucher_no, voucher_details in voucher_wise_amount.items():
|
||||
self.append(
|
||||
"tax_withheld_vouchers",
|
||||
{
|
||||
"voucher_name": voucher_no,
|
||||
"voucher_type": voucher_details.get("voucher_type"),
|
||||
"taxable_amount": voucher_details.get("amount"),
|
||||
},
|
||||
)
|
||||
|
||||
# calculate totals again after applying TDS
|
||||
self.calculate_taxes_and_totals()
|
||||
|
||||
@@ -1791,4 +1800,6 @@ def make_purchase_receipt(source_name, target_doc=None):
|
||||
target_doc,
|
||||
)
|
||||
|
||||
doc.set_onload("ignore_price_list", True)
|
||||
|
||||
return doc
|
||||
|
||||
@@ -338,59 +338,6 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin):
|
||||
|
||||
self.assertEqual(discrepancy_caused_by_exchange_rate_diff, amount)
|
||||
|
||||
@change_settings("Buying Settings", {"enable_discount_accounting": 1})
|
||||
def test_purchase_invoice_with_discount_accounting_enabled(self):
|
||||
|
||||
discount_account = create_account(
|
||||
account_name="Discount Account",
|
||||
parent_account="Indirect Expenses - _TC",
|
||||
company="_Test Company",
|
||||
)
|
||||
pi = make_purchase_invoice(discount_account=discount_account, rate=45)
|
||||
|
||||
expected_gle = [
|
||||
["_Test Account Cost for Goods Sold - _TC", 250.0, 0.0, nowdate()],
|
||||
["Creditors - _TC", 0.0, 225.0, nowdate()],
|
||||
["Discount Account - _TC", 0.0, 25.0, nowdate()],
|
||||
]
|
||||
|
||||
check_gl_entries(self, pi.name, expected_gle, nowdate())
|
||||
|
||||
@change_settings("Buying Settings", {"enable_discount_accounting": 1})
|
||||
def test_additional_discount_for_purchase_invoice_with_discount_accounting_enabled(self):
|
||||
|
||||
additional_discount_account = create_account(
|
||||
account_name="Discount Account",
|
||||
parent_account="Indirect Expenses - _TC",
|
||||
company="_Test Company",
|
||||
)
|
||||
|
||||
pi = make_purchase_invoice(do_not_save=1, parent_cost_center="Main - _TC")
|
||||
pi.apply_discount_on = "Grand Total"
|
||||
pi.additional_discount_account = additional_discount_account
|
||||
pi.additional_discount_percentage = 10
|
||||
pi.disable_rounded_total = 1
|
||||
pi.append(
|
||||
"taxes",
|
||||
{
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "_Test Account VAT - _TC",
|
||||
"cost_center": "Main - _TC",
|
||||
"description": "Test",
|
||||
"rate": 10,
|
||||
},
|
||||
)
|
||||
pi.submit()
|
||||
|
||||
expected_gle = [
|
||||
["_Test Account Cost for Goods Sold - _TC", 250.0, 0.0, nowdate()],
|
||||
["_Test Account VAT - _TC", 25.0, 0.0, nowdate()],
|
||||
["Creditors - _TC", 0.0, 247.5, nowdate()],
|
||||
["Discount Account - _TC", 0.0, 27.5, nowdate()],
|
||||
]
|
||||
|
||||
check_gl_entries(self, pi.name, expected_gle, nowdate())
|
||||
|
||||
def test_purchase_invoice_change_naming_series(self):
|
||||
pi = frappe.copy_doc(test_records[1])
|
||||
pi.insert()
|
||||
@@ -1596,6 +1543,37 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin):
|
||||
pi.save()
|
||||
self.assertEqual(pi.items[0].conversion_factor, 1000)
|
||||
|
||||
def test_batch_expiry_for_purchase_invoice(self):
|
||||
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
||||
|
||||
item = self.make_item(
|
||||
"_Test Batch Item For Return Check",
|
||||
{
|
||||
"is_purchase_item": 1,
|
||||
"is_stock_item": 1,
|
||||
"has_batch_no": 1,
|
||||
"create_new_batch": 1,
|
||||
"batch_number_series": "TBIRC.#####",
|
||||
},
|
||||
)
|
||||
|
||||
pi = make_purchase_invoice(
|
||||
qty=1,
|
||||
item_code=item.name,
|
||||
update_stock=True,
|
||||
)
|
||||
|
||||
pi.load_from_db()
|
||||
batch_no = pi.items[0].batch_no
|
||||
self.assertTrue(batch_no)
|
||||
|
||||
frappe.db.set_value("Batch", batch_no, "expiry_date", add_days(nowdate(), -1))
|
||||
|
||||
return_pi = make_return_doc(pi.doctype, pi.name)
|
||||
return_pi.save().submit()
|
||||
|
||||
self.assertTrue(return_pi.docstatus == 1)
|
||||
|
||||
|
||||
def check_gl_entries(doc, voucher_no, expected_gle, posting_date):
|
||||
gl_entries = frappe.db.sql(
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"discount_amount",
|
||||
"base_rate_with_margin",
|
||||
"sec_break2",
|
||||
"apply_tds",
|
||||
"rate",
|
||||
"amount",
|
||||
"item_tax_template",
|
||||
@@ -74,7 +75,6 @@
|
||||
"manufacturer_part_no",
|
||||
"accounting",
|
||||
"expense_account",
|
||||
"discount_account",
|
||||
"col_break5",
|
||||
"is_fixed_asset",
|
||||
"asset_location",
|
||||
@@ -215,6 +215,7 @@
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"depends_on": "eval:doc.uom != doc.stock_uom",
|
||||
"fieldname": "conversion_factor",
|
||||
"fieldtype": "Float",
|
||||
@@ -712,6 +713,7 @@
|
||||
"label": "Valuation Rate",
|
||||
"no_copy": 1,
|
||||
"options": "Company:company:default_currency",
|
||||
"precision": "6",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -820,6 +822,7 @@
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "eval: doc.margin_type || doc.discount_amount",
|
||||
"fieldname": "section_break_26",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Discount and Margin"
|
||||
@@ -860,24 +863,24 @@
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "discount_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Discount Account",
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
"fieldname": "product_bundle",
|
||||
"fieldtype": "Link",
|
||||
"label": "Product Bundle",
|
||||
"options": "Product Bundle",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "apply_tds",
|
||||
"fieldtype": "Check",
|
||||
"label": "Apply TDS"
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-06-17 05:31:10.520171",
|
||||
"modified": "2022-10-26 16:05:37.304788",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice Item",
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Repost Payment Ledger', {
|
||||
setup: function(frm) {
|
||||
frm.set_query("voucher_type", () => {
|
||||
return {
|
||||
filters: {
|
||||
name: ['in', ['Purchase Invoice', 'Sales Invoice', 'Payment Entry', 'Journal Entry']]
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
frm.fields_dict['repost_vouchers'].grid.get_field('voucher_type').get_query = function(doc) {
|
||||
return {
|
||||
filters: {
|
||||
name: ['in', ['Purchase Invoice', 'Sales Invoice', 'Payment Entry', 'Journal Entry']]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
frm.fields_dict['repost_vouchers'].grid.get_field('voucher_no').get_query = function(doc) {
|
||||
if (doc.company) {
|
||||
return {
|
||||
filters: {
|
||||
company: doc.company,
|
||||
docstatus: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
refresh: function(frm) {
|
||||
|
||||
if (frm.doc.docstatus==1 && ['Queued', 'Failed'].find(x => x == frm.doc.repost_status)) {
|
||||
frm.set_intro(__("Use 'Repost in background' button to trigger background job. Job can only be triggered when document is in Queued or Failed status."));
|
||||
var btn_label = __("Repost in background")
|
||||
|
||||
frm.add_custom_button(btn_label, () => {
|
||||
frappe.call({
|
||||
method: 'erpnext.accounts.doctype.repost_payment_ledger.repost_payment_ledger.execute_repost_payment_ledger',
|
||||
args: {
|
||||
docname: frm.doc.name,
|
||||
}
|
||||
});
|
||||
frappe.msgprint(__('Reposting in the background.'));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_rename": 1,
|
||||
"creation": "2022-10-19 21:59:33.553852",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"filters_section",
|
||||
"company",
|
||||
"posting_date",
|
||||
"column_break_4",
|
||||
"voucher_type",
|
||||
"add_manually",
|
||||
"status_section",
|
||||
"repost_status",
|
||||
"repost_error_log",
|
||||
"selected_vouchers_section",
|
||||
"repost_vouchers",
|
||||
"amended_from"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"default": "Today",
|
||||
"fieldname": "posting_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Posting Date",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "voucher_type",
|
||||
"fieldtype": "Link",
|
||||
"label": "Voucher Type",
|
||||
"options": "DocType"
|
||||
},
|
||||
{
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
"label": "Amended From",
|
||||
"no_copy": 1,
|
||||
"options": "Repost Payment Ledger",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Company",
|
||||
"options": "Company",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "selected_vouchers_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Vouchers"
|
||||
},
|
||||
{
|
||||
"fieldname": "filters_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Filters"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_4",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "repost_vouchers",
|
||||
"fieldtype": "Table",
|
||||
"label": "Selected Vouchers",
|
||||
"options": "Repost Payment Ledger Items"
|
||||
},
|
||||
{
|
||||
"fieldname": "repost_status",
|
||||
"fieldtype": "Select",
|
||||
"label": "Repost Status",
|
||||
"options": "\nQueued\nFailed\nCompleted",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "status_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Status"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "Ignore Voucher Type filter and Select Vouchers Manually",
|
||||
"fieldname": "add_manually",
|
||||
"fieldtype": "Check",
|
||||
"label": "Add Manually"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.repost_error_log",
|
||||
"fieldname": "repost_error_log",
|
||||
"fieldtype": "Long Text",
|
||||
"label": "Repost Error Log"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-11-08 07:38:40.079038",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Repost Payment Ledger",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts Manager",
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts User",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"permlevel": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import copy
|
||||
|
||||
import frappe
|
||||
from frappe import _, qb
|
||||
from frappe.model.document import Document
|
||||
from frappe.query_builder.custom import ConstantColumn
|
||||
from frappe.utils.background_jobs import is_job_queued
|
||||
|
||||
from erpnext.accounts.utils import _delete_pl_entries, create_payment_ledger_entry
|
||||
|
||||
VOUCHER_TYPES = ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"]
|
||||
|
||||
|
||||
def repost_ple_for_voucher(voucher_type, voucher_no, gle_map=None):
|
||||
if voucher_type and voucher_no and gle_map:
|
||||
_delete_pl_entries(voucher_type, voucher_no)
|
||||
create_payment_ledger_entry(gle_map, cancel=0)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def start_payment_ledger_repost(docname=None):
|
||||
"""
|
||||
Repost Payment Ledger Entries for Vouchers through Background Job
|
||||
"""
|
||||
if docname:
|
||||
repost_doc = frappe.get_doc("Repost Payment Ledger", docname)
|
||||
if repost_doc.docstatus == 1 and repost_doc.repost_status in ["Queued", "Failed"]:
|
||||
try:
|
||||
for entry in repost_doc.repost_vouchers:
|
||||
doc = frappe.get_doc(entry.voucher_type, entry.voucher_no)
|
||||
|
||||
if doc.doctype in ["Payment Entry", "Journal Entry"]:
|
||||
gle_map = doc.build_gl_map()
|
||||
else:
|
||||
gle_map = doc.get_gl_entries()
|
||||
|
||||
repost_ple_for_voucher(entry.voucher_type, entry.voucher_no, gle_map)
|
||||
|
||||
frappe.db.set_value(repost_doc.doctype, repost_doc.name, "repost_error_log", "")
|
||||
frappe.db.set_value(repost_doc.doctype, repost_doc.name, "repost_status", "Completed")
|
||||
except Exception as e:
|
||||
frappe.db.rollback()
|
||||
|
||||
traceback = frappe.get_traceback()
|
||||
if traceback:
|
||||
message = "Traceback: <br>" + traceback
|
||||
frappe.db.set_value(repost_doc.doctype, repost_doc.name, "repost_error_log", message)
|
||||
|
||||
frappe.db.set_value(repost_doc.doctype, repost_doc.name, "repost_status", "Failed")
|
||||
|
||||
|
||||
class RepostPaymentLedger(Document):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(RepostPaymentLedger, self).__init__(*args, **kwargs)
|
||||
self.vouchers = []
|
||||
|
||||
def before_validate(self):
|
||||
self.load_vouchers_based_on_filters()
|
||||
self.set_status()
|
||||
|
||||
def load_vouchers_based_on_filters(self):
|
||||
if not self.add_manually:
|
||||
self.repost_vouchers.clear()
|
||||
self.get_vouchers()
|
||||
self.extend("repost_vouchers", copy.deepcopy(self.vouchers))
|
||||
|
||||
def get_vouchers(self):
|
||||
self.vouchers.clear()
|
||||
|
||||
filter_on_voucher_types = [self.voucher_type] if self.voucher_type else VOUCHER_TYPES
|
||||
|
||||
for vtype in filter_on_voucher_types:
|
||||
doc = qb.DocType(vtype)
|
||||
doctype_name = ConstantColumn(vtype)
|
||||
query = (
|
||||
qb.from_(doc)
|
||||
.select(doctype_name.as_("voucher_type"), doc.name.as_("voucher_no"))
|
||||
.where(
|
||||
(doc.docstatus == 1)
|
||||
& (doc.company == self.company)
|
||||
& (doc.posting_date.gte(self.posting_date))
|
||||
)
|
||||
)
|
||||
entries = query.run(as_dict=True)
|
||||
self.vouchers.extend(entries)
|
||||
|
||||
def set_status(self):
|
||||
if self.docstatus == 0:
|
||||
self.repost_status = "Queued"
|
||||
|
||||
def on_submit(self):
|
||||
execute_repost_payment_ledger(self.name)
|
||||
frappe.msgprint(_("Repost started in the background"))
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def execute_repost_payment_ledger(docname):
|
||||
"""Repost Payment Ledger Entries by background job."""
|
||||
|
||||
job_name = "payment_ledger_repost_" + docname
|
||||
|
||||
if not is_job_queued(job_name):
|
||||
frappe.enqueue(
|
||||
method="erpnext.accounts.doctype.repost_payment_ledger.repost_payment_ledger.start_payment_ledger_repost",
|
||||
docname=docname,
|
||||
is_async=True,
|
||||
job_name=job_name,
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
frappe.listview_settings["Repost Payment Ledger"] = {
|
||||
add_fields: ["repost_status"],
|
||||
get_indicator: function(doc) {
|
||||
var colors = {
|
||||
'Queued': 'orange',
|
||||
'Completed': 'green',
|
||||
'Failed': 'red',
|
||||
};
|
||||
let status = doc.repost_status;
|
||||
return [__(status), colors[status], 'status,=,'+status];
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
|
||||
|
||||
class TestRepostPaymentLedger(FrappeTestCase):
|
||||
pass
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2022-10-20 10:44:18.796489",
|
||||
"doctype": "DocType",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"voucher_type",
|
||||
"voucher_no"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "voucher_type",
|
||||
"fieldtype": "Link",
|
||||
"label": "Voucher Type",
|
||||
"options": "DocType"
|
||||
},
|
||||
{
|
||||
"fieldname": "voucher_no",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"label": "Voucher No",
|
||||
"options": "voucher_type"
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-10-28 14:47:11.838109",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Repost Payment Ledger Items",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class RepostPaymentLedgerItems(Document):
|
||||
pass
|
||||
@@ -64,6 +64,25 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
|
||||
|
||||
this.frm.toggle_reqd("due_date", !this.frm.doc.is_return);
|
||||
|
||||
if (this.frm.doc.repost_required && this.frm.doc.docstatus===1) {
|
||||
this.frm.set_intro(__("Accounting entries for this invoice needs to be reposted. Please click on 'Repost' button to update."));
|
||||
this.frm.add_custom_button(__('Repost Accounting Entries'),
|
||||
() => {
|
||||
this.frm.call({
|
||||
doc: this.frm.doc,
|
||||
method: 'repost_accounting_entries',
|
||||
freeze: true,
|
||||
freeze_message: __('Reposting...'),
|
||||
callback: (r) => {
|
||||
if (!r.exc) {
|
||||
frappe.msgprint(__('Accounting Entries are reposted'));
|
||||
me.frm.refresh();
|
||||
}
|
||||
}
|
||||
});
|
||||
}).removeClass('btn-default').addClass('btn-warning');
|
||||
}
|
||||
|
||||
if (this.frm.doc.is_return) {
|
||||
this.frm.return_print_format = "Sales Invoice Return";
|
||||
}
|
||||
@@ -479,9 +498,13 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
|
||||
|
||||
is_cash_or_non_trade_discount() {
|
||||
this.frm.set_df_property("additional_discount_account", "hidden", 1 - this.frm.doc.is_cash_or_non_trade_discount);
|
||||
this.frm.set_df_property("additional_discount_account", "reqd", this.frm.doc.is_cash_or_non_trade_discount);
|
||||
|
||||
if (!this.frm.doc.is_cash_or_non_trade_discount) {
|
||||
this.frm.set_value("additional_discount_account", "");
|
||||
}
|
||||
|
||||
this.calculate_taxes_and_totals();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1024,7 +1047,7 @@ var select_loyalty_program = function(frm, loyalty_programs) {
|
||||
]
|
||||
});
|
||||
|
||||
dialog.set_primary_action(__("Set"), function() {
|
||||
dialog.set_primary_action(__("Set Loyalty Program"), function() {
|
||||
dialog.hide();
|
||||
return frappe.call({
|
||||
method: "frappe.client.set_value",
|
||||
|
||||
@@ -12,44 +12,29 @@
|
||||
"customer",
|
||||
"customer_name",
|
||||
"tax_id",
|
||||
"pos_profile",
|
||||
"is_pos",
|
||||
"is_consolidated",
|
||||
"is_return",
|
||||
"is_debit_note",
|
||||
"update_billed_amount_in_sales_order",
|
||||
"column_break1",
|
||||
"company",
|
||||
"company_tax_id",
|
||||
"column_break1",
|
||||
"posting_date",
|
||||
"posting_time",
|
||||
"set_posting_time",
|
||||
"due_date",
|
||||
"column_break_14",
|
||||
"is_pos",
|
||||
"pos_profile",
|
||||
"is_consolidated",
|
||||
"is_return",
|
||||
"return_against",
|
||||
"update_billed_amount_in_sales_order",
|
||||
"is_debit_note",
|
||||
"amended_from",
|
||||
"accounting_dimensions_section",
|
||||
"project",
|
||||
"dimension_col_break",
|
||||
"cost_center",
|
||||
"customer_po_details",
|
||||
"po_no",
|
||||
"column_break_23",
|
||||
"po_date",
|
||||
"address_and_contact",
|
||||
"customer_address",
|
||||
"address_display",
|
||||
"contact_person",
|
||||
"contact_display",
|
||||
"contact_mobile",
|
||||
"contact_email",
|
||||
"territory",
|
||||
"col_break4",
|
||||
"shipping_address_name",
|
||||
"shipping_address",
|
||||
"company_address",
|
||||
"company_address_display",
|
||||
"dispatch_address_name",
|
||||
"dispatch_address",
|
||||
"dimension_col_break",
|
||||
"project",
|
||||
"column_break_27",
|
||||
"campaign",
|
||||
"source",
|
||||
"currency_and_price_list",
|
||||
"currency",
|
||||
"conversion_rate",
|
||||
@@ -58,60 +43,35 @@
|
||||
"price_list_currency",
|
||||
"plc_conversion_rate",
|
||||
"ignore_pricing_rule",
|
||||
"sec_warehouse",
|
||||
"set_warehouse",
|
||||
"column_break_55",
|
||||
"set_target_warehouse",
|
||||
"items_section",
|
||||
"update_stock",
|
||||
"scan_barcode",
|
||||
"update_stock",
|
||||
"column_break_39",
|
||||
"set_warehouse",
|
||||
"set_target_warehouse",
|
||||
"section_break_42",
|
||||
"items",
|
||||
"pricing_rule_details",
|
||||
"pricing_rules",
|
||||
"packing_list",
|
||||
"packed_items",
|
||||
"product_bundle_help",
|
||||
"time_sheet_list",
|
||||
"timesheets",
|
||||
"total_billing_amount",
|
||||
"total_billing_hours",
|
||||
"section_break_30",
|
||||
"total_qty",
|
||||
"total_net_weight",
|
||||
"column_break_32",
|
||||
"base_total",
|
||||
"base_net_total",
|
||||
"column_break_32",
|
||||
"total_net_weight",
|
||||
"column_break_52",
|
||||
"total",
|
||||
"net_total",
|
||||
"taxes_section",
|
||||
"taxes_and_charges",
|
||||
"column_break_38",
|
||||
"shipping_rule",
|
||||
"column_break_55",
|
||||
"tax_category",
|
||||
"section_break_40",
|
||||
"taxes",
|
||||
"sec_tax_breakup",
|
||||
"other_charges_calculation",
|
||||
"section_break_43",
|
||||
"base_total_taxes_and_charges",
|
||||
"column_break_47",
|
||||
"total_taxes_and_charges",
|
||||
"loyalty_points_redemption",
|
||||
"loyalty_points",
|
||||
"loyalty_amount",
|
||||
"redeem_loyalty_points",
|
||||
"column_break_77",
|
||||
"loyalty_program",
|
||||
"loyalty_redemption_account",
|
||||
"loyalty_redemption_cost_center",
|
||||
"section_break_49",
|
||||
"apply_discount_on",
|
||||
"is_cash_or_non_trade_discount",
|
||||
"base_discount_amount",
|
||||
"additional_discount_account",
|
||||
"column_break_51",
|
||||
"additional_discount_percentage",
|
||||
"discount_amount",
|
||||
"totals",
|
||||
"base_grand_total",
|
||||
"base_rounding_adjustment",
|
||||
@@ -125,21 +85,28 @@
|
||||
"total_advance",
|
||||
"outstanding_amount",
|
||||
"disable_rounded_total",
|
||||
"column_break4",
|
||||
"write_off_amount",
|
||||
"base_write_off_amount",
|
||||
"write_off_outstanding_amount_automatically",
|
||||
"column_break_74",
|
||||
"write_off_account",
|
||||
"write_off_cost_center",
|
||||
"advances_section",
|
||||
"allocate_advances_automatically",
|
||||
"get_advances",
|
||||
"advances",
|
||||
"payment_schedule_section",
|
||||
"ignore_default_payment_terms_template",
|
||||
"payment_terms_template",
|
||||
"payment_schedule",
|
||||
"section_break_49",
|
||||
"apply_discount_on",
|
||||
"base_discount_amount",
|
||||
"is_cash_or_non_trade_discount",
|
||||
"additional_discount_account",
|
||||
"column_break_51",
|
||||
"additional_discount_percentage",
|
||||
"discount_amount",
|
||||
"sec_tax_breakup",
|
||||
"other_charges_calculation",
|
||||
"pricing_rule_details",
|
||||
"pricing_rules",
|
||||
"packing_list",
|
||||
"packed_items",
|
||||
"product_bundle_help",
|
||||
"time_sheet_list",
|
||||
"timesheets",
|
||||
"section_break_104",
|
||||
"total_billing_hours",
|
||||
"column_break_106",
|
||||
"total_billing_amount",
|
||||
"payments_tab",
|
||||
"payments_section",
|
||||
"cash_bank_account",
|
||||
"payments",
|
||||
@@ -152,47 +119,96 @@
|
||||
"column_break_90",
|
||||
"change_amount",
|
||||
"account_for_change_amount",
|
||||
"advances_section",
|
||||
"allocate_advances_automatically",
|
||||
"get_advances",
|
||||
"advances",
|
||||
"write_off_section",
|
||||
"write_off_amount",
|
||||
"base_write_off_amount",
|
||||
"write_off_outstanding_amount_automatically",
|
||||
"column_break_74",
|
||||
"write_off_account",
|
||||
"write_off_cost_center",
|
||||
"loyalty_points_redemption",
|
||||
"redeem_loyalty_points",
|
||||
"loyalty_points",
|
||||
"loyalty_amount",
|
||||
"column_break_77",
|
||||
"loyalty_program",
|
||||
"loyalty_redemption_account",
|
||||
"loyalty_redemption_cost_center",
|
||||
"contact_and_address_tab",
|
||||
"address_and_contact",
|
||||
"customer_address",
|
||||
"address_display",
|
||||
"col_break4",
|
||||
"contact_person",
|
||||
"contact_display",
|
||||
"contact_mobile",
|
||||
"contact_email",
|
||||
"territory",
|
||||
"shipping_address_section",
|
||||
"shipping_address_name",
|
||||
"shipping_address",
|
||||
"shipping_addr_col_break",
|
||||
"dispatch_address_name",
|
||||
"dispatch_address",
|
||||
"company_address_section",
|
||||
"company_address",
|
||||
"company_addr_col_break",
|
||||
"company_address_display",
|
||||
"terms_tab",
|
||||
"payment_schedule_section",
|
||||
"ignore_default_payment_terms_template",
|
||||
"payment_terms_template",
|
||||
"payment_schedule",
|
||||
"terms_section_break",
|
||||
"tc_name",
|
||||
"terms",
|
||||
"edit_printing_settings",
|
||||
"letter_head",
|
||||
"group_same_items",
|
||||
"select_print_heading",
|
||||
"column_break_84",
|
||||
"language",
|
||||
"more_information",
|
||||
"status",
|
||||
"inter_company_invoice_reference",
|
||||
"represents_company",
|
||||
"customer_group",
|
||||
"campaign",
|
||||
"col_break23",
|
||||
"is_internal_customer",
|
||||
"is_discounted",
|
||||
"source",
|
||||
"more_info_tab",
|
||||
"customer_po_details",
|
||||
"po_no",
|
||||
"column_break_23",
|
||||
"po_date",
|
||||
"more_info",
|
||||
"debit_to",
|
||||
"party_account_currency",
|
||||
"is_opening",
|
||||
"column_break8",
|
||||
"unrealized_profit_loss_account",
|
||||
"remarks",
|
||||
"against_income_account",
|
||||
"sales_team_section_break",
|
||||
"sales_partner",
|
||||
"column_break10",
|
||||
"amount_eligible_for_commission",
|
||||
"column_break10",
|
||||
"commission_rate",
|
||||
"total_commission",
|
||||
"section_break2",
|
||||
"sales_team",
|
||||
"edit_printing_settings",
|
||||
"letter_head",
|
||||
"group_same_items",
|
||||
"column_break_84",
|
||||
"select_print_heading",
|
||||
"language",
|
||||
"subscription_section",
|
||||
"from_date",
|
||||
"to_date",
|
||||
"column_break_140",
|
||||
"auto_repeat",
|
||||
"column_break_140",
|
||||
"to_date",
|
||||
"update_auto_repeat_reference",
|
||||
"against_income_account"
|
||||
"more_information",
|
||||
"status",
|
||||
"inter_company_invoice_reference",
|
||||
"represents_company",
|
||||
"customer_group",
|
||||
"col_break23",
|
||||
"is_internal_customer",
|
||||
"is_discounted",
|
||||
"remarks",
|
||||
"repost_required",
|
||||
"connections_tab"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -453,12 +469,11 @@
|
||||
"label": "Customer's Purchase Order Date"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "address_and_contact",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1,
|
||||
"label": "Address and Contact"
|
||||
"label": "Billing Address"
|
||||
},
|
||||
{
|
||||
"fieldname": "customer_address",
|
||||
@@ -560,7 +575,6 @@
|
||||
{
|
||||
"fieldname": "company_address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1,
|
||||
"label": "Company Address",
|
||||
@@ -649,16 +663,8 @@
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1,
|
||||
"label": "Ignore Pricing Rule",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "sec_warehouse",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1,
|
||||
"label": "Warehouse"
|
||||
},
|
||||
{
|
||||
"depends_on": "update_stock",
|
||||
"fieldname": "set_warehouse",
|
||||
@@ -672,6 +678,7 @@
|
||||
{
|
||||
"fieldname": "items_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_border": 1,
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1,
|
||||
"label": "Items",
|
||||
@@ -703,7 +710,6 @@
|
||||
"fieldtype": "Table",
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1,
|
||||
"label": "Items",
|
||||
"oldfieldname": "entries",
|
||||
"oldfieldtype": "Table",
|
||||
"options": "Sales Invoice Item",
|
||||
@@ -756,9 +762,10 @@
|
||||
{
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "eval:doc.total_billing_amount > 0",
|
||||
"depends_on": "eval: !doc.is_return",
|
||||
"depends_on": "eval:!doc.is_return",
|
||||
"fieldname": "time_sheet_list",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_border": 1,
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1,
|
||||
"label": "Time Sheet List"
|
||||
@@ -846,6 +853,7 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "total_net_weight",
|
||||
"fieldname": "total_net_weight",
|
||||
"fieldtype": "Float",
|
||||
"hide_days": 1,
|
||||
@@ -857,8 +865,10 @@
|
||||
{
|
||||
"fieldname": "taxes_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_border": 1,
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1,
|
||||
"label": "Taxes and Charges",
|
||||
"oldfieldtype": "Section Break",
|
||||
"options": "fa fa-money"
|
||||
},
|
||||
@@ -901,6 +911,7 @@
|
||||
{
|
||||
"fieldname": "section_break_40",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_border": 1,
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1
|
||||
},
|
||||
@@ -909,7 +920,6 @@
|
||||
"fieldtype": "Table",
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1,
|
||||
"label": "Sales Taxes and Charges",
|
||||
"oldfieldname": "other_charges",
|
||||
"oldfieldtype": "Table",
|
||||
"options": "Sales Taxes and Charges"
|
||||
@@ -1026,6 +1036,7 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "redeem_loyalty_points",
|
||||
"fieldname": "loyalty_redemption_account",
|
||||
"fieldtype": "Link",
|
||||
@@ -1047,7 +1058,6 @@
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "discount_amount",
|
||||
"fieldname": "section_break_49",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_days": 1,
|
||||
@@ -1103,6 +1113,7 @@
|
||||
"fieldtype": "Section Break",
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1,
|
||||
"label": "Totals",
|
||||
"oldfieldtype": "Section Break",
|
||||
"options": "fa fa-money",
|
||||
"print_hide": 1
|
||||
@@ -1284,8 +1295,6 @@
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "eval:(!doc.is_pos && !doc.is_return)",
|
||||
"fieldname": "payment_schedule_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_days": 1,
|
||||
@@ -1315,7 +1324,9 @@
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_pos===1||(doc.advances && doc.advances.length>0)",
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "eval:!doc.is_pos",
|
||||
"depends_on": "eval:doc.is_pos===1",
|
||||
"fieldname": "payments_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_days": 1,
|
||||
@@ -1324,6 +1335,7 @@
|
||||
"options": "fa fa-money"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "is_pos",
|
||||
"fieldname": "cash_bank_account",
|
||||
"fieldtype": "Link",
|
||||
@@ -1353,6 +1365,7 @@
|
||||
"hide_seconds": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.is_pos || doc.redeem_loyalty_points",
|
||||
"fieldname": "base_paid_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hide_days": 1,
|
||||
@@ -1384,10 +1397,13 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"depends_on": "is_pos",
|
||||
"fieldname": "section_break_88",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1
|
||||
"hide_seconds": 1,
|
||||
"label": "Changes"
|
||||
},
|
||||
{
|
||||
"depends_on": "is_pos",
|
||||
@@ -1419,6 +1435,7 @@
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "is_pos",
|
||||
"fieldname": "account_for_change_amount",
|
||||
"fieldtype": "Link",
|
||||
@@ -1428,17 +1445,6 @@
|
||||
"options": "Account",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "write_off_amount",
|
||||
"depends_on": "grand_total",
|
||||
"fieldname": "column_break4",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1,
|
||||
"label": "Write Off",
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"fieldname": "write_off_amount",
|
||||
"fieldtype": "Currency",
|
||||
@@ -1478,6 +1484,7 @@
|
||||
"hide_seconds": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "write_off_account",
|
||||
"fieldtype": "Link",
|
||||
"hide_days": 1,
|
||||
@@ -1496,8 +1503,6 @@
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "terms",
|
||||
"fieldname": "terms_section_break",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_days": 1,
|
||||
@@ -1531,7 +1536,7 @@
|
||||
"fieldtype": "Section Break",
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1,
|
||||
"label": "Printing Settings"
|
||||
"label": "Print Settings"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
@@ -1592,7 +1597,7 @@
|
||||
"fieldtype": "Section Break",
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1,
|
||||
"label": "More Information"
|
||||
"label": "Additional Info"
|
||||
},
|
||||
{
|
||||
"fieldname": "inter_company_invoice_reference",
|
||||
@@ -1703,6 +1708,7 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"default": "No",
|
||||
"fieldname": "is_opening",
|
||||
"fieldtype": "Select",
|
||||
@@ -1815,7 +1821,7 @@
|
||||
"fieldtype": "Section Break",
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1,
|
||||
"label": "Subscription Section"
|
||||
"label": "Subscription"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
@@ -1917,6 +1923,7 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_internal_customer",
|
||||
"description": "Unrealized Profit / Loss account for intra-company transfers",
|
||||
"fieldname": "unrealized_profit_loss_account",
|
||||
@@ -1936,10 +1943,6 @@
|
||||
"options": "Company",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_55",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.is_internal_customer && doc.update_stock",
|
||||
"fieldname": "set_target_warehouse",
|
||||
@@ -1963,6 +1966,7 @@
|
||||
"label": "Disable Rounded Total"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "additional_discount_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Discount Account",
|
||||
@@ -2010,6 +2014,106 @@
|
||||
"fieldname": "is_cash_or_non_trade_discount",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Cash or Non Trade Discount"
|
||||
},
|
||||
{
|
||||
"fieldname": "contact_and_address_tab",
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "Contact & Address"
|
||||
},
|
||||
{
|
||||
"fieldname": "payments_tab",
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "Payments"
|
||||
},
|
||||
{
|
||||
"fieldname": "terms_tab",
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "Terms"
|
||||
},
|
||||
{
|
||||
"fieldname": "more_info_tab",
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "More Info"
|
||||
},
|
||||
{
|
||||
"fieldname": "connections_tab",
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "Connections",
|
||||
"show_dashboard": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_14",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_39",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_42",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_border": 1,
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_55",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "shipping_address_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Shipping Address"
|
||||
},
|
||||
{
|
||||
"fieldname": "company_address_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Company Address"
|
||||
},
|
||||
{
|
||||
"fieldname": "shipping_addr_col_break",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "company_addr_col_break",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_27",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_52",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:(!doc.is_return && doc.total_billing_amount > 0)",
|
||||
"fieldname": "section_break_104",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_106",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "write_off_amount",
|
||||
"depends_on": "is_pos",
|
||||
"fieldname": "write_off_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1,
|
||||
"label": "Write Off",
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "repost_required",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Repost Required",
|
||||
"no_copy": 1,
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-file-text",
|
||||
@@ -2022,7 +2126,7 @@
|
||||
"link_fieldname": "consolidated_invoice"
|
||||
}
|
||||
],
|
||||
"modified": "2022-07-11 17:43:56.435382",
|
||||
"modified": "2022-11-15 09:33:47.870616",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice",
|
||||
|
||||
@@ -7,20 +7,13 @@ from frappe import _, msgprint, throw
|
||||
from frappe.contacts.doctype.address.address import get_address_display
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
from frappe.model.utils import get_fetch_values
|
||||
from frappe.utils import (
|
||||
add_days,
|
||||
add_months,
|
||||
cint,
|
||||
cstr,
|
||||
flt,
|
||||
formatdate,
|
||||
get_link_to_form,
|
||||
getdate,
|
||||
nowdate,
|
||||
)
|
||||
from frappe.utils import add_days, cint, cstr, flt, formatdate, get_link_to_form, getdate, nowdate
|
||||
|
||||
import erpnext
|
||||
from erpnext.accounts.deferred_revenue import validate_service_stop_date
|
||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
|
||||
get_accounting_dimensions,
|
||||
)
|
||||
from erpnext.accounts.doctype.loyalty_program.loyalty_program import (
|
||||
get_loyalty_program_details_with_points,
|
||||
validate_loyalty_points,
|
||||
@@ -32,10 +25,12 @@ from erpnext.accounts.general_ledger import get_round_off_account_and_cost_cente
|
||||
from erpnext.accounts.party import get_due_date, get_party_account, get_party_details
|
||||
from erpnext.accounts.utils import get_account_currency
|
||||
from erpnext.assets.doctype.asset.depreciation import (
|
||||
depreciate_asset,
|
||||
get_disposal_account_and_cost_center,
|
||||
get_gl_entries_on_asset_disposal,
|
||||
get_gl_entries_on_asset_regain,
|
||||
make_depreciation_entry,
|
||||
reset_depreciation_schedule,
|
||||
reverse_depreciation_entry_made_after_disposal,
|
||||
)
|
||||
from erpnext.controllers.accounts_controller import validate_account_head
|
||||
from erpnext.controllers.selling_controller import SellingController
|
||||
@@ -108,13 +103,11 @@ class SalesInvoice(SellingController):
|
||||
self.validate_debit_to_acc()
|
||||
self.clear_unallocated_advances("Sales Invoice Advance", "advances")
|
||||
self.add_remarks()
|
||||
self.validate_write_off_account()
|
||||
self.validate_account_for_change_amount()
|
||||
self.validate_fixed_asset()
|
||||
self.set_income_account_for_fixed_assets()
|
||||
self.validate_item_cost_centers()
|
||||
self.validate_income_account()
|
||||
self.check_conversion_rate()
|
||||
self.validate_accounts()
|
||||
|
||||
validate_inter_company_party(
|
||||
self.doctype, self.customer, self.company, self.inter_company_invoice_reference
|
||||
@@ -178,6 +171,11 @@ class SalesInvoice(SellingController):
|
||||
|
||||
self.reset_default_field_value("set_warehouse", "items", "warehouse")
|
||||
|
||||
def validate_accounts(self):
|
||||
self.validate_write_off_account()
|
||||
self.validate_account_for_change_amount()
|
||||
self.validate_income_account()
|
||||
|
||||
def validate_fixed_asset(self):
|
||||
for d in self.get("items"):
|
||||
if d.is_fixed_asset and d.meta.get_field("asset") and d.asset:
|
||||
@@ -186,7 +184,7 @@ class SalesInvoice(SellingController):
|
||||
if self.update_stock:
|
||||
frappe.throw(_("'Update Stock' cannot be checked for fixed asset sale"))
|
||||
|
||||
elif asset.status in ("Scrapped", "Cancelled") or (
|
||||
elif asset.status in ("Scrapped", "Cancelled", "Capitalized", "Decapitalized") or (
|
||||
asset.status == "Sold" and not self.is_return
|
||||
):
|
||||
frappe.throw(
|
||||
@@ -375,7 +373,8 @@ class SalesInvoice(SellingController):
|
||||
if self.update_stock == 1:
|
||||
self.repost_future_sle_and_gle()
|
||||
|
||||
frappe.db.set(self, "status", "Cancelled")
|
||||
self.db_set("status", "Cancelled")
|
||||
self.db_set("repost_required", 0)
|
||||
|
||||
if (
|
||||
frappe.db.get_single_value("Selling Settings", "sales_update_frequency") == "Each Transaction"
|
||||
@@ -522,6 +521,92 @@ class SalesInvoice(SellingController):
|
||||
def on_update(self):
|
||||
self.set_paid_amount()
|
||||
|
||||
def on_update_after_submit(self):
|
||||
if hasattr(self, "repost_required"):
|
||||
needs_repost = 0
|
||||
|
||||
# Check if any field affecting accounting entry is altered
|
||||
doc_before_update = self.get_doc_before_save()
|
||||
accounting_dimensions = get_accounting_dimensions() + ["cost_center", "project"]
|
||||
|
||||
# Check if opening entry check updated
|
||||
if doc_before_update.get("is_opening") != self.is_opening:
|
||||
needs_repost = 1
|
||||
|
||||
if not needs_repost:
|
||||
# Parent Level Accounts excluding party account
|
||||
for field in (
|
||||
"additional_discount_account",
|
||||
"cash_bank_account",
|
||||
"account_for_change_amount",
|
||||
"write_off_account",
|
||||
"loyalty_redemption_account",
|
||||
"unrealized_profit_loss_account",
|
||||
):
|
||||
if doc_before_update.get(field) != self.get(field):
|
||||
needs_repost = 1
|
||||
break
|
||||
|
||||
# Check for parent accounting dimensions
|
||||
for dimension in accounting_dimensions:
|
||||
if doc_before_update.get(dimension) != self.get(dimension):
|
||||
needs_repost = 1
|
||||
break
|
||||
|
||||
# Check for child tables
|
||||
if self.check_if_child_table_updated(
|
||||
"items",
|
||||
doc_before_update,
|
||||
("income_account", "expense_account", "discount_account"),
|
||||
accounting_dimensions,
|
||||
):
|
||||
needs_repost = 1
|
||||
|
||||
if self.check_if_child_table_updated(
|
||||
"taxes", doc_before_update, ("account_head",), accounting_dimensions
|
||||
):
|
||||
needs_repost = 1
|
||||
|
||||
self.validate_accounts()
|
||||
|
||||
# validate if deferred revenue is enabled for any item
|
||||
# Don't allow to update the invoice if deferred revenue is enabled
|
||||
for item in self.get("items"):
|
||||
if item.enable_deferred_revenue:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Deferred Revenue is enabled for item {0}. You cannot update the invoice after submission."
|
||||
).format(item.item_code)
|
||||
)
|
||||
|
||||
self.db_set("repost_required", needs_repost)
|
||||
|
||||
def check_if_child_table_updated(
|
||||
self, child_table, doc_before_update, fields_to_check, accounting_dimensions
|
||||
):
|
||||
# Check if any field affecting accounting entry is altered
|
||||
for index, item in enumerate(self.get(child_table)):
|
||||
for field in fields_to_check:
|
||||
if doc_before_update.get(child_table)[index].get(field) != item.get(field):
|
||||
return True
|
||||
|
||||
for dimension in accounting_dimensions:
|
||||
if doc_before_update.get(child_table)[index].get(dimension) != item.get(dimension):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@frappe.whitelist()
|
||||
def repost_accounting_entries(self):
|
||||
if self.repost_required:
|
||||
self.docstatus = 2
|
||||
self.make_gl_entries_on_cancel()
|
||||
self.docstatus = 1
|
||||
self.make_gl_entries()
|
||||
self.db_set("repost_required", 0)
|
||||
else:
|
||||
frappe.throw(_("No updates pending for reposting"))
|
||||
|
||||
def set_paid_amount(self):
|
||||
paid_amount = 0.0
|
||||
base_paid_amount = 0.0
|
||||
@@ -710,6 +795,7 @@ class SalesInvoice(SellingController):
|
||||
if (
|
||||
cint(frappe.db.get_single_value("Selling Settings", "maintain_same_sales_rate"))
|
||||
and not self.is_return
|
||||
and not self.is_internal_customer
|
||||
):
|
||||
self.validate_rate_with_reference_doc(
|
||||
[["Sales Order", "sales_order", "so_detail"], ["Delivery Note", "delivery_note", "dn_detail"]]
|
||||
@@ -1033,22 +1119,6 @@ class SalesInvoice(SellingController):
|
||||
)
|
||||
)
|
||||
|
||||
if self.apply_discount_on == "Grand Total" and self.get("is_cash_or_discount_account"):
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": self.additional_discount_account,
|
||||
"against": self.debit_to,
|
||||
"debit": self.base_discount_amount,
|
||||
"debit_in_account_currency": self.discount_amount,
|
||||
"cost_center": self.cost_center,
|
||||
"project": self.project,
|
||||
},
|
||||
self.currency,
|
||||
item=self,
|
||||
)
|
||||
)
|
||||
|
||||
def make_tax_gl_entries(self, gl_entries):
|
||||
enable_discount_accounting = cint(
|
||||
frappe.db.get_single_value("Selling Settings", "enable_discount_accounting")
|
||||
@@ -1107,23 +1177,25 @@ class SalesInvoice(SellingController):
|
||||
|
||||
if self.is_return:
|
||||
fixed_asset_gl_entries = get_gl_entries_on_asset_regain(
|
||||
asset, item.base_net_amount, item.finance_book
|
||||
asset, item.base_net_amount, item.finance_book, self.get("doctype"), self.get("name")
|
||||
)
|
||||
asset.db_set("disposal_date", None)
|
||||
|
||||
if asset.calculate_depreciation:
|
||||
self.reverse_depreciation_entry_made_after_sale(asset)
|
||||
self.reset_depreciation_schedule(asset)
|
||||
posting_date = frappe.db.get_value("Sales Invoice", self.return_against, "posting_date")
|
||||
reverse_depreciation_entry_made_after_disposal(asset, posting_date)
|
||||
reset_depreciation_schedule(asset, self.posting_date)
|
||||
|
||||
else:
|
||||
if asset.calculate_depreciation:
|
||||
depreciate_asset(asset, self.posting_date)
|
||||
asset.reload()
|
||||
|
||||
fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(
|
||||
asset, item.base_net_amount, item.finance_book
|
||||
asset, item.base_net_amount, item.finance_book, self.get("doctype"), self.get("name")
|
||||
)
|
||||
asset.db_set("disposal_date", self.posting_date)
|
||||
|
||||
if asset.calculate_depreciation:
|
||||
self.depreciate_asset(asset)
|
||||
|
||||
for gle in fixed_asset_gl_entries:
|
||||
gle["against"] = self.customer
|
||||
gl_entries.append(self.get_gl_dict(gle, item=item))
|
||||
@@ -1177,101 +1249,6 @@ class SalesInvoice(SellingController):
|
||||
self.check_finance_books(item, asset)
|
||||
return asset
|
||||
|
||||
def check_finance_books(self, item, asset):
|
||||
if (
|
||||
len(asset.finance_books) > 1 and not item.finance_book and asset.finance_books[0].finance_book
|
||||
):
|
||||
frappe.throw(
|
||||
_("Select finance book for the item {0} at row {1}").format(item.item_code, item.idx)
|
||||
)
|
||||
|
||||
def depreciate_asset(self, asset):
|
||||
asset.flags.ignore_validate_update_after_submit = True
|
||||
asset.prepare_depreciation_data(date_of_sale=self.posting_date)
|
||||
asset.save()
|
||||
|
||||
make_depreciation_entry(asset.name, self.posting_date)
|
||||
|
||||
def reset_depreciation_schedule(self, asset):
|
||||
asset.flags.ignore_validate_update_after_submit = True
|
||||
|
||||
# recreate original depreciation schedule of the asset
|
||||
asset.prepare_depreciation_data(date_of_return=self.posting_date)
|
||||
|
||||
self.modify_depreciation_schedule_for_asset_repairs(asset)
|
||||
asset.save()
|
||||
|
||||
def modify_depreciation_schedule_for_asset_repairs(self, asset):
|
||||
asset_repairs = frappe.get_all(
|
||||
"Asset Repair", filters={"asset": asset.name}, fields=["name", "increase_in_asset_life"]
|
||||
)
|
||||
|
||||
for repair in asset_repairs:
|
||||
if repair.increase_in_asset_life:
|
||||
asset_repair = frappe.get_doc("Asset Repair", repair.name)
|
||||
asset_repair.modify_depreciation_schedule()
|
||||
asset.prepare_depreciation_data()
|
||||
|
||||
def reverse_depreciation_entry_made_after_sale(self, asset):
|
||||
from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry
|
||||
|
||||
posting_date_of_original_invoice = self.get_posting_date_of_sales_invoice()
|
||||
|
||||
row = -1
|
||||
finance_book = asset.get("schedules")[0].get("finance_book")
|
||||
for schedule in asset.get("schedules"):
|
||||
if schedule.finance_book != finance_book:
|
||||
row = 0
|
||||
finance_book = schedule.finance_book
|
||||
else:
|
||||
row += 1
|
||||
|
||||
if schedule.schedule_date == posting_date_of_original_invoice:
|
||||
if not self.sale_was_made_on_original_schedule_date(
|
||||
asset, schedule, row, posting_date_of_original_invoice
|
||||
) or self.sale_happens_in_the_future(posting_date_of_original_invoice):
|
||||
|
||||
reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry)
|
||||
reverse_journal_entry.posting_date = nowdate()
|
||||
frappe.flags.is_reverse_depr_entry = True
|
||||
reverse_journal_entry.submit()
|
||||
|
||||
frappe.flags.is_reverse_depr_entry = False
|
||||
asset.flags.ignore_validate_update_after_submit = True
|
||||
schedule.journal_entry = None
|
||||
depreciation_amount = self.get_depreciation_amount_in_je(reverse_journal_entry)
|
||||
asset.finance_books[0].value_after_depreciation += depreciation_amount
|
||||
asset.save()
|
||||
|
||||
def get_posting_date_of_sales_invoice(self):
|
||||
return frappe.db.get_value("Sales Invoice", self.return_against, "posting_date")
|
||||
|
||||
# if the invoice had been posted on the date the depreciation was initially supposed to happen, the depreciation shouldn't be undone
|
||||
def sale_was_made_on_original_schedule_date(
|
||||
self, asset, schedule, row, posting_date_of_original_invoice
|
||||
):
|
||||
for finance_book in asset.get("finance_books"):
|
||||
if schedule.finance_book == finance_book.finance_book:
|
||||
orginal_schedule_date = add_months(
|
||||
finance_book.depreciation_start_date, row * cint(finance_book.frequency_of_depreciation)
|
||||
)
|
||||
|
||||
if orginal_schedule_date == posting_date_of_original_invoice:
|
||||
return True
|
||||
return False
|
||||
|
||||
def sale_happens_in_the_future(self, posting_date_of_original_invoice):
|
||||
if posting_date_of_original_invoice > getdate():
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def get_depreciation_amount_in_je(self, journal_entry):
|
||||
if journal_entry.accounts[0].debit_in_account_currency:
|
||||
return journal_entry.accounts[0].debit_in_account_currency
|
||||
else:
|
||||
return journal_entry.accounts[0].credit_in_account_currency
|
||||
|
||||
@property
|
||||
def enable_discount_accounting(self):
|
||||
if not hasattr(self, "_enable_discount_accounting"):
|
||||
@@ -1416,7 +1393,11 @@ class SalesInvoice(SellingController):
|
||||
|
||||
def make_write_off_gl_entry(self, gl_entries):
|
||||
# write off entries, applicable if only pos
|
||||
if self.write_off_account and flt(self.write_off_amount, self.precision("write_off_amount")):
|
||||
if (
|
||||
self.is_pos
|
||||
and self.write_off_account
|
||||
and flt(self.write_off_amount, self.precision("write_off_amount"))
|
||||
):
|
||||
write_off_account_currency = get_account_currency(self.write_off_account)
|
||||
default_cost_center = frappe.get_cached_value("Company", self.company, "cost_center")
|
||||
|
||||
@@ -2103,13 +2084,13 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
|
||||
target_detail_field = "sales_invoice_item" if doctype == "Sales Invoice" else "sales_order_item"
|
||||
source_document_warehouse_field = "target_warehouse"
|
||||
target_document_warehouse_field = "from_warehouse"
|
||||
received_items = get_received_items(source_name, target_doctype, target_detail_field)
|
||||
else:
|
||||
source_doc = frappe.get_doc(doctype, source_name)
|
||||
target_doctype = "Sales Invoice" if doctype == "Purchase Invoice" else "Sales Order"
|
||||
source_document_warehouse_field = "from_warehouse"
|
||||
target_document_warehouse_field = "target_warehouse"
|
||||
|
||||
received_items = get_received_items(source_name, target_doctype, target_detail_field)
|
||||
received_items = {}
|
||||
|
||||
validate_inter_company_transaction(source_doc, doctype)
|
||||
details = get_inter_company_details(source_doc, doctype)
|
||||
@@ -2133,6 +2114,9 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
|
||||
update_address(
|
||||
target_doc, "shipping_address", "shipping_address_display", source_doc.customer_address
|
||||
)
|
||||
update_address(
|
||||
target_doc, "billing_address", "billing_address_display", source_doc.customer_address
|
||||
)
|
||||
|
||||
if currency:
|
||||
target_doc.currency = currency
|
||||
@@ -2177,6 +2161,17 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
|
||||
|
||||
def update_item(source, target, source_parent):
|
||||
target.qty = flt(source.qty) - received_items.get(source.name, 0.0)
|
||||
if source.doctype == "Purchase Order Item" and target.doctype == "Sales Order Item":
|
||||
target.purchase_order = source.parent
|
||||
target.purchase_order_item = source.name
|
||||
|
||||
if (
|
||||
source.get("purchase_order")
|
||||
and source.get("purchase_order_item")
|
||||
and target.doctype == "Purchase Invoice Item"
|
||||
):
|
||||
target.purchase_order = source.purchase_order
|
||||
target.po_detail = source.purchase_order_item
|
||||
|
||||
item_field_map = {
|
||||
"doctype": target_doctype + " Item",
|
||||
@@ -2203,6 +2198,12 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
|
||||
"serial_no": "serial_no",
|
||||
}
|
||||
)
|
||||
elif target_doctype == "Sales Order":
|
||||
item_field_map["field_map"].update(
|
||||
{
|
||||
source_document_warehouse_field: "warehouse",
|
||||
}
|
||||
)
|
||||
|
||||
doclist = get_mapped_doc(
|
||||
doctype,
|
||||
@@ -2247,6 +2248,7 @@ def get_received_items(reference_name, doctype, reference_fieldname):
|
||||
|
||||
def set_purchase_references(doc):
|
||||
# add internal PO or PR links if any
|
||||
|
||||
if doc.is_internal_transfer():
|
||||
if doc.doctype == "Purchase Receipt":
|
||||
so_item_map = get_delivery_note_details(doc.inter_company_invoice_reference)
|
||||
@@ -2276,15 +2278,6 @@ def set_purchase_references(doc):
|
||||
warehouse_map,
|
||||
)
|
||||
|
||||
if list(so_item_map.values()):
|
||||
pd_item_map, parent_child_map, warehouse_map = get_pd_details(
|
||||
"Purchase Order Item", so_item_map, "sales_order_item"
|
||||
)
|
||||
|
||||
update_pi_items(
|
||||
doc, "po_detail", "purchase_order", so_item_map, pd_item_map, parent_child_map, warehouse_map
|
||||
)
|
||||
|
||||
|
||||
def update_pi_items(
|
||||
doc,
|
||||
@@ -2300,13 +2293,19 @@ def update_pi_items(
|
||||
item.set(parent_field, parent_child_map.get(sales_item_map.get(item.sales_invoice_item)))
|
||||
if doc.update_stock:
|
||||
item.warehouse = warehouse_map.get(sales_item_map.get(item.sales_invoice_item))
|
||||
if not item.warehouse and item.get("purchase_order") and item.get("purchase_order_item"):
|
||||
item.warehouse = frappe.db.get_value(
|
||||
"Purchase Order Item", item.purchase_order_item, "warehouse"
|
||||
)
|
||||
|
||||
|
||||
def update_pr_items(doc, sales_item_map, purchase_item_map, parent_child_map, warehouse_map):
|
||||
for item in doc.get("items"):
|
||||
item.purchase_order_item = purchase_item_map.get(sales_item_map.get(item.delivery_note_item))
|
||||
item.warehouse = warehouse_map.get(sales_item_map.get(item.delivery_note_item))
|
||||
item.purchase_order = parent_child_map.get(sales_item_map.get(item.delivery_note_item))
|
||||
if not item.warehouse and item.get("purchase_order") and item.get("purchase_order_item"):
|
||||
item.warehouse = frappe.db.get_value(
|
||||
"Purchase Order Item", item.purchase_order_item, "warehouse"
|
||||
)
|
||||
|
||||
|
||||
def get_delivery_note_details(internal_reference):
|
||||
@@ -2404,7 +2403,7 @@ def get_loyalty_programs(customer):
|
||||
lp_details = get_loyalty_programs(customer)
|
||||
|
||||
if len(lp_details) == 1:
|
||||
frappe.db.set(customer, "loyalty_program", lp_details[0])
|
||||
customer.db_set("loyalty_program", lp_details[0])
|
||||
return lp_details
|
||||
else:
|
||||
return lp_details
|
||||
@@ -2535,7 +2534,6 @@ def create_dunning(source_name, target_doc=None):
|
||||
target.closing_text = letter_text.get("closing_text")
|
||||
target.language = letter_text.get("language")
|
||||
amounts = calculate_interest_and_amount(
|
||||
target.posting_date,
|
||||
target.outstanding_amount,
|
||||
target.rate_of_interest,
|
||||
target.dunning_fee,
|
||||
|
||||
@@ -8,7 +8,7 @@ import frappe
|
||||
from frappe.model.dynamic_links import get_dynamic_link_map
|
||||
from frappe.model.naming import make_autoname
|
||||
from frappe.tests.utils import change_settings
|
||||
from frappe.utils import add_days, flt, getdate, nowdate
|
||||
from frappe.utils import add_days, flt, getdate, nowdate, today
|
||||
|
||||
import erpnext
|
||||
from erpnext.accounts.doctype.account.test_account import create_account, get_inventory_account
|
||||
@@ -32,10 +32,20 @@ from erpnext.stock.doctype.stock_entry.test_stock_entry import (
|
||||
get_qty_after_transaction,
|
||||
make_stock_entry,
|
||||
)
|
||||
from erpnext.stock.utils import get_incoming_rate
|
||||
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
|
||||
create_stock_reconciliation,
|
||||
)
|
||||
from erpnext.stock.utils import get_incoming_rate, get_stock_balance
|
||||
|
||||
|
||||
class TestSalesInvoice(unittest.TestCase):
|
||||
def setUp(self):
|
||||
from erpnext.stock.doctype.stock_ledger_entry.test_stock_ledger_entry import create_items
|
||||
|
||||
create_items(["_Test Internal Transfer Item"], uoms=[{"uom": "Box", "conversion_factor": 10}])
|
||||
create_internal_parties()
|
||||
setup_accounts()
|
||||
|
||||
def make(self):
|
||||
w = frappe.copy_doc(test_records[0])
|
||||
w.is_pos = 0
|
||||
@@ -955,7 +965,8 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
pos_return.insert()
|
||||
pos_return.submit()
|
||||
|
||||
self.assertEqual(pos_return.get("payments")[0].amount, -1000)
|
||||
self.assertEqual(pos_return.get("payments")[0].amount, -500)
|
||||
self.assertEqual(pos_return.get("payments")[1].amount, -500)
|
||||
|
||||
def test_pos_change_amount(self):
|
||||
make_pos_profile(
|
||||
@@ -1705,7 +1716,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
si.save()
|
||||
self.assertEqual(si.get("items")[0].rate, flt((price_list_rate * 25) / 100 + price_list_rate))
|
||||
|
||||
def test_outstanding_amount_after_advance_jv_cancelation(self):
|
||||
def test_outstanding_amount_after_advance_jv_cancellation(self):
|
||||
from erpnext.accounts.doctype.journal_entry.test_journal_entry import (
|
||||
test_records as jv_test_records,
|
||||
)
|
||||
@@ -1749,7 +1760,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
flt(si.rounded_total + si.total_advance, si.precision("outstanding_amount")),
|
||||
)
|
||||
|
||||
def test_outstanding_amount_after_advance_payment_entry_cancelation(self):
|
||||
def test_outstanding_amount_after_advance_payment_entry_cancellation(self):
|
||||
pe = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Payment Entry",
|
||||
@@ -2367,29 +2378,6 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
acc_settings.save()
|
||||
|
||||
def test_inter_company_transaction(self):
|
||||
from erpnext.selling.doctype.customer.test_customer import create_internal_customer
|
||||
|
||||
create_internal_customer(
|
||||
customer_name="_Test Internal Customer",
|
||||
represents_company="_Test Company 1",
|
||||
allowed_to_interact_with="Wind Power LLC",
|
||||
)
|
||||
|
||||
if not frappe.db.exists("Supplier", "_Test Internal Supplier"):
|
||||
supplier = frappe.get_doc(
|
||||
{
|
||||
"supplier_group": "_Test Supplier Group",
|
||||
"supplier_name": "_Test Internal Supplier",
|
||||
"doctype": "Supplier",
|
||||
"is_internal_supplier": 1,
|
||||
"represents_company": "Wind Power LLC",
|
||||
}
|
||||
)
|
||||
|
||||
supplier.append("companies", {"company": "_Test Company 1"})
|
||||
|
||||
supplier.insert()
|
||||
|
||||
si = create_sales_invoice(
|
||||
company="Wind Power LLC",
|
||||
customer="_Test Internal Customer",
|
||||
@@ -2440,38 +2428,6 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
"Expenses Included In Valuation - _TC1",
|
||||
)
|
||||
|
||||
if not frappe.db.exists("Customer", "_Test Internal Customer"):
|
||||
customer = frappe.get_doc(
|
||||
{
|
||||
"customer_group": "_Test Customer Group",
|
||||
"customer_name": "_Test Internal Customer",
|
||||
"customer_type": "Individual",
|
||||
"doctype": "Customer",
|
||||
"territory": "_Test Territory",
|
||||
"is_internal_customer": 1,
|
||||
"represents_company": "_Test Company 1",
|
||||
}
|
||||
)
|
||||
|
||||
customer.append("companies", {"company": "Wind Power LLC"})
|
||||
|
||||
customer.insert()
|
||||
|
||||
if not frappe.db.exists("Supplier", "_Test Internal Supplier"):
|
||||
supplier = frappe.get_doc(
|
||||
{
|
||||
"supplier_group": "_Test Supplier Group",
|
||||
"supplier_name": "_Test Internal Supplier",
|
||||
"doctype": "Supplier",
|
||||
"is_internal_supplier": 1,
|
||||
"represents_company": "Wind Power LLC",
|
||||
}
|
||||
)
|
||||
|
||||
supplier.append("companies", {"company": "_Test Company 1"})
|
||||
|
||||
supplier.insert()
|
||||
|
||||
# begin test
|
||||
si = create_sales_invoice(
|
||||
company="Wind Power LLC",
|
||||
@@ -2541,34 +2497,9 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
se.cancel()
|
||||
|
||||
def test_internal_transfer_gl_entry(self):
|
||||
## Create internal transfer account
|
||||
from erpnext.selling.doctype.customer.test_customer import create_internal_customer
|
||||
|
||||
account = create_account(
|
||||
account_name="Unrealized Profit",
|
||||
parent_account="Current Liabilities - TCP1",
|
||||
company="_Test Company with perpetual inventory",
|
||||
)
|
||||
|
||||
frappe.db.set_value(
|
||||
"Company", "_Test Company with perpetual inventory", "unrealized_profit_loss_account", account
|
||||
)
|
||||
|
||||
customer = create_internal_customer(
|
||||
"_Test Internal Customer 2",
|
||||
"_Test Company with perpetual inventory",
|
||||
"_Test Company with perpetual inventory",
|
||||
)
|
||||
|
||||
create_internal_supplier(
|
||||
"_Test Internal Supplier 2",
|
||||
"_Test Company with perpetual inventory",
|
||||
"_Test Company with perpetual inventory",
|
||||
)
|
||||
|
||||
si = create_sales_invoice(
|
||||
company="_Test Company with perpetual inventory",
|
||||
customer=customer,
|
||||
customer="_Test Internal Customer 2",
|
||||
debit_to="Debtors - TCP1",
|
||||
warehouse="Stores - TCP1",
|
||||
income_account="Sales - TCP1",
|
||||
@@ -2582,7 +2513,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
si.update_stock = 1
|
||||
si.items[0].target_warehouse = "Work In Progress - TCP1"
|
||||
|
||||
# Add stock to stores for succesful stock transfer
|
||||
# Add stock to stores for successful stock transfer
|
||||
make_stock_entry(
|
||||
target="Stores - TCP1", company="_Test Company with perpetual inventory", qty=1, basic_rate=100
|
||||
)
|
||||
@@ -2638,6 +2569,77 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
check_gl_entries(self, target_doc.name, pi_gl_entries, add_days(nowdate(), -1))
|
||||
|
||||
def test_internal_transfer_gl_precision_issues(self):
|
||||
# Make a stock queue of an item with two valuations
|
||||
|
||||
# Remove all existing stock for this
|
||||
if get_stock_balance("_Test Internal Transfer Item", "Stores - TCP1", "2022-04-10"):
|
||||
create_stock_reconciliation(
|
||||
item_code="_Test Internal Transfer Item",
|
||||
warehouse="Stores - TCP1",
|
||||
qty=0,
|
||||
rate=0,
|
||||
company="_Test Company with perpetual inventory",
|
||||
expense_account="Stock Adjustment - TCP1"
|
||||
if frappe.get_all("Stock Ledger Entry")
|
||||
else "Temporary Opening - TCP1",
|
||||
posting_date="2020-04-10",
|
||||
posting_time="14:00",
|
||||
)
|
||||
|
||||
make_stock_entry(
|
||||
item_code="_Test Internal Transfer Item",
|
||||
target="Stores - TCP1",
|
||||
qty=9000000,
|
||||
basic_rate=52.0,
|
||||
posting_date="2020-04-10",
|
||||
posting_time="14:00",
|
||||
)
|
||||
make_stock_entry(
|
||||
item_code="_Test Internal Transfer Item",
|
||||
target="Stores - TCP1",
|
||||
qty=60000000,
|
||||
basic_rate=52.349777,
|
||||
posting_date="2020-04-10",
|
||||
posting_time="14:00",
|
||||
)
|
||||
|
||||
# Make an internal transfer Sales Invoice Stock in non stock uom to check
|
||||
# for rounding errors while converting to stock uom
|
||||
si = create_sales_invoice(
|
||||
company="_Test Company with perpetual inventory",
|
||||
customer="_Test Internal Customer 2",
|
||||
item_code="_Test Internal Transfer Item",
|
||||
qty=5000000,
|
||||
uom="Box",
|
||||
debit_to="Debtors - TCP1",
|
||||
warehouse="Stores - TCP1",
|
||||
income_account="Sales - TCP1",
|
||||
expense_account="Cost of Goods Sold - TCP1",
|
||||
cost_center="Main - TCP1",
|
||||
currency="INR",
|
||||
do_not_save=1,
|
||||
)
|
||||
|
||||
# Check GL Entries with precision
|
||||
si.update_stock = 1
|
||||
si.items[0].target_warehouse = "Work In Progress - TCP1"
|
||||
si.items[0].conversion_factor = 10
|
||||
si.save()
|
||||
si.submit()
|
||||
|
||||
# Check if adjustment entry is created
|
||||
self.assertTrue(
|
||||
frappe.db.exists(
|
||||
"GL Entry",
|
||||
{
|
||||
"voucher_type": "Sales Invoice",
|
||||
"voucher_no": si.name,
|
||||
"remarks": "Rounding gain/loss Entry for Stock Transfer",
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
def test_item_tax_net_range(self):
|
||||
item = create_item("T Shirt")
|
||||
|
||||
@@ -2727,6 +2729,31 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1))
|
||||
|
||||
# Update Invoice post submit and then check GL Entries again
|
||||
|
||||
si.load_from_db()
|
||||
si.items[0].income_account = "Service - _TC"
|
||||
si.additional_discount_account = "_Test Account Sales - _TC"
|
||||
si.taxes[0].account_head = "TDS Payable - _TC"
|
||||
si.save()
|
||||
|
||||
si.load_from_db()
|
||||
self.assertTrue(si.repost_required)
|
||||
|
||||
si.repost_accounting_entries()
|
||||
|
||||
expected_gle = [
|
||||
["_Test Account Sales - _TC", 22.0, 0.0, nowdate()],
|
||||
["Debtors - _TC", 88, 0.0, nowdate()],
|
||||
["Service - _TC", 0.0, 100.0, nowdate()],
|
||||
["TDS Payable - _TC", 0.0, 10.0, nowdate()],
|
||||
]
|
||||
|
||||
check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1))
|
||||
|
||||
si.load_from_db()
|
||||
self.assertFalse(si.repost_required)
|
||||
|
||||
def test_asset_depreciation_on_sale_with_pro_rata(self):
|
||||
"""
|
||||
Tests if an Asset set to depreciate yearly on June 30, that gets sold on Sept 30, creates an additional depreciation entry on its date of sale.
|
||||
@@ -3077,7 +3104,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
[deferred_account, 2022.47, 0.0, "2019-03-15"],
|
||||
]
|
||||
|
||||
gl_entries = gl_entries = frappe.db.sql(
|
||||
gl_entries = frappe.db.sql(
|
||||
"""select account, debit, credit, posting_date
|
||||
from `tabGL Entry`
|
||||
where voucher_type='Journal Entry' and voucher_detail_no=%s and posting_date <= %s
|
||||
@@ -3196,6 +3223,53 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
"Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice", unlink_enabled
|
||||
)
|
||||
|
||||
def test_batch_expiry_for_sales_invoice_return(self):
|
||||
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
|
||||
item = make_item(
|
||||
"_Test Batch Item For Return Check",
|
||||
{
|
||||
"is_purchase_item": 1,
|
||||
"is_stock_item": 1,
|
||||
"has_batch_no": 1,
|
||||
"create_new_batch": 1,
|
||||
"batch_number_series": "TBIRC.#####",
|
||||
},
|
||||
)
|
||||
|
||||
pr = make_purchase_receipt(qty=1, item_code=item.name)
|
||||
|
||||
batch_no = pr.items[0].batch_no
|
||||
si = create_sales_invoice(qty=1, item_code=item.name, update_stock=1, batch_no=batch_no)
|
||||
|
||||
si.load_from_db()
|
||||
batch_no = si.items[0].batch_no
|
||||
self.assertTrue(batch_no)
|
||||
|
||||
frappe.db.set_value("Batch", batch_no, "expiry_date", add_days(today(), -1))
|
||||
|
||||
return_si = make_return_doc(si.doctype, si.name)
|
||||
return_si.save().submit()
|
||||
|
||||
self.assertTrue(return_si.docstatus == 1)
|
||||
|
||||
def test_sales_invoice_with_payable_tax_account(self):
|
||||
si = create_sales_invoice(do_not_submit=True)
|
||||
si.append(
|
||||
"taxes",
|
||||
{
|
||||
"charge_type": "Actual",
|
||||
"account_head": "Creditors - _TC",
|
||||
"description": "Test",
|
||||
"cost_center": "Main - _TC",
|
||||
"tax_amount": 10,
|
||||
"total": 10,
|
||||
"dont_recompute_tax": 0,
|
||||
},
|
||||
)
|
||||
self.assertRaises(frappe.ValidationError, si.submit)
|
||||
|
||||
|
||||
def get_sales_invoice_for_e_invoice():
|
||||
si = make_sales_invoice_for_ewaybill()
|
||||
@@ -3237,6 +3311,7 @@ def check_gl_entries(doc, voucher_no, expected_gle, posting_date):
|
||||
"""select account, debit, credit, posting_date
|
||||
from `tabGL Entry`
|
||||
where voucher_type='Sales Invoice' and voucher_no=%s and posting_date > %s
|
||||
and is_cancelled = 0
|
||||
order by posting_date asc, account asc""",
|
||||
(voucher_no, posting_date),
|
||||
as_dict=1,
|
||||
@@ -3275,6 +3350,7 @@ def create_sales_invoice(**args):
|
||||
"item_name": args.item_name or "_Test Item",
|
||||
"description": args.description or "_Test Item",
|
||||
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
||||
"target_warehouse": args.target_warehouse,
|
||||
"qty": args.qty or 1,
|
||||
"uom": args.uom or "Nos",
|
||||
"stock_uom": args.uom or "Nos",
|
||||
@@ -3287,8 +3363,9 @@ def create_sales_invoice(**args):
|
||||
"asset": args.asset or None,
|
||||
"cost_center": args.cost_center or "_Test Cost Center - _TC",
|
||||
"serial_no": args.serial_no,
|
||||
"conversion_factor": 1,
|
||||
"conversion_factor": args.get("conversion_factor", 1),
|
||||
"incoming_rate": args.incoming_rate or 0,
|
||||
"batch_no": args.batch_no or None,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -3399,6 +3476,34 @@ def get_taxes_and_charges():
|
||||
]
|
||||
|
||||
|
||||
def create_internal_parties():
|
||||
from erpnext.selling.doctype.customer.test_customer import create_internal_customer
|
||||
|
||||
create_internal_customer(
|
||||
customer_name="_Test Internal Customer",
|
||||
represents_company="_Test Company 1",
|
||||
allowed_to_interact_with="Wind Power LLC",
|
||||
)
|
||||
|
||||
create_internal_customer(
|
||||
customer_name="_Test Internal Customer 2",
|
||||
represents_company="_Test Company with perpetual inventory",
|
||||
allowed_to_interact_with="_Test Company with perpetual inventory",
|
||||
)
|
||||
|
||||
create_internal_supplier(
|
||||
supplier_name="_Test Internal Supplier",
|
||||
represents_company="Wind Power LLC",
|
||||
allowed_to_interact_with="_Test Company 1",
|
||||
)
|
||||
|
||||
create_internal_supplier(
|
||||
supplier_name="_Test Internal Supplier 2",
|
||||
represents_company="_Test Company with perpetual inventory",
|
||||
allowed_to_interact_with="_Test Company with perpetual inventory",
|
||||
)
|
||||
|
||||
|
||||
def create_internal_supplier(supplier_name, represents_company, allowed_to_interact_with):
|
||||
if not frappe.db.exists("Supplier", supplier_name):
|
||||
supplier = frappe.get_doc(
|
||||
@@ -3421,6 +3526,19 @@ def create_internal_supplier(supplier_name, represents_company, allowed_to_inter
|
||||
return supplier_name
|
||||
|
||||
|
||||
def setup_accounts():
|
||||
## Create internal transfer account
|
||||
account = create_account(
|
||||
account_name="Unrealized Profit",
|
||||
parent_account="Current Liabilities - TCP1",
|
||||
company="_Test Company with perpetual inventory",
|
||||
)
|
||||
|
||||
frappe.db.set_value(
|
||||
"Company", "_Test Company with perpetual inventory", "unrealized_profit_loss_account", account
|
||||
)
|
||||
|
||||
|
||||
def add_taxes(doc):
|
||||
doc.append(
|
||||
"taxes",
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"barcode",
|
||||
"has_item_scanned",
|
||||
"item_code",
|
||||
"col_break1",
|
||||
"item_name",
|
||||
@@ -96,6 +97,10 @@
|
||||
"delivery_note",
|
||||
"dn_detail",
|
||||
"delivered_qty",
|
||||
"internal_transfer_section",
|
||||
"purchase_order",
|
||||
"column_break_92",
|
||||
"purchase_order_item",
|
||||
"accounting_dimensions_section",
|
||||
"cost_center",
|
||||
"dimension_col_break",
|
||||
@@ -243,6 +248,7 @@
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "eval: doc.margin_type || doc.discount_amount",
|
||||
"fieldname": "discount_and_margin",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Discount and Margin"
|
||||
@@ -282,7 +288,6 @@
|
||||
"label": "Discount (%) on Price List Rate with Margin",
|
||||
"oldfieldname": "adj_rate",
|
||||
"oldfieldtype": "Float",
|
||||
"precision": "2",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
@@ -433,6 +438,7 @@
|
||||
"label": "Accounting Details"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "income_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Income Account",
|
||||
@@ -445,6 +451,7 @@
|
||||
"width": "120px"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "expense_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Expense Account",
|
||||
@@ -464,6 +471,7 @@
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"default": ":Company",
|
||||
"fieldname": "cost_center",
|
||||
"fieldtype": "Link",
|
||||
@@ -795,6 +803,7 @@
|
||||
"options": "Finance Book"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "project",
|
||||
"fieldtype": "Link",
|
||||
"label": "Project",
|
||||
@@ -829,6 +838,7 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "discount_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Discount Account",
|
||||
@@ -841,12 +851,46 @@
|
||||
"fieldtype": "Check",
|
||||
"label": "Grant Commission",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"depends_on": "eval:parent.is_internal_customer == 1",
|
||||
"fieldname": "internal_transfer_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Internal Transfer"
|
||||
},
|
||||
{
|
||||
"fieldname": "purchase_order",
|
||||
"fieldtype": "Link",
|
||||
"label": "Purchase Order",
|
||||
"options": "Purchase Order",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_92",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "purchase_order_item",
|
||||
"fieldtype": "Data",
|
||||
"label": "Purchase Order Item",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "barcode",
|
||||
"fieldname": "has_item_scanned",
|
||||
"fieldtype": "Check",
|
||||
"label": "Has Item Scanned",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-06-17 05:33:15.335912",
|
||||
"modified": "2022-11-02 12:53:12.693217",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice Item",
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
"oldfieldtype": "Data"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"columns": 2,
|
||||
"fieldname": "account_head",
|
||||
"fieldtype": "Link",
|
||||
@@ -63,6 +64,7 @@
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"default": ":Company",
|
||||
"fieldname": "cost_center",
|
||||
"fieldtype": "Link",
|
||||
@@ -216,12 +218,13 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-08-05 20:04:01.726867",
|
||||
"modified": "2022-10-17 13:08:17.776528",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Taxes and Charges",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "ASC"
|
||||
"sort_order": "ASC",
|
||||
"states": []
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
|
||||
import frappe
|
||||
from dateutil import relativedelta
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import date_diff, flt, get_first_day, get_last_day, getdate
|
||||
@@ -49,7 +50,7 @@ def get_plan_rate(
|
||||
start_date = getdate(start_date)
|
||||
end_date = getdate(end_date)
|
||||
|
||||
no_of_months = (end_date.year - start_date.year) * 12 + (end_date.month - start_date.month) + 1
|
||||
no_of_months = relativedelta.relativedelta(end_date, start_date).months + 1
|
||||
cost = plan.cost * no_of_months
|
||||
|
||||
# Adjust cost if start or end date is not month start or end
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"actions": [],
|
||||
"autoname": "autoincrement",
|
||||
"creation": "2022-09-13 16:18:59.404842",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"voucher_type",
|
||||
"voucher_name",
|
||||
"taxable_amount"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "voucher_type",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Voucher Type",
|
||||
"options": "DocType"
|
||||
},
|
||||
{
|
||||
"fieldname": "voucher_name",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Voucher Name",
|
||||
"options": "voucher_type"
|
||||
},
|
||||
{
|
||||
"fieldname": "taxable_amount",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Taxable Amount",
|
||||
"options": "Company:company:default_currency"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-09-13 23:40:41.479208",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Tax Withheld Vouchers",
|
||||
"naming_rule": "Autoincrement",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class TaxWithheldVouchers(Document):
|
||||
pass
|
||||
@@ -61,6 +61,9 @@ def get_party_details(inv):
|
||||
|
||||
|
||||
def get_party_tax_withholding_details(inv, tax_withholding_category=None):
|
||||
if inv.doctype == "Payment Entry":
|
||||
inv.tax_withholding_net_total = inv.net_total
|
||||
|
||||
pan_no = ""
|
||||
parties = []
|
||||
party_type, party = get_party_details(inv)
|
||||
@@ -109,7 +112,7 @@ def get_party_tax_withholding_details(inv, tax_withholding_category=None):
|
||||
).format(tax_withholding_category, inv.company, party)
|
||||
)
|
||||
|
||||
tax_amount, tax_deducted, tax_deducted_on_advances = get_tax_amount(
|
||||
tax_amount, tax_deducted, tax_deducted_on_advances, voucher_wise_amount = get_tax_amount(
|
||||
party_type, parties, inv, tax_details, posting_date, pan_no
|
||||
)
|
||||
|
||||
@@ -119,7 +122,7 @@ def get_party_tax_withholding_details(inv, tax_withholding_category=None):
|
||||
tax_row = get_tax_row_for_tcs(inv, tax_details, tax_amount, tax_deducted)
|
||||
|
||||
if inv.doctype == "Purchase Invoice":
|
||||
return tax_row, tax_deducted_on_advances
|
||||
return tax_row, tax_deducted_on_advances, voucher_wise_amount
|
||||
else:
|
||||
return tax_row
|
||||
|
||||
@@ -217,7 +220,9 @@ def get_lower_deduction_certificate(tax_details, pan_no):
|
||||
|
||||
|
||||
def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=None):
|
||||
vouchers = get_invoice_vouchers(parties, tax_details, inv.company, party_type=party_type)
|
||||
vouchers, voucher_wise_amount = get_invoice_vouchers(
|
||||
parties, tax_details, inv.company, party_type=party_type
|
||||
)
|
||||
advance_vouchers = get_advance_vouchers(
|
||||
parties,
|
||||
company=inv.company,
|
||||
@@ -236,16 +241,20 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
|
||||
tax_deducted = get_deducted_tax(taxable_vouchers, tax_details)
|
||||
|
||||
tax_amount = 0
|
||||
|
||||
if party_type == "Supplier":
|
||||
ldc = get_lower_deduction_certificate(tax_details, pan_no)
|
||||
if tax_deducted:
|
||||
net_total = inv.net_total
|
||||
net_total = inv.tax_withholding_net_total
|
||||
if ldc:
|
||||
tax_amount = get_tds_amount_from_ldc(
|
||||
ldc, parties, pan_no, tax_details, posting_date, net_total
|
||||
)
|
||||
else:
|
||||
tax_amount = net_total * tax_details.rate / 100 if net_total > 0 else 0
|
||||
|
||||
# once tds is deducted, not need to add vouchers in the invoice
|
||||
voucher_wise_amount = {}
|
||||
else:
|
||||
tax_amount = get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers)
|
||||
|
||||
@@ -261,12 +270,18 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
|
||||
if cint(tax_details.round_off_tax_amount):
|
||||
tax_amount = round(tax_amount)
|
||||
|
||||
return tax_amount, tax_deducted, tax_deducted_on_advances
|
||||
return tax_amount, tax_deducted, tax_deducted_on_advances, voucher_wise_amount
|
||||
|
||||
|
||||
def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
|
||||
dr_or_cr = "credit" if party_type == "Supplier" else "debit"
|
||||
doctype = "Purchase Invoice" if party_type == "Supplier" else "Sales Invoice"
|
||||
field = (
|
||||
"base_tax_withholding_net_total as base_net_total"
|
||||
if party_type == "Supplier"
|
||||
else "base_net_total"
|
||||
)
|
||||
voucher_wise_amount = {}
|
||||
vouchers = []
|
||||
|
||||
filters = {
|
||||
"company": company,
|
||||
@@ -281,29 +296,40 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
|
||||
{"apply_tds": 1, "tax_withholding_category": tax_details.get("tax_withholding_category")}
|
||||
)
|
||||
|
||||
invoices = frappe.get_all(doctype, filters=filters, pluck="name") or [""]
|
||||
invoices_details = frappe.get_all(doctype, filters=filters, fields=["name", field])
|
||||
|
||||
journal_entries = frappe.db.sql(
|
||||
for d in invoices_details:
|
||||
vouchers.append(d.name)
|
||||
voucher_wise_amount.update({d.name: {"amount": d.base_net_total, "voucher_type": doctype}})
|
||||
|
||||
journal_entries_details = frappe.db.sql(
|
||||
"""
|
||||
SELECT j.name
|
||||
SELECT j.name, ja.credit - ja.debit AS amount
|
||||
FROM `tabJournal Entry` j, `tabJournal Entry Account` ja
|
||||
WHERE
|
||||
j.docstatus = 1
|
||||
j.name = ja.parent
|
||||
AND j.docstatus = 1
|
||||
AND j.is_opening = 'No'
|
||||
AND j.posting_date between %s and %s
|
||||
AND ja.{dr_or_cr} > 0
|
||||
AND ja.party in %s
|
||||
""".format(
|
||||
dr_or_cr=dr_or_cr
|
||||
AND j.apply_tds = 1
|
||||
AND j.tax_withholding_category = %s
|
||||
""",
|
||||
(
|
||||
tax_details.from_date,
|
||||
tax_details.to_date,
|
||||
tuple(parties),
|
||||
tax_details.get("tax_withholding_category"),
|
||||
),
|
||||
(tax_details.from_date, tax_details.to_date, tuple(parties)),
|
||||
as_list=1,
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
if journal_entries:
|
||||
journal_entries = journal_entries[0]
|
||||
if journal_entries_details:
|
||||
for d in journal_entries_details:
|
||||
vouchers.append(d.name)
|
||||
voucher_wise_amount.update({d.name: {"amount": d.amount, "voucher_type": "Journal Entry"}})
|
||||
|
||||
return invoices + journal_entries
|
||||
return vouchers, voucher_wise_amount
|
||||
|
||||
|
||||
def get_advance_vouchers(
|
||||
@@ -318,9 +344,11 @@ def get_advance_vouchers(
|
||||
"is_cancelled": 0,
|
||||
"party_type": party_type,
|
||||
"party": ["in", parties],
|
||||
"against_voucher": ["is", "not set"],
|
||||
}
|
||||
|
||||
if party_type == "Customer":
|
||||
filters.update({"against_voucher": ["is", "not set"]})
|
||||
|
||||
if company:
|
||||
filters["company"] = company
|
||||
if from_date and to_date:
|
||||
@@ -330,23 +358,25 @@ def get_advance_vouchers(
|
||||
|
||||
|
||||
def get_taxes_deducted_on_advances_allocated(inv, tax_details):
|
||||
advances = [d.reference_name for d in inv.get("advances")]
|
||||
tax_info = []
|
||||
|
||||
if advances:
|
||||
pe = frappe.qb.DocType("Payment Entry").as_("pe")
|
||||
at = frappe.qb.DocType("Advance Taxes and Charges").as_("at")
|
||||
if inv.get("advances"):
|
||||
advances = [d.reference_name for d in inv.get("advances")]
|
||||
|
||||
tax_info = (
|
||||
frappe.qb.from_(at)
|
||||
.inner_join(pe)
|
||||
.on(pe.name == at.parent)
|
||||
.select(at.parent, at.name, at.tax_amount, at.allocated_amount)
|
||||
.where(pe.tax_withholding_category == tax_details.get("tax_withholding_category"))
|
||||
.where(at.parent.isin(advances))
|
||||
.where(at.account_head == tax_details.account_head)
|
||||
.run(as_dict=True)
|
||||
)
|
||||
if advances:
|
||||
pe = frappe.qb.DocType("Payment Entry").as_("pe")
|
||||
at = frappe.qb.DocType("Advance Taxes and Charges").as_("at")
|
||||
|
||||
tax_info = (
|
||||
frappe.qb.from_(at)
|
||||
.inner_join(pe)
|
||||
.on(pe.name == at.parent)
|
||||
.select(at.parent, at.name, at.tax_amount, at.allocated_amount)
|
||||
.where(pe.tax_withholding_category == tax_details.get("tax_withholding_category"))
|
||||
.where(at.parent.isin(advances))
|
||||
.where(at.account_head == tax_details.account_head)
|
||||
.run(as_dict=True)
|
||||
)
|
||||
|
||||
return tax_info
|
||||
|
||||
@@ -370,7 +400,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers):
|
||||
tds_amount = 0
|
||||
invoice_filters = {"name": ("in", vouchers), "docstatus": 1, "apply_tds": 1}
|
||||
|
||||
field = "sum(net_total)"
|
||||
field = "sum(tax_withholding_net_total)"
|
||||
|
||||
if cint(tax_details.consider_party_ledger_amount):
|
||||
invoice_filters.pop("apply_tds", None)
|
||||
@@ -393,17 +423,12 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers):
|
||||
)
|
||||
|
||||
supp_credit_amt += supp_jv_credit_amt
|
||||
supp_credit_amt += inv.net_total
|
||||
|
||||
debit_note_amount = get_debit_note_amount(
|
||||
parties, tax_details.from_date, tax_details.to_date, inv.company
|
||||
)
|
||||
supp_credit_amt -= debit_note_amount
|
||||
supp_credit_amt += inv.tax_withholding_net_total
|
||||
|
||||
threshold = tax_details.get("threshold", 0)
|
||||
cumulative_threshold = tax_details.get("cumulative_threshold", 0)
|
||||
|
||||
if (threshold and inv.net_total >= threshold) or (
|
||||
if (threshold and inv.tax_withholding_net_total >= threshold) or (
|
||||
cumulative_threshold and supp_credit_amt >= cumulative_threshold
|
||||
):
|
||||
if (cumulative_threshold and supp_credit_amt >= cumulative_threshold) and cint(
|
||||
@@ -411,8 +436,11 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers):
|
||||
):
|
||||
# Get net total again as TDS is calculated on net total
|
||||
# Grand is used to just check for threshold breach
|
||||
net_total = frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(net_total)") or 0.0
|
||||
net_total += inv.net_total
|
||||
net_total = (
|
||||
frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(tax_withholding_net_total)")
|
||||
or 0.0
|
||||
)
|
||||
net_total += inv.tax_withholding_net_total
|
||||
supp_credit_amt = net_total - cumulative_threshold
|
||||
|
||||
if ldc and is_valid_certificate(
|
||||
@@ -420,7 +448,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers):
|
||||
ldc.valid_upto,
|
||||
inv.get("posting_date") or inv.get("transaction_date"),
|
||||
tax_deducted,
|
||||
inv.net_total,
|
||||
inv.tax_withholding_net_total,
|
||||
ldc.certificate_limit,
|
||||
):
|
||||
tds_amount = get_ltds_amount(supp_credit_amt, 0, ldc.certificate_limit, ldc.rate, tax_details)
|
||||
@@ -503,7 +531,7 @@ def get_tds_amount_from_ldc(ldc, parties, pan_no, tax_details, posting_date, net
|
||||
limit_consumed = frappe.db.get_value(
|
||||
"Purchase Invoice",
|
||||
{"supplier": ("in", parties), "apply_tds": 1, "docstatus": 1},
|
||||
"sum(net_total)",
|
||||
"sum(tax_withholding_net_total)",
|
||||
)
|
||||
|
||||
if is_valid_certificate(
|
||||
@@ -516,22 +544,6 @@ def get_tds_amount_from_ldc(ldc, parties, pan_no, tax_details, posting_date, net
|
||||
return tds_amount
|
||||
|
||||
|
||||
def get_debit_note_amount(suppliers, from_date, to_date, company=None):
|
||||
|
||||
filters = {
|
||||
"supplier": ["in", suppliers],
|
||||
"is_return": 1,
|
||||
"docstatus": 1,
|
||||
"posting_date": ["between", (from_date, to_date)],
|
||||
}
|
||||
fields = ["abs(sum(net_total)) as net_total"]
|
||||
|
||||
if company:
|
||||
filters["company"] = company
|
||||
|
||||
return frappe.get_all("Purchase Invoice", filters, fields)[0].get("net_total") or 0.0
|
||||
|
||||
|
||||
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
|
||||
|
||||
@@ -52,7 +52,7 @@ class TestTaxWithholdingCategory(unittest.TestCase):
|
||||
invoices.append(pi)
|
||||
|
||||
# delete invoices to avoid clashing
|
||||
for d in invoices:
|
||||
for d in reversed(invoices):
|
||||
d.cancel()
|
||||
|
||||
def test_single_threshold_tds(self):
|
||||
@@ -88,7 +88,7 @@ class TestTaxWithholdingCategory(unittest.TestCase):
|
||||
self.assertEqual(pi.taxes_and_charges_deducted, 1000)
|
||||
|
||||
# delete invoices to avoid clashing
|
||||
for d in invoices:
|
||||
for d in reversed(invoices):
|
||||
d.cancel()
|
||||
|
||||
def test_tax_withholding_category_checks(self):
|
||||
@@ -114,7 +114,7 @@ class TestTaxWithholdingCategory(unittest.TestCase):
|
||||
# TDS should be applied only on 1000
|
||||
self.assertEqual(pi1.taxes[0].tax_amount, 1000)
|
||||
|
||||
for d in invoices:
|
||||
for d in reversed(invoices):
|
||||
d.cancel()
|
||||
|
||||
def test_cumulative_threshold_tcs(self):
|
||||
@@ -148,8 +148,8 @@ class TestTaxWithholdingCategory(unittest.TestCase):
|
||||
self.assertEqual(tcs_charged, 500)
|
||||
invoices.append(si)
|
||||
|
||||
# delete invoices to avoid clashing
|
||||
for d in invoices:
|
||||
# cancel invoices to avoid clashing
|
||||
for d in reversed(invoices):
|
||||
d.cancel()
|
||||
|
||||
def test_tds_calculation_on_net_total(self):
|
||||
@@ -182,8 +182,48 @@ class TestTaxWithholdingCategory(unittest.TestCase):
|
||||
|
||||
self.assertEqual(pi1.taxes[0].tax_amount, 4000)
|
||||
|
||||
# delete invoices to avoid clashing
|
||||
for d in invoices:
|
||||
# cancel invoices to avoid clashing
|
||||
for d in reversed(invoices):
|
||||
d.cancel()
|
||||
|
||||
def test_tds_calculation_on_net_total_partial_tds(self):
|
||||
frappe.db.set_value(
|
||||
"Supplier", "Test TDS Supplier4", "tax_withholding_category", "Cumulative Threshold TDS"
|
||||
)
|
||||
invoices = []
|
||||
|
||||
pi = create_purchase_invoice(supplier="Test TDS Supplier4", rate=20000, do_not_save=True)
|
||||
pi.extend(
|
||||
"items",
|
||||
[
|
||||
{
|
||||
"doctype": "Purchase Invoice Item",
|
||||
"item_code": frappe.db.get_value("Item", {"item_name": "TDS Item"}, "name"),
|
||||
"qty": 1,
|
||||
"rate": 20000,
|
||||
"cost_center": "Main - _TC",
|
||||
"expense_account": "Stock Received But Not Billed - _TC",
|
||||
"apply_tds": 0,
|
||||
},
|
||||
{
|
||||
"doctype": "Purchase Invoice Item",
|
||||
"item_code": frappe.db.get_value("Item", {"item_name": "TDS Item"}, "name"),
|
||||
"qty": 1,
|
||||
"rate": 35000,
|
||||
"cost_center": "Main - _TC",
|
||||
"expense_account": "Stock Received But Not Billed - _TC",
|
||||
"apply_tds": 1,
|
||||
},
|
||||
],
|
||||
)
|
||||
pi.save()
|
||||
pi.submit()
|
||||
invoices.append(pi)
|
||||
|
||||
self.assertEqual(pi.taxes[0].tax_amount, 5500)
|
||||
|
||||
# cancel invoices to avoid clashing
|
||||
for d in reversed(invoices):
|
||||
d.cancel()
|
||||
|
||||
def test_multi_category_single_supplier(self):
|
||||
@@ -207,8 +247,50 @@ class TestTaxWithholdingCategory(unittest.TestCase):
|
||||
|
||||
self.assertEqual(pi1.taxes[0].tax_amount, 250)
|
||||
|
||||
# delete invoices to avoid clashing
|
||||
for d in invoices:
|
||||
# cancel invoices to avoid clashing
|
||||
for d in reversed(invoices):
|
||||
d.cancel()
|
||||
|
||||
def test_tax_withholding_category_voucher_display(self):
|
||||
frappe.db.set_value(
|
||||
"Supplier", "Test TDS Supplier6", "tax_withholding_category", "Test Multi Invoice Category"
|
||||
)
|
||||
invoices = []
|
||||
|
||||
pi = create_purchase_invoice(supplier="Test TDS Supplier6", rate=4000, do_not_save=True)
|
||||
pi.apply_tds = 1
|
||||
pi.tax_withholding_category = "Test Multi Invoice Category"
|
||||
pi.save()
|
||||
pi.submit()
|
||||
invoices.append(pi)
|
||||
|
||||
pi1 = create_purchase_invoice(supplier="Test TDS Supplier6", rate=2000, do_not_save=True)
|
||||
pi1.apply_tds = 1
|
||||
pi1.is_return = 1
|
||||
pi1.items[0].qty = -1
|
||||
pi1.tax_withholding_category = "Test Multi Invoice Category"
|
||||
pi1.save()
|
||||
pi1.submit()
|
||||
invoices.append(pi1)
|
||||
|
||||
pi2 = create_purchase_invoice(supplier="Test TDS Supplier6", rate=9000, do_not_save=True)
|
||||
pi2.apply_tds = 1
|
||||
pi2.tax_withholding_category = "Test Multi Invoice Category"
|
||||
pi2.save()
|
||||
pi2.submit()
|
||||
invoices.append(pi2)
|
||||
|
||||
pi2.load_from_db()
|
||||
|
||||
self.assertTrue(pi2.taxes[0].tax_amount, 1100)
|
||||
|
||||
self.assertTrue(pi2.tax_withheld_vouchers[0].voucher_name == pi1.name)
|
||||
self.assertTrue(pi2.tax_withheld_vouchers[0].taxable_amount == pi1.net_total)
|
||||
self.assertTrue(pi2.tax_withheld_vouchers[1].voucher_name == pi.name)
|
||||
self.assertTrue(pi2.tax_withheld_vouchers[1].taxable_amount == pi.net_total)
|
||||
|
||||
# cancel invoices to avoid clashing
|
||||
for d in reversed(invoices):
|
||||
d.cancel()
|
||||
|
||||
|
||||
@@ -308,6 +390,7 @@ def create_records():
|
||||
"Test TDS Supplier3",
|
||||
"Test TDS Supplier4",
|
||||
"Test TDS Supplier5",
|
||||
"Test TDS Supplier6",
|
||||
]:
|
||||
if frappe.db.exists("Supplier", name):
|
||||
continue
|
||||
@@ -498,3 +581,22 @@ def create_tax_with_holding_category():
|
||||
"accounts": [{"company": "_Test Company", "account": "TDS - _TC"}],
|
||||
}
|
||||
).insert()
|
||||
|
||||
if not frappe.db.exists("Tax Withholding Category", "Test Multi Invoice Category"):
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Tax Withholding Category",
|
||||
"name": "Test Multi Invoice Category",
|
||||
"category_name": "Test Multi Invoice Category",
|
||||
"rates": [
|
||||
{
|
||||
"from_date": fiscal_year[1],
|
||||
"to_date": fiscal_year[2],
|
||||
"tax_withholding_rate": 10,
|
||||
"single_threshold": 5000,
|
||||
"cumulative_threshold": 10000,
|
||||
}
|
||||
],
|
||||
"accounts": [{"company": "_Test Company", "account": "TDS - _TC"}],
|
||||
}
|
||||
).insert()
|
||||
|
||||
@@ -128,6 +128,12 @@ def distribute_gl_based_on_cost_center_allocation(gl_map, precision=None):
|
||||
new_gl_map = []
|
||||
for d in gl_map:
|
||||
cost_center = d.get("cost_center")
|
||||
|
||||
# Validate budget against main cost center
|
||||
validate_expense_against_budget(
|
||||
d, expense_amount=flt(d.debit, precision) - flt(d.credit, precision)
|
||||
)
|
||||
|
||||
if cost_center and cost_center_allocation.get(cost_center):
|
||||
for sub_cost_center, percentage in cost_center_allocation.get(cost_center, {}).items():
|
||||
gle = copy.deepcopy(d)
|
||||
@@ -489,7 +495,6 @@ def make_reverse_gl_entries(
|
||||
).run(as_dict=1)
|
||||
|
||||
if gl_entries:
|
||||
create_payment_ledger_entry(gl_entries, cancel=1)
|
||||
create_payment_ledger_entry(
|
||||
gl_entries, cancel=1, adv_adj=adv_adj, update_outstanding=update_outstanding
|
||||
)
|
||||
|
||||
@@ -207,7 +207,7 @@ def set_address_details(
|
||||
)
|
||||
|
||||
if company_address:
|
||||
party_details.update({"company_address": company_address})
|
||||
party_details.company_address = company_address
|
||||
else:
|
||||
party_details.update(get_company_address(company))
|
||||
|
||||
@@ -219,12 +219,31 @@ def set_address_details(
|
||||
get_regional_address_details(party_details, doctype, company)
|
||||
|
||||
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"])
|
||||
if shipping_address:
|
||||
party_details.update(
|
||||
get_fetch_values(doctype, "shipping_address", party_details.shipping_address)
|
||||
shipping_address=shipping_address,
|
||||
shipping_address_display=get_address_display(shipping_address),
|
||||
**get_fetch_values(doctype, "shipping_address", shipping_address)
|
||||
)
|
||||
|
||||
if party_details.company_address:
|
||||
# billing address
|
||||
party_details.update(
|
||||
billing_address=party_details.company_address,
|
||||
billing_address_display=(
|
||||
party_details.company_address_display or get_address_display(party_details.company_address)
|
||||
),
|
||||
**get_fetch_values(doctype, "billing_address", party_details.company_address)
|
||||
)
|
||||
|
||||
# shipping address - if not already set
|
||||
if not party_details.shipping_address:
|
||||
party_details.update(
|
||||
shipping_address=party_details.billing_address,
|
||||
shipping_address_display=party_details.billing_address_display,
|
||||
**get_fetch_values(doctype, "shipping_address", party_details.billing_address)
|
||||
)
|
||||
|
||||
get_regional_address_details(party_details, doctype, company)
|
||||
|
||||
return party_details.get(billing_address_field), party_details.shipping_address_name
|
||||
|
||||
@@ -51,6 +51,8 @@ frappe.query_reports["Accounts Payable"] = {
|
||||
} else {
|
||||
frappe.query_report.set_filter_value('tax_id', "");
|
||||
}
|
||||
|
||||
frappe.query_report.refresh();
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -178,6 +178,11 @@ frappe.query_reports["Accounts Receivable"] = {
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "show_remarks",
|
||||
"label": __("Show Remarks"),
|
||||
"fieldtype": "Check",
|
||||
},
|
||||
{
|
||||
"fieldname": "customer_name",
|
||||
"label": __("Customer Name"),
|
||||
|
||||
@@ -119,6 +119,7 @@ class ReceivablePayableReport(object):
|
||||
party_account=ple.account,
|
||||
posting_date=ple.posting_date,
|
||||
account_currency=ple.account_currency,
|
||||
remarks=ple.remarks,
|
||||
invoiced=0.0,
|
||||
paid=0.0,
|
||||
credit_note=0.0,
|
||||
@@ -165,7 +166,7 @@ class ReceivablePayableReport(object):
|
||||
"range4",
|
||||
"range5",
|
||||
"future_amount",
|
||||
"remaining_balance"
|
||||
"remaining_balance",
|
||||
]
|
||||
|
||||
def get_voucher_balance(self, ple):
|
||||
@@ -178,6 +179,11 @@ class ReceivablePayableReport(object):
|
||||
|
||||
key = (ple.against_voucher_type, ple.against_voucher_no, ple.party)
|
||||
row = self.voucher_balance.get(key)
|
||||
|
||||
if not row:
|
||||
# no invoice, this is an invoice / stand-alone payment / credit note
|
||||
row = self.voucher_balance.get((ple.voucher_type, ple.voucher_no, ple.party))
|
||||
|
||||
return row
|
||||
|
||||
def update_voucher_balance(self, ple):
|
||||
@@ -187,7 +193,11 @@ class ReceivablePayableReport(object):
|
||||
if not row:
|
||||
return
|
||||
|
||||
amount = ple.amount
|
||||
# amount in "Party Currency", if its supplied. If not, amount in company currency
|
||||
if self.filters.get(scrub(self.party_type)):
|
||||
amount = ple.amount_in_account_currency
|
||||
else:
|
||||
amount = ple.amount
|
||||
amount_in_account_currency = ple.amount_in_account_currency
|
||||
|
||||
# update voucher
|
||||
@@ -685,9 +695,10 @@ class ReceivablePayableReport(object):
|
||||
ple.party,
|
||||
ple.posting_date,
|
||||
ple.due_date,
|
||||
ple.account_currency.as_("currency"),
|
||||
ple.account_currency,
|
||||
ple.amount,
|
||||
ple.amount_in_account_currency,
|
||||
ple.remarks,
|
||||
)
|
||||
.where(ple.delinked == 0)
|
||||
.where(Criterion.all(self.qb_selection_filter))
|
||||
@@ -722,6 +733,7 @@ class ReceivablePayableReport(object):
|
||||
def prepare_conditions(self):
|
||||
self.qb_selection_filter = []
|
||||
party_type_field = scrub(self.party_type)
|
||||
self.qb_selection_filter.append(self.ple.party_type == self.party_type)
|
||||
|
||||
self.add_common_filters(party_type_field=party_type_field)
|
||||
|
||||
@@ -736,7 +748,7 @@ class ReceivablePayableReport(object):
|
||||
|
||||
self.add_accounting_dimensions_filters()
|
||||
|
||||
def get_cost_center_conditions(self, conditions):
|
||||
def get_cost_center_conditions(self):
|
||||
lft, rgt = frappe.db.get_value("Cost Center", self.filters.cost_center, ["lft", "rgt"])
|
||||
cost_center_list = [
|
||||
center.name
|
||||
@@ -772,7 +784,7 @@ class ReceivablePayableReport(object):
|
||||
def add_customer_filters(
|
||||
self,
|
||||
):
|
||||
self.customter = qb.DocType("Customer")
|
||||
self.customer = qb.DocType("Customer")
|
||||
|
||||
if self.filters.get("customer_group"):
|
||||
self.get_hierarchical_filters("Customer Group", "customer_group")
|
||||
@@ -826,7 +838,7 @@ class ReceivablePayableReport(object):
|
||||
customer = self.customer
|
||||
groups = qb.from_(doc).select(doc.name).where((doc.lft >= lft) & (doc.rgt <= rgt))
|
||||
customers = qb.from_(customer).select(customer.name).where(customer[key].isin(groups))
|
||||
self.qb_selection_filter.append(ple.isin(ple.party.isin(customers)))
|
||||
self.qb_selection_filter.append(ple.party.isin(customers))
|
||||
|
||||
def add_accounting_dimensions_filters(self):
|
||||
accounting_dimensions = get_accounting_dimensions(as_list=False)
|
||||
@@ -965,6 +977,9 @@ class ReceivablePayableReport(object):
|
||||
options="Supplier Group",
|
||||
)
|
||||
|
||||
if self.filters.show_remarks:
|
||||
self.add_column(label=_("Remarks"), fieldname="remarks", fieldtype="Text", width=200),
|
||||
|
||||
def add_column(self, label, fieldname=None, fieldtype="Currency", options=None, width=120):
|
||||
if not fieldname:
|
||||
fieldname = scrub(label)
|
||||
@@ -994,7 +1009,7 @@ class ReceivablePayableReport(object):
|
||||
"{range3}-{range4}".format(
|
||||
range3=cint(self.filters["range3"]) + 1, range4=self.filters["range4"]
|
||||
),
|
||||
"{range4}-{above}".format(range4=cint(self.filters["range4"]) + 1, above=_("Above")),
|
||||
_("{range4}-Above").format(range4=cint(self.filters["range4"]) + 1),
|
||||
]
|
||||
):
|
||||
self.add_column(label=label, fieldname="range" + str(i + 1))
|
||||
|
||||
@@ -1,19 +1,27 @@
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
from frappe.utils import add_days, getdate, today
|
||||
|
||||
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
from erpnext.accounts.report.accounts_receivable.accounts_receivable import execute
|
||||
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||
|
||||
|
||||
class TestAccountsReceivable(unittest.TestCase):
|
||||
def test_accounts_receivable(self):
|
||||
class TestAccountsReceivable(FrappeTestCase):
|
||||
def setUp(self):
|
||||
frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company 2'")
|
||||
frappe.db.sql("delete from `tabSales Order` where company='_Test Company 2'")
|
||||
frappe.db.sql("delete from `tabPayment Entry` where company='_Test Company 2'")
|
||||
frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 2'")
|
||||
frappe.db.sql("delete from `tabPayment Ledger Entry` where company='_Test Company 2'")
|
||||
|
||||
def tearDown(self):
|
||||
frappe.db.rollback()
|
||||
|
||||
def test_accounts_receivable(self):
|
||||
filters = {
|
||||
"company": "_Test Company 2",
|
||||
"based_on_payment_terms": 1,
|
||||
@@ -66,6 +74,50 @@ class TestAccountsReceivable(unittest.TestCase):
|
||||
],
|
||||
)
|
||||
|
||||
def test_payment_againt_po_in_receivable_report(self):
|
||||
"""
|
||||
Payments made against Purchase Order will show up as outstanding amount
|
||||
"""
|
||||
|
||||
so = make_sales_order(
|
||||
company="_Test Company 2",
|
||||
customer="_Test Customer 2",
|
||||
warehouse="Finished Goods - _TC2",
|
||||
currency="EUR",
|
||||
debit_to="Debtors - _TC2",
|
||||
income_account="Sales - _TC2",
|
||||
expense_account="Cost of Goods Sold - _TC2",
|
||||
cost_center="Main - _TC2",
|
||||
)
|
||||
|
||||
pe = get_payment_entry(so.doctype, so.name)
|
||||
pe = pe.save().submit()
|
||||
|
||||
filters = {
|
||||
"company": "_Test Company 2",
|
||||
"based_on_payment_terms": 0,
|
||||
"report_date": today(),
|
||||
"range1": 30,
|
||||
"range2": 60,
|
||||
"range3": 90,
|
||||
"range4": 120,
|
||||
}
|
||||
|
||||
report = execute(filters)
|
||||
|
||||
expected_data_after_payment = [0, 1000, 0, -1000]
|
||||
|
||||
row = report[1][0]
|
||||
self.assertEqual(
|
||||
expected_data_after_payment,
|
||||
[
|
||||
row.invoiced,
|
||||
row.paid,
|
||||
row.credit_note,
|
||||
row.outstanding,
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def make_sales_invoice():
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
@@ -22,8 +22,7 @@ def get_columns():
|
||||
{
|
||||
"label": _("Payment Document Type"),
|
||||
"fieldname": "payment_document_type",
|
||||
"fieldtype": "Link",
|
||||
"options": "Doctype",
|
||||
"fieldtype": "Data",
|
||||
"width": 130,
|
||||
},
|
||||
{
|
||||
@@ -33,15 +32,15 @@ def get_columns():
|
||||
"options": "payment_document_type",
|
||||
"width": 140,
|
||||
},
|
||||
{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 100},
|
||||
{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 120},
|
||||
{"label": _("Cheque/Reference No"), "fieldname": "cheque_no", "width": 120},
|
||||
{"label": _("Clearance Date"), "fieldname": "clearance_date", "fieldtype": "Date", "width": 100},
|
||||
{"label": _("Clearance Date"), "fieldname": "clearance_date", "fieldtype": "Date", "width": 120},
|
||||
{
|
||||
"label": _("Against Account"),
|
||||
"fieldname": "against",
|
||||
"fieldtype": "Link",
|
||||
"options": "Account",
|
||||
"width": 170,
|
||||
"width": 200,
|
||||
},
|
||||
{"label": _("Amount"), "fieldname": "amount", "fieldtype": "Currency", "width": 120},
|
||||
]
|
||||
|
||||
@@ -75,7 +75,7 @@ frappe.query_reports["Budget Variance Report"] = {
|
||||
"formatter": function (value, row, column, data, default_formatter) {
|
||||
value = default_formatter(value, row, column, data);
|
||||
|
||||
if (column.fieldname.includes('variance')) {
|
||||
if (column.fieldname.includes(__("variance"))) {
|
||||
|
||||
if (data[column.fieldname] < 0) {
|
||||
value = "<span style='color:red'>" + value + "</span>";
|
||||
|
||||
@@ -383,8 +383,8 @@ def get_chart_data(filters, columns, data):
|
||||
"data": {
|
||||
"labels": labels,
|
||||
"datasets": [
|
||||
{"name": "Budget", "chartType": "bar", "values": budget_values},
|
||||
{"name": "Actual Expense", "chartType": "bar", "values": actual_values},
|
||||
{"name": _("Budget"), "chartType": "bar", "values": budget_values},
|
||||
{"name": _("Actual Expense"), "chartType": "bar", "values": actual_values},
|
||||
],
|
||||
},
|
||||
"type": "bar",
|
||||
|
||||
@@ -535,7 +535,11 @@ def get_accounts(root_type, companies):
|
||||
):
|
||||
if account.account_name not in added_accounts:
|
||||
accounts.append(account)
|
||||
added_accounts.append(account.account_name)
|
||||
if account.account_number:
|
||||
account_key = account.account_number + "-" + account.account_name
|
||||
else:
|
||||
account_key = account.account_name
|
||||
added_accounts.append(account_key)
|
||||
|
||||
return accounts
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user