mirror of
https://github.com/frappe/erpnext.git
synced 2026-07-24 13:15:03 +00:00
Compare commits
736 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f843d6aa53 | ||
|
|
c6ed332903 | ||
|
|
ad07abeb3f | ||
|
|
6bb2038323 | ||
|
|
3d1df94ad2 | ||
|
|
53aee98103 | ||
|
|
e52bd5c699 | ||
|
|
e1f9148351 | ||
|
|
093fb84377 | ||
|
|
737d305f45 | ||
|
|
193222a336 | ||
|
|
46bea91469 | ||
|
|
f09d472fcd | ||
|
|
4ff1a4361d | ||
|
|
c7c72b76c6 | ||
|
|
6aa3191cad | ||
|
|
1fdfcc00d6 | ||
|
|
af45eceeb2 | ||
|
|
7234330db9 | ||
|
|
2fa9b3a713 | ||
|
|
1aec8f57f2 | ||
|
|
24f350a1a8 | ||
|
|
387e0878b6 | ||
|
|
6c536bb6e0 | ||
|
|
0a061d4082 | ||
|
|
af9d653fb4 | ||
|
|
8969e19594 | ||
|
|
a979fcc25b | ||
|
|
9bd041c14c | ||
|
|
1bb6ef3356 | ||
|
|
5cc5bc246a | ||
|
|
2a0ed798aa | ||
|
|
033d98cd68 | ||
|
|
ec3dc95e6e | ||
|
|
57d86a3bc1 | ||
|
|
265916543d | ||
|
|
f63182df5a | ||
|
|
9d455cf4a6 | ||
|
|
ff9c40fde6 | ||
|
|
f0f20bb434 | ||
|
|
bbfd5a4cc8 | ||
|
|
3ae4a83504 | ||
|
|
1da33f4129 | ||
|
|
b8eff8d141 | ||
|
|
67e07b69dd | ||
|
|
7d262d1a08 | ||
|
|
5f8e8a3c29 | ||
|
|
2169bb7640 | ||
|
|
33754b9dc8 | ||
|
|
bb451360ba | ||
|
|
2cf4168009 | ||
|
|
80c016a1ad | ||
|
|
3619628e60 | ||
|
|
e2e9633ee1 | ||
|
|
abd111331e | ||
|
|
61b9727fa2 | ||
|
|
74643c804d | ||
|
|
e7162a86c6 | ||
|
|
b3cdd91d65 | ||
|
|
dd64cd1512 | ||
|
|
024c78f522 | ||
|
|
e6ec549c81 | ||
|
|
579167d1de | ||
|
|
10604664d4 | ||
|
|
97e96cec51 | ||
|
|
d2593f0af5 | ||
|
|
02fcf569e6 | ||
|
|
9afebd311f | ||
|
|
4f6a25539a | ||
|
|
ec46b8c73b | ||
|
|
a9cfe0c95c | ||
|
|
3d2fa59e6d | ||
|
|
b2eddedfb1 | ||
|
|
b7efef79dc | ||
|
|
ca6ec72ade | ||
|
|
680f2600be | ||
|
|
af9e44f059 | ||
|
|
dde92e0fdb | ||
|
|
d9aa115aaf | ||
|
|
6a48988c76 | ||
|
|
e62da1455c | ||
|
|
45a3580c20 | ||
|
|
9abb8e309e | ||
|
|
c5b6ba7556 | ||
|
|
cf902a1e2d | ||
|
|
4e3df334db | ||
|
|
9067adf141 | ||
|
|
d18eb0e63e | ||
|
|
3adeab45ce | ||
|
|
0fee76f409 | ||
|
|
74963e9711 | ||
|
|
7d8887916e | ||
|
|
e077c0a270 | ||
|
|
517108d10a | ||
|
|
a7094641d4 | ||
|
|
ad94f7516b | ||
|
|
1943d413a4 | ||
|
|
82fbef4163 | ||
|
|
83ad6df229 | ||
|
|
b9ba63b0d8 | ||
|
|
bc248bfaed | ||
|
|
0b31d6f630 | ||
|
|
7e461ce860 | ||
|
|
6946796047 | ||
|
|
6d39066d86 | ||
|
|
b6da70efec | ||
|
|
3068fe43c6 | ||
|
|
7709c640b5 | ||
|
|
8558e26648 | ||
|
|
34a91bd7e6 | ||
|
|
785462b513 | ||
|
|
0d5f8c5050 | ||
|
|
13088173eb | ||
|
|
c10dd29282 | ||
|
|
95da4157a9 | ||
|
|
1411d002f6 | ||
|
|
0f2601a6a6 | ||
|
|
c785ff9874 | ||
|
|
ab9cbc1e2c | ||
|
|
237dc4250a | ||
|
|
71fa045ba4 | ||
|
|
5e5e82a9f6 | ||
|
|
898dafe748 | ||
|
|
1f7e941d68 | ||
|
|
395f44c8b7 | ||
|
|
ea1662c15b | ||
|
|
fd8c856af2 | ||
|
|
672497ba63 | ||
|
|
9c410d8a27 | ||
|
|
8317ff8bb2 | ||
|
|
70eb94280e | ||
|
|
fef3a79bf5 | ||
|
|
9bcf4eb5bc | ||
|
|
1696a8982c | ||
|
|
671fc7b919 | ||
|
|
335815a75d | ||
|
|
b856548d47 | ||
|
|
1c26bf5b07 | ||
|
|
b3041ea7cd | ||
|
|
ea7625556c | ||
|
|
8e887f39a2 | ||
|
|
8f038aa832 | ||
|
|
fda2633e40 | ||
|
|
817040b986 | ||
|
|
45624cae77 | ||
|
|
8c48a6a865 | ||
|
|
adb7c16349 | ||
|
|
aafd674c07 | ||
|
|
75c2e28788 | ||
|
|
321aba642d | ||
|
|
402b273d9b | ||
|
|
e0a9ae612e | ||
|
|
b9e4df557b | ||
|
|
632bd3b310 | ||
|
|
735d7f732d | ||
|
|
455c1dda7a | ||
|
|
1b18bf0388 | ||
|
|
336ca33a98 | ||
|
|
3c0f38fa02 | ||
|
|
fd30b8f422 | ||
|
|
30e33bef9e | ||
|
|
67e64dbc01 | ||
|
|
bae83679c0 | ||
|
|
0c12c7339c | ||
|
|
bf8a26c31e | ||
|
|
fb36d01dd2 | ||
|
|
c8d6e8f19d | ||
|
|
4e17c40589 | ||
|
|
5d5cb0ac28 | ||
|
|
a20957d355 | ||
|
|
f8efa0508a | ||
|
|
6aa7c404f8 | ||
|
|
105a44e0d2 | ||
|
|
7919ced975 | ||
|
|
848e7c2e0e | ||
|
|
104b317929 | ||
|
|
ad0aca2fc6 | ||
|
|
584a8f52a5 | ||
|
|
48e2baa903 | ||
|
|
43d539f321 | ||
|
|
0e7ab9526c | ||
|
|
8a542bc9df | ||
|
|
e73dec158b | ||
|
|
2155916001 | ||
|
|
78383c3133 | ||
|
|
cfc47bc011 | ||
|
|
6bd8c048b6 | ||
|
|
6089023df7 | ||
|
|
3cb8173034 | ||
|
|
d9e7b480eb | ||
|
|
7691937e59 | ||
|
|
99a9f98688 | ||
|
|
7b23cb27ce | ||
|
|
c28adea2d0 | ||
|
|
57804e9d87 | ||
|
|
bf81b42219 | ||
|
|
1a3fd763ac | ||
|
|
0a6b5bc83f | ||
|
|
fb18f78b77 | ||
|
|
835b1b4f0d | ||
|
|
3c18669057 | ||
|
|
57a0fa5e87 | ||
|
|
578bdff141 | ||
|
|
c20b962842 | ||
|
|
5c4b738833 | ||
|
|
8a1f2ed65a | ||
|
|
def13f14a3 | ||
|
|
7e04dcae0a | ||
|
|
21a75eb2ba | ||
|
|
290dbf470a | ||
|
|
171699537c | ||
|
|
91346d5e9e | ||
|
|
06ca3b5112 | ||
|
|
e220be5415 | ||
|
|
413c2b229a | ||
|
|
1039079e37 | ||
|
|
60cb5ac419 | ||
|
|
ebbbc85fc0 | ||
|
|
7f6d36cdc9 | ||
|
|
4a9de4dc07 | ||
|
|
cb555ae825 | ||
|
|
e5ffe3fea4 | ||
|
|
7fcf958d08 | ||
|
|
cfe9ed0dfa | ||
|
|
f2fe8053dc | ||
|
|
aa22cca67a | ||
|
|
c4893df048 | ||
|
|
b16b24fd10 | ||
|
|
2f2385b354 | ||
|
|
8a2a32820a | ||
|
|
41462f3c36 | ||
|
|
a9ff40db67 | ||
|
|
0d95f56474 | ||
|
|
da65ddc68a | ||
|
|
368f5e29d2 | ||
|
|
92f97b6a68 | ||
|
|
7d035fac36 | ||
|
|
6c1a5e8a44 | ||
|
|
995bdd3c48 | ||
|
|
5acc06dfe6 | ||
|
|
168d070d73 | ||
|
|
6f3e5e1891 | ||
|
|
d48e33dbd7 | ||
|
|
7af8ca1bd1 | ||
|
|
a3e92489c2 | ||
|
|
4793e9c24c | ||
|
|
dd8dcd29c3 | ||
|
|
10624f3fa6 | ||
|
|
a0aa16c68c | ||
|
|
67361bef73 | ||
|
|
acf399c4e0 | ||
|
|
66a33bf7d9 | ||
|
|
aa27a0e85c | ||
|
|
a9dbff0eae | ||
|
|
a9ed42f93d | ||
|
|
cce1116b4e | ||
|
|
fc488a2b46 | ||
|
|
f53ee9026b | ||
|
|
5bd479e8ee | ||
|
|
531ef15e30 | ||
|
|
6dc6da1ce2 | ||
|
|
f9dbcef5c9 | ||
|
|
1ca4370033 | ||
|
|
62882ea65b | ||
|
|
0c08324fab | ||
|
|
2417d9492f | ||
|
|
c6886e28f2 | ||
|
|
b865d58b81 | ||
|
|
af6a0f3a9d | ||
|
|
130d254b5a | ||
|
|
82abcc94fa | ||
|
|
f635a1739e | ||
|
|
c75236579f | ||
|
|
6a0bd556d7 | ||
|
|
5d042ad496 | ||
|
|
59938a46eb | ||
|
|
1284b9b17d | ||
|
|
c5c179490b | ||
|
|
1345bc2f82 | ||
|
|
e256a7439e | ||
|
|
e67195b0b9 | ||
|
|
e4eb353065 | ||
|
|
b665da7cb4 | ||
|
|
7be4ba9351 | ||
|
|
41dadba990 | ||
|
|
17ec1d6b28 | ||
|
|
d4c2c4e170 | ||
|
|
23a2d789ec | ||
|
|
5e68fdbaee | ||
|
|
9c55efa8c6 | ||
|
|
256b6950f5 | ||
|
|
956368e457 | ||
|
|
fd3ff6be18 | ||
|
|
03f688acd9 | ||
|
|
fd5c577b40 | ||
|
|
e4834d2a37 | ||
|
|
1e76bdcc3c | ||
|
|
b6c6406206 | ||
|
|
253a698708 | ||
|
|
b819d8cdb6 | ||
|
|
84f44588d8 | ||
|
|
14961e55c8 | ||
|
|
db48fc2559 | ||
|
|
713cbcea44 | ||
|
|
0a31e2ea92 | ||
|
|
087278baba | ||
|
|
faff772619 | ||
|
|
fa1af6d74e | ||
|
|
01d2548235 | ||
|
|
5b11d18d86 | ||
|
|
e4cd23746b | ||
|
|
d2ed36f167 | ||
|
|
1222fb074f | ||
|
|
486be1049c | ||
|
|
9319153ab3 | ||
|
|
9ea539d335 | ||
|
|
f6054a2de8 | ||
|
|
6a9f368292 | ||
|
|
9ee4841520 | ||
|
|
39e125b8a2 | ||
|
|
37b614af0e | ||
|
|
1dc3c40b00 | ||
|
|
8ce5e04777 | ||
|
|
d6cdc3378a | ||
|
|
18ccd6b253 | ||
|
|
5ee33bdbe9 | ||
|
|
a965773c97 | ||
|
|
821606f001 | ||
|
|
95695f0628 | ||
|
|
755b8490e7 | ||
|
|
8a2c719359 | ||
|
|
eaa6218003 | ||
|
|
8710866886 | ||
|
|
f09db536af | ||
|
|
c848b97757 | ||
|
|
8fcd86cb9e | ||
|
|
8c69991c12 | ||
|
|
6b07ae89c0 | ||
|
|
703cee6abf | ||
|
|
5a5361815f | ||
|
|
9831c5beca | ||
|
|
139627b331 | ||
|
|
dfd410ded2 | ||
|
|
02e3fc141a | ||
|
|
65fb3f2c48 | ||
|
|
5befddca39 | ||
|
|
189663f576 | ||
|
|
b56156296b | ||
|
|
6732c9ab14 | ||
|
|
38546cae33 | ||
|
|
efdb55a083 | ||
|
|
a569fa36bd | ||
|
|
237ee1a9c6 | ||
|
|
1ae2376a83 | ||
|
|
d0d35f03d0 | ||
|
|
58a43be4f5 | ||
|
|
869d0c37b8 | ||
|
|
c84961be85 | ||
|
|
89dd0bad28 | ||
|
|
b293e4b110 | ||
|
|
be104e5dcf | ||
|
|
051ae36bec | ||
|
|
12ad34b44a | ||
|
|
fc975cfc3a | ||
|
|
1a1ce9a743 | ||
|
|
4e3caa8ece | ||
|
|
28c9f3103f | ||
|
|
c417cf079c | ||
|
|
99f5ece31c | ||
|
|
5448be6f34 | ||
|
|
38ed43ca59 | ||
|
|
2e825a108f | ||
|
|
43a19da27a | ||
|
|
8ea9b0a415 | ||
|
|
d50cceaafa | ||
|
|
0a7afd77e2 | ||
|
|
adb864828a | ||
|
|
18d432a843 | ||
|
|
d3d511d31a | ||
|
|
6950c10c6f | ||
|
|
28a6e1deec | ||
|
|
2f5208b08d | ||
|
|
7ee6e83605 | ||
|
|
0a2bbfaf24 | ||
|
|
2ab6c8fa60 | ||
|
|
61381963b7 | ||
|
|
5c448de2ae | ||
|
|
f42d656cd3 | ||
|
|
144facf203 | ||
|
|
0cd8102e0e | ||
|
|
ffbc812271 | ||
|
|
056854fd9e | ||
|
|
e7e3af1684 | ||
|
|
f214789e4b | ||
|
|
0407f018dd | ||
|
|
592965a4d9 | ||
|
|
5b695d6e9f | ||
|
|
245b8be435 | ||
|
|
840225d47b | ||
|
|
0fcb79f361 | ||
|
|
74ddc04031 | ||
|
|
d65000a361 | ||
|
|
6f37035617 | ||
|
|
aa6280f5cd | ||
|
|
64078da47b | ||
|
|
d9013dc7c9 | ||
|
|
a23f929005 | ||
|
|
4f75a3a29b | ||
|
|
d9e6900e07 | ||
|
|
b4a3e5f70b | ||
|
|
aee9a61e0d | ||
|
|
ccb8edd7a3 | ||
|
|
bb2bcc8a30 | ||
|
|
9720406086 | ||
|
|
3c0e22c98c | ||
|
|
f912d78023 | ||
|
|
61257d829e | ||
|
|
c9014b1ba9 | ||
|
|
9ad46f7487 | ||
|
|
b094178734 | ||
|
|
a3ec869c58 | ||
|
|
7177514b0d | ||
|
|
277f5273a4 | ||
|
|
8ae7bb2bc4 | ||
|
|
7aa8eab228 | ||
|
|
70439c9cd8 | ||
|
|
bbec37f713 | ||
|
|
8ddf30201f | ||
|
|
4e70ecb97d | ||
|
|
666dd27d42 | ||
|
|
410196974e | ||
|
|
b9d5c71405 | ||
|
|
18b3c4802b | ||
|
|
cbe476d455 | ||
|
|
b4b6b694af | ||
|
|
aa299d0faf | ||
|
|
1f6c5c295f | ||
|
|
5d8dafc2ec | ||
|
|
34eb306e39 | ||
|
|
439af124f9 | ||
|
|
8abcf42e20 | ||
|
|
485f6cd7a2 | ||
|
|
5447decd9e | ||
|
|
1287f16acd | ||
|
|
589c5335d1 | ||
|
|
5f82999397 | ||
|
|
115cedb8e9 | ||
|
|
d2491e403b | ||
|
|
44d6ce49b5 | ||
|
|
7b795734e8 | ||
|
|
b6bbd0efcf | ||
|
|
7522e598db | ||
|
|
5ec0289d98 | ||
|
|
576374a8a7 | ||
|
|
030abc14a4 | ||
|
|
4c779300fd | ||
|
|
c5921c605f | ||
|
|
da23bb6bf4 | ||
|
|
0c303c4f54 | ||
|
|
746e879187 | ||
|
|
a24362a83f | ||
|
|
69a5a7f11e | ||
|
|
d062a4b6b5 | ||
|
|
ab79a9554b | ||
|
|
3e5a65e090 | ||
|
|
f5d8d6e4e6 | ||
|
|
fae4805d3f | ||
|
|
bc55dafdae | ||
|
|
cb74ff870d | ||
|
|
d1458c6c0a | ||
|
|
3c84ef3b5e | ||
|
|
a8b2f222ea | ||
|
|
2894640d56 | ||
|
|
ee1c3831bc | ||
|
|
fc5f5c8fd9 | ||
|
|
a45ff8e3ba | ||
|
|
2caac24071 | ||
|
|
74dd64501c | ||
|
|
bd8b94c9dd | ||
|
|
928bc96d76 | ||
|
|
d1569b9581 | ||
|
|
67c9f00917 | ||
|
|
c75303b0ab | ||
|
|
ee56de3091 | ||
|
|
938cde30e3 | ||
|
|
d53d6cabb4 | ||
|
|
0550b3537d | ||
|
|
ea6f08cb26 | ||
|
|
96a05f65aa | ||
|
|
0e63f4ba08 | ||
|
|
74b943392b | ||
|
|
9245133b99 | ||
|
|
05aae4341e | ||
|
|
e85c3a50cd | ||
|
|
dc04d44a20 | ||
|
|
e2e5ba345c | ||
|
|
07924d3c7c | ||
|
|
bd29244d66 | ||
|
|
b384ed74cd | ||
|
|
72892a40d6 | ||
|
|
8209507a8f | ||
|
|
ca5384343f | ||
|
|
a4e92cf577 | ||
|
|
113048347c | ||
|
|
8b144a38cc | ||
|
|
632c65cd59 | ||
|
|
70ab59f473 | ||
|
|
c4d6cca9f1 | ||
|
|
795d318fcc | ||
|
|
6dbb02d293 | ||
|
|
ea4c91f51c | ||
|
|
395da09dae | ||
|
|
9b08258955 | ||
|
|
7f2aedb67c | ||
|
|
1045dc49f5 | ||
|
|
a569fed4da | ||
|
|
9af9b438af | ||
|
|
3e321d5c24 | ||
|
|
e2f53cdc83 | ||
|
|
f9834504c8 | ||
|
|
ec19926a97 | ||
|
|
39790f05e7 | ||
|
|
317b53a8b6 | ||
|
|
101612e599 | ||
|
|
838ed77797 | ||
|
|
8ff718249e | ||
|
|
88c2ba54ab | ||
|
|
a52fe009cc | ||
|
|
79b691fe18 | ||
|
|
29f1a219d1 | ||
|
|
73e3ba2c30 | ||
|
|
78f69e448b | ||
|
|
52890341d5 | ||
|
|
725684a0c3 | ||
|
|
dc59831aa9 | ||
|
|
2fa841821a | ||
|
|
3b14810564 | ||
|
|
8ed066ba1f | ||
|
|
f344369068 | ||
|
|
71d9a52a07 | ||
|
|
0af125f6fe | ||
|
|
b432f3358e | ||
|
|
441bb760ee | ||
|
|
f69da75e33 | ||
|
|
12739ec464 | ||
|
|
6a317a8e78 | ||
|
|
cb1cc96210 | ||
|
|
b47f5830f4 | ||
|
|
855373a253 | ||
|
|
c8ed6b1e79 | ||
|
|
00bbc76605 | ||
|
|
0df4f1738c | ||
|
|
12a2da49ee | ||
|
|
65d6efdaee | ||
|
|
f861a3e720 | ||
|
|
767d17ce2f | ||
|
|
0f455060ab | ||
|
|
861f2726a2 | ||
|
|
55640be627 | ||
|
|
d837b6cedc | ||
|
|
27b2988938 | ||
|
|
c739c2d355 | ||
|
|
231f65ff59 | ||
|
|
368afc551d | ||
|
|
3a328b0413 | ||
|
|
970b21075e | ||
|
|
d769a2036b | ||
|
|
56a3b9abc4 | ||
|
|
6844dd75da | ||
|
|
0b6ef55e78 | ||
|
|
cd0fcfd84c | ||
|
|
43118e3551 | ||
|
|
9bd6d119c4 | ||
|
|
2aa045865f | ||
|
|
ec3a462acd | ||
|
|
a6066009aa | ||
|
|
60b19fe935 | ||
|
|
70b52c37d5 | ||
|
|
178ad9b4d6 | ||
|
|
b4311a41c5 | ||
|
|
c6e0dbb1c4 | ||
|
|
d476eb79b4 | ||
|
|
10ea82001f | ||
|
|
794bb6ebdd | ||
|
|
c3a9513978 | ||
|
|
a4a019f9d9 | ||
|
|
67ed21d443 | ||
|
|
c89d750e5c | ||
|
|
c253f0621d | ||
|
|
3202b0a486 | ||
|
|
4a37ee8908 | ||
|
|
bdbfd2ad0c | ||
|
|
933d1262f2 | ||
|
|
c4752e36eb | ||
|
|
0da919c091 | ||
|
|
12b5d72e70 | ||
|
|
272d2bc0b3 | ||
|
|
55b7904e2f | ||
|
|
ed709b36b4 | ||
|
|
bf1fc47564 | ||
|
|
db6953dc78 | ||
|
|
438e0f5d49 | ||
|
|
5a9476e0d4 | ||
|
|
f77a735469 | ||
|
|
4451b7eda9 | ||
|
|
426f0bc168 | ||
|
|
a8b87ccce0 | ||
|
|
c31fc19f08 | ||
|
|
5e817b2aee | ||
|
|
393857d7de | ||
|
|
0ffff47b64 | ||
|
|
6397590f07 | ||
|
|
2620ac31f9 | ||
|
|
22c4f82fc6 | ||
|
|
ae5414fced | ||
|
|
61584c8601 | ||
|
|
99dfe6c571 | ||
|
|
fd1ab37bc8 | ||
|
|
bd969477b1 | ||
|
|
a816788039 | ||
|
|
c099a410c8 | ||
|
|
e33c44ae33 | ||
|
|
4930233bbe | ||
|
|
e383727394 | ||
|
|
3d2dcd8c59 | ||
|
|
6647f45eb5 | ||
|
|
bd9625d150 | ||
|
|
c832291bd6 | ||
|
|
7c67c38bc5 | ||
|
|
8076deb080 | ||
|
|
dd8566ecde | ||
|
|
fa7d496413 | ||
|
|
3b8ad79445 | ||
|
|
558c3284a0 | ||
|
|
95f7807b78 | ||
|
|
6ece7fe265 | ||
|
|
5aa2241439 | ||
|
|
68d7b77314 | ||
|
|
93e8b5d87e | ||
|
|
6b082b5edb | ||
|
|
093720b870 | ||
|
|
e1697fd9cc | ||
|
|
c2f952045c | ||
|
|
2b5bca4ce6 | ||
|
|
de6f0f7a05 | ||
|
|
1f3584b9a8 | ||
|
|
5bfdf0af4d | ||
|
|
14a50a9403 | ||
|
|
b724fec6c9 | ||
|
|
1d5ea4feee | ||
|
|
7fedff260d | ||
|
|
9fc78e44ea | ||
|
|
be97087d32 | ||
|
|
b0f829e541 | ||
|
|
0455a96dcd | ||
|
|
d771ba2895 | ||
|
|
c8e5b42dba | ||
|
|
424dbef139 | ||
|
|
6256ca81b1 | ||
|
|
fda451f3e3 | ||
|
|
2307385210 | ||
|
|
bd4b5da11b | ||
|
|
ef7f9c6ecc | ||
|
|
097c643a59 | ||
|
|
dd560d676e | ||
|
|
794fd75ca1 | ||
|
|
f49e66e721 | ||
|
|
8fd1e08763 | ||
|
|
c5d108c954 | ||
|
|
f2c43ca81e | ||
|
|
9d6ee99d99 | ||
|
|
0578cf5a73 | ||
|
|
499f9198b9 | ||
|
|
4daab871d5 | ||
|
|
17585710b2 | ||
|
|
56e7887511 | ||
|
|
08c782709a | ||
|
|
fb20982194 | ||
|
|
9453add3dd | ||
|
|
c62cf98d7e | ||
|
|
ea8e302833 | ||
|
|
5d3fd9dc43 | ||
|
|
c9ba147615 | ||
|
|
7ef57533ec | ||
|
|
712abe4cd2 | ||
|
|
1a93977ef7 | ||
|
|
fc078c1d45 | ||
|
|
77532b96b8 | ||
|
|
4171c5ceeb | ||
|
|
fa07e0fb9d | ||
|
|
e80702b6c2 | ||
|
|
5817adbb64 | ||
|
|
4de4cb055f | ||
|
|
19c841eb64 | ||
|
|
5f10e0ac26 | ||
|
|
64bb910015 | ||
|
|
6b7848232c | ||
|
|
b9df3793fb | ||
|
|
09f95858c0 | ||
|
|
fcd7548220 | ||
|
|
b882d7046b | ||
|
|
b33f82d4e8 | ||
|
|
cfb00fb887 | ||
|
|
44f0c077ff | ||
|
|
3b2aa5ead3 | ||
|
|
2c045179a3 | ||
|
|
da83af1213 | ||
|
|
61fc2d3263 | ||
|
|
a66be0f5c5 | ||
|
|
cf1f777ae2 | ||
|
|
c7b5309dcd | ||
|
|
45d454b14d | ||
|
|
f390b8062c | ||
|
|
90adf076f3 | ||
|
|
2b6942d9ce | ||
|
|
4dca806737 | ||
|
|
3379cac956 | ||
|
|
ed48755a6f | ||
|
|
c43e759b87 | ||
|
|
05e9af60bd | ||
|
|
de9220568e | ||
|
|
4879858657 | ||
|
|
773eb6ce68 | ||
|
|
8e1201d31e | ||
|
|
bf401290d1 | ||
|
|
af33f71562 | ||
|
|
25c26b5904 | ||
|
|
ae8a0940f5 | ||
|
|
de423f8183 | ||
|
|
c7bf050e8b | ||
|
|
39b8e150bf | ||
|
|
849ec84852 | ||
|
|
de9c73c5cd | ||
|
|
04201028d1 | ||
|
|
086e5c4dac | ||
|
|
9578ead7f9 |
14
.github/workflows/docker-release.yml
vendored
Normal file
14
.github/workflows/docker-release.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
name: Trigger Docker build on release
|
||||
on:
|
||||
release:
|
||||
types: [released]
|
||||
jobs:
|
||||
curl:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: alpine:latest
|
||||
steps:
|
||||
- name: curl
|
||||
run: |
|
||||
apk add curl bash
|
||||
curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token ${{ secrets.TRAVIS_CI_TOKEN }}" -d '{"request":{"branch":"master"}}' https://api.travis-ci.com/repo/frappe%2Ffrappe_docker/requests
|
||||
@@ -5,7 +5,7 @@ import frappe
|
||||
from erpnext.hooks import regional_overrides
|
||||
from frappe.utils import getdate
|
||||
|
||||
__version__ = '12.8.0'
|
||||
__version__ = '12.11.0'
|
||||
|
||||
def get_default_company(user=None):
|
||||
'''Get default company for user'''
|
||||
|
||||
@@ -159,7 +159,7 @@ def book_deferred_income_or_expense(doc, posting_date=None):
|
||||
total_days, total_booking_days, account_currency)
|
||||
|
||||
make_gl_entries(doc, credit_account, debit_account, against,
|
||||
amount, base_amount, end_date, project, account_currency, item.cost_center, item.name)
|
||||
amount, base_amount, end_date, project, account_currency, item.cost_center, item)
|
||||
|
||||
if getdate(end_date) < getdate(posting_date) and not last_gl_entry:
|
||||
_book_deferred_revenue_or_expense(item)
|
||||
@@ -170,7 +170,7 @@ def book_deferred_income_or_expense(doc, posting_date=None):
|
||||
_book_deferred_revenue_or_expense(item)
|
||||
|
||||
def make_gl_entries(doc, credit_account, debit_account, against,
|
||||
amount, base_amount, posting_date, project, account_currency, cost_center, voucher_detail_no):
|
||||
amount, base_amount, posting_date, project, account_currency, cost_center, item):
|
||||
# GL Entry for crediting the amount in the deferred expense
|
||||
from erpnext.accounts.general_ledger import make_gl_entries
|
||||
|
||||
@@ -184,10 +184,10 @@ def make_gl_entries(doc, credit_account, debit_account, against,
|
||||
"credit": base_amount,
|
||||
"credit_in_account_currency": amount,
|
||||
"cost_center": cost_center,
|
||||
"voucher_detail_no": voucher_detail_no,
|
||||
"voucher_detail_no": item.name,
|
||||
'posting_date': posting_date,
|
||||
'project': project
|
||||
}, account_currency)
|
||||
}, account_currency, item=item)
|
||||
)
|
||||
# GL Entry to debit the amount from the expense
|
||||
gl_entries.append(
|
||||
@@ -197,10 +197,10 @@ def make_gl_entries(doc, credit_account, debit_account, against,
|
||||
"debit": base_amount,
|
||||
"debit_in_account_currency": amount,
|
||||
"cost_center": cost_center,
|
||||
"voucher_detail_no": voucher_detail_no,
|
||||
"voucher_detail_no": item.name,
|
||||
'posting_date': posting_date,
|
||||
'project': project
|
||||
}, account_currency)
|
||||
}, account_currency, item=item)
|
||||
)
|
||||
|
||||
if gl_entries:
|
||||
|
||||
@@ -89,7 +89,7 @@ class Account(NestedSet):
|
||||
throw(_("Root cannot be edited."), RootNotEditable)
|
||||
|
||||
if not self.parent_account and not self.is_group:
|
||||
frappe.throw(_("Root Account must be a group"))
|
||||
frappe.throw(_("The root account {0} must be a group").format(frappe.bold(self.name)))
|
||||
|
||||
def validate_root_company_and_sync_account_to_children(self):
|
||||
# ignore validation while creating new compnay or while syncing to child companies
|
||||
@@ -244,6 +244,8 @@ class Account(NestedSet):
|
||||
|
||||
super(Account, self).on_trash(True)
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_parent_account(doctype, txt, searchfield, start, page_len, filters):
|
||||
return frappe.db.sql("""select name from tabAccount
|
||||
where is_group = 1 and docstatus != 2 and company = %s
|
||||
|
||||
@@ -14,6 +14,9 @@ frappe.treeview_settings["Account"] = {
|
||||
on_change: function() {
|
||||
var me = frappe.treeview_settings['Account'].treeview;
|
||||
var company = me.page.fields_dict.company.get_value();
|
||||
if (!company) {
|
||||
frappe.throw(__("Please set a Company"));
|
||||
}
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.account.account.get_root_company",
|
||||
args: {
|
||||
|
||||
@@ -225,7 +225,7 @@ def build_tree_from_json(chart_template, chart_data=None):
|
||||
|
||||
account['parent_account'] = parent
|
||||
account['expandable'] = True if identify_is_group(child) else False
|
||||
account['value'] = (child.get('account_number') + ' - ' + account_name) \
|
||||
account['value'] = (cstr(child.get('account_number')).strip() + ' - ' + account_name) \
|
||||
if child.get('account_number') else account_name
|
||||
accounts.append(account)
|
||||
_import_accounts(child, account['value'])
|
||||
|
||||
@@ -69,6 +69,7 @@ class TestAccount(unittest.TestCase):
|
||||
acc.account_name = "Accumulated Depreciation"
|
||||
acc.parent_account = "Fixed Assets - _TC"
|
||||
acc.company = "_Test Company"
|
||||
acc.account_type = "Accumulated Depreciation"
|
||||
acc.insert()
|
||||
|
||||
doc = frappe.get_doc("Account", "Securities and Deposits - _TC")
|
||||
@@ -149,7 +150,7 @@ def _make_test_records(verbose):
|
||||
|
||||
# fixed asset depreciation
|
||||
["_Test Fixed Asset", "Current Assets", 0, "Fixed Asset", None],
|
||||
["_Test Accumulated Depreciations", "Current Assets", 0, None, None],
|
||||
["_Test Accumulated Depreciations", "Current Assets", 0, "Accumulated Depreciation", None],
|
||||
["_Test Depreciations", "Expenses", 0, None, None],
|
||||
["_Test Gain/Loss on Asset Disposal", "Expenses", 0, None, None],
|
||||
|
||||
|
||||
@@ -162,9 +162,9 @@ def toggle_disabling(doc):
|
||||
|
||||
def get_doctypes_with_dimensions():
|
||||
doclist = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "Asset",
|
||||
"Expense Claim", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note", "Sales Invoice Item", "Purchase Invoice Item",
|
||||
"Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item", "Purchase Receipt Item",
|
||||
"Stock Entry Detail", "Payment Entry Deduction", "Sales Taxes and Charges", "Purchase Taxes and Charges", "Shipping Rule",
|
||||
"Expense Claim", "Expense Claim Detail", "Expense Taxes and Charges", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note",
|
||||
"Sales Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item",
|
||||
"Purchase Receipt Item", "Stock Entry Detail", "Payment Entry Deduction", "Sales Taxes and Charges", "Purchase Taxes and Charges", "Shipping Rule",
|
||||
"Landed Cost Item", "Asset Value Adjustment", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation",
|
||||
"Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item", "Subscription",
|
||||
"Subscription Plan"]
|
||||
@@ -206,12 +206,13 @@ def get_dimension_filters():
|
||||
WHERE disabled = 0
|
||||
""", as_dict=1)
|
||||
|
||||
default_dimensions = frappe.db.sql("""SELECT parent, company, default_dimension
|
||||
FROM `tabAccounting Dimension Detail`""", as_dict=1)
|
||||
default_dimensions = frappe.db.sql("""SELECT p.fieldname, c.company, c.default_dimension
|
||||
FROM `tabAccounting Dimension Detail` c, `tabAccounting Dimension` p
|
||||
WHERE c.parent = p.name""", as_dict=1)
|
||||
|
||||
default_dimensions_map = {}
|
||||
for dimension in default_dimensions:
|
||||
default_dimensions_map.setdefault(dimension['company'], {})
|
||||
default_dimensions_map[dimension['company']][dimension['parent']] = dimension['default_dimension']
|
||||
default_dimensions_map.setdefault(dimension.company, {})
|
||||
default_dimensions_map[dimension.company][dimension.fieldname] = dimension.default_dimension
|
||||
|
||||
return dimension_filters, default_dimensions_map
|
||||
|
||||
@@ -1,210 +1,210 @@
|
||||
{
|
||||
"creation": "2013-06-24 15:49:57",
|
||||
"description": "Settings for Accounts",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Other",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"auto_accounting_for_stock",
|
||||
"acc_frozen_upto",
|
||||
"frozen_accounts_modifier",
|
||||
"determine_address_tax_category_from",
|
||||
"over_billing_allowance",
|
||||
"column_break_4",
|
||||
"credit_controller",
|
||||
"check_supplier_invoice_uniqueness",
|
||||
"make_payment_via_journal_entry",
|
||||
"unlink_payment_on_cancellation_of_invoice",
|
||||
"unlink_advance_payment_on_cancelation_of_order",
|
||||
"book_asset_depreciation_entry_automatically",
|
||||
"allow_cost_center_in_entry_of_bs_account",
|
||||
"add_taxes_from_item_tax_template",
|
||||
"automatically_fetch_payment_terms",
|
||||
"print_settings",
|
||||
"show_inclusive_tax_in_print",
|
||||
"column_break_12",
|
||||
"show_payment_schedule_in_print",
|
||||
"currency_exchange_section",
|
||||
"allow_stale",
|
||||
"stale_days",
|
||||
"report_settings_sb",
|
||||
"use_custom_cash_flow"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"default": "1",
|
||||
"description": "If enabled, the system will post accounting entries for inventory automatically.",
|
||||
"fieldname": "auto_accounting_for_stock",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Make Accounting Entry For Every Stock Movement"
|
||||
},
|
||||
{
|
||||
"description": "Accounting entry frozen up to this date, nobody can do / modify entry except role specified below.",
|
||||
"fieldname": "acc_frozen_upto",
|
||||
"fieldtype": "Date",
|
||||
"in_list_view": 1,
|
||||
"label": "Accounts Frozen Upto"
|
||||
},
|
||||
{
|
||||
"description": "Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts",
|
||||
"fieldname": "frozen_accounts_modifier",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Role Allowed to Set Frozen Accounts & Edit Frozen Entries",
|
||||
"options": "Role"
|
||||
},
|
||||
{
|
||||
"default": "Billing Address",
|
||||
"description": "Address used to determine Tax Category in transactions.",
|
||||
"fieldname": "determine_address_tax_category_from",
|
||||
"fieldtype": "Select",
|
||||
"label": "Determine Address Tax Category From",
|
||||
"options": "Billing Address\nShipping Address"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_4",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"description": "Role that is allowed to submit transactions that exceed credit limits set.",
|
||||
"fieldname": "credit_controller",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Credit Controller",
|
||||
"options": "Role"
|
||||
},
|
||||
{
|
||||
"fieldname": "check_supplier_invoice_uniqueness",
|
||||
"fieldtype": "Check",
|
||||
"label": "Check Supplier Invoice Number Uniqueness"
|
||||
},
|
||||
{
|
||||
"fieldname": "make_payment_via_journal_entry",
|
||||
"fieldtype": "Check",
|
||||
"label": "Make Payment via Journal Entry"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "unlink_payment_on_cancellation_of_invoice",
|
||||
"fieldtype": "Check",
|
||||
"label": "Unlink Payment on Cancellation of Invoice"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "unlink_advance_payment_on_cancelation_of_order",
|
||||
"fieldtype": "Check",
|
||||
"label": "Unlink Advance Payment on Cancelation of Order"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "book_asset_depreciation_entry_automatically",
|
||||
"fieldtype": "Check",
|
||||
"label": "Book Asset Depreciation Entry Automatically"
|
||||
},
|
||||
{
|
||||
"fieldname": "allow_cost_center_in_entry_of_bs_account",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allow Cost Center In Entry of Balance Sheet Account"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "add_taxes_from_item_tax_template",
|
||||
"fieldtype": "Check",
|
||||
"label": "Automatically Add Taxes and Charges from Item Tax Template"
|
||||
},
|
||||
{
|
||||
"fieldname": "print_settings",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Print Settings"
|
||||
},
|
||||
{
|
||||
"fieldname": "show_inclusive_tax_in_print",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show Inclusive Tax In Print"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_12",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "show_payment_schedule_in_print",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show Payment Schedule in Print"
|
||||
},
|
||||
{
|
||||
"fieldname": "currency_exchange_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Currency Exchange Settings"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "allow_stale",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Allow Stale Exchange Rates"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"depends_on": "eval:doc.allow_stale==0",
|
||||
"fieldname": "stale_days",
|
||||
"fieldtype": "Int",
|
||||
"label": "Stale Days"
|
||||
},
|
||||
{
|
||||
"fieldname": "report_settings_sb",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Report Settings"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "Only select if you have setup Cash Flow Mapper documents",
|
||||
"fieldname": "use_custom_cash_flow",
|
||||
"fieldtype": "Check",
|
||||
"label": "Use Custom Cash Flow Format"
|
||||
},
|
||||
{
|
||||
"fieldname": "automatically_fetch_payment_terms",
|
||||
"fieldtype": "Check",
|
||||
"label": "Automatically Fetch Payment Terms"
|
||||
},
|
||||
{
|
||||
"description": "Percentage you are allowed to bill more against the amount ordered. For example: If the order value is $100 for an item and tolerance is set as 10% then you are allowed to bill for $110.",
|
||||
"fieldname": "over_billing_allowance",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Over Billing Allowance (%)"
|
||||
}
|
||||
],
|
||||
"icon": "icon-cog",
|
||||
"idx": 1,
|
||||
"issingle": 1,
|
||||
"modified": "2019-07-04 18:20:55.789946",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Accounts Settings",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"role": "Accounts Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"read": 1,
|
||||
"role": "Sales User"
|
||||
},
|
||||
{
|
||||
"read": 1,
|
||||
"role": "Purchase User"
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"sort_order": "ASC",
|
||||
"track_changes": 1
|
||||
"creation": "2013-06-24 15:49:57",
|
||||
"description": "Settings for Accounts",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Other",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"auto_accounting_for_stock",
|
||||
"acc_frozen_upto",
|
||||
"frozen_accounts_modifier",
|
||||
"determine_address_tax_category_from",
|
||||
"over_billing_allowance",
|
||||
"column_break_4",
|
||||
"credit_controller",
|
||||
"check_supplier_invoice_uniqueness",
|
||||
"make_payment_via_journal_entry",
|
||||
"unlink_payment_on_cancellation_of_invoice",
|
||||
"unlink_advance_payment_on_cancelation_of_order",
|
||||
"book_asset_depreciation_entry_automatically",
|
||||
"add_taxes_from_item_tax_template",
|
||||
"automatically_fetch_payment_terms",
|
||||
"print_settings",
|
||||
"show_inclusive_tax_in_print",
|
||||
"column_break_12",
|
||||
"show_payment_schedule_in_print",
|
||||
"currency_exchange_section",
|
||||
"allow_stale",
|
||||
"stale_days",
|
||||
"report_settings_sb",
|
||||
"use_custom_cash_flow"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"default": "1",
|
||||
"description": "If enabled, the system will post accounting entries for inventory automatically.",
|
||||
"fieldname": "auto_accounting_for_stock",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Make Accounting Entry For Every Stock Movement"
|
||||
},
|
||||
{
|
||||
"description": "Accounting entry frozen up to this date, nobody can do / modify entry except role specified below.",
|
||||
"fieldname": "acc_frozen_upto",
|
||||
"fieldtype": "Date",
|
||||
"in_list_view": 1,
|
||||
"label": "Accounts Frozen Upto"
|
||||
},
|
||||
{
|
||||
"description": "Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts",
|
||||
"fieldname": "frozen_accounts_modifier",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Role Allowed to Set Frozen Accounts & Edit Frozen Entries",
|
||||
"options": "Role"
|
||||
},
|
||||
{
|
||||
"default": "Billing Address",
|
||||
"description": "Address used to determine Tax Category in transactions.",
|
||||
"fieldname": "determine_address_tax_category_from",
|
||||
"fieldtype": "Select",
|
||||
"label": "Determine Address Tax Category From",
|
||||
"options": "Billing Address\nShipping Address"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_4",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"description": "Role that is allowed to submit transactions that exceed credit limits set.",
|
||||
"fieldname": "credit_controller",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Credit Controller",
|
||||
"options": "Role"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "check_supplier_invoice_uniqueness",
|
||||
"fieldtype": "Check",
|
||||
"label": "Check Supplier Invoice Number Uniqueness"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "make_payment_via_journal_entry",
|
||||
"fieldtype": "Check",
|
||||
"label": "Make Payment via Journal Entry"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "unlink_payment_on_cancellation_of_invoice",
|
||||
"fieldtype": "Check",
|
||||
"label": "Unlink Payment on Cancellation of Invoice"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "unlink_advance_payment_on_cancelation_of_order",
|
||||
"fieldtype": "Check",
|
||||
"label": "Unlink Advance Payment on Cancelation of Order"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "book_asset_depreciation_entry_automatically",
|
||||
"fieldtype": "Check",
|
||||
"label": "Book Asset Depreciation Entry Automatically"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "add_taxes_from_item_tax_template",
|
||||
"fieldtype": "Check",
|
||||
"label": "Automatically Add Taxes and Charges from Item Tax Template"
|
||||
},
|
||||
{
|
||||
"fieldname": "print_settings",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Print Settings"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "show_inclusive_tax_in_print",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show Inclusive Tax In Print"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_12",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "show_payment_schedule_in_print",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show Payment Schedule in Print"
|
||||
},
|
||||
{
|
||||
"fieldname": "currency_exchange_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Currency Exchange Settings"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "allow_stale",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Allow Stale Exchange Rates"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"depends_on": "eval:doc.allow_stale==0",
|
||||
"fieldname": "stale_days",
|
||||
"fieldtype": "Int",
|
||||
"label": "Stale Days"
|
||||
},
|
||||
{
|
||||
"fieldname": "report_settings_sb",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Report Settings"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "Only select if you have setup Cash Flow Mapper documents",
|
||||
"fieldname": "use_custom_cash_flow",
|
||||
"fieldtype": "Check",
|
||||
"label": "Use Custom Cash Flow Format"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "automatically_fetch_payment_terms",
|
||||
"fieldtype": "Check",
|
||||
"label": "Automatically Fetch Payment Terms"
|
||||
},
|
||||
{
|
||||
"description": "Percentage you are allowed to bill more against the amount ordered. For example: If the order value is $100 for an item and tolerance is set as 10% then you are allowed to bill for $110.",
|
||||
"fieldname": "over_billing_allowance",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Over Billing Allowance (%)"
|
||||
}
|
||||
],
|
||||
"icon": "icon-cog",
|
||||
"idx": 1,
|
||||
"issingle": 1,
|
||||
"modified": "2020-03-11 13:09:26.235848",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Accounts Settings",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"role": "Accounts Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"read": 1,
|
||||
"role": "Sales User"
|
||||
},
|
||||
{
|
||||
"read": 1,
|
||||
"role": "Purchase User"
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "ASC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -20,7 +20,6 @@ class AccountsSettings(Document):
|
||||
|
||||
self.validate_stale_days()
|
||||
self.enable_payment_schedule_in_print()
|
||||
self.enable_fields_for_cost_center_settings()
|
||||
|
||||
def validate_stale_days(self):
|
||||
if not self.allow_stale and cint(self.stale_days) <= 0:
|
||||
@@ -33,8 +32,3 @@ class AccountsSettings(Document):
|
||||
for doctype in ("Sales Order", "Sales Invoice", "Purchase Order", "Purchase Invoice"):
|
||||
make_property_setter(doctype, "due_date", "print_hide", show_in_print, "Check")
|
||||
make_property_setter(doctype, "payment_schedule", "print_hide", 0 if show_in_print else 1, "Check")
|
||||
|
||||
def enable_fields_for_cost_center_settings(self):
|
||||
show_field = 0 if cint(self.allow_cost_center_in_entry_of_bs_account) else 1
|
||||
for doctype in ("Sales Invoice", "Purchase Invoice", "Payment Entry"):
|
||||
make_property_setter(doctype, "cost_center", "hidden", show_field, "Check")
|
||||
|
||||
@@ -60,12 +60,13 @@ class BankReconciliation(Document):
|
||||
""".format(condition=condition), {"account": self.account, "from":self.from_date,
|
||||
"to": self.to_date, "bank_account": self.bank_account}, as_dict=1)
|
||||
|
||||
pos_entries = []
|
||||
|
||||
pos_sales_invoices, pos_purchase_invoices = [], []
|
||||
if self.include_pos_transactions:
|
||||
pos_entries = frappe.db.sql("""
|
||||
pos_sales_invoices = frappe.db.sql("""
|
||||
select
|
||||
"Sales Invoice Payment" as payment_document, sip.name as payment_entry, sip.amount as debit,
|
||||
si.posting_date, si.debit_to as against_account, sip.clearance_date,
|
||||
si.posting_date, si.customer as against_account, sip.clearance_date,
|
||||
account.account_currency, 0 as credit
|
||||
from `tabSales Invoice Payment` sip, `tabSales Invoice` si, `tabAccount` account
|
||||
where
|
||||
@@ -75,7 +76,20 @@ class BankReconciliation(Document):
|
||||
si.posting_date ASC, si.name DESC
|
||||
""", {"account":self.account, "from":self.from_date, "to":self.to_date}, as_dict=1)
|
||||
|
||||
entries = sorted(list(payment_entries)+list(journal_entries+list(pos_entries)),
|
||||
pos_purchase_invoices = frappe.db.sql("""
|
||||
select
|
||||
"Purchase Invoice" as payment_document, pi.name as payment_entry, pi.paid_amount as credit,
|
||||
pi.posting_date, pi.supplier as against_account, pi.clearance_date,
|
||||
account.account_currency, 0 as debit
|
||||
from `tabPurchase Invoice` pi, `tabAccount` account
|
||||
where
|
||||
pi.cash_bank_account=%(account)s and pi.docstatus=1 and account.name = pi.cash_bank_account
|
||||
and pi.posting_date >= %(from)s and pi.posting_date <= %(to)s
|
||||
order by
|
||||
pi.posting_date ASC, pi.name DESC
|
||||
""", {"account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1)
|
||||
|
||||
entries = sorted(list(payment_entries) + list(journal_entries + list(pos_sales_invoices) + list(pos_purchase_invoices)),
|
||||
key=lambda k: k['posting_date'] or getdate(nowdate()))
|
||||
|
||||
self.set('payment_entries', [])
|
||||
|
||||
@@ -9,6 +9,7 @@ from frappe.utils import flt, getdate, add_months, get_last_day, fmt_money, nowd
|
||||
from frappe.model.naming import make_autoname
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
from frappe.model.document import Document
|
||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
|
||||
|
||||
class BudgetError(frappe.ValidationError): pass
|
||||
class DuplicateBudgetError(frappe.ValidationError): pass
|
||||
@@ -98,30 +99,32 @@ def validate_expense_against_budget(args):
|
||||
if not (args.get('account') and args.get('cost_center')) and args.item_code:
|
||||
args.cost_center, args.account = get_item_details(args)
|
||||
|
||||
if not (args.cost_center or args.project) and not args.account:
|
||||
if not args.account:
|
||||
return
|
||||
|
||||
for budget_against in ['project', 'cost_center']:
|
||||
for budget_against in ['project', 'cost_center'] + get_accounting_dimensions():
|
||||
if (args.get(budget_against) and args.account
|
||||
and frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"})):
|
||||
|
||||
if args.project and budget_against == 'project':
|
||||
condition = "and b.project=%s" % frappe.db.escape(args.project)
|
||||
args.budget_against_field = "Project"
|
||||
doctype = frappe.unscrub(budget_against)
|
||||
|
||||
elif args.cost_center and budget_against == 'cost_center':
|
||||
cc_lft, cc_rgt = frappe.db.get_value("Cost Center", args.cost_center, ["lft", "rgt"])
|
||||
condition = """and exists(select name from `tabCost Center`
|
||||
where lft<=%s and rgt>=%s and name=b.cost_center)""" % (cc_lft, cc_rgt)
|
||||
args.budget_against_field = "Cost Center"
|
||||
if frappe.get_cached_value('DocType', doctype, 'is_tree'):
|
||||
lft, rgt = frappe.db.get_value(doctype, args.get(budget_against), ["lft", "rgt"])
|
||||
condition = """and exists(select name from `tab%s`
|
||||
where lft<=%s and rgt>=%s and name=b.%s)""" % (doctype, lft, rgt, budget_against) #nosec
|
||||
args.is_tree = True
|
||||
else:
|
||||
condition = "and b.%s=%s" % (budget_against, frappe.db.escape(args.get(budget_against)))
|
||||
args.is_tree = False
|
||||
|
||||
args.budget_against = args.get(budget_against)
|
||||
args.budget_against_field = budget_against
|
||||
args.budget_against_doctype = doctype
|
||||
|
||||
budget_records = frappe.db.sql("""
|
||||
select
|
||||
b.{budget_against_field} as budget_against, ba.budget_amount, b.monthly_distribution,
|
||||
ifnull(b.applicable_on_material_request, 0) as for_material_request,
|
||||
ifnull(applicable_on_purchase_order,0) as for_purchase_order,
|
||||
ifnull(applicable_on_purchase_order, 0) as for_purchase_order,
|
||||
ifnull(applicable_on_booking_actual_expenses,0) as for_actual_expenses,
|
||||
b.action_if_annual_budget_exceeded, b.action_if_accumulated_monthly_budget_exceeded,
|
||||
b.action_if_annual_budget_exceeded_on_mr, b.action_if_accumulated_monthly_budget_exceeded_on_mr,
|
||||
@@ -132,9 +135,7 @@ def validate_expense_against_budget(args):
|
||||
b.name=ba.parent and b.fiscal_year=%s
|
||||
and ba.account=%s and b.docstatus=1
|
||||
{condition}
|
||||
""".format(condition=condition,
|
||||
budget_against_field=frappe.scrub(args.get("budget_against_field"))),
|
||||
(args.fiscal_year, args.account), as_dict=True)
|
||||
""".format(condition=condition, budget_against_field=budget_against), (args.fiscal_year, args.account), as_dict=True) #nosec
|
||||
|
||||
if budget_records:
|
||||
validate_budget_records(args, budget_records)
|
||||
@@ -230,10 +231,10 @@ def get_ordered_amount(args, budget):
|
||||
|
||||
def get_other_condition(args, budget, for_doc):
|
||||
condition = "expense_account = '%s'" % (args.expense_account)
|
||||
budget_against_field = frappe.scrub(args.get("budget_against_field"))
|
||||
budget_against_field = args.get("budget_against_field")
|
||||
|
||||
if budget_against_field and args.get(budget_against_field):
|
||||
condition += " and child.%s = '%s'" %(budget_against_field, args.get(budget_against_field))
|
||||
condition += " and child.%s = '%s'" % (budget_against_field, args.get(budget_against_field))
|
||||
|
||||
if args.get('fiscal_year'):
|
||||
date_field = 'schedule_date' if for_doc == 'Material Request' else 'transaction_date'
|
||||
@@ -246,19 +247,30 @@ def get_other_condition(args, budget, for_doc):
|
||||
return condition
|
||||
|
||||
def get_actual_expense(args):
|
||||
if not args.budget_against_doctype:
|
||||
args.budget_against_doctype = frappe.unscrub(args.budget_against_field)
|
||||
|
||||
budget_against_field = args.get('budget_against_field')
|
||||
condition1 = " and gle.posting_date <= %(month_end_date)s" \
|
||||
if args.get("month_end_date") else ""
|
||||
if args.budget_against_field == "Cost Center":
|
||||
lft_rgt = frappe.db.get_value(args.budget_against_field,
|
||||
args.budget_against, ["lft", "rgt"], as_dict=1)
|
||||
|
||||
if args.is_tree:
|
||||
lft_rgt = frappe.db.get_value(args.budget_against_doctype,
|
||||
args.get(budget_against_field), ["lft", "rgt"], as_dict=1)
|
||||
|
||||
args.update(lft_rgt)
|
||||
condition2 = """and exists(select name from `tabCost Center`
|
||||
where lft>=%(lft)s and rgt<=%(rgt)s and name=gle.cost_center)"""
|
||||
|
||||
elif args.budget_against_field == "Project":
|
||||
condition2 = "and exists(select name from `tabProject` where name=gle.project and gle.project = %(budget_against)s)"
|
||||
condition2 = """and exists(select name from `tab{doctype}`
|
||||
where lft>=%(lft)s and rgt<=%(rgt)s
|
||||
and name=gle.{budget_against_field})""".format(doctype=args.budget_against_doctype, #nosec
|
||||
budget_against_field=budget_against_field)
|
||||
else:
|
||||
condition2 = """and exists(select name from `tab{doctype}`
|
||||
where name=gle.{budget_against} and
|
||||
gle.{budget_against} = %({budget_against})s)""".format(doctype=args.budget_against_doctype,
|
||||
budget_against = budget_against_field)
|
||||
|
||||
return flt(frappe.db.sql("""
|
||||
amount = flt(frappe.db.sql("""
|
||||
select sum(gle.debit) - sum(gle.credit)
|
||||
from `tabGL Entry` gle
|
||||
where gle.account=%(account)s
|
||||
@@ -267,7 +279,9 @@ def get_actual_expense(args):
|
||||
and gle.company=%(company)s
|
||||
and gle.docstatus=1
|
||||
{condition2}
|
||||
""".format(condition1=condition1, condition2=condition2), (args))[0][0])
|
||||
""".format(condition1=condition1, condition2=condition2), (args))[0][0]) #nosec
|
||||
|
||||
return amount
|
||||
|
||||
def get_accumulated_monthly_budget(monthly_distribution, posting_date, fiscal_year, annual_budget):
|
||||
distribution = {}
|
||||
|
||||
@@ -13,7 +13,7 @@ from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journ
|
||||
|
||||
class TestBudget(unittest.TestCase):
|
||||
def test_monthly_budget_crossed_ignore(self):
|
||||
set_total_expense_zero("2013-02-28", "Cost Center")
|
||||
set_total_expense_zero("2013-02-28", "cost_center")
|
||||
|
||||
budget = make_budget(budget_against="Cost Center")
|
||||
|
||||
@@ -26,7 +26,7 @@ class TestBudget(unittest.TestCase):
|
||||
budget.cancel()
|
||||
|
||||
def test_monthly_budget_crossed_stop1(self):
|
||||
set_total_expense_zero("2013-02-28", "Cost Center")
|
||||
set_total_expense_zero("2013-02-28", "cost_center")
|
||||
|
||||
budget = make_budget(budget_against="Cost Center")
|
||||
|
||||
@@ -41,7 +41,7 @@ class TestBudget(unittest.TestCase):
|
||||
budget.cancel()
|
||||
|
||||
def test_exception_approver_role(self):
|
||||
set_total_expense_zero("2013-02-28", "Cost Center")
|
||||
set_total_expense_zero("2013-02-28", "cost_center")
|
||||
|
||||
budget = make_budget(budget_against="Cost Center")
|
||||
|
||||
@@ -114,7 +114,7 @@ class TestBudget(unittest.TestCase):
|
||||
budget.cancel()
|
||||
|
||||
def test_monthly_budget_crossed_stop2(self):
|
||||
set_total_expense_zero("2013-02-28", "Project")
|
||||
set_total_expense_zero("2013-02-28", "project")
|
||||
|
||||
budget = make_budget(budget_against="Project")
|
||||
|
||||
@@ -129,7 +129,7 @@ class TestBudget(unittest.TestCase):
|
||||
budget.cancel()
|
||||
|
||||
def test_yearly_budget_crossed_stop1(self):
|
||||
set_total_expense_zero("2013-02-28", "Cost Center")
|
||||
set_total_expense_zero("2013-02-28", "cost_center")
|
||||
|
||||
budget = make_budget(budget_against="Cost Center")
|
||||
|
||||
@@ -141,7 +141,7 @@ class TestBudget(unittest.TestCase):
|
||||
budget.cancel()
|
||||
|
||||
def test_yearly_budget_crossed_stop2(self):
|
||||
set_total_expense_zero("2013-02-28", "Project")
|
||||
set_total_expense_zero("2013-02-28", "project")
|
||||
|
||||
budget = make_budget(budget_against="Project")
|
||||
|
||||
@@ -153,7 +153,7 @@ class TestBudget(unittest.TestCase):
|
||||
budget.cancel()
|
||||
|
||||
def test_monthly_budget_on_cancellation1(self):
|
||||
set_total_expense_zero("2013-02-28", "Cost Center")
|
||||
set_total_expense_zero("2013-02-28", "cost_center")
|
||||
|
||||
budget = make_budget(budget_against="Cost Center")
|
||||
|
||||
@@ -177,7 +177,7 @@ class TestBudget(unittest.TestCase):
|
||||
budget.cancel()
|
||||
|
||||
def test_monthly_budget_on_cancellation2(self):
|
||||
set_total_expense_zero("2013-02-28", "Project")
|
||||
set_total_expense_zero("2013-02-28", "project")
|
||||
|
||||
budget = make_budget(budget_against="Project")
|
||||
|
||||
@@ -201,8 +201,8 @@ class TestBudget(unittest.TestCase):
|
||||
budget.cancel()
|
||||
|
||||
def test_monthly_budget_against_group_cost_center(self):
|
||||
set_total_expense_zero("2013-02-28", "Cost Center")
|
||||
set_total_expense_zero("2013-02-28", "Cost Center", "_Test Cost Center 2 - _TC")
|
||||
set_total_expense_zero("2013-02-28", "cost_center")
|
||||
set_total_expense_zero("2013-02-28", "cost_center", "_Test Cost Center 2 - _TC")
|
||||
|
||||
budget = make_budget(budget_against="Cost Center", cost_center="_Test Company - _TC")
|
||||
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||
@@ -241,25 +241,30 @@ class TestBudget(unittest.TestCase):
|
||||
|
||||
|
||||
def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None):
|
||||
if budget_against_field == "Project":
|
||||
if budget_against_field == "project":
|
||||
budget_against = "_Test Project"
|
||||
else:
|
||||
budget_against = budget_against_CC or "_Test Cost Center - _TC"
|
||||
existing_expense = get_actual_expense(frappe._dict({
|
||||
|
||||
args = frappe._dict({
|
||||
"account": "_Test Account Cost for Goods Sold - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"monthly_end_date": posting_date,
|
||||
"company": "_Test Company",
|
||||
"fiscal_year": "_Test Fiscal Year 2013",
|
||||
"budget_against_field": budget_against_field,
|
||||
"budget_against": budget_against
|
||||
}))
|
||||
})
|
||||
|
||||
if not args.get(budget_against_field):
|
||||
args[budget_against_field] = budget_against
|
||||
|
||||
existing_expense = get_actual_expense(args)
|
||||
|
||||
if existing_expense:
|
||||
if budget_against_field == "Cost Center":
|
||||
if budget_against_field == "cost_center":
|
||||
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
||||
"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True)
|
||||
elif budget_against_field == "Project":
|
||||
elif budget_against_field == "project":
|
||||
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
||||
"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True, project="_Test Project", posting_date="2013-02-28")
|
||||
|
||||
|
||||
@@ -17,17 +17,60 @@ frappe.ui.form.on('Chart of Accounts Importer', {
|
||||
if (frm.page && frm.page.show_import_button) {
|
||||
create_import_button(frm);
|
||||
}
|
||||
},
|
||||
|
||||
// show download template button when company is properly selected
|
||||
if(frm.doc.company) {
|
||||
// download the csv template file
|
||||
frm.add_custom_button(__("Download template"), function () {
|
||||
let get_template_url = 'erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.download_template';
|
||||
open_url_post(frappe.request.url, { cmd: get_template_url, doctype: frm.doc.doctype });
|
||||
});
|
||||
} else {
|
||||
frm.set_value("import_file", "");
|
||||
}
|
||||
download_template: function(frm) {
|
||||
var d = new frappe.ui.Dialog({
|
||||
title: __("Download Template"),
|
||||
fields: [
|
||||
{
|
||||
label : "File Type",
|
||||
fieldname: "file_type",
|
||||
fieldtype: "Select",
|
||||
reqd: 1,
|
||||
options: ["Excel", "CSV"]
|
||||
},
|
||||
{
|
||||
label: "Template Type",
|
||||
fieldname: "template_type",
|
||||
fieldtype: "Select",
|
||||
reqd: 1,
|
||||
options: ["Sample Template", "Blank Template"],
|
||||
change: () => {
|
||||
let template_type = d.get_value('template_type');
|
||||
|
||||
if (template_type === "Sample Template") {
|
||||
d.set_df_property('template_type', 'description',
|
||||
`The Sample Template contains all the required accounts pre filled in the template.
|
||||
You can add more accounts or change existing accounts in the template as per your choice.`);
|
||||
} else {
|
||||
d.set_df_property('template_type', 'description',
|
||||
`The Blank Template contains just the account type and root type required to build the Chart
|
||||
of Accounts. Please enter the account names and add more rows as per your requirement.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
primary_action: function() {
|
||||
var data = d.get_values();
|
||||
|
||||
if (!data.template_type) {
|
||||
frappe.throw(__('Please select <b>Template Type</b> to download template'));
|
||||
}
|
||||
|
||||
open_url_post(
|
||||
'/api/method/erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.download_template',
|
||||
{
|
||||
file_type: data.file_type,
|
||||
template_type: data.template_type
|
||||
}
|
||||
);
|
||||
|
||||
d.hide();
|
||||
},
|
||||
primary_action_label: __('Download')
|
||||
});
|
||||
d.show();
|
||||
},
|
||||
|
||||
import_file: function (frm) {
|
||||
@@ -41,21 +84,24 @@ frappe.ui.form.on('Chart of Accounts Importer', {
|
||||
},
|
||||
|
||||
company: function (frm) {
|
||||
// validate that no Gl Entry record for the company exists.
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.validate_company",
|
||||
args: {
|
||||
company: frm.doc.company
|
||||
},
|
||||
callback: function(r) {
|
||||
if(r.message===false) {
|
||||
frm.set_value("company", "");
|
||||
frappe.throw(__("Transactions against the company already exist! "));
|
||||
} else {
|
||||
frm.trigger("refresh");
|
||||
if (frm.doc.company) {
|
||||
// validate that no Gl Entry record for the company exists.
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.validate_company",
|
||||
args: {
|
||||
company: frm.doc.company
|
||||
},
|
||||
callback: function(r) {
|
||||
if(r.message===false) {
|
||||
frm.set_value("company", "");
|
||||
frappe.throw(__(`Transactions against the company already exist!
|
||||
Chart Of accounts can be imported for company with no transactions`));
|
||||
} else {
|
||||
frm.trigger("refresh");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -77,7 +123,7 @@ var validate_csv_data = function(frm) {
|
||||
};
|
||||
|
||||
var create_import_button = function(frm) {
|
||||
frm.page.set_primary_action(__("Start Import"), function () {
|
||||
frm.page.set_primary_action(__("Import"), function () {
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.import_coa",
|
||||
args: {
|
||||
@@ -118,7 +164,8 @@ var generate_tree_preview = function(frm) {
|
||||
args: {
|
||||
file_name: frm.doc.import_file,
|
||||
parent: parent,
|
||||
doctype: 'Chart of Accounts Importer'
|
||||
doctype: 'Chart of Accounts Importer',
|
||||
file_type: frm.doc.file_type
|
||||
},
|
||||
onclick: function(node) {
|
||||
parent = node.value;
|
||||
|
||||
@@ -1,226 +1,71 @@
|
||||
{
|
||||
"allow_copy": 1,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2019-02-01 12:24:34.761380",
|
||||
"custom": 0,
|
||||
"actions": [],
|
||||
"allow_copy": 1,
|
||||
"creation": "2019-02-01 12:24:34.761380",
|
||||
"description": "Import Chart of Accounts from a csv file",
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Other",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Other",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"company",
|
||||
"download_template",
|
||||
"import_file",
|
||||
"chart_preview",
|
||||
"chart_tree"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Company",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Company",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Company",
|
||||
"options": "Company"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "import_file_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "",
|
||||
"depends_on": "company",
|
||||
"fieldname": "import_file",
|
||||
"fieldtype": "Attach",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Attach custom Chart of Accounts file",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"label": "Attach custom Chart of Accounts file"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"columns": 0,
|
||||
"fieldname": "chart_preview",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Chart Preview",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Chart Preview"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "chart_tree",
|
||||
"fieldtype": "HTML",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Chart Tree",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Chart Tree"
|
||||
},
|
||||
{
|
||||
"depends_on": "company",
|
||||
"fieldname": "download_template",
|
||||
"fieldtype": "Button",
|
||||
"label": "Download Template"
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 1,
|
||||
"hide_toolbar": 1,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 1,
|
||||
"is_submittable": 0,
|
||||
"issingle": 1,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-02-04 23:10:30.136807",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Chart of Accounts Importer",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
],
|
||||
"hide_toolbar": 1,
|
||||
"in_create": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2020-02-28 08:49:11.422846",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Chart of Accounts Importer",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 0,
|
||||
"email": 0,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 0,
|
||||
"read": 1,
|
||||
"report": 0,
|
||||
"role": "Accounts Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"create": 1,
|
||||
"read": 1,
|
||||
"role": "Accounts Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 1,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 0,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
||||
@@ -4,18 +4,28 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from functools import reduce
|
||||
import frappe, csv
|
||||
import frappe, csv, os
|
||||
from frappe import _
|
||||
from frappe.utils import cstr
|
||||
from frappe.utils import cstr, cint
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils.csvutils import UnicodeWriter
|
||||
from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts, build_tree_from_json
|
||||
from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file, read_xls_file_from_attached_file
|
||||
|
||||
class ChartofAccountsImporter(Document):
|
||||
pass
|
||||
|
||||
@frappe.whitelist()
|
||||
def validate_company(company):
|
||||
parent_company, allow_account_creation_against_child_company = frappe.db.get_value('Company',
|
||||
{'name': company}, ['parent_company',
|
||||
'allow_account_creation_against_child_company'])
|
||||
|
||||
if parent_company and (not allow_account_creation_against_child_company):
|
||||
frappe.throw(_("""{0} is a child company. Please import accounts against parent company
|
||||
or enable {1} in company master""").format(frappe.bold(company),
|
||||
frappe.bold('Allow Account Creation Against Child Company')), title='Wrong Company')
|
||||
|
||||
if frappe.db.get_all('GL Entry', {"company": company}, "name", limit=1):
|
||||
return False
|
||||
|
||||
@@ -25,42 +35,85 @@ def import_coa(file_name, company):
|
||||
unset_existing_data(company)
|
||||
|
||||
# create accounts
|
||||
forest = build_forest(generate_data_from_csv(file_name))
|
||||
file_doc, extension = get_file(file_name)
|
||||
|
||||
if extension == 'csv':
|
||||
data = generate_data_from_csv(file_doc)
|
||||
else:
|
||||
data = generate_data_from_excel(file_doc, extension)
|
||||
|
||||
forest = build_forest(data)
|
||||
create_charts(company, custom_chart=forest)
|
||||
|
||||
# trigger on_update for company to reset default accounts
|
||||
set_default_accounts(company)
|
||||
|
||||
def generate_data_from_csv(file_name, as_dict=False):
|
||||
''' read csv file and return the generated nested tree '''
|
||||
if not file_name.endswith('.csv'):
|
||||
frappe.throw("Only CSV files can be used to for importing data. Please check the file format you are trying to upload")
|
||||
def get_file(file_name):
|
||||
file_doc = frappe.get_doc("File", {"file_url": file_name})
|
||||
parts = file_doc.get_extension()
|
||||
extension = parts[1]
|
||||
extension = extension.lstrip(".")
|
||||
|
||||
if extension not in ('csv', 'xlsx', 'xls'):
|
||||
frappe.throw("Only CSV and Excel files can be used to for importing data. Please check the file format you are trying to upload")
|
||||
|
||||
return file_doc, extension
|
||||
|
||||
def generate_data_from_csv(file_doc, as_dict=False):
|
||||
''' read csv file and return the generated nested tree '''
|
||||
|
||||
file_doc = frappe.get_doc('File', {"file_url": file_name})
|
||||
file_path = file_doc.get_full_path()
|
||||
|
||||
data = []
|
||||
with open(file_path, 'r') as in_file:
|
||||
csv_reader = list(csv.reader(in_file))
|
||||
headers = csv_reader[1][1:]
|
||||
del csv_reader[0:2] # delete top row and headers row
|
||||
headers = csv_reader[0]
|
||||
del csv_reader[0] # delete top row and headers row
|
||||
|
||||
for row in csv_reader:
|
||||
if as_dict:
|
||||
data.append({frappe.scrub(header): row[index+1] for index, header in enumerate(headers)})
|
||||
data.append({frappe.scrub(header): row[index] for index, header in enumerate(headers)})
|
||||
else:
|
||||
if not row[2]: row[2] = row[1]
|
||||
data.append(row[1:])
|
||||
if not row[1]: row[1] = row[0]
|
||||
data.append(row)
|
||||
|
||||
# convert csv data
|
||||
return data
|
||||
|
||||
def generate_data_from_excel(file_doc, extension, as_dict=False):
|
||||
content = file_doc.get_content()
|
||||
|
||||
if extension == "xlsx":
|
||||
rows = read_xlsx_file_from_attached_file(fcontent=content)
|
||||
elif extension == "xls":
|
||||
rows = read_xls_file_from_attached_file(content)
|
||||
|
||||
data = []
|
||||
headers = rows[0]
|
||||
del rows[0]
|
||||
|
||||
for row in rows:
|
||||
if as_dict:
|
||||
data.append({frappe.scrub(header): row[index] for index, header in enumerate(headers)})
|
||||
else:
|
||||
if not row[1]: row[1] = row[0]
|
||||
data.append(row)
|
||||
|
||||
return data
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_coa(doctype, parent, is_root=False, file_name=None):
|
||||
''' called by tree view (to fetch node's children) '''
|
||||
|
||||
file_doc, extension = get_file(file_name)
|
||||
parent = None if parent==_('All Accounts') else parent
|
||||
forest = build_forest(generate_data_from_csv(file_name))
|
||||
|
||||
if extension == 'csv':
|
||||
data = generate_data_from_csv(file_doc)
|
||||
else:
|
||||
data = generate_data_from_excel(file_doc, extension)
|
||||
|
||||
forest = build_forest(data)
|
||||
accounts = build_tree_from_json("", chart_data=forest) # returns alist of dict in a tree render-able form
|
||||
|
||||
# filter out to show data for the selected node only
|
||||
@@ -91,15 +144,18 @@ def build_forest(data):
|
||||
|
||||
# returns the path of any node in list format
|
||||
def return_parent(data, child):
|
||||
from frappe import _
|
||||
|
||||
for row in data:
|
||||
account_name, parent_account = row[0:2]
|
||||
if parent_account == account_name == child:
|
||||
return [parent_account]
|
||||
elif account_name == child:
|
||||
parent_account_list = return_parent(data, parent_account)
|
||||
if not parent_account_list and parent_account:
|
||||
frappe.throw(_("The parent account {0} does not exists")
|
||||
.format(parent_account))
|
||||
if not parent_account_list:
|
||||
frappe.throw(_("The parent account {0} does not exists in the uploaded template").format(
|
||||
frappe.bold(parent_account)))
|
||||
|
||||
return [child] + parent_account_list
|
||||
|
||||
charts_map, paths = {}, []
|
||||
@@ -114,7 +170,7 @@ def build_forest(data):
|
||||
error_messages.append("Row {0}: Please enter Account Name".format(line_no))
|
||||
|
||||
charts_map[account_name] = {}
|
||||
if is_group == 1: charts_map[account_name]["is_group"] = is_group
|
||||
if cint(is_group) == 1: charts_map[account_name]["is_group"] = is_group
|
||||
if account_type: charts_map[account_name]["account_type"] = account_type
|
||||
if root_type: charts_map[account_name]["root_type"] = root_type
|
||||
if account_number: charts_map[account_name]["account_number"] = account_number
|
||||
@@ -132,24 +188,94 @@ def build_forest(data):
|
||||
|
||||
return out
|
||||
|
||||
def build_response_as_excel(writer):
|
||||
filename = frappe.generate_hash("", 10)
|
||||
with open(filename, 'wb') as f:
|
||||
f.write(cstr(writer.getvalue()).encode('utf-8'))
|
||||
f = open(filename)
|
||||
reader = csv.reader(f)
|
||||
|
||||
from frappe.utils.xlsxutils import make_xlsx
|
||||
xlsx_file = make_xlsx(reader, "Chart Of Accounts Importer Template")
|
||||
|
||||
f.close()
|
||||
os.remove(filename)
|
||||
|
||||
# write out response as a xlsx type
|
||||
frappe.response['filename'] = 'coa_importer_template.xlsx'
|
||||
frappe.response['filecontent'] = xlsx_file.getvalue()
|
||||
frappe.response['type'] = 'binary'
|
||||
|
||||
@frappe.whitelist()
|
||||
def download_template():
|
||||
def download_template(file_type, template_type):
|
||||
data = frappe._dict(frappe.local.form_dict)
|
||||
|
||||
writer = get_template(template_type)
|
||||
|
||||
if file_type == 'CSV':
|
||||
# download csv file
|
||||
frappe.response['result'] = cstr(writer.getvalue())
|
||||
frappe.response['type'] = 'csv'
|
||||
frappe.response['doctype'] = 'Chart of Accounts Importer'
|
||||
else:
|
||||
build_response_as_excel(writer)
|
||||
|
||||
def get_template(template_type):
|
||||
|
||||
fields = ["Account Name", "Parent Account", "Account Number", "Is Group", "Account Type", "Root Type"]
|
||||
writer = UnicodeWriter()
|
||||
writer.writerow(fields)
|
||||
|
||||
writer.writerow([_('Chart of Accounts Template')])
|
||||
writer.writerow([_("Column Labels : ")] + fields)
|
||||
writer.writerow([_("Start entering data from here : ")])
|
||||
if template_type == 'Blank Template':
|
||||
for root_type in get_root_types():
|
||||
writer.writerow(['', '', '', 1, '', root_type])
|
||||
|
||||
for account in get_mandatory_group_accounts():
|
||||
writer.writerow(['', '', '', 1, account, "Asset"])
|
||||
|
||||
for account_type in get_mandatory_account_types():
|
||||
writer.writerow(['', '', '', 0, account_type.get('account_type'), account_type.get('root_type')])
|
||||
else:
|
||||
writer = get_sample_template(writer)
|
||||
|
||||
return writer
|
||||
|
||||
def get_sample_template(writer):
|
||||
template = [
|
||||
["Application Of Funds(Assets)", "", "", 1, "", "Asset"],
|
||||
["Sources Of Funds(Liabilities)", "", "", 1, "", "Liability"],
|
||||
["Equity", "", "", 1, "", "Equity"],
|
||||
["Expenses", "", "", 1, "", "Expense"],
|
||||
["Income", "", "", 1, "", "Income"],
|
||||
["Bank Accounts", "Application Of Funds(Assets)", "", 1, "Bank", "Asset"],
|
||||
["Cash In Hand", "Application Of Funds(Assets)", "", 1, "Cash", "Asset"],
|
||||
["Stock Assets", "Application Of Funds(Assets)", "", 1, "Stock", "Asset"],
|
||||
["Cost Of Goods Sold", "Expenses", "", 0, "Cost of Goods Sold", "Expense"],
|
||||
["Asset Depreciation", "Expenses", "", 0, "Depreciation", "Expense"],
|
||||
["Fixed Assets", "Application Of Funds(Assets)", "", 0, "Fixed Asset", "Asset"],
|
||||
["Accounts Payable", "Sources Of Funds(Liabilities)", "", 0, "Payable", "Liability"],
|
||||
["Accounts Receivable", "Application Of Funds(Assets)", "", 1, "Receivable", "Asset"],
|
||||
["Stock Expenses", "Expenses", "", 0, "Stock Adjustment", "Expense"],
|
||||
["Sample Bank", "Bank Accounts", "", 0, "Bank", "Asset"],
|
||||
["Cash", "Cash In Hand", "", 0, "Cash", "Asset"],
|
||||
["Stores", "Stock Assets", "", 0, "Stock", "Asset"],
|
||||
]
|
||||
|
||||
for row in template:
|
||||
writer.writerow(row)
|
||||
|
||||
return writer
|
||||
|
||||
# download csv file
|
||||
frappe.response['result'] = cstr(writer.getvalue())
|
||||
frappe.response['type'] = 'csv'
|
||||
frappe.response['doctype'] = data.get('doctype')
|
||||
|
||||
@frappe.whitelist()
|
||||
def validate_accounts(file_name):
|
||||
accounts = generate_data_from_csv(file_name, as_dict=True)
|
||||
|
||||
file_doc, extension = get_file(file_name)
|
||||
|
||||
if extension == 'csv':
|
||||
accounts = generate_data_from_csv(file_doc, as_dict=True)
|
||||
else:
|
||||
accounts = generate_data_from_excel(file_doc, extension, as_dict=True)
|
||||
|
||||
accounts_dict = {}
|
||||
for account in accounts:
|
||||
@@ -174,12 +300,38 @@ def validate_root(accounts):
|
||||
for account in roots:
|
||||
if not account.get("root_type") and account.get("account_name"):
|
||||
error_messages.append("Please enter Root Type for account- {0}".format(account.get("account_name")))
|
||||
elif account.get("root_type") not in ("Asset", "Liability", "Expense", "Income", "Equity") and account.get("account_name"):
|
||||
elif account.get("root_type") not in get_root_types() and account.get("account_name"):
|
||||
error_messages.append("Root Type for {0} must be one of the Asset, Liability, Income, Expense and Equity".format(account.get("account_name")))
|
||||
|
||||
if error_messages:
|
||||
return "<br>".join(error_messages)
|
||||
|
||||
def get_root_types():
|
||||
return ('Asset', 'Liability', 'Expense', 'Income', 'Equity')
|
||||
|
||||
def get_report_type(root_type):
|
||||
if root_type in ('Asset', 'Liability', 'Equity'):
|
||||
return 'Balance Sheet'
|
||||
else:
|
||||
return 'Profit and Loss'
|
||||
|
||||
def get_mandatory_group_accounts():
|
||||
return ('Bank', 'Cash', 'Stock')
|
||||
|
||||
def get_mandatory_account_types():
|
||||
return [
|
||||
{'account_type': 'Cost of Goods Sold', 'root_type': 'Expense'},
|
||||
{'account_type': 'Depreciation', 'root_type': 'Expense'},
|
||||
{'account_type': 'Fixed Asset', 'root_type': 'Asset'},
|
||||
{'account_type': 'Payable', 'root_type': 'Liability'},
|
||||
{'account_type': 'Receivable', 'root_type': 'Asset'},
|
||||
{'account_type': 'Stock Adjustment', 'root_type': 'Expense'},
|
||||
{'account_type': 'Bank', 'root_type': 'Asset'},
|
||||
{'account_type': 'Cash', 'root_type': 'Asset'},
|
||||
{'account_type': 'Stock', 'root_type': 'Asset'}
|
||||
]
|
||||
|
||||
|
||||
def validate_account_types(accounts):
|
||||
account_types_for_ledger = ["Cost of Goods Sold", "Depreciation", "Fixed Asset", "Payable", "Receivable", "Stock Adjustment"]
|
||||
account_types = [accounts[d]["account_type"] for d in accounts if not accounts[d]['is_group'] == 1]
|
||||
|
||||
@@ -18,7 +18,7 @@ frappe.ui.form.on('Cost Center', {
|
||||
},
|
||||
refresh: function(frm) {
|
||||
if (!frm.is_new()) {
|
||||
frm.add_custom_button(__('Update Cost Center Number'), function () {
|
||||
frm.add_custom_button(__('Update Cost Center Name / Number'), function () {
|
||||
frm.trigger("update_cost_center_number");
|
||||
});
|
||||
}
|
||||
@@ -47,35 +47,51 @@ frappe.ui.form.on('Cost Center', {
|
||||
},
|
||||
update_cost_center_number: function(frm) {
|
||||
var d = new frappe.ui.Dialog({
|
||||
title: __('Update Cost Center Number'),
|
||||
title: __('Update Cost Center Name / Number'),
|
||||
fields: [
|
||||
{
|
||||
"label": 'Cost Center Number',
|
||||
"label": "Cost Center Name",
|
||||
"fieldname": "cost_center_name",
|
||||
"fieldtype": "Data",
|
||||
"reqd": 1,
|
||||
"default": frm.doc.cost_center_name
|
||||
},
|
||||
{
|
||||
"label": "Cost Center Number",
|
||||
"fieldname": "cost_center_number",
|
||||
"fieldtype": "Data",
|
||||
"reqd": 1
|
||||
"default": frm.doc.cost_center_number
|
||||
},
|
||||
{
|
||||
"label": __("Merge with existing"),
|
||||
"fieldname": "merge",
|
||||
"fieldtype": "Check",
|
||||
"default": 0
|
||||
}
|
||||
],
|
||||
primary_action: function() {
|
||||
var data = d.get_values();
|
||||
if(data.cost_center_number === frm.doc.cost_center_number) {
|
||||
if(data.cost_center_name === frm.doc.cost_center_name && data.cost_center_number === frm.doc.cost_center_number) {
|
||||
d.hide();
|
||||
return;
|
||||
}
|
||||
frappe.dom.freeze();
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.utils.update_number_field",
|
||||
method: "erpnext.accounts.utils.update_cost_center",
|
||||
args: {
|
||||
doctype_name: frm.doc.doctype,
|
||||
name: frm.doc.name,
|
||||
field_name: d.fields[0].fieldname,
|
||||
number_value: data.cost_center_number,
|
||||
company: frm.doc.company
|
||||
docname: frm.doc.name,
|
||||
cost_center_name: data.cost_center_name,
|
||||
cost_center_number: cstr(data.cost_center_number),
|
||||
company: frm.doc.company,
|
||||
merge: data.merge
|
||||
},
|
||||
callback: function(r) {
|
||||
frappe.dom.unfreeze();
|
||||
if(!r.exc) {
|
||||
if(r.message) {
|
||||
frappe.set_route("Form", "Cost Center", r.message);
|
||||
} else {
|
||||
me.frm.set_value("cost_center_name", data.cost_center_name);
|
||||
me.frm.set_value("cost_center_number", data.cost_center_number);
|
||||
}
|
||||
d.hide();
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
"actions": [],
|
||||
"allow_copy": 1,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"creation": "2013-01-23 19:57:17",
|
||||
"description": "Track separate Income and Expense for product verticals or divisions.",
|
||||
"doctype": "DocType",
|
||||
@@ -126,7 +125,7 @@
|
||||
"idx": 1,
|
||||
"is_tree": 1,
|
||||
"links": [],
|
||||
"modified": "2020-03-18 18:26:01.540170",
|
||||
"modified": "2020-06-12 16:09:30.025214",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Cost Center",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe, erpnext
|
||||
from frappe import _
|
||||
from frappe.utils import flt, fmt_money, getdate, formatdate
|
||||
from frappe.utils import flt, fmt_money, getdate, formatdate, cint
|
||||
from frappe.model.document import Document
|
||||
from frappe.model.naming import set_name_from_naming_options
|
||||
from frappe.model.meta import get_field_precision
|
||||
@@ -75,12 +75,6 @@ class GLEntry(Document):
|
||||
if not self.cost_center and self.voucher_type != 'Period Closing Voucher':
|
||||
frappe.throw(_("{0} {1}: Cost Center is required for 'Profit and Loss' account {2}. Please set up a default Cost Center for the Company.")
|
||||
.format(self.voucher_type, self.voucher_no, self.account))
|
||||
else:
|
||||
from erpnext.accounts.utils import get_allow_cost_center_in_entry_of_bs_account
|
||||
if not get_allow_cost_center_in_entry_of_bs_account() and self.cost_center:
|
||||
self.cost_center = None
|
||||
if self.project:
|
||||
self.project = None
|
||||
|
||||
def validate_dimensions_for_pl_and_bs(self):
|
||||
|
||||
@@ -115,8 +109,8 @@ class GLEntry(Document):
|
||||
from tabAccount where name=%s""", self.account, as_dict=1)[0]
|
||||
|
||||
if ret.is_group==1:
|
||||
frappe.throw(_("{0} {1}: Account {2} cannot be a Group")
|
||||
.format(self.voucher_type, self.voucher_no, self.account))
|
||||
frappe.throw(_('''{0} {1}: Account {2} is a Group Account and group accounts cannot be used in
|
||||
transactions''').format(self.voucher_type, self.voucher_no, self.account))
|
||||
|
||||
if ret.docstatus==2:
|
||||
frappe.throw(_("{0} {1}: Account {2} is inactive")
|
||||
@@ -137,10 +131,17 @@ class GLEntry(Document):
|
||||
|
||||
return self.cost_center_company[self.cost_center]
|
||||
|
||||
def _check_is_group():
|
||||
return cint(frappe.get_cached_value('Cost Center', self.cost_center, 'is_group'))
|
||||
|
||||
if self.cost_center and _get_cost_center_company() != self.company:
|
||||
frappe.throw(_("{0} {1}: Cost Center {2} does not belong to Company {3}")
|
||||
.format(self.voucher_type, self.voucher_no, self.cost_center, self.company))
|
||||
|
||||
if self.cost_center and _check_is_group():
|
||||
frappe.throw(_("""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot
|
||||
be used in transactions""").format(self.voucher_type, self.voucher_no, frappe.bold(self.cost_center)))
|
||||
|
||||
def validate_party(self):
|
||||
validate_party_frozen_disabled(self.party_type, self.party)
|
||||
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe, json
|
||||
import frappe, json, erpnext
|
||||
from frappe import _
|
||||
from frappe.utils import flt, getdate, nowdate, add_days
|
||||
from erpnext.controllers.accounts_controller import AccountsController
|
||||
from erpnext.accounts.general_ledger import make_gl_entries
|
||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
|
||||
|
||||
class InvoiceDiscounting(AccountsController):
|
||||
def validate(self):
|
||||
@@ -81,10 +82,15 @@ class InvoiceDiscounting(AccountsController):
|
||||
def make_gl_entries(self):
|
||||
company_currency = frappe.get_cached_value('Company', self.company, "default_currency")
|
||||
|
||||
|
||||
gl_entries = []
|
||||
invoice_fields = ["debit_to", "party_account_currency", "conversion_rate", "cost_center"]
|
||||
accounting_dimensions = get_accounting_dimensions()
|
||||
|
||||
invoice_fields.extend(accounting_dimensions)
|
||||
|
||||
for d in self.invoices:
|
||||
inv = frappe.db.get_value("Sales Invoice", d.sales_invoice,
|
||||
["debit_to", "party_account_currency", "conversion_rate", "cost_center"], as_dict=1)
|
||||
inv = frappe.db.get_value("Sales Invoice", d.sales_invoice, invoice_fields, as_dict=1)
|
||||
|
||||
if d.outstanding_amount:
|
||||
outstanding_in_company_currency = flt(d.outstanding_amount * inv.conversion_rate,
|
||||
@@ -102,7 +108,7 @@ class InvoiceDiscounting(AccountsController):
|
||||
"cost_center": inv.cost_center,
|
||||
"against_voucher": d.sales_invoice,
|
||||
"against_voucher_type": "Sales Invoice"
|
||||
}, inv.party_account_currency))
|
||||
}, inv.party_account_currency, item=inv))
|
||||
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
"account": self.accounts_receivable_credit,
|
||||
@@ -115,7 +121,7 @@ class InvoiceDiscounting(AccountsController):
|
||||
"cost_center": inv.cost_center,
|
||||
"against_voucher": d.sales_invoice,
|
||||
"against_voucher_type": "Sales Invoice"
|
||||
}, ar_credit_account_currency))
|
||||
}, ar_credit_account_currency, item=inv))
|
||||
|
||||
make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding='No')
|
||||
|
||||
@@ -128,16 +134,19 @@ class InvoiceDiscounting(AccountsController):
|
||||
je.append("accounts", {
|
||||
"account": self.bank_account,
|
||||
"debit_in_account_currency": flt(self.total_amount) - flt(self.bank_charges),
|
||||
"cost_center": erpnext.get_default_cost_center(self.company)
|
||||
})
|
||||
|
||||
je.append("accounts", {
|
||||
"account": self.bank_charges_account,
|
||||
"debit_in_account_currency": flt(self.bank_charges)
|
||||
"debit_in_account_currency": flt(self.bank_charges),
|
||||
"cost_center": erpnext.get_default_cost_center(self.company)
|
||||
})
|
||||
|
||||
je.append("accounts", {
|
||||
"account": self.short_term_loan,
|
||||
"credit_in_account_currency": flt(self.total_amount),
|
||||
"cost_center": erpnext.get_default_cost_center(self.company),
|
||||
"reference_type": "Invoice Discounting",
|
||||
"reference_name": self.name
|
||||
})
|
||||
@@ -145,6 +154,7 @@ class InvoiceDiscounting(AccountsController):
|
||||
je.append("accounts", {
|
||||
"account": self.accounts_receivable_discounted,
|
||||
"debit_in_account_currency": flt(d.outstanding_amount),
|
||||
"cost_center": erpnext.get_default_cost_center(self.company),
|
||||
"reference_type": "Invoice Discounting",
|
||||
"reference_name": self.name,
|
||||
"party_type": "Customer",
|
||||
@@ -154,6 +164,7 @@ class InvoiceDiscounting(AccountsController):
|
||||
je.append("accounts", {
|
||||
"account": self.accounts_receivable_credit,
|
||||
"credit_in_account_currency": flt(d.outstanding_amount),
|
||||
"cost_center": erpnext.get_default_cost_center(self.company),
|
||||
"reference_type": "Invoice Discounting",
|
||||
"reference_name": self.name,
|
||||
"party_type": "Customer",
|
||||
@@ -171,13 +182,15 @@ class InvoiceDiscounting(AccountsController):
|
||||
je.append("accounts", {
|
||||
"account": self.short_term_loan,
|
||||
"debit_in_account_currency": flt(self.total_amount),
|
||||
"cost_center": erpnext.get_default_cost_center(self.company),
|
||||
"reference_type": "Invoice Discounting",
|
||||
"reference_name": self.name,
|
||||
})
|
||||
|
||||
je.append("accounts", {
|
||||
"account": self.bank_account,
|
||||
"credit_in_account_currency": flt(self.total_amount)
|
||||
"credit_in_account_currency": flt(self.total_amount),
|
||||
"cost_center": erpnext.get_default_cost_center(self.company)
|
||||
})
|
||||
|
||||
if getdate(self.loan_end_date) > getdate(nowdate()):
|
||||
@@ -187,6 +200,7 @@ class InvoiceDiscounting(AccountsController):
|
||||
je.append("accounts", {
|
||||
"account": self.accounts_receivable_discounted,
|
||||
"credit_in_account_currency": flt(outstanding_amount),
|
||||
"cost_center": erpnext.get_default_cost_center(self.company),
|
||||
"reference_type": "Invoice Discounting",
|
||||
"reference_name": self.name,
|
||||
"party_type": "Customer",
|
||||
@@ -196,6 +210,7 @@ class InvoiceDiscounting(AccountsController):
|
||||
je.append("accounts", {
|
||||
"account": self.accounts_receivable_unpaid,
|
||||
"debit_in_account_currency": flt(outstanding_amount),
|
||||
"cost_center": erpnext.get_default_cost_center(self.company),
|
||||
"reference_type": "Invoice Discounting",
|
||||
"reference_name": self.name,
|
||||
"party_type": "Customer",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
{
|
||||
"doctype": "Item Tax Template",
|
||||
"title": "_Test Account Excise Duty @ 10",
|
||||
"company": "_Test Company",
|
||||
"taxes": [
|
||||
{
|
||||
"doctype": "Item Tax Template Detail",
|
||||
@@ -14,6 +15,7 @@
|
||||
{
|
||||
"doctype": "Item Tax Template",
|
||||
"title": "_Test Account Excise Duty @ 12",
|
||||
"company": "_Test Company",
|
||||
"taxes": [
|
||||
{
|
||||
"doctype": "Item Tax Template Detail",
|
||||
@@ -26,6 +28,7 @@
|
||||
{
|
||||
"doctype": "Item Tax Template",
|
||||
"title": "_Test Account Excise Duty @ 15",
|
||||
"company": "_Test Company",
|
||||
"taxes": [
|
||||
{
|
||||
"doctype": "Item Tax Template Detail",
|
||||
@@ -38,6 +41,7 @@
|
||||
{
|
||||
"doctype": "Item Tax Template",
|
||||
"title": "_Test Account Excise Duty @ 20",
|
||||
"company": "_Test Company",
|
||||
"taxes": [
|
||||
{
|
||||
"doctype": "Item Tax Template Detail",
|
||||
@@ -50,6 +54,7 @@
|
||||
{
|
||||
"doctype": "Item Tax Template",
|
||||
"title": "_Test Item Tax Template 1",
|
||||
"company": "_Test Company",
|
||||
"taxes": [
|
||||
{
|
||||
"doctype": "Item Tax Template Detail",
|
||||
|
||||
@@ -561,20 +561,20 @@ class JournalEntry(AccountsController):
|
||||
|
||||
if self.write_off_based_on == 'Accounts Receivable':
|
||||
jd1.party_type = "Customer"
|
||||
jd1.credit = flt(d.outstanding_amount, self.precision("credit", "accounts"))
|
||||
jd1.credit_in_account_currency = flt(d.outstanding_amount, self.precision("credit", "accounts"))
|
||||
jd1.reference_type = "Sales Invoice"
|
||||
jd1.reference_name = cstr(d.name)
|
||||
elif self.write_off_based_on == 'Accounts Payable':
|
||||
jd1.party_type = "Supplier"
|
||||
jd1.debit = flt(d.outstanding_amount, self.precision("debit", "accounts"))
|
||||
jd1.debit_in_account_currency = flt(d.outstanding_amount, self.precision("debit", "accounts"))
|
||||
jd1.reference_type = "Purchase Invoice"
|
||||
jd1.reference_name = cstr(d.name)
|
||||
|
||||
jd2 = self.append('accounts', {})
|
||||
if self.write_off_based_on == 'Accounts Receivable':
|
||||
jd2.debit = total
|
||||
jd2.debit_in_account_currency = total
|
||||
elif self.write_off_based_on == 'Accounts Payable':
|
||||
jd2.credit = total
|
||||
jd2.credit_in_account_currency = total
|
||||
|
||||
self.validate_total_debit_and_credit()
|
||||
|
||||
@@ -836,13 +836,34 @@ def get_opening_accounts(company):
|
||||
return [{"account": a, "balance": get_balance_on(a)} for a in accounts]
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
|
||||
return frappe.db.sql("""select jv.name, jv.posting_date, jv.user_remark
|
||||
from `tabJournal Entry` jv, `tabJournal Entry Account` jv_detail
|
||||
where jv_detail.parent = jv.name and jv_detail.account = %s and ifnull(jv_detail.party, '') = %s
|
||||
and (jv_detail.reference_type is null or jv_detail.reference_type = '')
|
||||
and jv.docstatus = 1 and jv.`{0}` like %s order by jv.name desc limit %s, %s""".format(searchfield),
|
||||
(filters.get("account"), cstr(filters.get("party")), "%{0}%".format(txt), start, page_len))
|
||||
if not frappe.db.has_column('Journal Entry', searchfield):
|
||||
return []
|
||||
|
||||
return frappe.db.sql("""
|
||||
SELECT jv.name, jv.posting_date, jv.user_remark
|
||||
FROM `tabJournal Entry` jv, `tabJournal Entry Account` jv_detail
|
||||
WHERE jv_detail.parent = jv.name
|
||||
AND jv_detail.account = %(account)s
|
||||
AND IFNULL(jv_detail.party, '') = %(party)s
|
||||
AND (
|
||||
jv_detail.reference_type IS NULL
|
||||
OR jv_detail.reference_type = ''
|
||||
)
|
||||
AND jv.docstatus = 1
|
||||
AND jv.`{0}` LIKE %(txt)s
|
||||
ORDER BY jv.name DESC
|
||||
LIMIT %(offset)s, %(limit)s
|
||||
""".format(searchfield), dict(
|
||||
account=filters.get("account"),
|
||||
party=cstr(filters.get("party")),
|
||||
txt="%{0}%".format(txt),
|
||||
offset=start,
|
||||
limit=page_len
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
||||
@@ -204,11 +204,8 @@ class TestJournalEntry(unittest.TestCase):
|
||||
self.assertEqual(jv.inter_company_journal_entry_reference, "")
|
||||
self.assertEqual(jv1.inter_company_journal_entry_reference, "")
|
||||
|
||||
def test_jv_for_enable_allow_cost_center_in_entry_of_bs_account(self):
|
||||
def test_jv_with_cost_centre(self):
|
||||
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
|
||||
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
|
||||
accounts_settings.allow_cost_center_in_entry_of_bs_account = 1
|
||||
accounts_settings.save()
|
||||
cost_center = "_Test Cost Center for BS Account - _TC"
|
||||
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
|
||||
jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, cost_center = cost_center, save=False)
|
||||
@@ -237,15 +234,45 @@ class TestJournalEntry(unittest.TestCase):
|
||||
for gle in gl_entries:
|
||||
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
|
||||
|
||||
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
|
||||
accounts_settings.save()
|
||||
def test_jv_with_project(self):
|
||||
from erpnext.projects.doctype.project.test_project import make_project
|
||||
project = make_project({
|
||||
'project_name': 'Journal Entry Project',
|
||||
'project_template_name': 'Test Project Template',
|
||||
'start_date': '2020-01-01'
|
||||
})
|
||||
|
||||
def test_jv_account_and_party_balance_for_enable_allow_cost_center_in_entry_of_bs_account(self):
|
||||
jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, save=False)
|
||||
for d in jv.accounts:
|
||||
d.project = project.project_name
|
||||
jv.voucher_type = "Bank Entry"
|
||||
jv.multi_currency = 0
|
||||
jv.cheque_no = "112233"
|
||||
jv.cheque_date = nowdate()
|
||||
jv.insert()
|
||||
jv.submit()
|
||||
|
||||
expected_values = {
|
||||
"_Test Cash - _TC": {
|
||||
"project": project.project_name
|
||||
},
|
||||
"_Test Bank - _TC": {
|
||||
"project": project.project_name
|
||||
}
|
||||
}
|
||||
|
||||
gl_entries = frappe.db.sql("""select account, project, debit, credit
|
||||
from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
|
||||
order by account asc""", jv.name, as_dict=1)
|
||||
|
||||
self.assertTrue(gl_entries)
|
||||
|
||||
for gle in gl_entries:
|
||||
self.assertEqual(expected_values[gle.account]["project"], gle.project)
|
||||
|
||||
def test_jv_account_and_party_balance_with_cost_centre(self):
|
||||
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
|
||||
from erpnext.accounts.utils import get_balance_on
|
||||
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
|
||||
accounts_settings.allow_cost_center_in_entry_of_bs_account = 1
|
||||
accounts_settings.save()
|
||||
cost_center = "_Test Cost Center for BS Account - _TC"
|
||||
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
|
||||
jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, cost_center = cost_center, save=False)
|
||||
@@ -261,9 +288,6 @@ class TestJournalEntry(unittest.TestCase):
|
||||
account_balance = get_balance_on(account="_Test Bank - _TC", cost_center=cost_center)
|
||||
self.assertEqual(expected_account_balance, account_balance)
|
||||
|
||||
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
|
||||
accounts_settings.save()
|
||||
|
||||
def make_journal_entry(account1, account2, amount, cost_center=None, posting_date=None, exchange_rate=1, save=True, submit=False, project=None):
|
||||
if not cost_center:
|
||||
cost_center = "_Test Cost Center - _TC"
|
||||
|
||||
@@ -41,10 +41,16 @@ def get_loyalty_program_details_with_points(customer, loyalty_program=None, expi
|
||||
loyalty_program = frappe.get_doc("Loyalty Program", loyalty_program)
|
||||
lp_details.update(get_loyalty_details(customer, loyalty_program.name, expiry_date, company, include_expired_entry))
|
||||
|
||||
tier_spent_level = sorted([d.as_dict() for d in loyalty_program.collection_rules],
|
||||
key=lambda rule:rule.min_spent, reverse=True)
|
||||
# sort collection rule, first item on list will be lowest min_spent
|
||||
tier_spent_level = sorted(
|
||||
[d.as_dict() for d in loyalty_program.collection_rules],
|
||||
key=lambda rule: rule.min_spent, reverse=False,
|
||||
)
|
||||
|
||||
# looping and apply tier from lowest min_spent
|
||||
for i, d in enumerate(tier_spent_level):
|
||||
if i==0 or (lp_details.total_spent+current_transaction_amount) <= d.min_spent:
|
||||
# if cumulative spend more than min_spent then continue to next tier
|
||||
if (lp_details.total_spent + current_transaction_amount) >= d.min_spent:
|
||||
lp_details.tier_name = d.tier_name
|
||||
lp_details.collection_factor = d.collection_factor
|
||||
else:
|
||||
|
||||
@@ -68,6 +68,7 @@ class TestLoyaltyProgram(unittest.TestCase):
|
||||
|
||||
lpe = frappe.get_doc('Loyalty Point Entry', {'sales_invoice': si_original.name, 'customer': si_original.customer})
|
||||
|
||||
customer.load_from_db()
|
||||
self.assertEqual(si_original.get('loyalty_program'), customer.loyalty_program)
|
||||
self.assertEqual(lpe.get('loyalty_program_tier'), customer.loyalty_program_tier)
|
||||
self.assertEqual(lpe.loyalty_points, earned_points)
|
||||
|
||||
@@ -68,6 +68,9 @@ class OpeningInvoiceCreationTool(Document):
|
||||
if not self.company:
|
||||
frappe.throw(_("Please select the Company"))
|
||||
|
||||
company_details = frappe.get_cached_value('Company', self.company,
|
||||
["default_currency", "default_letter_head"], as_dict=1) or {}
|
||||
|
||||
for row in self.invoices:
|
||||
if not row.qty:
|
||||
row.qty = 1.0
|
||||
@@ -99,6 +102,12 @@ class OpeningInvoiceCreationTool(Document):
|
||||
if not args:
|
||||
continue
|
||||
|
||||
if company_details:
|
||||
args.update({
|
||||
"currency": company_details.get("default_currency"),
|
||||
"letter_head": company_details.get("default_letter_head")
|
||||
})
|
||||
|
||||
doc = frappe.get_doc(args).insert()
|
||||
doc.submit()
|
||||
names.append(doc.name)
|
||||
@@ -172,8 +181,7 @@ class OpeningInvoiceCreationTool(Document):
|
||||
"due_date": row.due_date,
|
||||
"posting_date": row.posting_date,
|
||||
frappe.scrub(party_type): row.party,
|
||||
"doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice",
|
||||
"currency": frappe.get_cached_value('Company', self.company, "default_currency")
|
||||
"doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice"
|
||||
})
|
||||
|
||||
accounting_dimension = get_accounting_dimensions()
|
||||
|
||||
@@ -25,7 +25,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
});
|
||||
frm.set_query("party_type", function() {
|
||||
return{
|
||||
"filters": {
|
||||
filters: {
|
||||
"name": ["in", Object.keys(frappe.boot.party_account_types)],
|
||||
}
|
||||
}
|
||||
@@ -33,14 +33,16 @@ frappe.ui.form.on('Payment Entry', {
|
||||
frm.set_query("party_bank_account", function() {
|
||||
return {
|
||||
filters: {
|
||||
"is_company_account":0
|
||||
is_company_account: 0,
|
||||
party_type: frm.doc.party_type,
|
||||
party: frm.doc.party
|
||||
}
|
||||
}
|
||||
});
|
||||
frm.set_query("bank_account", function() {
|
||||
return {
|
||||
filters: {
|
||||
"is_company_account":1
|
||||
is_company_account: 1
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -104,6 +106,21 @@ frappe.ui.form.on('Payment Entry', {
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query('payment_term', 'references', function(frm, cdt, cdn) {
|
||||
const child = locals[cdt][cdn];
|
||||
if (in_list(['Purchase Invoice', 'Sales Invoice'], child.reference_doctype) && child.reference_name) {
|
||||
let payment_term_list = frappe.get_list('Payment Schedule', {'parent': child.reference_name});
|
||||
|
||||
payment_term_list = payment_term_list.map(pt => pt.payment_term);
|
||||
|
||||
return {
|
||||
filters: {
|
||||
'name': ['in', payment_term_list]
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
frm.set_query("reference_name", "references", function(doc, cdt, cdn) {
|
||||
const child = locals[cdt][cdn];
|
||||
const filters = {"docstatus": 1, "company": doc.company};
|
||||
@@ -272,7 +289,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
frm.set_value("contact_email", "");
|
||||
frm.set_value("contact_person", "");
|
||||
}
|
||||
if(frm.doc.payment_type && frm.doc.party_type && frm.doc.party) {
|
||||
if(frm.doc.payment_type && frm.doc.party_type && frm.doc.party && frm.doc.company) {
|
||||
if(!frm.doc.posting_date) {
|
||||
frappe.msgprint(__("Please select Posting Date before selecting Party"))
|
||||
frm.set_value("party", "");
|
||||
@@ -311,7 +328,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
() => {
|
||||
frm.set_party_account_based_on_party = false;
|
||||
if (r.message.bank_account) {
|
||||
frm.set_value("party_bank_account", r.message.bank_account);
|
||||
frm.set_value("bank_account", r.message.bank_account);
|
||||
}
|
||||
}
|
||||
]);
|
||||
@@ -1018,4 +1035,4 @@ frappe.ui.form.on('Payment Entry', {
|
||||
});
|
||||
}
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -6,7 +6,7 @@ from __future__ import unicode_literals
|
||||
import frappe, erpnext, json
|
||||
from frappe import _, scrub, ValidationError
|
||||
from frappe.utils import flt, comma_or, nowdate, getdate
|
||||
from erpnext.accounts.utils import get_outstanding_invoices, get_account_currency, get_balance_on, get_allow_cost_center_in_entry_of_bs_account
|
||||
from erpnext.accounts.utils import get_outstanding_invoices, get_account_currency, get_balance_on
|
||||
from erpnext.accounts.party import get_party_account
|
||||
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
|
||||
from erpnext.setup.utils import get_exchange_rate
|
||||
@@ -60,6 +60,7 @@ class PaymentEntry(AccountsController):
|
||||
self.set_remarks()
|
||||
self.validate_duplicate_entry()
|
||||
self.validate_allocated_amount()
|
||||
self.validate_paid_invoices()
|
||||
self.ensure_supplier_is_not_blocked()
|
||||
self.set_status()
|
||||
|
||||
@@ -71,9 +72,9 @@ class PaymentEntry(AccountsController):
|
||||
self.update_outstanding_amounts()
|
||||
self.update_advance_paid()
|
||||
self.update_expense_claim()
|
||||
self.update_payment_schedule()
|
||||
self.set_status()
|
||||
|
||||
|
||||
def on_cancel(self):
|
||||
self.setup_party_account_field()
|
||||
self.make_gl_entries(cancel=1)
|
||||
@@ -81,9 +82,10 @@ class PaymentEntry(AccountsController):
|
||||
self.update_advance_paid()
|
||||
self.update_expense_claim()
|
||||
self.delink_advance_entry_references()
|
||||
self.update_payment_schedule(cancel=1)
|
||||
self.set_payment_req_status()
|
||||
self.set_status()
|
||||
|
||||
|
||||
def set_payment_req_status(self):
|
||||
from erpnext.accounts.doctype.payment_request.payment_request import update_payment_req_status
|
||||
update_payment_req_status(self, None)
|
||||
@@ -94,10 +96,10 @@ class PaymentEntry(AccountsController):
|
||||
def validate_duplicate_entry(self):
|
||||
reference_names = []
|
||||
for d in self.get("references"):
|
||||
if (d.reference_doctype, d.reference_name) in reference_names:
|
||||
if (d.reference_doctype, d.reference_name, d.payment_term) in reference_names:
|
||||
frappe.throw(_("Row #{0}: Duplicate entry in References {1} {2}")
|
||||
.format(d.idx, d.reference_doctype, d.reference_name))
|
||||
reference_names.append((d.reference_doctype, d.reference_name))
|
||||
reference_names.append((d.reference_doctype, d.reference_name, d.payment_term))
|
||||
|
||||
def set_bank_account_data(self):
|
||||
if self.bank_account:
|
||||
@@ -225,6 +227,8 @@ class PaymentEntry(AccountsController):
|
||||
valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry")
|
||||
elif self.party_type == "Employee":
|
||||
valid_reference_doctypes = ("Expense Claim", "Journal Entry", "Employee Advance")
|
||||
elif self.party_type == "Shareholder":
|
||||
valid_reference_doctypes = ("Journal Entry")
|
||||
|
||||
for d in self.get("references"):
|
||||
if not d.allocated_amount:
|
||||
@@ -264,6 +268,25 @@ class PaymentEntry(AccountsController):
|
||||
frappe.throw(_("{0} {1} must be submitted")
|
||||
.format(d.reference_doctype, d.reference_name))
|
||||
|
||||
def validate_paid_invoices(self):
|
||||
no_oustanding_refs = {}
|
||||
|
||||
for d in self.get("references"):
|
||||
if not d.allocated_amount:
|
||||
continue
|
||||
|
||||
if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Fees"):
|
||||
outstanding_amount, is_return = frappe.get_cached_value(d.reference_doctype, d.reference_name, ["outstanding_amount", "is_return"])
|
||||
if outstanding_amount <= 0 and not is_return:
|
||||
no_oustanding_refs.setdefault(d.reference_doctype, []).append(d)
|
||||
|
||||
for k, v in no_oustanding_refs.items():
|
||||
frappe.msgprint(_("{} - {} now have {} as they had no outstanding amount left before submitting the Payment Entry.<br><br>\
|
||||
If this is undesirable please cancel the corresponding Payment Entry.")
|
||||
.format(k, frappe.bold(", ".join([d.reference_name for d in v])), frappe.bold("negative outstanding amount")),
|
||||
title=_("Warning"), indicator="orange")
|
||||
|
||||
|
||||
def validate_journal_entry(self):
|
||||
for d in self.get("references"):
|
||||
if d.allocated_amount and d.reference_doctype == "Journal Entry":
|
||||
@@ -285,6 +308,38 @@ class PaymentEntry(AccountsController):
|
||||
frappe.throw(_("Against Journal Entry {0} does not have any unmatched {1} entry")
|
||||
.format(d.reference_name, dr_or_cr))
|
||||
|
||||
def update_payment_schedule(self, cancel=0):
|
||||
invoice_payment_amount_map = {}
|
||||
invoice_paid_amount_map = {}
|
||||
|
||||
for reference in self.get('references'):
|
||||
if reference.payment_term and reference.reference_name:
|
||||
key = (reference.payment_term, reference.reference_name)
|
||||
invoice_payment_amount_map.setdefault(key, 0.0)
|
||||
invoice_payment_amount_map[key] += reference.allocated_amount
|
||||
|
||||
if not invoice_paid_amount_map.get(key):
|
||||
payment_schedule = frappe.get_all('Payment Schedule', filters={'parent': reference.reference_name},
|
||||
fields=['paid_amount', 'payment_amount', 'payment_term'])
|
||||
for term in payment_schedule:
|
||||
invoice_key = (term.payment_term, reference.reference_name)
|
||||
invoice_paid_amount_map.setdefault(invoice_key, {})
|
||||
invoice_paid_amount_map[invoice_key]['outstanding'] = term.payment_amount - term.paid_amount
|
||||
|
||||
for key, amount in iteritems(invoice_payment_amount_map):
|
||||
if cancel:
|
||||
frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` - %s
|
||||
WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0]))
|
||||
else:
|
||||
outstanding = flt(invoice_paid_amount_map.get(key, {}).get('outstanding'))
|
||||
|
||||
if amount > outstanding:
|
||||
frappe.throw(_('Cannot allocate more than {0} against payment term {1}').format(outstanding, key[0]))
|
||||
|
||||
if amount and outstanding:
|
||||
frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` + %s
|
||||
WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0]))
|
||||
|
||||
def set_status(self):
|
||||
if self.docstatus == 2:
|
||||
self.status = 'Cancelled'
|
||||
@@ -452,7 +507,7 @@ class PaymentEntry(AccountsController):
|
||||
"against": against_account,
|
||||
"account_currency": self.party_account_currency,
|
||||
"cost_center": self.cost_center
|
||||
})
|
||||
}, item=self)
|
||||
|
||||
dr_or_cr = "credit" if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit"
|
||||
|
||||
@@ -496,7 +551,7 @@ class PaymentEntry(AccountsController):
|
||||
"credit_in_account_currency": self.paid_amount,
|
||||
"credit": self.base_paid_amount,
|
||||
"cost_center": self.cost_center
|
||||
})
|
||||
}, item=self)
|
||||
)
|
||||
if self.payment_type in ("Receive", "Internal Transfer"):
|
||||
gl_entries.append(
|
||||
@@ -507,7 +562,7 @@ class PaymentEntry(AccountsController):
|
||||
"debit_in_account_currency": self.received_amount,
|
||||
"debit": self.base_received_amount,
|
||||
"cost_center": self.cost_center
|
||||
})
|
||||
}, item=self)
|
||||
)
|
||||
|
||||
def add_deductions_gl_entries(self, gl_entries):
|
||||
@@ -602,7 +657,7 @@ def get_outstanding_reference_documents(args):
|
||||
.format(frappe.db.escape(args["voucher_type"]), frappe.db.escape(args["voucher_no"]))
|
||||
|
||||
# Add cost center condition
|
||||
if args.get("cost_center") and get_allow_cost_center_in_entry_of_bs_account():
|
||||
if args.get("cost_center"):
|
||||
condition += " and cost_center='%s'" % args.get("cost_center")
|
||||
|
||||
date_fields_dict = {
|
||||
@@ -1012,15 +1067,22 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
||||
if doc.doctype == "Purchase Invoice" and doc.invoice_is_blocked():
|
||||
frappe.msgprint(_('{0} is on hold till {1}'.format(doc.name, doc.release_date)))
|
||||
else:
|
||||
pe.append("references", {
|
||||
'reference_doctype': dt,
|
||||
'reference_name': dn,
|
||||
"bill_no": doc.get("bill_no"),
|
||||
"due_date": doc.get("due_date"),
|
||||
'total_amount': grand_total,
|
||||
'outstanding_amount': outstanding_amount,
|
||||
'allocated_amount': outstanding_amount
|
||||
})
|
||||
if (doc.doctype in ('Sales Invoice', 'Purchase Invoice')
|
||||
and frappe.get_value('Payment Terms Template',
|
||||
{'name': doc.payment_terms_template}, 'allocate_payment_based_on_payment_terms')):
|
||||
|
||||
for reference in get_reference_as_per_payment_terms(doc.payment_schedule, dt, dn, doc, grand_total, outstanding_amount):
|
||||
pe.append('references', reference)
|
||||
else:
|
||||
pe.append("references", {
|
||||
'reference_doctype': dt,
|
||||
'reference_name': dn,
|
||||
"bill_no": doc.get("bill_no"),
|
||||
"due_date": doc.get("due_date"),
|
||||
'total_amount': grand_total,
|
||||
'outstanding_amount': outstanding_amount,
|
||||
'allocated_amount': outstanding_amount
|
||||
})
|
||||
|
||||
pe.setup_party_account_field()
|
||||
pe.set_missing_values()
|
||||
@@ -1029,6 +1091,25 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
||||
pe.set_amounts()
|
||||
return pe
|
||||
|
||||
def get_reference_as_per_payment_terms(payment_schedule, dt, dn, doc, grand_total, outstanding_amount):
|
||||
references = []
|
||||
for payment_term in payment_schedule:
|
||||
payment_term_outstanding = flt(payment_term.payment_amount - payment_term.paid_amount,
|
||||
payment_term.precision('payment_amount'))
|
||||
|
||||
if payment_term_outstanding:
|
||||
references.append({
|
||||
'reference_doctype': dt,
|
||||
'reference_name': dn,
|
||||
'bill_no': doc.get('bill_no'),
|
||||
'due_date': doc.get('due_date'),
|
||||
'total_amount': grand_total,
|
||||
'outstanding_amount': outstanding_amount,
|
||||
'payment_term': payment_term.payment_term,
|
||||
'allocated_amount': payment_term_outstanding
|
||||
})
|
||||
|
||||
return references
|
||||
|
||||
def get_paid_amount(dt, dn, party_type, party, account, due_date):
|
||||
if party_type=="Customer":
|
||||
|
||||
12
erpnext/accounts/doctype/payment_entry/payment_entry_list.js
Normal file
12
erpnext/accounts/doctype/payment_entry/payment_entry_list.js
Normal file
@@ -0,0 +1,12 @@
|
||||
frappe.listview_settings['Payment Entry'] = {
|
||||
|
||||
onload: function(listview) {
|
||||
listview.page.fields_dict.party_type.get_query = function() {
|
||||
return {
|
||||
"filters": {
|
||||
"name": ["in", Object.keys(frappe.boot.party_account_types)],
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -149,6 +149,30 @@ class TestPaymentEntry(unittest.TestCase):
|
||||
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", pi.name, "outstanding_amount"))
|
||||
self.assertEqual(outstanding_amount, 0)
|
||||
|
||||
def test_payment_entry_against_payment_terms(self):
|
||||
si = create_sales_invoice(do_not_save=1, qty=1, rate=200)
|
||||
create_payment_terms_template()
|
||||
si.payment_terms_template = 'Test Receivable Template'
|
||||
|
||||
si.append('taxes', {
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "_Test Account Service Tax - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "Service Tax",
|
||||
"rate": 18
|
||||
})
|
||||
si.save()
|
||||
|
||||
si.submit()
|
||||
|
||||
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Cash - _TC")
|
||||
pe.submit()
|
||||
si.load_from_db()
|
||||
|
||||
self.assertEqual(pe.references[0].payment_term, 'Basic Amount Receivable')
|
||||
self.assertEqual(pe.references[1].payment_term, 'Tax Receivable')
|
||||
self.assertEqual(si.payment_schedule[0].paid_amount, 200.0)
|
||||
self.assertEqual(si.payment_schedule[1].paid_amount, 36.0)
|
||||
|
||||
def test_payment_against_sales_invoice_to_check_status(self):
|
||||
si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
|
||||
@@ -438,11 +462,8 @@ class TestPaymentEntry(unittest.TestCase):
|
||||
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"))
|
||||
self.assertEqual(outstanding_amount, 0)
|
||||
|
||||
def test_payment_entry_against_sales_invoice_for_enable_allow_cost_center_in_entry_of_bs_account(self):
|
||||
def test_payment_entry_against_sales_invoice_with_cost_centre(self):
|
||||
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
|
||||
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
|
||||
accounts_settings.allow_cost_center_in_entry_of_bs_account = 1
|
||||
accounts_settings.save()
|
||||
cost_center = "_Test Cost Center for BS Account - _TC"
|
||||
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
|
||||
|
||||
@@ -477,39 +498,8 @@ class TestPaymentEntry(unittest.TestCase):
|
||||
for gle in gl_entries:
|
||||
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
|
||||
|
||||
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
|
||||
accounts_settings.save()
|
||||
|
||||
def test_payment_entry_against_sales_invoice_for_disable_allow_cost_center_in_entry_of_bs_account(self):
|
||||
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
|
||||
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
|
||||
accounts_settings.save()
|
||||
si = create_sales_invoice(debit_to="Debtors - _TC")
|
||||
|
||||
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC")
|
||||
|
||||
pe.reference_no = "112211-2"
|
||||
pe.reference_date = nowdate()
|
||||
pe.paid_to = "_Test Bank - _TC"
|
||||
pe.paid_amount = si.grand_total
|
||||
pe.insert()
|
||||
pe.submit()
|
||||
|
||||
gl_entries = frappe.db.sql("""select account, cost_center, account_currency, debit, credit,
|
||||
debit_in_account_currency, credit_in_account_currency
|
||||
from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s
|
||||
order by account asc""", pe.name, as_dict=1)
|
||||
|
||||
self.assertTrue(gl_entries)
|
||||
|
||||
for gle in gl_entries:
|
||||
self.assertEqual(gle.cost_center, None)
|
||||
|
||||
def test_payment_entry_against_purchase_invoice_for_enable_allow_cost_center_in_entry_of_bs_account(self):
|
||||
def test_payment_entry_against_purchase_invoice_with_cost_center(self):
|
||||
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
|
||||
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
|
||||
accounts_settings.allow_cost_center_in_entry_of_bs_account = 1
|
||||
accounts_settings.save()
|
||||
cost_center = "_Test Cost Center for BS Account - _TC"
|
||||
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
|
||||
|
||||
@@ -544,40 +534,9 @@ class TestPaymentEntry(unittest.TestCase):
|
||||
for gle in gl_entries:
|
||||
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
|
||||
|
||||
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
|
||||
accounts_settings.save()
|
||||
|
||||
def test_payment_entry_against_purchase_invoice_for_disable_allow_cost_center_in_entry_of_bs_account(self):
|
||||
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
|
||||
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
|
||||
accounts_settings.save()
|
||||
pi = make_purchase_invoice(credit_to="Creditors - _TC")
|
||||
|
||||
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
|
||||
|
||||
pe.reference_no = "112222-2"
|
||||
pe.reference_date = nowdate()
|
||||
pe.paid_from = "_Test Bank - _TC"
|
||||
pe.paid_amount = pi.grand_total
|
||||
pe.insert()
|
||||
pe.submit()
|
||||
|
||||
gl_entries = frappe.db.sql("""select account, cost_center, account_currency, debit, credit,
|
||||
debit_in_account_currency, credit_in_account_currency
|
||||
from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s
|
||||
order by account asc""", pe.name, as_dict=1)
|
||||
|
||||
self.assertTrue(gl_entries)
|
||||
|
||||
for gle in gl_entries:
|
||||
self.assertEqual(gle.cost_center, None)
|
||||
|
||||
def test_payment_entry_account_and_party_balance_for_enable_allow_cost_center_in_entry_of_bs_account(self):
|
||||
def test_payment_entry_account_and_party_balance_with_cost_center(self):
|
||||
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
|
||||
from erpnext.accounts.utils import get_balance_on
|
||||
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
|
||||
accounts_settings.allow_cost_center_in_entry_of_bs_account = 1
|
||||
accounts_settings.save()
|
||||
cost_center = "_Test Cost Center for BS Account - _TC"
|
||||
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
|
||||
|
||||
@@ -608,5 +567,36 @@ class TestPaymentEntry(unittest.TestCase):
|
||||
self.assertEqual(expected_party_balance, party_balance)
|
||||
self.assertEqual(expected_party_account_balance, party_account_balance)
|
||||
|
||||
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
|
||||
accounts_settings.save()
|
||||
def create_payment_terms_template():
|
||||
|
||||
create_payment_term('Basic Amount Receivable')
|
||||
create_payment_term('Tax Receivable')
|
||||
|
||||
if not frappe.db.exists('Payment Terms Template', 'Test Receivable Template'):
|
||||
payment_term_template = frappe.get_doc({
|
||||
'doctype': 'Payment Terms Template',
|
||||
'template_name': 'Test Receivable Template',
|
||||
'allocate_payment_based_on_payment_terms': 1,
|
||||
'terms': [{
|
||||
'doctype': 'Payment Terms Template Detail',
|
||||
'payment_term': 'Basic Amount Receivable',
|
||||
'invoice_portion': 84.746,
|
||||
'credit_days_based_on': 'Day(s) after invoice date',
|
||||
'credit_days': 1
|
||||
},
|
||||
{
|
||||
'doctype': 'Payment Terms Template Detail',
|
||||
'payment_term': 'Tax Receivable',
|
||||
'invoice_portion': 15.254,
|
||||
'credit_days_based_on': 'Day(s) after invoice date',
|
||||
'credit_days': 2
|
||||
}]
|
||||
}).insert()
|
||||
|
||||
|
||||
def create_payment_term(name):
|
||||
if not frappe.db.exists('Payment Term', name):
|
||||
frappe.get_doc({
|
||||
'doctype': 'Payment Term',
|
||||
'payment_term_name': name
|
||||
}).insert()
|
||||
|
||||
@@ -1,343 +1,107 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"actions": [],
|
||||
"creation": "2016-06-01 16:55:32.196722",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"reference_doctype",
|
||||
"reference_name",
|
||||
"due_date",
|
||||
"bill_no",
|
||||
"payment_term",
|
||||
"column_break_4",
|
||||
"total_amount",
|
||||
"outstanding_amount",
|
||||
"allocated_amount",
|
||||
"exchange_rate"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "reference_doctype",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Type",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "DocType",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "reference_name",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 1,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "reference_doctype",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "due_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Due Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "bill_no",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Supplier Invoice No",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_4",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "total_amount",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Total Amount",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "outstanding_amount",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Outstanding",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "allocated_amount",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Allocated",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Allocated"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:(doc.reference_doctype=='Purchase Invoice')",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "exchange_rate",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Exchange Rate",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "payment_term",
|
||||
"fieldtype": "Link",
|
||||
"label": "Payment Term",
|
||||
"options": "Payment Term"
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-05-01 13:24:56.586677",
|
||||
"links": [],
|
||||
"modified": "2020-03-13 12:07:19.362539",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Entry Reference",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -26,6 +26,8 @@ class PaymentOrder(Document):
|
||||
for d in self.references:
|
||||
frappe.db.set_value(self.payment_order_type, d.get(frappe.scrub(self.payment_order_type)), ref_field, status)
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_mop_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
return frappe.db.sql(""" select mode_of_payment from `tabPayment Order Reference`
|
||||
where parent = %(parent)s and mode_of_payment like %(txt)s
|
||||
@@ -36,6 +38,8 @@ def get_mop_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
'txt': "%%%s%%" % txt
|
||||
})
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_supplier_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
return frappe.db.sql(""" select supplier from `tabPayment Order Reference`
|
||||
where parent = %(parent)s and supplier like %(txt)s and
|
||||
@@ -80,10 +84,10 @@ def make_journal_entry(doc, supplier, mode_of_payment=None):
|
||||
paid_amt += d.amount
|
||||
|
||||
je.append('accounts', {
|
||||
'account': doc.references[0].account,
|
||||
'account': doc.account,
|
||||
'credit_in_account_currency': paid_amt
|
||||
})
|
||||
|
||||
je.flags.ignore_mandatory = True
|
||||
je.save()
|
||||
frappe.msgprint(_("{0} {1} created").format(je.doctype, je.name))
|
||||
frappe.msgprint(_("{0} {1} created").format(je.doctype, je.name))
|
||||
|
||||
@@ -73,6 +73,10 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
this.frm.set_value('party_type', '');
|
||||
this.frm.set_value('party', '');
|
||||
this.frm.set_value('receivable_payable_account', '');
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
|
||||
@@ -48,7 +48,8 @@ class PaymentReconciliation(Document):
|
||||
select
|
||||
"Journal Entry" as reference_type, t1.name as reference_name,
|
||||
t1.posting_date, t1.remark as remarks, t2.name as reference_row,
|
||||
{dr_or_cr} as amount, t2.is_advance
|
||||
{dr_or_cr} as amount, t2.is_advance,
|
||||
t2.account_currency as currency
|
||||
from
|
||||
`tabJournal Entry` t1, `tabJournal Entry Account` t2
|
||||
where
|
||||
@@ -88,7 +89,8 @@ class PaymentReconciliation(Document):
|
||||
if self.party_type == 'Customer' else "Purchase Invoice")
|
||||
|
||||
return frappe.db.sql(""" SELECT `tab{doc}`.name as reference_name, %(voucher_type)s as reference_type,
|
||||
(sum(`tabGL Entry`.{dr_or_cr}) - sum(`tabGL Entry`.{reconciled_dr_or_cr})) as amount
|
||||
(sum(`tabGL Entry`.{dr_or_cr}) - sum(`tabGL Entry`.{reconciled_dr_or_cr})) as amount,
|
||||
account_currency as currency
|
||||
FROM `tab{doc}`, `tabGL Entry`
|
||||
WHERE
|
||||
(`tab{doc}`.name = `tabGL Entry`.against_voucher or `tab{doc}`.name = `tabGL Entry`.voucher_no)
|
||||
@@ -101,10 +103,10 @@ class PaymentReconciliation(Document):
|
||||
Having
|
||||
amount > 0
|
||||
""".format(
|
||||
doc=voucher_type,
|
||||
dr_or_cr=dr_or_cr,
|
||||
reconciled_dr_or_cr=reconciled_dr_or_cr,
|
||||
party_type_field=frappe.scrub(self.party_type)),
|
||||
doc=voucher_type,
|
||||
dr_or_cr=dr_or_cr,
|
||||
reconciled_dr_or_cr=reconciled_dr_or_cr,
|
||||
party_type_field=frappe.scrub(self.party_type)),
|
||||
{
|
||||
'party': self.party,
|
||||
'party_type': self.party_type,
|
||||
@@ -141,6 +143,7 @@ class PaymentReconciliation(Document):
|
||||
ent.invoice_number = e.get('voucher_no')
|
||||
ent.invoice_date = e.get('posting_date')
|
||||
ent.amount = flt(e.get('invoice_amount'))
|
||||
ent.currency = e.get('currency')
|
||||
ent.outstanding_amount = e.get('outstanding_amount')
|
||||
|
||||
def reconcile(self, args):
|
||||
@@ -170,7 +173,7 @@ class PaymentReconciliation(Document):
|
||||
reconcile_against_document(lst)
|
||||
|
||||
if dr_or_cr_notes:
|
||||
reconcile_dr_cr_note(dr_or_cr_notes)
|
||||
reconcile_dr_cr_note(dr_or_cr_notes, self.company)
|
||||
|
||||
msgprint(_("Successfully Reconciled"))
|
||||
self.get_unreconciled_entries()
|
||||
@@ -261,7 +264,7 @@ class PaymentReconciliation(Document):
|
||||
|
||||
return cond
|
||||
|
||||
def reconcile_dr_cr_note(dr_cr_notes):
|
||||
def reconcile_dr_cr_note(dr_cr_notes, company):
|
||||
for d in dr_cr_notes:
|
||||
voucher_type = ('Credit Note'
|
||||
if d.voucher_type == 'Sales Invoice' else 'Debit Note')
|
||||
@@ -269,10 +272,14 @@ def reconcile_dr_cr_note(dr_cr_notes):
|
||||
reconcile_dr_or_cr = ('debit_in_account_currency'
|
||||
if d.dr_or_cr == 'credit_in_account_currency' else 'credit_in_account_currency')
|
||||
|
||||
company_currency = erpnext.get_company_currency(company)
|
||||
|
||||
jv = frappe.get_doc({
|
||||
"doctype": "Journal Entry",
|
||||
"voucher_type": voucher_type,
|
||||
"posting_date": today(),
|
||||
"company": company,
|
||||
"multi_currency": 1 if d.currency != company_currency else 0,
|
||||
"accounts": [
|
||||
{
|
||||
'account': d.account,
|
||||
@@ -280,7 +287,8 @@ def reconcile_dr_cr_note(dr_cr_notes):
|
||||
'party_type': d.party_type,
|
||||
d.dr_or_cr: abs(d.allocated_amount),
|
||||
'reference_type': d.against_voucher_type,
|
||||
'reference_name': d.against_voucher
|
||||
'reference_name': d.against_voucher,
|
||||
'cost_center': erpnext.get_default_cost_center(company)
|
||||
},
|
||||
{
|
||||
'account': d.account,
|
||||
@@ -289,7 +297,8 @@ def reconcile_dr_cr_note(dr_cr_notes):
|
||||
reconcile_dr_or_cr: (abs(d.allocated_amount)
|
||||
if abs(d.unadjusted_amount) > abs(d.allocated_amount) else abs(d.unadjusted_amount)),
|
||||
'reference_type': d.voucher_type,
|
||||
'reference_name': d.voucher_no
|
||||
'reference_name': d.voucher_no,
|
||||
'cost_center': erpnext.get_default_cost_center(company)
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
@@ -1,183 +1,80 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2014-07-09 16:14:23.672922",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"actions": [],
|
||||
"creation": "2014-07-09 16:14:23.672922",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"invoice_type",
|
||||
"invoice_number",
|
||||
"invoice_date",
|
||||
"col_break1",
|
||||
"amount",
|
||||
"outstanding_amount",
|
||||
"currency"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "invoice_type",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Invoice Type",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Sales Invoice\nPurchase Invoice\nJournal Entry",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "invoice_type",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"label": "Invoice Type",
|
||||
"options": "Sales Invoice\nPurchase Invoice\nJournal Entry",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "invoice_number",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Invoice Number",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "invoice_type",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "invoice_number",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Invoice Number",
|
||||
"options": "invoice_type",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "invoice_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Invoice Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "invoice_date",
|
||||
"fieldtype": "Date",
|
||||
"in_list_view": 1,
|
||||
"label": "Invoice Date",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "col_break1",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "col_break1",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Amount",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "amount",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Amount",
|
||||
"options": "currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "outstanding_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Outstanding Amount",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
"fieldname": "outstanding_amount",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Outstanding Amount",
|
||||
"options": "currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "currency",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
"label": "Currency",
|
||||
"options": "Currency"
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2016-07-11 03:28:03.588476",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Reconciliation Invoice",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_seen": 0
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-07-19 18:12:27.964073",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Reconciliation Invoice",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2014-07-09 16:13:35.452759",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"reference_type",
|
||||
"reference_name",
|
||||
@@ -16,7 +18,8 @@
|
||||
"difference_account",
|
||||
"difference_amount",
|
||||
"sec_break1",
|
||||
"remark"
|
||||
"remark",
|
||||
"currency"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -73,6 +76,7 @@
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Amount",
|
||||
"options": "currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
@@ -81,6 +85,7 @@
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Allocated amount",
|
||||
"options": "currency",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
@@ -106,16 +111,25 @@
|
||||
"fieldname": "difference_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Difference Amount",
|
||||
"options": "currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_10",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "currency",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
"label": "Currency",
|
||||
"options": "Currency"
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"modified": "2019-06-24 00:08:11.150796",
|
||||
"links": [],
|
||||
"modified": "2020-07-19 18:12:41.682347",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Reconciliation Payment",
|
||||
|
||||
@@ -69,7 +69,7 @@ class PaymentRequest(Document):
|
||||
elif self.payment_request_type == 'Inward':
|
||||
self.db_set('status', 'Requested')
|
||||
|
||||
send_mail = self.payment_gateway_validation()
|
||||
send_mail = self.payment_gateway_validation() if self.payment_gateway else None
|
||||
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
|
||||
|
||||
if (hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart") \
|
||||
@@ -140,9 +140,6 @@ class PaymentRequest(Document):
|
||||
})
|
||||
|
||||
def set_as_paid(self):
|
||||
if frappe.session.user == "Guest":
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
payment_entry = self.create_payment_entry()
|
||||
self.make_invoice()
|
||||
|
||||
@@ -254,7 +251,7 @@ class PaymentRequest(Document):
|
||||
|
||||
if status in ["Authorized", "Completed"]:
|
||||
redirect_to = None
|
||||
self.run_method("set_as_paid")
|
||||
self.set_as_paid()
|
||||
|
||||
# if shopping cart enabled and in session
|
||||
if (shopping_cart_settings.enabled and hasattr(frappe.local, "session")
|
||||
@@ -326,7 +323,7 @@ def make_payment_request(**args):
|
||||
"reference_doctype": args.dt,
|
||||
"reference_name": args.dn,
|
||||
"party_type": args.get("party_type") or "Customer",
|
||||
"party": args.get("party") or ref_doc.customer,
|
||||
"party": args.get("party") or ref_doc.get("customer"),
|
||||
"bank_account": bank_account
|
||||
})
|
||||
|
||||
@@ -420,7 +417,7 @@ def make_payment_entry(docname):
|
||||
|
||||
def update_payment_req_status(doc, method):
|
||||
from erpnext.accounts.doctype.payment_entry.payment_entry import get_reference_details
|
||||
|
||||
|
||||
for ref in doc.references:
|
||||
payment_request_name = frappe.db.get_value("Payment Request",
|
||||
{"reference_doctype": ref.reference_doctype, "reference_name": ref.reference_name,
|
||||
@@ -430,7 +427,7 @@ def update_payment_req_status(doc, method):
|
||||
ref_details = get_reference_details(ref.reference_doctype, ref.reference_name, doc.party_account_currency)
|
||||
pay_req_doc = frappe.get_doc('Payment Request', payment_request_name)
|
||||
status = pay_req_doc.status
|
||||
|
||||
|
||||
if status != "Paid" and not ref_details.outstanding_amount:
|
||||
status = 'Paid'
|
||||
elif status != "Partially Paid" and ref_details.outstanding_amount != ref_details.total_amount:
|
||||
|
||||
@@ -1,243 +1,82 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "",
|
||||
"beta": 0,
|
||||
"creation": "2017-08-10 15:38:00.080575",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"actions": [],
|
||||
"creation": "2017-08-10 15:38:00.080575",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"payment_term",
|
||||
"description",
|
||||
"due_date",
|
||||
"invoice_portion",
|
||||
"payment_amount",
|
||||
"mode_of_payment",
|
||||
"paid_amount"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fieldname": "payment_term",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payment Term",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Payment Term",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"columns": 2,
|
||||
"fieldname": "payment_term",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Payment Term",
|
||||
"options": "Payment Term",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fetch_from": "",
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Description",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"columns": 2,
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Small Text",
|
||||
"in_list_view": 1,
|
||||
"label": "Description"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fieldname": "due_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Due Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"columns": 2,
|
||||
"fieldname": "due_date",
|
||||
"fieldtype": "Date",
|
||||
"in_list_view": 1,
|
||||
"label": "Due Date",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fetch_from": "",
|
||||
"fieldname": "invoice_portion",
|
||||
"fieldtype": "Percent",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Invoice Portion",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"columns": 2,
|
||||
"fieldname": "invoice_portion",
|
||||
"fieldtype": "Percent",
|
||||
"in_list_view": 1,
|
||||
"label": "Invoice Portion",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fieldname": "payment_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payment Amount",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "currency",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"columns": 2,
|
||||
"fieldname": "payment_amount",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Payment Amount",
|
||||
"options": "currency",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "mode_of_payment",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Mode of Payment",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Mode of Payment",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"fieldname": "mode_of_payment",
|
||||
"fieldtype": "Link",
|
||||
"label": "Mode of Payment",
|
||||
"options": "Mode of Payment"
|
||||
},
|
||||
{
|
||||
"fieldname": "paid_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Paid Amount"
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-09-06 17:35:44.580209",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Schedule",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-03-13 17:58:24.729526",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Schedule",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -1,164 +1,84 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:template_name",
|
||||
"beta": 0,
|
||||
"creation": "2017-08-10 15:34:28.058054",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:template_name",
|
||||
"creation": "2017-08-10 15:34:28.058054",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"template_name",
|
||||
"allocate_payment_based_on_payment_terms",
|
||||
"terms"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "template_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Template Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "template_name",
|
||||
"fieldtype": "Data",
|
||||
"label": "Template Name",
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "terms",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payment Terms",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Payment Terms Template Detail",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
"fieldname": "terms",
|
||||
"fieldtype": "Table",
|
||||
"label": "Payment Terms",
|
||||
"options": "Payment Terms Template Detail",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "If this checkbox is checked, paid amount will be splitted and allocated as per the amounts in payment schedule against each payment term",
|
||||
"fieldname": "allocate_payment_based_on_payment_terms",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allocate Payment Based On Payment Terms"
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-01-24 11:13:31.158613",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Terms Template",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2020-04-01 15:35:18.112619",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Terms Template",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts User",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -7,6 +7,8 @@ from frappe.utils import flt
|
||||
from frappe import _
|
||||
from erpnext.accounts.utils import get_account_currency
|
||||
from erpnext.controllers.accounts_controller import AccountsController
|
||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (get_accounting_dimensions,
|
||||
get_dimension_filters)
|
||||
|
||||
class PeriodClosingVoucher(AccountsController):
|
||||
def validate(self):
|
||||
@@ -49,7 +51,15 @@ class PeriodClosingVoucher(AccountsController):
|
||||
def make_gl_entries(self):
|
||||
gl_entries = []
|
||||
net_pl_balance = 0
|
||||
pl_accounts = self.get_pl_balances()
|
||||
dimension_fields = ['t1.cost_center']
|
||||
|
||||
accounting_dimensions = get_accounting_dimensions()
|
||||
for dimension in accounting_dimensions:
|
||||
dimension_fields.append('t1.{0}'.format(dimension))
|
||||
|
||||
dimension_filters, default_dimensions = get_dimension_filters()
|
||||
|
||||
pl_accounts = self.get_pl_balances(dimension_fields)
|
||||
|
||||
for acc in pl_accounts:
|
||||
if flt(acc.balance_in_company_currency):
|
||||
@@ -65,34 +75,41 @@ class PeriodClosingVoucher(AccountsController):
|
||||
if flt(acc.balance_in_account_currency) > 0 else 0,
|
||||
"credit": abs(flt(acc.balance_in_company_currency)) \
|
||||
if flt(acc.balance_in_company_currency) > 0 else 0
|
||||
}))
|
||||
}, item=acc))
|
||||
|
||||
net_pl_balance += flt(acc.balance_in_company_currency)
|
||||
|
||||
if net_pl_balance:
|
||||
cost_center = frappe.db.get_value("Company", self.company, "cost_center")
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
gl_entry = self.get_gl_dict({
|
||||
"account": self.closing_account_head,
|
||||
"debit_in_account_currency": abs(net_pl_balance) if net_pl_balance > 0 else 0,
|
||||
"debit": abs(net_pl_balance) if net_pl_balance > 0 else 0,
|
||||
"credit_in_account_currency": abs(net_pl_balance) if net_pl_balance < 0 else 0,
|
||||
"credit": abs(net_pl_balance) if net_pl_balance < 0 else 0,
|
||||
"cost_center": cost_center
|
||||
}))
|
||||
})
|
||||
|
||||
for dimension in accounting_dimensions:
|
||||
gl_entry.update({
|
||||
dimension: default_dimensions.get(self.company, {}).get(dimension)
|
||||
})
|
||||
|
||||
gl_entries.append(gl_entry)
|
||||
|
||||
from erpnext.accounts.general_ledger import make_gl_entries
|
||||
make_gl_entries(gl_entries)
|
||||
|
||||
def get_pl_balances(self):
|
||||
def get_pl_balances(self, dimension_fields):
|
||||
"""Get balance for pl accounts"""
|
||||
return frappe.db.sql("""
|
||||
select
|
||||
t1.account, t1.cost_center, t2.account_currency,
|
||||
t1.account, t2.account_currency, {dimension_fields},
|
||||
sum(t1.debit_in_account_currency) - sum(t1.credit_in_account_currency) as balance_in_account_currency,
|
||||
sum(t1.debit) - sum(t1.credit) as balance_in_company_currency
|
||||
from `tabGL Entry` t1, `tabAccount` t2
|
||||
where t1.account = t2.name and t2.report_type = 'Profit and Loss'
|
||||
and t2.docstatus < 2 and t2.company = %s
|
||||
and t1.posting_date between %s and %s
|
||||
group by t1.account, t1.cost_center
|
||||
""", (self.company, self.get("year_start_date"), self.posting_date), as_dict=1)
|
||||
group by t1.account, {dimension_fields}
|
||||
""".format(dimension_fields = ', '.join(dimension_fields)), (self.company, self.get("year_start_date"), self.posting_date), as_dict=1)
|
||||
|
||||
@@ -115,6 +115,8 @@ def get_item_groups(pos_profile):
|
||||
def get_series():
|
||||
return frappe.get_meta("Sales Invoice").get_field("naming_series").options or ""
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def pos_profile_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
user = frappe.session['user']
|
||||
company = filters.get('company') or frappe.defaults.get_user_default('company')
|
||||
|
||||
@@ -15,12 +15,12 @@ class TestPOSProfile(unittest.TestCase):
|
||||
pos_profile = get_pos_profile("_Test Company") or {}
|
||||
if pos_profile:
|
||||
doc = frappe.get_doc("POS Profile", pos_profile.get("name"))
|
||||
doc.append('item_groups', {'item_group': '_Test Item Group'})
|
||||
doc.append('customer_groups', {'customer_group': '_Test Customer Group'})
|
||||
doc.set('item_groups', [{'item_group': '_Test Item Group'}])
|
||||
doc.set('customer_groups', [{'customer_group': '_Test Customer Group'}])
|
||||
doc.save()
|
||||
items = get_items_list(doc, doc.company)
|
||||
customers = get_customers_list(doc)
|
||||
|
||||
|
||||
products_count = frappe.db.sql(""" select count(name) from tabItem where item_group = '_Test Item Group'""", as_list=1)
|
||||
customers_count = frappe.db.sql(""" select count(name) from tabCustomer where customer_group = '_Test Customer Group'""")
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ from six import string_types
|
||||
apply_on_dict = {"Item Code": "items",
|
||||
"Item Group": "item_groups", "Brand": "brands"}
|
||||
|
||||
other_fields = ["other_item_code", "other_item_group", "other_brand"]
|
||||
|
||||
class PricingRule(Document):
|
||||
def validate(self):
|
||||
self.validate_mandatory()
|
||||
@@ -51,6 +53,13 @@ class PricingRule(Document):
|
||||
if tocheck and not self.get(tocheck):
|
||||
throw(_("{0} is required").format(self.meta.get_label(tocheck)), frappe.MandatoryError)
|
||||
|
||||
if self.apply_rule_on_other:
|
||||
o_field = 'other_' + frappe.scrub(self.apply_rule_on_other)
|
||||
if not self.get(o_field) and o_field in other_fields:
|
||||
frappe.throw(_("For the 'Apply Rule On Other' condition the field {0} is mandatory")
|
||||
.format(frappe.bold(self.apply_rule_on_other)))
|
||||
|
||||
|
||||
if self.price_or_product_discount == 'Price' and not self.rate_or_discount:
|
||||
throw(_("Rate or Discount is required for the price discount."), frappe.MandatoryError)
|
||||
|
||||
@@ -84,13 +93,27 @@ class PricingRule(Document):
|
||||
for f in options:
|
||||
if not f: continue
|
||||
|
||||
f = frappe.scrub(f)
|
||||
if f!=fieldname:
|
||||
self.set(f, None)
|
||||
scrubbed_f = frappe.scrub(f)
|
||||
|
||||
if logic_field == 'apply_on':
|
||||
apply_on_f = apply_on_dict.get(f, f)
|
||||
else:
|
||||
apply_on_f = scrubbed_f
|
||||
|
||||
if scrubbed_f != fieldname:
|
||||
self.set(apply_on_f, None)
|
||||
|
||||
if self.mixed_conditions and self.get("same_item"):
|
||||
self.same_item = 0
|
||||
|
||||
apply_rule_on_other = frappe.scrub(self.apply_rule_on_other or "")
|
||||
|
||||
cleanup_other_fields = (other_fields if not apply_rule_on_other
|
||||
else [o_field for o_field in other_fields if o_field != 'other_' + apply_rule_on_other])
|
||||
|
||||
for other_field in cleanup_other_fields:
|
||||
self.set(other_field, None)
|
||||
|
||||
def validate_rate_or_discount(self):
|
||||
for field in ["Rate"]:
|
||||
if flt(self.get(frappe.scrub(field))) < 0:
|
||||
@@ -103,7 +126,7 @@ class PricingRule(Document):
|
||||
self.same_item = 1
|
||||
|
||||
def validate_max_discount(self):
|
||||
if self.rate_or_discount == "Discount Percentage" and self.items:
|
||||
if self.rate_or_discount == "Discount Percentage" and self.get("items"):
|
||||
for d in self.items:
|
||||
max_discount = frappe.get_cached_value("Item", d.item_code, "max_discount")
|
||||
if max_discount and flt(self.discount_percentage) > flt(max_discount):
|
||||
@@ -241,13 +264,14 @@ 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)
|
||||
if pricing_rule.apply_rule_on_other else frappe.scrub(pricing_rule.get('apply_on')))
|
||||
})
|
||||
|
||||
if pricing_rule.coupon_code_based==1 and args.coupon_code==None:
|
||||
return item_details
|
||||
|
||||
|
||||
if not pricing_rule.validate_applied_rule:
|
||||
if pricing_rule.price_or_product_discount == "Price":
|
||||
apply_price_discount_rule(pricing_rule, item_details, args)
|
||||
@@ -256,7 +280,7 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa
|
||||
|
||||
item_details.has_pricing_rule = 1
|
||||
|
||||
item_details.pricing_rules = ','.join([d.pricing_rule for d in rules])
|
||||
item_details.pricing_rules = frappe.as_json([d.pricing_rule for d in rules])
|
||||
|
||||
if not doc: return item_details
|
||||
|
||||
@@ -346,7 +370,7 @@ def set_discount_amount(rate, item_details):
|
||||
|
||||
def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None):
|
||||
from erpnext.accounts.doctype.pricing_rule.utils import get_pricing_rule_items
|
||||
for d in pricing_rules.split(','):
|
||||
for d in json.loads(pricing_rules):
|
||||
if not d or not frappe.db.exists("Pricing Rule", d): continue
|
||||
pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
|
||||
|
||||
@@ -412,14 +436,15 @@ def make_pricing_rule(doctype, docname):
|
||||
|
||||
return doc
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_item_uoms(doctype, txt, searchfield, start, page_len, filters):
|
||||
items = [filters.get('value')]
|
||||
if filters.get('apply_on') != 'Item Code':
|
||||
field = frappe.scrub(filters.get('apply_on'))
|
||||
items = [d.name for d in frappe.db.get_all("Item", filters={field: filters.get('value')})]
|
||||
|
||||
items = frappe.db.sql_list("""select name
|
||||
from `tabItem` where {0} = %s""".format(field), filters.get('value'))
|
||||
|
||||
return frappe.get_all('UOM Conversion Detail',
|
||||
filters = {'parent': ('in', items), 'uom': ("like", "{0}%".format(txt))},
|
||||
fields = ["distinct uom"], as_list=1)
|
||||
return frappe.get_all('UOM Conversion Detail', filters={
|
||||
'parent': ('in', items),
|
||||
'uom': ("like", "{0}%".format(txt))
|
||||
}, fields = ["distinct uom"], as_list=1)
|
||||
|
||||
@@ -385,6 +385,50 @@ class TestPricingRule(unittest.TestCase):
|
||||
so.load_from_db()
|
||||
self.assertEqual(so.items[1].is_free_item, 1)
|
||||
self.assertEqual(so.items[1].item_code, "_Test Item 2")
|
||||
|
||||
def test_cumulative_pricing_rule(self):
|
||||
frappe.delete_doc_if_exists('Pricing Rule', '_Test Cumulative Pricing Rule')
|
||||
test_record = {
|
||||
"doctype": "Pricing Rule",
|
||||
"title": "_Test Cumulative Pricing Rule",
|
||||
"apply_on": "Item Code",
|
||||
"currency": "USD",
|
||||
"items": [{
|
||||
"item_code": "_Test Item",
|
||||
}],
|
||||
"is_cumulative": 1,
|
||||
"selling": 1,
|
||||
"applicable_for": "Customer",
|
||||
"customer": "_Test Customer",
|
||||
"rate_or_discount": "Discount Percentage",
|
||||
"rate": 0,
|
||||
"min_amt": 0,
|
||||
"max_amt": 10000,
|
||||
"discount_percentage": 17.5,
|
||||
"price_or_product_discount": "Price",
|
||||
"company": "_Test Company",
|
||||
"valid_from": frappe.utils.nowdate(),
|
||||
"valid_upto": frappe.utils.nowdate()
|
||||
}
|
||||
frappe.get_doc(test_record.copy()).insert()
|
||||
|
||||
args = frappe._dict({
|
||||
"item_code": "_Test Item",
|
||||
"company": "_Test Company",
|
||||
"price_list": "_Test Price List",
|
||||
"currency": "_Test Currency",
|
||||
"doctype": "Sales Invoice",
|
||||
"conversion_rate": 1,
|
||||
"price_list_currency": "_Test Currency",
|
||||
"plc_conversion_rate": 1,
|
||||
"order_type": "Sales",
|
||||
"customer": "_Test Customer",
|
||||
"name": None,
|
||||
"transaction_date": frappe.utils.nowdate()
|
||||
})
|
||||
details = get_item_details(args)
|
||||
|
||||
self.assertTrue(details)
|
||||
|
||||
def make_pricing_rule(**args):
|
||||
args = frappe._dict(args)
|
||||
|
||||
@@ -4,13 +4,23 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe, copy, json
|
||||
from frappe import throw, _
|
||||
|
||||
import copy
|
||||
import json
|
||||
|
||||
from six import string_types
|
||||
from frappe.utils import flt, cint, get_datetime, get_link_to_form, today
|
||||
|
||||
import frappe
|
||||
from erpnext.accounts.doctype.pricing_rule.pricing_rule import set_transaction_type
|
||||
from erpnext.setup.doctype.item_group.item_group import get_child_item_groups
|
||||
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
|
||||
from erpnext.stock.get_item_details import get_conversion_factor
|
||||
from erpnext.stock.get_item_details import get_conversion_factor, get_default_income_account
|
||||
from erpnext.stock.doctype.item.item import get_item_defaults
|
||||
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
|
||||
from erpnext.setup.doctype.brand.brand import get_brand_defaults
|
||||
from frappe import _, throw
|
||||
from frappe.utils import cint, flt, get_datetime, get_link_to_form, getdate, today
|
||||
|
||||
|
||||
class MultiplePricingRuleConflict(frappe.ValidationError): pass
|
||||
|
||||
@@ -313,7 +323,9 @@ def apply_internal_priority(pricing_rules, field_set, args):
|
||||
filtered_rules = []
|
||||
for field in field_set:
|
||||
if args.get(field):
|
||||
filtered_rules = filter(lambda x: x[field]==args[field], pricing_rules)
|
||||
# filter function always returns a filter object even if empty
|
||||
# list conversion is necessary to check for an empty result
|
||||
filtered_rules = list(filter(lambda x: x.get(field)==args.get(field), pricing_rules))
|
||||
if filtered_rules: break
|
||||
|
||||
return filtered_rules or pricing_rules
|
||||
@@ -330,9 +342,9 @@ def get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args):
|
||||
if pr_doc.mixed_conditions:
|
||||
amt = args.get('qty') * args.get("price_list_rate")
|
||||
if args.get("item_code") != row.get("item_code"):
|
||||
amt = row.get('qty') * (row.get("price_list_rate") or args.get("rate"))
|
||||
amt = flt(row.get('qty')) * flt(row.get("price_list_rate") or args.get("rate"))
|
||||
|
||||
sum_qty += row.get("stock_qty") or args.get("stock_qty") or args.get("qty")
|
||||
sum_qty += flt(row.get("stock_qty")) or flt(args.get("stock_qty")) or flt(args.get("qty"))
|
||||
sum_amt += amt
|
||||
|
||||
if pr_doc.is_cumulative:
|
||||
@@ -360,8 +372,7 @@ def get_qty_amount_data_for_cumulative(pr_doc, doc, items=[]):
|
||||
sum_qty, sum_amt = [0, 0]
|
||||
doctype = doc.get('parenttype') or doc.doctype
|
||||
|
||||
date_field = ('transaction_date'
|
||||
if doc.get('transaction_date') else 'posting_date')
|
||||
date_field = 'transaction_date' if frappe.get_meta(doctype).has_field('transaction_date') else 'posting_date'
|
||||
|
||||
child_doctype = '{0} Item'.format(doctype)
|
||||
apply_on = frappe.scrub(pr_doc.get('apply_on'))
|
||||
@@ -408,9 +419,28 @@ def apply_pricing_rule_on_transaction(doc):
|
||||
values = {}
|
||||
conditions = get_other_conditions(conditions, values, doc)
|
||||
|
||||
pricing_rules = frappe.db.sql(""" Select `tabPricing Rule`.* from `tabPricing Rule`
|
||||
where {conditions} and `tabPricing Rule`.disable = 0
|
||||
""".format(conditions = conditions), values, as_dict=1)
|
||||
args = frappe._dict({
|
||||
'doctype': doc.doctype,
|
||||
'transaction_type': None,
|
||||
})
|
||||
set_transaction_type(args)
|
||||
tran_type_condition = '{} = 1'.format(args.transaction_type)
|
||||
|
||||
sql = """
|
||||
SELECT
|
||||
`tabPricing Rule`.*
|
||||
FROM
|
||||
`tabPricing Rule`
|
||||
WHERE
|
||||
{conditions} and
|
||||
{tran_type_condition} and
|
||||
`tabPricing Rule`.disable = 0
|
||||
""".format(
|
||||
conditions=conditions,
|
||||
tran_type_condition=tran_type_condition,
|
||||
)
|
||||
|
||||
pricing_rules = frappe.db.sql(sql, values, as_dict=1)
|
||||
|
||||
if pricing_rules:
|
||||
pricing_rules = filter_pricing_rules_for_qty_amount(doc.total_qty,
|
||||
@@ -441,7 +471,7 @@ def apply_pricing_rule_on_transaction(doc):
|
||||
doc.set_missing_values()
|
||||
|
||||
def get_applied_pricing_rules(item_row):
|
||||
return (item_row.get("pricing_rules").split(',')
|
||||
return (json.loads(item_row.get("pricing_rules"))
|
||||
if item_row.get("pricing_rules") else [])
|
||||
|
||||
def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
|
||||
@@ -475,6 +505,14 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
|
||||
if item_details.get("parenttype") == 'Sales Order':
|
||||
item_details.free_item_data['delivery_date'] = doc.delivery_date if doc else today()
|
||||
|
||||
company = args.get('company') or doc.company
|
||||
item_details.free_item_data['income_account'] = get_default_income_account(
|
||||
args=args,
|
||||
item=get_item_defaults(free_item, company),
|
||||
item_group=get_item_group_defaults(free_item, company),
|
||||
brand=get_brand_defaults(free_item, company),
|
||||
)
|
||||
|
||||
def apply_pricing_rule_for_free_items(doc, pricing_rule_args, set_missing_values=False):
|
||||
if pricing_rule_args.get('item_code'):
|
||||
items = [d.item_code for d in doc.items
|
||||
@@ -502,18 +540,16 @@ def get_pricing_rule_items(pr_doc):
|
||||
return list(set(apply_on_data))
|
||||
|
||||
def validate_coupon_code(coupon_name):
|
||||
from frappe.utils import today,getdate
|
||||
coupon=frappe.get_doc("Coupon Code",coupon_name)
|
||||
coupon = frappe.get_doc("Coupon Code", coupon_name)
|
||||
|
||||
if coupon.valid_from:
|
||||
if coupon.valid_from > getdate(today()) :
|
||||
frappe.throw(_("Sorry,coupon code validity has not started"))
|
||||
if coupon.valid_from > getdate(today()):
|
||||
frappe.throw(_("Sorry, this coupon code's validity has not started"))
|
||||
elif coupon.valid_upto:
|
||||
if coupon.valid_upto < getdate(today()) :
|
||||
frappe.throw(_("Sorry,coupon code validity has expired"))
|
||||
elif coupon.used>=coupon.maximum_use:
|
||||
frappe.throw(_("Sorry,coupon code are exhausted"))
|
||||
else:
|
||||
return
|
||||
if coupon.valid_upto < getdate(today()):
|
||||
frappe.throw(_("Sorry, this coupon code's validity has expired"))
|
||||
elif coupon.used >= coupon.maximum_use:
|
||||
frappe.throw(_("Sorry, this coupon code is no longer valid"))
|
||||
|
||||
def update_coupon_code_count(coupon_name,transaction_type):
|
||||
coupon=frappe.get_doc("Coupon Code",coupon_name)
|
||||
|
||||
@@ -261,12 +261,25 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
||||
price_list: this.frm.doc.buying_price_list
|
||||
}, function() {
|
||||
me.apply_pricing_rule();
|
||||
|
||||
me.frm.doc.apply_tds = me.frm.supplier_tds ? 1 : 0;
|
||||
me.frm.doc.tax_withholding_category = me.frm.supplier_tds;
|
||||
me.frm.set_df_property("apply_tds", "read_only", me.frm.supplier_tds ? 0 : 1);
|
||||
me.frm.set_df_property("tax_withholding_category", "hidden", me.frm.supplier_tds ? 0 : 1);
|
||||
})
|
||||
},
|
||||
|
||||
apply_tds: function(frm) {
|
||||
var me = this;
|
||||
|
||||
if (!me.frm.doc.apply_tds) {
|
||||
me.frm.set_value("tax_withholding_category", '');
|
||||
me.frm.set_df_property("tax_withholding_category", "hidden", 1);
|
||||
} else {
|
||||
me.frm.set_value("tax_withholding_category", me.frm.supplier_tds);
|
||||
me.frm.set_df_property("tax_withholding_category", "hidden", 0);
|
||||
}
|
||||
},
|
||||
|
||||
credit_to: function() {
|
||||
var me = this;
|
||||
if(this.frm.doc.credit_to) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-05-21 16:16:39",
|
||||
@@ -16,6 +15,7 @@
|
||||
"is_paid",
|
||||
"is_return",
|
||||
"apply_tds",
|
||||
"tax_withholding_category",
|
||||
"column_break1",
|
||||
"company",
|
||||
"posting_date",
|
||||
@@ -25,6 +25,7 @@
|
||||
"accounting_dimensions_section",
|
||||
"cost_center",
|
||||
"dimension_col_break",
|
||||
"project",
|
||||
"sb_14",
|
||||
"on_hold",
|
||||
"release_date",
|
||||
@@ -73,9 +74,9 @@
|
||||
"base_total",
|
||||
"base_net_total",
|
||||
"column_break_28",
|
||||
"total_net_weight",
|
||||
"total",
|
||||
"net_total",
|
||||
"total_net_weight",
|
||||
"taxes_section",
|
||||
"tax_category",
|
||||
"column_break_49",
|
||||
@@ -962,8 +963,10 @@
|
||||
{
|
||||
"fieldname": "clearance_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 1,
|
||||
"label": "Clearance Date"
|
||||
"label": "Clearance Date",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "col_br_payments",
|
||||
@@ -1232,6 +1235,7 @@
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "subscription_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Subscription Section",
|
||||
@@ -1284,13 +1288,26 @@
|
||||
{
|
||||
"fieldname": "dimension_col_break",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "project",
|
||||
"fieldtype": "Link",
|
||||
"label": "Project",
|
||||
"options": "Project"
|
||||
},
|
||||
{
|
||||
"fieldname": "tax_withholding_category",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
"label": "Tax Withholding Category",
|
||||
"options": "Tax Withholding Category",
|
||||
"print_hide": 1
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-file-text",
|
||||
"idx": 204,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2019-12-30 19:13:49.610538",
|
||||
"modified": "2020-08-20 11:08:19.611710",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice",
|
||||
|
||||
@@ -413,6 +413,8 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
self.make_tax_gl_entries(gl_entries)
|
||||
|
||||
gl_entries = make_regional_gl_entries(gl_entries, self)
|
||||
|
||||
gl_entries = merge_similar_entries(gl_entries)
|
||||
|
||||
self.make_payment_gl_entries(gl_entries)
|
||||
@@ -451,8 +453,9 @@ class PurchaseInvoice(BuyingController):
|
||||
if self.party_account_currency==self.company_currency else grand_total,
|
||||
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
||||
"against_voucher_type": self.doctype,
|
||||
"cost_center": self.cost_center
|
||||
}, self.party_account_currency)
|
||||
"cost_center": self.cost_center,
|
||||
"project": self.project
|
||||
}, self.party_account_currency, item=self)
|
||||
)
|
||||
|
||||
def make_item_gl_entries(self, gl_entries):
|
||||
@@ -492,7 +495,7 @@ class PurchaseInvoice(BuyingController):
|
||||
"debit": warehouse_debit_amount,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"cost_center": item.cost_center,
|
||||
"project": item.project
|
||||
"project": item.project or self.project
|
||||
}, account_currency, item=item)
|
||||
)
|
||||
|
||||
@@ -505,7 +508,7 @@ class PurchaseInvoice(BuyingController):
|
||||
"cost_center": item.cost_center,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"credit": flt(amount),
|
||||
"project": item.project
|
||||
"project": item.project or self.project
|
||||
}, item=item))
|
||||
|
||||
# sub-contracting warehouse
|
||||
@@ -518,6 +521,7 @@ class PurchaseInvoice(BuyingController):
|
||||
"account": supplier_warehouse_account,
|
||||
"against": item.expense_account,
|
||||
"cost_center": item.cost_center,
|
||||
"project": item.project or self.project,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"credit": flt(item.rm_supp_cost)
|
||||
}, warehouse_account[self.supplier_warehouse]["account_currency"], item=item))
|
||||
@@ -536,7 +540,7 @@ class PurchaseInvoice(BuyingController):
|
||||
"against": self.supplier,
|
||||
"debit": amount,
|
||||
"cost_center": item.cost_center,
|
||||
"project": item.project
|
||||
"project": item.project or self.project
|
||||
}, account_currency, item=item))
|
||||
|
||||
# If asset is bought through this document and not linked to PR
|
||||
@@ -549,7 +553,7 @@ class PurchaseInvoice(BuyingController):
|
||||
"cost_center": item.cost_center,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"credit": flt(item.landed_cost_voucher_amount),
|
||||
"project": item.project
|
||||
"project": item.project or self.project
|
||||
}, item=item))
|
||||
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
@@ -558,7 +562,7 @@ class PurchaseInvoice(BuyingController):
|
||||
"cost_center": item.cost_center,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"debit": flt(item.landed_cost_voucher_amount),
|
||||
"project": item.project
|
||||
"project": item.project or self.project
|
||||
}, item=item))
|
||||
|
||||
# update gross amount of asset bought through this document
|
||||
@@ -584,7 +588,8 @@ class PurchaseInvoice(BuyingController):
|
||||
"against": self.supplier,
|
||||
"debit": flt(item.item_tax_amount, item.precision("item_tax_amount")),
|
||||
"remarks": self.remarks or "Accounting Entry for Stock",
|
||||
"cost_center": self.cost_center
|
||||
"cost_center": self.cost_center,
|
||||
"project": item.project or self.project
|
||||
}, item=item)
|
||||
)
|
||||
|
||||
@@ -613,7 +618,8 @@ class PurchaseInvoice(BuyingController):
|
||||
"debit": base_asset_amount,
|
||||
"debit_in_account_currency": (base_asset_amount
|
||||
if arbnb_currency == self.company_currency else asset_amount),
|
||||
"cost_center": item.cost_center
|
||||
"cost_center": item.cost_center,
|
||||
"project": item.project or self.project
|
||||
}, item=item))
|
||||
|
||||
if item.item_tax_amount:
|
||||
@@ -623,6 +629,7 @@ class PurchaseInvoice(BuyingController):
|
||||
"against": self.supplier,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
||||
"cost_center": item.cost_center,
|
||||
"project": item.project or self.project,
|
||||
"credit": item.item_tax_amount,
|
||||
"credit_in_account_currency": (item.item_tax_amount
|
||||
if asset_eiiav_currency == self.company_currency else
|
||||
@@ -639,7 +646,8 @@ class PurchaseInvoice(BuyingController):
|
||||
"debit": base_asset_amount,
|
||||
"debit_in_account_currency": (base_asset_amount
|
||||
if cwip_account_currency == self.company_currency else asset_amount),
|
||||
"cost_center": self.cost_center
|
||||
"cost_center": self.cost_center,
|
||||
"project": item.project or self.project
|
||||
}, item=item))
|
||||
|
||||
if item.item_tax_amount and not cint(erpnext.is_perpetual_inventory_enabled(self.company)):
|
||||
@@ -650,6 +658,7 @@ class PurchaseInvoice(BuyingController):
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
||||
"cost_center": item.cost_center,
|
||||
"credit": item.item_tax_amount,
|
||||
"project": item.project or self.project,
|
||||
"credit_in_account_currency": (item.item_tax_amount
|
||||
if asset_eiiav_currency == self.company_currency else
|
||||
item.item_tax_amount / self.conversion_rate)
|
||||
@@ -665,7 +674,7 @@ class PurchaseInvoice(BuyingController):
|
||||
"cost_center": item.cost_center,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"credit": flt(item.landed_cost_voucher_amount),
|
||||
"project": item.project
|
||||
"project": item.project or self.project
|
||||
}, item=item))
|
||||
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
@@ -674,7 +683,7 @@ class PurchaseInvoice(BuyingController):
|
||||
"cost_center": item.cost_center,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"debit": flt(item.landed_cost_voucher_amount),
|
||||
"project": item.project
|
||||
"project": item.project or self.project
|
||||
}, item=item))
|
||||
|
||||
# update gross amount of assets bought through this document
|
||||
@@ -709,7 +718,7 @@ class PurchaseInvoice(BuyingController):
|
||||
"debit": stock_adjustment_amt,
|
||||
"remarks": self.get("remarks") or _("Stock Adjustment"),
|
||||
"cost_center": item.cost_center,
|
||||
"project": item.project
|
||||
"project": item.project or self.project
|
||||
}, account_currency, item=item)
|
||||
)
|
||||
|
||||
@@ -801,8 +810,9 @@ class PurchaseInvoice(BuyingController):
|
||||
if self.party_account_currency==self.company_currency else self.paid_amount,
|
||||
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
||||
"against_voucher_type": self.doctype,
|
||||
"cost_center": self.cost_center
|
||||
}, self.party_account_currency)
|
||||
"cost_center": self.cost_center,
|
||||
"project": self.project
|
||||
}, self.party_account_currency, item=self)
|
||||
)
|
||||
|
||||
gl_entries.append(
|
||||
@@ -813,7 +823,7 @@ class PurchaseInvoice(BuyingController):
|
||||
"credit_in_account_currency": self.base_paid_amount \
|
||||
if bank_account_currency==self.company_currency else self.paid_amount,
|
||||
"cost_center": self.cost_center
|
||||
}, bank_account_currency)
|
||||
}, bank_account_currency, item=self)
|
||||
)
|
||||
|
||||
def make_write_off_gl_entry(self, gl_entries):
|
||||
@@ -833,8 +843,9 @@ class PurchaseInvoice(BuyingController):
|
||||
if self.party_account_currency==self.company_currency else self.write_off_amount,
|
||||
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
||||
"against_voucher_type": self.doctype,
|
||||
"cost_center": self.cost_center
|
||||
}, self.party_account_currency)
|
||||
"cost_center": self.cost_center,
|
||||
"project": self.project
|
||||
}, self.party_account_currency, item=self)
|
||||
)
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
@@ -844,7 +855,7 @@ class PurchaseInvoice(BuyingController):
|
||||
"credit_in_account_currency": self.base_write_off_amount \
|
||||
if write_off_account_currency==self.company_currency else self.write_off_amount,
|
||||
"cost_center": self.cost_center or self.write_off_cost_center
|
||||
})
|
||||
}, item=self)
|
||||
)
|
||||
|
||||
def make_gle_for_rounding_adjustment(self, gl_entries):
|
||||
@@ -863,8 +874,7 @@ class PurchaseInvoice(BuyingController):
|
||||
"debit_in_account_currency": self.rounding_adjustment,
|
||||
"debit": self.base_rounding_adjustment,
|
||||
"cost_center": self.cost_center or round_off_cost_center,
|
||||
}
|
||||
))
|
||||
}, item=self))
|
||||
|
||||
def on_cancel(self):
|
||||
super(PurchaseInvoice, self).on_cancel()
|
||||
@@ -959,7 +969,7 @@ class PurchaseInvoice(BuyingController):
|
||||
if not self.apply_tds:
|
||||
return
|
||||
|
||||
tax_withholding_details = get_party_tax_withholding_details(self)
|
||||
tax_withholding_details = get_party_tax_withholding_details(self, self.tax_withholding_category)
|
||||
|
||||
if not tax_withholding_details:
|
||||
return
|
||||
@@ -982,6 +992,40 @@ class PurchaseInvoice(BuyingController):
|
||||
# calculate totals again after applying TDS
|
||||
self.calculate_taxes_and_totals()
|
||||
|
||||
def set_status(self, update=False, status=None, update_modified=True):
|
||||
if self.is_new():
|
||||
if self.get('amended_from'):
|
||||
self.status = 'Draft'
|
||||
return
|
||||
|
||||
precision = self.precision("outstanding_amount")
|
||||
outstanding_amount = flt(self.outstanding_amount, precision)
|
||||
due_date = getdate(self.due_date)
|
||||
nowdate = getdate()
|
||||
|
||||
if not status:
|
||||
if self.docstatus == 2:
|
||||
status = "Cancelled"
|
||||
elif self.docstatus == 1:
|
||||
if outstanding_amount > 0 and due_date < nowdate:
|
||||
self.status = "Overdue"
|
||||
elif outstanding_amount > 0 and due_date >= nowdate:
|
||||
self.status = "Unpaid"
|
||||
#Check if outstanding amount is 0 due to debit note issued against invoice
|
||||
elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
|
||||
self.status = "Debit Note Issued"
|
||||
elif self.is_return == 1:
|
||||
self.status = "Return"
|
||||
elif outstanding_amount<=0:
|
||||
self.status = "Paid"
|
||||
else:
|
||||
self.status = "Submitted"
|
||||
else:
|
||||
self.status = "Draft"
|
||||
|
||||
if update:
|
||||
self.db_set('status', self.status, update_modified = update_modified)
|
||||
|
||||
def get_list_context(context=None):
|
||||
from erpnext.controllers.website_list_for_contact import get_list_context
|
||||
list_context = get_list_context(context)
|
||||
@@ -993,6 +1037,10 @@ def get_list_context(context=None):
|
||||
})
|
||||
return list_context
|
||||
|
||||
@erpnext.allow_regional
|
||||
def make_regional_gl_entries(gl_entries, doc):
|
||||
return gl_entries
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_debit_note(source_name, target_doc=None):
|
||||
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
||||
|
||||
@@ -16,7 +16,7 @@ frappe.listview_settings['Purchase Invoice'] = {
|
||||
} else if(frappe.datetime.get_diff(doc.due_date) < 0) {
|
||||
return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<,Today"];
|
||||
} else {
|
||||
return [__("Unpaid"), "orange", "outstanding_amount,>,0|due,>=,Today"];
|
||||
return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>=,Today"];
|
||||
}
|
||||
} else if(cint(doc.is_return)) {
|
||||
return [__("Return"), "darkgrey", "is_return,=,Yes"];
|
||||
|
||||
@@ -15,6 +15,7 @@ from erpnext.controllers.accounts_controller import get_payment_terms
|
||||
from erpnext.exceptions import InvalidCurrency
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import get_qty_after_transaction
|
||||
from erpnext.accounts.doctype.account.test_account import get_inventory_account
|
||||
from erpnext.projects.doctype.project.test_project import make_project
|
||||
|
||||
test_dependencies = ["Item", "Cost Center", "Payment Term", "Payment Terms Template"]
|
||||
test_ignore = ["Serial No"]
|
||||
@@ -86,6 +87,8 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
pe.submit()
|
||||
|
||||
pi_doc = frappe.get_doc('Purchase Invoice', pi_doc.name)
|
||||
pi_doc.load_from_db()
|
||||
self.assertTrue(pi_doc.status, "Paid")
|
||||
|
||||
self.assertRaises(frappe.LinkExistsError, pi_doc.cancel)
|
||||
unlink_payment_on_cancel_of_invoice()
|
||||
@@ -203,7 +206,9 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
|
||||
pi.insert()
|
||||
pi.submit()
|
||||
pi.load_from_db()
|
||||
|
||||
self.assertTrue(pi.status, "Unpaid")
|
||||
self.check_gle_for_pi(pi.name)
|
||||
|
||||
def check_gle_for_pi(self, pi):
|
||||
@@ -234,6 +239,9 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
|
||||
pi = frappe.copy_doc(test_records[0])
|
||||
pi.insert()
|
||||
pi.load_from_db()
|
||||
|
||||
self.assertTrue(pi.status, "Draft")
|
||||
pi.naming_series = 'TEST-'
|
||||
|
||||
self.assertRaises(frappe.CannotChangeConstantError, pi.save)
|
||||
@@ -248,6 +256,8 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
pi.get("taxes").pop(1)
|
||||
pi.insert()
|
||||
pi.submit()
|
||||
pi.load_from_db()
|
||||
self.assertTrue(pi.status, "Unpaid")
|
||||
|
||||
gl_entries = frappe.db.sql("""select account, debit, credit
|
||||
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
|
||||
@@ -425,6 +435,8 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
)
|
||||
|
||||
def test_total_purchase_cost_for_project(self):
|
||||
make_project({'project_name':'_Test Project'})
|
||||
|
||||
existing_purchase_cost = frappe.db.sql("""select sum(base_net_amount)
|
||||
from `tabPurchase Invoice Item` where project = '_Test Project' and docstatus=1""")
|
||||
existing_purchase_cost = existing_purchase_cost and existing_purchase_cost[0][0] or 0
|
||||
@@ -599,6 +611,11 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
# return entry
|
||||
pi1 = make_purchase_invoice(is_return=1, return_against=pi.name, qty=-2, rate=50, update_stock=1)
|
||||
|
||||
pi.load_from_db()
|
||||
self.assertTrue(pi.status, "Debit Note Issued")
|
||||
pi1.load_from_db()
|
||||
self.assertTrue(pi1.status, "Return")
|
||||
|
||||
actual_qty_2 = get_qty_after_transaction()
|
||||
self.assertEqual(actual_qty_1 - 2, actual_qty_2)
|
||||
|
||||
@@ -771,6 +788,8 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import get_outstanding_amount
|
||||
|
||||
pi = make_purchase_invoice(item_code = "_Test Item", qty = (5 * -1), rate=500, is_return = 1)
|
||||
pi.load_from_db()
|
||||
self.assertTrue(pi.status, "Return")
|
||||
|
||||
outstanding_amount = get_outstanding_amount(pi.doctype,
|
||||
pi.name, "Creditors - _TC", pi.supplier, "Supplier")
|
||||
@@ -791,11 +810,8 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
pi_doc = frappe.get_doc('Purchase Invoice', pi.name)
|
||||
self.assertEqual(pi_doc.outstanding_amount, 0)
|
||||
|
||||
def test_purchase_invoice_for_enable_allow_cost_center_in_entry_of_bs_account(self):
|
||||
def test_purchase_invoice_with_cost_center(self):
|
||||
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
|
||||
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
|
||||
accounts_settings.allow_cost_center_in_entry_of_bs_account = 1
|
||||
accounts_settings.save()
|
||||
cost_center = "_Test Cost Center for BS Account - _TC"
|
||||
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
|
||||
|
||||
@@ -821,13 +837,7 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
for gle in gl_entries:
|
||||
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
|
||||
|
||||
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
|
||||
accounts_settings.save()
|
||||
|
||||
def test_purchase_invoice_for_disable_allow_cost_center_in_entry_of_bs_account(self):
|
||||
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
|
||||
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
|
||||
accounts_settings.save()
|
||||
def test_purchase_invoice_without_cost_center(self):
|
||||
cost_center = "_Test Cost Center - _TC"
|
||||
pi = make_purchase_invoice(credit_to="Creditors - _TC")
|
||||
|
||||
@@ -850,6 +860,42 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
for gle in gl_entries:
|
||||
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
|
||||
|
||||
def test_purchase_invoice_with_project_link(self):
|
||||
project = make_project({
|
||||
'project_name': 'Purchase Invoice Project',
|
||||
'project_template_name': 'Test Project Template',
|
||||
'start_date': '2020-01-01'
|
||||
})
|
||||
item_project = make_project({
|
||||
'project_name': 'Purchase Invoice Item Project',
|
||||
'project_template_name': 'Test Project Template',
|
||||
'start_date': '2019-06-01'
|
||||
})
|
||||
|
||||
pi = make_purchase_invoice(credit_to="Creditors - _TC" ,do_not_save=1)
|
||||
pi.items[0].project = item_project.project_name
|
||||
pi.project = project.project_name
|
||||
|
||||
pi.submit()
|
||||
|
||||
expected_values = {
|
||||
"Creditors - _TC": {
|
||||
"project": project.project_name
|
||||
},
|
||||
"_Test Account Cost for Goods Sold - _TC": {
|
||||
"project": item_project.project_name
|
||||
}
|
||||
}
|
||||
|
||||
gl_entries = frappe.db.sql("""select account, cost_center, project, account_currency, debit, credit,
|
||||
debit_in_account_currency, credit_in_account_currency
|
||||
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
|
||||
order by account asc""", pi.name, as_dict=1)
|
||||
|
||||
self.assertTrue(gl_entries)
|
||||
|
||||
for gle in gl_entries:
|
||||
self.assertEqual(expected_values[gle.account]["project"], gle.project)
|
||||
|
||||
def unlink_payment_on_cancel_of_invoice(enable=1):
|
||||
accounts_settings = frappe.get_doc("Accounts Settings")
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"actions": [],
|
||||
"autoname": "hash",
|
||||
"creation": "2013-05-22 12:43:10",
|
||||
"doctype": "DocType",
|
||||
@@ -86,6 +85,7 @@
|
||||
"item_tax_rate",
|
||||
"bom",
|
||||
"include_exploded_items",
|
||||
"purchase_invoice_item",
|
||||
"col_break6",
|
||||
"purchase_order",
|
||||
"po_detail",
|
||||
@@ -764,12 +764,21 @@
|
||||
"label": "Asset Category",
|
||||
"options": "Asset Category",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:parent.update_stock == 1",
|
||||
"fieldname": "purchase_invoice_item",
|
||||
"fieldtype": "Data",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Purchase Invoice Item",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-04-07 18:34:35.104178",
|
||||
"modified": "2020-06-30 16:48:01.398356",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice Item",
|
||||
|
||||
@@ -180,14 +180,16 @@ def get_items_list(pos_profile, company):
|
||||
i.name, i.item_code, i.item_name, i.description, i.item_group, i.has_batch_no,
|
||||
i.has_serial_no, i.is_stock_item, i.brand, i.stock_uom, i.image,
|
||||
id.expense_account, id.selling_cost_center, id.default_warehouse,
|
||||
i.sales_uom, c.conversion_factor
|
||||
i.sales_uom, c.conversion_factor, it.item_tax_template, it.valid_from
|
||||
from
|
||||
`tabItem` i
|
||||
left join `tabItem Default` id on id.parent = i.name and id.company = %s
|
||||
left join `tabItem Tax` it on it.parent = i.name
|
||||
left join `tabUOM Conversion Detail` c on i.name = c.parent and i.sales_uom = c.uom
|
||||
where
|
||||
i.disabled = 0 and i.has_variants = 0 and i.is_sales_item = 1
|
||||
{cond}
|
||||
group by i.item_code
|
||||
""".format(cond=cond), tuple([company] + args_list), as_dict=1)
|
||||
|
||||
|
||||
|
||||
@@ -25,18 +25,26 @@ frappe.ui.form.on("Sales Invoice", {
|
||||
if(frm.doc.docstatus == 1 && !frm.is_dirty()
|
||||
&& !frm.doc.is_return && !frm.doc.ewaybill) {
|
||||
|
||||
frm.add_custom_button('e-Way Bill JSON', () => {
|
||||
var w = window.open(
|
||||
frappe.urllib.get_full_url(
|
||||
"/api/method/erpnext.regional.india.utils.generate_ewb_json?"
|
||||
+ "dt=" + encodeURIComponent(frm.doc.doctype)
|
||||
+ "&dn=" + encodeURIComponent(frm.doc.name)
|
||||
)
|
||||
);
|
||||
if (!w) {
|
||||
frappe.msgprint(__("Please enable pop-ups")); return;
|
||||
}
|
||||
}, __("Make"));
|
||||
frm.add_custom_button('E-Way Bill JSON', () => {
|
||||
frappe.call({
|
||||
method: 'erpnext.regional.india.utils.generate_ewb_json',
|
||||
args: {
|
||||
'dt': frm.doc.doctype,
|
||||
'dn': [frm.doc.name]
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
const args = {
|
||||
cmd: 'erpnext.regional.india.utils.download_ewb_json',
|
||||
data: r.message,
|
||||
docname: frm.doc.name
|
||||
};
|
||||
open_url_post(frappe.request.url, args);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}, __("Create"));
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -16,17 +16,23 @@ frappe.listview_settings['Sales Invoice'].onload = function (doclist) {
|
||||
}
|
||||
}
|
||||
|
||||
var w = window.open(
|
||||
frappe.urllib.get_full_url(
|
||||
"/api/method/erpnext.regional.india.utils.generate_ewb_json?"
|
||||
+ "dt=" + encodeURIComponent(doclist.doctype)
|
||||
+ "&dn=" + encodeURIComponent(docnames)
|
||||
)
|
||||
);
|
||||
if (!w) {
|
||||
frappe.msgprint(__("Please enable pop-ups")); return;
|
||||
}
|
||||
|
||||
frappe.call({
|
||||
method: 'erpnext.regional.india.utils.generate_ewb_json',
|
||||
args: {
|
||||
'dt': doclist.doctype,
|
||||
'dn': docnames
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
const args = {
|
||||
cmd: 'erpnext.regional.india.utils.download_ewb_json',
|
||||
data: r.message,
|
||||
docname: docnames
|
||||
};
|
||||
open_url_post(frappe.request.url, args);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
doclist.page.add_actions_menu_item(__('Generate e-Way Bill JSON'), action, false);
|
||||
|
||||
@@ -32,6 +32,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
me.frm.script_manager.trigger("is_pos");
|
||||
me.frm.refresh_fields();
|
||||
}
|
||||
erpnext.queries.setup_warehouse_query(this.frm);
|
||||
},
|
||||
|
||||
refresh: function(doc, dt, dn) {
|
||||
|
||||
@@ -74,9 +74,9 @@
|
||||
"base_total",
|
||||
"base_net_total",
|
||||
"column_break_32",
|
||||
"total_net_weight",
|
||||
"total",
|
||||
"net_total",
|
||||
"total_net_weight",
|
||||
"taxes_section",
|
||||
"taxes_and_charges",
|
||||
"column_break_38",
|
||||
@@ -148,9 +148,9 @@
|
||||
"edit_printing_settings",
|
||||
"letter_head",
|
||||
"group_same_items",
|
||||
"language",
|
||||
"column_break_84",
|
||||
"select_print_heading",
|
||||
"column_break_84",
|
||||
"language",
|
||||
"more_information",
|
||||
"inter_company_invoice_reference",
|
||||
"customer_group",
|
||||
@@ -396,7 +396,7 @@
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "po_no",
|
||||
"fieldtype": "Data",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Customer's Purchase Order",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1
|
||||
@@ -1493,6 +1493,7 @@
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "subscription_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Subscription Section"
|
||||
@@ -1568,7 +1569,7 @@
|
||||
"icon": "fa fa-file-text",
|
||||
"idx": 181,
|
||||
"is_submittable": 1,
|
||||
"modified": "2020-02-10 04:57:11.221180",
|
||||
"modified": "2020-07-01 12:41:29.484813",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice",
|
||||
|
||||
@@ -552,6 +552,8 @@ class SalesInvoice(SellingController):
|
||||
continue
|
||||
|
||||
for d in self.get('items'):
|
||||
if not d.item_code: continue
|
||||
|
||||
is_stock_item = frappe.get_cached_value('Item', d.item_code, 'is_stock_item')
|
||||
if (d.item_code and is_stock_item ==1 and not d.get(key.lower().replace(' ', '_')) and not self.get(value[1])):
|
||||
msgprint(_("{0} is mandatory for Item {1}").format(key, d.item_code), raise_exception=1)
|
||||
@@ -574,14 +576,14 @@ class SalesInvoice(SellingController):
|
||||
|
||||
def validate_item_code(self):
|
||||
for d in self.get('items'):
|
||||
if not d.item_code:
|
||||
if not d.item_code and self.is_opening == "No":
|
||||
msgprint(_("Item Code required at Row No {0}").format(d.idx), raise_exception=True)
|
||||
|
||||
def validate_warehouse(self):
|
||||
super(SalesInvoice, self).validate_warehouse()
|
||||
|
||||
for d in self.get_item_list():
|
||||
if not d.warehouse and frappe.get_cached_value("Item", d.item_code, "is_stock_item"):
|
||||
if not d.warehouse and d.item_code and frappe.get_cached_value("Item", d.item_code, "is_stock_item"):
|
||||
frappe.throw(_("Warehouse required for stock Item {0}").format(d.item_code))
|
||||
|
||||
def validate_delivery_note(self):
|
||||
@@ -784,8 +786,9 @@ class SalesInvoice(SellingController):
|
||||
if self.party_account_currency==self.company_currency else grand_total,
|
||||
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
||||
"against_voucher_type": self.doctype,
|
||||
"cost_center": self.cost_center
|
||||
}, self.party_account_currency)
|
||||
"cost_center": self.cost_center,
|
||||
"project": self.project
|
||||
}, self.party_account_currency, item=self)
|
||||
)
|
||||
|
||||
def make_tax_gl_entries(self, gl_entries):
|
||||
@@ -802,7 +805,7 @@ class SalesInvoice(SellingController):
|
||||
tax.precision("base_tax_amount_after_discount_amount")) if account_currency==self.company_currency else
|
||||
flt(tax.tax_amount_after_discount_amount, tax.precision("tax_amount_after_discount_amount"))),
|
||||
"cost_center": tax.cost_center
|
||||
}, account_currency)
|
||||
}, account_currency, item=tax)
|
||||
)
|
||||
|
||||
def make_item_gl_entries(self, gl_entries):
|
||||
@@ -822,7 +825,7 @@ class SalesInvoice(SellingController):
|
||||
|
||||
for gle in fixed_asset_gl_entries:
|
||||
gle["against"] = self.customer
|
||||
gl_entries.append(self.get_gl_dict(gle))
|
||||
gl_entries.append(self.get_gl_dict(gle, item=item))
|
||||
|
||||
asset.db_set("disposal_date", self.posting_date)
|
||||
asset.set_status("Sold" if self.docstatus==1 else None)
|
||||
@@ -839,7 +842,8 @@ class SalesInvoice(SellingController):
|
||||
"credit_in_account_currency": (flt(item.base_net_amount, item.precision("base_net_amount"))
|
||||
if account_currency==self.company_currency
|
||||
else flt(item.net_amount, item.precision("net_amount"))),
|
||||
"cost_center": item.cost_center
|
||||
"cost_center": item.cost_center,
|
||||
"project": item.project or self.project
|
||||
}, account_currency, item=item)
|
||||
)
|
||||
|
||||
@@ -860,7 +864,7 @@ class SalesInvoice(SellingController):
|
||||
"against_voucher": self.return_against if cint(self.is_return) else self.name,
|
||||
"against_voucher_type": self.doctype,
|
||||
"cost_center": self.cost_center
|
||||
})
|
||||
}, item=self)
|
||||
)
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
@@ -869,7 +873,7 @@ class SalesInvoice(SellingController):
|
||||
"against": self.customer,
|
||||
"debit": self.loyalty_amount,
|
||||
"remark": "Loyalty Points redeemed by the customer"
|
||||
})
|
||||
}, item=self)
|
||||
)
|
||||
|
||||
def make_pos_gl_entries(self, gl_entries):
|
||||
@@ -890,7 +894,7 @@ class SalesInvoice(SellingController):
|
||||
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
||||
"against_voucher_type": self.doctype,
|
||||
"cost_center": self.cost_center
|
||||
}, self.party_account_currency)
|
||||
}, self.party_account_currency, item=self)
|
||||
)
|
||||
|
||||
payment_mode_account_currency = get_account_currency(payment_mode.account)
|
||||
@@ -903,7 +907,7 @@ class SalesInvoice(SellingController):
|
||||
if payment_mode_account_currency==self.company_currency \
|
||||
else payment_mode.amount,
|
||||
"cost_center": self.cost_center
|
||||
}, payment_mode_account_currency)
|
||||
}, payment_mode_account_currency, item=self)
|
||||
)
|
||||
|
||||
def make_gle_for_change_amount(self, gl_entries):
|
||||
@@ -920,8 +924,9 @@ class SalesInvoice(SellingController):
|
||||
if self.party_account_currency==self.company_currency else flt(self.change_amount),
|
||||
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
||||
"against_voucher_type": self.doctype,
|
||||
"cost_center": self.cost_center
|
||||
}, self.party_account_currency)
|
||||
"cost_center": self.cost_center,
|
||||
"project": self.project
|
||||
}, self.party_account_currency, item=self)
|
||||
)
|
||||
|
||||
gl_entries.append(
|
||||
@@ -930,7 +935,7 @@ class SalesInvoice(SellingController):
|
||||
"against": self.customer,
|
||||
"credit": self.base_change_amount,
|
||||
"cost_center": self.cost_center
|
||||
})
|
||||
}, item=self)
|
||||
)
|
||||
else:
|
||||
frappe.throw(_("Select change amount account"), title="Mandatory Field")
|
||||
@@ -953,8 +958,9 @@ class SalesInvoice(SellingController):
|
||||
else flt(self.write_off_amount, self.precision("write_off_amount"))),
|
||||
"against_voucher": self.return_against if cint(self.is_return) else self.name,
|
||||
"against_voucher_type": self.doctype,
|
||||
"cost_center": self.cost_center
|
||||
}, self.party_account_currency)
|
||||
"cost_center": self.cost_center,
|
||||
"project": self.project
|
||||
}, self.party_account_currency, item=self)
|
||||
)
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
@@ -965,7 +971,7 @@ class SalesInvoice(SellingController):
|
||||
self.precision("base_write_off_amount")) if write_off_account_currency==self.company_currency
|
||||
else flt(self.write_off_amount, self.precision("write_off_amount"))),
|
||||
"cost_center": self.cost_center or self.write_off_cost_center or default_cost_center
|
||||
}, write_off_account_currency)
|
||||
}, write_off_account_currency, item=self)
|
||||
)
|
||||
|
||||
def make_gle_for_rounding_adjustment(self, gl_entries):
|
||||
@@ -982,8 +988,7 @@ class SalesInvoice(SellingController):
|
||||
"credit": flt(self.base_rounding_adjustment,
|
||||
self.precision("base_rounding_adjustment")),
|
||||
"cost_center": self.cost_center or round_off_cost_center,
|
||||
}
|
||||
))
|
||||
}, item=self))
|
||||
|
||||
def update_billing_status_in_dn(self, update_modified=True):
|
||||
updated_delivery_notes = []
|
||||
@@ -1104,7 +1109,10 @@ class SalesInvoice(SellingController):
|
||||
expiry_date=self.posting_date, include_expired_entry=True)
|
||||
if lp_details and getdate(lp_details.from_date) <= getdate(self.posting_date) and \
|
||||
(not lp_details.to_date or getdate(lp_details.to_date) >= getdate(self.posting_date)):
|
||||
points_earned = cint(eligible_amount/lp_details.collection_factor)
|
||||
|
||||
collection_factor = lp_details.collection_factor if lp_details.collection_factor else 1.0
|
||||
points_earned = cint(eligible_amount/collection_factor)
|
||||
|
||||
doc = frappe.get_doc({
|
||||
"doctype": "Loyalty Point Entry",
|
||||
"company": self.company,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"company": "_Test Company",
|
||||
"conversion_rate": 1.0,
|
||||
"currency": "INR",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"customer": "_Test Customer",
|
||||
"customer_name": "_Test Customer",
|
||||
"debit_to": "_Test Receivable - _TC",
|
||||
@@ -37,7 +38,8 @@
|
||||
"charge_type": "On Net Total",
|
||||
"description": "VAT",
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "taxes",
|
||||
"parentfield": "taxes",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"rate": 6
|
||||
},
|
||||
{
|
||||
@@ -45,7 +47,8 @@
|
||||
"charge_type": "On Net Total",
|
||||
"description": "Service Tax",
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "taxes",
|
||||
"parentfield": "taxes",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"rate": 6.36
|
||||
}
|
||||
],
|
||||
@@ -76,6 +79,7 @@
|
||||
"customer_name": "_Test Customer",
|
||||
"debit_to": "_Test Receivable - _TC",
|
||||
"doctype": "Sales Invoice",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"items": [
|
||||
{
|
||||
"amount": 500.0,
|
||||
@@ -107,7 +111,8 @@
|
||||
"charge_type": "On Net Total",
|
||||
"description": "VAT",
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "taxes",
|
||||
"parentfield": "taxes",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"rate": 16
|
||||
},
|
||||
{
|
||||
@@ -115,7 +120,8 @@
|
||||
"charge_type": "On Net Total",
|
||||
"description": "Service Tax",
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "taxes",
|
||||
"parentfield": "taxes",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"rate": 10
|
||||
}
|
||||
],
|
||||
@@ -132,6 +138,7 @@
|
||||
"customer_name": "_Test Customer",
|
||||
"debit_to": "_Test Receivable - _TC",
|
||||
"doctype": "Sales Invoice",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"items": [
|
||||
{
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
@@ -259,6 +266,7 @@
|
||||
"customer_name": "_Test Customer",
|
||||
"debit_to": "_Test Receivable - _TC",
|
||||
"doctype": "Sales Invoice",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"items": [
|
||||
{
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
|
||||
@@ -206,10 +206,19 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
"rate": 14,
|
||||
'included_in_print_rate': 1
|
||||
})
|
||||
si.append("taxes", {
|
||||
"charge_type": "On Item Quantity",
|
||||
"account_head": "_Test Account Education Cess - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "CESS",
|
||||
"rate": 5,
|
||||
'included_in_print_rate': 1
|
||||
})
|
||||
si.insert()
|
||||
|
||||
# with inclusive tax
|
||||
self.assertEqual(si.net_total, 4385.96)
|
||||
self.assertEqual(si.items[0].net_amount, 3947.368421052631)
|
||||
self.assertEqual(si.net_total, 3947.37)
|
||||
self.assertEqual(si.grand_total, 5000)
|
||||
|
||||
si.reload()
|
||||
@@ -222,8 +231,8 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
si.save()
|
||||
|
||||
# with inclusive tax and additional discount
|
||||
self.assertEqual(si.net_total, 4285.96)
|
||||
self.assertEqual(si.grand_total, 4885.99)
|
||||
self.assertEqual(si.net_total, 3847.37)
|
||||
self.assertEqual(si.grand_total, 4886)
|
||||
|
||||
si.reload()
|
||||
|
||||
@@ -235,7 +244,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
si.save()
|
||||
|
||||
# with inclusive tax and additional discount
|
||||
self.assertEqual(si.net_total, 4298.25)
|
||||
self.assertEqual(si.net_total, 3859.65)
|
||||
self.assertEqual(si.grand_total, 4900.00)
|
||||
|
||||
def test_sales_invoice_discount_amount(self):
|
||||
@@ -1575,11 +1584,8 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
si_doc = frappe.get_doc('Sales Invoice', si.name)
|
||||
self.assertEqual(si_doc.outstanding_amount, 0)
|
||||
|
||||
def test_sales_invoice_for_enable_allow_cost_center_in_entry_of_bs_account(self):
|
||||
def test_sales_invoice_with_cost_center(self):
|
||||
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
|
||||
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
|
||||
accounts_settings.allow_cost_center_in_entry_of_bs_account = 1
|
||||
accounts_settings.save()
|
||||
cost_center = "_Test Cost Center for BS Account - _TC"
|
||||
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
|
||||
|
||||
@@ -1604,14 +1610,47 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
for gle in gl_entries:
|
||||
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
|
||||
|
||||
def test_sales_invoice_with_project_link(self):
|
||||
from erpnext.projects.doctype.project.test_project import make_project
|
||||
|
||||
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
|
||||
accounts_settings.save()
|
||||
project = make_project({
|
||||
'project_name': 'Sales Invoice Project',
|
||||
'project_template_name': 'Test Project Template',
|
||||
'start_date': '2020-01-01'
|
||||
})
|
||||
item_project = make_project({
|
||||
'project_name': 'Sales Invoice Item Project',
|
||||
'project_template_name': 'Test Project Template',
|
||||
'start_date': '2019-06-01'
|
||||
})
|
||||
|
||||
def test_sales_invoice_for_disable_allow_cost_center_in_entry_of_bs_account(self):
|
||||
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
|
||||
accounts_settings.allow_cost_center_in_entry_of_bs_account = 1
|
||||
accounts_settings.save()
|
||||
sales_invoice = create_sales_invoice(do_not_save=1)
|
||||
sales_invoice.items[0].project = item_project.project_name
|
||||
sales_invoice.project = project.project_name
|
||||
|
||||
sales_invoice.submit()
|
||||
|
||||
expected_values = {
|
||||
"Debtors - _TC": {
|
||||
"project": project.project_name
|
||||
},
|
||||
"Sales - _TC": {
|
||||
"project": item_project.project_name
|
||||
}
|
||||
}
|
||||
|
||||
gl_entries = frappe.db.sql("""select account, cost_center, project, account_currency, debit, credit,
|
||||
debit_in_account_currency, credit_in_account_currency
|
||||
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
|
||||
order by account asc""", sales_invoice.name, as_dict=1)
|
||||
|
||||
self.assertTrue(gl_entries)
|
||||
|
||||
for gle in gl_entries:
|
||||
self.assertEqual(expected_values[gle.account]["project"], gle.project)
|
||||
|
||||
def test_sales_invoice_without_cost_center(self):
|
||||
cost_center = "_Test Cost Center - _TC"
|
||||
si = create_sales_invoice(debit_to="Debtors - _TC")
|
||||
|
||||
@@ -1634,9 +1673,6 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
for gle in gl_entries:
|
||||
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
|
||||
|
||||
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
|
||||
accounts_settings.save()
|
||||
|
||||
def test_deferred_revenue(self):
|
||||
deferred_account = create_account(account_name="Deferred Revenue",
|
||||
parent_account="Current Liabilities - _TC", company="_Test Company")
|
||||
@@ -1834,7 +1870,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
si.submit()
|
||||
|
||||
data = get_ewb_data("Sales Invoice", si.name)
|
||||
data = get_ewb_data("Sales Invoice", [si.name])
|
||||
|
||||
self.assertEqual(data['version'], '1.0.1118')
|
||||
self.assertEqual(data['billLists'][0]['fromGstin'], '27AAECE4835E1ZR')
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"actions": [],
|
||||
"autoname": "hash",
|
||||
"creation": "2013-06-04 11:02:19",
|
||||
"doctype": "DocType",
|
||||
@@ -87,6 +86,7 @@
|
||||
"edit_references",
|
||||
"sales_order",
|
||||
"so_detail",
|
||||
"sales_invoice_item",
|
||||
"column_break_74",
|
||||
"delivery_note",
|
||||
"dn_detail",
|
||||
@@ -94,6 +94,7 @@
|
||||
"accounting_dimensions_section",
|
||||
"cost_center",
|
||||
"dimension_col_break",
|
||||
"project",
|
||||
"section_break_54",
|
||||
"page_break"
|
||||
],
|
||||
@@ -783,12 +784,28 @@
|
||||
"fieldtype": "Link",
|
||||
"label": "Finance Book",
|
||||
"options": "Finance Book"
|
||||
},
|
||||
{
|
||||
"fieldname": "project",
|
||||
"fieldtype": "Link",
|
||||
"label": "Project",
|
||||
"options": "Project"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:parent.update_stock == 1",
|
||||
"fieldname": "sales_invoice_item",
|
||||
"fieldtype": "Data",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Sales Invoice Item",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2019-12-04 12:22:38.517710",
|
||||
"modified": "2020-08-20 11:24:41.749986",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice Item",
|
||||
|
||||
@@ -1,314 +1,89 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2016-05-08 23:49:38.842621",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"creation": "2016-05-08 23:49:38.842621",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"default",
|
||||
"mode_of_payment",
|
||||
"amount",
|
||||
"column_break_3",
|
||||
"account",
|
||||
"type",
|
||||
"base_amount",
|
||||
"clearance_date"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:parent.doctype == 'POS Profile'",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "default",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Default",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"default": "0",
|
||||
"depends_on": "eval:parent.doctype == 'POS Profile'",
|
||||
"fieldname": "default",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Default"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "mode_of_payment",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Mode of Payment",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Mode of Payment",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "mode_of_payment",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Mode of Payment",
|
||||
"options": "Mode of Payment",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "0",
|
||||
"depends_on": "eval:parent.doctype == 'Sales Invoice'",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Amount",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "currency",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"default": "0",
|
||||
"depends_on": "eval:parent.doctype == 'Sales Invoice'",
|
||||
"fieldname": "amount",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Amount",
|
||||
"options": "currency",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "account",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Account",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Account",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Account",
|
||||
"options": "Account",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "mode_of_payment.type",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "type",
|
||||
"fieldtype": "Read Only",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Type",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fetch_from": "mode_of_payment.type",
|
||||
"fieldname": "type",
|
||||
"fieldtype": "Read Only",
|
||||
"label": "Type"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "base_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Base Amount (Company Currency)",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "base_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Base Amount (Company Currency)",
|
||||
"no_copy": 1,
|
||||
"options": "Company:company:default_currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "clearance_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Clearance Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"fieldname": "clearance_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Clearance Date",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-03-19 14:54:56.524556",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice Payment",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 0,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
],
|
||||
"istable": 1,
|
||||
"modified": "2020-08-03 13:07:49.260534",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice Payment",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
||||
@@ -45,7 +45,9 @@ class ShippingRule(Document):
|
||||
shipping_amount = 0.0
|
||||
by_value = False
|
||||
|
||||
self.validate_countries(doc)
|
||||
if doc.get_shipping_address():
|
||||
# validate country only if there is address
|
||||
self.validate_countries(doc)
|
||||
|
||||
if self.calculate_based_on == 'Net Total':
|
||||
value = doc.base_net_total
|
||||
|
||||
@@ -6,23 +6,42 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import flt
|
||||
from frappe.utils import flt, getdate
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
|
||||
class TaxWithholdingCategory(Document):
|
||||
pass
|
||||
|
||||
def get_party_tax_withholding_details(ref_doc):
|
||||
tax_withholding_category = frappe.db.get_value('Supplier', ref_doc.supplier, 'tax_withholding_category')
|
||||
def get_party_tax_withholding_details(ref_doc, tax_withholding_category=None):
|
||||
|
||||
pan_no = ''
|
||||
suppliers = []
|
||||
|
||||
if not tax_withholding_category:
|
||||
tax_withholding_category, pan_no = frappe.db.get_value('Supplier', ref_doc.supplier, ['tax_withholding_category', 'pan'])
|
||||
|
||||
if not tax_withholding_category:
|
||||
return
|
||||
|
||||
if not pan_no:
|
||||
pan_no = frappe.db.get_value('Supplier', ref_doc.supplier, 'pan')
|
||||
|
||||
# Get others suppliers with the same PAN No
|
||||
if pan_no:
|
||||
suppliers = [d.name for d in frappe.get_all('Supplier', fields=['name'], filters={'pan': pan_no})]
|
||||
|
||||
if not suppliers:
|
||||
suppliers.append(ref_doc.supplier)
|
||||
|
||||
fy = get_fiscal_year(ref_doc.posting_date, company=ref_doc.company)
|
||||
tax_details = get_tax_withholding_details(tax_withholding_category, fy[0], ref_doc.company)
|
||||
if not tax_details:
|
||||
frappe.throw(_('Please set associated account in Tax Withholding Category {0} against Company {1}')
|
||||
.format(tax_withholding_category, ref_doc.company))
|
||||
tds_amount = get_tds_amount(ref_doc, tax_details, fy)
|
||||
|
||||
tds_amount = get_tds_amount(suppliers, ref_doc.net_total, ref_doc.company,
|
||||
tax_details, fy, ref_doc.posting_date, pan_no)
|
||||
|
||||
tax_row = get_tax_row(tax_details, tds_amount)
|
||||
|
||||
return tax_row
|
||||
@@ -39,7 +58,7 @@ def get_tax_withholding_details(tax_withholding_category, fiscal_year, company):
|
||||
"rate": tax_rate_detail.tax_withholding_rate,
|
||||
"threshold": tax_rate_detail.single_threshold,
|
||||
"cumulative_threshold": tax_rate_detail.cumulative_threshold,
|
||||
"description": tax_withholding.category_name
|
||||
"description": tax_withholding.category_name if tax_withholding.category_name else tax_withholding_category
|
||||
})
|
||||
|
||||
def get_tax_withholding_rates(tax_withholding, fiscal_year):
|
||||
@@ -51,6 +70,7 @@ def get_tax_withholding_rates(tax_withholding, fiscal_year):
|
||||
frappe.throw(_("No Tax Withholding data found for the current Fiscal Year."))
|
||||
|
||||
def get_tax_row(tax_details, tds_amount):
|
||||
|
||||
return {
|
||||
"category": "Total",
|
||||
"add_deduct_tax": "Deduct",
|
||||
@@ -60,25 +80,36 @@ def get_tax_row(tax_details, tds_amount):
|
||||
"tax_amount": tds_amount
|
||||
}
|
||||
|
||||
def get_tds_amount(ref_doc, tax_details, fiscal_year_details):
|
||||
def get_tds_amount(suppliers, net_total, company, tax_details, fiscal_year_details, posting_date, pan_no=None):
|
||||
fiscal_year, year_start_date, year_end_date = fiscal_year_details
|
||||
tds_amount = 0
|
||||
tds_deducted = 0
|
||||
|
||||
def _get_tds(amount):
|
||||
def _get_tds(amount, rate):
|
||||
if amount <= 0:
|
||||
return 0
|
||||
|
||||
return amount * tax_details.rate / 100
|
||||
return amount * rate / 100
|
||||
|
||||
ldc_name = frappe.db.get_value('Lower Deduction Certificate',
|
||||
{
|
||||
'pan_no': pan_no,
|
||||
'fiscal_year': fiscal_year
|
||||
}, 'name')
|
||||
ldc = ''
|
||||
|
||||
if ldc_name:
|
||||
ldc = frappe.get_doc('Lower Deduction Certificate', ldc_name)
|
||||
|
||||
entries = frappe.db.sql("""
|
||||
select voucher_no, credit
|
||||
from `tabGL Entry`
|
||||
where party=%s and fiscal_year=%s and credit > 0
|
||||
""", (ref_doc.supplier, fiscal_year), as_dict=1)
|
||||
where company = %s and
|
||||
party in %s and fiscal_year=%s and credit > 0
|
||||
""", (company, tuple(suppliers), fiscal_year), as_dict=1)
|
||||
|
||||
vouchers = [d.voucher_no for d in entries]
|
||||
advance_vouchers = get_advance_vouchers(ref_doc.supplier, fiscal_year)
|
||||
advance_vouchers = get_advance_vouchers(suppliers, fiscal_year=fiscal_year, company=company)
|
||||
|
||||
tds_vouchers = vouchers + advance_vouchers
|
||||
|
||||
@@ -93,7 +124,20 @@ def get_tds_amount(ref_doc, tax_details, fiscal_year_details):
|
||||
tds_deducted = tds_deducted[0][0] if tds_deducted and tds_deducted[0][0] else 0
|
||||
|
||||
if tds_deducted:
|
||||
tds_amount = _get_tds(ref_doc.net_total)
|
||||
if ldc:
|
||||
limit_consumed = frappe.db.get_value('Purchase Invoice',
|
||||
{
|
||||
'supplier': ('in', suppliers),
|
||||
'apply_tds': 1,
|
||||
'docstatus': 1
|
||||
}, 'sum(net_total)')
|
||||
|
||||
if ldc and is_valid_certificate(ldc.valid_from, ldc.valid_upto, posting_date, limit_consumed, net_total,
|
||||
ldc.certificate_limit):
|
||||
|
||||
tds_amount = get_ltds_amount(net_total, limit_consumed, ldc.certificate_limit, ldc.rate, tax_details)
|
||||
else:
|
||||
tds_amount = _get_tds(net_total, tax_details.rate)
|
||||
else:
|
||||
supplier_credit_amount = frappe.get_all('Purchase Invoice Item',
|
||||
fields = ['sum(net_amount)'],
|
||||
@@ -106,43 +150,79 @@ def get_tds_amount(ref_doc, tax_details, fiscal_year_details):
|
||||
fields = ['sum(credit_in_account_currency)'],
|
||||
filters = {
|
||||
'parent': ('in', vouchers), 'docstatus': 1,
|
||||
'party': ref_doc.supplier,
|
||||
'party': ('in', suppliers),
|
||||
'reference_type': ('not in', ['Purchase Invoice'])
|
||||
}, as_list=1)
|
||||
|
||||
supplier_credit_amount += (jv_supplier_credit_amt[0][0]
|
||||
if jv_supplier_credit_amt and jv_supplier_credit_amt[0][0] else 0)
|
||||
|
||||
supplier_credit_amount += ref_doc.net_total
|
||||
supplier_credit_amount += net_total
|
||||
|
||||
debit_note_amount = get_debit_note_amount(ref_doc.supplier, year_start_date, year_end_date)
|
||||
debit_note_amount = get_debit_note_amount(suppliers, year_start_date, year_end_date)
|
||||
supplier_credit_amount -= debit_note_amount
|
||||
|
||||
if ((tax_details.get('threshold', 0) and supplier_credit_amount >= tax_details.threshold)
|
||||
or (tax_details.get('cumulative_threshold', 0) and supplier_credit_amount >= tax_details.cumulative_threshold)):
|
||||
tds_amount = _get_tds(supplier_credit_amount)
|
||||
|
||||
if ldc and is_valid_certificate(ldc.valid_from, ldc.valid_upto, posting_date, tds_deducted, net_total,
|
||||
ldc.certificate_limit):
|
||||
tds_amount = get_ltds_amount(supplier_credit_amount, 0, ldc.certificate_limit, ldc.rate,
|
||||
tax_details)
|
||||
else:
|
||||
tds_amount = _get_tds(supplier_credit_amount, tax_details.rate)
|
||||
|
||||
return tds_amount
|
||||
|
||||
def get_advance_vouchers(supplier, fiscal_year=None, company=None, from_date=None, to_date=None):
|
||||
def get_advance_vouchers(suppliers, fiscal_year=None, company=None, from_date=None, to_date=None):
|
||||
condition = "fiscal_year=%s" % fiscal_year
|
||||
|
||||
if company:
|
||||
condition += "and company =%s" % (company)
|
||||
if from_date and to_date:
|
||||
condition = "company=%s and posting_date between %s and %s" % (company, from_date, to_date)
|
||||
condition += "and posting_date between %s and %s" % (from_date, to_date)
|
||||
|
||||
## Appending the same supplier again if length of suppliers list is 1
|
||||
## since tuple of single element list contains None, For example ('Test Supplier 1', )
|
||||
## and the below query fails
|
||||
if len(suppliers) == 1:
|
||||
suppliers.append(suppliers[0])
|
||||
|
||||
return frappe.db.sql_list("""
|
||||
select distinct voucher_no
|
||||
from `tabGL Entry`
|
||||
where party=%s and %s and debit > 0
|
||||
""", (supplier, condition)) or []
|
||||
where party in %s and %s and debit > 0
|
||||
""", (tuple(suppliers), condition)) or []
|
||||
|
||||
def get_debit_note_amount(supplier, year_start_date, year_end_date, company=None):
|
||||
condition = ""
|
||||
def get_debit_note_amount(suppliers, year_start_date, year_end_date, company=None):
|
||||
condition = "and 1=1"
|
||||
if company:
|
||||
condition = " and company=%s " % company
|
||||
|
||||
if len(suppliers) == 1:
|
||||
suppliers.append(suppliers[0])
|
||||
|
||||
return flt(frappe.db.sql("""
|
||||
select abs(sum(net_total))
|
||||
from `tabPurchase Invoice`
|
||||
where supplier=%s %s and is_return=1 and docstatus=1
|
||||
and posting_date between %s and %s
|
||||
""", (supplier, condition, year_start_date, year_end_date)))
|
||||
where supplier in %s and is_return=1 and docstatus=1
|
||||
and posting_date between %s and %s %s
|
||||
""", (tuple(suppliers), year_start_date, year_end_date, condition)))
|
||||
|
||||
def get_ltds_amount(current_amount, deducted_amount, certificate_limit, rate, tax_details):
|
||||
if current_amount < (certificate_limit - deducted_amount):
|
||||
return current_amount * rate/100
|
||||
else:
|
||||
ltds_amount = (certificate_limit - deducted_amount)
|
||||
tds_amount = current_amount - ltds_amount
|
||||
|
||||
return ltds_amount * rate/100 + tds_amount * tax_details.rate/100
|
||||
|
||||
def is_valid_certificate(valid_from, valid_upto, posting_date, deducted_amount, current_amount, certificate_limit):
|
||||
valid = False
|
||||
|
||||
if ((getdate(valid_from) <= getdate(posting_date) <= getdate(valid_upto)) and
|
||||
certificate_limit > deducted_amount):
|
||||
valid = True
|
||||
|
||||
return valid
|
||||
|
||||
@@ -45,8 +45,8 @@ def validate_accounting_period(gl_map):
|
||||
}, as_dict=1)
|
||||
|
||||
if accounting_periods:
|
||||
frappe.throw(_("You can't create accounting entries in the closed accounting period {0}")
|
||||
.format(accounting_periods[0].name), ClosedAccountingPeriod)
|
||||
frappe.throw(_("You cannot create or cancel any accounting entries within in the closed Accounting Period {0}")
|
||||
.format(frappe.bold(accounting_periods[0].name)), ClosedAccountingPeriod)
|
||||
|
||||
def process_gl_map(gl_map, merge_entries=True):
|
||||
if merge_entries:
|
||||
@@ -296,6 +296,7 @@ def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
|
||||
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no), as_dict=True)
|
||||
|
||||
if gl_entries:
|
||||
validate_accounting_period(gl_entries)
|
||||
check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
|
||||
|
||||
frappe.db.sql("""delete from `tabGL Entry` where voucher_type=%s and voucher_no=%s""",
|
||||
|
||||
@@ -285,6 +285,8 @@ def get_matching_transactions_payments(description_matching):
|
||||
else:
|
||||
return []
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def payment_entry_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
account = frappe.db.get_value("Bank Account", filters.get("bank_account"), "account")
|
||||
if not account:
|
||||
@@ -313,6 +315,8 @@ def payment_entry_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
}
|
||||
)
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def journal_entry_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
account = frappe.db.get_value("Bank Account", filters.get("bank_account"), "account")
|
||||
|
||||
@@ -348,6 +352,8 @@ def journal_entry_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
}
|
||||
)
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def sales_invoices_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
return frappe.db.sql("""
|
||||
SELECT
|
||||
@@ -373,4 +379,4 @@ def sales_invoices_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
'start': start,
|
||||
'page_len': page_len
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1062,7 +1062,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
}
|
||||
if(index < me.page_len) {
|
||||
$(frappe.render_template("pos_item", {
|
||||
item_code: obj.name,
|
||||
item_code: escape(obj.name),
|
||||
item_price: item_price,
|
||||
item_name: obj.name === obj.item_name ? "" : obj.item_name,
|
||||
item_image: obj.image,
|
||||
@@ -1098,7 +1098,8 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
|
||||
get_items: function (item_code) {
|
||||
// To search item as per the key enter
|
||||
|
||||
item_code = unescape(item_code);
|
||||
item_code = item_code === "undefined" ? undefined : item_code;
|
||||
var me = this;
|
||||
this.item_serial_no = {};
|
||||
this.item_batch_no = {};
|
||||
@@ -1164,7 +1165,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
$(this).addClass('active');
|
||||
me.numeric_val = "";
|
||||
me.numeric_id = ""
|
||||
me.item_code = $(this).attr("data-item-code");
|
||||
me.item_code = unescape($(this).attr("data-item-code"));
|
||||
me.render_selected_item()
|
||||
me.bind_qty_event()
|
||||
me.update_rate()
|
||||
@@ -1176,33 +1177,33 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
var me = this;
|
||||
|
||||
$(this.wrapper).on("change", ".pos-item-qty", function () {
|
||||
var item_code = $(this).parents(".pos-selected-item-action").attr("data-item-code");
|
||||
var item_code = unescape($(this).parents(".pos-selected-item-action").attr("data-item-code"));
|
||||
var qty = $(this).val();
|
||||
me.update_qty(item_code, qty);
|
||||
me.update_value();
|
||||
})
|
||||
|
||||
$(this.wrapper).on("focusout", ".pos-item-qty", function () {
|
||||
var item_code = $(this).parents(".pos-selected-item-action").attr("data-item-code");
|
||||
var item_code = unescape($(this).parents(".pos-selected-item-action").attr("data-item-code"));
|
||||
var qty = $(this).val();
|
||||
me.update_qty(item_code, qty, true);
|
||||
me.update_value();
|
||||
})
|
||||
|
||||
$(this.wrapper).find("[data-action='increase-qty']").on("click", function () {
|
||||
var item_code = $(this).parents(".pos-bill-item").attr("data-item-code");
|
||||
var item_code = unescape($(this).parents(".pos-bill-item").attr("data-item-code"));
|
||||
var qty = flt($(this).parents(".pos-bill-item").find('.pos-item-qty').val()) + 1;
|
||||
me.update_qty(item_code, qty);
|
||||
})
|
||||
|
||||
$(this.wrapper).find("[data-action='decrease-qty']").on("click", function () {
|
||||
var item_code = $(this).parents(".pos-bill-item").attr("data-item-code");
|
||||
var item_code = unescape($(this).parents(".pos-bill-item").attr("data-item-code"));
|
||||
var qty = flt($(this).parents(".pos-bill-item").find('.pos-item-qty').val()) - 1;
|
||||
me.update_qty(item_code, qty);
|
||||
})
|
||||
|
||||
$(this.wrapper).on("change", ".pos-item-disc", function () {
|
||||
var item_code = $(this).parents(".pos-selected-item-action").attr("data-item-code");
|
||||
var item_code = unescape($(this).parents(".pos-selected-item-action").attr("data-item-code"));
|
||||
var discount = $(this).val();
|
||||
if(discount > 100){
|
||||
discount = $(this).val('');
|
||||
@@ -1253,7 +1254,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
update_rate: function () {
|
||||
var me = this;
|
||||
$(this.wrapper).on("change", ".pos-item-price", function () {
|
||||
var item_code = $(this).parents(".pos-selected-item-action").attr("data-item-code");
|
||||
var item_code = unescape($(this).parents(".pos-selected-item-action").attr("data-item-code"));
|
||||
me.set_item_details(item_code, "rate", $(this).val());
|
||||
me.update_value()
|
||||
})
|
||||
@@ -1282,9 +1283,17 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
this.child_doc = this.get_child_item(this.item_code);
|
||||
$(this.wrapper).find('.selected-item').empty();
|
||||
if(this.child_doc.length) {
|
||||
this.child_doc[0]["allow_user_to_edit_rate"] = this.pos_profile_data["allow_user_to_edit_rate"] ? true : false,
|
||||
this.child_doc[0]["allow_user_to_edit_discount"] = this.pos_profile_data["allow_user_to_edit_discount"] ? true : false;
|
||||
this.selected_row = $(frappe.render_template("pos_selected_item", this.child_doc[0]))
|
||||
this.selected_row = $(frappe.render_template("pos_selected_item", {
|
||||
idx: this.child_doc[0].idx,
|
||||
item_code: escape(this.child_doc[0].item_code),
|
||||
qty: this.child_doc[0].qty,
|
||||
price_list_rate: this.child_doc[0].price_list_rate,
|
||||
allow_user_to_edit_rate: this.pos_profile_data["allow_user_to_edit_rate"] ? true : false,
|
||||
allow_user_to_edit_discount: this.pos_profile_data["allow_user_to_edit_discount"] ? true : false,
|
||||
discount_percentage: this.child_doc[0].discount_percentage,
|
||||
rate: this.child_doc[0].rate,
|
||||
amount: this.child_doc[0].amount
|
||||
}))
|
||||
$(this.wrapper).find('.selected-item').html(this.selected_row)
|
||||
}
|
||||
|
||||
@@ -1458,7 +1467,40 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
this.child.batch_no = this.item_batch_no[this.child.item_code];
|
||||
this.child.serial_no = (this.item_serial_no[this.child.item_code]
|
||||
? this.item_serial_no[this.child.item_code][0] : '');
|
||||
this.child.item_tax_rate = JSON.stringify(this.tax_data[this.child.item_code]);
|
||||
|
||||
const tax_template_is_valid = true;
|
||||
if (this.items && this.items[0].valid_from) {
|
||||
tax_template_is_valid = frappe.datetime.get_diff(frappe.datetime.now_date(),
|
||||
this.items[0].valid_from) > 0;
|
||||
}
|
||||
|
||||
this.child.item_tax_template = tax_template_is_valid ? this.items[0].item_tax_template : '';
|
||||
this.child.item_tax_rate = JSON.stringify(this.tax_data[this.child.item_tax_template]);
|
||||
|
||||
if (this.child.item_tax_rate) {
|
||||
this.add_taxes_from_item_tax_template(this.child.item_tax_rate);
|
||||
}
|
||||
},
|
||||
|
||||
add_taxes_from_item_tax_template: function(item_tax_map) {
|
||||
let me = this;
|
||||
|
||||
if(item_tax_map && cint(frappe.defaults.get_default("add_taxes_from_item_tax_template"))) {
|
||||
if(typeof (item_tax_map) == "string") {
|
||||
item_tax_map = JSON.parse(item_tax_map);
|
||||
}
|
||||
|
||||
$.each(item_tax_map, function(tax, rate) {
|
||||
let found = (me.frm.doc.taxes || []).find(d => d.account_head === tax);
|
||||
if(!found) {
|
||||
let child = frappe.model.add_child(me.frm.doc, "taxes");
|
||||
child.charge_type = "On Net Total";
|
||||
child.account_head = tax;
|
||||
child.description = String(tax);
|
||||
child.rate = rate;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
update_paid_amount_status: function (update_paid_amount) {
|
||||
@@ -1502,7 +1544,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
|
||||
$.each(this.frm.doc.items || [], function (i, d) {
|
||||
$(frappe.render_template("pos_bill_item_new", {
|
||||
item_code: d.item_code,
|
||||
item_code: escape(d.item_code),
|
||||
item_name: (d.item_name === d.item_code || !d.item_name) ? "" : ("<br>" + d.item_name),
|
||||
qty: d.qty,
|
||||
discount_percentage: d.discount_percentage || 0.0,
|
||||
|
||||
@@ -163,7 +163,7 @@ def get_default_price_list(party):
|
||||
def set_price_list(party_details, party, party_type, given_price_list, pos=None):
|
||||
# price list
|
||||
price_list = get_permitted_documents('Price List')
|
||||
|
||||
|
||||
# if there is only one permitted document based on user permissions, set it
|
||||
if price_list and len(price_list) == 1:
|
||||
price_list = price_list[0]
|
||||
@@ -468,23 +468,25 @@ def get_timeline_data(doctype, name):
|
||||
from frappe.desk.form.load import get_communication_data
|
||||
|
||||
out = {}
|
||||
fields = 'date(creation), count(name)'
|
||||
fields = 'creation, count(*)'
|
||||
after = add_years(None, -1).strftime('%Y-%m-%d')
|
||||
group_by='group by date(creation)'
|
||||
group_by='group by Date(creation)'
|
||||
|
||||
data = get_communication_data(doctype, name, after=after, group_by='group by date(creation)',
|
||||
fields='date(C.creation) as creation, count(C.name)',as_dict=False)
|
||||
data = get_communication_data(doctype, name, after=after, group_by='group by creation',
|
||||
fields='C.creation as creation, count(C.name)',as_dict=False)
|
||||
|
||||
# fetch and append data from Activity Log
|
||||
data += frappe.db.sql("""select {fields}
|
||||
from `tabActivity Log`
|
||||
where (reference_doctype="{doctype}" and reference_name="{name}")
|
||||
or (timeline_doctype in ("{doctype}") and timeline_name="{name}")
|
||||
or (reference_doctype in ("Quotation", "Opportunity") and timeline_name="{name}")
|
||||
where (reference_doctype=%(doctype)s and reference_name=%(name)s)
|
||||
or (timeline_doctype in (%(doctype)s) and timeline_name=%(name)s)
|
||||
or (reference_doctype in ("Quotation", "Opportunity") and timeline_name=%(name)s)
|
||||
and status!='Success' and creation > {after}
|
||||
{group_by} order by creation desc
|
||||
""".format(doctype=frappe.db.escape(doctype), name=frappe.db.escape(name), fields=fields,
|
||||
group_by=group_by, after=after), as_dict=False)
|
||||
""".format(fields=fields, group_by=group_by, after=after), {
|
||||
"doctype": doctype,
|
||||
"name": name
|
||||
}, as_dict=False)
|
||||
|
||||
timeline_items = dict(data)
|
||||
|
||||
@@ -603,10 +605,16 @@ def get_party_shipping_address(doctype, name):
|
||||
else:
|
||||
return ''
|
||||
|
||||
def get_partywise_advanced_payment_amount(party_type, posting_date = None):
|
||||
def get_partywise_advanced_payment_amount(party_type, posting_date = None, future_payment=0, company=None):
|
||||
cond = "1=1"
|
||||
if posting_date:
|
||||
cond = "posting_date <= '{0}'".format(posting_date)
|
||||
if future_payment:
|
||||
cond = "posting_date <= '{0}' OR DATE(creation) <= '{0}' """.format(posting_date)
|
||||
else:
|
||||
cond = "posting_date <= '{0}'".format(posting_date)
|
||||
|
||||
if company:
|
||||
cond += "and company = '{0}'".format(company)
|
||||
|
||||
data = frappe.db.sql(""" SELECT party, sum({0}) as amount
|
||||
FROM `tabGL Entry`
|
||||
|
||||
@@ -61,7 +61,7 @@ def make_sales_invoice():
|
||||
debit_to = 'Debtors - _TC2',
|
||||
income_account = 'Sales - _TC2',
|
||||
expense_account = 'Cost of Goods Sold - _TC2',
|
||||
cost_center = '_Test Company 2 - _TC2')
|
||||
cost_center = 'Main - _TC2')
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -135,12 +135,5 @@ frappe.query_reports["Accounts Payable"] = {
|
||||
}
|
||||
}
|
||||
|
||||
erpnext.dimension_filters.forEach((dimension) => {
|
||||
frappe.query_reports["Accounts Payable"].filters.splice(9, 0 ,{
|
||||
"fieldname": dimension["fieldname"],
|
||||
"label": __(dimension["label"]),
|
||||
"fieldtype": "Link",
|
||||
"options": dimension["document_type"]
|
||||
});
|
||||
});
|
||||
erpnext.utils.add_dimensions('Accounts Payable', 9);
|
||||
|
||||
|
||||
@@ -104,12 +104,5 @@ frappe.query_reports["Accounts Payable Summary"] = {
|
||||
}
|
||||
}
|
||||
|
||||
erpnext.dimension_filters.forEach((dimension) => {
|
||||
frappe.query_reports["Accounts Payable Summary"].filters.splice(9, 0 ,{
|
||||
"fieldname": dimension["fieldname"],
|
||||
"label": __(dimension["label"]),
|
||||
"fieldtype": "Link",
|
||||
"options": dimension["document_type"]
|
||||
});
|
||||
});
|
||||
erpnext.utils.add_dimensions('Accounts Payable Summary', 9);
|
||||
|
||||
|
||||
@@ -199,12 +199,5 @@ frappe.query_reports["Accounts Receivable"] = {
|
||||
}
|
||||
}
|
||||
|
||||
erpnext.dimension_filters.forEach((dimension) => {
|
||||
frappe.query_reports["Accounts Receivable"].filters.splice(9, 0 ,{
|
||||
"fieldname": dimension["fieldname"],
|
||||
"label": __(dimension["label"]),
|
||||
"fieldtype": "Link",
|
||||
"options": dimension["document_type"]
|
||||
});
|
||||
});
|
||||
erpnext.utils.add_dimensions('Accounts Receivable', 9);
|
||||
|
||||
|
||||
@@ -169,9 +169,11 @@ class ReceivablePayableReport(object):
|
||||
|
||||
def append_subtotal_row(self, party):
|
||||
sub_total_row = self.total_row_map.get(party)
|
||||
self.data.append(sub_total_row)
|
||||
self.data.append({})
|
||||
self.update_sub_total_row(sub_total_row, 'Total')
|
||||
|
||||
if sub_total_row:
|
||||
self.data.append(sub_total_row)
|
||||
self.data.append({})
|
||||
self.update_sub_total_row(sub_total_row, 'Total')
|
||||
|
||||
def get_voucher_balance(self, gle):
|
||||
if self.filters.get("sales_person"):
|
||||
@@ -232,7 +234,8 @@ class ReceivablePayableReport(object):
|
||||
|
||||
if self.filters.get('group_by_party'):
|
||||
self.append_subtotal_row(self.previous_party)
|
||||
self.data.append(self.total_row_map.get('Total'))
|
||||
if self.data:
|
||||
self.data.append(self.total_row_map.get('Total'))
|
||||
|
||||
def append_row(self, row):
|
||||
self.allocate_future_payments(row)
|
||||
@@ -344,26 +347,28 @@ class ReceivablePayableReport(object):
|
||||
def allocate_outstanding_based_on_payment_terms(self, row):
|
||||
self.get_payment_terms(row)
|
||||
for term in row.payment_terms:
|
||||
term.outstanding = term.invoiced
|
||||
|
||||
# update "paid" and "oustanding" for this term
|
||||
self.allocate_closing_to_term(row, term, 'paid')
|
||||
if not term.paid:
|
||||
self.allocate_closing_to_term(row, term, 'paid')
|
||||
|
||||
# update "credit_note" and "oustanding" for this term
|
||||
if term.outstanding:
|
||||
self.allocate_closing_to_term(row, term, 'credit_note')
|
||||
|
||||
row.payment_terms = sorted(row.payment_terms, key=lambda x: x['due_date'])
|
||||
|
||||
def get_payment_terms(self, row):
|
||||
# build payment_terms for row
|
||||
payment_terms_details = frappe.db.sql("""
|
||||
select
|
||||
si.name, si.party_account_currency, si.currency, si.conversion_rate,
|
||||
ps.due_date, ps.payment_amount, ps.description
|
||||
ps.due_date, ps.payment_amount, ps.description, ps.paid_amount
|
||||
from `tab{0}` si, `tabPayment Schedule` ps
|
||||
where
|
||||
si.name = ps.parent and
|
||||
si.name = %s
|
||||
order by ps.due_date
|
||||
order by ps.paid_amount desc, due_date
|
||||
""".format(row.voucher_type), row.voucher_no, as_dict = 1)
|
||||
|
||||
|
||||
@@ -389,11 +394,14 @@ class ReceivablePayableReport(object):
|
||||
"invoiced": invoiced,
|
||||
"invoice_grand_total": row.invoiced,
|
||||
"payment_term": d.description,
|
||||
"paid": 0.0,
|
||||
"paid": d.paid_amount,
|
||||
"credit_note": 0.0,
|
||||
"outstanding": 0.0
|
||||
"outstanding": invoiced - d.paid_amount
|
||||
}))
|
||||
|
||||
if d.paid_amount:
|
||||
row['paid'] -= d.paid_amount
|
||||
|
||||
def allocate_closing_to_term(self, row, term, key):
|
||||
if row[key]:
|
||||
if row[key] > term.outstanding:
|
||||
@@ -529,7 +537,7 @@ class ReceivablePayableReport(object):
|
||||
|
||||
def get_ageing_data(self, entry_date, row):
|
||||
# [0-30, 30-60, 60-90, 90-120, 120-above]
|
||||
row.range1 = row.range2 = row.range3 = row.range4 = range5 = 0.0
|
||||
row.range1 = row.range2 = row.range3 = row.range4 = row.range5 = 0.0
|
||||
|
||||
if not (self.age_as_on and entry_date):
|
||||
return
|
||||
@@ -554,6 +562,14 @@ class ReceivablePayableReport(object):
|
||||
conditions, values = self.prepare_conditions()
|
||||
order_by = self.get_order_by_condition()
|
||||
|
||||
if self.filters.show_future_payments:
|
||||
values.insert(2, self.filters.report_date)
|
||||
|
||||
date_condition = """AND (posting_date <= %s
|
||||
OR (against_voucher IS NULL AND DATE(creation) <= %s))"""
|
||||
else:
|
||||
date_condition = "AND posting_date <=%s"
|
||||
|
||||
if self.filters.get(scrub(self.party_type)):
|
||||
select_fields = "debit_in_account_currency as debit, credit_in_account_currency as credit"
|
||||
else:
|
||||
@@ -569,9 +585,8 @@ class ReceivablePayableReport(object):
|
||||
docstatus < 2
|
||||
and party_type=%s
|
||||
and (party is not null and party != '')
|
||||
and posting_date <= %s
|
||||
{1} {2}"""
|
||||
.format(select_fields, conditions, order_by), values, as_dict=True)
|
||||
{1} {2} {3}"""
|
||||
.format(select_fields, date_condition, conditions, order_by), values, as_dict=True)
|
||||
|
||||
def get_sales_invoices_or_customers_based_on_sales_person(self):
|
||||
if self.filters.get("sales_person"):
|
||||
@@ -628,8 +643,10 @@ class ReceivablePayableReport(object):
|
||||
account_type = "Receivable" if self.party_type == "Customer" else "Payable"
|
||||
accounts = [d.name for d in frappe.get_all("Account",
|
||||
filters={"account_type": account_type, "company": self.filters.company})]
|
||||
conditions.append("account in (%s)" % ','.join(['%s'] *len(accounts)))
|
||||
values += accounts
|
||||
|
||||
if accounts:
|
||||
conditions.append("account in (%s)" % ','.join(['%s'] *len(accounts)))
|
||||
values += accounts
|
||||
|
||||
def add_customer_filters(self, conditions, values):
|
||||
if self.filters.get("customer_group"):
|
||||
|
||||
@@ -63,7 +63,7 @@ def make_sales_invoice():
|
||||
debit_to = 'Debtors - _TC2',
|
||||
income_account = 'Sales - _TC2',
|
||||
expense_account = 'Cost of Goods Sold - _TC2',
|
||||
cost_center = '_Test Company 2 - _TC2',
|
||||
cost_center = 'Main - _TC2',
|
||||
do_not_save=1)
|
||||
|
||||
si.append('payment_schedule', dict(due_date=getdate(add_days(today(), 30)), invoice_portion=30.00, payment_amount=30))
|
||||
@@ -83,14 +83,14 @@ def make_payment(docname):
|
||||
|
||||
def make_credit_note(docname):
|
||||
create_sales_invoice(company="_Test Company 2",
|
||||
customer = '_Test Customer 2',
|
||||
currency = 'EUR',
|
||||
qty = -1,
|
||||
warehouse = 'Finished Goods - _TC2',
|
||||
debit_to = 'Debtors - _TC2',
|
||||
income_account = 'Sales - _TC2',
|
||||
expense_account = 'Cost of Goods Sold - _TC2',
|
||||
cost_center = '_Test Company 2 - _TC2',
|
||||
is_return = 1,
|
||||
return_against = docname)
|
||||
customer = '_Test Customer 2',
|
||||
currency = 'EUR',
|
||||
qty = -1,
|
||||
warehouse = 'Finished Goods - _TC2',
|
||||
debit_to = 'Debtors - _TC2',
|
||||
income_account = 'Sales - _TC2',
|
||||
expense_account = 'Cost of Goods Sold - _TC2',
|
||||
cost_center = 'Main - _TC2',
|
||||
is_return = 1,
|
||||
return_against = docname)
|
||||
|
||||
|
||||
@@ -111,7 +111,12 @@ frappe.query_reports["Accounts Receivable Summary"] = {
|
||||
"fieldname":"based_on_payment_terms",
|
||||
"label": __("Based On Payment Terms"),
|
||||
"fieldtype": "Check",
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname":"show_future_payments",
|
||||
"label": __("Show Future Payments"),
|
||||
"fieldtype": "Check",
|
||||
},
|
||||
],
|
||||
|
||||
onload: function(report) {
|
||||
@@ -122,11 +127,4 @@ frappe.query_reports["Accounts Receivable Summary"] = {
|
||||
}
|
||||
}
|
||||
|
||||
erpnext.dimension_filters.forEach((dimension) => {
|
||||
frappe.query_reports["Accounts Receivable Summary"].filters.splice(9, 0 ,{
|
||||
"fieldname": dimension["fieldname"],
|
||||
"label": __(dimension["label"]),
|
||||
"fieldtype": "Link",
|
||||
"options": dimension["document_type"]
|
||||
});
|
||||
});
|
||||
erpnext.utils.add_dimensions('Accounts Receivable Summary', 9);
|
||||
|
||||
@@ -33,7 +33,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
|
||||
self.get_party_total(args)
|
||||
|
||||
party_advance_amount = get_partywise_advanced_payment_amount(self.party_type,
|
||||
self.filters.report_date) or {}
|
||||
self.filters.report_date, self.filters.show_future_payments, self.filters.company) or {}
|
||||
|
||||
for party, party_dict in iteritems(self.party_total):
|
||||
if party_dict.outstanding == 0:
|
||||
|
||||
@@ -93,7 +93,7 @@ def get_assets(filters):
|
||||
sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period,
|
||||
sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period
|
||||
from (SELECT a.asset_category,
|
||||
ifnull(sum(case when ds.schedule_date < %(from_date)s then
|
||||
ifnull(sum(case when ds.schedule_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then
|
||||
ds.depreciation_amount
|
||||
else
|
||||
0
|
||||
@@ -111,13 +111,11 @@ def get_assets(filters):
|
||||
0
|
||||
end), 0) as depreciation_amount_during_the_period
|
||||
from `tabAsset` a, `tabDepreciation Schedule` ds
|
||||
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent
|
||||
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent and ifnull(ds.journal_entry, '') != ''
|
||||
group by a.asset_category
|
||||
union
|
||||
SELECT a.asset_category,
|
||||
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
|
||||
and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s)
|
||||
then
|
||||
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) then
|
||||
0
|
||||
else
|
||||
a.opening_accumulated_depreciation
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
||||
frappe.query_reports["Balance Sheet"] = $.extend({}, erpnext.financial_statements);
|
||||
|
||||
erpnext.utils.add_dimensions('Balance Sheet', 10);
|
||||
|
||||
frappe.query_reports["Balance Sheet"]["filters"].push({
|
||||
"fieldname": "accumulated_values",
|
||||
"label": __("Accumulated Values"),
|
||||
|
||||
@@ -2,16 +2,19 @@
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import datetime
|
||||
from six import iteritems
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import flt
|
||||
from frappe.utils import formatdate
|
||||
from frappe.utils import flt, formatdate
|
||||
|
||||
from erpnext.controllers.trends import get_period_date_ranges, get_period_month_ranges
|
||||
|
||||
from six import iteritems
|
||||
from pprint import pprint
|
||||
|
||||
def execute(filters=None):
|
||||
if not filters: filters = {}
|
||||
if not filters:
|
||||
filters = {}
|
||||
|
||||
columns = get_columns(filters)
|
||||
if filters.get("budget_against_filter"):
|
||||
@@ -43,20 +46,25 @@ def execute(filters=None):
|
||||
|
||||
period_data[0] += last_total
|
||||
|
||||
if(filters.get("show_cumulative")):
|
||||
if filters.get("show_cumulative"):
|
||||
last_total = period_data[0] - period_data[1]
|
||||
|
||||
period_data[2] = period_data[0] - period_data[1]
|
||||
row += period_data
|
||||
totals[2] = totals[0] - totals[1]
|
||||
if filters["period"] != "Yearly" :
|
||||
if filters["period"] != "Yearly":
|
||||
row += totals
|
||||
data.append(row)
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
def get_columns(filters):
|
||||
columns = [_(filters.get("budget_against")) + ":Link/%s:150"%(filters.get("budget_against")), _("Account") + ":Link/Account:150"]
|
||||
columns = [
|
||||
_(filters.get("budget_against"))
|
||||
+ ":Link/%s:150" % (filters.get("budget_against")),
|
||||
_("Account") + ":Link/Account:150"
|
||||
]
|
||||
|
||||
group_months = False if filters["period"] == "Monthly" else True
|
||||
|
||||
@@ -65,84 +73,181 @@ def get_columns(filters):
|
||||
for year in fiscal_year:
|
||||
for from_date, to_date in get_period_date_ranges(filters["period"], year[0]):
|
||||
if filters["period"] == "Yearly":
|
||||
labels = [_("Budget") + " " + str(year[0]), _("Actual ") + " " + str(year[0]), _("Variance ") + " " + str(year[0])]
|
||||
labels = [
|
||||
_("Budget") + " " + str(year[0]),
|
||||
_("Actual ") + " " + str(year[0]),
|
||||
_("Variance ") + " " + str(year[0])
|
||||
]
|
||||
for label in labels:
|
||||
columns.append(label+":Float:150")
|
||||
columns.append(label + ":Float:150")
|
||||
else:
|
||||
for label in [_("Budget") + " (%s)" + " " + str(year[0]), _("Actual") + " (%s)" + " " + str(year[0]), _("Variance") + " (%s)" + " " + str(year[0])]:
|
||||
for label in [
|
||||
_("Budget") + " (%s)" + " " + str(year[0]),
|
||||
_("Actual") + " (%s)" + " " + str(year[0]),
|
||||
_("Variance") + " (%s)" + " " + str(year[0])
|
||||
]:
|
||||
if group_months:
|
||||
label = label % (formatdate(from_date, format_string="MMM") + "-" + formatdate(to_date, format_string="MMM"))
|
||||
label = label % (
|
||||
formatdate(from_date, format_string="MMM")
|
||||
+ "-"
|
||||
+ formatdate(to_date, format_string="MMM")
|
||||
)
|
||||
else:
|
||||
label = label % formatdate(from_date, format_string="MMM")
|
||||
|
||||
columns.append(label+":Float:150")
|
||||
columns.append(label + ":Float:150")
|
||||
|
||||
if filters["period"] != "Yearly" :
|
||||
return columns + [_("Total Budget") + ":Float:150", _("Total Actual") + ":Float:150",
|
||||
_("Total Variance") + ":Float:150"]
|
||||
if filters["period"] != "Yearly":
|
||||
return columns + [
|
||||
_("Total Budget") + ":Float:150",
|
||||
_("Total Actual") + ":Float:150",
|
||||
_("Total Variance") + ":Float:150"
|
||||
]
|
||||
else:
|
||||
return columns
|
||||
|
||||
|
||||
def get_cost_centers(filters):
|
||||
cond = "and 1=1"
|
||||
order_by = ""
|
||||
if filters.get("budget_against") == "Cost Center":
|
||||
cond = "order by lft"
|
||||
order_by = "order by lft"
|
||||
|
||||
if filters.get("budget_against") in ["Cost Center", "Project"]:
|
||||
return frappe.db.sql_list("""select name from `tab{tab}` where company=%s
|
||||
{cond}""".format(tab=filters.get("budget_against"), cond=cond), filters.get("company"))
|
||||
return frappe.db.sql_list(
|
||||
"""
|
||||
select
|
||||
name
|
||||
from
|
||||
`tab{tab}`
|
||||
where
|
||||
company = %s
|
||||
{order_by}
|
||||
""".format(tab=filters.get("budget_against"), order_by=order_by),
|
||||
filters.get("company"))
|
||||
else:
|
||||
return frappe.db.sql_list("""select name from `tab{tab}`""".format(tab=filters.get("budget_against"))) #nosec
|
||||
return frappe.db.sql_list(
|
||||
"""
|
||||
select
|
||||
name
|
||||
from
|
||||
`tab{tab}`
|
||||
""".format(tab=filters.get("budget_against"))) # nosec
|
||||
|
||||
#Get dimension & target details
|
||||
|
||||
# Get dimension & target details
|
||||
def get_dimension_target_details(filters):
|
||||
budget_against = frappe.scrub(filters.get("budget_against"))
|
||||
cond = ""
|
||||
if filters.get("budget_against_filter"):
|
||||
cond += " and b.{budget_against} in (%s)".format(budget_against = \
|
||||
frappe.scrub(filters.get('budget_against'))) % ', '.join(['%s']* len(filters.get('budget_against_filter')))
|
||||
cond += """ and b.{budget_against} in (%s)""".format(
|
||||
budget_against=budget_against) % ", ".join(["%s"] * len(filters.get("budget_against_filter")))
|
||||
|
||||
return frappe.db.sql("""
|
||||
select b.{budget_against} as budget_against, b.monthly_distribution, ba.account, ba.budget_amount,b.fiscal_year
|
||||
from `tabBudget` b, `tabBudget Account` ba
|
||||
where b.name=ba.parent and b.docstatus = 1 and b.fiscal_year between %s and %s
|
||||
and b.budget_against = %s and b.company=%s {cond} order by b.fiscal_year
|
||||
""".format(budget_against=filters.get("budget_against").replace(" ", "_").lower(), cond=cond),
|
||||
tuple([filters.from_fiscal_year,filters.to_fiscal_year,filters.budget_against, filters.company] + filters.get('budget_against_filter')),
|
||||
as_dict=True)
|
||||
return frappe.db.sql(
|
||||
"""
|
||||
select
|
||||
b.{budget_against} as budget_against,
|
||||
b.monthly_distribution,
|
||||
ba.account,
|
||||
ba.budget_amount,
|
||||
b.fiscal_year
|
||||
from
|
||||
`tabBudget` b,
|
||||
`tabBudget Account` ba
|
||||
where
|
||||
b.name = ba.parent
|
||||
and b.docstatus = 1
|
||||
and b.fiscal_year between %s and %s
|
||||
and b.budget_against = %s
|
||||
and b.company = %s
|
||||
{cond}
|
||||
order by
|
||||
b.fiscal_year
|
||||
""".format(
|
||||
budget_against=budget_against,
|
||||
cond=cond,
|
||||
),
|
||||
tuple(
|
||||
[
|
||||
filters.from_fiscal_year,
|
||||
filters.to_fiscal_year,
|
||||
filters.budget_against,
|
||||
filters.company,
|
||||
]
|
||||
+ filters.get("budget_against_filter")
|
||||
), as_dict=True)
|
||||
|
||||
|
||||
#Get target distribution details of accounts of cost center
|
||||
# Get target distribution details of accounts of cost center
|
||||
def get_target_distribution_details(filters):
|
||||
target_details = {}
|
||||
for d in frappe.db.sql("""select md.name, mdp.month, mdp.percentage_allocation
|
||||
from `tabMonthly Distribution Percentage` mdp, `tabMonthly Distribution` md
|
||||
where mdp.parent=md.name and md.fiscal_year between %s and %s order by md.fiscal_year""",(filters.from_fiscal_year, filters.to_fiscal_year), as_dict=1):
|
||||
target_details.setdefault(d.name, {}).setdefault(d.month, flt(d.percentage_allocation))
|
||||
for d in frappe.db.sql(
|
||||
"""
|
||||
select
|
||||
md.name,
|
||||
mdp.month,
|
||||
mdp.percentage_allocation
|
||||
from
|
||||
`tabMonthly Distribution Percentage` mdp,
|
||||
`tabMonthly Distribution` md
|
||||
where
|
||||
mdp.parent = md.name
|
||||
and md.fiscal_year between %s and %s
|
||||
order by
|
||||
md.fiscal_year
|
||||
""",
|
||||
(filters.from_fiscal_year, filters.to_fiscal_year), as_dict=1):
|
||||
target_details.setdefault(d.name, {}).setdefault(
|
||||
d.month, flt(d.percentage_allocation)
|
||||
)
|
||||
|
||||
return target_details
|
||||
|
||||
#Get actual details from gl entry
|
||||
# Get actual details from gl entry
|
||||
def get_actual_details(name, filters):
|
||||
cond = "1=1"
|
||||
budget_against=filters.get("budget_against").replace(" ", "_").lower()
|
||||
budget_against = frappe.scrub(filters.get("budget_against"))
|
||||
cond = ""
|
||||
|
||||
if filters.get("budget_against") == "Cost Center":
|
||||
cc_lft, cc_rgt = frappe.db.get_value("Cost Center", name, ["lft", "rgt"])
|
||||
cond = "lft>='{lft}' and rgt<='{rgt}'".format(lft = cc_lft, rgt=cc_rgt)
|
||||
cond = """
|
||||
and lft >= "{lft}"
|
||||
and rgt <= "{rgt}"
|
||||
""".format(lft=cc_lft, rgt=cc_rgt)
|
||||
|
||||
ac_details = frappe.db.sql("""select gl.account, gl.debit, gl.credit,gl.fiscal_year,
|
||||
MONTHNAME(gl.posting_date) as month_name, b.{budget_against} as budget_against
|
||||
from `tabGL Entry` gl, `tabBudget Account` ba, `tabBudget` b
|
||||
where
|
||||
b.name = ba.parent
|
||||
and b.docstatus = 1
|
||||
and ba.account=gl.account
|
||||
and b.{budget_against} = gl.{budget_against}
|
||||
and gl.fiscal_year between %s and %s
|
||||
and b.{budget_against}=%s
|
||||
and exists(select name from `tab{tab}` where name=gl.{budget_against} and {cond}) group by gl.name order by gl.fiscal_year
|
||||
""".format(tab = filters.budget_against, budget_against = budget_against, cond = cond,from_year=filters.from_fiscal_year,to_year=filters.to_fiscal_year),
|
||||
(filters.from_fiscal_year, filters.to_fiscal_year, name), as_dict=1)
|
||||
ac_details = frappe.db.sql(
|
||||
"""
|
||||
select
|
||||
gl.account,
|
||||
gl.debit,
|
||||
gl.credit,
|
||||
gl.fiscal_year,
|
||||
MONTHNAME(gl.posting_date) as month_name,
|
||||
b.{budget_against} as budget_against
|
||||
from
|
||||
`tabGL Entry` gl,
|
||||
`tabBudget Account` ba,
|
||||
`tabBudget` b
|
||||
where
|
||||
b.name = ba.parent
|
||||
and b.docstatus = 1
|
||||
and ba.account=gl.account
|
||||
and b.{budget_against} = gl.{budget_against}
|
||||
and gl.fiscal_year between %s and %s
|
||||
and b.{budget_against} = %s
|
||||
and exists(
|
||||
select
|
||||
name
|
||||
from
|
||||
`tab{tab}`
|
||||
where
|
||||
name = gl.{budget_against}
|
||||
{cond}
|
||||
)
|
||||
group by
|
||||
gl.name
|
||||
order by gl.fiscal_year
|
||||
""".format(tab=filters.budget_against, budget_against=budget_against, cond=cond),
|
||||
(filters.from_fiscal_year, filters.to_fiscal_year, name), as_dict=1)
|
||||
|
||||
cc_actual_details = {}
|
||||
for d in ac_details:
|
||||
@@ -151,7 +256,6 @@ def get_actual_details(name, filters):
|
||||
return cc_actual_details
|
||||
|
||||
def get_dimension_account_month_map(filters):
|
||||
import datetime
|
||||
dimension_target_details = get_dimension_target_details(filters)
|
||||
tdd = get_target_distribution_details(filters)
|
||||
|
||||
@@ -161,28 +265,43 @@ def get_dimension_account_month_map(filters):
|
||||
actual_details = get_actual_details(ccd.budget_against, filters)
|
||||
|
||||
for month_id in range(1, 13):
|
||||
month = datetime.date(2013, month_id, 1).strftime('%B')
|
||||
cam_map.setdefault(ccd.budget_against, {}).setdefault(ccd.account, {}).setdefault(ccd.fiscal_year,{})\
|
||||
.setdefault(month, frappe._dict({
|
||||
"target": 0.0, "actual": 0.0
|
||||
}))
|
||||
month = datetime.date(2013, month_id, 1).strftime("%B")
|
||||
cam_map.setdefault(ccd.budget_against, {}).setdefault(
|
||||
ccd.account, {}
|
||||
).setdefault(ccd.fiscal_year, {}).setdefault(
|
||||
month, frappe._dict({"target": 0.0, "actual": 0.0})
|
||||
)
|
||||
|
||||
tav_dict = cam_map[ccd.budget_against][ccd.account][ccd.fiscal_year][month]
|
||||
month_percentage = tdd.get(ccd.monthly_distribution, {}).get(month, 0) \
|
||||
if ccd.monthly_distribution else 100.0/12
|
||||
month_percentage = (
|
||||
tdd.get(ccd.monthly_distribution, {}).get(month, 0)
|
||||
if ccd.monthly_distribution
|
||||
else 100.0 / 12
|
||||
)
|
||||
|
||||
tav_dict.target = flt(ccd.budget_amount) * month_percentage / 100
|
||||
|
||||
for ad in actual_details.get(ccd.account, []):
|
||||
if ad.month_name == month:
|
||||
tav_dict.actual += flt(ad.debit) - flt(ad.credit)
|
||||
if ad.month_name == month and ad.fiscal_year == ccd.fiscal_year:
|
||||
tav_dict.actual += flt(ad.debit) - flt(ad.credit)
|
||||
|
||||
return cam_map
|
||||
|
||||
|
||||
def get_fiscal_years(filters):
|
||||
|
||||
fiscal_year = frappe.db.sql("""select name from `tabFiscal Year` where
|
||||
name between %(from_fiscal_year)s and %(to_fiscal_year)s""",
|
||||
{'from_fiscal_year': filters["from_fiscal_year"], 'to_fiscal_year': filters["to_fiscal_year"]})
|
||||
fiscal_year = frappe.db.sql(
|
||||
"""
|
||||
select
|
||||
name
|
||||
from
|
||||
`tabFiscal Year`
|
||||
where
|
||||
name between %(from_fiscal_year)s and %(to_fiscal_year)s
|
||||
""",
|
||||
{
|
||||
"from_fiscal_year": filters["from_fiscal_year"],
|
||||
"to_fiscal_year": filters["to_fiscal_year"]
|
||||
})
|
||||
|
||||
return fiscal_year
|
||||
|
||||
@@ -5,6 +5,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
||||
frappe.query_reports["Cash Flow"] = $.extend({},
|
||||
erpnext.financial_statements);
|
||||
|
||||
erpnext.utils.add_dimensions('Cash Flow', 10);
|
||||
|
||||
// The last item in the array is the definition for Presentation Currency
|
||||
// filter. It won't be used in cash flow for now so we pop it. Please take
|
||||
// of this if you are working here.
|
||||
|
||||
@@ -8,6 +8,7 @@ from __future__ import unicode_literals
|
||||
import re
|
||||
from past.builtins import cmp
|
||||
import functools
|
||||
import math
|
||||
|
||||
import frappe, erpnext
|
||||
from erpnext.accounts.report.utils import get_currency, convert_to_presentation_currency
|
||||
@@ -42,7 +43,7 @@ def get_period_list(from_fiscal_year, to_fiscal_year, periodicity, accumulated_v
|
||||
start_date = year_start_date
|
||||
months = get_months(year_start_date, year_end_date)
|
||||
|
||||
for i in range(months // months_to_add):
|
||||
for i in range(math.ceil(months / months_to_add)):
|
||||
period = frappe._dict({
|
||||
"from_date": start_date
|
||||
})
|
||||
@@ -50,9 +51,8 @@ def get_period_list(from_fiscal_year, to_fiscal_year, periodicity, accumulated_v
|
||||
to_date = add_months(start_date, months_to_add)
|
||||
start_date = to_date
|
||||
|
||||
if to_date == get_first_day(to_date):
|
||||
# if to_date is the first day, get the last day of previous month
|
||||
to_date = add_days(to_date, -1)
|
||||
# Subtract one day from to_date, as it may be first day in next fiscal year or month
|
||||
to_date = add_days(to_date, -1)
|
||||
|
||||
if to_date <= year_end_date:
|
||||
# the normal case
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<h4 class="text-center">
|
||||
{% if (filters.party_name) { %}
|
||||
{%= filters.party_name %}
|
||||
{% } else if (filters.party) { %}
|
||||
{% } else if (filters.party && filters.party.length) { %}
|
||||
{%= filters.party %}
|
||||
{% } else if (filters.account) { %}
|
||||
{%= filters.account %}
|
||||
|
||||
@@ -160,12 +160,5 @@ frappe.query_reports["General Ledger"] = {
|
||||
]
|
||||
}
|
||||
|
||||
erpnext.dimension_filters.forEach((dimension) => {
|
||||
frappe.query_reports["General Ledger"].filters.splice(15, 0 ,{
|
||||
"fieldname": dimension["fieldname"],
|
||||
"label": __(dimension["label"]),
|
||||
"fieldtype": "Link",
|
||||
"options": dimension["document_type"]
|
||||
});
|
||||
});
|
||||
erpnext.utils.add_dimensions('General Ledger', 15)
|
||||
|
||||
|
||||
@@ -293,6 +293,9 @@ def get_accountwise_gle(filters, gl_entries, gle_map):
|
||||
data[key].debit_in_account_currency += flt(gle.debit_in_account_currency)
|
||||
data[key].credit_in_account_currency += flt(gle.credit_in_account_currency)
|
||||
|
||||
if data[key].against_voucher and gle.against_voucher:
|
||||
data[key].against_voucher += ', ' + gle.against_voucher
|
||||
|
||||
from_date, to_date = getdate(filters.from_date), getdate(filters.to_date)
|
||||
for gle in gl_entries:
|
||||
if (gle.posting_date < from_date or
|
||||
|
||||
@@ -8,7 +8,6 @@ from frappe.utils import flt
|
||||
from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data)
|
||||
import copy
|
||||
|
||||
|
||||
def execute(filters=None):
|
||||
period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
|
||||
filters.periodicity, filters.accumulated_values, filters.company)
|
||||
@@ -27,17 +26,26 @@ def execute(filters=None):
|
||||
|
||||
|
||||
gross_income = get_revenue(income, period_list)
|
||||
|
||||
gross_expense = get_revenue(expense, period_list)
|
||||
|
||||
if(len(gross_income)==0 and len(gross_expense)== 0):
|
||||
data.append({"account_name": "'" + _("Nothing is included in gross") + "'",
|
||||
"account": "'" + _("Nothing is included in gross") + "'"})
|
||||
|
||||
data.append({
|
||||
"account_name": "'" + _("Nothing is included in gross") + "'",
|
||||
"account": "'" + _("Nothing is included in gross") + "'"
|
||||
})
|
||||
return columns, data
|
||||
|
||||
data.append({"account_name": "'" + _("Included in Gross Profit") + "'",
|
||||
"account": "'" + _("Included in Gross Profit") + "'"})
|
||||
|
||||
# to avoid error eg: gross_income[0] : list index out of range
|
||||
if not gross_income:
|
||||
gross_income = [{}]
|
||||
if not gross_expense:
|
||||
gross_expense = [{}]
|
||||
|
||||
data.append({
|
||||
"account_name": "'" + _("Included in Gross Profit") + "'",
|
||||
"account": "'" + _("Included in Gross Profit") + "'"
|
||||
})
|
||||
|
||||
data.append({})
|
||||
data.extend(gross_income or [])
|
||||
@@ -111,7 +119,6 @@ def set_total(node, value, complete_list, totals):
|
||||
|
||||
|
||||
def get_profit(gross_income, gross_expense, period_list, company, profit_type, currency=None, consolidated=False):
|
||||
|
||||
profit_loss = {
|
||||
"account_name": "'" + _(profit_type) + "'",
|
||||
"account": "'" + _(profit_type) + "'",
|
||||
@@ -123,7 +130,9 @@ def get_profit(gross_income, gross_expense, period_list, company, profit_type, c
|
||||
|
||||
for period in period_list:
|
||||
key = period if consolidated else period.key
|
||||
profit_loss[key] = flt(gross_income[0].get(key, 0)) - flt(gross_expense[0].get(key, 0))
|
||||
gross_income_for_period = flt(gross_income[0].get(key, 0)) if gross_income else 0
|
||||
gross_expense_for_period = flt(gross_expense[0].get(key, 0)) if gross_expense else 0
|
||||
profit_loss[key] = gross_income_for_period - gross_expense_for_period
|
||||
|
||||
if profit_loss[key]:
|
||||
has_value=True
|
||||
@@ -143,12 +152,18 @@ def get_net_profit(non_gross_income, gross_income, gross_expense, non_gross_expe
|
||||
|
||||
for period in period_list:
|
||||
key = period if consolidated else period.key
|
||||
total_income = flt(gross_income[0].get(key, 0)) + flt(non_gross_income[0].get(key, 0))
|
||||
total_expense = flt(gross_expense[0].get(key, 0)) + flt(non_gross_expense[0].get(key, 0))
|
||||
gross_income_for_period = flt(gross_income[0].get(key, 0)) if gross_income else 0
|
||||
non_gross_income_for_period = flt(non_gross_income[0].get(key, 0)) if non_gross_income else 0
|
||||
|
||||
gross_expense_for_period = flt(gross_expense[0].get(key, 0)) if gross_expense else 0
|
||||
non_gross_expense_for_period = flt(non_gross_expense[0].get(key, 0)) if non_gross_expense else 0
|
||||
|
||||
total_income = gross_income_for_period + non_gross_income_for_period
|
||||
total_expense = gross_expense_for_period + non_gross_expense_for_period
|
||||
profit_loss[key] = flt(total_income) - flt(total_expense)
|
||||
|
||||
if profit_loss[key]:
|
||||
has_value=True
|
||||
|
||||
if has_value:
|
||||
return profit_loss
|
||||
return profit_loss
|
||||
@@ -55,27 +55,27 @@ def get_columns(group_wise_columns, filters):
|
||||
columns = []
|
||||
column_map = frappe._dict({
|
||||
"parent": _("Sales Invoice") + ":Link/Sales Invoice:120",
|
||||
"posting_date": _("Posting Date") + ":Date",
|
||||
"posting_time": _("Posting Time"),
|
||||
"item_code": _("Item Code") + ":Link/Item",
|
||||
"item_name": _("Item Name"),
|
||||
"item_group": _("Item Group") + ":Link/Item Group",
|
||||
"brand": _("Brand"),
|
||||
"description": _("Description"),
|
||||
"warehouse": _("Warehouse") + ":Link/Warehouse",
|
||||
"qty": _("Qty") + ":Float",
|
||||
"base_rate": _("Avg. Selling Rate") + ":Currency/currency",
|
||||
"buying_rate": _("Valuation Rate") + ":Currency/currency",
|
||||
"base_amount": _("Selling Amount") + ":Currency/currency",
|
||||
"buying_amount": _("Buying Amount") + ":Currency/currency",
|
||||
"gross_profit": _("Gross Profit") + ":Currency/currency",
|
||||
"gross_profit_percent": _("Gross Profit %") + ":Percent",
|
||||
"project": _("Project") + ":Link/Project",
|
||||
"posting_date": _("Posting Date") + ":Date:100",
|
||||
"posting_time": _("Posting Time") + ":Data:100",
|
||||
"item_code": _("Item Code") + ":Link/Item:100",
|
||||
"item_name": _("Item Name") + ":Data:100",
|
||||
"item_group": _("Item Group") + ":Link/Item Group:100",
|
||||
"brand": _("Brand") + ":Link/Brand:100",
|
||||
"description": _("Description") +":Data:100",
|
||||
"warehouse": _("Warehouse") + ":Link/Warehouse:100",
|
||||
"qty": _("Qty") + ":Float:80",
|
||||
"base_rate": _("Avg. Selling Rate") + ":Currency/currency:100",
|
||||
"buying_rate": _("Valuation Rate") + ":Currency/currency:100",
|
||||
"base_amount": _("Selling Amount") + ":Currency/currency:100",
|
||||
"buying_amount": _("Buying Amount") + ":Currency/currency:100",
|
||||
"gross_profit": _("Gross Profit") + ":Currency/currency:100",
|
||||
"gross_profit_percent": _("Gross Profit %") + ":Percent:100",
|
||||
"project": _("Project") + ":Link/Project:100",
|
||||
"sales_person": _("Sales person"),
|
||||
"allocated_amount": _("Allocated Amount") + ":Currency/currency",
|
||||
"customer": _("Customer") + ":Link/Customer",
|
||||
"customer_group": _("Customer Group") + ":Link/Customer Group",
|
||||
"territory": _("Territory") + ":Link/Territory"
|
||||
"allocated_amount": _("Allocated Amount") + ":Currency/currency:100",
|
||||
"customer": _("Customer") + ":Link/Customer:100",
|
||||
"customer_group": _("Customer Group") + ":Link/Customer Group:100",
|
||||
"territory": _("Territory") + ":Link/Territory:100"
|
||||
})
|
||||
|
||||
for col in group_wise_columns.get(scrub(filters.group_by)):
|
||||
@@ -85,7 +85,8 @@ def get_columns(group_wise_columns, filters):
|
||||
"fieldname": "currency",
|
||||
"label" : _("Currency"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Currency"
|
||||
"options": "Currency",
|
||||
"hidden": 1
|
||||
})
|
||||
|
||||
return columns
|
||||
@@ -277,7 +278,7 @@ class GrossProfitGenerator(object):
|
||||
from `tabPurchase Invoice Item` a
|
||||
where a.item_code = %s and a.docstatus=1
|
||||
and modified <= %s
|
||||
order by a.modified desc limit 1""", (item_code,self.filters.to_date))
|
||||
order by a.modified desc limit 1""", (item_code, self.filters.to_date))
|
||||
else:
|
||||
last_purchase_rate = frappe.db.sql("""
|
||||
select (a.base_rate / a.conversion_factor)
|
||||
|
||||
@@ -102,7 +102,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
|
||||
data.append(row)
|
||||
|
||||
if filters.get('group_by'):
|
||||
if filters.get('group_by') and item_list:
|
||||
total_row = total_row_map.get(prev_group_by_value or d.get('item_name'))
|
||||
total_row['percent_gt'] = flt(total_row['total']/grand_total * 100)
|
||||
data.append(total_row)
|
||||
|
||||
@@ -111,7 +111,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
|
||||
data.append(row)
|
||||
|
||||
if filters.get('group_by'):
|
||||
if filters.get('group_by') and item_list:
|
||||
total_row = total_row_map.get(prev_group_by_value or d.get('item_name'))
|
||||
total_row['percent_gt'] = flt(total_row['total']/grand_total * 100)
|
||||
data.append(total_row)
|
||||
|
||||
@@ -6,6 +6,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
||||
frappe.query_reports["Profit and Loss Statement"] = $.extend({},
|
||||
erpnext.financial_statements);
|
||||
|
||||
erpnext.utils.add_dimensions('Profit and Loss Statement', 10);
|
||||
|
||||
frappe.query_reports["Profit and Loss Statement"]["filters"].push(
|
||||
{
|
||||
"fieldname": "project",
|
||||
|
||||
@@ -56,11 +56,4 @@ frappe.query_reports["Purchase Register"] = {
|
||||
]
|
||||
}
|
||||
|
||||
erpnext.dimension_filters.forEach((dimension) => {
|
||||
frappe.query_reports["Purchase Register"].filters.splice(7, 0 ,{
|
||||
"fieldname": dimension["fieldname"],
|
||||
"label": __(dimension["label"]),
|
||||
"fieldtype": "Link",
|
||||
"options": dimension["document_type"]
|
||||
});
|
||||
});
|
||||
erpnext.utils.add_dimensions('Purchase Register', 7);
|
||||
@@ -68,12 +68,5 @@ frappe.query_reports["Sales Register"] = {
|
||||
]
|
||||
}
|
||||
|
||||
erpnext.dimension_filters.forEach((dimension) => {
|
||||
frappe.query_reports["Sales Register"].filters.splice(7, 0 ,{
|
||||
"fieldname": dimension["fieldname"],
|
||||
"label": __(dimension["label"]),
|
||||
"fieldtype": "Link",
|
||||
"options": dimension["document_type"]
|
||||
});
|
||||
});
|
||||
erpnext.utils.add_dimensions('Sales Register', 7);
|
||||
|
||||
|
||||
@@ -44,9 +44,14 @@ def get_result(filters):
|
||||
out = []
|
||||
for supplier in filters.supplier:
|
||||
tds = frappe.get_doc("Tax Withholding Category", supplier.tax_withholding_category)
|
||||
rate = [d.tax_withholding_rate for d in tds.rates if d.fiscal_year == filters.fiscal_year][0]
|
||||
rate = [d.tax_withholding_rate for d in tds.rates if d.fiscal_year == filters.fiscal_year]
|
||||
|
||||
if rate:
|
||||
rate = rate[0]
|
||||
|
||||
try:
|
||||
account = [d.account for d in tds.accounts if d.company == filters.company][0]
|
||||
|
||||
except IndexError:
|
||||
account = []
|
||||
total_invoiced_amount, tds_deducted = get_invoice_and_tds_amount(supplier.name, account,
|
||||
@@ -76,7 +81,7 @@ def get_invoice_and_tds_amount(supplier, account, company, from_date, to_date):
|
||||
supplier_credit_amount = flt(sum([d.credit for d in entries]))
|
||||
|
||||
vouchers = [d.voucher_no for d in entries]
|
||||
vouchers += get_advance_vouchers(supplier, company=company,
|
||||
vouchers += get_advance_vouchers([supplier], company=company,
|
||||
from_date=from_date, to_date=to_date)
|
||||
|
||||
tds_deducted = 0
|
||||
@@ -89,7 +94,7 @@ def get_invoice_and_tds_amount(supplier, account, company, from_date, to_date):
|
||||
""".format(', '.join(["'%s'" % d for d in vouchers])),
|
||||
(account, from_date, to_date, company))[0][0])
|
||||
|
||||
debit_note_amount = get_debit_note_amount(supplier, from_date, to_date, company=company)
|
||||
debit_note_amount = get_debit_note_amount([supplier], from_date, to_date, company=company)
|
||||
|
||||
total_invoiced_amount = supplier_credit_amount + tds_deducted - debit_note_amount
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
||||
"default": frappe.defaults.get_user_default("year_end_date"),
|
||||
},
|
||||
{
|
||||
"fieldname":"cost_center",
|
||||
"fieldname": "cost_center",
|
||||
"label": __("Cost Center"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Cost Center",
|
||||
@@ -61,7 +61,13 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname":"finance_book",
|
||||
"fieldname": "project",
|
||||
"label": __("Project"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Project"
|
||||
},
|
||||
{
|
||||
"fieldname": "finance_book",
|
||||
"label": __("Finance Book"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Finance Book",
|
||||
@@ -96,14 +102,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
||||
"initial_depth": 3
|
||||
}
|
||||
|
||||
erpnext.dimension_filters.forEach((dimension) => {
|
||||
frappe.query_reports["Trial Balance"].filters.splice(5, 0 ,{
|
||||
"fieldname": dimension["fieldname"],
|
||||
"label": __(dimension["label"]),
|
||||
"fieldtype": "Link",
|
||||
"options": dimension["document_type"]
|
||||
});
|
||||
});
|
||||
erpnext.utils.add_dimensions('Trial Balance', 6);
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -69,6 +69,11 @@ def get_data(filters):
|
||||
gl_entries_by_account = {}
|
||||
|
||||
opening_balances = get_opening_balances(filters)
|
||||
|
||||
#add filter inside list so that the query in financial_statements.py doesn't break
|
||||
if filters.project:
|
||||
filters.project = [filters.project]
|
||||
|
||||
set_gl_entries_by_account(filters.company, filters.from_date,
|
||||
filters.to_date, min_lft, max_rgt, filters, gl_entries_by_account, ignore_closing_entries=not flt(filters.with_period_closing_entry))
|
||||
|
||||
@@ -102,6 +107,9 @@ def get_rootwise_opening_balances(filters, report_type):
|
||||
additional_conditions += """ and cost_center in (select name from `tabCost Center`
|
||||
where lft >= %s and rgt <= %s)""" % (lft, rgt)
|
||||
|
||||
if filters.project:
|
||||
additional_conditions += " and project = %(project)s"
|
||||
|
||||
if filters.finance_book:
|
||||
fb_conditions = " AND finance_book = %(finance_book)s"
|
||||
if filters.include_default_book_entries:
|
||||
@@ -116,6 +124,7 @@ def get_rootwise_opening_balances(filters, report_type):
|
||||
"from_date": filters.from_date,
|
||||
"report_type": report_type,
|
||||
"year_start_date": filters.year_start_date,
|
||||
"project": filters.project,
|
||||
"finance_book": filters.finance_book,
|
||||
"company_fb": frappe.db.get_value("Company", filters.company, 'default_finance_book')
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ def get_balance_on(account=None, date=None, party_type=None, party=None, company
|
||||
acc = frappe.get_doc("Account", account)
|
||||
|
||||
try:
|
||||
year_start_date = get_fiscal_year(date, verbose=0)[1]
|
||||
year_start_date = get_fiscal_year(date, company=company, verbose=0)[1]
|
||||
except FiscalYearError:
|
||||
if getdate(date) > getdate(nowdate()):
|
||||
# if fiscal year not found and the date is greater than today
|
||||
@@ -124,14 +124,12 @@ def get_balance_on(account=None, date=None, party_type=None, party=None, company
|
||||
# hence, assuming balance as 0.0
|
||||
return 0.0
|
||||
|
||||
allow_cost_center_in_entry_of_bs_account = get_allow_cost_center_in_entry_of_bs_account()
|
||||
|
||||
if account:
|
||||
report_type = acc.report_type
|
||||
else:
|
||||
report_type = ""
|
||||
|
||||
if cost_center and (allow_cost_center_in_entry_of_bs_account or report_type =='Profit and Loss'):
|
||||
if cost_center and report_type == 'Profit and Loss':
|
||||
cc = frappe.get_doc("Cost Center", cost_center)
|
||||
if cc.is_group:
|
||||
cond.append(""" exists (
|
||||
@@ -658,7 +656,8 @@ def get_outstanding_invoices(party_type, party, account, condition=None, filters
|
||||
invoice_list = frappe.db.sql("""
|
||||
select
|
||||
voucher_no, voucher_type, posting_date, due_date,
|
||||
ifnull(sum({dr_or_cr}), 0) as invoice_amount
|
||||
ifnull(sum({dr_or_cr}), 0) as invoice_amount,
|
||||
account_currency as currency
|
||||
from
|
||||
`tabGL Entry`
|
||||
where
|
||||
@@ -715,7 +714,8 @@ def get_outstanding_invoices(party_type, party, account, condition=None, filters
|
||||
'invoice_amount': flt(d.invoice_amount),
|
||||
'payment_amount': payment_amount,
|
||||
'outstanding_amount': outstanding_amount,
|
||||
'due_date': d.due_date
|
||||
'due_date': d.due_date,
|
||||
'currency': d.currency
|
||||
})
|
||||
)
|
||||
|
||||
@@ -767,10 +767,10 @@ def get_children(doctype, parent, company, is_root=False):
|
||||
company_currency = frappe.get_cached_value('Company', company, "default_currency")
|
||||
for each in acc:
|
||||
each["company_currency"] = company_currency
|
||||
each["balance"] = flt(get_balance_on(each.get("value"), in_account_currency=False))
|
||||
each["balance"] = flt(get_balance_on(each.get("value"), in_account_currency=False, company=company))
|
||||
|
||||
if each.account_currency != company_currency:
|
||||
each["balance_in_account_currency"] = flt(get_balance_on(each.get("value")))
|
||||
each["balance_in_account_currency"] = flt(get_balance_on(each.get("value"), company=company))
|
||||
|
||||
return acc
|
||||
|
||||
@@ -817,48 +817,37 @@ def create_payment_gateway_account(gateway):
|
||||
pass
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_number_field(doctype_name, name, field_name, number_value, company):
|
||||
def update_cost_center(docname, cost_center_name, cost_center_number, company, merge):
|
||||
'''
|
||||
doctype_name = Name of the DocType
|
||||
name = Docname being referred
|
||||
field_name = Name of the field thats holding the 'number' attribute
|
||||
number_value = Numeric value entered in field_name
|
||||
|
||||
Stores the number entered in the dialog to the DocType's field.
|
||||
|
||||
Renames the document by adding the number as a prefix to the current name and updates
|
||||
all transaction where it was present.
|
||||
'''
|
||||
doc_title = frappe.db.get_value(doctype_name, name, frappe.scrub(doctype_name)+"_name")
|
||||
validate_field_number("Cost Center", docname, cost_center_number, company, "cost_center_number")
|
||||
|
||||
validate_field_number(doctype_name, name, number_value, company, field_name)
|
||||
if cost_center_number:
|
||||
frappe.db.set_value("Cost Center", docname, "cost_center_number", cost_center_number.strip())
|
||||
else:
|
||||
frappe.db.set_value("Cost Center", docname, "cost_center_number", "")
|
||||
|
||||
frappe.db.set_value(doctype_name, name, field_name, number_value)
|
||||
frappe.db.set_value("Cost Center", docname, "cost_center_name", cost_center_name.strip())
|
||||
|
||||
if doc_title[0].isdigit():
|
||||
separator = " - " if " - " in doc_title else " "
|
||||
doc_title = doc_title.split(separator, 1)[1]
|
||||
|
||||
frappe.db.set_value(doctype_name, name, frappe.scrub(doctype_name)+"_name", doc_title)
|
||||
|
||||
new_name = get_autoname_with_number(number_value, doc_title, name, company)
|
||||
|
||||
if name != new_name:
|
||||
frappe.rename_doc(doctype_name, name, new_name)
|
||||
new_name = get_autoname_with_number(cost_center_number, cost_center_name, docname, company)
|
||||
if docname != new_name:
|
||||
frappe.rename_doc("Cost Center", docname, new_name, force=1, merge=merge)
|
||||
return new_name
|
||||
|
||||
def validate_field_number(doctype_name, name, number_value, company, field_name):
|
||||
def validate_field_number(doctype_name, docname, number_value, company, field_name):
|
||||
''' Validate if the number entered isn't already assigned to some other document. '''
|
||||
if number_value:
|
||||
filters = {field_name: number_value, "name": ["!=", docname]}
|
||||
if company:
|
||||
doctype_with_same_number = frappe.db.get_value(doctype_name,
|
||||
{field_name: number_value, "company": company, "name": ["!=", name]})
|
||||
else:
|
||||
doctype_with_same_number = frappe.db.get_value(doctype_name,
|
||||
{field_name: number_value, "name": ["!=", name]})
|
||||
filters["company"] = company
|
||||
|
||||
doctype_with_same_number = frappe.db.get_value(doctype_name, filters)
|
||||
|
||||
if doctype_with_same_number:
|
||||
frappe.throw(_("{0} Number {1} already used in account {2}")
|
||||
.format(doctype_name, number_value, doctype_with_same_number))
|
||||
frappe.throw(_("{0} Number {1} is already used in {2} {3}")
|
||||
.format(doctype_name, number_value, doctype_name.lower(), doctype_with_same_number))
|
||||
|
||||
def get_autoname_with_number(number_value, doc_title, name, company):
|
||||
''' append title with prefix as number and suffix as company's abbreviation separated by '-' '''
|
||||
@@ -888,11 +877,6 @@ def get_coa(doctype, parent, is_root, chart=None):
|
||||
|
||||
return accounts
|
||||
|
||||
def get_allow_cost_center_in_entry_of_bs_account():
|
||||
def generator():
|
||||
return cint(frappe.db.get_value('Accounts Settings', None, 'allow_cost_center_in_entry_of_bs_account'))
|
||||
return frappe.local_cache("get_allow_cost_center_in_entry_of_bs_account", (), generator, regenerate_if_none=True)
|
||||
|
||||
def get_stock_accounts(company):
|
||||
return frappe.get_all("Account", filters = {
|
||||
"account_type": "Stock",
|
||||
|
||||
@@ -22,6 +22,7 @@ class Asset(AccountsController):
|
||||
self.validate_item()
|
||||
self.set_missing_values()
|
||||
self.prepare_depreciation_data()
|
||||
self.validate_gross_and_purchase_amount()
|
||||
if self.get("schedules"):
|
||||
self.validate_expected_value_after_useful_life()
|
||||
|
||||
@@ -31,7 +32,7 @@ class Asset(AccountsController):
|
||||
self.validate_in_use_date()
|
||||
self.set_status()
|
||||
self.make_asset_movement()
|
||||
if not self.booked_fixed_asset and is_cwip_accounting_enabled(self.asset_category):
|
||||
if not self.booked_fixed_asset and self.validate_make_gl_entry():
|
||||
self.make_gl_entries()
|
||||
|
||||
def before_cancel(self):
|
||||
@@ -123,6 +124,14 @@ class Asset(AccountsController):
|
||||
|
||||
if self.available_for_use_date and getdate(self.available_for_use_date) < getdate(self.purchase_date):
|
||||
frappe.throw(_("Available-for-use Date should be after purchase date"))
|
||||
|
||||
def validate_gross_and_purchase_amount(self):
|
||||
if self.is_existing_asset: return
|
||||
|
||||
if self.gross_purchase_amount and self.gross_purchase_amount != self.purchase_receipt_amount:
|
||||
frappe.throw(_("Gross Purchase Amount should be {} to purchase amount of one single Asset. {}\
|
||||
Please do not book expense of multiple assets against one single Asset.")
|
||||
.format(frappe.bold("equal"), "<br>"), title=_("Invalid Gross Purchase Amount"))
|
||||
|
||||
def cancel_auto_gen_movement(self):
|
||||
movements = frappe.db.sql(
|
||||
@@ -398,6 +407,8 @@ class Asset(AccountsController):
|
||||
row.expected_value_after_useful_life = asset_value_after_full_schedule
|
||||
|
||||
def validate_cancellation(self):
|
||||
if self.status in ("In Maintenance", "Out of Order"):
|
||||
frappe.throw(_("There are active maintenance or repairs against the asset. You must complete all of them before cancelling the asset."))
|
||||
if self.status not in ("Submitted", "Partially Depreciated", "Fully Depreciated"):
|
||||
frappe.throw(_("Asset cannot be cancelled, as it is already {0}").format(self.status))
|
||||
|
||||
@@ -447,18 +458,55 @@ class Asset(AccountsController):
|
||||
for d in self.get('finance_books'):
|
||||
if d.finance_book == self.default_finance_book:
|
||||
return cint(d.idx) - 1
|
||||
|
||||
def validate_make_gl_entry(self):
|
||||
purchase_document = self.get_purchase_document()
|
||||
asset_bought_with_invoice = purchase_document == self.purchase_invoice
|
||||
fixed_asset_account, cwip_account = self.get_asset_accounts()
|
||||
cwip_enabled = is_cwip_accounting_enabled(self.asset_category)
|
||||
# check if expense already has been booked in case of cwip was enabled after purchasing asset
|
||||
expense_booked = False
|
||||
cwip_booked = False
|
||||
|
||||
if asset_bought_with_invoice:
|
||||
expense_booked = frappe.db.sql("""SELECT name FROM `tabGL Entry` WHERE voucher_no = %s and account = %s""",
|
||||
(purchase_document, fixed_asset_account), as_dict=1)
|
||||
else:
|
||||
cwip_booked = frappe.db.sql("""SELECT name FROM `tabGL Entry` WHERE voucher_no = %s and account = %s""",
|
||||
(purchase_document, cwip_account), as_dict=1)
|
||||
|
||||
if cwip_enabled and (expense_booked or not cwip_booked):
|
||||
# if expense has already booked from invoice or cwip is booked from receipt
|
||||
return False
|
||||
elif not cwip_enabled and (not expense_booked or cwip_booked):
|
||||
# if cwip is disabled but expense hasn't been booked yet
|
||||
return True
|
||||
elif cwip_enabled:
|
||||
# default condition
|
||||
return True
|
||||
|
||||
def get_purchase_document(self):
|
||||
asset_bought_with_invoice = self.purchase_invoice and frappe.db.get_value('Purchase Invoice', self.purchase_invoice, 'update_stock')
|
||||
purchase_document = self.purchase_invoice if asset_bought_with_invoice else self.purchase_receipt
|
||||
|
||||
return purchase_document
|
||||
|
||||
def get_asset_accounts(self):
|
||||
fixed_asset_account = get_asset_category_account('fixed_asset_account', asset=self.name,
|
||||
asset_category = self.asset_category, company = self.company)
|
||||
|
||||
cwip_account = get_asset_account("capital_work_in_progress_account",
|
||||
self.name, self.asset_category, self.company)
|
||||
|
||||
return fixed_asset_account, cwip_account
|
||||
|
||||
def make_gl_entries(self):
|
||||
gl_entries = []
|
||||
|
||||
if ((self.purchase_receipt \
|
||||
or (self.purchase_invoice and frappe.db.get_value('Purchase Invoice', self.purchase_invoice, 'update_stock')))
|
||||
and self.purchase_receipt_amount and self.available_for_use_date <= nowdate()):
|
||||
fixed_asset_account = get_asset_category_account('fixed_asset_account', asset=self.name,
|
||||
asset_category = self.asset_category, company = self.company)
|
||||
purchase_document = self.get_purchase_document()
|
||||
fixed_asset_account, cwip_account = self.get_asset_accounts()
|
||||
|
||||
cwip_account = get_asset_account("capital_work_in_progress_account",
|
||||
self.name, self.asset_category, self.company)
|
||||
if (purchase_document and self.purchase_receipt_amount and self.available_for_use_date <= nowdate()):
|
||||
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
"account": cwip_account,
|
||||
@@ -468,7 +516,7 @@ class Asset(AccountsController):
|
||||
"credit": self.purchase_receipt_amount,
|
||||
"credit_in_account_currency": self.purchase_receipt_amount,
|
||||
"cost_center": self.cost_center
|
||||
}))
|
||||
}, item=self))
|
||||
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
"account": fixed_asset_account,
|
||||
@@ -478,7 +526,7 @@ class Asset(AccountsController):
|
||||
"debit": self.purchase_receipt_amount,
|
||||
"debit_in_account_currency": self.purchase_receipt_amount,
|
||||
"cost_center": self.cost_center
|
||||
}))
|
||||
}, item=self))
|
||||
|
||||
if gl_entries:
|
||||
from erpnext.accounts.general_ledger import make_gl_entries
|
||||
@@ -611,7 +659,7 @@ def get_asset_account(account_name, asset=None, asset_category=None, company=Non
|
||||
if asset:
|
||||
account = get_asset_category_account(account_name, asset=asset,
|
||||
asset_category = asset_category, company = company)
|
||||
|
||||
|
||||
if not asset and not account:
|
||||
account = get_asset_category_account(account_name, asset_category = asset_category, company = company)
|
||||
|
||||
|
||||
@@ -58,7 +58,8 @@ def make_depreciation_entry(asset_name, date=None):
|
||||
"account": accumulated_depreciation_account,
|
||||
"credit_in_account_currency": d.depreciation_amount,
|
||||
"reference_type": "Asset",
|
||||
"reference_name": asset.name
|
||||
"reference_name": asset.name,
|
||||
"cost_center": ""
|
||||
}
|
||||
|
||||
debit_entry = {
|
||||
@@ -196,12 +197,14 @@ def get_gl_entries_on_asset_disposal(asset, selling_amount=0, finance_book=None)
|
||||
{
|
||||
"account": fixed_asset_account,
|
||||
"credit_in_account_currency": asset.gross_purchase_amount,
|
||||
"credit": asset.gross_purchase_amount
|
||||
"credit": asset.gross_purchase_amount,
|
||||
"cost_center": depreciation_cost_center
|
||||
},
|
||||
{
|
||||
"account": accumulated_depr_account,
|
||||
"debit_in_account_currency": accumulated_depr_amount,
|
||||
"debit": accumulated_depr_amount
|
||||
"debit": accumulated_depr_amount,
|
||||
"cost_center": depreciation_cost_center
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -82,7 +82,6 @@ class TestAsset(unittest.TestCase):
|
||||
doc.set_missing_values()
|
||||
self.assertEquals(doc.items[0].is_fixed_asset, 1)
|
||||
|
||||
|
||||
def test_schedule_for_straight_line_method(self):
|
||||
pr = make_purchase_receipt(item_code="Macbook Pro",
|
||||
qty=1, rate=100000.0, location="Test Location")
|
||||
@@ -564,6 +563,81 @@ class TestAsset(unittest.TestCase):
|
||||
|
||||
self.assertEqual(gle, expected_gle)
|
||||
|
||||
def test_gle_with_cwip_toggling(self):
|
||||
# TEST: purchase an asset with cwip enabled and then disable cwip and try submitting the asset
|
||||
frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 1)
|
||||
|
||||
pr = make_purchase_receipt(item_code="Macbook Pro",
|
||||
qty=1, rate=5000, do_not_submit=True, location="Test Location")
|
||||
pr.set('taxes', [{
|
||||
'category': 'Total',
|
||||
'add_deduct_tax': 'Add',
|
||||
'charge_type': 'On Net Total',
|
||||
'account_head': '_Test Account Service Tax - _TC',
|
||||
'description': '_Test Account Service Tax',
|
||||
'cost_center': 'Main - _TC',
|
||||
'rate': 5.0
|
||||
}, {
|
||||
'category': 'Valuation and Total',
|
||||
'add_deduct_tax': 'Add',
|
||||
'charge_type': 'On Net Total',
|
||||
'account_head': '_Test Account Shipping Charges - _TC',
|
||||
'description': '_Test Account Shipping Charges',
|
||||
'cost_center': 'Main - _TC',
|
||||
'rate': 5.0
|
||||
}])
|
||||
pr.submit()
|
||||
expected_gle = (
|
||||
("Asset Received But Not Billed - _TC", 0.0, 5250.0),
|
||||
("CWIP Account - _TC", 5250.0, 0.0)
|
||||
)
|
||||
pr_gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
|
||||
where voucher_type='Purchase Receipt' and voucher_no = %s
|
||||
order by account""", pr.name)
|
||||
self.assertEqual(pr_gle, expected_gle)
|
||||
|
||||
pi = make_invoice(pr.name)
|
||||
pi.submit()
|
||||
expected_gle = (
|
||||
("_Test Account Service Tax - _TC", 250.0, 0.0),
|
||||
("_Test Account Shipping Charges - _TC", 250.0, 0.0),
|
||||
("Asset Received But Not Billed - _TC", 5250.0, 0.0),
|
||||
("Creditors - _TC", 0.0, 5500.0),
|
||||
("Expenses Included In Asset Valuation - _TC", 0.0, 250.0),
|
||||
)
|
||||
pi_gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
|
||||
where voucher_type='Purchase Invoice' and voucher_no = %s
|
||||
order by account""", pi.name)
|
||||
self.assertEqual(pi_gle, expected_gle)
|
||||
|
||||
asset = frappe.db.get_value('Asset', {'purchase_receipt': pr.name, 'docstatus': 0}, 'name')
|
||||
asset_doc = frappe.get_doc('Asset', asset)
|
||||
month_end_date = get_last_day(nowdate())
|
||||
asset_doc.available_for_use_date = nowdate() if nowdate() != month_end_date else add_days(nowdate(), -15)
|
||||
self.assertEqual(asset_doc.gross_purchase_amount, 5250.0)
|
||||
asset_doc.append("finance_books", {
|
||||
"expected_value_after_useful_life": 200,
|
||||
"depreciation_method": "Straight Line",
|
||||
"total_number_of_depreciations": 3,
|
||||
"frequency_of_depreciation": 10,
|
||||
"depreciation_start_date": month_end_date
|
||||
})
|
||||
|
||||
# disable cwip and try submitting
|
||||
frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 0)
|
||||
asset_doc.submit()
|
||||
# asset should have gl entries even if cwip is disabled
|
||||
expected_gle = (
|
||||
("_Test Fixed Asset - _TC", 5250.0, 0.0),
|
||||
("CWIP Account - _TC", 0.0, 5250.0)
|
||||
)
|
||||
gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
|
||||
where voucher_type='Asset' and voucher_no = %s
|
||||
order by account""", asset_doc.name)
|
||||
self.assertEqual(gle, expected_gle)
|
||||
|
||||
frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 1)
|
||||
|
||||
def test_expense_head(self):
|
||||
pr = make_purchase_receipt(item_code="Macbook Pro",
|
||||
qty=2, rate=200000.0, location="Test Location")
|
||||
@@ -599,6 +673,7 @@ def create_asset(**args):
|
||||
"purchase_date": "2015-01-01",
|
||||
"calculate_depreciation": 0,
|
||||
"gross_purchase_amount": 100000,
|
||||
"purchase_receipt_amount": 100000,
|
||||
"expected_value_after_useful_life": 10000,
|
||||
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
||||
"available_for_use_date": "2020-06-06",
|
||||
|
||||
@@ -11,12 +11,54 @@ from frappe.model.document import Document
|
||||
class AssetCategory(Document):
|
||||
def validate(self):
|
||||
self.validate_finance_books()
|
||||
self.validate_account_types()
|
||||
self.validate_account_currency()
|
||||
|
||||
def validate_finance_books(self):
|
||||
for d in self.finance_books:
|
||||
for field in ("Total Number of Depreciations", "Frequency of Depreciation"):
|
||||
if cint(d.get(frappe.scrub(field)))<1:
|
||||
frappe.throw(_("Row {0}: {1} must be greater than 0").format(d.idx, field), frappe.MandatoryError)
|
||||
|
||||
def validate_account_currency(self):
|
||||
account_types = [
|
||||
'fixed_asset_account', 'accumulated_depreciation_account', 'depreciation_expense_account', 'capital_work_in_progress_account'
|
||||
]
|
||||
invalid_accounts = []
|
||||
for d in self.accounts:
|
||||
company_currency = frappe.get_value('Company', d.get('company_name'), 'default_currency')
|
||||
for type_of_account in account_types:
|
||||
if d.get(type_of_account):
|
||||
account_currency = frappe.get_value("Account", d.get(type_of_account), "account_currency")
|
||||
if account_currency != company_currency:
|
||||
invalid_accounts.append(frappe._dict({ 'type': type_of_account, 'idx': d.idx, 'account': d.get(type_of_account) }))
|
||||
|
||||
for d in invalid_accounts:
|
||||
frappe.throw(_("Row #{}: Currency of {} - {} doesn't matches company currency.")
|
||||
.format(d.idx, frappe.bold(frappe.unscrub(d.type)), frappe.bold(d.account)),
|
||||
title=_("Invalid Account"))
|
||||
|
||||
|
||||
def validate_account_types(self):
|
||||
account_type_map = {
|
||||
'fixed_asset_account': { 'account_type': 'Fixed Asset' },
|
||||
'accumulated_depreciation_account': { 'account_type': 'Accumulated Depreciation' },
|
||||
'depreciation_expense_account': { 'root_type': 'Expense' },
|
||||
'capital_work_in_progress_account': { 'account_type': 'Capital Work in Progress' }
|
||||
}
|
||||
for d in self.accounts:
|
||||
for fieldname in account_type_map.keys():
|
||||
if d.get(fieldname):
|
||||
selected_account = d.get(fieldname)
|
||||
key_to_match = next(iter(account_type_map.get(fieldname))) # acount_type or root_type
|
||||
selected_key_type = frappe.db.get_value('Account', selected_account, key_to_match)
|
||||
expected_key_type = account_type_map[fieldname][key_to_match]
|
||||
|
||||
if selected_key_type != expected_key_type:
|
||||
frappe.throw(_("Row #{}: {} of {} should be {}. Please modify the account or select a different account.")
|
||||
.format(d.idx, frappe.unscrub(key_to_match), frappe.bold(selected_account), frappe.bold(expected_key_type)),
|
||||
title=_("Invalid Account"))
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_asset_category_account(fieldname, item=None, asset=None, account=None, asset_category = None, company = None):
|
||||
|
||||
@@ -24,26 +24,6 @@ frappe.ui.form.on('Asset Maintenance', {
|
||||
return indicator;
|
||||
}
|
||||
);
|
||||
|
||||
frm.set_query('select_serial_no', function(doc){
|
||||
return {
|
||||
asset: frm.doc.asset_name
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
select_serial_no: (frm) => {
|
||||
let serial_nos = frm.doc.serial_no || frm.doc.select_serial_no;
|
||||
if (serial_nos) {
|
||||
serial_nos = serial_nos.split('\n');
|
||||
serial_nos.push(frm.doc.select_serial_no);
|
||||
|
||||
const unique_sn = serial_nos.filter(function(elem, index, self) {
|
||||
return index === self.indexOf(elem);
|
||||
});
|
||||
|
||||
frm.set_value("serial_no", unique_sn.join('\n'));
|
||||
}
|
||||
},
|
||||
|
||||
refresh: (frm) => {
|
||||
@@ -93,25 +73,6 @@ frappe.ui.form.on('Asset Maintenance Task', {
|
||||
},
|
||||
end_date: (frm, cdt, cdn) => {
|
||||
get_next_due_date(frm, cdt, cdn);
|
||||
},
|
||||
assign_to: (frm, cdt, cdn) => {
|
||||
var d = locals[cdt][cdn];
|
||||
if (frm.doc.__islocal) {
|
||||
frappe.model.set_value(cdt, cdn, "assign_to", "");
|
||||
frappe.model.set_value(cdt, cdn, "assign_to_name", "");
|
||||
frappe.throw(__("Please save before assigning task."));
|
||||
}
|
||||
if (d.assign_to) {
|
||||
return frappe.call({
|
||||
method: 'erpnext.assets.doctype.asset_maintenance.asset_maintenance.assign_tasks',
|
||||
args: {
|
||||
asset_maintenance_name: frm.doc.name,
|
||||
assign_to_member: d.assign_to,
|
||||
maintenance_task: d.maintenance_task,
|
||||
next_due_date: d.next_due_date
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,559 +1,140 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "field:asset_name",
|
||||
"beta": 0,
|
||||
"creation": "2017-10-19 16:50:22.879545",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"actions": [],
|
||||
"autoname": "field:asset_name",
|
||||
"creation": "2017-10-19 16:50:22.879545",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"asset_name",
|
||||
"asset_category",
|
||||
"company",
|
||||
"column_break_3",
|
||||
"item_code",
|
||||
"item_name",
|
||||
"section_break_6",
|
||||
"maintenance_team",
|
||||
"column_break_9",
|
||||
"maintenance_manager",
|
||||
"maintenance_manager_name",
|
||||
"section_break_8",
|
||||
"asset_maintenance_tasks"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "asset_name",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Asset Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Asset",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "asset_name",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Asset Name",
|
||||
"options": "Asset",
|
||||
"reqd": 1,
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "asset_name.asset_category",
|
||||
"fieldname": "asset_category",
|
||||
"fieldtype": "Read Only",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Asset Category",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "asset_category",
|
||||
"fieldtype": "Read Only",
|
||||
"label": "Asset Category"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "asset_name.item_code",
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Read Only",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Item Code",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Read Only",
|
||||
"label": "Item Code"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "asset_name.item_name",
|
||||
"fieldname": "item_name",
|
||||
"fieldtype": "Read Only",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Item Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "item_name",
|
||||
"fieldtype": "Read Only",
|
||||
"label": "Item Name"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Company",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Company",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"label": "Company",
|
||||
"options": "Company",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "select_serial_no",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Select Serial No",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Serial No",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "section_break_6",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "serial_no",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Serial No",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "maintenance_team",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Maintenance Team",
|
||||
"options": "Asset Maintenance Team",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_6",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "column_break_9",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "maintenance_team",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Maintenance Team",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Asset Maintenance Team",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_9",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "maintenance_team.maintenance_manager",
|
||||
"fieldname": "maintenance_manager",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Maintenance Manager",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "maintenance_manager",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Maintenance Manager",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "maintenance_team.maintenance_manager_name",
|
||||
"fieldname": "maintenance_manager_name",
|
||||
"fieldtype": "Read Only",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Maintenance Manager Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "maintenance_manager_name",
|
||||
"fieldtype": "Read Only",
|
||||
"label": "Maintenance Manager Name"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_8",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Tasks",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "section_break_8",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Tasks"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "asset_maintenance_tasks",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Maintenance Tasks",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Asset Maintenance Task",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"fieldname": "asset_maintenance_tasks",
|
||||
"fieldtype": "Table",
|
||||
"label": "Maintenance Tasks",
|
||||
"options": "Asset Maintenance Task",
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-05-22 17:20:54.711885",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Assets",
|
||||
"name": "Asset Maintenance",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2020-05-28 20:28:32.993823",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Assets",
|
||||
"name": "Asset Maintenance",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Quality Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Quality Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Manufacturing User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Manufacturing User",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user