Compare commits
883 Commits
v15.21.2
...
fix-get_si
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0255642deb | ||
|
|
2178fdc65a | ||
|
|
eabf706f37 | ||
|
|
d4d9e6c6fa | ||
|
|
2eb318a5ce | ||
|
|
b897225136 | ||
|
|
bb18ae82cf | ||
|
|
74ceb6da5e | ||
|
|
e0ad52b500 | ||
|
|
8252d92e6a | ||
|
|
b15795392b | ||
|
|
274c65c451 | ||
|
|
1b4fbbb115 | ||
|
|
f95403d1dc | ||
|
|
2d2ff7cf52 | ||
|
|
d32a407e08 | ||
|
|
5e0d017497 | ||
|
|
ec07b42ea2 | ||
|
|
3b861798e6 | ||
|
|
b498094a97 | ||
|
|
62bbcbc7ef | ||
|
|
96530b9c0b | ||
|
|
0cbcc55d9a | ||
|
|
6b5c54bcbc | ||
|
|
c2dde04aa2 | ||
|
|
c14986f9e6 | ||
|
|
62a6945bd2 | ||
|
|
be8bb235dc | ||
|
|
498bf15ecd | ||
|
|
210d7711b4 | ||
|
|
cf988434e9 | ||
|
|
466625213b | ||
|
|
9ba6ff67d5 | ||
|
|
eab22eb282 | ||
|
|
7e198ccb21 | ||
|
|
3d54fd8389 | ||
|
|
d0ea598cdf | ||
|
|
6de8c18f98 | ||
|
|
5923b48ede | ||
|
|
7d264696f3 | ||
|
|
b96c063c93 | ||
|
|
2bad706dcf | ||
|
|
7fa3e82ac7 | ||
|
|
7ddbfa10c9 | ||
|
|
4bd437b59d | ||
|
|
fca8028e3c | ||
|
|
cd293a5173 | ||
|
|
07b605a287 | ||
|
|
54383cfb55 | ||
|
|
4cc3c1b765 | ||
|
|
4d56f725fe | ||
|
|
c34f09c503 | ||
|
|
fe43dab4d7 | ||
|
|
b69cdeb4a6 | ||
|
|
15dc5c7e99 | ||
|
|
e05bf9d32a | ||
|
|
3fb7886418 | ||
|
|
bae7c64964 | ||
|
|
68c0e188e8 | ||
|
|
e912e9597d | ||
|
|
610d4f5cb6 | ||
|
|
cdd5441435 | ||
|
|
f09e2130a1 | ||
|
|
0819675fce | ||
|
|
784b6dcfea | ||
|
|
245effcccd | ||
|
|
0d01bd8a5a | ||
|
|
57b6a98703 | ||
|
|
e1b0fffd0c | ||
|
|
c2f88f29dc | ||
|
|
277dade9f9 | ||
|
|
e2cf1ea73e | ||
|
|
df409d80e0 | ||
|
|
5b1571879c | ||
|
|
bb839b2924 | ||
|
|
5bc2035bd0 | ||
|
|
9e973476b2 | ||
|
|
5e2669f4b6 | ||
|
|
0f1be03faf | ||
|
|
351ee5b8fe | ||
|
|
e84c9f7c51 | ||
|
|
7da9ffa3bd | ||
|
|
4415212a2d | ||
|
|
6401908f41 | ||
|
|
beee98da6d | ||
|
|
60c33ac3e6 | ||
|
|
1bc74bde29 | ||
|
|
12c40ef2e4 | ||
|
|
b250a21a2b | ||
|
|
4e28b5a199 | ||
|
|
739434b727 | ||
|
|
9ea963bfe9 | ||
|
|
6e5484ea03 | ||
|
|
d048644327 | ||
|
|
1a9e091d12 | ||
|
|
1b0d9643cd | ||
|
|
92bc962f60 | ||
|
|
a9576f0cf6 | ||
|
|
4f8a16848f | ||
|
|
bacf2b7431 | ||
|
|
34d8bc4701 | ||
|
|
510fdf7bf6 | ||
|
|
0346f47c1d | ||
|
|
3f9693b31f | ||
|
|
877cc7255d | ||
|
|
70abedc57a | ||
|
|
0c6de4ecb2 | ||
|
|
a517125d64 | ||
|
|
b7f283b2f0 | ||
|
|
cd37fd790b | ||
|
|
f983e09f92 | ||
|
|
3b4b2275de | ||
|
|
b5340c5ec0 | ||
|
|
10074e9980 | ||
|
|
bbee9b5637 | ||
|
|
915d864166 | ||
|
|
8555617295 | ||
|
|
b71b0d5997 | ||
|
|
a117ef3cb8 | ||
|
|
33b631e395 | ||
|
|
9b1c22250f | ||
|
|
4feecb69d8 | ||
|
|
12560e2407 | ||
|
|
0890b414b1 | ||
|
|
d54f8318fb | ||
|
|
fe77b9d633 | ||
|
|
47ee801d37 | ||
|
|
705ae7da14 | ||
|
|
7223106417 | ||
|
|
5a5758423e | ||
|
|
b09c9354fb | ||
|
|
d00f6672a8 | ||
|
|
06d6220a2a | ||
|
|
3011322b22 | ||
|
|
e560029736 | ||
|
|
b1ba210332 | ||
|
|
cb9114442b | ||
|
|
eb5bb9f9a9 | ||
|
|
87ba3b64f7 | ||
|
|
47f7b65058 | ||
|
|
161ae1edd1 | ||
|
|
d097ad6c19 | ||
|
|
a5d5223c0e | ||
|
|
1a1629196d | ||
|
|
d0ed8ef83b | ||
|
|
0773f66feb | ||
|
|
2dc49c834a | ||
|
|
787333896c | ||
|
|
283763dfb2 | ||
|
|
07175367d8 | ||
|
|
61219ca4ce | ||
|
|
c20995ec2f | ||
|
|
6a0a08b59c | ||
|
|
d370c60a6c | ||
|
|
a6ab53236e | ||
|
|
9a00edb031 | ||
|
|
ae353398d9 | ||
|
|
9983283f95 | ||
|
|
6d5bdc6c68 | ||
|
|
80e69210db | ||
|
|
793e3ad78e | ||
|
|
4057682c87 | ||
|
|
5e68b7e3a6 | ||
|
|
495b47db16 | ||
|
|
4aa960b744 | ||
|
|
dd6c192695 | ||
|
|
8772628912 | ||
|
|
a99d0a65b0 | ||
|
|
a09241e3c7 | ||
|
|
c7b961ffa2 | ||
|
|
32a608f948 | ||
|
|
8d79365e0d | ||
|
|
71e833c3f2 | ||
|
|
b5f3013005 | ||
|
|
39ef75e2d0 | ||
|
|
42813d38c3 | ||
|
|
55147781f3 | ||
|
|
c9fd182268 | ||
|
|
0743289925 | ||
|
|
4d75159247 | ||
|
|
baa1978128 | ||
|
|
259f313af7 | ||
|
|
863116f1cd | ||
|
|
5cb5e09dbb | ||
|
|
fa1c7b663c | ||
|
|
a9a84cc7d4 | ||
|
|
ff967c45f7 | ||
|
|
9a5c422074 | ||
|
|
5a83a16e60 | ||
|
|
1704180f38 | ||
|
|
0cf9ff0a04 | ||
|
|
41ae2a2dc5 | ||
|
|
f7b2380ec1 | ||
|
|
5eeb650dfd | ||
|
|
a1b95606b1 | ||
|
|
726ac6bda1 | ||
|
|
d6201ce5c7 | ||
|
|
e7e23fbc96 | ||
|
|
e7544e9fc1 | ||
|
|
eaf86a6461 | ||
|
|
f53ba178a8 | ||
|
|
c68ad73c6e | ||
|
|
6851c5042f | ||
|
|
da96578afb | ||
|
|
27f05145ae | ||
|
|
1b3ba25220 | ||
|
|
13cba5068b | ||
|
|
db24e24882 | ||
|
|
e7984b3ef9 | ||
|
|
b562b4cf99 | ||
|
|
69d7a640ee | ||
|
|
fa2d33cb50 | ||
|
|
2588970d55 | ||
|
|
d4ac57704c | ||
|
|
15c90551b6 | ||
|
|
0acd0f50c5 | ||
|
|
acb6e8e120 | ||
|
|
ccff588563 | ||
|
|
780c4278e6 | ||
|
|
2d6506ecec | ||
|
|
a97b3db749 | ||
|
|
cc15f695b4 | ||
|
|
c41cbb3e29 | ||
|
|
6ad298adfc | ||
|
|
db9829e83f | ||
|
|
956c3c50a0 | ||
|
|
40c1acc961 | ||
|
|
7babfd4ac4 | ||
|
|
65df4b6aa8 | ||
|
|
2721ee3a8d | ||
|
|
137b5a6108 | ||
|
|
b023e5d6b3 | ||
|
|
89a0e9c245 | ||
|
|
89326bd657 | ||
|
|
8ddc26eb2e | ||
|
|
ca0c3eb184 | ||
|
|
0465c9aabb | ||
|
|
a045916aca | ||
|
|
be312cea4c | ||
|
|
9611e9bd7f | ||
|
|
08ed3cd313 | ||
|
|
231ab83562 | ||
|
|
f45dd740c5 | ||
|
|
0156339f34 | ||
|
|
f99bb61181 | ||
|
|
6a47a2ceaf | ||
|
|
525f656cc1 | ||
|
|
d34787cf6d | ||
|
|
9424bbc01c | ||
|
|
16c297c2ec | ||
|
|
8beec58670 | ||
|
|
bac811bd5e | ||
|
|
eb9ee3f79b | ||
|
|
5da3e532c9 | ||
|
|
b1d9f3132d | ||
|
|
aaa9036eca | ||
|
|
005c5a587f | ||
|
|
47c78a5a73 | ||
|
|
24ccb3eb78 | ||
|
|
a56b79cc72 | ||
|
|
84ee50e492 | ||
|
|
16b7401d4c | ||
|
|
1a5d56977e | ||
|
|
6812e91893 | ||
|
|
4ca84eadb6 | ||
|
|
27aba02d16 | ||
|
|
3d7ad71b22 | ||
|
|
8d5045ef4c | ||
|
|
00261094c8 | ||
|
|
9471d8fff9 | ||
|
|
ab6e92aae1 | ||
|
|
fb3421fcce | ||
|
|
c8693cdf37 | ||
|
|
3df1d75bdd | ||
|
|
fc12238fcc | ||
|
|
15fff84bb5 | ||
|
|
01aadbef85 | ||
|
|
ccdcb7dfcc | ||
|
|
94fabe0321 | ||
|
|
dd39da0b77 | ||
|
|
3688d9412e | ||
|
|
4918aeb4c6 | ||
|
|
d659d407a0 | ||
|
|
262cafc430 | ||
|
|
11190aac4c | ||
|
|
6d31563920 | ||
|
|
b6957ddac2 | ||
|
|
b2d8a44199 | ||
|
|
2815d196de | ||
|
|
64266c4d38 | ||
|
|
f4d418ea6d | ||
|
|
a008f5f611 | ||
|
|
74eab91042 | ||
|
|
9aeb3932d0 | ||
|
|
b6a7549407 | ||
|
|
9087e1443e | ||
|
|
5952cfa673 | ||
|
|
a1e0197a8b | ||
|
|
af7bc4f178 | ||
|
|
023bc36592 | ||
|
|
ceeb724acc | ||
|
|
5e9016ffab | ||
|
|
6bc40373f2 | ||
|
|
14c8c8c33d | ||
|
|
0925706d5e | ||
|
|
ca14ae8f1b | ||
|
|
90c6d4dc85 | ||
|
|
228aa1a244 | ||
|
|
993e2bfbf9 | ||
|
|
ea3071db66 | ||
|
|
395299803f | ||
|
|
5e2d21c033 | ||
|
|
c4c3090f46 | ||
|
|
6492019383 | ||
|
|
f6831fba13 | ||
|
|
16db6c2f47 | ||
|
|
bd3dc6482e | ||
|
|
f7b7b2b438 | ||
|
|
99fbd8ad18 | ||
|
|
9d20256366 | ||
|
|
85f2a6dd54 | ||
|
|
de2eba0d98 | ||
|
|
705ef4f5a3 | ||
|
|
b2dde55f2c | ||
|
|
d53b34c0ce | ||
|
|
a427029151 | ||
|
|
2de3e6ce6d | ||
|
|
03a38ed025 | ||
|
|
26503a205f | ||
|
|
5a25c80f2e | ||
|
|
4c8a8c3bcd | ||
|
|
decdbd2782 | ||
|
|
eb4c476490 | ||
|
|
68f5dd3e7b | ||
|
|
68aee8c144 | ||
|
|
b24e28953b | ||
|
|
9656412bba | ||
|
|
61280117eb | ||
|
|
961bdf0d24 | ||
|
|
080aa30407 | ||
|
|
eecf9cd1d8 | ||
|
|
e2a519464b | ||
|
|
60a81a563e | ||
|
|
3a66aefd2c | ||
|
|
54f7cf59fd | ||
|
|
ba10f7d04c | ||
|
|
691e3bb24f | ||
|
|
832734ff4c | ||
|
|
3d4156cc7d | ||
|
|
cc053ad894 | ||
|
|
ae294ee470 | ||
|
|
e4bdd3a28d | ||
|
|
63313eef6f | ||
|
|
1da9087cc4 | ||
|
|
de3795a7d4 | ||
|
|
cfd3230c75 | ||
|
|
e38b06bf2d | ||
|
|
7cb0b1b7c3 | ||
|
|
62b4a263f8 | ||
|
|
96b13c59c1 | ||
|
|
1423b38d50 | ||
|
|
ff27cccff4 | ||
|
|
abc7d30024 | ||
|
|
591f4ebdca | ||
|
|
ef8e4191cd | ||
|
|
cf97e3c21f | ||
|
|
2e8739fff7 | ||
|
|
3da0aa6a0b | ||
|
|
97be527ee9 | ||
|
|
cac3b4a0d8 | ||
|
|
96f31847b2 | ||
|
|
85f9f8d176 | ||
|
|
7b0cd03f88 | ||
|
|
68585f6f2b | ||
|
|
857f2b5a01 | ||
|
|
258148b615 | ||
|
|
7145b040f1 | ||
|
|
874766a82f | ||
|
|
096a2c8cd0 | ||
|
|
4a047fefb8 | ||
|
|
fddf341f44 | ||
|
|
fe5fc5bd3a | ||
|
|
0255e09285 | ||
|
|
2b0b15f4d1 | ||
|
|
8e4b591ea2 | ||
|
|
663bb8726c | ||
|
|
2add802d0d | ||
|
|
adfcdb3b65 | ||
|
|
9fadf5f426 | ||
|
|
3e6306348a | ||
|
|
458dd51af7 | ||
|
|
23b0b8ba36 | ||
|
|
592fc81260 | ||
|
|
c232acbe9b | ||
|
|
640dfab827 | ||
|
|
01044ca8e9 | ||
|
|
2633d7dca3 | ||
|
|
ecb533c4d1 | ||
|
|
5fc19dab54 | ||
|
|
58114e7b24 | ||
|
|
1763824e5f | ||
|
|
aee2e12f39 | ||
|
|
500573067a | ||
|
|
8052103197 | ||
|
|
78ab11f991 | ||
|
|
3263f2023c | ||
|
|
8f00481c5f | ||
|
|
9872e371a2 | ||
|
|
37b3ac7952 | ||
|
|
d891bd7fac | ||
|
|
d366a91d9e | ||
|
|
8c3713b649 | ||
|
|
729fc738af | ||
|
|
add238c892 | ||
|
|
ad3634be7c | ||
|
|
5a53a4b044 | ||
|
|
5a97fa6336 | ||
|
|
657bb9d682 | ||
|
|
b9f5a1c85d | ||
|
|
592ce45da7 | ||
|
|
d9b3b95854 | ||
|
|
5426b93387 | ||
|
|
284a40aa63 | ||
|
|
2989e36b1d | ||
|
|
d8245cef72 | ||
|
|
edf4514d8b | ||
|
|
477d9fa87e | ||
|
|
01f133f8c8 | ||
|
|
d32147f8fe | ||
|
|
86aeacf393 | ||
|
|
dd80d3b9b9 | ||
|
|
98cba5ed30 | ||
|
|
ca8a5b45ba | ||
|
|
9d6a06aec5 | ||
|
|
dd016e6ced | ||
|
|
0fe6dcd742 | ||
|
|
bcbe6c4a53 | ||
|
|
f09c5f32cf | ||
|
|
3ca7e442bc | ||
|
|
8ac647ece7 | ||
|
|
4307d3b5c9 | ||
|
|
aa17110bde | ||
|
|
3f6d805033 | ||
|
|
64b44a360a | ||
|
|
040cc8d22f | ||
|
|
3be345e605 | ||
|
|
e1cea25781 | ||
|
|
a2ede7d6d5 | ||
|
|
7e4dd33ab0 | ||
|
|
45d5cff47d | ||
|
|
745e3bfb73 | ||
|
|
762906f240 | ||
|
|
9ec6f1e1d6 | ||
|
|
628ea42b63 | ||
|
|
1efff268b0 | ||
|
|
b206b0583b | ||
|
|
816b1b6bd5 | ||
|
|
24fcd67f8b | ||
|
|
c99c805743 | ||
|
|
f258ab5e98 | ||
|
|
880a85d2af | ||
|
|
efd31a429c | ||
|
|
728cc9f725 | ||
|
|
52305e3000 | ||
|
|
1657337887 | ||
|
|
362f377f61 | ||
|
|
8bdb61cb87 | ||
|
|
67f43d37df | ||
|
|
3543f86c63 | ||
|
|
627165dc7c | ||
|
|
874774fe6c | ||
|
|
ee76af7681 | ||
|
|
f9713eeb56 | ||
|
|
0ca7527f7a | ||
|
|
2a41da94d4 | ||
|
|
73586fd9b2 | ||
|
|
9c889b37fb | ||
|
|
b11ae4b54c | ||
|
|
e3af1dc864 | ||
|
|
8b04c1d4f6 | ||
|
|
74f9e34182 | ||
|
|
1fddc30350 | ||
|
|
e2bb4e2baa | ||
|
|
9006c9b747 | ||
|
|
6518582ed3 | ||
|
|
5c308a4f9a | ||
|
|
ac91030b31 | ||
|
|
9680edfcc3 | ||
|
|
56e991b7f4 | ||
|
|
9903049c7a | ||
|
|
f34ffc2062 | ||
|
|
f50d933a25 | ||
|
|
ce092bf23b | ||
|
|
fb06ad7330 | ||
|
|
80ab4eea8c | ||
|
|
83a13e22b7 | ||
|
|
ee0c64215d | ||
|
|
5ac8bd7f08 | ||
|
|
2f3fc12c08 | ||
|
|
331ad62f3c | ||
|
|
f6e93f084a | ||
|
|
7b2eacd4d8 | ||
|
|
a9bf906545 | ||
|
|
97090ff367 | ||
|
|
6c8b6de4c9 | ||
|
|
f91752cad2 | ||
|
|
cc60c328fe | ||
|
|
ff5b1b7ded | ||
|
|
a1ff7cab7e | ||
|
|
32f622ef80 | ||
|
|
969616ed09 | ||
|
|
ca62cde9aa | ||
|
|
3d1e3a9cde | ||
|
|
184848edf9 | ||
|
|
65ba5b3000 | ||
|
|
3a487bd33a | ||
|
|
56e92b702c | ||
|
|
45299fe4b3 | ||
|
|
434c2a1815 | ||
|
|
f31002636b | ||
|
|
b5dd0c8630 | ||
|
|
9c7b19e0b7 | ||
|
|
3a51a3f37e | ||
|
|
a5a5341643 | ||
|
|
20c6e9fca2 | ||
|
|
5b3eba7bee | ||
|
|
8c6e341a71 | ||
|
|
545ef3c234 | ||
|
|
7842c9fba8 | ||
|
|
7e43d7b131 | ||
|
|
089da459f7 | ||
|
|
b27af6b5c8 | ||
|
|
c69fb80222 | ||
|
|
134201794a | ||
|
|
2df767f596 | ||
|
|
da80e4dbce | ||
|
|
5cc8603cff | ||
|
|
426c245032 | ||
|
|
9731b74ad3 | ||
|
|
ee29526bbe | ||
|
|
3e884d347a | ||
|
|
6e0362dee8 | ||
|
|
2499675ad1 | ||
|
|
908b21f7fd | ||
|
|
e93a19ffb5 | ||
|
|
3e77c0b564 | ||
|
|
e769e750ec | ||
|
|
c31ee8ea33 | ||
|
|
894ae1fe0f | ||
|
|
5fae2f6d57 | ||
|
|
ad5edbb1de | ||
|
|
780b827adc | ||
|
|
8e010ef063 | ||
|
|
09f9764bbd | ||
|
|
a59c942cd4 | ||
|
|
b097bb20d9 | ||
|
|
2f9e96e324 | ||
|
|
56b8d1b927 | ||
|
|
5b446d4575 | ||
|
|
9ae5c979e8 | ||
|
|
ea7565889f | ||
|
|
4a111f7362 | ||
|
|
ffd171a26b | ||
|
|
95edd82638 | ||
|
|
e07c3aad6b | ||
|
|
a393a6b76c | ||
|
|
d380bf8179 | ||
|
|
8062d2be3b | ||
|
|
d17e37c581 | ||
|
|
6f432b8e45 | ||
|
|
94faa44697 | ||
|
|
9fde782403 | ||
|
|
ecc305dd59 | ||
|
|
6f6d5cb4cf | ||
|
|
8634abc021 | ||
|
|
a74e1f1600 | ||
|
|
59438ee8d4 | ||
|
|
4ed9927a30 | ||
|
|
ae508144cd | ||
|
|
a52a1b49af | ||
|
|
93295bf25b | ||
|
|
73639db910 | ||
|
|
c320288690 | ||
|
|
0aa1636d04 | ||
|
|
194d70f8a0 | ||
|
|
af35590549 | ||
|
|
a248b13cc3 | ||
|
|
b0dfc936a1 | ||
|
|
73090fa130 | ||
|
|
ade09bc709 | ||
|
|
8d9f391309 | ||
|
|
ebd74a4e5b | ||
|
|
c4f8f3613f | ||
|
|
e5a8ad54e2 | ||
|
|
815c616f18 | ||
|
|
1d8fcd66e6 | ||
|
|
6ca3b26820 | ||
|
|
18a2e6ecb2 | ||
|
|
922fffda1f | ||
|
|
1fc5844025 | ||
|
|
45b4bfc947 | ||
|
|
92f6d2c87c | ||
|
|
c2bda2c705 | ||
|
|
4b4b176fcf | ||
|
|
a27374fd8f | ||
|
|
56ac3424d2 | ||
|
|
696e2108ac | ||
|
|
89612f2605 | ||
|
|
a8216b9727 | ||
|
|
779260fb49 | ||
|
|
860b67e9c0 | ||
|
|
47ba357bea | ||
|
|
3502c01aa4 | ||
|
|
6e3e094c95 | ||
|
|
a89afb65d7 | ||
|
|
9a171db97f | ||
|
|
48567ef755 | ||
|
|
b57eba6eaf | ||
|
|
70d99eebc0 | ||
|
|
4783e4beee | ||
|
|
450c2470e9 | ||
|
|
09439334ca | ||
|
|
f9c88ea7bc | ||
|
|
33eedb97dc | ||
|
|
2984a86f37 | ||
|
|
5f5d75a0bb | ||
|
|
6f231e4c83 | ||
|
|
c750e4d7ef | ||
|
|
291a499124 | ||
|
|
67b36a0823 | ||
|
|
162c0497d1 | ||
|
|
416bd400bb | ||
|
|
ee60fa940c | ||
|
|
6210b24c64 | ||
|
|
68c6ad6036 | ||
|
|
10b9570429 | ||
|
|
11c8d9fcf1 | ||
|
|
61705047b0 | ||
|
|
ea4b6ff27b | ||
|
|
5ce395a60a | ||
|
|
ac79b8483f | ||
|
|
0135293127 | ||
|
|
adff287160 | ||
|
|
b651b36fff | ||
|
|
5a068410c6 | ||
|
|
ebb186c8df | ||
|
|
d582a73795 | ||
|
|
e845b63228 | ||
|
|
cfe0479dfb | ||
|
|
67e74d03ed | ||
|
|
8722318081 | ||
|
|
2d272fa51c | ||
|
|
15840d408b | ||
|
|
f9fc6c9c9d | ||
|
|
30c6b83a10 | ||
|
|
1754d027b3 | ||
|
|
a1f8595a6a | ||
|
|
5cce522ecd | ||
|
|
758ec720de | ||
|
|
6280031722 | ||
|
|
ff0343d2cc | ||
|
|
3d00d74fed | ||
|
|
30402033bc | ||
|
|
de445b32f5 | ||
|
|
34d3eb88b3 | ||
|
|
a9d91189b0 | ||
|
|
84f0d1ff1f | ||
|
|
98a8288da2 | ||
|
|
0e100cd451 | ||
|
|
e5a018f84c | ||
|
|
2b02ef0066 | ||
|
|
e5bc8fccb1 | ||
|
|
0c5bdbdcf3 | ||
|
|
a3191f1c8c | ||
|
|
787784e937 | ||
|
|
539ff03a7e | ||
|
|
e42a3e0084 | ||
|
|
8fbd4cea5b | ||
|
|
05f24ede96 | ||
|
|
54b323e557 | ||
|
|
9231706227 | ||
|
|
1f88b1ef84 | ||
|
|
10242235bc | ||
|
|
73b65ac82e | ||
|
|
f52916a2c3 | ||
|
|
98d6cdd53c | ||
|
|
e1a87a802d | ||
|
|
d9e284366d | ||
|
|
56e9a46c17 | ||
|
|
2ce6bbf291 | ||
|
|
568d5bfbe8 | ||
|
|
60435daba3 | ||
|
|
f14d1eb871 | ||
|
|
d4c0dbfacc | ||
|
|
469ae2c7f1 | ||
|
|
a9372c42cd | ||
|
|
edf67444ea | ||
|
|
4867ca353c | ||
|
|
4015723591 | ||
|
|
f70d779034 | ||
|
|
23beb46d15 | ||
|
|
e019d43d0b | ||
|
|
c37e374fdd | ||
|
|
cd1e016163 | ||
|
|
1f4b381748 | ||
|
|
fb9a80923b | ||
|
|
ed1c198897 | ||
|
|
1b808e1d7c | ||
|
|
639f427d6d | ||
|
|
539f0251d9 | ||
|
|
d91d9b02d4 | ||
|
|
0104897d69 | ||
|
|
5530a5b303 | ||
|
|
a9fceeb00f | ||
|
|
34e3538b55 | ||
|
|
851a234988 | ||
|
|
17893eff59 | ||
|
|
54e8ce1ac5 | ||
|
|
38e5e4a893 | ||
|
|
65cc804186 | ||
|
|
8fa677b8e8 | ||
|
|
6bd56d2d5f | ||
|
|
204face50d | ||
|
|
c5f5aa8208 | ||
|
|
3d9938221a | ||
|
|
f0a1f4ac7c | ||
|
|
ec1a7869f8 | ||
|
|
7e67d42d1d | ||
|
|
da9a7ff63d | ||
|
|
47f913abcb | ||
|
|
fc4199504f | ||
|
|
0fc14b72ca | ||
|
|
804ed0d26a | ||
|
|
028b3e2fbf | ||
|
|
e0a03789ae | ||
|
|
7a3e4a8a05 | ||
|
|
e16cc38b70 | ||
|
|
c43b30cf36 | ||
|
|
07a8024b45 | ||
|
|
b64147cce9 | ||
|
|
058bb95171 | ||
|
|
daf2ec063c | ||
|
|
77af247450 | ||
|
|
1fd888175f | ||
|
|
fb0ec74d08 | ||
|
|
eb73017798 | ||
|
|
139a68fd0f | ||
|
|
d758fc1b89 | ||
|
|
056b74b162 | ||
|
|
e752f8f0ef | ||
|
|
54a32adef3 | ||
|
|
ca69845238 | ||
|
|
d2fdda8bcd | ||
|
|
afc64ed9ee | ||
|
|
500435b856 | ||
|
|
a15484fe3d | ||
|
|
76c61c1b04 | ||
|
|
de58c67991 | ||
|
|
8a34a4ba68 | ||
|
|
208d5042ee | ||
|
|
ece7049390 | ||
|
|
e72afd0bd6 | ||
|
|
83db7c6a65 | ||
|
|
62384e9321 | ||
|
|
1081df3d7e | ||
|
|
3a8736374c | ||
|
|
f276fbba4f | ||
|
|
fd78f868e1 | ||
|
|
d99a56bc27 | ||
|
|
48c66b68ab | ||
|
|
8d9b90f3f5 | ||
|
|
4a86375e89 | ||
|
|
b60c57a97d | ||
|
|
8903c1bc6f | ||
|
|
e33fd450fd | ||
|
|
dc5d2c7406 | ||
|
|
1612d7ba3f | ||
|
|
681782121c | ||
|
|
35cf944cb7 | ||
|
|
4bbad7f448 | ||
|
|
74b6bfb9eb | ||
|
|
46ea868559 | ||
|
|
3290df5593 | ||
|
|
8e3b9ec879 | ||
|
|
787fc8737f | ||
|
|
fcfcf6957e | ||
|
|
8ffa2bfe25 | ||
|
|
d69b0d76dd | ||
|
|
2bcff4c7f2 | ||
|
|
d436a40739 | ||
|
|
5deba1b6f9 | ||
|
|
886102d462 | ||
|
|
7be578485e | ||
|
|
7c1b990c55 | ||
|
|
74a0d6408a | ||
|
|
92cbe580e6 | ||
|
|
d92eb0c603 | ||
|
|
11d956fa18 | ||
|
|
8e523961dc | ||
|
|
cdbe1c87d4 | ||
|
|
3f42128fff | ||
|
|
4dff2c7a0d | ||
|
|
547993f801 | ||
|
|
946228d783 | ||
|
|
23df4205f8 | ||
|
|
89f484282a | ||
|
|
9ef26e1df0 | ||
|
|
025acc0e48 | ||
|
|
6942ab1012 | ||
|
|
3bfb7b79f2 | ||
|
|
24788ddcc0 | ||
|
|
adf313a6d3 | ||
|
|
7f39318340 | ||
|
|
a432290a82 | ||
|
|
2b64e1ca8b | ||
|
|
514d5434a3 | ||
|
|
7e600a6494 | ||
|
|
17ebc1ea80 | ||
|
|
b099590b2c | ||
|
|
5323bb7bee | ||
|
|
55dbcee36a | ||
|
|
ec9434aae3 | ||
|
|
d1ec0a6093 | ||
|
|
5136fe196b | ||
|
|
4aa841786f | ||
|
|
98cc7434d2 | ||
|
|
35020a9423 | ||
|
|
1cc1c9aa38 | ||
|
|
9d392970f0 | ||
|
|
b0d440c34b | ||
|
|
21c3d9c371 | ||
|
|
dae66eab36 | ||
|
|
9474908449 | ||
|
|
eec4057e8d | ||
|
|
08bc77fb95 | ||
|
|
fff97b1cd2 | ||
|
|
2a7d1c4c8d | ||
|
|
0b674b608b | ||
|
|
14b009b093 | ||
|
|
530922848f | ||
|
|
79d51a0a0b | ||
|
|
85488cd0dc | ||
|
|
ce7ac29d06 | ||
|
|
4f363f5bf3 | ||
|
|
ff7108a3b1 | ||
|
|
77cc91d06b | ||
|
|
45395027d3 | ||
|
|
6d5ccde864 | ||
|
|
961d2d9926 | ||
|
|
75441017c6 | ||
|
|
ed2457bddf | ||
|
|
4471ad581e | ||
|
|
7ecc0d5a04 | ||
|
|
78fe567419 | ||
|
|
5ae9c2f62b | ||
|
|
705dadae8e | ||
|
|
40cdde8820 | ||
|
|
64497c9228 | ||
|
|
188175be84 | ||
|
|
2b4fa98941 | ||
|
|
36a996d704 | ||
|
|
7f1d916f04 | ||
|
|
e15546b42f | ||
|
|
8c61fe2ca5 | ||
|
|
7f865492d0 | ||
|
|
726fba61f3 | ||
|
|
0b85a525fb | ||
|
|
35d92abe73 | ||
|
|
28445058ef | ||
|
|
aab5737ff3 | ||
|
|
115f024260 | ||
|
|
952e8cf60c | ||
|
|
fcfdb9b566 | ||
|
|
ff1dc72d74 | ||
|
|
b3486b43c4 | ||
|
|
f705bf2efe | ||
|
|
6e1565c32c | ||
|
|
4ea43ebc5d | ||
|
|
f292a0cc4c | ||
|
|
4c5a83d6cf | ||
|
|
82774f89b1 | ||
|
|
19b220f39c | ||
|
|
725a7f90e9 |
@@ -9,13 +9,6 @@ trim_trailing_whitespace = true
|
||||
charset = utf-8
|
||||
|
||||
# python, js indentation settings
|
||||
[{*.py,*.js,*.vue,*.css,*.scss,*.html}]
|
||||
[{*.py,*.js}]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
max_line_length = 110
|
||||
|
||||
# JSON files - mostly doctype schema files
|
||||
[{*.json}]
|
||||
insert_final_newline = false
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
@@ -124,7 +124,6 @@
|
||||
"beforeEach": true,
|
||||
"onScan": true,
|
||||
"extend_cscript": true,
|
||||
"localforage": true,
|
||||
"Plaid": true
|
||||
"localforage": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,5 +30,5 @@ b147b85e6ac19a9220cd1e2958a6ebd99373283a
|
||||
# bulk format python code with black
|
||||
baec607ff5905b1c67531096a9cf50ec7ff00a5d
|
||||
|
||||
# ruff
|
||||
960ef14b7a68cfec9e309ec12845f521cb6a721c
|
||||
# bulk refactor with sourcery
|
||||
eb9ee3f79b94e594fc6dfa4f6514580e125eee8c
|
||||
|
||||
60
.github/helper/translation.py
vendored
Normal file
60
.github/helper/translation.py
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
import re
|
||||
import sys
|
||||
|
||||
errors_encounter = 0
|
||||
pattern = re.compile(r"_\(([\"']{,3})(?P<message>((?!\1).)*)\1(\s*,\s*context\s*=\s*([\"'])(?P<py_context>((?!\5).)*)\5)*(\s*,(\s*?.*?\n*?)*(,\s*([\"'])(?P<js_context>((?!\11).)*)\11)*)*\)")
|
||||
words_pattern = re.compile(r"_{1,2}\([\"'`]{1,3}.*?[a-zA-Z]")
|
||||
start_pattern = re.compile(r"_{1,2}\([f\"'`]{1,3}")
|
||||
f_string_pattern = re.compile(r"_\(f[\"']")
|
||||
starts_with_f_pattern = re.compile(r"_\(f")
|
||||
|
||||
# skip first argument
|
||||
files = sys.argv[1:]
|
||||
files_to_scan = [_file for _file in files if _file.endswith(('.py', '.js'))]
|
||||
|
||||
for _file in files_to_scan:
|
||||
with open(_file, 'r') as f:
|
||||
print(f'Checking: {_file}')
|
||||
file_lines = f.readlines()
|
||||
for line_number, line in enumerate(file_lines, 1):
|
||||
if 'frappe-lint: disable-translate' in line:
|
||||
continue
|
||||
|
||||
start_matches = start_pattern.search(line)
|
||||
if start_matches:
|
||||
starts_with_f = starts_with_f_pattern.search(line)
|
||||
|
||||
if starts_with_f:
|
||||
has_f_string = f_string_pattern.search(line)
|
||||
if has_f_string:
|
||||
errors_encounter += 1
|
||||
print(f'\nF-strings are not supported for translations at line number {line_number}\n{line.strip()[:100]}')
|
||||
continue
|
||||
else:
|
||||
continue
|
||||
|
||||
match = pattern.search(line)
|
||||
error_found = False
|
||||
|
||||
if not match and line.endswith((',\n', '[\n')):
|
||||
# concat remaining text to validate multiline pattern
|
||||
line = "".join(file_lines[line_number - 1:])
|
||||
line = line[start_matches.start() + 1:]
|
||||
match = pattern.match(line)
|
||||
|
||||
if not match:
|
||||
error_found = True
|
||||
print(f'\nTranslation syntax error at line number {line_number}\n{line.strip()[:100]}')
|
||||
|
||||
if not error_found and not words_pattern.search(line):
|
||||
error_found = True
|
||||
print(f'\nTranslation is useless because it has no words at line number {line_number}\n{line.strip()[:100]}')
|
||||
|
||||
if error_found:
|
||||
errors_encounter += 1
|
||||
|
||||
if errors_encounter > 0:
|
||||
print('\nVisit "https://frappeframework.com/docs/user/en/translations" to learn about valid translation strings.')
|
||||
sys.exit(1)
|
||||
else:
|
||||
print('\nGood To Go!')
|
||||
26
.github/workflows/backport.yml
vendored
Normal file
26
.github/workflows/backport.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Backport
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- closed
|
||||
- labeled
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: Checkout Actions
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: "frappe/backport"
|
||||
path: ./actions
|
||||
ref: develop
|
||||
- name: Install Actions
|
||||
run: npm install --production --prefix ./actions
|
||||
- name: Run backport
|
||||
uses: ./actions/backport
|
||||
with:
|
||||
token: ${{secrets.RELEASE_TOKEN}}
|
||||
labelsToAdd: "backport"
|
||||
title: "{{originalTitle}}"
|
||||
32
.github/workflows/initiate_release.yml
vendored
Normal file
32
.github/workflows/initiate_release.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# This workflow is agnostic to branches. Only maintain on develop branch.
|
||||
# To add/remove versions just modify the matrix.
|
||||
|
||||
name: Create weekly release pull requests
|
||||
on:
|
||||
schedule:
|
||||
# 9:30 UTC => 3 PM IST Tuesday
|
||||
- cron: "30 9 * * 2"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
stable-release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version: ["14", "15"]
|
||||
|
||||
steps:
|
||||
- uses: octokit/request-action@v2.x
|
||||
with:
|
||||
route: POST /repos/{owner}/{repo}/pulls
|
||||
owner: frappe
|
||||
repo: erpnext
|
||||
title: |-
|
||||
"chore: release v${{ matrix.version }}"
|
||||
body: "Automated weekly release."
|
||||
base: version-${{ matrix.version }}
|
||||
head: version-${{ matrix.version }}-hotfix
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
21
.github/workflows/lock.yml
vendored
Normal file
21
.github/workflows/lock.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
name: 'Lock threads'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
|
||||
jobs:
|
||||
lock:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v5
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
issue-inactive-days: 14
|
||||
pr-inactive-days: 14
|
||||
1
.github/workflows/patch.yml
vendored
1
.github/workflows/patch.yml
vendored
@@ -134,6 +134,7 @@ jobs:
|
||||
}
|
||||
|
||||
update_to_version 14
|
||||
update_to_version 15
|
||||
|
||||
echo "Updating to latest version"
|
||||
git -C "apps/frappe" checkout -q -f "${GITHUB_BASE_REF:-${GITHUB_REF##*/}}"
|
||||
|
||||
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@@ -2,23 +2,21 @@ name: Generate Semantic Release
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- version-15
|
||||
- version-13
|
||||
jobs:
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Entire Repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
node-version: 18
|
||||
- name: Setup dependencies
|
||||
run: |
|
||||
npm install @semantic-release/git @semantic-release/exec --no-save
|
||||
|
||||
26
.github/workflows/server-tests-mariadb.yml
vendored
26
.github/workflows/server-tests-mariadb.yml
vendored
@@ -117,7 +117,7 @@ jobs:
|
||||
FRAPPE_BRANCH: ${{ github.event.inputs.branch }}
|
||||
|
||||
- name: Run Tests
|
||||
run: 'cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --app erpnext --total-builds 4 --build-number ${{ matrix.container }}'
|
||||
run: 'cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --app erpnext --with-coverage --total-builds 4 --build-number ${{ matrix.container }}'
|
||||
env:
|
||||
TYPE: server
|
||||
CI_BUILD_ID: ${{ github.run_id }}
|
||||
@@ -126,3 +126,27 @@ jobs:
|
||||
- name: Show bench output
|
||||
if: ${{ always() }}
|
||||
run: cat ~/frappe-bench/bench_start.log || true
|
||||
|
||||
- name: Upload coverage data
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: coverage-${{ matrix.container }}
|
||||
path: /home/runner/frappe-bench/sites/coverage.xml
|
||||
|
||||
coverage:
|
||||
name: Coverage Wrap Up
|
||||
needs: test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
|
||||
- name: Upload coverage data
|
||||
uses: codecov/codecov-action@v2
|
||||
with:
|
||||
name: MariaDB
|
||||
fail_ci_if_error: true
|
||||
verbose: true
|
||||
|
||||
46
.mergify.yml
46
.mergify.yml
@@ -17,6 +17,7 @@ pull_request_rules:
|
||||
- base=version-12
|
||||
- base=version-14
|
||||
- base=version-15
|
||||
- base=version-16
|
||||
actions:
|
||||
close:
|
||||
comment:
|
||||
@@ -24,16 +25,6 @@ pull_request_rules:
|
||||
@{{author}}, thanks for the contribution, but we do not accept pull requests on a stable branch. Please raise PR on an appropriate hotfix branch.
|
||||
https://github.com/frappe/erpnext/wiki/Pull-Request-Checklist#which-branch
|
||||
|
||||
- name: Auto-close PRs on pre-release branch
|
||||
conditions:
|
||||
- base=version-13-pre-release
|
||||
actions:
|
||||
close:
|
||||
comment:
|
||||
message: |
|
||||
@{{author}}, pre-release branch is not maintained anymore. Releases are directly done by merging hotfix branch to stable branches.
|
||||
|
||||
|
||||
- name: backport to develop
|
||||
conditions:
|
||||
- label="backport develop"
|
||||
@@ -54,13 +45,13 @@ pull_request_rules:
|
||||
assignees:
|
||||
- "{{ author }}"
|
||||
|
||||
- name: backport to version-14-pre-release
|
||||
- name: backport to version-15-hotfix
|
||||
conditions:
|
||||
- label="backport version-14-pre-release"
|
||||
- label="backport version-15-hotfix"
|
||||
actions:
|
||||
backport:
|
||||
branches:
|
||||
- version-14-pre-release
|
||||
- version-15-hotfix
|
||||
assignees:
|
||||
- "{{ author }}"
|
||||
|
||||
@@ -74,35 +65,6 @@ pull_request_rules:
|
||||
assignees:
|
||||
- "{{ author }}"
|
||||
|
||||
- name: backport to version-13-pre-release
|
||||
conditions:
|
||||
- label="backport version-13-pre-release"
|
||||
actions:
|
||||
backport:
|
||||
branches:
|
||||
- version-13-pre-release
|
||||
assignees:
|
||||
- "{{ author }}"
|
||||
|
||||
- name: backport to version-12-hotfix
|
||||
conditions:
|
||||
- label="backport version-12-hotfix"
|
||||
actions:
|
||||
backport:
|
||||
branches:
|
||||
- version-12-hotfix
|
||||
assignees:
|
||||
- "{{ author }}"
|
||||
|
||||
- name: backport to version-12-pre-release
|
||||
conditions:
|
||||
- label="backport version-12-pre-release"
|
||||
actions:
|
||||
backport:
|
||||
branches:
|
||||
- version-12-pre-release
|
||||
assignees:
|
||||
- "{{ author }}"
|
||||
|
||||
- name: Automatic merge on CI success and review
|
||||
conditions:
|
||||
|
||||
@@ -20,23 +20,6 @@ repos:
|
||||
- id: check-yaml
|
||||
- id: debug-statements
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: v2.7.1
|
||||
hooks:
|
||||
- id: prettier
|
||||
types_or: [javascript, vue, scss]
|
||||
# Ignore any files that might contain jinja / bundles
|
||||
exclude: |
|
||||
(?x)^(
|
||||
erpnext/public/dist/.*|
|
||||
cypress/.*|
|
||||
.*node_modules.*|
|
||||
.*boilerplate.*|
|
||||
erpnext/public/js/controllers/.*|
|
||||
erpnext/templates/pages/order.js|
|
||||
erpnext/templates/includes/.*
|
||||
)$
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-eslint
|
||||
rev: v8.44.0
|
||||
hooks:
|
||||
@@ -55,15 +38,28 @@ repos:
|
||||
erpnext/templates/includes/.*
|
||||
)$
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.2.0
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 6.0.0
|
||||
hooks:
|
||||
- id: ruff
|
||||
name: "Run ruff linter and apply fixes"
|
||||
args: ["--fix"]
|
||||
- id: flake8
|
||||
additional_dependencies: [
|
||||
'flake8-bugbear',
|
||||
'flake8-tuple',
|
||||
]
|
||||
args: ['--config', '.github/helper/.flake8_strict']
|
||||
exclude: ".*setup.py$"
|
||||
|
||||
- id: ruff-format
|
||||
name: "Format Python code"
|
||||
- repo: https://github.com/adityahase/black
|
||||
rev: 9cb0a69f4d0030cdf687eddf314468b39ed54119
|
||||
hooks:
|
||||
- id: black
|
||||
additional_dependencies: ['click==8.0.4']
|
||||
|
||||
- repo: https://github.com/PyCQA/isort
|
||||
rev: 5.12.0
|
||||
hooks:
|
||||
- id: isort
|
||||
exclude: ".*setup.py$"
|
||||
|
||||
|
||||
ci:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"branches": ["version-15"],
|
||||
"branches": ["version-13"],
|
||||
"plugins": [
|
||||
"@semantic-release/commit-analyzer", {
|
||||
"preset": "angular",
|
||||
@@ -21,4 +21,4 @@
|
||||
],
|
||||
"@semantic-release/github"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -73,8 +73,6 @@ New passwords will be created for the ERPNext "Administrator" user, the MariaDB
|
||||
1. [Issue Guidelines](https://github.com/frappe/erpnext/wiki/Issue-Guidelines)
|
||||
1. [Report Security Vulnerabilities](https://erpnext.com/security)
|
||||
1. [Pull Request Requirements](https://github.com/frappe/erpnext/wiki/Contribution-Guidelines)
|
||||
1. [Translations](https://translate.erpnext.com)
|
||||
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -1,13 +1,25 @@
|
||||
module.exports = {
|
||||
parserPreset: "conventional-changelog-conventionalcommits",
|
||||
parserPreset: 'conventional-changelog-conventionalcommits',
|
||||
rules: {
|
||||
"subject-empty": [2, "never"],
|
||||
"type-case": [2, "always", "lower-case"],
|
||||
"type-empty": [2, "never"],
|
||||
"type-enum": [
|
||||
'subject-empty': [2, 'never'],
|
||||
'type-case': [2, 'always', 'lower-case'],
|
||||
'type-empty': [2, 'never'],
|
||||
'type-enum': [
|
||||
2,
|
||||
"always",
|
||||
["build", "chore", "ci", "docs", "feat", "fix", "perf", "refactor", "revert", "style", "test"],
|
||||
'always',
|
||||
[
|
||||
'build',
|
||||
'chore',
|
||||
'ci',
|
||||
'docs',
|
||||
'feat',
|
||||
'fix',
|
||||
'perf',
|
||||
'refactor',
|
||||
'revert',
|
||||
'style',
|
||||
'test',
|
||||
],
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import inspect
|
||||
|
||||
import frappe
|
||||
|
||||
__version__ = "15.21.2"
|
||||
__version__ = "16.0.0-dev"
|
||||
|
||||
|
||||
def get_default_company(user=None):
|
||||
@@ -13,7 +13,7 @@ def get_default_company(user=None):
|
||||
if not user:
|
||||
user = frappe.session.user
|
||||
|
||||
companies = get_user_default_as_list("company", user)
|
||||
companies = get_user_default_as_list(user, "company")
|
||||
if companies:
|
||||
default_company = companies[0]
|
||||
else:
|
||||
@@ -37,7 +37,9 @@ def get_default_cost_center(company):
|
||||
if not frappe.flags.company_cost_center:
|
||||
frappe.flags.company_cost_center = {}
|
||||
if company not in frappe.flags.company_cost_center:
|
||||
frappe.flags.company_cost_center[company] = frappe.get_cached_value("Company", company, "cost_center")
|
||||
frappe.flags.company_cost_center[company] = frappe.get_cached_value(
|
||||
"Company", company, "cost_center"
|
||||
)
|
||||
return frappe.flags.company_cost_center[company]
|
||||
|
||||
|
||||
|
||||
@@ -11,14 +11,14 @@ class ERPNextAddress(Address):
|
||||
def validate(self):
|
||||
self.validate_reference()
|
||||
self.update_compnay_address()
|
||||
super().validate()
|
||||
super(ERPNextAddress, self).validate()
|
||||
|
||||
def link_address(self):
|
||||
"""Link address based on owner"""
|
||||
if self.is_your_company_address:
|
||||
return
|
||||
|
||||
return super().link_address()
|
||||
return super(ERPNextAddress, self).link_address()
|
||||
|
||||
def update_compnay_address(self):
|
||||
for link in self.get("links"):
|
||||
@@ -26,11 +26,11 @@ class ERPNextAddress(Address):
|
||||
self.is_your_company_address = 1
|
||||
|
||||
def validate_reference(self):
|
||||
if self.is_your_company_address and not [row for row in self.links if row.link_doctype == "Company"]:
|
||||
if self.is_your_company_address and not [
|
||||
row for row in self.links if row.link_doctype == "Company"
|
||||
]:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Address needs to be linked to a Company. Please add a row for Company in the Links table."
|
||||
),
|
||||
_("Address needs to be linked to a Company. Please add a row for Company in the Links table."),
|
||||
title=_("Company Not Linked"),
|
||||
)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
frappe.provide("frappe.dashboards.chart_sources");
|
||||
frappe.provide('frappe.dashboards.chart_sources');
|
||||
|
||||
frappe.dashboards.chart_sources["Account Balance Timeline"] = {
|
||||
method: "erpnext.accounts.dashboard_chart_source.account_balance_timeline.account_balance_timeline.get",
|
||||
@@ -9,14 +9,14 @@ frappe.dashboards.chart_sources["Account Balance Timeline"] = {
|
||||
fieldtype: "Link",
|
||||
options: "Company",
|
||||
default: frappe.defaults.get_user_default("Company"),
|
||||
reqd: 1,
|
||||
reqd: 1
|
||||
},
|
||||
{
|
||||
fieldname: "account",
|
||||
label: __("Account"),
|
||||
fieldtype: "Link",
|
||||
options: "Account",
|
||||
reqd: 1,
|
||||
reqd: 1
|
||||
},
|
||||
],
|
||||
]
|
||||
};
|
||||
|
||||
@@ -37,7 +37,7 @@ def get(
|
||||
filters = frappe.parse_json(filters) or frappe.parse_json(chart.filters_json)
|
||||
|
||||
account = filters.get("account")
|
||||
filters.get("company")
|
||||
company = filters.get("company")
|
||||
|
||||
if not account and chart_name:
|
||||
frappe.throw(
|
||||
@@ -83,6 +83,7 @@ def build_result(account, dates, gl_entries):
|
||||
|
||||
# get balances in debit
|
||||
for entry in gl_entries:
|
||||
|
||||
# entry date is after the current pointer, so move the pointer forward
|
||||
while getdate(entry.posting_date) > result[date_index][0]:
|
||||
date_index += 1
|
||||
@@ -132,6 +133,8 @@ def get_dates_from_timegrain(from_date, to_date, timegrain):
|
||||
|
||||
dates = [get_period_ending(from_date, timegrain)]
|
||||
while getdate(dates[-1]) < getdate(to_date):
|
||||
date = get_period_ending(add_to_date(dates[-1], years=years, months=months, days=days), timegrain)
|
||||
date = get_period_ending(
|
||||
add_to_date(dates[-1], years=years, months=months, days=days), timegrain
|
||||
)
|
||||
dates.append(date)
|
||||
return dates
|
||||
|
||||
@@ -24,10 +24,14 @@ from erpnext.accounts.utils import get_account_currency
|
||||
def validate_service_stop_date(doc):
|
||||
"""Validates service_stop_date for Purchase Invoice and Sales Invoice"""
|
||||
|
||||
enable_check = "enable_deferred_revenue" if doc.doctype == "Sales Invoice" else "enable_deferred_expense"
|
||||
enable_check = (
|
||||
"enable_deferred_revenue" if doc.doctype == "Sales Invoice" else "enable_deferred_expense"
|
||||
)
|
||||
|
||||
old_stop_dates = {}
|
||||
old_doc = frappe.db.get_all(f"{doc.doctype} Item", {"parent": doc.name}, ["name", "service_stop_date"])
|
||||
old_doc = frappe.db.get_all(
|
||||
"{0} Item".format(doc.doctype), {"parent": doc.name}, ["name", "service_stop_date"]
|
||||
)
|
||||
|
||||
for d in old_doc:
|
||||
old_stop_dates[d.name] = d.service_stop_date or ""
|
||||
@@ -58,14 +62,16 @@ def build_conditions(process_type, account, company):
|
||||
)
|
||||
|
||||
if account:
|
||||
conditions += f"AND {deferred_account}='{account}'"
|
||||
conditions += "AND %s='%s'" % (deferred_account, account)
|
||||
elif company:
|
||||
conditions += f"AND p.company = {frappe.db.escape(company)}"
|
||||
|
||||
return conditions
|
||||
|
||||
|
||||
def convert_deferred_expense_to_expense(deferred_process, start_date=None, end_date=None, conditions=""):
|
||||
def convert_deferred_expense_to_expense(
|
||||
deferred_process, start_date=None, end_date=None, conditions=""
|
||||
):
|
||||
# book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM
|
||||
|
||||
if not start_date:
|
||||
@@ -75,14 +81,16 @@ def convert_deferred_expense_to_expense(deferred_process, start_date=None, end_d
|
||||
|
||||
# check for the purchase invoice for which GL entries has to be done
|
||||
invoices = frappe.db.sql_list(
|
||||
f"""
|
||||
"""
|
||||
select distinct item.parent
|
||||
from `tabPurchase Invoice Item` item, `tabPurchase Invoice` p
|
||||
where item.service_start_date<=%s and item.service_end_date>=%s
|
||||
and item.enable_deferred_expense = 1 and item.parent=p.name
|
||||
and item.docstatus = 1 and ifnull(item.amount, 0) > 0
|
||||
{conditions}
|
||||
""",
|
||||
{0}
|
||||
""".format(
|
||||
conditions
|
||||
),
|
||||
(end_date, start_date),
|
||||
) # nosec
|
||||
|
||||
@@ -95,7 +103,9 @@ def convert_deferred_expense_to_expense(deferred_process, start_date=None, end_d
|
||||
send_mail(deferred_process)
|
||||
|
||||
|
||||
def convert_deferred_revenue_to_income(deferred_process, start_date=None, end_date=None, conditions=""):
|
||||
def convert_deferred_revenue_to_income(
|
||||
deferred_process, start_date=None, end_date=None, conditions=""
|
||||
):
|
||||
# book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM
|
||||
|
||||
if not start_date:
|
||||
@@ -105,14 +115,16 @@ def convert_deferred_revenue_to_income(deferred_process, start_date=None, end_da
|
||||
|
||||
# check for the sales invoice for which GL entries has to be done
|
||||
invoices = frappe.db.sql_list(
|
||||
f"""
|
||||
"""
|
||||
select distinct item.parent
|
||||
from `tabSales Invoice Item` item, `tabSales Invoice` p
|
||||
where item.service_start_date<=%s and item.service_end_date>=%s
|
||||
and item.enable_deferred_revenue = 1 and item.parent=p.name
|
||||
and item.docstatus = 1 and ifnull(item.amount, 0) > 0
|
||||
{conditions}
|
||||
""",
|
||||
{0}
|
||||
""".format(
|
||||
conditions
|
||||
),
|
||||
(end_date, start_date),
|
||||
) # nosec
|
||||
|
||||
@@ -220,7 +232,7 @@ def calculate_monthly_amount(
|
||||
if amount + already_booked_amount_in_account_currency > item.net_amount:
|
||||
amount = item.net_amount - already_booked_amount_in_account_currency
|
||||
|
||||
if not (get_first_day(start_date) == start_date and get_last_day(end_date) == end_date):
|
||||
if get_first_day(start_date) != start_date or get_last_day(end_date) != end_date:
|
||||
partial_month = flt(date_diff(end_date, start_date)) / flt(
|
||||
date_diff(get_last_day(end_date), get_first_day(start_date))
|
||||
)
|
||||
@@ -231,7 +243,9 @@ def calculate_monthly_amount(
|
||||
already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(
|
||||
doc, item
|
||||
)
|
||||
base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount"))
|
||||
base_amount = flt(
|
||||
item.base_net_amount - already_booked_amount, item.precision("base_net_amount")
|
||||
)
|
||||
if account_currency == doc.company_currency:
|
||||
amount = base_amount
|
||||
else:
|
||||
@@ -251,13 +265,17 @@ def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, a
|
||||
if account_currency == doc.company_currency:
|
||||
amount = base_amount
|
||||
else:
|
||||
amount = flt(item.net_amount * total_booking_days / flt(total_days), item.precision("net_amount"))
|
||||
amount = flt(
|
||||
item.net_amount * total_booking_days / flt(total_days), item.precision("net_amount")
|
||||
)
|
||||
else:
|
||||
already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(
|
||||
doc, item
|
||||
)
|
||||
|
||||
base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount"))
|
||||
base_amount = flt(
|
||||
item.base_net_amount - already_booked_amount, item.precision("base_net_amount")
|
||||
)
|
||||
if account_currency == doc.company_currency:
|
||||
amount = base_amount
|
||||
else:
|
||||
@@ -278,22 +296,26 @@ def get_already_booked_amount(doc, item):
|
||||
|
||||
gl_entries_details = frappe.db.sql(
|
||||
"""
|
||||
select sum({}) as total_credit, sum({}) as total_credit_in_account_currency, voucher_detail_no
|
||||
select sum({0}) as total_credit, sum({1}) as total_credit_in_account_currency, voucher_detail_no
|
||||
from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
|
||||
and is_cancelled = 0
|
||||
group by voucher_detail_no
|
||||
""".format(total_credit_debit, total_credit_debit_currency),
|
||||
""".format(
|
||||
total_credit_debit, total_credit_debit_currency
|
||||
),
|
||||
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
|
||||
as_dict=True,
|
||||
)
|
||||
|
||||
journal_entry_details = frappe.db.sql(
|
||||
"""
|
||||
SELECT sum(c.{}) as total_credit, sum(c.{}) as total_credit_in_account_currency, reference_detail_no
|
||||
SELECT sum(c.{0}) as total_credit, sum(c.{1}) as total_credit_in_account_currency, reference_detail_no
|
||||
FROM `tabJournal Entry` p , `tabJournal Entry Account` c WHERE p.name = c.parent and
|
||||
p.company = %s and c.account=%s and c.reference_type=%s and c.reference_name=%s and c.reference_detail_no=%s
|
||||
and p.docstatus < 2 group by reference_detail_no
|
||||
""".format(total_credit_debit, total_credit_debit_currency),
|
||||
""".format(
|
||||
total_credit_debit, total_credit_debit_currency
|
||||
),
|
||||
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
|
||||
as_dict=True,
|
||||
)
|
||||
@@ -315,7 +337,9 @@ def get_already_booked_amount(doc, item):
|
||||
|
||||
|
||||
def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
|
||||
enable_check = "enable_deferred_revenue" if doc.doctype == "Sales Invoice" else "enable_deferred_expense"
|
||||
enable_check = (
|
||||
"enable_deferred_revenue" if doc.doctype == "Sales Invoice" else "enable_deferred_expense"
|
||||
)
|
||||
|
||||
accounts_frozen_upto = frappe.db.get_single_value("Accounts Settings", "acc_frozen_upto")
|
||||
|
||||
@@ -334,9 +358,11 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
|
||||
|
||||
account_currency = get_account_currency(item.expense_account or item.income_account)
|
||||
if doc.doctype == "Sales Invoice":
|
||||
against_type = "Customer"
|
||||
against, project = doc.customer, doc.project
|
||||
credit_account, debit_account = item.income_account, item.deferred_revenue_account
|
||||
else:
|
||||
against_type = "Supplier"
|
||||
against, project = doc.supplier, item.project
|
||||
credit_account, debit_account = item.deferred_expense_account, item.expense_account
|
||||
|
||||
@@ -389,6 +415,7 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
|
||||
doc,
|
||||
credit_account,
|
||||
debit_account,
|
||||
against_type,
|
||||
against,
|
||||
amount,
|
||||
base_amount,
|
||||
@@ -416,7 +443,9 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
|
||||
via_journal_entry = cint(
|
||||
frappe.db.get_singles_value("Accounts Settings", "book_deferred_entries_via_journal_entry")
|
||||
)
|
||||
submit_journal_entry = cint(frappe.db.get_singles_value("Accounts Settings", "submit_journal_entries"))
|
||||
submit_journal_entry = cint(
|
||||
frappe.db.get_singles_value("Accounts Settings", "submit_journal_entries")
|
||||
)
|
||||
book_deferred_entries_based_on = frappe.db.get_singles_value(
|
||||
"Accounts Settings", "book_deferred_entries_based_on"
|
||||
)
|
||||
@@ -436,7 +465,9 @@ def process_deferred_accounting(posting_date=None):
|
||||
posting_date = today()
|
||||
|
||||
if not cint(
|
||||
frappe.db.get_singles_value("Accounts Settings", "automatically_process_deferred_accounting_entry")
|
||||
frappe.db.get_singles_value(
|
||||
"Accounts Settings", "automatically_process_deferred_accounting_entry"
|
||||
)
|
||||
):
|
||||
return
|
||||
|
||||
@@ -466,6 +497,7 @@ def make_gl_entries(
|
||||
doc,
|
||||
credit_account,
|
||||
debit_account,
|
||||
against_type,
|
||||
against,
|
||||
amount,
|
||||
base_amount,
|
||||
@@ -487,7 +519,9 @@ def make_gl_entries(
|
||||
doc.get_gl_dict(
|
||||
{
|
||||
"account": credit_account,
|
||||
"against_type": against_type,
|
||||
"against": against,
|
||||
"against_link": against,
|
||||
"credit": base_amount,
|
||||
"credit_in_account_currency": amount,
|
||||
"cost_center": cost_center,
|
||||
@@ -506,7 +540,9 @@ def make_gl_entries(
|
||||
doc.get_gl_dict(
|
||||
{
|
||||
"account": debit_account,
|
||||
"against_type": against_type,
|
||||
"against": against,
|
||||
"against_link": against,
|
||||
"debit": base_amount,
|
||||
"debit_in_account_currency": amount,
|
||||
"cost_center": cost_center,
|
||||
@@ -559,13 +595,16 @@ def book_revenue_via_journal_entry(
|
||||
deferred_process=None,
|
||||
submit="No",
|
||||
):
|
||||
|
||||
if amount == 0:
|
||||
return
|
||||
|
||||
journal_entry = frappe.new_doc("Journal Entry")
|
||||
journal_entry.posting_date = posting_date
|
||||
journal_entry.company = doc.company
|
||||
journal_entry.voucher_type = "Deferred Revenue" if doc.doctype == "Sales Invoice" else "Deferred Expense"
|
||||
journal_entry.voucher_type = (
|
||||
"Deferred Revenue" if doc.doctype == "Sales Invoice" else "Deferred Expense"
|
||||
)
|
||||
journal_entry.process_deferred_accounting = deferred_process
|
||||
|
||||
debit_entry = {
|
||||
@@ -614,6 +653,7 @@ def book_revenue_via_journal_entry(
|
||||
|
||||
|
||||
def get_deferred_booking_accounts(doctype, voucher_detail_no, dr_or_cr):
|
||||
|
||||
if doctype == "Sales Invoice":
|
||||
credit_account, debit_account = frappe.db.get_value(
|
||||
"Sales Invoice Item",
|
||||
|
||||
@@ -26,14 +26,19 @@ frappe.ui.form.on("Account", {
|
||||
frm.toggle_enable(["is_group", "company"], false);
|
||||
|
||||
if (cint(frm.doc.is_group) == 0) {
|
||||
frm.toggle_display("freeze_account", frm.doc.__onload && frm.doc.__onload.can_freeze_account);
|
||||
frm.toggle_display(
|
||||
"freeze_account",
|
||||
frm.doc.__onload && frm.doc.__onload.can_freeze_account
|
||||
);
|
||||
}
|
||||
|
||||
// read-only for root accounts
|
||||
if (!frm.is_new()) {
|
||||
if (!frm.doc.parent_account) {
|
||||
frm.set_read_only();
|
||||
frm.set_intro(__("This is a root account and cannot be edited."));
|
||||
frm.set_intro(
|
||||
__("This is a root account and cannot be edited.")
|
||||
);
|
||||
} else {
|
||||
// credit days and type if customer or supplier
|
||||
frm.set_intro(null);
|
||||
@@ -75,33 +80,27 @@ frappe.ui.form.on("Account", {
|
||||
);
|
||||
|
||||
if (frm.doc.is_group == 1) {
|
||||
frm.add_custom_button(
|
||||
__("Convert to Non-Group"),
|
||||
function () {
|
||||
return frappe.call({
|
||||
doc: frm.doc,
|
||||
method: "convert_group_to_ledger",
|
||||
callback: function () {
|
||||
frm.refresh();
|
||||
},
|
||||
});
|
||||
},
|
||||
__("Actions")
|
||||
);
|
||||
} else if (cint(frm.doc.is_group) == 0 && frappe.boot.user.can_read.indexOf("GL Entry") !== -1) {
|
||||
frm.add_custom_button(
|
||||
__("General Ledger"),
|
||||
function () {
|
||||
frappe.route_options = {
|
||||
account: frm.doc.name,
|
||||
from_date: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
|
||||
to_date: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
|
||||
company: frm.doc.company,
|
||||
};
|
||||
frappe.set_route("query-report", "General Ledger");
|
||||
},
|
||||
__("View")
|
||||
);
|
||||
frm.add_custom_button(__('Convert to Non-Group'), function () {
|
||||
return frappe.call({
|
||||
doc: frm.doc,
|
||||
method: 'convert_group_to_ledger',
|
||||
callback: function() {
|
||||
frm.refresh();
|
||||
}
|
||||
});
|
||||
}, __('Actions'));
|
||||
|
||||
} else if (cint(frm.doc.is_group) == 0
|
||||
&& frappe.boot.user.can_read.indexOf("GL Entry") !== -1) {
|
||||
frm.add_custom_button(__('General Ledger'), function () {
|
||||
frappe.route_options = {
|
||||
"account": frm.doc.name,
|
||||
"from_date": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
|
||||
"to_date": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
|
||||
"company": frm.doc.company
|
||||
};
|
||||
frappe.set_route("query-report", "General Ledger");
|
||||
}, __('View'));
|
||||
|
||||
frm.add_custom_button(
|
||||
__("Convert to Group"),
|
||||
@@ -194,8 +193,14 @@ frappe.ui.form.on("Account", {
|
||||
if (r.message) {
|
||||
frappe.set_route("Form", "Account", r.message);
|
||||
} else {
|
||||
frm.set_value("account_number", data.account_number);
|
||||
frm.set_value("account_name", data.account_name);
|
||||
frm.set_value(
|
||||
"account_number",
|
||||
data.account_number
|
||||
);
|
||||
frm.set_value(
|
||||
"account_name",
|
||||
data.account_name
|
||||
);
|
||||
}
|
||||
d.hide();
|
||||
}
|
||||
|
||||
@@ -88,11 +88,11 @@ class Account(NestedSet):
|
||||
if frappe.local.flags.ignore_update_nsm:
|
||||
return
|
||||
else:
|
||||
super().on_update()
|
||||
super(Account, self).on_update()
|
||||
|
||||
def onload(self):
|
||||
frozen_accounts_modifier = frappe.db.get_value(
|
||||
"Accounts Settings", "Accounts Settings", "frozen_accounts_modifier"
|
||||
frozen_accounts_modifier = frappe.db.get_single_value(
|
||||
"Accounts Settings", "frozen_accounts_modifier"
|
||||
)
|
||||
if not frozen_accounts_modifier or frozen_accounts_modifier in frappe.get_roles():
|
||||
self.set_onload("can_freeze_account", True)
|
||||
@@ -118,7 +118,6 @@ class Account(NestedSet):
|
||||
self.validate_balance_must_be_debit_or_credit()
|
||||
self.validate_account_currency()
|
||||
self.validate_root_company_and_sync_account_to_children()
|
||||
self.validate_receivable_payable_account_type()
|
||||
|
||||
def validate_parent_child_account_type(self):
|
||||
if self.parent_account:
|
||||
@@ -189,24 +188,6 @@ class Account(NestedSet):
|
||||
"Balance Sheet" if self.root_type in ("Asset", "Liability", "Equity") else "Profit and Loss"
|
||||
)
|
||||
|
||||
def validate_receivable_payable_account_type(self):
|
||||
doc_before_save = self.get_doc_before_save()
|
||||
receivable_payable_types = ["Receivable", "Payable"]
|
||||
if (
|
||||
doc_before_save
|
||||
and doc_before_save.account_type in receivable_payable_types
|
||||
and doc_before_save.account_type != self.account_type
|
||||
):
|
||||
# check for ledger entries
|
||||
if frappe.db.get_all("GL Entry", filters={"account": self.name, "is_cancelled": 0}, limit=1):
|
||||
msg = _(
|
||||
"There are ledger entries against this account. Changing {0} to non-{1} in live system will cause incorrect output in 'Accounts {2}' report"
|
||||
).format(
|
||||
frappe.bold("Account Type"), doc_before_save.account_type, doc_before_save.account_type
|
||||
)
|
||||
frappe.msgprint(msg)
|
||||
self.add_comment("Comment", msg)
|
||||
|
||||
def validate_root_details(self):
|
||||
doc_before_save = self.get_doc_before_save()
|
||||
|
||||
@@ -218,7 +199,9 @@ class Account(NestedSet):
|
||||
|
||||
def validate_root_company_and_sync_account_to_children(self):
|
||||
# ignore validation while creating new compnay or while syncing to child companies
|
||||
if frappe.local.flags.ignore_root_company_validation or self.flags.ignore_root_company_validation:
|
||||
if (
|
||||
frappe.local.flags.ignore_root_company_validation or self.flags.ignore_root_company_validation
|
||||
):
|
||||
return
|
||||
ancestors = get_root_company(self.company)
|
||||
if ancestors:
|
||||
@@ -416,7 +399,7 @@ class Account(NestedSet):
|
||||
if self.check_gle_exists():
|
||||
throw(_("Account with existing transaction can not be deleted"))
|
||||
|
||||
super().on_trash(True)
|
||||
super(Account, self).on_trash(True)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
@@ -424,8 +407,9 @@ class Account(NestedSet):
|
||||
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 = {}
|
||||
and {} like {} order by name limit {} offset {}""".format("%s", searchfield, "%s", "%s", "%s"),
|
||||
where is_group = 1 and docstatus != 2 and company = %s
|
||||
and %s like %s order by name limit %s offset %s"""
|
||||
% ("%s", searchfield, "%s", "%s", "%s"),
|
||||
(filters["company"], "%%%s%%" % txt, page_len, start),
|
||||
as_list=1,
|
||||
)
|
||||
@@ -591,5 +575,7 @@ def sync_update_account_number_in_child(
|
||||
if old_acc_number:
|
||||
filters["account_number"] = old_acc_number
|
||||
|
||||
for d in frappe.db.get_values("Account", filters=filters, fieldname=["company", "name"], as_dict=True):
|
||||
for d in frappe.db.get_values(
|
||||
"Account", filters=filters, fieldname=["company", "name"], as_dict=True
|
||||
):
|
||||
update_account_number(d["name"], account_name, account_number, from_descendant=True)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
frappe.provide("frappe.treeview_settings");
|
||||
frappe.provide("frappe.treeview_settings")
|
||||
|
||||
frappe.treeview_settings["Account"] = {
|
||||
breadcrumb: "Accounts",
|
||||
@@ -7,12 +7,12 @@ frappe.treeview_settings["Account"] = {
|
||||
filters: [
|
||||
{
|
||||
fieldname: "company",
|
||||
fieldtype: "Select",
|
||||
fieldtype:"Select",
|
||||
options: erpnext.utils.get_tree_options("company"),
|
||||
label: __("Company"),
|
||||
default: erpnext.utils.get_tree_default("company"),
|
||||
on_change: function () {
|
||||
var me = frappe.treeview_settings["Account"].treeview;
|
||||
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"));
|
||||
@@ -22,36 +22,30 @@ frappe.treeview_settings["Account"] = {
|
||||
args: {
|
||||
company: company,
|
||||
},
|
||||
callback: function (r) {
|
||||
if (r.message) {
|
||||
callback: function(r) {
|
||||
if(r.message) {
|
||||
let root_company = r.message.length ? r.message[0] : "";
|
||||
me.page.fields_dict.root_company.set_value(root_company);
|
||||
|
||||
frappe.db.get_value(
|
||||
"Company",
|
||||
{ name: company },
|
||||
"allow_account_creation_against_child_company",
|
||||
(r) => {
|
||||
frappe.flags.ignore_root_company_validation =
|
||||
r.allow_account_creation_against_child_company;
|
||||
}
|
||||
);
|
||||
frappe.db.get_value("Company", {"name": company}, "allow_account_creation_against_child_company", (r) => {
|
||||
frappe.flags.ignore_root_company_validation = r.allow_account_creation_against_child_company;
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
fieldname: "root_company",
|
||||
fieldtype: "Data",
|
||||
fieldtype:"Data",
|
||||
label: __("Root Company"),
|
||||
hidden: true,
|
||||
disable_onchange: true,
|
||||
},
|
||||
disable_onchange: true
|
||||
}
|
||||
],
|
||||
root_label: "Accounts",
|
||||
get_tree_nodes: "erpnext.accounts.utils.get_children",
|
||||
on_get_node: function (nodes, deep = false) {
|
||||
get_tree_nodes: 'erpnext.accounts.utils.get_children',
|
||||
on_get_node: function(nodes, deep=false) {
|
||||
if (frappe.boot.user.can_read.indexOf("GL Entry") == -1) return;
|
||||
|
||||
let accounts = [];
|
||||
@@ -63,231 +57,151 @@ frappe.treeview_settings["Account"] = {
|
||||
}
|
||||
|
||||
frappe.db.get_single_value("Accounts Settings", "show_balance_in_coa").then((value) => {
|
||||
if (value) {
|
||||
if(value) {
|
||||
|
||||
const get_balances = frappe.call({
|
||||
method: "erpnext.accounts.utils.get_account_balances",
|
||||
method: 'erpnext.accounts.utils.get_account_balances',
|
||||
args: {
|
||||
accounts: accounts,
|
||||
company: cur_tree.args.company,
|
||||
company: cur_tree.args.company
|
||||
},
|
||||
});
|
||||
|
||||
get_balances.then((r) => {
|
||||
get_balances.then(r => {
|
||||
if (!r.message || r.message.length == 0) return;
|
||||
|
||||
for (let account of r.message) {
|
||||
|
||||
const node = cur_tree.nodes && cur_tree.nodes[account.value];
|
||||
if (!node || node.is_root) continue;
|
||||
|
||||
// show Dr if positive since balance is calculated as debit - credit else show Cr
|
||||
const balance = account.balance_in_account_currency || account.balance;
|
||||
const dr_or_cr = balance > 0 ? "Dr" : "Cr";
|
||||
const dr_or_cr = balance > 0 ? __("Dr"): __("Cr");
|
||||
const format = (value, currency) => format_currency(Math.abs(value), currency);
|
||||
|
||||
if (account.balance !== undefined) {
|
||||
node.parent && node.parent.find(".balance-area").remove();
|
||||
$(
|
||||
'<span class="balance-area pull-right">' +
|
||||
(account.balance_in_account_currency
|
||||
? format(
|
||||
account.balance_in_account_currency,
|
||||
account.account_currency
|
||||
) + " / "
|
||||
: "") +
|
||||
format(account.balance, account.company_currency) +
|
||||
" " +
|
||||
dr_or_cr +
|
||||
"</span>"
|
||||
).insertBefore(node.$ul);
|
||||
if (account.balance!==undefined) {
|
||||
node.parent && node.parent.find('.balance-area').remove();
|
||||
$('<span class="balance-area pull-right">'
|
||||
+ (account.balance_in_account_currency ?
|
||||
(format(account.balance_in_account_currency, account.account_currency) + " / ") : "")
|
||||
+ format(account.balance, account.company_currency)
|
||||
+ " " + dr_or_cr
|
||||
+ '</span>').insertBefore(node.$ul);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
add_tree_node: "erpnext.accounts.utils.add_ac",
|
||||
menu_items: [
|
||||
add_tree_node: 'erpnext.accounts.utils.add_ac',
|
||||
menu_items:[
|
||||
{
|
||||
label: __("New Company"),
|
||||
action: function () {
|
||||
frappe.new_doc("Company", true);
|
||||
},
|
||||
condition: 'frappe.boot.user.can_create.indexOf("Company") !== -1',
|
||||
},
|
||||
label: __('New Company'),
|
||||
action: function() { frappe.new_doc("Company", true) },
|
||||
condition: 'frappe.boot.user.can_create.indexOf("Company") !== -1'
|
||||
}
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
fieldtype: "Data",
|
||||
fieldname: "account_name",
|
||||
label: __("New Account Name"),
|
||||
reqd: true,
|
||||
description: __(
|
||||
"Name of new Account. Note: Please don't create accounts for Customers and Suppliers"
|
||||
),
|
||||
},
|
||||
{
|
||||
fieldtype: "Data",
|
||||
fieldname: "account_number",
|
||||
label: __("Account Number"),
|
||||
description: __("Number of new Account, it will be included in the account name as a prefix"),
|
||||
},
|
||||
{
|
||||
fieldtype: "Check",
|
||||
fieldname: "is_group",
|
||||
label: __("Is Group"),
|
||||
description: __(
|
||||
"Further accounts can be made under Groups, but entries can be made against non-Groups"
|
||||
),
|
||||
},
|
||||
{
|
||||
fieldtype: "Select",
|
||||
fieldname: "root_type",
|
||||
label: __("Root Type"),
|
||||
options: ["Asset", "Liability", "Equity", "Income", "Expense"].join("\n"),
|
||||
depends_on: "eval:doc.is_group && !doc.parent_account",
|
||||
},
|
||||
{
|
||||
fieldtype: "Select",
|
||||
fieldname: "account_type",
|
||||
label: __("Account Type"),
|
||||
options: frappe.get_meta("Account").fields.filter((d) => d.fieldname == "account_type")[0]
|
||||
.options,
|
||||
description: __("Optional. This setting will be used to filter in various transactions."),
|
||||
},
|
||||
{
|
||||
fieldtype: "Float",
|
||||
fieldname: "tax_rate",
|
||||
label: __("Tax Rate"),
|
||||
depends_on: 'eval:doc.is_group==0&&doc.account_type=="Tax"',
|
||||
},
|
||||
{
|
||||
fieldtype: "Link",
|
||||
fieldname: "account_currency",
|
||||
label: __("Currency"),
|
||||
options: "Currency",
|
||||
description: __("Optional. Sets company's default currency, if not specified."),
|
||||
{fieldtype:'Data', fieldname:'account_name', label:__('New Account Name'), reqd:true,
|
||||
description: __("Name of new Account. Note: Please don't create accounts for Customers and Suppliers")},
|
||||
{fieldtype:'Data', fieldname:'account_number', label:__('Account Number'),
|
||||
description: __("Number of new Account, it will be included in the account name as a prefix")},
|
||||
{fieldtype:'Check', fieldname:'is_group', label:__('Is Group'),
|
||||
description: __('Further accounts can be made under Groups, but entries can be made against non-Groups')},
|
||||
{fieldtype:'Select', fieldname:'root_type', label:__('Root Type'),
|
||||
options: ['Asset', 'Liability', 'Equity', 'Income', 'Expense'].join('\n'),
|
||||
depends_on: 'eval:doc.is_group && !doc.parent_account'},
|
||||
{fieldtype:'Select', fieldname:'account_type', label:__('Account Type'),
|
||||
options: frappe.get_meta("Account").fields.filter(d => d.fieldname=='account_type')[0].options,
|
||||
description: __("Optional. This setting will be used to filter in various transactions.")
|
||||
},
|
||||
{fieldtype:'Float', fieldname:'tax_rate', label:__('Tax Rate'),
|
||||
depends_on: 'eval:doc.is_group==0&&doc.account_type=="Tax"'},
|
||||
{fieldtype:'Link', fieldname:'account_currency', label:__('Currency'), options:"Currency",
|
||||
description: __("Optional. Sets company's default currency, if not specified.")}
|
||||
],
|
||||
ignore_fields: ["parent_account"],
|
||||
onload: function (treeview) {
|
||||
frappe.treeview_settings["Account"].treeview = {};
|
||||
$.extend(frappe.treeview_settings["Account"].treeview, treeview);
|
||||
ignore_fields:["parent_account"],
|
||||
onload: function(treeview) {
|
||||
frappe.treeview_settings['Account'].treeview = {};
|
||||
$.extend(frappe.treeview_settings['Account'].treeview, treeview);
|
||||
function get_company() {
|
||||
return treeview.page.fields_dict.company.get_value();
|
||||
}
|
||||
|
||||
// tools
|
||||
treeview.page.add_inner_button(
|
||||
__("Chart of Cost Centers"),
|
||||
function () {
|
||||
frappe.set_route("Tree", "Cost Center", { company: get_company() });
|
||||
},
|
||||
__("View")
|
||||
);
|
||||
treeview.page.add_inner_button(__("Chart of Cost Centers"), function() {
|
||||
frappe.set_route('Tree', 'Cost Center', {company: get_company()});
|
||||
}, __('View'));
|
||||
|
||||
treeview.page.add_inner_button(
|
||||
__("Opening Invoice Creation Tool"),
|
||||
function () {
|
||||
frappe.set_route("Form", "Opening Invoice Creation Tool", { company: get_company() });
|
||||
},
|
||||
__("View")
|
||||
);
|
||||
treeview.page.add_inner_button(__("Opening Invoice Creation Tool"), function() {
|
||||
frappe.set_route('Form', 'Opening Invoice Creation Tool', {company: get_company()});
|
||||
}, __('View'));
|
||||
|
||||
treeview.page.add_inner_button(
|
||||
__("Period Closing Voucher"),
|
||||
function () {
|
||||
frappe.set_route("List", "Period Closing Voucher", { company: get_company() });
|
||||
},
|
||||
__("View")
|
||||
);
|
||||
treeview.page.add_inner_button(__("Period Closing Voucher"), function() {
|
||||
frappe.set_route('List', 'Period Closing Voucher', {company: get_company()});
|
||||
}, __('View'));
|
||||
|
||||
treeview.page.add_inner_button(
|
||||
__("Journal Entry"),
|
||||
function () {
|
||||
frappe.new_doc("Journal Entry", { company: get_company() });
|
||||
},
|
||||
__("Create")
|
||||
);
|
||||
treeview.page.add_inner_button(
|
||||
__("Company"),
|
||||
function () {
|
||||
frappe.new_doc("Company");
|
||||
},
|
||||
__("Create")
|
||||
);
|
||||
|
||||
treeview.page.add_inner_button(__("Journal Entry"), function() {
|
||||
frappe.new_doc('Journal Entry', {company: get_company()});
|
||||
}, __('Create'));
|
||||
treeview.page.add_inner_button(__("Company"), function() {
|
||||
frappe.new_doc('Company');
|
||||
}, __('Create'));
|
||||
|
||||
// financial statements
|
||||
for (let report of [
|
||||
"Trial Balance",
|
||||
"General Ledger",
|
||||
"Balance Sheet",
|
||||
"Profit and Loss Statement",
|
||||
"Cash Flow Statement",
|
||||
"Accounts Payable",
|
||||
"Accounts Receivable",
|
||||
]) {
|
||||
treeview.page.add_inner_button(
|
||||
__(report),
|
||||
function () {
|
||||
frappe.set_route("query-report", report, { company: get_company() });
|
||||
},
|
||||
__("Financial Statements")
|
||||
);
|
||||
for (let report of ['Trial Balance', 'General Ledger', 'Balance Sheet',
|
||||
'Profit and Loss Statement', 'Cash Flow Statement', 'Accounts Payable', 'Accounts Receivable']) {
|
||||
treeview.page.add_inner_button(__(report), function() {
|
||||
frappe.set_route('query-report', report, {company: get_company()});
|
||||
}, __('Financial Statements'));
|
||||
}
|
||||
},
|
||||
post_render: function (treeview) {
|
||||
frappe.treeview_settings["Account"].treeview["tree"] = treeview.tree;
|
||||
treeview.page.set_primary_action(
|
||||
__("New"),
|
||||
function () {
|
||||
let root_company = treeview.page.fields_dict.root_company.get_value();
|
||||
|
||||
if (root_company) {
|
||||
frappe.throw(__("Please add the account to root level Company - {0}"), [root_company]);
|
||||
} else {
|
||||
treeview.new_node();
|
||||
}
|
||||
},
|
||||
"add"
|
||||
);
|
||||
},
|
||||
post_render: function(treeview) {
|
||||
frappe.treeview_settings['Account'].treeview["tree"] = treeview.tree;
|
||||
treeview.page.set_primary_action(__("New"), function() {
|
||||
let root_company = treeview.page.fields_dict.root_company.get_value();
|
||||
|
||||
if(root_company) {
|
||||
frappe.throw(__("Please add the account to root level Company - {0}"), [root_company]);
|
||||
} else {
|
||||
treeview.new_node();
|
||||
}
|
||||
}, "add");
|
||||
},
|
||||
toolbar: [
|
||||
{
|
||||
label: __("Add Child"),
|
||||
condition: function (node) {
|
||||
return (
|
||||
frappe.boot.user.can_create.indexOf("Account") !== -1 &&
|
||||
(!frappe.treeview_settings[
|
||||
"Account"
|
||||
].treeview.page.fields_dict.root_company.get_value() ||
|
||||
frappe.flags.ignore_root_company_validation) &&
|
||||
node.expandable &&
|
||||
!node.hide_add
|
||||
);
|
||||
label:__("Add Child"),
|
||||
condition: function(node) {
|
||||
return frappe.boot.user.can_create.indexOf("Account") !== -1
|
||||
&& (!frappe.treeview_settings['Account'].treeview.page.fields_dict.root_company.get_value()
|
||||
|| frappe.flags.ignore_root_company_validation)
|
||||
&& node.expandable && !node.hide_add;
|
||||
},
|
||||
click: function () {
|
||||
var me = frappe.views.trees["Account"];
|
||||
click: function() {
|
||||
var me = frappe.views.trees['Account'];
|
||||
me.new_node();
|
||||
},
|
||||
btnClass: "hidden-xs",
|
||||
btnClass: "hidden-xs"
|
||||
},
|
||||
{
|
||||
condition: function (node) {
|
||||
return !node.root && frappe.boot.user.can_read.indexOf("GL Entry") !== -1;
|
||||
condition: function(node) {
|
||||
return !node.root && frappe.boot.user.can_read.indexOf("GL Entry") !== -1
|
||||
},
|
||||
label: __("View Ledger"),
|
||||
click: function (node, btn) {
|
||||
click: function(node, btn) {
|
||||
frappe.route_options = {
|
||||
account: node.label,
|
||||
from_date: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
|
||||
to_date: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
|
||||
company:
|
||||
frappe.treeview_settings["Account"].treeview.page.fields_dict.company.get_value(),
|
||||
"account": node.label,
|
||||
"from_date": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
|
||||
"to_date": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
|
||||
"company": frappe.treeview_settings['Account'].treeview.page.fields_dict.company.get_value()
|
||||
};
|
||||
frappe.set_route("query-report", "General Ledger");
|
||||
},
|
||||
btnClass: "hidden-xs",
|
||||
},
|
||||
btnClass: "hidden-xs"
|
||||
}
|
||||
],
|
||||
extend_toolbar: true,
|
||||
};
|
||||
extend_toolbar: true
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ def create_charts(
|
||||
"tax_rate",
|
||||
"account_currency",
|
||||
]:
|
||||
|
||||
account_number = cstr(child.get("account_number")).strip()
|
||||
account_name, account_name_in_db = add_suffix_if_duplicate(
|
||||
account_name, account_number, accounts
|
||||
@@ -38,9 +39,7 @@ def create_charts(
|
||||
|
||||
is_group = identify_is_group(child)
|
||||
report_type = (
|
||||
"Balance Sheet"
|
||||
if root_type in ["Asset", "Liability", "Equity"]
|
||||
else "Profit and Loss"
|
||||
"Balance Sheet" if root_type in ["Asset", "Liability", "Equity"] else "Profit and Loss"
|
||||
)
|
||||
|
||||
account = frappe.get_doc(
|
||||
@@ -75,7 +74,7 @@ def create_charts(
|
||||
# after all accounts are already inserted.
|
||||
frappe.local.flags.ignore_update_nsm = True
|
||||
_import_accounts(chart, None, None, root_account=True)
|
||||
rebuild_tree("Account", "parent_account")
|
||||
rebuild_tree("Account")
|
||||
frappe.local.flags.ignore_update_nsm = False
|
||||
|
||||
|
||||
@@ -142,7 +141,7 @@ def get_chart(chart_template, existing_company=None):
|
||||
for fname in os.listdir(path):
|
||||
fname = frappe.as_unicode(fname)
|
||||
if fname.endswith(".json"):
|
||||
with open(os.path.join(path, fname)) as f:
|
||||
with open(os.path.join(path, fname), "r") as f:
|
||||
chart = f.read()
|
||||
if chart and json.loads(chart).get("name") == chart_template:
|
||||
return json.loads(chart).get("tree")
|
||||
@@ -174,7 +173,7 @@ def get_charts_for_country(country, with_standard=False):
|
||||
for fname in os.listdir(path):
|
||||
fname = frappe.as_unicode(fname)
|
||||
if (fname.startswith(country_code) or fname.startswith(country)) and fname.endswith(".json"):
|
||||
with open(os.path.join(path, fname)) as f:
|
||||
with open(os.path.join(path, fname), "r") as f:
|
||||
_get_chart_name(f.read())
|
||||
|
||||
# if more than one charts, returned then add the standard
|
||||
@@ -232,6 +231,8 @@ def build_account_tree(tree, parent, all_accounts):
|
||||
tree[child.account_name]["account_type"] = child.account_type
|
||||
if child.tax_rate:
|
||||
tree[child.account_name]["tax_rate"] = child.tax_rate
|
||||
if child.account_currency:
|
||||
tree[child.account_name]["account_currency"] = child.account_currency
|
||||
if not parent:
|
||||
tree[child.account_name]["root_type"] = child.root_type
|
||||
|
||||
@@ -248,13 +249,7 @@ def validate_bank_account(coa, bank_account):
|
||||
|
||||
def _get_account_names(account_master):
|
||||
for account_name, child in account_master.items():
|
||||
if account_name not in [
|
||||
"account_number",
|
||||
"account_type",
|
||||
"root_type",
|
||||
"is_group",
|
||||
"tax_rate",
|
||||
]:
|
||||
if account_name not in ["account_number", "account_type", "root_type", "is_group", "tax_rate"]:
|
||||
accounts.append(account_name)
|
||||
|
||||
_get_account_names(child)
|
||||
|
||||
@@ -56,9 +56,7 @@
|
||||
"Constru\u00e7\u00f5es em Andamento de Im\u00f3veis Destinados \u00e0 Venda": {},
|
||||
"Estoques Destinados \u00e0 Doa\u00e7\u00e3o": {},
|
||||
"Im\u00f3veis Destinados \u00e0 Venda": {},
|
||||
"Insumos (materiais diretos)": {
|
||||
"account_type": "Stock"
|
||||
},
|
||||
"Insumos (materiais diretos)": {},
|
||||
"Insumos Agropecu\u00e1rios": {},
|
||||
"Mercadorias para Revenda": {},
|
||||
"Outras 11": {},
|
||||
@@ -148,65 +146,6 @@
|
||||
"root_type": "Asset"
|
||||
},
|
||||
"CUSTOS DE PRODU\u00c7\u00c3O": {
|
||||
"CUSTO DOS PRODUTOS E SERVI\u00c7OS VENDIDOS": {
|
||||
"CUSTO DOS PRODUTOS VENDIDOS": {
|
||||
"CUSTO DOS PRODUTOS VENDIDOS PARA AS DEMAIS ATIVIDADES": {
|
||||
"Custos dos Produtos Vendidos em Geral": {
|
||||
"account_type": "Cost of Goods Sold"
|
||||
},
|
||||
"Outros Custos 4": {},
|
||||
"account_type": "Cost of Goods Sold"
|
||||
},
|
||||
"CUSTO DOS PRODUTOS VENDIDOS PARA ASSIST\u00caNCIA SOCIAL": {
|
||||
"Custos dos Produtos para Assist\u00eancia Social - Gratuidades": {},
|
||||
"Custos dos Produtos para Assist\u00eancia Social - Vendidos": {},
|
||||
"Outras": {}
|
||||
},
|
||||
"CUSTO DOS PRODUTOS VENDIDOS PARA EDUCA\u00c7\u00c3O": {
|
||||
"Custos dos Produtos para Educa\u00e7\u00e3o - Gratuidades": {},
|
||||
"Custos dos Produtos para Educa\u00e7\u00e3o - Vendidos": {},
|
||||
"Outros Custos 6": {}
|
||||
},
|
||||
"CUSTO DOS PRODUTOS VENDIDOS PARA SA\u00daDE": {
|
||||
"Custos dos Produtos para Sa\u00fade - Gratuidades": {},
|
||||
"Custos dos Produtos para Sa\u00fade \u2013 Vendidos": {},
|
||||
"Outros Custos 5": {}
|
||||
},
|
||||
"account_type": "Cost of Goods Sold"
|
||||
},
|
||||
"CUSTO DOS SERVI\u00c7OS PRESTADOS": {
|
||||
"CUSTO DOS SERVI\u00c7OS PRESTADOS PARA AS DEMAIS ATIVIDADES": {
|
||||
"Custo dos Servi\u00e7os Prestados em Geral": {},
|
||||
"Outros Custos": {}
|
||||
},
|
||||
"CUSTO DOS SERVI\u00c7OS PRESTADOS PARA ASSIST\u00caNCIA SOCIAL": {
|
||||
"Custo dos Servi\u00e7os Prestados a Conv\u00eanios/Contratos/Parcerias": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es 1": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es/Subven\u00e7\u00f5es Vinculadas 1": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Gratuidade 1": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Pacientes Particulares": {},
|
||||
"Outros Custos 2": {}
|
||||
},
|
||||
"CUSTO DOS SERVI\u00c7OS PRESTADOS PARA EDUCA\u00c7\u00c3O": {
|
||||
"Custo dos Servi\u00e7os Prestados a Alunos N\u00e3o Bolsistas": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Conv\u00eanios/Contratos/Parcerias (Exceto PROUNI)": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es/Subven\u00e7\u00f5es Vinculadas": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Gratuidade": {},
|
||||
"Custo dos Servi\u00e7os Prestados ao PROUNI": {},
|
||||
"Outros Custos 1": {}
|
||||
},
|
||||
"CUSTO DOS SERVI\u00c7OS PRESTADOS PARA SA\u00daDE": {
|
||||
"Custo dos Servi\u00e7os Prestados a Conv\u00eanios SUS": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Conv\u00eanios/Contratos/Parcerias 1": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es 2": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es/Subven\u00e7\u00f5es Vinculadas 2": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Gratuidade 2": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Pacientes Particulares 1": {},
|
||||
"Outros Custos 3": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"CUSTO DOS BENS E SERVI\u00c7OS PRODUZIDOS": {
|
||||
"CUSTO DOS PRODUTOS DE FABRICA\u00c7\u00c3O PR\u00d3PRIA PRODUZIDOS": {
|
||||
"Alimenta\u00e7\u00e3o do Trabalhador": {},
|
||||
@@ -682,9 +621,7 @@
|
||||
"Receita das Unidades Imobili\u00e1rias Vendidas": {},
|
||||
"Receita de Exporta\u00e7\u00e3o Direta de Mercadorias e Produtos": {},
|
||||
"Receita de Exporta\u00e7\u00e3o de Servi\u00e7os": {},
|
||||
"Receita de Loca\u00e7\u00e3o de Bens M\u00f3veis e Im\u00f3veis": {
|
||||
"account_type": "Income Account"
|
||||
},
|
||||
"Receita de Loca\u00e7\u00e3o de Bens M\u00f3veis e Im\u00f3veis": {},
|
||||
"Receita de Vendas de Mercadorias e Produtos a Comercial Exportadora com Fim Espec\u00edfico de Exporta\u00e7\u00e3o": {}
|
||||
}
|
||||
}
|
||||
@@ -708,6 +645,65 @@
|
||||
}
|
||||
},
|
||||
"RESULTADO OPERACIONAL": {
|
||||
"CUSTO DOS PRODUTOS E SERVI\u00c7OS VENDIDOS": {
|
||||
"CUSTO DOS PRODUTOS VENDIDOS": {
|
||||
"CUSTO DOS PRODUTOS VENDIDOS PARA AS DEMAIS ATIVIDADES": {
|
||||
"Custos dos Produtos Vendidos em Geral": {
|
||||
"account_type": "Cost of Goods Sold"
|
||||
},
|
||||
"Outros Custos 4": {},
|
||||
"account_type": "Cost of Goods Sold"
|
||||
},
|
||||
"CUSTO DOS PRODUTOS VENDIDOS PARA ASSIST\u00caNCIA SOCIAL": {
|
||||
"Custos dos Produtos para Assist\u00eancia Social - Gratuidades": {},
|
||||
"Custos dos Produtos para Assist\u00eancia Social - Vendidos": {},
|
||||
"Outras": {}
|
||||
},
|
||||
"CUSTO DOS PRODUTOS VENDIDOS PARA EDUCA\u00c7\u00c3O": {
|
||||
"Custos dos Produtos para Educa\u00e7\u00e3o - Gratuidades": {},
|
||||
"Custos dos Produtos para Educa\u00e7\u00e3o - Vendidos": {},
|
||||
"Outros Custos 6": {}
|
||||
},
|
||||
"CUSTO DOS PRODUTOS VENDIDOS PARA SA\u00daDE": {
|
||||
"Custos dos Produtos para Sa\u00fade - Gratuidades": {},
|
||||
"Custos dos Produtos para Sa\u00fade \u2013 Vendidos": {},
|
||||
"Outros Custos 5": {}
|
||||
},
|
||||
"account_type": "Cost of Goods Sold"
|
||||
},
|
||||
"CUSTO DOS SERVI\u00c7OS PRESTADOS": {
|
||||
"CUSTO DOS SERVI\u00c7OS PRESTADOS PARA AS DEMAIS ATIVIDADES": {
|
||||
"Custo dos Servi\u00e7os Prestados em Geral": {},
|
||||
"Outros Custos": {}
|
||||
},
|
||||
"CUSTO DOS SERVI\u00c7OS PRESTADOS PARA ASSIST\u00caNCIA SOCIAL": {
|
||||
"Custo dos Servi\u00e7os Prestados a Conv\u00eanios/Contratos/Parcerias": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es 1": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es/Subven\u00e7\u00f5es Vinculadas 1": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Gratuidade 1": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Pacientes Particulares": {},
|
||||
"Outros Custos 2": {}
|
||||
},
|
||||
"CUSTO DOS SERVI\u00c7OS PRESTADOS PARA EDUCA\u00c7\u00c3O": {
|
||||
"Custo dos Servi\u00e7os Prestados a Alunos N\u00e3o Bolsistas": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Conv\u00eanios/Contratos/Parcerias (Exceto PROUNI)": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es/Subven\u00e7\u00f5es Vinculadas": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Gratuidade": {},
|
||||
"Custo dos Servi\u00e7os Prestados ao PROUNI": {},
|
||||
"Outros Custos 1": {}
|
||||
},
|
||||
"CUSTO DOS SERVI\u00c7OS PRESTADOS PARA SA\u00daDE": {
|
||||
"Custo dos Servi\u00e7os Prestados a Conv\u00eanios SUS": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Conv\u00eanios/Contratos/Parcerias 1": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es 2": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es/Subven\u00e7\u00f5es Vinculadas 2": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Gratuidade 2": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Pacientes Particulares 1": {},
|
||||
"Outros Custos 3": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"DESPESAS OPERACIONAIS": {
|
||||
"DESPESAS OPERACIONAIS 1": {
|
||||
"DESPESAS OPERACIONAIS 2": {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1653,4 +1653,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"country_code": "ni",
|
||||
"name": "Nicaragua - Catalogo de Cuentas",
|
||||
"name": "Nicaragua - Catálogo de Cuentas",
|
||||
"tree": {
|
||||
"Activo": {
|
||||
"Activo Corriente": {
|
||||
@@ -491,4 +491,4 @@
|
||||
"root_type": "Liability"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import unittest
|
||||
|
||||
import frappe
|
||||
from frappe.test_runner import make_test_records
|
||||
from frappe.utils import nowdate
|
||||
|
||||
from erpnext.accounts.doctype.account.account import (
|
||||
InvalidAccountMergeError,
|
||||
@@ -261,20 +260,28 @@ class TestAccount(unittest.TestCase):
|
||||
acc.insert()
|
||||
|
||||
self.assertTrue(
|
||||
frappe.db.exists("Account", {"account_name": "Test Group Account", "company": "_Test Company 4"})
|
||||
frappe.db.exists(
|
||||
"Account", {"account_name": "Test Group Account", "company": "_Test Company 4"}
|
||||
)
|
||||
)
|
||||
self.assertTrue(
|
||||
frappe.db.exists("Account", {"account_name": "Test Group Account", "company": "_Test Company 5"})
|
||||
frappe.db.exists(
|
||||
"Account", {"account_name": "Test Group Account", "company": "_Test Company 5"}
|
||||
)
|
||||
)
|
||||
|
||||
# Try renaming child company account
|
||||
acc_tc_5 = frappe.db.get_value(
|
||||
"Account", {"account_name": "Test Group Account", "company": "_Test Company 5"}
|
||||
)
|
||||
self.assertRaises(frappe.ValidationError, update_account_number, acc_tc_5, "Test Modified Account")
|
||||
self.assertRaises(
|
||||
frappe.ValidationError, update_account_number, acc_tc_5, "Test Modified Account"
|
||||
)
|
||||
|
||||
# Rename child company account with allow_account_creation_against_child_company enabled
|
||||
frappe.db.set_value("Company", "_Test Company 5", "allow_account_creation_against_child_company", 1)
|
||||
frappe.db.set_value(
|
||||
"Company", "_Test Company 5", "allow_account_creation_against_child_company", 1
|
||||
)
|
||||
|
||||
update_account_number(acc_tc_5, "Test Modified Account")
|
||||
self.assertTrue(
|
||||
@@ -283,7 +290,9 @@ class TestAccount(unittest.TestCase):
|
||||
)
|
||||
)
|
||||
|
||||
frappe.db.set_value("Company", "_Test Company 5", "allow_account_creation_against_child_company", 0)
|
||||
frappe.db.set_value(
|
||||
"Company", "_Test Company 5", "allow_account_creation_against_child_company", 0
|
||||
)
|
||||
|
||||
to_delete = [
|
||||
"Test Group Account - _TC3",
|
||||
@@ -308,24 +317,13 @@ class TestAccount(unittest.TestCase):
|
||||
self.assertEqual(acc.account_currency, "INR")
|
||||
|
||||
# Make a JV against this account
|
||||
make_journal_entry("Test Currency Account - _TC", "Miscellaneous Expenses - _TC", 100, submit=True)
|
||||
make_journal_entry(
|
||||
"Test Currency Account - _TC", "Miscellaneous Expenses - _TC", 100, submit=True
|
||||
)
|
||||
|
||||
acc.account_currency = "USD"
|
||||
self.assertRaises(frappe.ValidationError, acc.save)
|
||||
|
||||
def test_account_balance(self):
|
||||
from erpnext.accounts.utils import get_balance_on
|
||||
|
||||
if not frappe.db.exists("Account", "Test Percent Account %5 - _TC"):
|
||||
acc = frappe.new_doc("Account")
|
||||
acc.account_name = "Test Percent Account %5"
|
||||
acc.parent_account = "Tax Assets - _TC"
|
||||
acc.company = "_Test Company"
|
||||
acc.insert()
|
||||
|
||||
balance = get_balance_on(account="Test Percent Account %5 - _TC", date=nowdate())
|
||||
self.assertEqual(balance, 0)
|
||||
|
||||
|
||||
def _make_test_records(verbose=None):
|
||||
from frappe.test_runner import make_test_objects
|
||||
|
||||
@@ -40,12 +40,16 @@ class AccountClosingBalance(Document):
|
||||
def make_closing_entries(closing_entries, voucher_name, company, closing_date):
|
||||
accounting_dimensions = get_accounting_dimensions()
|
||||
|
||||
previous_closing_entries = get_previous_closing_entries(company, closing_date, accounting_dimensions)
|
||||
previous_closing_entries = get_previous_closing_entries(
|
||||
company, closing_date, accounting_dimensions
|
||||
)
|
||||
combined_entries = closing_entries + previous_closing_entries
|
||||
|
||||
merged_entries = aggregate_with_last_account_closing_balance(combined_entries, accounting_dimensions)
|
||||
merged_entries = aggregate_with_last_account_closing_balance(
|
||||
combined_entries, accounting_dimensions
|
||||
)
|
||||
|
||||
for _key, value in merged_entries.items():
|
||||
for key, value in merged_entries.items():
|
||||
cle = frappe.new_doc("Account Closing Balance")
|
||||
cle.update(value)
|
||||
cle.update(value["dimensions"])
|
||||
|
||||
@@ -1,89 +1,74 @@
|
||||
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Accounting Dimension", {
|
||||
refresh: function (frm) {
|
||||
frm.set_query("document_type", () => {
|
||||
frappe.ui.form.on('Accounting Dimension', {
|
||||
refresh: function(frm) {
|
||||
frm.set_query('document_type', () => {
|
||||
let invalid_doctypes = frappe.model.core_doctypes_list;
|
||||
invalid_doctypes.push(
|
||||
"Accounting Dimension",
|
||||
"Project",
|
||||
"Cost Center",
|
||||
"Accounting Dimension Detail",
|
||||
"Company"
|
||||
);
|
||||
invalid_doctypes.push('Accounting Dimension', 'Project',
|
||||
'Cost Center', 'Accounting Dimension Detail', 'Company');
|
||||
|
||||
return {
|
||||
filters: {
|
||||
name: ["not in", invalid_doctypes],
|
||||
},
|
||||
name: ['not in', invalid_doctypes]
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query("offsetting_account", "dimension_defaults", function (doc, cdt, cdn) {
|
||||
frm.set_query("offsetting_account", "dimension_defaults", function(doc, cdt, cdn) {
|
||||
let d = locals[cdt][cdn];
|
||||
return {
|
||||
filters: {
|
||||
company: d.company,
|
||||
root_type: ["in", ["Asset", "Liability"]],
|
||||
is_group: 0,
|
||||
},
|
||||
};
|
||||
is_group: 0
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!frm.is_new()) {
|
||||
frm.add_custom_button(__("Show {0}", [frm.doc.document_type]), function () {
|
||||
frm.add_custom_button(__('Show {0}', [frm.doc.document_type]), function () {
|
||||
frappe.set_route("List", frm.doc.document_type);
|
||||
});
|
||||
|
||||
let button = frm.doc.disabled ? "Enable" : "Disable";
|
||||
|
||||
frm.add_custom_button(__(button), function () {
|
||||
frm.set_value("disabled", 1 - frm.doc.disabled);
|
||||
frm.add_custom_button(__(button), function() {
|
||||
|
||||
frm.set_value('disabled', 1 - frm.doc.disabled);
|
||||
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.disable_dimension",
|
||||
args: {
|
||||
doc: frm.doc,
|
||||
doc: frm.doc
|
||||
},
|
||||
freeze: true,
|
||||
callback: function (r) {
|
||||
callback: function(r) {
|
||||
let message = frm.doc.disabled ? "Dimension Disabled" : "Dimension Enabled";
|
||||
frm.save();
|
||||
frappe.show_alert({ message: __(message), indicator: "green" });
|
||||
},
|
||||
frappe.show_alert({message:__(message), indicator:'green'});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
label: function (frm) {
|
||||
frm.set_value("fieldname", frappe.model.scrub(frm.doc.label));
|
||||
},
|
||||
document_type: function(frm) {
|
||||
|
||||
document_type: function (frm) {
|
||||
frm.set_value("label", frm.doc.document_type);
|
||||
frm.set_value('label', frm.doc.document_type);
|
||||
frm.set_value('fieldname', frappe.model.scrub(frm.doc.document_type));
|
||||
|
||||
frappe.db.get_value(
|
||||
"Accounting Dimension",
|
||||
{ document_type: frm.doc.document_type },
|
||||
"document_type",
|
||||
(r) => {
|
||||
if (r && r.document_type) {
|
||||
frm.set_df_property(
|
||||
"document_type",
|
||||
"description",
|
||||
"Document type is already set as dimension"
|
||||
);
|
||||
}
|
||||
frappe.db.get_value('Accounting Dimension', {'document_type': frm.doc.document_type}, 'document_type', (r) => {
|
||||
if (r && r.document_type) {
|
||||
frm.set_df_property('document_type', 'description', "Document type is already set as dimension");
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Accounting Dimension Detail", {
|
||||
dimension_defaults_add: function (frm, cdt, cdn) {
|
||||
frappe.ui.form.on('Accounting Dimension Detail', {
|
||||
dimension_defaults_add: function(frm, cdt, cdn) {
|
||||
let row = locals[cdt][cdn];
|
||||
row.reference_document = frm.doc.document_type;
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
@@ -11,10 +11,6 @@ from frappe.model import core_doctypes_list
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import cstr
|
||||
|
||||
from erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger import (
|
||||
get_allowed_types_from_settings,
|
||||
)
|
||||
|
||||
|
||||
class AccountingDimension(Document):
|
||||
# begin: auto-generated types
|
||||
@@ -40,8 +36,7 @@ class AccountingDimension(Document):
|
||||
self.set_fieldname_and_label()
|
||||
|
||||
def validate(self):
|
||||
if self.document_type in (
|
||||
*core_doctypes_list,
|
||||
if self.document_type in core_doctypes_list + (
|
||||
"Accounting Dimension",
|
||||
"Project",
|
||||
"Cost Center",
|
||||
@@ -49,10 +44,13 @@ class AccountingDimension(Document):
|
||||
"Company",
|
||||
"Account",
|
||||
):
|
||||
|
||||
msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type)
|
||||
frappe.throw(msg)
|
||||
|
||||
exists = frappe.db.get_value("Accounting Dimension", {"document_type": self.document_type}, ["name"])
|
||||
exists = frappe.db.get_value(
|
||||
"Accounting Dimension", {"document_type": self.document_type}, ["name"]
|
||||
)
|
||||
|
||||
if exists and self.is_new():
|
||||
frappe.throw(_("Document Type already used as a dimension"))
|
||||
@@ -108,9 +106,9 @@ def make_dimension_in_accounting_doctypes(doc, doclist=None):
|
||||
|
||||
doc_count = len(get_accounting_dimensions())
|
||||
count = 0
|
||||
repostable_doctypes = get_allowed_types_from_settings()
|
||||
|
||||
for doctype in doclist:
|
||||
|
||||
if (doc_count + 1) % 2 == 0:
|
||||
insert_after_field = "dimension_col_break"
|
||||
else:
|
||||
@@ -123,7 +121,6 @@ def make_dimension_in_accounting_doctypes(doc, doclist=None):
|
||||
"options": doc.document_type,
|
||||
"insert_after": insert_after_field,
|
||||
"owner": "Administrator",
|
||||
"allow_on_submit": 1 if doctype in repostable_doctypes else 0,
|
||||
}
|
||||
|
||||
meta = frappe.get_meta(doctype, cached=False)
|
||||
@@ -145,7 +142,7 @@ def add_dimension_to_budget_doctype(df, doc):
|
||||
df.update(
|
||||
{
|
||||
"insert_after": "cost_center",
|
||||
"depends_on": f"eval:doc.budget_against == '{doc.document_type}'",
|
||||
"depends_on": "eval:doc.budget_against == '{0}'".format(doc.document_type),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -179,17 +176,19 @@ def delete_accounting_dimension(doc):
|
||||
frappe.db.sql(
|
||||
"""
|
||||
DELETE FROM `tabCustom Field`
|
||||
WHERE fieldname = {}
|
||||
AND dt IN ({})""".format("%s", ", ".join(["%s"] * len(doclist))), # nosec
|
||||
tuple([doc.fieldname, *doclist]),
|
||||
WHERE fieldname = %s
|
||||
AND dt IN (%s)"""
|
||||
% ("%s", ", ".join(["%s"] * len(doclist))), # nosec
|
||||
tuple([doc.fieldname] + doclist),
|
||||
)
|
||||
|
||||
frappe.db.sql(
|
||||
"""
|
||||
DELETE FROM `tabProperty Setter`
|
||||
WHERE field_name = {}
|
||||
AND doc_type IN ({})""".format("%s", ", ".join(["%s"] * len(doclist))), # nosec
|
||||
tuple([doc.fieldname, *doclist]),
|
||||
WHERE field_name = %s
|
||||
AND doc_type IN (%s)"""
|
||||
% ("%s", ", ".join(["%s"] * len(doclist))), # nosec
|
||||
tuple([doc.fieldname] + doclist),
|
||||
)
|
||||
|
||||
budget_against_property = frappe.get_doc("Property Setter", "Budget-budget_against-options")
|
||||
@@ -238,6 +237,7 @@ def get_doctypes_with_dimensions():
|
||||
|
||||
|
||||
def get_accounting_dimensions(as_list=True, filters=None):
|
||||
|
||||
if not filters:
|
||||
filters = {"disabled": 0}
|
||||
|
||||
@@ -266,6 +266,7 @@ def get_checks_for_pl_and_bs_accounts():
|
||||
|
||||
|
||||
def get_dimension_with_children(doctype, dimensions):
|
||||
|
||||
if isinstance(dimensions, str):
|
||||
dimensions = [dimensions]
|
||||
|
||||
@@ -273,7 +274,9 @@ def get_dimension_with_children(doctype, dimensions):
|
||||
|
||||
for dimension in dimensions:
|
||||
lft, rgt = frappe.db.get_value(doctype, dimension, ["lft", "rgt"])
|
||||
children = frappe.get_all(doctype, filters={"lft": [">=", lft], "rgt": ["<=", rgt]}, order_by="lft")
|
||||
children = frappe.get_all(
|
||||
doctype, filters={"lft": [">=", lft], "rgt": ["<=", rgt]}, order_by="lft"
|
||||
)
|
||||
all_dimensions += [c.name for c in children]
|
||||
|
||||
return all_dimensions
|
||||
@@ -281,10 +284,14 @@ def get_dimension_with_children(doctype, dimensions):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_dimensions(with_cost_center_and_project=False):
|
||||
|
||||
c = frappe.qb.DocType("Accounting Dimension Detail")
|
||||
p = frappe.qb.DocType("Accounting Dimension")
|
||||
dimension_filters = (
|
||||
frappe.qb.from_(p).select(p.label, p.fieldname, p.document_type).where(p.disabled == 0).run(as_dict=1)
|
||||
frappe.qb.from_(p)
|
||||
.select(p.label, p.fieldname, p.document_type)
|
||||
.where(p.disabled == 0)
|
||||
.run(as_dict=1)
|
||||
)
|
||||
default_dimensions = (
|
||||
frappe.qb.from_(c)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Accounting Dimension Filter", {
|
||||
refresh: function (frm, cdt, cdn) {
|
||||
let help_content = `<table class="table table-bordered" style="background-color: var(--scrollbar-track-color);">
|
||||
frappe.ui.form.on('Accounting Dimension Filter', {
|
||||
refresh: function(frm, cdt, cdn) {
|
||||
let help_content =
|
||||
`<table class="table table-bordered" style="background-color: var(--scrollbar-track-color);">
|
||||
<tr><td>
|
||||
<p>
|
||||
<i class="fa fa-hand-right"></i>
|
||||
@@ -12,80 +13,77 @@ frappe.ui.form.on("Accounting Dimension Filter", {
|
||||
</td></tr>
|
||||
</table>`;
|
||||
|
||||
frm.set_df_property("dimension_filter_help", "options", help_content);
|
||||
frm.set_df_property('dimension_filter_help', 'options', help_content);
|
||||
},
|
||||
onload: function (frm) {
|
||||
frm.set_query("applicable_on_account", "accounts", function () {
|
||||
onload: function(frm) {
|
||||
frm.set_query('applicable_on_account', 'accounts', function() {
|
||||
return {
|
||||
filters: {
|
||||
company: frm.doc.company,
|
||||
},
|
||||
'company': frm.doc.company
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
frappe.db.get_list("Accounting Dimension", { fields: ["document_type"] }).then((res) => {
|
||||
let options = ["Cost Center", "Project"];
|
||||
frappe.db.get_list('Accounting Dimension',
|
||||
{fields: ['document_type']}).then((res) => {
|
||||
let options = ['Cost Center', 'Project'];
|
||||
|
||||
res.forEach((dimension) => {
|
||||
options.push(dimension.document_type);
|
||||
});
|
||||
|
||||
frm.set_df_property("accounting_dimension", "options", options);
|
||||
frm.set_df_property('accounting_dimension', 'options', options);
|
||||
});
|
||||
|
||||
frm.trigger("setup_filters");
|
||||
frm.trigger('setup_filters');
|
||||
},
|
||||
|
||||
setup_filters: function (frm) {
|
||||
setup_filters: function(frm) {
|
||||
let filters = {};
|
||||
|
||||
if (frm.doc.accounting_dimension) {
|
||||
frappe.model.with_doctype(frm.doc.accounting_dimension, function () {
|
||||
frappe.model.with_doctype(frm.doc.accounting_dimension, function() {
|
||||
if (frappe.model.is_tree(frm.doc.accounting_dimension)) {
|
||||
filters["is_group"] = 0;
|
||||
filters['is_group'] = 0;
|
||||
}
|
||||
|
||||
if (frappe.meta.has_field(frm.doc.accounting_dimension, "company")) {
|
||||
filters["company"] = frm.doc.company;
|
||||
if (frappe.meta.has_field(frm.doc.accounting_dimension, 'company')) {
|
||||
filters['company'] = frm.doc.company;
|
||||
}
|
||||
|
||||
frm.set_query("dimension_value", "dimensions", function () {
|
||||
frm.set_query('dimension_value', 'dimensions', function() {
|
||||
return {
|
||||
filters: filters,
|
||||
filters: filters
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
accounting_dimension: function (frm) {
|
||||
accounting_dimension: function(frm) {
|
||||
frm.clear_table("dimensions");
|
||||
let row = frm.add_child("dimensions");
|
||||
row.accounting_dimension = frm.doc.accounting_dimension;
|
||||
frm.fields_dict["dimensions"].grid.update_docfield_property(
|
||||
"dimension_value",
|
||||
"label",
|
||||
frm.doc.accounting_dimension
|
||||
);
|
||||
frm.fields_dict["dimensions"].grid.update_docfield_property("dimension_value", "label", frm.doc.accounting_dimension);
|
||||
frm.refresh_field("dimensions");
|
||||
frm.trigger("setup_filters");
|
||||
frm.trigger('setup_filters');
|
||||
},
|
||||
apply_restriction_on_values: function (frm) {
|
||||
apply_restriction_on_values: function(frm) {
|
||||
/** If restriction on values is not applied, we should set "allow_or_restrict" to "Restrict" with an empty allowed dimension table.
|
||||
* Hence it's not "restricted" on any value.
|
||||
*/
|
||||
*/
|
||||
if (!frm.doc.apply_restriction_on_values) {
|
||||
frm.set_value("allow_or_restrict", "Restrict");
|
||||
frm.clear_table("dimensions");
|
||||
frm.refresh_field("dimensions");
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Allowed Dimension", {
|
||||
dimensions_add: function (frm, cdt, cdn) {
|
||||
frappe.ui.form.on('Allowed Dimension', {
|
||||
dimensions_add: function(frm, cdt, cdn) {
|
||||
let row = locals[cdt][cdn];
|
||||
row.accounting_dimension = frm.doc.accounting_dimension;
|
||||
frm.refresh_field("dimensions");
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
@@ -55,7 +55,9 @@ class TestAccountingDimensionFilter(unittest.TestCase):
|
||||
|
||||
|
||||
def create_accounting_dimension_filter():
|
||||
if not frappe.db.get_value("Accounting Dimension Filter", {"accounting_dimension": "Cost Center"}):
|
||||
if not frappe.db.get_value(
|
||||
"Accounting Dimension Filter", {"accounting_dimension": "Cost Center"}
|
||||
):
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Accounting Dimension Filter",
|
||||
|
||||
@@ -1,33 +1,30 @@
|
||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Accounting Period", {
|
||||
onload: function (frm) {
|
||||
if (
|
||||
frm.doc.closed_documents.length === 0 ||
|
||||
(frm.doc.closed_documents.length === 1 && frm.doc.closed_documents[0].document_type == undefined)
|
||||
) {
|
||||
frappe.ui.form.on('Accounting Period', {
|
||||
onload: function(frm) {
|
||||
if(frm.doc.closed_documents.length === 0 || (frm.doc.closed_documents.length === 1 && frm.doc.closed_documents[0].document_type == undefined)) {
|
||||
frappe.call({
|
||||
method: "get_doctypes_for_closing",
|
||||
doc: frm.doc,
|
||||
callback: function (r) {
|
||||
if (r.message) {
|
||||
doc:frm.doc,
|
||||
callback: function(r) {
|
||||
if(r.message) {
|
||||
cur_frm.clear_table("closed_documents");
|
||||
r.message.forEach(function (element) {
|
||||
r.message.forEach(function(element) {
|
||||
var c = frm.add_child("closed_documents");
|
||||
c.document_type = element.document_type;
|
||||
c.closed = element.closed;
|
||||
});
|
||||
refresh_field("closed_documents");
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
frm.set_query("document_type", "closed_documents", () => {
|
||||
return {
|
||||
query: "erpnext.controllers.queries.get_doctypes_for_closing",
|
||||
};
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
@@ -84,10 +84,7 @@ class AccountingPeriod(Document):
|
||||
for doctype_for_closing in self.get_doctypes_for_closing():
|
||||
self.append(
|
||||
"closed_documents",
|
||||
{
|
||||
"document_type": doctype_for_closing.document_type,
|
||||
"closed": doctype_for_closing.closed,
|
||||
},
|
||||
{"document_type": doctype_for_closing.document_type, "closed": doctype_for_closing.closed},
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -34,7 +34,9 @@ class TestAccountingPeriod(unittest.TestCase):
|
||||
ap1 = create_accounting_period(period_name="Test Accounting Period 2")
|
||||
ap1.save()
|
||||
|
||||
doc = create_sales_invoice(do_not_save=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC")
|
||||
doc = create_sales_invoice(
|
||||
do_not_save=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC"
|
||||
)
|
||||
self.assertRaises(ClosedAccountingPeriod, doc.save)
|
||||
|
||||
def tearDown(self):
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Accounts Settings", {
|
||||
refresh: function (frm) {},
|
||||
frappe.ui.form.on('Accounts Settings', {
|
||||
refresh: function(frm) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2013-06-24 15:49:57",
|
||||
"description": "Settings for Accounts",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Other",
|
||||
"editable_grid": 1,
|
||||
@@ -461,7 +462,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2024-01-30 14:04:26.553554",
|
||||
"modified": "2023-11-20 09:37:47.650347",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Accounts Settings",
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
frappe.ui.form.on("Accounts Settings", {
|
||||
refresh: function (frm) {
|
||||
|
||||
frappe.ui.form.on('Accounts Settings', {
|
||||
refresh: function(frm) {
|
||||
frm.set_df_property("acc_frozen_upto", "label", "Books Closed Through");
|
||||
frm.set_df_property(
|
||||
"frozen_accounts_modifier",
|
||||
"label",
|
||||
"Role Allowed to Close Books & Make Changes to Closed Periods"
|
||||
);
|
||||
frm.set_df_property("frozen_accounts_modifier", "label", "Role Allowed to Close Books & Make Changes to Closed Periods");
|
||||
frm.set_df_property("credit_controller", "label", "Credit Manager");
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,36 +1,38 @@
|
||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
frappe.provide("erpnext.integrations");
|
||||
frappe.provide('erpnext.integrations');
|
||||
|
||||
frappe.ui.form.on("Bank", {
|
||||
onload: function (frm) {
|
||||
frappe.ui.form.on('Bank', {
|
||||
onload: function(frm) {
|
||||
add_fields_to_mapping_table(frm);
|
||||
},
|
||||
refresh: function (frm) {
|
||||
refresh: function(frm) {
|
||||
add_fields_to_mapping_table(frm);
|
||||
frm.toggle_display(["address_html", "contact_html"], !frm.doc.__islocal);
|
||||
frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal);
|
||||
|
||||
if (frm.doc.__islocal) {
|
||||
frm.set_df_property("address_and_contact", "hidden", 1);
|
||||
frm.set_df_property('address_and_contact', 'hidden', 1);
|
||||
frappe.contacts.clear_address_and_contact(frm);
|
||||
} else {
|
||||
frm.set_df_property("address_and_contact", "hidden", 0);
|
||||
}
|
||||
else {
|
||||
frm.set_df_property('address_and_contact', 'hidden', 0);
|
||||
frappe.contacts.render_address_and_contact(frm);
|
||||
}
|
||||
if (frm.doc.plaid_access_token) {
|
||||
frm.add_custom_button(__("Refresh Plaid Link"), () => {
|
||||
frm.add_custom_button(__('Refresh Plaid Link'), () => {
|
||||
new erpnext.integrations.refreshPlaidLink(frm.doc.plaid_access_token);
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
let add_fields_to_mapping_table = function (frm) {
|
||||
let options = [];
|
||||
|
||||
frappe.model.with_doctype("Bank Transaction", function () {
|
||||
frappe.model.with_doctype("Bank Transaction", function() {
|
||||
let meta = frappe.get_meta("Bank Transaction");
|
||||
meta.fields.forEach((value) => {
|
||||
meta.fields.forEach(value => {
|
||||
if (!["Section Break", "Column Break"].includes(value.fieldtype)) {
|
||||
options.push(value.fieldname);
|
||||
}
|
||||
@@ -38,32 +40,30 @@ let add_fields_to_mapping_table = function (frm) {
|
||||
});
|
||||
|
||||
frm.fields_dict.bank_transaction_mapping.grid.update_docfield_property(
|
||||
"bank_transaction_field",
|
||||
"options",
|
||||
options
|
||||
'bank_transaction_field', 'options', options
|
||||
);
|
||||
};
|
||||
|
||||
erpnext.integrations.refreshPlaidLink = class refreshPlaidLink {
|
||||
constructor(access_token) {
|
||||
this.access_token = access_token;
|
||||
this.plaidUrl = "https://cdn.plaid.com/link/v2/stable/link-initialize.js";
|
||||
this.plaidUrl = 'https://cdn.plaid.com/link/v2/stable/link-initialize.js';
|
||||
this.init_config();
|
||||
}
|
||||
|
||||
async init_config() {
|
||||
this.plaid_env = await frappe.db.get_single_value("Plaid Settings", "plaid_env");
|
||||
this.plaid_env = await frappe.db.get_single_value('Plaid Settings', 'plaid_env');
|
||||
this.token = await this.get_link_token_for_update();
|
||||
this.init_plaid();
|
||||
}
|
||||
|
||||
async get_link_token_for_update() {
|
||||
const token = frappe.xcall(
|
||||
"erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.get_link_token_for_update",
|
||||
'erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.get_link_token_for_update',
|
||||
{ access_token: this.access_token }
|
||||
);
|
||||
)
|
||||
if (!token) {
|
||||
frappe.throw(__("Cannot retrieve link token for update. Check Error Log for more information"));
|
||||
frappe.throw(__('Cannot retrieve link token for update. Check Error Log for more information'));
|
||||
}
|
||||
return token;
|
||||
}
|
||||
@@ -90,45 +90,35 @@ erpnext.integrations.refreshPlaidLink = class refreshPlaidLink {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
const el = document.createElement("script");
|
||||
el.type = "text/javascript";
|
||||
const el = document.createElement('script');
|
||||
el.type = 'text/javascript';
|
||||
el.async = true;
|
||||
el.src = src;
|
||||
el.addEventListener("load", resolve);
|
||||
el.addEventListener("error", reject);
|
||||
el.addEventListener("abort", reject);
|
||||
el.addEventListener('load', resolve);
|
||||
el.addEventListener('error', reject);
|
||||
el.addEventListener('abort', reject);
|
||||
document.head.appendChild(el);
|
||||
});
|
||||
}
|
||||
|
||||
onScriptLoaded(me) {
|
||||
me.linkHandler = Plaid.create({
|
||||
// eslint-disable-line no-undef
|
||||
me.linkHandler = Plaid.create({ // eslint-disable-line no-undef
|
||||
env: me.plaid_env,
|
||||
token: me.token,
|
||||
onSuccess: me.plaid_success,
|
||||
onSuccess: me.plaid_success
|
||||
});
|
||||
}
|
||||
|
||||
onScriptError(error) {
|
||||
frappe.msgprint(
|
||||
__(
|
||||
"There was an issue connecting to Plaid's authentication server. Check browser console for more information"
|
||||
)
|
||||
);
|
||||
frappe.msgprint(__("There was an issue connecting to Plaid's authentication server. Check browser console for more information"));
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
plaid_success(token, response) {
|
||||
frappe
|
||||
.xcall(
|
||||
"erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.update_bank_account_ids",
|
||||
{
|
||||
response: response,
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
frappe.show_alert({ message: __("Plaid Link Updated"), indicator: "green" });
|
||||
});
|
||||
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.update_bank_account_ids', {
|
||||
response: response,
|
||||
}).then(() => {
|
||||
frappe.show_alert({ message: __('Plaid Link Updated'), indicator: 'green' });
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,49 +1,45 @@
|
||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Bank Account", {
|
||||
setup: function (frm) {
|
||||
frm.set_query("account", function () {
|
||||
frappe.ui.form.on('Bank Account', {
|
||||
setup: function(frm) {
|
||||
frm.set_query("account", function() {
|
||||
return {
|
||||
filters: {
|
||||
account_type: "Bank",
|
||||
company: frm.doc.company,
|
||||
is_group: 0,
|
||||
},
|
||||
'account_type': 'Bank',
|
||||
'company': frm.doc.company,
|
||||
'is_group': 0
|
||||
}
|
||||
};
|
||||
});
|
||||
frm.set_query("party_type", function () {
|
||||
frm.set_query("party_type", function() {
|
||||
return {
|
||||
query: "erpnext.setup.doctype.party_type.party_type.get_party_type",
|
||||
};
|
||||
});
|
||||
},
|
||||
refresh: function (frm) {
|
||||
frappe.dynamic_link = { doc: frm.doc, fieldname: "name", doctype: "Bank Account" };
|
||||
refresh: function(frm) {
|
||||
frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Bank Account' }
|
||||
|
||||
frm.toggle_display(["address_html", "contact_html"], !frm.doc.__islocal);
|
||||
frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal);
|
||||
|
||||
if (frm.doc.__islocal) {
|
||||
frappe.contacts.clear_address_and_contact(frm);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
frappe.contacts.render_address_and_contact(frm);
|
||||
}
|
||||
|
||||
if (frm.doc.integration_id) {
|
||||
frm.add_custom_button(__("Unlink external integrations"), function () {
|
||||
frappe.confirm(
|
||||
__(
|
||||
"This action will unlink this account from any external service integrating ERPNext with your bank accounts. It cannot be undone. Are you certain ?"
|
||||
),
|
||||
function () {
|
||||
frm.set_value("integration_id", "");
|
||||
}
|
||||
);
|
||||
frm.add_custom_button(__("Unlink external integrations"), function() {
|
||||
frappe.confirm(__("This action will unlink this account from any external service integrating ERPNext with your bank accounts. It cannot be undone. Are you certain ?"), function() {
|
||||
frm.set_value("integration_id", "");
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
is_company_account: function (frm) {
|
||||
frm.set_df_property("account", "reqd", frm.doc.is_company_account);
|
||||
},
|
||||
is_company_account: function(frm) {
|
||||
frm.set_df_property('account', 'reqd', frm.doc.is_company_account);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -9,7 +9,6 @@ from frappe.contacts.address_and_contact import (
|
||||
load_address_and_contact,
|
||||
)
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import comma_and, get_link_to_form
|
||||
|
||||
|
||||
class BankAccount(Document):
|
||||
@@ -53,20 +52,6 @@ class BankAccount(Document):
|
||||
def validate(self):
|
||||
self.validate_company()
|
||||
self.validate_iban()
|
||||
self.validate_account()
|
||||
self.update_default_bank_account()
|
||||
|
||||
def validate_account(self):
|
||||
if self.account:
|
||||
if accounts := frappe.db.get_all(
|
||||
"Bank Account", filters={"account": self.account, "name": ["!=", self.name]}, as_list=1
|
||||
):
|
||||
frappe.throw(
|
||||
_("'{0}' account is already used by {1}. Use another account.").format(
|
||||
frappe.bold(self.account),
|
||||
frappe.bold(comma_and([get_link_to_form(self.doctype, x[0]) for x in accounts])),
|
||||
)
|
||||
)
|
||||
|
||||
def validate_company(self):
|
||||
if self.is_company_account and not self.company:
|
||||
@@ -101,51 +86,19 @@ class BankAccount(Document):
|
||||
if to_check % 97 != 1:
|
||||
frappe.throw(_("IBAN is not valid"))
|
||||
|
||||
def update_default_bank_account(self):
|
||||
if self.is_default and not self.disabled:
|
||||
frappe.db.set_value(
|
||||
"Bank Account",
|
||||
{
|
||||
"party_type": self.party_type,
|
||||
"party": self.party,
|
||||
"is_company_account": self.is_company_account,
|
||||
"is_default": 1,
|
||||
"disabled": 0,
|
||||
},
|
||||
"is_default",
|
||||
0,
|
||||
)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_bank_account(doctype, docname):
|
||||
doc = frappe.new_doc("Bank Account")
|
||||
doc.party_type = doctype
|
||||
doc.party = docname
|
||||
doc.is_default = 1
|
||||
|
||||
return doc
|
||||
|
||||
|
||||
def get_party_bank_account(party_type, party):
|
||||
return frappe.db.get_value(
|
||||
"Bank Account",
|
||||
{"party_type": party_type, "party": party, "is_default": 1, "disabled": 0},
|
||||
"name",
|
||||
)
|
||||
|
||||
|
||||
def get_default_company_bank_account(company, party_type, party):
|
||||
default_company_bank_account = frappe.db.get_value(party_type, party, "default_bank_account")
|
||||
if default_company_bank_account:
|
||||
if company != frappe.get_cached_value("Bank Account", default_company_bank_account, "company"):
|
||||
default_company_bank_account = None
|
||||
|
||||
if not default_company_bank_account:
|
||||
default_company_bank_account = frappe.db.get_value(
|
||||
"Bank Account", {"company": company, "is_company_account": 1, "is_default": 1}
|
||||
)
|
||||
|
||||
return default_company_bank_account
|
||||
return frappe.db.get_value(party_type, party, "default_bank_account")
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
||||
@@ -37,11 +37,11 @@ class TestBankAccount(unittest.TestCase):
|
||||
try:
|
||||
bank_account.validate_iban()
|
||||
except ValidationError:
|
||||
msg = f"BankAccount.validate_iban() failed for valid IBAN {iban}"
|
||||
msg = "BankAccount.validate_iban() failed for valid IBAN {}".format(iban)
|
||||
self.fail(msg=msg)
|
||||
|
||||
for not_iban in invalid_ibans:
|
||||
bank_account.iban = not_iban
|
||||
msg = f"BankAccount.validate_iban() accepted invalid IBAN {not_iban}"
|
||||
msg = "BankAccount.validate_iban() accepted invalid IBAN {}".format(not_iban)
|
||||
with self.assertRaises(ValidationError, msg=msg):
|
||||
bank_account.validate_iban()
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Bank Account Subtype", {
|
||||
refresh: function () {},
|
||||
frappe.ui.form.on('Bank Account Subtype', {
|
||||
refresh: function() {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Bank Account Type", {
|
||||
frappe.ui.form.on('Bank Account Type', {
|
||||
// refresh: function(frm) {
|
||||
|
||||
// }
|
||||
});
|
||||
|
||||
@@ -2,76 +2,80 @@
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
frappe.ui.form.on("Bank Clearance", {
|
||||
setup: function (frm) {
|
||||
setup: function(frm) {
|
||||
frm.add_fetch("account", "account_currency", "account_currency");
|
||||
|
||||
frm.set_query("account", function () {
|
||||
frm.set_query("account", function() {
|
||||
return {
|
||||
filters: {
|
||||
account_type: ["in", ["Bank", "Cash"]],
|
||||
is_group: 0,
|
||||
},
|
||||
"filters": {
|
||||
"account_type": ["in",["Bank","Cash"]],
|
||||
"is_group": 0,
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query("bank_account", function () {
|
||||
return {
|
||||
filters: {
|
||||
is_company_account: 1,
|
||||
'is_company_account': 1
|
||||
},
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
onload: function (frm) {
|
||||
let default_bank_account = frappe.defaults.get_user_default("Company")
|
||||
? locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]
|
||||
: "";
|
||||
onload: function(frm) {
|
||||
|
||||
let default_bank_account = frappe.defaults.get_user_default("Company")?
|
||||
locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]: "";
|
||||
frm.set_value("account", default_bank_account);
|
||||
|
||||
|
||||
|
||||
frm.set_value("from_date", frappe.datetime.month_start());
|
||||
frm.set_value("to_date", frappe.datetime.month_end());
|
||||
},
|
||||
|
||||
refresh: function (frm) {
|
||||
refresh: function(frm) {
|
||||
frm.disable_save();
|
||||
frm.add_custom_button(__("Get Payment Entries"), () => frm.trigger("get_payment_entries"));
|
||||
frm.add_custom_button(__('Get Payment Entries'), () =>
|
||||
frm.trigger("get_payment_entries")
|
||||
);
|
||||
|
||||
frm.change_custom_button_type(__("Get Payment Entries"), null, "primary");
|
||||
frm.change_custom_button_type(__('Get Payment Entries'), null, 'primary');
|
||||
},
|
||||
|
||||
update_clearance_date: function (frm) {
|
||||
update_clearance_date: function(frm) {
|
||||
return frappe.call({
|
||||
method: "update_clearance_date",
|
||||
doc: frm.doc,
|
||||
callback: function (r, rt) {
|
||||
callback: function(r, rt) {
|
||||
frm.refresh_field("payment_entries");
|
||||
frm.refresh_fields();
|
||||
|
||||
if (!frm.doc.payment_entries.length) {
|
||||
frm.change_custom_button_type(__("Get Payment Entries"), null, "primary");
|
||||
frm.change_custom_button_type(__("Update Clearance Date"), null, "default");
|
||||
frm.change_custom_button_type(__('Get Payment Entries'), null, 'primary');
|
||||
frm.change_custom_button_type(__('Update Clearance Date'), null, 'default');
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
get_payment_entries: function (frm) {
|
||||
get_payment_entries: function(frm) {
|
||||
return frappe.call({
|
||||
method: "get_payment_entries",
|
||||
doc: frm.doc,
|
||||
callback: function (r, rt) {
|
||||
callback: function(r, rt) {
|
||||
frm.refresh_field("payment_entries");
|
||||
|
||||
if (frm.doc.payment_entries.length) {
|
||||
frm.add_custom_button(__("Update Clearance Date"), () =>
|
||||
frm.add_custom_button(__('Update Clearance Date'), () =>
|
||||
frm.trigger("update_clearance_date")
|
||||
);
|
||||
|
||||
frm.change_custom_button_type(__("Get Payment Entries"), null, "default");
|
||||
frm.change_custom_button_type(__("Update Clearance Date"), null, "primary");
|
||||
frm.change_custom_button_type(__('Get Payment Entries'), null, 'default');
|
||||
frm.change_custom_button_type(__('Update Clearance Date'), null, 'primary');
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
@@ -5,9 +5,7 @@
|
||||
import frappe
|
||||
from frappe import _, msgprint
|
||||
from frappe.model.document import Document
|
||||
from frappe.query_builder.custom import ConstantColumn
|
||||
from frappe.utils import flt, fmt_money, getdate
|
||||
from pypika import Order
|
||||
|
||||
import erpnext
|
||||
|
||||
@@ -127,7 +125,7 @@ def get_payment_entries_for_bank_clearance(
|
||||
condition = "and (clearance_date IS NULL or clearance_date='0000-00-00')"
|
||||
|
||||
journal_entries = frappe.db.sql(
|
||||
f"""
|
||||
"""
|
||||
select
|
||||
"Journal Entry" as payment_document, t1.name as payment_entry,
|
||||
t1.cheque_no as cheque_number, t1.cheque_date,
|
||||
@@ -141,7 +139,9 @@ def get_payment_entries_for_bank_clearance(
|
||||
and ifnull(t1.is_opening, 'No') = 'No' {condition}
|
||||
group by t2.account, t1.name
|
||||
order by t1.posting_date ASC, t1.name DESC
|
||||
""",
|
||||
""".format(
|
||||
condition=condition
|
||||
),
|
||||
{"account": account, "from": from_date, "to": to_date},
|
||||
as_dict=1,
|
||||
)
|
||||
@@ -150,7 +150,7 @@ def get_payment_entries_for_bank_clearance(
|
||||
condition += "and bank_account = %(bank_account)s"
|
||||
|
||||
payment_entries = frappe.db.sql(
|
||||
f"""
|
||||
"""
|
||||
select
|
||||
"Payment Entry" as payment_document, name as payment_entry,
|
||||
reference_no as cheque_number, reference_date as cheque_date,
|
||||
@@ -165,7 +165,9 @@ def get_payment_entries_for_bank_clearance(
|
||||
{condition}
|
||||
order by
|
||||
posting_date ASC, name DESC
|
||||
""",
|
||||
""".format(
|
||||
condition=condition
|
||||
),
|
||||
{
|
||||
"account": account,
|
||||
"from": from_date,
|
||||
@@ -177,65 +179,45 @@ def get_payment_entries_for_bank_clearance(
|
||||
|
||||
pos_sales_invoices, pos_purchase_invoices = [], []
|
||||
if include_pos_transactions:
|
||||
si_payment = frappe.qb.DocType("Sales Invoice Payment")
|
||||
si = frappe.qb.DocType("Sales Invoice")
|
||||
acc = frappe.qb.DocType("Account")
|
||||
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.customer as against_account, sip.clearance_date,
|
||||
account.account_currency, 0 as credit
|
||||
from `tabSales Invoice Payment` sip, `tabSales Invoice` si, `tabAccount` account
|
||||
where
|
||||
sip.account=%(account)s and si.docstatus=1 and sip.parent = si.name
|
||||
and account.name = sip.account and si.posting_date >= %(from)s and si.posting_date <= %(to)s
|
||||
order by
|
||||
si.posting_date ASC, si.name DESC
|
||||
""",
|
||||
{"account": account, "from": from_date, "to": to_date},
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
pos_sales_invoices = (
|
||||
frappe.qb.from_(si_payment)
|
||||
.inner_join(si)
|
||||
.on(si_payment.parent == si.name)
|
||||
.inner_join(acc)
|
||||
.on(si_payment.account == acc.name)
|
||||
.select(
|
||||
ConstantColumn("Sales Invoice").as_("payment_document"),
|
||||
si.name.as_("payment_entry"),
|
||||
si_payment.reference_no.as_("cheque_number"),
|
||||
si_payment.amount.as_("debit"),
|
||||
si.posting_date,
|
||||
si.customer.as_("against_account"),
|
||||
si_payment.clearance_date,
|
||||
acc.account_currency,
|
||||
ConstantColumn(0).as_("credit"),
|
||||
)
|
||||
.where(
|
||||
(si.docstatus == 1)
|
||||
& (si_payment.account == account)
|
||||
& (si.posting_date >= from_date)
|
||||
& (si.posting_date <= to_date)
|
||||
)
|
||||
.orderby(si.posting_date)
|
||||
.orderby(si.name, order=Order.desc)
|
||||
).run(as_dict=True)
|
||||
|
||||
pi = frappe.qb.DocType("Purchase Invoice")
|
||||
|
||||
pos_purchase_invoices = (
|
||||
frappe.qb.from_(pi)
|
||||
.inner_join(acc)
|
||||
.on(pi.cash_bank_account == acc.name)
|
||||
.select(
|
||||
ConstantColumn("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,
|
||||
acc.account_currency,
|
||||
ConstantColumn(0).as_("debit"),
|
||||
)
|
||||
.where(
|
||||
(pi.docstatus == 1)
|
||||
& (pi.cash_bank_account == account)
|
||||
& (pi.posting_date >= from_date)
|
||||
& (pi.posting_date <= to_date)
|
||||
)
|
||||
.orderby(pi.posting_date)
|
||||
.orderby(pi.name, order=Order.desc)
|
||||
).run(as_dict=True)
|
||||
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": account, "from": from_date, "to": to_date},
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
entries = (
|
||||
list(payment_entries) + list(journal_entries) + list(pos_sales_invoices) + list(pos_purchase_invoices)
|
||||
list(payment_entries)
|
||||
+ list(journal_entries)
|
||||
+ list(pos_sales_invoices)
|
||||
+ list(pos_purchase_invoices)
|
||||
)
|
||||
|
||||
return entries
|
||||
|
||||
@@ -68,7 +68,9 @@ class TestBankClearance(unittest.TestCase):
|
||||
)
|
||||
loan.submit()
|
||||
make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=getdate())
|
||||
repayment_entry = create_repayment_entry(loan.name, "_Test Customer", getdate(), loan.loan_amount)
|
||||
repayment_entry = create_repayment_entry(
|
||||
loan.name, "_Test Customer", getdate(), loan.loan_amount
|
||||
)
|
||||
repayment_entry.save()
|
||||
repayment_entry.submit()
|
||||
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
cur_frm.add_fetch("bank_account", "account", "account");
|
||||
cur_frm.add_fetch("bank_account", "bank_account_no", "bank_account_no");
|
||||
cur_frm.add_fetch("bank_account", "iban", "iban");
|
||||
cur_frm.add_fetch("bank_account", "branch_code", "branch_code");
|
||||
cur_frm.add_fetch("bank", "swift_number", "swift_number");
|
||||
cur_frm.add_fetch('bank_account','account','account');
|
||||
cur_frm.add_fetch('bank_account','bank_account_no','bank_account_no');
|
||||
cur_frm.add_fetch('bank_account','iban','iban');
|
||||
cur_frm.add_fetch('bank_account','branch_code','branch_code');
|
||||
cur_frm.add_fetch('bank','swift_number','swift_number');
|
||||
|
||||
frappe.ui.form.on("Bank Guarantee", {
|
||||
setup: function (frm) {
|
||||
frm.set_query("bank", function () {
|
||||
frappe.ui.form.on('Bank Guarantee', {
|
||||
setup: function(frm) {
|
||||
frm.set_query("bank", function() {
|
||||
return {
|
||||
filters: {
|
||||
company: frm.doc.company
|
||||
}
|
||||
};
|
||||
});
|
||||
frm.set_query("bank_account", function() {
|
||||
return {
|
||||
filters: {
|
||||
company: frm.doc.company,
|
||||
},
|
||||
};
|
||||
bank: frm.doc.bank
|
||||
}
|
||||
}
|
||||
});
|
||||
frm.set_query("bank_account", function () {
|
||||
frm.set_query("project", function() {
|
||||
return {
|
||||
filters: {
|
||||
company: frm.doc.company,
|
||||
bank: frm.doc.bank,
|
||||
},
|
||||
};
|
||||
});
|
||||
frm.set_query("project", function () {
|
||||
return {
|
||||
filters: {
|
||||
customer: frm.doc.customer,
|
||||
},
|
||||
customer: frm.doc.customer
|
||||
}
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
bg_type: function (frm) {
|
||||
bg_type: function(frm) {
|
||||
if (frm.doc.bg_type == "Receiving") {
|
||||
frm.set_value("reference_doctype", "Sales Order");
|
||||
} else if (frm.doc.bg_type == "Providing") {
|
||||
@@ -41,33 +41,34 @@ frappe.ui.form.on("Bank Guarantee", {
|
||||
}
|
||||
},
|
||||
|
||||
reference_docname: function (frm) {
|
||||
reference_docname: function(frm) {
|
||||
if (frm.doc.reference_docname && frm.doc.reference_doctype) {
|
||||
let party_field = frm.doc.reference_doctype == "Sales Order" ? "customer" : "supplier";
|
||||
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.bank_guarantee.bank_guarantee.get_voucher_details",
|
||||
args: {
|
||||
bank_guarantee_type: frm.doc.bg_type,
|
||||
reference_name: frm.doc.reference_docname,
|
||||
"bank_guarantee_type": frm.doc.bg_type,
|
||||
"reference_name": frm.doc.reference_docname
|
||||
},
|
||||
callback: function (r) {
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
if (r.message[party_field]) frm.set_value(party_field, r.message[party_field]);
|
||||
if (r.message.project) frm.set_value("project", r.message.project);
|
||||
if (r.message.grand_total) frm.set_value("amount", r.message.grand_total);
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
start_date: function (frm) {
|
||||
start_date: function(frm) {
|
||||
var end_date = frappe.datetime.add_days(cur_frm.doc.start_date, cur_frm.doc.validity - 1);
|
||||
cur_frm.set_value("end_date", end_date);
|
||||
},
|
||||
validity: function (frm) {
|
||||
validity: function(frm) {
|
||||
var end_date = frappe.datetime.add_days(cur_frm.doc.start_date, cur_frm.doc.validity - 1);
|
||||
cur_frm.set_value("end_date", end_date);
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
@@ -8,22 +8,21 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
||||
return {
|
||||
filters: {
|
||||
company: frm.doc.company,
|
||||
is_company_account: 1,
|
||||
'is_company_account': 1
|
||||
},
|
||||
};
|
||||
});
|
||||
let no_bank_transactions_text = `<div class="text-muted text-center">${__(
|
||||
"No Matching Bank Transactions Found"
|
||||
)}</div>`;
|
||||
let no_bank_transactions_text =
|
||||
`<div class="text-muted text-center">${__("No Matching Bank Transactions Found")}</div>`
|
||||
set_field_options("no_bank_transactions", no_bank_transactions_text);
|
||||
},
|
||||
|
||||
onload: function (frm) {
|
||||
// Set default filter dates
|
||||
let today = frappe.datetime.get_today();
|
||||
let today = frappe.datetime.get_today()
|
||||
frm.doc.bank_statement_from_date = frappe.datetime.add_months(today, -1);
|
||||
frm.doc.bank_statement_to_date = today;
|
||||
frm.trigger("bank_account");
|
||||
frm.trigger('bank_account');
|
||||
},
|
||||
|
||||
filter_by_reference_date: function (frm) {
|
||||
@@ -38,27 +37,34 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
||||
|
||||
refresh: function (frm) {
|
||||
frm.disable_save();
|
||||
frappe.require("bank-reconciliation-tool.bundle.js", () => frm.trigger("make_reconciliation_tool"));
|
||||
|
||||
frm.add_custom_button(__("Upload Bank Statement"), () =>
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.bank_statement_import.bank_statement_import.upload_bank_statement",
|
||||
args: {
|
||||
dt: frm.doc.doctype,
|
||||
dn: frm.doc.name,
|
||||
company: frm.doc.company,
|
||||
bank_account: frm.doc.bank_account,
|
||||
},
|
||||
callback: function (r) {
|
||||
if (!r.exc) {
|
||||
var doc = frappe.model.sync(r.message);
|
||||
frappe.set_route("Form", doc[0].doctype, doc[0].name);
|
||||
}
|
||||
},
|
||||
})
|
||||
frappe.require("bank-reconciliation-tool.bundle.js", () =>
|
||||
frm.trigger("make_reconciliation_tool")
|
||||
);
|
||||
|
||||
frm.add_custom_button(__("Auto Reconcile"), function () {
|
||||
frm.add_custom_button(__("Upload Bank Statement"), () =>
|
||||
frappe.call({
|
||||
method:
|
||||
"erpnext.accounts.doctype.bank_statement_import.bank_statement_import.upload_bank_statement",
|
||||
args: {
|
||||
dt: frm.doc.doctype,
|
||||
dn: frm.doc.name,
|
||||
company: frm.doc.company,
|
||||
bank_account: frm.doc.bank_account,
|
||||
},
|
||||
callback: function (r) {
|
||||
if (!r.exc) {
|
||||
var doc = frappe.model.sync(r.message);
|
||||
frappe.set_route(
|
||||
"Form",
|
||||
doc[0].doctype,
|
||||
doc[0].name
|
||||
);
|
||||
}
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
frm.add_custom_button(__('Auto Reconcile'), function() {
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.auto_reconcile_vouchers",
|
||||
args: {
|
||||
@@ -69,22 +75,33 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
||||
from_reference_date: frm.doc.from_reference_date,
|
||||
to_reference_date: frm.doc.to_reference_date,
|
||||
},
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
frm.add_custom_button(__("Get Unreconciled Entries"), function () {
|
||||
frm.add_custom_button(__('Get Unreconciled Entries'), function() {
|
||||
frm.trigger("make_reconciliation_tool");
|
||||
});
|
||||
frm.change_custom_button_type(__("Get Unreconciled Entries"), null, "primary");
|
||||
frm.change_custom_button_type(__('Get Unreconciled Entries'), null, 'primary');
|
||||
|
||||
},
|
||||
|
||||
bank_account: function (frm) {
|
||||
frappe.db.get_value("Bank Account", frm.doc.bank_account, "account", (r) => {
|
||||
frappe.db.get_value("Account", r.account, "account_currency", (r) => {
|
||||
frm.doc.account_currency = r.account_currency;
|
||||
frm.trigger("render_chart");
|
||||
});
|
||||
});
|
||||
frappe.db.get_value(
|
||||
"Bank Account",
|
||||
frm.doc.bank_account,
|
||||
"account",
|
||||
(r) => {
|
||||
frappe.db.get_value(
|
||||
"Account",
|
||||
r.account,
|
||||
"account_currency",
|
||||
(r) => {
|
||||
frm.doc.account_currency = r.account_currency;
|
||||
frm.trigger("render_chart");
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
frm.trigger("get_account_opening_balance");
|
||||
},
|
||||
|
||||
@@ -103,7 +120,11 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
||||
) {
|
||||
frm.trigger("render_chart");
|
||||
frm.trigger("render");
|
||||
frappe.utils.scroll_to(frm.get_field("reconciliation_tool_cards").$wrapper, true, 30);
|
||||
frappe.utils.scroll_to(
|
||||
frm.get_field("reconciliation_tool_cards").$wrapper,
|
||||
true,
|
||||
30
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -112,10 +133,11 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
||||
get_account_opening_balance(frm) {
|
||||
if (frm.doc.bank_account && frm.doc.bank_statement_from_date) {
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance",
|
||||
method:
|
||||
"erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance",
|
||||
args: {
|
||||
bank_account: frm.doc.bank_account,
|
||||
till_date: frappe.datetime.add_days(frm.doc.bank_statement_from_date, -1),
|
||||
till_date: frappe.datetime.add_days(frm.doc.bank_statement_from_date, -1)
|
||||
},
|
||||
callback: (response) => {
|
||||
frm.set_value("account_opening_balance", response.message);
|
||||
@@ -127,7 +149,8 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
||||
get_cleared_balance(frm) {
|
||||
if (frm.doc.bank_account && frm.doc.bank_statement_to_date) {
|
||||
return frappe.call({
|
||||
method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance",
|
||||
method:
|
||||
"erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance",
|
||||
args: {
|
||||
bank_account: frm.doc.bank_account,
|
||||
till_date: frm.doc.bank_statement_to_date,
|
||||
@@ -140,30 +163,41 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
||||
},
|
||||
|
||||
render_chart(frm) {
|
||||
frm.cards_manager = new erpnext.accounts.bank_reconciliation.NumberCardManager({
|
||||
$reconciliation_tool_cards: frm.get_field("reconciliation_tool_cards").$wrapper,
|
||||
bank_statement_closing_balance: frm.doc.bank_statement_closing_balance,
|
||||
cleared_balance: frm.cleared_balance,
|
||||
currency: frm.doc.account_currency,
|
||||
});
|
||||
frm.cards_manager = new erpnext.accounts.bank_reconciliation.NumberCardManager(
|
||||
{
|
||||
$reconciliation_tool_cards: frm.get_field(
|
||||
"reconciliation_tool_cards"
|
||||
).$wrapper,
|
||||
bank_statement_closing_balance:
|
||||
frm.doc.bank_statement_closing_balance,
|
||||
cleared_balance: frm.cleared_balance,
|
||||
currency: frm.doc.account_currency,
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
render(frm) {
|
||||
if (frm.doc.bank_account) {
|
||||
frm.bank_reconciliation_data_table_manager =
|
||||
new erpnext.accounts.bank_reconciliation.DataTableManager({
|
||||
frm.bank_reconciliation_data_table_manager = new erpnext.accounts.bank_reconciliation.DataTableManager(
|
||||
{
|
||||
company: frm.doc.company,
|
||||
bank_account: frm.doc.bank_account,
|
||||
$reconciliation_tool_dt: frm.get_field("reconciliation_tool_dt").$wrapper,
|
||||
$no_bank_transactions: frm.get_field("no_bank_transactions").$wrapper,
|
||||
$reconciliation_tool_dt: frm.get_field(
|
||||
"reconciliation_tool_dt"
|
||||
).$wrapper,
|
||||
$no_bank_transactions: frm.get_field(
|
||||
"no_bank_transactions"
|
||||
).$wrapper,
|
||||
bank_statement_from_date: frm.doc.bank_statement_from_date,
|
||||
bank_statement_to_date: frm.doc.bank_statement_to_date,
|
||||
filter_by_reference_date: frm.doc.filter_by_reference_date,
|
||||
from_reference_date: frm.doc.from_reference_date,
|
||||
to_reference_date: frm.doc.to_reference_date,
|
||||
bank_statement_closing_balance: frm.doc.bank_statement_closing_balance,
|
||||
bank_statement_closing_balance:
|
||||
frm.doc.bank_statement_closing_balance,
|
||||
cards_manager: frm.cards_manager,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@ from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.query_builder.custom import ConstantColumn
|
||||
from frappe.utils import cint, flt
|
||||
from pypika.terms import Parameter
|
||||
|
||||
from erpnext import get_default_cost_center
|
||||
from erpnext.accounts.doctype.bank_transaction.bank_transaction import get_total_allocated_amount
|
||||
@@ -81,7 +82,9 @@ def get_bank_transactions(bank_account, from_date=None, to_date=None):
|
||||
def get_account_balance(bank_account, till_date):
|
||||
# returns account balance till the specified date
|
||||
account = frappe.db.get_value("Bank Account", bank_account, "account")
|
||||
filters = frappe._dict({"account": account, "report_date": till_date, "include_pos_transactions": 1})
|
||||
filters = frappe._dict(
|
||||
{"account": account, "report_date": till_date, "include_pos_transactions": 1}
|
||||
)
|
||||
data = get_entries(filters)
|
||||
|
||||
balance_as_per_system = get_balance_on(filters["account"], filters["report_date"])
|
||||
@@ -94,7 +97,10 @@ def get_account_balance(bank_account, till_date):
|
||||
amounts_not_reflected_in_system = get_amounts_not_reflected_in_system(filters)
|
||||
|
||||
bank_bal = (
|
||||
flt(balance_as_per_system) - flt(total_debit) + flt(total_credit) + amounts_not_reflected_in_system
|
||||
flt(balance_as_per_system)
|
||||
- flt(total_debit)
|
||||
+ flt(total_credit)
|
||||
+ amounts_not_reflected_in_system
|
||||
)
|
||||
|
||||
return bank_bal
|
||||
@@ -503,18 +509,6 @@ def check_matching(
|
||||
to_reference_date,
|
||||
):
|
||||
exact_match = True if "exact_match" in document_types else False
|
||||
|
||||
common_filters = frappe._dict(
|
||||
{
|
||||
"amount": transaction.unallocated_amount,
|
||||
"payment_type": "Receive" if transaction.deposit > 0.0 else "Pay",
|
||||
"reference_no": transaction.reference_number,
|
||||
"party_type": transaction.party_type,
|
||||
"party": transaction.party,
|
||||
"bank_account": bank_account,
|
||||
}
|
||||
)
|
||||
|
||||
queries = get_queries(
|
||||
bank_account,
|
||||
company,
|
||||
@@ -526,14 +520,24 @@ def check_matching(
|
||||
from_reference_date,
|
||||
to_reference_date,
|
||||
exact_match,
|
||||
common_filters,
|
||||
)
|
||||
|
||||
filters = {
|
||||
"amount": transaction.unallocated_amount,
|
||||
"payment_type": "Receive" if transaction.deposit > 0.0 else "Pay",
|
||||
"reference_no": transaction.reference_number,
|
||||
"party_type": transaction.party_type,
|
||||
"party": transaction.party,
|
||||
"bank_account": bank_account,
|
||||
}
|
||||
|
||||
matching_vouchers = []
|
||||
for query in queries:
|
||||
matching_vouchers.extend(query.run(as_dict=True))
|
||||
matching_vouchers.extend(frappe.db.sql(query, filters, as_dict=True))
|
||||
|
||||
return sorted(matching_vouchers, key=lambda x: x["rank"], reverse=True) if matching_vouchers else []
|
||||
return (
|
||||
sorted(matching_vouchers, key=lambda x: x["rank"], reverse=True) if matching_vouchers else []
|
||||
)
|
||||
|
||||
|
||||
def get_queries(
|
||||
@@ -547,7 +551,6 @@ def get_queries(
|
||||
from_reference_date,
|
||||
to_reference_date,
|
||||
exact_match,
|
||||
common_filters,
|
||||
):
|
||||
# get queries to get matching vouchers
|
||||
account_from_to = "paid_to" if transaction.deposit > 0.0 else "paid_from"
|
||||
@@ -568,7 +571,6 @@ def get_queries(
|
||||
filter_by_reference_date,
|
||||
from_reference_date,
|
||||
to_reference_date,
|
||||
common_filters,
|
||||
)
|
||||
or []
|
||||
)
|
||||
@@ -588,7 +590,6 @@ def get_matching_queries(
|
||||
filter_by_reference_date,
|
||||
from_reference_date,
|
||||
to_reference_date,
|
||||
common_filters,
|
||||
):
|
||||
queries = []
|
||||
currency = get_account_currency(bank_account)
|
||||
@@ -603,7 +604,6 @@ def get_matching_queries(
|
||||
filter_by_reference_date,
|
||||
from_reference_date,
|
||||
to_reference_date,
|
||||
common_filters,
|
||||
)
|
||||
queries.append(query)
|
||||
|
||||
@@ -616,17 +616,16 @@ def get_matching_queries(
|
||||
filter_by_reference_date,
|
||||
from_reference_date,
|
||||
to_reference_date,
|
||||
common_filters,
|
||||
)
|
||||
queries.append(query)
|
||||
|
||||
if transaction.deposit > 0.0 and "sales_invoice" in document_types:
|
||||
query = get_si_matching_query(exact_match, currency, common_filters)
|
||||
query = get_si_matching_query(exact_match, currency)
|
||||
queries.append(query)
|
||||
|
||||
if transaction.withdrawal > 0.0:
|
||||
if "purchase_invoice" in document_types:
|
||||
query = get_pi_matching_query(exact_match, currency, common_filters)
|
||||
query = get_pi_matching_query(exact_match, currency)
|
||||
queries.append(query)
|
||||
|
||||
if "bank_transaction" in document_types:
|
||||
@@ -647,13 +646,17 @@ def get_bt_matching_query(exact_match, transaction):
|
||||
amount_rank = frappe.qb.terms.Case().when(amount_equality, 1).else_(0)
|
||||
amount_condition = amount_equality if exact_match else getattr(bt, field) > 0.0
|
||||
|
||||
ref_rank = frappe.qb.terms.Case().when(bt.reference_number == transaction.reference_number, 1).else_(0)
|
||||
ref_rank = (
|
||||
frappe.qb.terms.Case().when(bt.reference_number == transaction.reference_number, 1).else_(0)
|
||||
)
|
||||
unallocated_rank = (
|
||||
frappe.qb.terms.Case().when(bt.unallocated_amount == transaction.unallocated_amount, 1).else_(0)
|
||||
)
|
||||
|
||||
party_condition = (
|
||||
(bt.party_type == transaction.party_type) & (bt.party == transaction.party) & bt.party.isnotnull()
|
||||
(bt.party_type == transaction.party_type)
|
||||
& (bt.party == transaction.party)
|
||||
& bt.party.isnotnull()
|
||||
)
|
||||
party_rank = frappe.qb.terms.Case().when(party_condition, 1).else_(0)
|
||||
|
||||
@@ -677,7 +680,7 @@ def get_bt_matching_query(exact_match, transaction):
|
||||
.where(amount_condition)
|
||||
.where(bt.docstatus == 1)
|
||||
)
|
||||
return query
|
||||
return str(query)
|
||||
|
||||
|
||||
def get_pe_matching_query(
|
||||
@@ -689,7 +692,6 @@ def get_pe_matching_query(
|
||||
filter_by_reference_date,
|
||||
from_reference_date,
|
||||
to_reference_date,
|
||||
common_filters,
|
||||
):
|
||||
# get matching payment entries query
|
||||
to_from = "to" if transaction.deposit > 0.0 else "from"
|
||||
@@ -705,7 +707,9 @@ def get_pe_matching_query(
|
||||
amount_condition = amount_equality if exact_match else pe.paid_amount > 0.0
|
||||
|
||||
party_condition = (
|
||||
(pe.party_type == transaction.party_type) & (pe.party == transaction.party) & pe.party.isnotnull()
|
||||
(pe.party_type == transaction.party_type)
|
||||
& (pe.party == transaction.party)
|
||||
& pe.party.isnotnull()
|
||||
)
|
||||
party_rank = frappe.qb.terms.Case().when(party_condition, 1).else_(0)
|
||||
|
||||
@@ -730,16 +734,16 @@ def get_pe_matching_query(
|
||||
.where(pe.docstatus == 1)
|
||||
.where(pe.payment_type.isin([payment_type, "Internal Transfer"]))
|
||||
.where(pe.clearance_date.isnull())
|
||||
.where(getattr(pe, account_from_to) == common_filters.bank_account)
|
||||
.where(getattr(pe, account_from_to) == Parameter("%(bank_account)s"))
|
||||
.where(amount_condition)
|
||||
.where(filter_by_date)
|
||||
.orderby(pe.reference_date if cint(filter_by_reference_date) else pe.posting_date)
|
||||
)
|
||||
|
||||
if frappe.flags.auto_reconcile_vouchers is True:
|
||||
if frappe.flags.auto_reconcile_vouchers == True:
|
||||
query = query.where(ref_condition)
|
||||
|
||||
return query
|
||||
return str(query)
|
||||
|
||||
|
||||
def get_je_matching_query(
|
||||
@@ -750,7 +754,6 @@ def get_je_matching_query(
|
||||
filter_by_reference_date,
|
||||
from_reference_date,
|
||||
to_reference_date,
|
||||
common_filters,
|
||||
):
|
||||
# get matching journal entry query
|
||||
# We have mapping at the bank level
|
||||
@@ -790,29 +793,29 @@ def get_je_matching_query(
|
||||
.where(je.docstatus == 1)
|
||||
.where(je.voucher_type != "Opening Entry")
|
||||
.where(je.clearance_date.isnull())
|
||||
.where(jea.account == common_filters.bank_account)
|
||||
.where(jea.account == Parameter("%(bank_account)s"))
|
||||
.where(amount_equality if exact_match else getattr(jea, amount_field) > 0.0)
|
||||
.where(je.docstatus == 1)
|
||||
.where(filter_by_date)
|
||||
.orderby(je.cheque_date if cint(filter_by_reference_date) else je.posting_date)
|
||||
)
|
||||
|
||||
if frappe.flags.auto_reconcile_vouchers is True:
|
||||
if frappe.flags.auto_reconcile_vouchers == True:
|
||||
query = query.where(ref_condition)
|
||||
|
||||
return query
|
||||
return str(query)
|
||||
|
||||
|
||||
def get_si_matching_query(exact_match, currency, common_filters):
|
||||
def get_si_matching_query(exact_match, currency):
|
||||
# get matching sales invoice query
|
||||
si = frappe.qb.DocType("Sales Invoice")
|
||||
sip = frappe.qb.DocType("Sales Invoice Payment")
|
||||
|
||||
amount_equality = sip.amount == common_filters.amount
|
||||
amount_equality = sip.amount == Parameter("%(amount)s")
|
||||
amount_rank = frappe.qb.terms.Case().when(amount_equality, 1).else_(0)
|
||||
amount_condition = amount_equality if exact_match else sip.amount > 0.0
|
||||
|
||||
party_condition = si.customer == common_filters.party
|
||||
party_condition = si.customer == Parameter("%(party)s")
|
||||
party_rank = frappe.qb.terms.Case().when(party_condition, 1).else_(0)
|
||||
|
||||
query = (
|
||||
@@ -833,23 +836,23 @@ def get_si_matching_query(exact_match, currency, common_filters):
|
||||
)
|
||||
.where(si.docstatus == 1)
|
||||
.where(sip.clearance_date.isnull())
|
||||
.where(sip.account == common_filters.bank_account)
|
||||
.where(sip.account == Parameter("%(bank_account)s"))
|
||||
.where(amount_condition)
|
||||
.where(si.currency == currency)
|
||||
)
|
||||
|
||||
return query
|
||||
return str(query)
|
||||
|
||||
|
||||
def get_pi_matching_query(exact_match, currency, common_filters):
|
||||
def get_pi_matching_query(exact_match, currency):
|
||||
# get matching purchase invoice query when they are also used as payment entries (is_paid)
|
||||
purchase_invoice = frappe.qb.DocType("Purchase Invoice")
|
||||
|
||||
amount_equality = purchase_invoice.paid_amount == common_filters.amount
|
||||
amount_equality = purchase_invoice.paid_amount == Parameter("%(amount)s")
|
||||
amount_rank = frappe.qb.terms.Case().when(amount_equality, 1).else_(0)
|
||||
amount_condition = amount_equality if exact_match else purchase_invoice.paid_amount > 0.0
|
||||
|
||||
party_condition = purchase_invoice.supplier == common_filters.party
|
||||
party_condition = purchase_invoice.supplier == Parameter("%(party)s")
|
||||
party_rank = frappe.qb.terms.Case().when(party_condition, 1).else_(0)
|
||||
|
||||
query = (
|
||||
@@ -869,9 +872,9 @@ def get_pi_matching_query(exact_match, currency, common_filters):
|
||||
.where(purchase_invoice.docstatus == 1)
|
||||
.where(purchase_invoice.is_paid == 1)
|
||||
.where(purchase_invoice.clearance_date.isnull())
|
||||
.where(purchase_invoice.cash_bank_account == common_filters.bank_account)
|
||||
.where(purchase_invoice.cash_bank_account == Parameter("%(bank_account)s"))
|
||||
.where(amount_condition)
|
||||
.where(purchase_invoice.currency == currency)
|
||||
)
|
||||
|
||||
return query
|
||||
return str(query)
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe import qb
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
from frappe.utils import add_days, today
|
||||
from frappe.tests.utils import FrappeTestCase, change_settings
|
||||
from frappe.utils import add_days, flt, getdate, today
|
||||
|
||||
from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import (
|
||||
auto_reconcile_vouchers,
|
||||
@@ -21,7 +22,7 @@ class TestBankReconciliationTool(AccountsTestMixin, FrappeTestCase):
|
||||
self.create_customer()
|
||||
self.clear_old_entries()
|
||||
bank_dt = qb.DocType("Bank")
|
||||
qb.from_(bank_dt).delete().where(bank_dt.name == "HDFC").run()
|
||||
q = qb.from_(bank_dt).delete().where(bank_dt.name == "HDFC").run()
|
||||
self.create_bank_account()
|
||||
|
||||
def tearDown(self):
|
||||
@@ -75,7 +76,6 @@ class TestBankReconciliationTool(AccountsTestMixin, FrappeTestCase):
|
||||
"deposit": 100,
|
||||
"bank_account": self.bank_account,
|
||||
"reference_number": "123",
|
||||
"currency": "INR",
|
||||
}
|
||||
)
|
||||
.save()
|
||||
|
||||
@@ -2,14 +2,26 @@
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Bank Statement Import", {
|
||||
onload(frm) {
|
||||
frm.set_query("bank_account", function (doc) {
|
||||
return {
|
||||
filters: {
|
||||
company: doc.company,
|
||||
},
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
setup(frm) {
|
||||
frappe.realtime.on("data_import_refresh", ({ data_import }) => {
|
||||
frm.import_in_progress = false;
|
||||
if (data_import !== frm.doc.name) return;
|
||||
frappe.model.clear_doc("Bank Statement Import", frm.doc.name);
|
||||
frappe.model.with_doc("Bank Statement Import", frm.doc.name).then(() => {
|
||||
frm.refresh();
|
||||
});
|
||||
frappe.model
|
||||
.with_doc("Bank Statement Import", frm.doc.name)
|
||||
.then(() => {
|
||||
frm.refresh();
|
||||
});
|
||||
});
|
||||
frappe.realtime.on("data_import_progress", (data) => {
|
||||
frm.import_in_progress = true;
|
||||
@@ -36,9 +48,20 @@ frappe.ui.form.on("Bank Statement Import", {
|
||||
: __("Updating {0} of {1}, {2}", message_args);
|
||||
}
|
||||
if (data.skipping) {
|
||||
message = __("Skipping {0} of {1}, {2}", [data.current, data.total, eta_message]);
|
||||
message = __(
|
||||
"Skipping {0} of {1}, {2}",
|
||||
[
|
||||
data.current,
|
||||
data.total,
|
||||
eta_message,
|
||||
]
|
||||
);
|
||||
}
|
||||
frm.dashboard.show_progress(__("Import Progress"), percent, message);
|
||||
frm.dashboard.show_progress(
|
||||
__("Import Progress"),
|
||||
percent,
|
||||
message
|
||||
);
|
||||
frm.page.set_indicator(__("In Progress"), "orange");
|
||||
|
||||
// hide progress when complete
|
||||
@@ -80,12 +103,15 @@ frappe.ui.form.on("Bank Statement Import", {
|
||||
frm.trigger("show_report_error_button");
|
||||
|
||||
if (frm.doc.status === "Partial Success") {
|
||||
frm.add_custom_button(__("Export Errored Rows"), () => frm.trigger("export_errored_rows"));
|
||||
frm.add_custom_button(__("Export Errored Rows"), () =>
|
||||
frm.trigger("export_errored_rows")
|
||||
);
|
||||
}
|
||||
|
||||
if (frm.doc.status.includes("Success")) {
|
||||
frm.add_custom_button(__("Go to {0} List", [__(frm.doc.reference_doctype)]), () =>
|
||||
frappe.set_route("List", frm.doc.reference_doctype)
|
||||
frm.add_custom_button(
|
||||
__("Go to {0} List", [__(frm.doc.reference_doctype)]),
|
||||
() => frappe.set_route("List", frm.doc.reference_doctype)
|
||||
);
|
||||
}
|
||||
},
|
||||
@@ -102,8 +128,13 @@ frappe.ui.form.on("Bank Statement Import", {
|
||||
frm.disable_save();
|
||||
if (frm.doc.status !== "Success") {
|
||||
if (!frm.is_new() && frm.has_import_file()) {
|
||||
let label = frm.doc.status === "Pending" ? __("Start Import") : __("Retry");
|
||||
frm.page.set_primary_action(label, () => frm.events.start_import(frm));
|
||||
let label =
|
||||
frm.doc.status === "Pending"
|
||||
? __("Start Import")
|
||||
: __("Retry");
|
||||
frm.page.set_primary_action(label, () =>
|
||||
frm.events.start_import(frm)
|
||||
);
|
||||
} else {
|
||||
frm.page.set_primary_action(__("Save"), () => frm.save());
|
||||
}
|
||||
@@ -145,24 +176,24 @@ frappe.ui.form.on("Bank Statement Import", {
|
||||
message =
|
||||
successful_records.length > 1
|
||||
? __(
|
||||
"Successfully imported {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.",
|
||||
message_args
|
||||
)
|
||||
"Successfully imported {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.",
|
||||
message_args
|
||||
)
|
||||
: __(
|
||||
"Successfully imported {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.",
|
||||
message_args
|
||||
);
|
||||
"Successfully imported {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.",
|
||||
message_args
|
||||
);
|
||||
} else {
|
||||
message =
|
||||
successful_records.length > 1
|
||||
? __(
|
||||
"Successfully updated {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.",
|
||||
message_args
|
||||
)
|
||||
"Successfully updated {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.",
|
||||
message_args
|
||||
)
|
||||
: __(
|
||||
"Successfully updated {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.",
|
||||
message_args
|
||||
);
|
||||
"Successfully updated {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.",
|
||||
message_args
|
||||
);
|
||||
}
|
||||
}
|
||||
frm.dashboard.set_headline(message);
|
||||
@@ -205,7 +236,8 @@ frappe.ui.form.on("Bank Statement Import", {
|
||||
},
|
||||
|
||||
download_template() {
|
||||
let method = "/api/method/frappe.core.doctype.data_import.data_import.download_template";
|
||||
let method =
|
||||
"/api/method/frappe.core.doctype.data_import.data_import.download_template";
|
||||
|
||||
open_url_post(method, {
|
||||
doctype: "Bank Transaction",
|
||||
@@ -218,7 +250,7 @@ frappe.ui.form.on("Bank Statement Import", {
|
||||
"description",
|
||||
"reference_number",
|
||||
"bank_account",
|
||||
"currency",
|
||||
"currency"
|
||||
],
|
||||
},
|
||||
});
|
||||
@@ -289,7 +321,10 @@ frappe.ui.form.on("Bank Statement Import", {
|
||||
show_import_preview(frm, preview_data) {
|
||||
let import_log = JSON.parse(frm.doc.statement_import_log || "[]");
|
||||
|
||||
if (frm.import_preview && frm.import_preview.doctype === frm.doc.reference_doctype) {
|
||||
if (
|
||||
frm.import_preview &&
|
||||
frm.import_preview.doctype === frm.doc.reference_doctype
|
||||
) {
|
||||
frm.import_preview.preview_data = preview_data;
|
||||
frm.import_preview.import_log = import_log;
|
||||
frm.import_preview.refresh();
|
||||
@@ -305,10 +340,19 @@ frappe.ui.form.on("Bank Statement Import", {
|
||||
frm,
|
||||
events: {
|
||||
remap_column(changed_map) {
|
||||
let template_options = JSON.parse(frm.doc.template_options || "{}");
|
||||
template_options.column_to_field_map = template_options.column_to_field_map || {};
|
||||
Object.assign(template_options.column_to_field_map, changed_map);
|
||||
frm.set_value("template_options", JSON.stringify(template_options));
|
||||
let template_options = JSON.parse(
|
||||
frm.doc.template_options || "{}"
|
||||
);
|
||||
template_options.column_to_field_map =
|
||||
template_options.column_to_field_map || {};
|
||||
Object.assign(
|
||||
template_options.column_to_field_map,
|
||||
changed_map
|
||||
);
|
||||
frm.set_value(
|
||||
"template_options",
|
||||
JSON.stringify(template_options)
|
||||
);
|
||||
frm.save().then(() => frm.trigger("import_file"));
|
||||
},
|
||||
},
|
||||
@@ -342,7 +386,8 @@ frappe.ui.form.on("Bank Statement Import", {
|
||||
let other_warnings = [];
|
||||
for (let warning of warnings) {
|
||||
if (warning.row) {
|
||||
warnings_by_row[warning.row] = warnings_by_row[warning.row] || [];
|
||||
warnings_by_row[warning.row] =
|
||||
warnings_by_row[warning.row] || [];
|
||||
warnings_by_row[warning.row].push(warning);
|
||||
} else {
|
||||
other_warnings.push(warning);
|
||||
@@ -357,7 +402,9 @@ frappe.ui.form.on("Bank Statement Import", {
|
||||
if (w.field) {
|
||||
let label =
|
||||
w.field.label +
|
||||
(w.field.parent !== frm.doc.reference_doctype ? ` (${w.field.parent})` : "");
|
||||
(w.field.parent !== frm.doc.reference_doctype
|
||||
? ` (${w.field.parent})`
|
||||
: "");
|
||||
return `<li>${label}: ${w.message}</li>`;
|
||||
}
|
||||
return `<li>${w.message}</li>`;
|
||||
@@ -376,9 +423,10 @@ frappe.ui.form.on("Bank Statement Import", {
|
||||
.map((warning) => {
|
||||
let header = "";
|
||||
if (warning.col) {
|
||||
let column_number = `<span class="text-uppercase">${__("Column {0}", [
|
||||
warning.col,
|
||||
])}</span>`;
|
||||
let column_number = `<span class="text-uppercase">${__(
|
||||
"Column {0}",
|
||||
[warning.col]
|
||||
)}</span>`;
|
||||
let column_header = columns[warning.col].header_title;
|
||||
header = `${column_number} (${column_header})`;
|
||||
}
|
||||
@@ -417,28 +465,36 @@ frappe.ui.form.on("Bank Statement Import", {
|
||||
let html = "";
|
||||
if (log.success) {
|
||||
if (frm.doc.import_type === "Insert New Records") {
|
||||
html = __("Successfully imported {0}", [
|
||||
`<span class="underline">${frappe.utils.get_form_link(
|
||||
frm.doc.reference_doctype,
|
||||
log.docname,
|
||||
true
|
||||
)}<span>`,
|
||||
]);
|
||||
html = __(
|
||||
"Successfully imported {0}", [
|
||||
`<span class="underline">${frappe.utils.get_form_link(
|
||||
frm.doc.reference_doctype,
|
||||
log.docname,
|
||||
true
|
||||
)}<span>`,
|
||||
]
|
||||
);
|
||||
} else {
|
||||
html = __("Successfully updated {0}", [
|
||||
`<span class="underline">${frappe.utils.get_form_link(
|
||||
frm.doc.reference_doctype,
|
||||
log.docname,
|
||||
true
|
||||
)}<span>`,
|
||||
]);
|
||||
html = __(
|
||||
"Successfully updated {0}", [
|
||||
`<span class="underline">${frappe.utils.get_form_link(
|
||||
frm.doc.reference_doctype,
|
||||
log.docname,
|
||||
true
|
||||
)}<span>`,
|
||||
]
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let messages = log.messages
|
||||
.map(JSON.parse)
|
||||
.map((m) => {
|
||||
let title = m.title ? `<strong>${m.title}</strong>` : "";
|
||||
let message = m.message ? `<div>${m.message}</div>` : "";
|
||||
let title = m.title
|
||||
? `<strong>${m.title}</strong>`
|
||||
: "";
|
||||
let message = m.message
|
||||
? `<div>${m.message}</div>`
|
||||
: "";
|
||||
return title + message;
|
||||
})
|
||||
.join("");
|
||||
|
||||
@@ -45,7 +45,7 @@ class BankStatementImport(DataImport):
|
||||
# end: auto-generated types
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
super(BankStatementImport, self).__init__(*args, **kwargs)
|
||||
|
||||
def validate(self):
|
||||
doc_before_save = self.get_doc_before_save()
|
||||
@@ -54,6 +54,7 @@ class BankStatementImport(DataImport):
|
||||
or (doc_before_save and doc_before_save.import_file != self.import_file)
|
||||
or (doc_before_save and doc_before_save.google_sheets_url != self.google_sheets_url)
|
||||
):
|
||||
|
||||
template_options_dict = {}
|
||||
column_to_field_map = {}
|
||||
bank = frappe.get_doc("Bank", self.bank)
|
||||
@@ -68,6 +69,7 @@ class BankStatementImport(DataImport):
|
||||
self.validate_google_sheets_url()
|
||||
|
||||
def start_import(self):
|
||||
|
||||
preview = frappe.get_doc("Bank Statement Import", self.name).get_preview_from_template(
|
||||
self.import_file, self.google_sheets_url
|
||||
)
|
||||
@@ -123,7 +125,7 @@ def download_errored_template(data_import_name):
|
||||
def parse_data_from_template(raw_data):
|
||||
data = []
|
||||
|
||||
for _i, row in enumerate(raw_data):
|
||||
for i, row in enumerate(raw_data):
|
||||
if all(v in INVALID_VALUES for v in row):
|
||||
# empty row
|
||||
continue
|
||||
@@ -133,7 +135,9 @@ def parse_data_from_template(raw_data):
|
||||
return data
|
||||
|
||||
|
||||
def start_import(data_import, bank_account, import_file_path, google_sheets_url, bank, template_options):
|
||||
def start_import(
|
||||
data_import, bank_account, import_file_path, google_sheets_url, bank, template_options
|
||||
):
|
||||
"""This method runs in background job"""
|
||||
|
||||
update_mapping_db(bank, template_options)
|
||||
@@ -144,9 +148,6 @@ def start_import(data_import, bank_account, import_file_path, google_sheets_url,
|
||||
import_file = ImportFile("Bank Transaction", file=file, import_type="Insert New Records")
|
||||
|
||||
data = parse_data_from_template(import_file.raw_data)
|
||||
# Importer expects 'Data Import' class, which has 'payload_count' attribute
|
||||
if not data_import.get("payload_count"):
|
||||
data_import.payload_count = len(data) - 1
|
||||
|
||||
if import_file_path:
|
||||
add_bank_account(data, bank_account)
|
||||
|
||||
@@ -1,34 +1,36 @@
|
||||
let imports_in_progress = [];
|
||||
|
||||
frappe.listview_settings["Bank Statement Import"] = {
|
||||
frappe.listview_settings['Bank Statement Import'] = {
|
||||
onload(listview) {
|
||||
frappe.realtime.on("data_import_progress", (data) => {
|
||||
frappe.realtime.on('data_import_progress', data => {
|
||||
if (!imports_in_progress.includes(data.data_import)) {
|
||||
imports_in_progress.push(data.data_import);
|
||||
}
|
||||
});
|
||||
frappe.realtime.on("data_import_refresh", (data) => {
|
||||
imports_in_progress = imports_in_progress.filter((d) => d !== data.data_import);
|
||||
frappe.realtime.on('data_import_refresh', data => {
|
||||
imports_in_progress = imports_in_progress.filter(
|
||||
d => d !== data.data_import
|
||||
);
|
||||
listview.refresh();
|
||||
});
|
||||
},
|
||||
get_indicator: function (doc) {
|
||||
get_indicator: function(doc) {
|
||||
var colors = {
|
||||
Pending: "orange",
|
||||
"Not Started": "orange",
|
||||
"Partial Success": "orange",
|
||||
Success: "green",
|
||||
"In Progress": "orange",
|
||||
Error: "red",
|
||||
'Pending': 'orange',
|
||||
'Not Started': 'orange',
|
||||
'Partial Success': 'orange',
|
||||
'Success': 'green',
|
||||
'In Progress': 'orange',
|
||||
'Error': 'red'
|
||||
};
|
||||
let status = doc.status;
|
||||
if (imports_in_progress.includes(doc.name)) {
|
||||
status = "In Progress";
|
||||
status = 'In Progress';
|
||||
}
|
||||
if (status == "Pending") {
|
||||
status = "Not Started";
|
||||
if (status == 'Pending') {
|
||||
status = 'Not Started';
|
||||
}
|
||||
return [__(status), colors[status], "status,=," + doc.status];
|
||||
return [__(status), colors[status], 'status,=,' + doc.status];
|
||||
},
|
||||
hide_name_column: true,
|
||||
hide_name_column: true
|
||||
};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from typing import Tuple, Union
|
||||
|
||||
import frappe
|
||||
from frappe.utils import flt
|
||||
from rapidfuzz import fuzz, process
|
||||
@@ -17,7 +19,7 @@ class AutoMatchParty:
|
||||
def get(self, key):
|
||||
return self.__dict__.get(key, None)
|
||||
|
||||
def match(self) -> tuple | None:
|
||||
def match(self) -> Union[Tuple, None]:
|
||||
result = None
|
||||
result = AutoMatchbyAccountIBAN(
|
||||
bank_party_account_number=self.bank_party_account_number,
|
||||
@@ -48,7 +50,7 @@ class AutoMatchbyAccountIBAN:
|
||||
result = self.match_account_in_party()
|
||||
return result
|
||||
|
||||
def match_account_in_party(self) -> tuple | None:
|
||||
def match_account_in_party(self) -> Union[Tuple, None]:
|
||||
"""Check if there is a IBAN/Account No. match in Customer/Supplier/Employee"""
|
||||
result = None
|
||||
parties = get_parties_in_order(self.deposit)
|
||||
@@ -95,7 +97,7 @@ class AutoMatchbyPartyNameDescription:
|
||||
def get(self, key):
|
||||
return self.__dict__.get(key, None)
|
||||
|
||||
def match(self) -> tuple | None:
|
||||
def match(self) -> Union[Tuple, None]:
|
||||
# fuzzy search by customer/supplier & employee
|
||||
if not (self.bank_party_name or self.description):
|
||||
return None
|
||||
@@ -103,14 +105,15 @@ class AutoMatchbyPartyNameDescription:
|
||||
result = self.match_party_name_desc_in_party()
|
||||
return result
|
||||
|
||||
def match_party_name_desc_in_party(self) -> tuple | None:
|
||||
def match_party_name_desc_in_party(self) -> Union[Tuple, None]:
|
||||
"""Fuzzy search party name and/or description against parties in the system"""
|
||||
result = None
|
||||
parties = get_parties_in_order(self.deposit)
|
||||
|
||||
for party in parties:
|
||||
filters = {"status": "Active"} if party == "Employee" else {"disabled": 0}
|
||||
names = frappe.get_all(party, filters=filters, pluck=party.lower() + "_name")
|
||||
field = party.lower() + "_name"
|
||||
names = frappe.get_all(party, filters=filters, fields=[f"{field} as party_name", "name"])
|
||||
|
||||
for field in ["bank_party_name", "description"]:
|
||||
if not self.get(field):
|
||||
@@ -127,9 +130,13 @@ class AutoMatchbyPartyNameDescription:
|
||||
|
||||
return result
|
||||
|
||||
def fuzzy_search_and_return_result(self, party, names, field) -> tuple | None:
|
||||
def fuzzy_search_and_return_result(self, party, names, field) -> Union[Tuple, None]:
|
||||
skip = False
|
||||
result = process.extract(query=self.get(field), choices=names, scorer=fuzz.token_set_ratio)
|
||||
result = process.extract(
|
||||
query=self.get(field),
|
||||
choices={row.get("name"): row.get("party_name") for row in names},
|
||||
scorer=fuzz.token_set_ratio,
|
||||
)
|
||||
party_name, skip = self.process_fuzzy_result(result)
|
||||
|
||||
if not party_name:
|
||||
@@ -140,21 +147,21 @@ class AutoMatchbyPartyNameDescription:
|
||||
party_name,
|
||||
), skip
|
||||
|
||||
def process_fuzzy_result(self, result: list | None):
|
||||
def process_fuzzy_result(self, result: Union[list, None]):
|
||||
"""
|
||||
If there are multiple valid close matches return None as result may be faulty.
|
||||
Return the result only if one accurate match stands out.
|
||||
|
||||
Returns: Result, Skip (whether or not to discontinue matching)
|
||||
"""
|
||||
PARTY, SCORE, CUTOFF = 0, 1, 80
|
||||
SCORE, PARTY_ID, CUTOFF = 1, 2, 80
|
||||
|
||||
if not result or not len(result):
|
||||
return None, False
|
||||
|
||||
first_result = result[0]
|
||||
if len(result) == 1:
|
||||
return (first_result[PARTY] if first_result[SCORE] > CUTOFF else None), True
|
||||
return (first_result[PARTY_ID] if first_result[SCORE] > CUTOFF else None), True
|
||||
|
||||
second_result = result[1]
|
||||
if first_result[SCORE] > CUTOFF:
|
||||
@@ -163,7 +170,7 @@ class AutoMatchbyPartyNameDescription:
|
||||
if first_result[SCORE] == second_result[SCORE]:
|
||||
return None, True
|
||||
|
||||
return first_result[PARTY], True
|
||||
return first_result[PARTY_ID], True
|
||||
else:
|
||||
return None, False
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
frappe.ui.form.on("Bank Transaction", {
|
||||
onload(frm) {
|
||||
frm.set_query("payment_document", "payment_entries", function () {
|
||||
frm.set_query("payment_document", "payment_entries", function() {
|
||||
const payment_doctypes = frm.events.get_payment_doctypes(frm);
|
||||
return {
|
||||
filters: {
|
||||
@@ -23,7 +23,7 @@ frappe.ui.form.on("Bank Transaction", {
|
||||
set_bank_statement_filter(frm);
|
||||
},
|
||||
|
||||
setup: function (frm) {
|
||||
setup: function(frm) {
|
||||
frm.set_query("party_type", function () {
|
||||
return {
|
||||
filters: {
|
||||
@@ -33,10 +33,16 @@ frappe.ui.form.on("Bank Transaction", {
|
||||
});
|
||||
},
|
||||
|
||||
get_payment_doctypes: function () {
|
||||
get_payment_doctypes: function() {
|
||||
// get payment doctypes from all the apps
|
||||
return ["Payment Entry", "Journal Entry", "Sales Invoice", "Purchase Invoice", "Bank Transaction"];
|
||||
},
|
||||
return [
|
||||
"Payment Entry",
|
||||
"Journal Entry",
|
||||
"Sales Invoice",
|
||||
"Purchase Invoice",
|
||||
"Bank Transaction",
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Bank Transaction Payments", {
|
||||
@@ -48,11 +54,10 @@ frappe.ui.form.on("Bank Transaction Payments", {
|
||||
const update_clearance_date = (frm, cdt, cdn) => {
|
||||
if (frm.doc.docstatus === 1) {
|
||||
frappe
|
||||
.xcall("erpnext.accounts.doctype.bank_transaction.bank_transaction.unclear_reference_payment", {
|
||||
doctype: cdt,
|
||||
docname: cdn,
|
||||
bt_name: frm.doc.name,
|
||||
})
|
||||
.xcall(
|
||||
"erpnext.accounts.doctype.bank_transaction.bank_transaction.unclear_reference_payment",
|
||||
{ doctype: cdt, docname: cdn, bt_name: frm.doc.name }
|
||||
)
|
||||
.then((e) => {
|
||||
if (e == "success") {
|
||||
frappe.show_alert({
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.docstatus import DocStatus
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import flt
|
||||
|
||||
@@ -49,26 +48,6 @@ class BankTransaction(Document):
|
||||
|
||||
def validate(self):
|
||||
self.validate_duplicate_references()
|
||||
self.validate_currency()
|
||||
|
||||
def validate_currency(self):
|
||||
"""
|
||||
Bank Transaction should be on the same currency as the Bank Account.
|
||||
"""
|
||||
if self.currency and self.bank_account:
|
||||
if account := frappe.get_cached_value("Bank Account", self.bank_account, "account"):
|
||||
account_currency = frappe.get_cached_value("Account", account, "account_currency")
|
||||
|
||||
if self.currency != account_currency:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Transaction currency: {0} cannot be different from Bank Account({1}) currency: {2}"
|
||||
).format(
|
||||
frappe.bold(self.currency),
|
||||
frappe.bold(self.bank_account),
|
||||
frappe.bold(account_currency),
|
||||
)
|
||||
)
|
||||
|
||||
def set_status(self):
|
||||
if self.docstatus == 2:
|
||||
@@ -96,13 +75,10 @@ class BankTransaction(Document):
|
||||
pe.append(reference)
|
||||
|
||||
def update_allocated_amount(self):
|
||||
allocated_amount = (
|
||||
self.allocated_amount = (
|
||||
sum(p.allocated_amount for p in self.payment_entries) if self.payment_entries else 0.0
|
||||
)
|
||||
unallocated_amount = abs(flt(self.withdrawal) - flt(self.deposit)) - allocated_amount
|
||||
|
||||
self.allocated_amount = flt(allocated_amount, self.precision("allocated_amount"))
|
||||
self.unallocated_amount = flt(unallocated_amount, self.precision("unallocated_amount"))
|
||||
self.unallocated_amount = abs(flt(self.withdrawal) - flt(self.deposit)) - self.allocated_amount
|
||||
|
||||
def before_submit(self):
|
||||
self.allocate_payment_entries()
|
||||
@@ -182,7 +158,7 @@ class BankTransaction(Document):
|
||||
frappe.throw(_("Voucher {0} is over-allocated by {1}").format(unallocated_amount))
|
||||
|
||||
for payment_entry in to_remove:
|
||||
self.remove(payment_entry)
|
||||
self.remove(to_remove)
|
||||
|
||||
@frappe.whitelist()
|
||||
def remove_payment_entries(self):
|
||||
@@ -237,7 +213,9 @@ def get_clearance_details(transaction, payment_entry):
|
||||
"""
|
||||
gl_bank_account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
|
||||
gles = get_related_bank_gl_entries(payment_entry.payment_document, payment_entry.payment_entry)
|
||||
bt_allocations = get_total_allocated_amount(payment_entry.payment_document, payment_entry.payment_entry)
|
||||
bt_allocations = get_total_allocated_amount(
|
||||
payment_entry.payment_document, payment_entry.payment_entry
|
||||
)
|
||||
|
||||
unallocated_amount = min(
|
||||
transaction.unallocated_amount,
|
||||
@@ -332,6 +310,7 @@ def get_total_allocated_amount(doctype, docname):
|
||||
|
||||
def get_paid_amount(payment_entry, currency, gl_bank_account):
|
||||
if payment_entry.payment_document in ["Payment Entry", "Sales Invoice", "Purchase Invoice"]:
|
||||
|
||||
paid_amount_field = "paid_amount"
|
||||
if payment_entry.payment_document == "Payment Entry":
|
||||
doc = frappe.get_doc("Payment Entry", payment_entry.payment_entry)
|
||||
@@ -370,7 +349,9 @@ def get_paid_amount(payment_entry, currency, gl_bank_account):
|
||||
)
|
||||
|
||||
elif payment_entry.payment_document == "Loan Repayment":
|
||||
return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "amount_paid")
|
||||
return frappe.db.get_value(
|
||||
payment_entry.payment_document, payment_entry.payment_entry, "amount_paid"
|
||||
)
|
||||
|
||||
elif payment_entry.payment_document == "Bank Transaction":
|
||||
dep, wth = frappe.db.get_value(
|
||||
@@ -380,7 +361,9 @@ def get_paid_amount(payment_entry, currency, gl_bank_account):
|
||||
|
||||
else:
|
||||
frappe.throw(
|
||||
f"Please reconcile {payment_entry.payment_document}: {payment_entry.payment_entry} manually"
|
||||
"Please reconcile {0}: {1} manually".format(
|
||||
payment_entry.payment_document, payment_entry.payment_entry
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -432,21 +415,3 @@ def unclear_reference_payment(doctype, docname, bt_name):
|
||||
bt = frappe.get_doc("Bank Transaction", bt_name)
|
||||
set_voucher_clearance(doctype, docname, None, bt)
|
||||
return docname
|
||||
|
||||
|
||||
def remove_from_bank_transaction(doctype, docname):
|
||||
"""Remove a (cancelled) voucher from all Bank Transactions."""
|
||||
for bt_name in get_reconciled_bank_transactions(doctype, docname):
|
||||
bt = frappe.get_doc("Bank Transaction", bt_name)
|
||||
if bt.docstatus == DocStatus.cancelled():
|
||||
continue
|
||||
|
||||
modified = False
|
||||
|
||||
for pe in bt.payment_entries:
|
||||
if pe.payment_document == doctype and pe.payment_entry == docname:
|
||||
bt.remove(pe)
|
||||
modified = True
|
||||
|
||||
if modified:
|
||||
bt.save()
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
frappe.listview_settings["Bank Transaction"] = {
|
||||
frappe.listview_settings['Bank Transaction'] = {
|
||||
add_fields: ["unallocated_amount"],
|
||||
get_indicator: function (doc) {
|
||||
if (doc.docstatus == 2) {
|
||||
get_indicator: function(doc) {
|
||||
if(doc.docstatus == 2) {
|
||||
return [__("Cancelled"), "red", "docstatus,=,2"];
|
||||
} else if (flt(doc.unallocated_amount) <= 0) {
|
||||
} else if(flt(doc.unallocated_amount)<=0) {
|
||||
return [__("Reconciled"), "green", "unallocated_amount,=,0"];
|
||||
} else if (flt(doc.unallocated_amount) > 0) {
|
||||
} else if(flt(doc.unallocated_amount)>0) {
|
||||
return [__("Unreconciled"), "orange", "unallocated_amount,>,0"];
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@@ -18,12 +18,12 @@ def upload_bank_statement():
|
||||
fcontent = frappe.local.uploaded_file
|
||||
fname = frappe.local.uploaded_filename
|
||||
|
||||
if frappe.safe_encode(fname).lower().endswith(b"csv"):
|
||||
if frappe.safe_encode(fname).lower().endswith("csv".encode("utf-8")):
|
||||
from frappe.utils.csvutils import read_csv_content
|
||||
|
||||
rows = read_csv_content(fcontent, False)
|
||||
|
||||
elif frappe.safe_encode(fname).lower().endswith(b"xlsx"):
|
||||
elif frappe.safe_encode(fname).lower().endswith("xlsx".encode("utf-8")):
|
||||
from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file
|
||||
|
||||
rows = read_xlsx_file_from_attached_file(fcontent=fcontent)
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
# See license.txt
|
||||
|
||||
import json
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe import utils
|
||||
from frappe.model.docstatus import DocStatus
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
|
||||
from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import (
|
||||
@@ -32,16 +32,8 @@ class TestBankTransaction(FrappeTestCase):
|
||||
frappe.db.delete(dt)
|
||||
clear_loan_transactions()
|
||||
make_pos_profile()
|
||||
|
||||
# generate and use a uniq hash identifier for 'Bank Account' and it's linked GL 'Account' to avoid validation error
|
||||
uniq_identifier = frappe.generate_hash(length=10)
|
||||
gl_account = create_gl_account("_Test Bank " + uniq_identifier)
|
||||
bank_account = create_bank_account(
|
||||
gl_account=gl_account, bank_account_name="Checking Account " + uniq_identifier
|
||||
)
|
||||
|
||||
add_transactions(bank_account=bank_account)
|
||||
add_vouchers(gl_account=gl_account)
|
||||
add_transactions()
|
||||
add_vouchers()
|
||||
|
||||
# This test checks if ERPNext is able to provide a linked payment for a bank transaction based on the amount of the bank transaction.
|
||||
def test_linked_payments(self):
|
||||
@@ -89,29 +81,6 @@ class TestBankTransaction(FrappeTestCase):
|
||||
clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date")
|
||||
self.assertFalse(clearance_date)
|
||||
|
||||
def test_cancel_voucher(self):
|
||||
bank_transaction = frappe.get_doc(
|
||||
"Bank Transaction",
|
||||
dict(description="1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G"),
|
||||
)
|
||||
payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1700))
|
||||
vouchers = json.dumps(
|
||||
[
|
||||
{
|
||||
"payment_doctype": "Payment Entry",
|
||||
"payment_name": payment.name,
|
||||
"amount": bank_transaction.unallocated_amount,
|
||||
}
|
||||
]
|
||||
)
|
||||
reconcile_vouchers(bank_transaction.name, vouchers)
|
||||
payment.reload()
|
||||
payment.cancel()
|
||||
bank_transaction.reload()
|
||||
self.assertEqual(bank_transaction.docstatus, DocStatus.submitted())
|
||||
self.assertEqual(bank_transaction.unallocated_amount, 1700)
|
||||
self.assertEqual(bank_transaction.payment_entries, [])
|
||||
|
||||
# Check if ERPNext can correctly filter a linked payments based on the debit/credit amount
|
||||
def test_debit_credit_output(self):
|
||||
bank_transaction = frappe.get_doc(
|
||||
@@ -227,9 +196,7 @@ def clear_loan_transactions():
|
||||
frappe.db.delete("Loan Repayment")
|
||||
|
||||
|
||||
def create_bank_account(
|
||||
bank_name="Citi Bank", gl_account="_Test Bank - _TC", bank_account_name="Checking Account"
|
||||
):
|
||||
def create_bank_account(bank_name="Citi Bank", account_name="_Test Bank - _TC"):
|
||||
try:
|
||||
frappe.get_doc(
|
||||
{
|
||||
@@ -241,35 +208,21 @@ def create_bank_account(
|
||||
pass
|
||||
|
||||
try:
|
||||
bank_account = frappe.get_doc(
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Bank Account",
|
||||
"account_name": bank_account_name,
|
||||
"account_name": "Checking Account",
|
||||
"bank": bank_name,
|
||||
"account": gl_account,
|
||||
"account": account_name,
|
||||
}
|
||||
).insert(ignore_if_duplicate=True)
|
||||
except frappe.DuplicateEntryError:
|
||||
pass
|
||||
|
||||
return bank_account.name
|
||||
|
||||
def add_transactions():
|
||||
create_bank_account()
|
||||
|
||||
def create_gl_account(gl_account_name="_Test Bank - _TC"):
|
||||
gl_account = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Account",
|
||||
"company": "_Test Company",
|
||||
"parent_account": "Current Assets - _TC",
|
||||
"account_type": "Bank",
|
||||
"is_group": 0,
|
||||
"account_name": gl_account_name,
|
||||
}
|
||||
).insert()
|
||||
return gl_account.name
|
||||
|
||||
|
||||
def add_transactions(bank_account="_Test Bank - _TC"):
|
||||
doc = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Bank Transaction",
|
||||
@@ -277,7 +230,7 @@ def add_transactions(bank_account="_Test Bank - _TC"):
|
||||
"date": "2018-10-23",
|
||||
"deposit": 1200,
|
||||
"currency": "INR",
|
||||
"bank_account": bank_account,
|
||||
"bank_account": "Checking Account - Citi Bank",
|
||||
}
|
||||
).insert()
|
||||
doc.submit()
|
||||
@@ -289,7 +242,7 @@ def add_transactions(bank_account="_Test Bank - _TC"):
|
||||
"date": "2018-10-23",
|
||||
"deposit": 1700,
|
||||
"currency": "INR",
|
||||
"bank_account": bank_account,
|
||||
"bank_account": "Checking Account - Citi Bank",
|
||||
}
|
||||
).insert()
|
||||
doc.submit()
|
||||
@@ -301,7 +254,7 @@ def add_transactions(bank_account="_Test Bank - _TC"):
|
||||
"date": "2018-10-26",
|
||||
"withdrawal": 690,
|
||||
"currency": "INR",
|
||||
"bank_account": bank_account,
|
||||
"bank_account": "Checking Account - Citi Bank",
|
||||
}
|
||||
).insert()
|
||||
doc.submit()
|
||||
@@ -313,7 +266,7 @@ def add_transactions(bank_account="_Test Bank - _TC"):
|
||||
"date": "2018-10-27",
|
||||
"deposit": 3900,
|
||||
"currency": "INR",
|
||||
"bank_account": bank_account,
|
||||
"bank_account": "Checking Account - Citi Bank",
|
||||
}
|
||||
).insert()
|
||||
doc.submit()
|
||||
@@ -325,13 +278,13 @@ def add_transactions(bank_account="_Test Bank - _TC"):
|
||||
"date": "2018-10-27",
|
||||
"withdrawal": 109080,
|
||||
"currency": "INR",
|
||||
"bank_account": bank_account,
|
||||
"bank_account": "Checking Account - Citi Bank",
|
||||
}
|
||||
).insert()
|
||||
doc.submit()
|
||||
|
||||
|
||||
def add_vouchers(gl_account="_Test Bank - _TC"):
|
||||
def add_vouchers():
|
||||
try:
|
||||
frappe.get_doc(
|
||||
{
|
||||
@@ -347,7 +300,7 @@ def add_vouchers(gl_account="_Test Bank - _TC"):
|
||||
|
||||
pi = make_purchase_invoice(supplier="Conrad Electronic", qty=1, rate=690)
|
||||
|
||||
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account=gl_account)
|
||||
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
|
||||
pe.reference_no = "Conrad Oct 18"
|
||||
pe.reference_date = "2018-10-24"
|
||||
pe.insert()
|
||||
@@ -366,14 +319,14 @@ def add_vouchers(gl_account="_Test Bank - _TC"):
|
||||
pass
|
||||
|
||||
pi = make_purchase_invoice(supplier="Mr G", qty=1, rate=1200)
|
||||
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account=gl_account)
|
||||
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
|
||||
pe.reference_no = "Herr G Oct 18"
|
||||
pe.reference_date = "2018-10-24"
|
||||
pe.insert()
|
||||
pe.submit()
|
||||
|
||||
pi = make_purchase_invoice(supplier="Mr G", qty=1, rate=1700)
|
||||
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account=gl_account)
|
||||
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
|
||||
pe.reference_no = "Herr G Nov 18"
|
||||
pe.reference_date = "2018-11-01"
|
||||
pe.insert()
|
||||
@@ -404,10 +357,10 @@ def add_vouchers(gl_account="_Test Bank - _TC"):
|
||||
pass
|
||||
|
||||
pi = make_purchase_invoice(supplier="Poore Simon's", qty=1, rate=3900, is_paid=1, do_not_save=1)
|
||||
pi.cash_bank_account = gl_account
|
||||
pi.cash_bank_account = "_Test Bank - _TC"
|
||||
pi.insert()
|
||||
pi.submit()
|
||||
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account=gl_account)
|
||||
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
|
||||
pe.reference_no = "Poore Simon's Oct 18"
|
||||
pe.reference_date = "2018-10-28"
|
||||
pe.paid_amount = 690
|
||||
@@ -416,7 +369,7 @@ def add_vouchers(gl_account="_Test Bank - _TC"):
|
||||
pe.submit()
|
||||
|
||||
si = create_sales_invoice(customer="Poore Simon's", qty=1, rate=3900)
|
||||
pe = get_payment_entry("Sales Invoice", si.name, bank_account=gl_account)
|
||||
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC")
|
||||
pe.reference_no = "Poore Simon's Oct 18"
|
||||
pe.reference_date = "2018-10-28"
|
||||
pe.insert()
|
||||
@@ -436,13 +389,19 @@ def add_vouchers(gl_account="_Test Bank - _TC"):
|
||||
|
||||
mode_of_payment = frappe.get_doc({"doctype": "Mode of Payment", "name": "Cash"})
|
||||
|
||||
if not frappe.db.get_value("Mode of Payment Account", {"company": "_Test Company", "parent": "Cash"}):
|
||||
mode_of_payment.append("accounts", {"company": "_Test Company", "default_account": gl_account})
|
||||
if not frappe.db.get_value(
|
||||
"Mode of Payment Account", {"company": "_Test Company", "parent": "Cash"}
|
||||
):
|
||||
mode_of_payment.append(
|
||||
"accounts", {"company": "_Test Company", "default_account": "_Test Bank - _TC"}
|
||||
)
|
||||
mode_of_payment.save()
|
||||
|
||||
si = create_sales_invoice(customer="Fayva", qty=1, rate=109080, do_not_save=1)
|
||||
si.is_pos = 1
|
||||
si.append("payments", {"mode_of_payment": "Cash", "account": gl_account, "amount": 109080})
|
||||
si.append(
|
||||
"payments", {"mode_of_payment": "Cash", "account": "_Test Bank - _TC", "amount": 109080}
|
||||
)
|
||||
si.insert()
|
||||
si.submit()
|
||||
|
||||
|
||||
@@ -6,18 +6,18 @@ frappe.ui.form.on("Bisect Accounting Statements", {
|
||||
frm.trigger("render_heatmap");
|
||||
},
|
||||
refresh(frm) {
|
||||
frm.add_custom_button(__("Bisect Left"), () => {
|
||||
frm.add_custom_button(__('Bisect Left'), () => {
|
||||
frm.trigger("bisect_left");
|
||||
});
|
||||
|
||||
frm.add_custom_button(__("Bisect Right"), () => {
|
||||
frm.add_custom_button(__('Bisect Right'), () => {
|
||||
frm.trigger("bisect_right");
|
||||
});
|
||||
|
||||
frm.add_custom_button(__("Up"), () => {
|
||||
frm.add_custom_button(__('Up'), () => {
|
||||
frm.trigger("move_up");
|
||||
});
|
||||
frm.add_custom_button(__("Build Tree"), () => {
|
||||
frm.add_custom_button(__('Build Tree'), () => {
|
||||
frm.trigger("build_tree");
|
||||
});
|
||||
},
|
||||
@@ -26,16 +26,16 @@ frappe.ui.form.on("Bisect Accounting Statements", {
|
||||
bisect_heatmap.addClass("bisect_heatmap_location");
|
||||
|
||||
// milliseconds in a day
|
||||
let msiad = 24 * 60 * 60 * 1000;
|
||||
let msiad=24*60*60*1000;
|
||||
let datapoints = {};
|
||||
let fr_dt = new Date(frm.doc.from_date).getTime();
|
||||
let to_dt = new Date(frm.doc.to_date).getTime();
|
||||
let bisect_start = new Date(frm.doc.current_from_date).getTime();
|
||||
let bisect_end = new Date(frm.doc.current_to_date).getTime();
|
||||
|
||||
for (let x = fr_dt; x <= to_dt; x += msiad) {
|
||||
let epoch_in_seconds = x / 1000;
|
||||
if (bisect_start <= x && x <= bisect_end) {
|
||||
for(let x=fr_dt; x <= to_dt; x+=msiad){
|
||||
let epoch_in_seconds = x/1000;
|
||||
if ((bisect_start <= x) && (x <= bisect_end )) {
|
||||
datapoints[epoch_in_seconds] = 1.0;
|
||||
} else {
|
||||
datapoints[epoch_in_seconds] = 0.0;
|
||||
@@ -49,19 +49,19 @@ frappe.ui.form.on("Bisect Accounting Statements", {
|
||||
start: new Date(frm.doc.from_date),
|
||||
end: new Date(frm.doc.to_date),
|
||||
},
|
||||
countLabel: "Bisecting",
|
||||
countLabel: 'Bisecting',
|
||||
discreteDomains: 1,
|
||||
});
|
||||
},
|
||||
bisect_left(frm) {
|
||||
frm.call({
|
||||
doc: frm.doc,
|
||||
method: "bisect_left",
|
||||
method: 'bisect_left',
|
||||
freeze: true,
|
||||
freeze_message: __("Bisecting Left ..."),
|
||||
callback: (r) => {
|
||||
frm.trigger("render_heatmap");
|
||||
},
|
||||
}
|
||||
});
|
||||
},
|
||||
bisect_right(frm) {
|
||||
@@ -69,10 +69,10 @@ frappe.ui.form.on("Bisect Accounting Statements", {
|
||||
doc: frm.doc,
|
||||
freeze: true,
|
||||
freeze_message: __("Bisecting Right ..."),
|
||||
method: "bisect_right",
|
||||
method: 'bisect_right',
|
||||
callback: (r) => {
|
||||
frm.trigger("render_heatmap");
|
||||
},
|
||||
}
|
||||
});
|
||||
},
|
||||
move_up(frm) {
|
||||
@@ -80,10 +80,10 @@ frappe.ui.form.on("Bisect Accounting Statements", {
|
||||
doc: frm.doc,
|
||||
freeze: true,
|
||||
freeze_message: __("Moving up in tree ..."),
|
||||
method: "move_up",
|
||||
method: 'move_up',
|
||||
callback: (r) => {
|
||||
frm.trigger("render_heatmap");
|
||||
},
|
||||
}
|
||||
});
|
||||
},
|
||||
build_tree(frm) {
|
||||
@@ -91,10 +91,10 @@ frappe.ui.form.on("Bisect Accounting Statements", {
|
||||
doc: frm.doc,
|
||||
freeze: true,
|
||||
freeze_message: __("Rebuilding BTree for period ..."),
|
||||
method: "build_tree",
|
||||
method: 'build_tree',
|
||||
callback: (r) => {
|
||||
frm.trigger("render_heatmap");
|
||||
},
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -2,48 +2,48 @@
|
||||
// For license information, please see license.txt
|
||||
frappe.provide("erpnext.accounts.dimensions");
|
||||
|
||||
frappe.ui.form.on("Budget", {
|
||||
onload: function (frm) {
|
||||
frm.set_query("account", "accounts", function () {
|
||||
frappe.ui.form.on('Budget', {
|
||||
onload: function(frm) {
|
||||
frm.set_query("account", "accounts", function() {
|
||||
return {
|
||||
filters: {
|
||||
company: frm.doc.company,
|
||||
report_type: "Profit and Loss",
|
||||
is_group: 0,
|
||||
},
|
||||
is_group: 0
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query("monthly_distribution", function () {
|
||||
frm.set_query("monthly_distribution", function() {
|
||||
return {
|
||||
filters: {
|
||||
fiscal_year: frm.doc.fiscal_year,
|
||||
},
|
||||
fiscal_year: frm.doc.fiscal_year
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
|
||||
},
|
||||
|
||||
refresh: function (frm) {
|
||||
frm.trigger("toggle_reqd_fields");
|
||||
refresh: function(frm) {
|
||||
frm.trigger("toggle_reqd_fields")
|
||||
},
|
||||
|
||||
budget_against: function (frm) {
|
||||
frm.trigger("set_null_value");
|
||||
frm.trigger("toggle_reqd_fields");
|
||||
budget_against: function(frm) {
|
||||
frm.trigger("set_null_value")
|
||||
frm.trigger("toggle_reqd_fields")
|
||||
},
|
||||
|
||||
set_null_value: function (frm) {
|
||||
if (frm.doc.budget_against == "Cost Center") {
|
||||
frm.set_value("project", null);
|
||||
set_null_value: function(frm) {
|
||||
if(frm.doc.budget_against == 'Cost Center') {
|
||||
frm.set_value('project', null)
|
||||
} else {
|
||||
frm.set_value("cost_center", null);
|
||||
frm.set_value('cost_center', null)
|
||||
}
|
||||
},
|
||||
|
||||
toggle_reqd_fields: function (frm) {
|
||||
frm.toggle_reqd("cost_center", frm.doc.budget_against == "Cost Center");
|
||||
frm.toggle_reqd("project", frm.doc.budget_against == "Project");
|
||||
},
|
||||
toggle_reqd_fields: function(frm) {
|
||||
frm.toggle_reqd("cost_center", frm.doc.budget_against=="Cost Center");
|
||||
frm.toggle_reqd("project", frm.doc.budget_against=="Project");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -70,11 +70,10 @@ class Budget(Document):
|
||||
select
|
||||
b.name, ba.account from `tabBudget` b, `tabBudget Account` ba
|
||||
where
|
||||
ba.parent = b.name and b.docstatus < 2 and b.company = {} and {}={} and
|
||||
b.fiscal_year={} and b.name != {} and ba.account in ({}) """.format(
|
||||
"%s", budget_against_field, "%s", "%s", "%s", ",".join(["%s"] * len(accounts))
|
||||
),
|
||||
(self.company, budget_against, self.fiscal_year, self.name, *tuple(accounts)),
|
||||
ba.parent = b.name and b.docstatus < 2 and b.company = %s and %s=%s and
|
||||
b.fiscal_year=%s and b.name != %s and ba.account in (%s) """
|
||||
% ("%s", budget_against_field, "%s", "%s", "%s", ",".join(["%s"] * len(accounts))),
|
||||
(self.company, budget_against, self.fiscal_year, self.name) + tuple(accounts),
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
@@ -97,14 +96,12 @@ class Budget(Document):
|
||||
if account_details.is_group:
|
||||
frappe.throw(_("Budget cannot be assigned against Group Account {0}").format(d.account))
|
||||
elif account_details.company != self.company:
|
||||
frappe.throw(
|
||||
_("Account {0} does not belongs to company {1}").format(d.account, self.company)
|
||||
)
|
||||
frappe.throw(_("Account {0} does not belongs to company {1}").format(d.account, self.company))
|
||||
elif account_details.report_type != "Profit and Loss":
|
||||
frappe.throw(
|
||||
_(
|
||||
"Budget cannot be assigned against {0}, as it's not an Income or Expense account"
|
||||
).format(d.account)
|
||||
_("Budget cannot be assigned against {0}, as it's not an Income or Expense account").format(
|
||||
d.account
|
||||
)
|
||||
)
|
||||
|
||||
if d.account in account_list:
|
||||
@@ -177,24 +174,30 @@ def validate_expense_against_budget(args, expense_amount=0):
|
||||
and args.account
|
||||
and frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"})
|
||||
):
|
||||
|
||||
doctype = dimension.get("document_type")
|
||||
|
||||
if frappe.get_cached_value("DocType", doctype, "is_tree"):
|
||||
lft, rgt = frappe.db.get_value(doctype, args.get(budget_against), ["lft", "rgt"])
|
||||
condition = f"""and exists(select name from `tab{doctype}`
|
||||
where lft<={lft} and rgt>={rgt} and name=b.{budget_against})""" # nosec
|
||||
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 = f"and b.{budget_against}={frappe.db.escape(args.get(budget_against))}"
|
||||
condition = "and b.%s=%s" % (budget_against, frappe.db.escape(args.get(budget_against)))
|
||||
args.is_tree = False
|
||||
|
||||
args.budget_against_field = budget_against
|
||||
args.budget_against_doctype = doctype
|
||||
|
||||
budget_records = frappe.db.sql(
|
||||
f"""
|
||||
"""
|
||||
select
|
||||
b.{budget_against} as budget_against, ba.budget_amount, b.monthly_distribution,
|
||||
b.{budget_against_field} as budget_against, ba.budget_amount, b.monthly_distribution,
|
||||
ifnull(b.applicable_on_material_request, 0) as for_material_request,
|
||||
ifnull(applicable_on_purchase_order, 0) as for_purchase_order,
|
||||
ifnull(applicable_on_booking_actual_expenses,0) as for_actual_expenses,
|
||||
@@ -207,7 +210,9 @@ def validate_expense_against_budget(args, expense_amount=0):
|
||||
b.name=ba.parent and b.fiscal_year=%s
|
||||
and ba.account=%s and b.docstatus=1
|
||||
{condition}
|
||||
""",
|
||||
""".format(
|
||||
condition=condition, budget_against_field=budget_against
|
||||
),
|
||||
(args.fiscal_year, args.account),
|
||||
as_dict=True,
|
||||
) # nosec
|
||||
@@ -219,18 +224,12 @@ def validate_expense_against_budget(args, expense_amount=0):
|
||||
def validate_budget_records(args, budget_records, expense_amount):
|
||||
for budget in budget_records:
|
||||
if flt(budget.budget_amount):
|
||||
amount = expense_amount or get_amount(args, budget)
|
||||
yearly_action, monthly_action = get_actions(args, budget)
|
||||
args["for_material_request"] = budget.for_material_request
|
||||
args["for_purchase_order"] = budget.for_purchase_order
|
||||
|
||||
if yearly_action in ("Stop", "Warn"):
|
||||
compare_expense_with_budget(
|
||||
args,
|
||||
flt(budget.budget_amount),
|
||||
_("Annual"),
|
||||
yearly_action,
|
||||
budget.budget_against,
|
||||
expense_amount,
|
||||
args, flt(budget.budget_amount), _("Annual"), yearly_action, budget.budget_against, amount
|
||||
)
|
||||
|
||||
if monthly_action in ["Stop", "Warn"]:
|
||||
@@ -241,32 +240,18 @@ def validate_budget_records(args, budget_records, expense_amount):
|
||||
args["month_end_date"] = get_last_day(args.posting_date)
|
||||
|
||||
compare_expense_with_budget(
|
||||
args,
|
||||
budget_amount,
|
||||
_("Accumulated Monthly"),
|
||||
monthly_action,
|
||||
budget.budget_against,
|
||||
expense_amount,
|
||||
args, budget_amount, _("Accumulated Monthly"), monthly_action, budget.budget_against, amount
|
||||
)
|
||||
|
||||
|
||||
def compare_expense_with_budget(args, budget_amount, action_for, action, budget_against, amount=0):
|
||||
args.actual_expense, args.requested_amount, args.ordered_amount = get_actual_expense(args), 0, 0
|
||||
if not amount:
|
||||
args.requested_amount, args.ordered_amount = get_requested_amount(args), get_ordered_amount(args)
|
||||
|
||||
if args.get("doctype") == "Material Request" and args.for_material_request:
|
||||
amount = args.requested_amount + args.ordered_amount
|
||||
|
||||
elif args.get("doctype") == "Purchase Order" and args.for_purchase_order:
|
||||
amount = args.ordered_amount
|
||||
|
||||
total_expense = args.actual_expense + amount
|
||||
actual_expense = get_actual_expense(args)
|
||||
total_expense = actual_expense + amount
|
||||
|
||||
if total_expense > budget_amount:
|
||||
if args.actual_expense > budget_amount:
|
||||
if actual_expense > budget_amount:
|
||||
error_tense = _("is already")
|
||||
diff = args.actual_expense - budget_amount
|
||||
diff = actual_expense - budget_amount
|
||||
else:
|
||||
error_tense = _("will be")
|
||||
diff = total_expense - budget_amount
|
||||
@@ -283,10 +268,9 @@ def compare_expense_with_budget(args, budget_amount, action_for, action, budget_
|
||||
frappe.bold(fmt_money(diff, currency=currency)),
|
||||
)
|
||||
|
||||
msg += get_expense_breakup(args, currency, budget_against)
|
||||
|
||||
if frappe.flags.exception_approver_role and frappe.flags.exception_approver_role in frappe.get_roles(
|
||||
frappe.session.user
|
||||
if (
|
||||
frappe.flags.exception_approver_role
|
||||
and frappe.flags.exception_approver_role in frappe.get_roles(frappe.session.user)
|
||||
):
|
||||
action = "Warn"
|
||||
|
||||
@@ -296,83 +280,6 @@ def compare_expense_with_budget(args, budget_amount, action_for, action, budget_
|
||||
frappe.msgprint(msg, indicator="orange", title=_("Budget Exceeded"))
|
||||
|
||||
|
||||
def get_expense_breakup(args, currency, budget_against):
|
||||
msg = "<hr>Total Expenses booked through - <ul>"
|
||||
|
||||
common_filters = frappe._dict(
|
||||
{
|
||||
args.budget_against_field: budget_against,
|
||||
"account": args.account,
|
||||
"company": args.company,
|
||||
}
|
||||
)
|
||||
|
||||
msg += (
|
||||
"<li>"
|
||||
+ frappe.utils.get_link_to_report(
|
||||
"General Ledger",
|
||||
label="Actual Expenses",
|
||||
filters=common_filters.copy().update(
|
||||
{
|
||||
"from_date": frappe.get_cached_value("Fiscal Year", args.fiscal_year, "year_start_date"),
|
||||
"to_date": frappe.get_cached_value("Fiscal Year", args.fiscal_year, "year_end_date"),
|
||||
"is_cancelled": 0,
|
||||
}
|
||||
),
|
||||
)
|
||||
+ " - "
|
||||
+ frappe.bold(fmt_money(args.actual_expense, currency=currency))
|
||||
+ "</li>"
|
||||
)
|
||||
|
||||
msg += (
|
||||
"<li>"
|
||||
+ frappe.utils.get_link_to_report(
|
||||
"Material Request",
|
||||
label="Material Requests",
|
||||
report_type="Report Builder",
|
||||
doctype="Material Request",
|
||||
filters=common_filters.copy().update(
|
||||
{
|
||||
"status": [["!=", "Stopped"]],
|
||||
"docstatus": 1,
|
||||
"material_request_type": "Purchase",
|
||||
"schedule_date": [["fiscal year", "2023-2024"]],
|
||||
"item_code": args.item_code,
|
||||
"per_ordered": [["<", 100]],
|
||||
}
|
||||
),
|
||||
)
|
||||
+ " - "
|
||||
+ frappe.bold(fmt_money(args.requested_amount, currency=currency))
|
||||
+ "</li>"
|
||||
)
|
||||
|
||||
msg += (
|
||||
"<li>"
|
||||
+ frappe.utils.get_link_to_report(
|
||||
"Purchase Order",
|
||||
label="Unbilled Orders",
|
||||
report_type="Report Builder",
|
||||
doctype="Purchase Order",
|
||||
filters=common_filters.copy().update(
|
||||
{
|
||||
"status": [["!=", "Closed"]],
|
||||
"docstatus": 1,
|
||||
"transaction_date": [["fiscal year", "2023-2024"]],
|
||||
"item_code": args.item_code,
|
||||
"per_billed": [["<", 100]],
|
||||
}
|
||||
),
|
||||
)
|
||||
+ " - "
|
||||
+ frappe.bold(fmt_money(args.ordered_amount, currency=currency))
|
||||
+ "</li></ul>"
|
||||
)
|
||||
|
||||
return msg
|
||||
|
||||
|
||||
def get_actions(args, budget):
|
||||
yearly_action = budget.action_if_annual_budget_exceeded
|
||||
monthly_action = budget.action_if_accumulated_monthly_budget_exceeded
|
||||
@@ -388,15 +295,31 @@ def get_actions(args, budget):
|
||||
return yearly_action, monthly_action
|
||||
|
||||
|
||||
def get_requested_amount(args):
|
||||
def get_amount(args, budget):
|
||||
amount = 0
|
||||
|
||||
if args.get("doctype") == "Material Request" and budget.for_material_request:
|
||||
amount = (
|
||||
get_requested_amount(args, budget) + get_ordered_amount(args, budget) + get_actual_expense(args)
|
||||
)
|
||||
|
||||
elif args.get("doctype") == "Purchase Order" and budget.for_purchase_order:
|
||||
amount = get_ordered_amount(args, budget) + get_actual_expense(args)
|
||||
|
||||
return amount
|
||||
|
||||
|
||||
def get_requested_amount(args, budget):
|
||||
item_code = args.get("item_code")
|
||||
condition = get_other_condition(args, "Material Request")
|
||||
condition = get_other_condition(args, budget, "Material Request")
|
||||
|
||||
data = frappe.db.sql(
|
||||
""" select ifnull((sum(child.stock_qty - child.ordered_qty) * rate), 0) as amount
|
||||
from `tabMaterial Request Item` child, `tabMaterial Request` parent where parent.name = child.parent and
|
||||
child.item_code = %s and parent.docstatus = 1 and child.stock_qty > child.ordered_qty and {} and
|
||||
parent.material_request_type = 'Purchase' and parent.status != 'Stopped'""".format(condition),
|
||||
child.item_code = %s and parent.docstatus = 1 and child.stock_qty > child.ordered_qty and {0} and
|
||||
parent.material_request_type = 'Purchase' and parent.status != 'Stopped'""".format(
|
||||
condition
|
||||
),
|
||||
item_code,
|
||||
as_list=1,
|
||||
)
|
||||
@@ -404,15 +327,17 @@ def get_requested_amount(args):
|
||||
return data[0][0] if data else 0
|
||||
|
||||
|
||||
def get_ordered_amount(args):
|
||||
def get_ordered_amount(args, budget):
|
||||
item_code = args.get("item_code")
|
||||
condition = get_other_condition(args, "Purchase Order")
|
||||
condition = get_other_condition(args, budget, "Purchase Order")
|
||||
|
||||
data = frappe.db.sql(
|
||||
f""" select ifnull(sum(child.amount - child.billed_amt), 0) as amount
|
||||
""" select ifnull(sum(child.amount - child.billed_amt), 0) as amount
|
||||
from `tabPurchase Order Item` child, `tabPurchase Order` parent where
|
||||
parent.name = child.parent and child.item_code = %s and parent.docstatus = 1 and child.amount > child.billed_amt
|
||||
and parent.status != 'Closed' and {condition}""",
|
||||
and parent.status != 'Closed' and {0}""".format(
|
||||
condition
|
||||
),
|
||||
item_code,
|
||||
as_list=1,
|
||||
)
|
||||
@@ -420,12 +345,12 @@ def get_ordered_amount(args):
|
||||
return data[0][0] if data else 0
|
||||
|
||||
|
||||
def get_other_condition(args, for_doc):
|
||||
def get_other_condition(args, budget, for_doc):
|
||||
condition = "expense_account = '%s'" % (args.expense_account)
|
||||
budget_against_field = args.get("budget_against_field")
|
||||
|
||||
if budget_against_field and args.get(budget_against_field):
|
||||
condition += f" and child.{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"
|
||||
@@ -433,8 +358,12 @@ def get_other_condition(args, for_doc):
|
||||
"Fiscal Year", args.get("fiscal_year"), ["year_start_date", "year_end_date"]
|
||||
)
|
||||
|
||||
condition += f""" and parent.{date_field}
|
||||
between '{start_date}' and '{end_date}' """
|
||||
condition += """ and parent.%s
|
||||
between '%s' and '%s' """ % (
|
||||
date_field,
|
||||
start_date,
|
||||
end_date,
|
||||
)
|
||||
|
||||
return condition
|
||||
|
||||
@@ -453,17 +382,21 @@ def get_actual_expense(args):
|
||||
|
||||
args.update(lft_rgt)
|
||||
|
||||
condition2 = f"""and exists(select name from `tab{args.budget_against_doctype}`
|
||||
condition2 = """and exists(select name from `tab{doctype}`
|
||||
where lft>=%(lft)s and rgt<=%(rgt)s
|
||||
and name=gle.{budget_against_field})"""
|
||||
and name=gle.{budget_against_field})""".format(
|
||||
doctype=args.budget_against_doctype, budget_against_field=budget_against_field # nosec
|
||||
)
|
||||
else:
|
||||
condition2 = f"""and exists(select name from `tab{args.budget_against_doctype}`
|
||||
where name=gle.{budget_against_field} and
|
||||
gle.{budget_against_field} = %({budget_against_field})s)"""
|
||||
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
|
||||
)
|
||||
|
||||
amount = flt(
|
||||
frappe.db.sql(
|
||||
f"""
|
||||
"""
|
||||
select sum(gle.debit) - sum(gle.credit)
|
||||
from `tabGL Entry` gle
|
||||
where
|
||||
@@ -474,7 +407,9 @@ def get_actual_expense(args):
|
||||
and gle.company=%(company)s
|
||||
and gle.docstatus=1
|
||||
{condition2}
|
||||
""",
|
||||
""".format(
|
||||
condition1=condition1, condition2=condition2
|
||||
),
|
||||
(args),
|
||||
)[0][0]
|
||||
) # nosec
|
||||
|
||||
@@ -41,7 +41,9 @@ class TestBudget(unittest.TestCase):
|
||||
|
||||
budget = make_budget(budget_against="Cost Center")
|
||||
|
||||
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||
frappe.db.set_value(
|
||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
||||
)
|
||||
|
||||
jv = make_journal_entry(
|
||||
"_Test Account Cost for Goods Sold - _TC",
|
||||
@@ -61,7 +63,9 @@ class TestBudget(unittest.TestCase):
|
||||
|
||||
budget = make_budget(budget_against="Cost Center")
|
||||
|
||||
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||
frappe.db.set_value(
|
||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
||||
)
|
||||
|
||||
jv = make_journal_entry(
|
||||
"_Test Account Cost for Goods Sold - _TC",
|
||||
@@ -93,7 +97,9 @@ class TestBudget(unittest.TestCase):
|
||||
)
|
||||
|
||||
fiscal_year = get_fiscal_year(nowdate())[0]
|
||||
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||
frappe.db.set_value(
|
||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
||||
)
|
||||
frappe.db.set_value("Budget", budget.name, "fiscal_year", fiscal_year)
|
||||
|
||||
mr = frappe.get_doc(
|
||||
@@ -132,7 +138,9 @@ class TestBudget(unittest.TestCase):
|
||||
)
|
||||
|
||||
fiscal_year = get_fiscal_year(nowdate())[0]
|
||||
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||
frappe.db.set_value(
|
||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
||||
)
|
||||
frappe.db.set_value("Budget", budget.name, "fiscal_year", fiscal_year)
|
||||
|
||||
po = create_purchase_order(transaction_date=nowdate(), do_not_submit=True)
|
||||
@@ -150,7 +158,9 @@ class TestBudget(unittest.TestCase):
|
||||
|
||||
budget = make_budget(budget_against="Project")
|
||||
|
||||
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||
frappe.db.set_value(
|
||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
||||
)
|
||||
|
||||
project = frappe.get_value("Project", {"project_name": "_Test Project"})
|
||||
|
||||
@@ -213,7 +223,7 @@ class TestBudget(unittest.TestCase):
|
||||
if month > 9:
|
||||
month = 9
|
||||
|
||||
for _i in range(month + 1):
|
||||
for i in range(month + 1):
|
||||
jv = make_journal_entry(
|
||||
"_Test Account Cost for Goods Sold - _TC",
|
||||
"_Test Bank - _TC",
|
||||
@@ -227,7 +237,9 @@ class TestBudget(unittest.TestCase):
|
||||
frappe.db.get_value("GL Entry", {"voucher_type": "Journal Entry", "voucher_no": jv.name})
|
||||
)
|
||||
|
||||
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||
frappe.db.set_value(
|
||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
||||
)
|
||||
|
||||
self.assertRaises(BudgetError, jv.cancel)
|
||||
|
||||
@@ -243,7 +255,7 @@ class TestBudget(unittest.TestCase):
|
||||
month = 9
|
||||
|
||||
project = frappe.get_value("Project", {"project_name": "_Test Project"})
|
||||
for _i in range(month + 1):
|
||||
for i in range(month + 1):
|
||||
jv = make_journal_entry(
|
||||
"_Test Account Cost for Goods Sold - _TC",
|
||||
"_Test Bank - _TC",
|
||||
@@ -258,7 +270,9 @@ class TestBudget(unittest.TestCase):
|
||||
frappe.db.get_value("GL Entry", {"voucher_type": "Journal Entry", "voucher_no": jv.name})
|
||||
)
|
||||
|
||||
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||
frappe.db.set_value(
|
||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
||||
)
|
||||
|
||||
self.assertRaises(BudgetError, jv.cancel)
|
||||
|
||||
@@ -270,7 +284,9 @@ class TestBudget(unittest.TestCase):
|
||||
set_total_expense_zero(nowdate(), "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")
|
||||
frappe.db.set_value(
|
||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
||||
)
|
||||
|
||||
jv = make_journal_entry(
|
||||
"_Test Account Cost for Goods Sold - _TC",
|
||||
@@ -300,7 +316,9 @@ class TestBudget(unittest.TestCase):
|
||||
).insert(ignore_permissions=True)
|
||||
|
||||
budget = make_budget(budget_against="Cost Center", cost_center=cost_center)
|
||||
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||
frappe.db.set_value(
|
||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
||||
)
|
||||
|
||||
jv = make_journal_entry(
|
||||
"_Test Account Cost for Goods Sold - _TC",
|
||||
@@ -405,11 +423,13 @@ def make_budget(**args):
|
||||
fiscal_year = get_fiscal_year(nowdate())[0]
|
||||
|
||||
if budget_against == "Project":
|
||||
project_name = "{}%".format("_Test Project/" + fiscal_year)
|
||||
project_name = "{0}%".format("_Test Project/" + fiscal_year)
|
||||
budget_list = frappe.get_all("Budget", fields=["name"], filters={"name": ("like", project_name)})
|
||||
else:
|
||||
cost_center_name = "{}%".format(cost_center or "_Test Cost Center - _TC/" + fiscal_year)
|
||||
budget_list = frappe.get_all("Budget", fields=["name"], filters={"name": ("like", cost_center_name)})
|
||||
cost_center_name = "{0}%".format(cost_center or "_Test Cost Center - _TC/" + fiscal_year)
|
||||
budget_list = frappe.get_all(
|
||||
"Budget", fields=["name"], filters={"name": ("like", cost_center_name)}
|
||||
)
|
||||
for d in budget_list:
|
||||
frappe.db.sql("delete from `tabBudget` where name = %(name)s", d)
|
||||
frappe.db.sql("delete from `tabBudget Account` where parent = %(name)s", d)
|
||||
@@ -431,18 +451,24 @@ def make_budget(**args):
|
||||
budget.action_if_annual_budget_exceeded = "Stop"
|
||||
budget.action_if_accumulated_monthly_budget_exceeded = "Ignore"
|
||||
budget.budget_against = budget_against
|
||||
budget.append("accounts", {"account": "_Test Account Cost for Goods Sold - _TC", "budget_amount": 200000})
|
||||
budget.append(
|
||||
"accounts", {"account": "_Test Account Cost for Goods Sold - _TC", "budget_amount": 200000}
|
||||
)
|
||||
|
||||
if args.applicable_on_material_request:
|
||||
budget.applicable_on_material_request = 1
|
||||
budget.action_if_annual_budget_exceeded_on_mr = args.action_if_annual_budget_exceeded_on_mr or "Warn"
|
||||
budget.action_if_annual_budget_exceeded_on_mr = (
|
||||
args.action_if_annual_budget_exceeded_on_mr or "Warn"
|
||||
)
|
||||
budget.action_if_accumulated_monthly_budget_exceeded_on_mr = (
|
||||
args.action_if_accumulated_monthly_budget_exceeded_on_mr or "Warn"
|
||||
)
|
||||
|
||||
if args.applicable_on_purchase_order:
|
||||
budget.applicable_on_purchase_order = 1
|
||||
budget.action_if_annual_budget_exceeded_on_po = args.action_if_annual_budget_exceeded_on_po or "Warn"
|
||||
budget.action_if_annual_budget_exceeded_on_po = (
|
||||
args.action_if_annual_budget_exceeded_on_po or "Warn"
|
||||
)
|
||||
budget.action_if_accumulated_monthly_budget_exceeded_on_po = (
|
||||
args.action_if_accumulated_monthly_budget_exceeded_on_po or "Warn"
|
||||
)
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
frappe.ui.form.on("Cashier Closing", {
|
||||
setup: function (frm) {
|
||||
frappe.ui.form.on('Cashier Closing', {
|
||||
|
||||
setup: function(frm){
|
||||
if (frm.doc.user == "" || frm.doc.user == null) {
|
||||
frm.doc.user = frappe.session.user;
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
frappe.ui.form.on("Chart of Accounts Importer", {
|
||||
frappe.ui.form.on('Chart of Accounts Importer', {
|
||||
onload: function (frm) {
|
||||
frm.set_value("company", "");
|
||||
frm.set_value("import_file", "");
|
||||
@@ -8,34 +8,31 @@ frappe.ui.form.on("Chart of Accounts Importer", {
|
||||
frm.disable_save();
|
||||
|
||||
// make company mandatory
|
||||
frm.set_df_property("company", "reqd", frm.doc.company ? 0 : 1);
|
||||
frm.set_df_property("import_file_section", "hidden", frm.doc.company ? 0 : 1);
|
||||
frm.set_df_property('company', 'reqd', frm.doc.company ? 0 : 1);
|
||||
frm.set_df_property('import_file_section', 'hidden', frm.doc.company ? 0 : 1);
|
||||
|
||||
if (frm.doc.import_file) {
|
||||
frappe.run_serially([
|
||||
() => generate_tree_preview(frm),
|
||||
() => create_import_button(frm),
|
||||
() => frm.set_df_property("chart_preview", "hidden", 0),
|
||||
() => frm.set_df_property('chart_preview', 'hidden', 0)
|
||||
]);
|
||||
}
|
||||
|
||||
frm.set_df_property(
|
||||
"chart_preview",
|
||||
"hidden",
|
||||
$(frm.fields_dict["chart_tree"].wrapper).html() != "" ? 0 : 1
|
||||
);
|
||||
frm.set_df_property('chart_preview', 'hidden',
|
||||
$(frm.fields_dict['chart_tree'].wrapper).html()!="" ? 0 : 1);
|
||||
},
|
||||
|
||||
download_template: function (frm) {
|
||||
download_template: function(frm) {
|
||||
var d = new frappe.ui.Dialog({
|
||||
title: __("Download Template"),
|
||||
fields: [
|
||||
{
|
||||
label: "File Type",
|
||||
label : "File Type",
|
||||
fieldname: "file_type",
|
||||
fieldtype: "Select",
|
||||
reqd: 1,
|
||||
options: ["Excel", "CSV"],
|
||||
options: ["Excel", "CSV"]
|
||||
},
|
||||
{
|
||||
label: "Template Type",
|
||||
@@ -44,27 +41,21 @@ frappe.ui.form.on("Chart of Accounts Importer", {
|
||||
reqd: 1,
|
||||
options: ["Sample Template", "Blank Template"],
|
||||
change: () => {
|
||||
let template_type = d.get_value("template_type");
|
||||
let template_type = d.get_value('template_type');
|
||||
|
||||
if (template_type === "Sample Template") {
|
||||
d.set_df_property(
|
||||
"template_type",
|
||||
"description",
|
||||
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.`
|
||||
);
|
||||
You can add more accounts or change existing accounts in the template as per your choice.`);
|
||||
} else {
|
||||
d.set_df_property(
|
||||
"template_type",
|
||||
"description",
|
||||
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.`
|
||||
);
|
||||
of Accounts. Please enter the account names and add more rows as per your requirement.`);
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Company",
|
||||
label : "Company",
|
||||
fieldname: "company",
|
||||
fieldtype: "Link",
|
||||
reqd: 1,
|
||||
@@ -72,25 +63,25 @@ frappe.ui.form.on("Chart of Accounts Importer", {
|
||||
default: frm.doc.company,
|
||||
},
|
||||
],
|
||||
primary_action: function () {
|
||||
primary_action: function() {
|
||||
let data = d.get_values();
|
||||
|
||||
if (!data.template_type) {
|
||||
frappe.throw(__("Please select <b>Template Type</b> to download template"));
|
||||
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",
|
||||
'/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,
|
||||
company: data.company,
|
||||
company: data.company
|
||||
}
|
||||
);
|
||||
|
||||
d.hide();
|
||||
},
|
||||
primary_action_label: __("Download"),
|
||||
primary_action_label: __('Download')
|
||||
});
|
||||
d.show();
|
||||
},
|
||||
@@ -98,7 +89,7 @@ frappe.ui.form.on("Chart of Accounts Importer", {
|
||||
import_file: function (frm) {
|
||||
if (!frm.doc.import_file) {
|
||||
frm.page.set_indicator("");
|
||||
$(frm.fields_dict["chart_tree"].wrapper).empty(); // empty wrapper on removing file
|
||||
$(frm.fields_dict['chart_tree'].wrapper).empty(); // empty wrapper on removing file
|
||||
}
|
||||
},
|
||||
|
||||
@@ -108,97 +99,89 @@ frappe.ui.form.on("Chart of Accounts Importer", {
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.validate_company",
|
||||
args: {
|
||||
company: frm.doc.company,
|
||||
company: frm.doc.company
|
||||
},
|
||||
callback: function (r) {
|
||||
if (r.message === false) {
|
||||
callback: function(r) {
|
||||
if(r.message===false) {
|
||||
frm.set_value("company", "");
|
||||
frappe.throw(
|
||||
__(
|
||||
"Transactions against the Company already exist! Chart of Accounts can only be imported for a Company with no transactions."
|
||||
)
|
||||
);
|
||||
frappe.throw(__("Transactions against the Company already exist! Chart of Accounts can only be imported for a Company with no transactions."));
|
||||
} else {
|
||||
frm.trigger("refresh");
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
var create_import_button = function (frm) {
|
||||
frm.page
|
||||
.set_primary_action(__("Import"), function () {
|
||||
return frappe.call({
|
||||
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.import_coa",
|
||||
args: {
|
||||
file_name: frm.doc.import_file,
|
||||
company: frm.doc.company,
|
||||
},
|
||||
freeze: true,
|
||||
freeze_message: __("Creating Accounts..."),
|
||||
callback: function (r) {
|
||||
if (!r.exc) {
|
||||
clearInterval(frm.page["interval"]);
|
||||
frm.page.set_indicator(__("Import Successful"), "blue");
|
||||
create_reset_button(frm);
|
||||
}
|
||||
},
|
||||
});
|
||||
})
|
||||
.addClass("btn btn-primary");
|
||||
};
|
||||
|
||||
var create_reset_button = function (frm) {
|
||||
frm.page
|
||||
.set_primary_action(__("Reset"), function () {
|
||||
frm.page.clear_primary_action();
|
||||
delete frm.page["show_import_button"];
|
||||
frm.reload_doc();
|
||||
})
|
||||
.addClass("btn btn-primary");
|
||||
};
|
||||
|
||||
var validate_coa = function (frm) {
|
||||
if (frm.doc.import_file) {
|
||||
let parent = __("All Accounts");
|
||||
var create_import_button = function(frm) {
|
||||
frm.page.set_primary_action(__("Import"), function () {
|
||||
return frappe.call({
|
||||
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.get_coa",
|
||||
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.import_coa",
|
||||
args: {
|
||||
file_name: frm.doc.import_file,
|
||||
parent: parent,
|
||||
doctype: "Chart of Accounts Importer",
|
||||
file_type: frm.doc.file_type,
|
||||
for_validate: 1,
|
||||
company: frm.doc.company
|
||||
},
|
||||
callback: function (r) {
|
||||
if (r.message["show_import_button"]) {
|
||||
frm.page["show_import_button"] = Boolean(r.message["show_import_button"]);
|
||||
freeze: true,
|
||||
freeze_message: __("Creating Accounts..."),
|
||||
callback: function(r) {
|
||||
if (!r.exc) {
|
||||
clearInterval(frm.page["interval"]);
|
||||
frm.page.set_indicator(__('Import Successful'), 'blue');
|
||||
create_reset_button(frm);
|
||||
}
|
||||
}
|
||||
});
|
||||
}).addClass('btn btn-primary');
|
||||
};
|
||||
|
||||
var create_reset_button = function(frm) {
|
||||
frm.page.set_primary_action(__("Reset"), function () {
|
||||
frm.page.clear_primary_action();
|
||||
delete frm.page["show_import_button"];
|
||||
frm.reload_doc();
|
||||
}).addClass('btn btn-primary');
|
||||
};
|
||||
|
||||
var validate_coa = function(frm) {
|
||||
if (frm.doc.import_file) {
|
||||
let parent = __('All Accounts');
|
||||
return frappe.call({
|
||||
'method': 'erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.get_coa',
|
||||
'args': {
|
||||
file_name: frm.doc.import_file,
|
||||
parent: parent,
|
||||
doctype: 'Chart of Accounts Importer',
|
||||
file_type: frm.doc.file_type,
|
||||
for_validate: 1
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r.message['show_import_button']) {
|
||||
frm.page['show_import_button'] = Boolean(r.message['show_import_button']);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var generate_tree_preview = function (frm) {
|
||||
let parent = __("All Accounts");
|
||||
$(frm.fields_dict["chart_tree"].wrapper).empty(); // empty wrapper to load new data
|
||||
var generate_tree_preview = function(frm) {
|
||||
let parent = __('All Accounts');
|
||||
$(frm.fields_dict['chart_tree'].wrapper).empty(); // empty wrapper to load new data
|
||||
|
||||
// generate tree structure based on the csv data
|
||||
return new frappe.ui.Tree({
|
||||
parent: $(frm.fields_dict["chart_tree"].wrapper),
|
||||
parent: $(frm.fields_dict['chart_tree'].wrapper),
|
||||
label: parent,
|
||||
expandable: true,
|
||||
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.get_coa",
|
||||
method: 'erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.get_coa',
|
||||
args: {
|
||||
file_name: frm.doc.import_file,
|
||||
parent: parent,
|
||||
doctype: "Chart of Accounts Importer",
|
||||
file_type: frm.doc.file_type,
|
||||
doctype: 'Chart of Accounts Importer',
|
||||
file_type: frm.doc.file_type
|
||||
},
|
||||
onclick: function (node) {
|
||||
onclick: function(node) {
|
||||
parent = node.value;
|
||||
},
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -38,7 +38,9 @@ class ChartofAccountsImporter(Document):
|
||||
|
||||
def validate(self):
|
||||
if self.import_file:
|
||||
get_coa("Chart of Accounts Importer", "All Accounts", file_name=self.import_file, for_validate=1)
|
||||
get_coa(
|
||||
"Chart of Accounts Importer", "All Accounts", file_name=self.import_file, for_validate=1
|
||||
)
|
||||
|
||||
|
||||
def validate_columns(data):
|
||||
@@ -114,7 +116,7 @@ def generate_data_from_csv(file_doc, as_dict=False):
|
||||
file_path = file_doc.get_full_path()
|
||||
|
||||
data = []
|
||||
with open(file_path) as in_file:
|
||||
with open(file_path, "r") as in_file:
|
||||
csv_reader = list(csv.reader(in_file))
|
||||
headers = csv_reader[0]
|
||||
del csv_reader[0] # delete top row and headers row
|
||||
@@ -213,10 +215,10 @@ def build_forest(data):
|
||||
for row in data:
|
||||
account_name, parent_account, account_number, parent_account_number = row[0:4]
|
||||
if account_number:
|
||||
account_name = f"{account_number} - {account_name}"
|
||||
account_name = "{} - {}".format(account_number, account_name)
|
||||
if parent_account_number:
|
||||
parent_account_number = cstr(parent_account_number).strip()
|
||||
parent_account = f"{parent_account_number} - {parent_account}"
|
||||
parent_account = "{} - {}".format(parent_account_number, parent_account)
|
||||
|
||||
if parent_account == account_name == child:
|
||||
return [parent_account]
|
||||
@@ -228,7 +230,7 @@ def build_forest(data):
|
||||
frappe.bold(parent_account)
|
||||
)
|
||||
)
|
||||
return [child, *parent_account_list]
|
||||
return [child] + parent_account_list
|
||||
|
||||
charts_map, paths = {}, []
|
||||
|
||||
@@ -248,12 +250,12 @@ def build_forest(data):
|
||||
) = i
|
||||
|
||||
if not account_name:
|
||||
error_messages.append(f"Row {line_no}: Please enter Account Name")
|
||||
error_messages.append("Row {0}: Please enter Account Name".format(line_no))
|
||||
|
||||
name = account_name
|
||||
if account_number:
|
||||
account_number = cstr(account_number).strip()
|
||||
account_name = f"{account_number} - {account_name}"
|
||||
account_name = "{} - {}".format(account_number, account_name)
|
||||
|
||||
charts_map[account_name] = {}
|
||||
charts_map[account_name]["account_name"] = name
|
||||
@@ -350,9 +352,9 @@ def get_template(template_type, company):
|
||||
|
||||
def get_sample_template(writer, company):
|
||||
currency = frappe.db.get_value("Company", company, "default_currency")
|
||||
with open(os.path.join(os.path.dirname(__file__), "coa_sample_template.csv")) as f:
|
||||
with open(os.path.join(os.path.dirname(__file__), "coa_sample_template.csv"), "r") as f:
|
||||
for row in f:
|
||||
row = [*row.strip().split(","), currency]
|
||||
row = row.strip().split(",") + [currency]
|
||||
writer.writerow(row)
|
||||
|
||||
return writer
|
||||
@@ -461,7 +463,7 @@ def unset_existing_data(company):
|
||||
"Purchase Taxes and Charges Template",
|
||||
]:
|
||||
frappe.db.sql(
|
||||
f'''delete from `tab{doctype}` where `company`="%s"''' % (company) # nosec
|
||||
'''delete from `tab{0}` where `company`="%s"'''.format(doctype) % (company) # nosec
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -3,20 +3,18 @@
|
||||
|
||||
frappe.provide("erpnext.cheque_print");
|
||||
|
||||
frappe.ui.form.on("Cheque Print Template", {
|
||||
refresh: function (frm) {
|
||||
if (!frm.doc.__islocal) {
|
||||
frm.add_custom_button(
|
||||
frm.doc.has_print_format ? __("Update Print Format") : __("Create Print Format"),
|
||||
function () {
|
||||
frappe.ui.form.on('Cheque Print Template', {
|
||||
refresh: function(frm) {
|
||||
if(!frm.doc.__islocal) {
|
||||
frm.add_custom_button(frm.doc.has_print_format?__("Update Print Format"):__("Create Print Format"),
|
||||
function() {
|
||||
erpnext.cheque_print.view_cheque_print(frm);
|
||||
}
|
||||
).addClass("btn-primary");
|
||||
}).addClass("btn-primary");
|
||||
|
||||
$(frm.fields_dict.cheque_print_preview.wrapper).empty();
|
||||
$(frm.fields_dict.cheque_print_preview.wrapper).empty()
|
||||
|
||||
var template =
|
||||
'<div style="position: relative; overflow-x: scroll;">\
|
||||
|
||||
var template = '<div style="position: relative; overflow-x: scroll;">\
|
||||
<div id="cheque_preview" style="width: {{ cheque_width }}cm; \
|
||||
height: {{ cheque_height }}cm;\
|
||||
background-repeat: no-repeat;\
|
||||
@@ -50,30 +48,30 @@ frappe.ui.form.on("Cheque Print Template", {
|
||||
</div>\
|
||||
</div>';
|
||||
|
||||
$(frappe.render(template, frm.doc)).appendTo(frm.fields_dict.cheque_print_preview.wrapper);
|
||||
$(frappe.render(template, frm.doc)).appendTo(frm.fields_dict.cheque_print_preview.wrapper)
|
||||
|
||||
if (frm.doc.scanned_cheque) {
|
||||
$(frm.fields_dict.cheque_print_preview.wrapper)
|
||||
.find("#cheque_preview")
|
||||
.css("background-image", "url(" + frm.doc.scanned_cheque + ")");
|
||||
$(frm.fields_dict.cheque_print_preview.wrapper).find("#cheque_preview").css('background-image', 'url(' + frm.doc.scanned_cheque + ')');
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
erpnext.cheque_print.view_cheque_print = function (frm) {
|
||||
|
||||
erpnext.cheque_print.view_cheque_print = function(frm) {
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.cheque_print_template.cheque_print_template.create_or_update_cheque_print_format",
|
||||
args: {
|
||||
template_name: frm.doc.name,
|
||||
args:{
|
||||
"template_name": frm.doc.name
|
||||
},
|
||||
callback: function (r) {
|
||||
callback: function(r) {
|
||||
if (!r.exe && !frm.doc.has_print_format) {
|
||||
var doc = frappe.model.sync(r.message);
|
||||
frappe.set_route("Form", r.message.doctype, r.message.name);
|
||||
} else {
|
||||
frappe.msgprint(__("Print settings updated in respective print format"));
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
else {
|
||||
frappe.msgprint(__("Print settings updated in respective print format"))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -66,71 +66,71 @@ def create_or_update_cheque_print_format(template_name):
|
||||
|
||||
cheque_print.html = """
|
||||
<style>
|
||||
.print-format {{
|
||||
.print-format {
|
||||
padding: 0px;
|
||||
}}
|
||||
@media screen {{
|
||||
.print-format {{
|
||||
}
|
||||
@media screen {
|
||||
.print-format {
|
||||
padding: 0in;
|
||||
}}
|
||||
}}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div style="position: relative; top:{starting_position_from_top_edge}cm">
|
||||
<div style="width:{cheque_width}cm;height:{cheque_height}cm;">
|
||||
<span style="top:{acc_pay_dist_from_top_edge}cm; left:{acc_pay_dist_from_left_edge}cm;
|
||||
<div style="position: relative; top:%(starting_position_from_top_edge)scm">
|
||||
<div style="width:%(cheque_width)scm;height:%(cheque_height)scm;">
|
||||
<span style="top:%(acc_pay_dist_from_top_edge)scm; left:%(acc_pay_dist_from_left_edge)scm;
|
||||
border-bottom: solid 1px;border-top:solid 1px; width:2cm;text-align: center; position: absolute;">
|
||||
{message_to_show}
|
||||
%(message_to_show)s
|
||||
</span>
|
||||
<span style="top:{date_dist_from_top_edge}cm; left:{date_dist_from_left_edge}cm;
|
||||
<span style="top:%(date_dist_from_top_edge)scm; left:%(date_dist_from_left_edge)scm;
|
||||
position: absolute;">
|
||||
{{{{ frappe.utils.formatdate(doc.reference_date) or '' }}}}
|
||||
{{ frappe.utils.formatdate(doc.reference_date) or '' }}
|
||||
</span>
|
||||
<span style="top:{acc_no_dist_from_top_edge}cm;left:{acc_no_dist_from_left_edge}cm;
|
||||
<span style="top:%(acc_no_dist_from_top_edge)scm;left:%(acc_no_dist_from_left_edge)scm;
|
||||
position: absolute; min-width: 6cm;">
|
||||
{{{{ doc.account_no or '' }}}}
|
||||
{{ doc.account_no or '' }}
|
||||
</span>
|
||||
<span style="top:{payer_name_from_top_edge}cm;left: {payer_name_from_left_edge}cm;
|
||||
<span style="top:%(payer_name_from_top_edge)scm;left: %(payer_name_from_left_edge)scm;
|
||||
position: absolute; min-width: 6cm;">
|
||||
{{{{doc.party_name}}}}
|
||||
{{doc.party_name}}
|
||||
</span>
|
||||
<span style="top:{amt_in_words_from_top_edge}cm; left:{amt_in_words_from_left_edge}cm;
|
||||
position: absolute; display: block; width: {amt_in_word_width}cm;
|
||||
line-height:{amt_in_words_line_spacing}cm; word-wrap: break-word;">
|
||||
{{{{frappe.utils.money_in_words(doc.base_paid_amount or doc.base_received_amount)}}}}
|
||||
<span style="top:%(amt_in_words_from_top_edge)scm; left:%(amt_in_words_from_left_edge)scm;
|
||||
position: absolute; display: block; width: %(amt_in_word_width)scm;
|
||||
line-height:%(amt_in_words_line_spacing)scm; word-wrap: break-word;">
|
||||
{{frappe.utils.money_in_words(doc.base_paid_amount or doc.base_received_amount)}}
|
||||
</span>
|
||||
<span style="top:{amt_in_figures_from_top_edge}cm;left: {amt_in_figures_from_left_edge}cm;
|
||||
<span style="top:%(amt_in_figures_from_top_edge)scm;left: %(amt_in_figures_from_left_edge)scm;
|
||||
position: absolute; min-width: 4cm;">
|
||||
{{{{doc.get_formatted("base_paid_amount") or doc.get_formatted("base_received_amount")}}}}
|
||||
{{doc.get_formatted("base_paid_amount") or doc.get_formatted("base_received_amount")}}
|
||||
</span>
|
||||
<span style="top:{signatory_from_top_edge}cm;left: {signatory_from_left_edge}cm;
|
||||
<span style="top:%(signatory_from_top_edge)scm;left: %(signatory_from_left_edge)scm;
|
||||
position: absolute; min-width: 6cm;">
|
||||
{{{{doc.company}}}}
|
||||
{{doc.company}}
|
||||
</span>
|
||||
</div>
|
||||
</div>""".format(
|
||||
starting_position_from_top_edge=doc.starting_position_from_top_edge
|
||||
</div>""" % {
|
||||
"starting_position_from_top_edge": doc.starting_position_from_top_edge
|
||||
if doc.cheque_size == "A4"
|
||||
else 0.0,
|
||||
cheque_width=doc.cheque_width,
|
||||
cheque_height=doc.cheque_height,
|
||||
acc_pay_dist_from_top_edge=doc.acc_pay_dist_from_top_edge,
|
||||
acc_pay_dist_from_left_edge=doc.acc_pay_dist_from_left_edge,
|
||||
message_to_show=doc.message_to_show if doc.message_to_show else _("Account Pay Only"),
|
||||
date_dist_from_top_edge=doc.date_dist_from_top_edge,
|
||||
date_dist_from_left_edge=doc.date_dist_from_left_edge,
|
||||
acc_no_dist_from_top_edge=doc.acc_no_dist_from_top_edge,
|
||||
acc_no_dist_from_left_edge=doc.acc_no_dist_from_left_edge,
|
||||
payer_name_from_top_edge=doc.payer_name_from_top_edge,
|
||||
payer_name_from_left_edge=doc.payer_name_from_left_edge,
|
||||
amt_in_words_from_top_edge=doc.amt_in_words_from_top_edge,
|
||||
amt_in_words_from_left_edge=doc.amt_in_words_from_left_edge,
|
||||
amt_in_word_width=doc.amt_in_word_width,
|
||||
amt_in_words_line_spacing=doc.amt_in_words_line_spacing,
|
||||
amt_in_figures_from_top_edge=doc.amt_in_figures_from_top_edge,
|
||||
amt_in_figures_from_left_edge=doc.amt_in_figures_from_left_edge,
|
||||
signatory_from_top_edge=doc.signatory_from_top_edge,
|
||||
signatory_from_left_edge=doc.signatory_from_left_edge,
|
||||
)
|
||||
"cheque_width": doc.cheque_width,
|
||||
"cheque_height": doc.cheque_height,
|
||||
"acc_pay_dist_from_top_edge": doc.acc_pay_dist_from_top_edge,
|
||||
"acc_pay_dist_from_left_edge": doc.acc_pay_dist_from_left_edge,
|
||||
"message_to_show": doc.message_to_show if doc.message_to_show else _("Account Pay Only"),
|
||||
"date_dist_from_top_edge": doc.date_dist_from_top_edge,
|
||||
"date_dist_from_left_edge": doc.date_dist_from_left_edge,
|
||||
"acc_no_dist_from_top_edge": doc.acc_no_dist_from_top_edge,
|
||||
"acc_no_dist_from_left_edge": doc.acc_no_dist_from_left_edge,
|
||||
"payer_name_from_top_edge": doc.payer_name_from_top_edge,
|
||||
"payer_name_from_left_edge": doc.payer_name_from_left_edge,
|
||||
"amt_in_words_from_top_edge": doc.amt_in_words_from_top_edge,
|
||||
"amt_in_words_from_left_edge": doc.amt_in_words_from_left_edge,
|
||||
"amt_in_word_width": doc.amt_in_word_width,
|
||||
"amt_in_words_line_spacing": doc.amt_in_words_line_spacing,
|
||||
"amt_in_figures_from_top_edge": doc.amt_in_figures_from_top_edge,
|
||||
"amt_in_figures_from_left_edge": doc.amt_in_figures_from_left_edge,
|
||||
"signatory_from_top_edge": doc.signatory_from_top_edge,
|
||||
"signatory_from_left_edge": doc.signatory_from_left_edge,
|
||||
}
|
||||
|
||||
cheque_print.save(ignore_permissions=True)
|
||||
|
||||
|
||||
@@ -3,80 +3,75 @@
|
||||
|
||||
frappe.provide("erpnext.accounts");
|
||||
|
||||
frappe.ui.form.on("Cost Center", {
|
||||
onload: function (frm) {
|
||||
frm.set_query("parent_cost_center", function () {
|
||||
|
||||
|
||||
frappe.ui.form.on('Cost Center', {
|
||||
onload: function(frm) {
|
||||
frm.set_query("parent_cost_center", function() {
|
||||
return {
|
||||
filters: {
|
||||
company: frm.doc.company,
|
||||
is_group: 1,
|
||||
},
|
||||
};
|
||||
is_group: 1
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
refresh: function (frm) {
|
||||
refresh: function(frm) {
|
||||
if (!frm.is_new()) {
|
||||
frm.add_custom_button(__("Update Cost Center Name / Number"), function () {
|
||||
frm.add_custom_button(__('Update Cost Center Name / Number'), function () {
|
||||
frm.trigger("update_cost_center_number");
|
||||
});
|
||||
}
|
||||
|
||||
let intro_txt = "";
|
||||
let intro_txt = '';
|
||||
let doc = frm.doc;
|
||||
frm.toggle_display("cost_center_name", doc.__islocal);
|
||||
frm.toggle_enable(["is_group", "company"], doc.__islocal);
|
||||
frm.toggle_display('cost_center_name', doc.__islocal);
|
||||
frm.toggle_enable(['is_group', 'company'], doc.__islocal);
|
||||
|
||||
if (!doc.__islocal && doc.is_group == 1) {
|
||||
intro_txt += __(
|
||||
"Note: This Cost Center is a Group. Cannot make accounting entries against groups."
|
||||
);
|
||||
if(!doc.__islocal && doc.is_group==1) {
|
||||
intro_txt += __('Note: This Cost Center is a Group. Cannot make accounting entries against groups.');
|
||||
}
|
||||
|
||||
frm.events.hide_unhide_group_ledger(frm);
|
||||
|
||||
frm.toggle_display("sb1", doc.is_group == 0);
|
||||
frm.toggle_display('sb1', doc.is_group==0);
|
||||
frm.set_intro(intro_txt);
|
||||
|
||||
if (!frm.doc.__islocal) {
|
||||
frm.add_custom_button(__("Chart of Cost Centers"), function () {
|
||||
frappe.set_route("Tree", "Cost Center");
|
||||
});
|
||||
if(!frm.doc.__islocal) {
|
||||
frm.add_custom_button(__('Chart of Cost Centers'),
|
||||
function() { frappe.set_route("Tree", "Cost Center"); });
|
||||
|
||||
frm.add_custom_button(__("Budget"), function () {
|
||||
frappe.set_route("List", "Budget", { cost_center: frm.doc.name });
|
||||
});
|
||||
frm.add_custom_button(__('Budget'),
|
||||
function() { frappe.set_route("List", "Budget", {'cost_center': frm.doc.name}); });
|
||||
}
|
||||
},
|
||||
update_cost_center_number: function (frm) {
|
||||
update_cost_center_number: function(frm) {
|
||||
var d = new frappe.ui.Dialog({
|
||||
title: __("Update Cost Center Name / Number"),
|
||||
title: __('Update Cost Center Name / Number'),
|
||||
fields: [
|
||||
{
|
||||
label: "Cost Center Name",
|
||||
fieldname: "cost_center_name",
|
||||
fieldtype: "Data",
|
||||
reqd: 1,
|
||||
default: frm.doc.cost_center_name,
|
||||
"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",
|
||||
default: frm.doc.cost_center_number,
|
||||
"label": "Cost Center Number",
|
||||
"fieldname": "cost_center_number",
|
||||
"fieldtype": "Data",
|
||||
"default": frm.doc.cost_center_number
|
||||
},
|
||||
{
|
||||
label: __("Merge with existing"),
|
||||
fieldname: "merge",
|
||||
fieldtype: "Check",
|
||||
default: 0,
|
||||
},
|
||||
"label": __("Merge with existing"),
|
||||
"fieldname": "merge",
|
||||
"fieldtype": "Check",
|
||||
"default": 0
|
||||
}
|
||||
],
|
||||
primary_action: function () {
|
||||
primary_action: function() {
|
||||
let data = d.get_values();
|
||||
if (
|
||||
data.cost_center_name === frm.doc.cost_center_name &&
|
||||
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;
|
||||
}
|
||||
@@ -88,12 +83,12 @@ frappe.ui.form.on("Cost Center", {
|
||||
cost_center_name: data.cost_center_name,
|
||||
cost_center_number: cstr(data.cost_center_number),
|
||||
company: frm.doc.company,
|
||||
merge: data.merge,
|
||||
merge: data.merge
|
||||
},
|
||||
callback: function (r) {
|
||||
callback: function(r) {
|
||||
frappe.dom.unfreeze();
|
||||
if (!r.exc) {
|
||||
if (r.message) {
|
||||
if(!r.exc) {
|
||||
if(r.message) {
|
||||
frappe.set_route("Form", "Cost Center", r.message);
|
||||
} else {
|
||||
frm.set_value("cost_center_name", data.cost_center_name);
|
||||
@@ -101,42 +96,44 @@ frappe.ui.form.on("Cost Center", {
|
||||
}
|
||||
d.hide();
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
},
|
||||
primary_action_label: __("Update"),
|
||||
primary_action_label: __('Update')
|
||||
});
|
||||
d.show();
|
||||
},
|
||||
|
||||
parent_cost_center(frm) {
|
||||
if (!frm.doc.company) {
|
||||
frappe.msgprint(__("Please enter company name first"));
|
||||
if(!frm.doc.company) {
|
||||
frappe.msgprint(__('Please enter company name first'));
|
||||
}
|
||||
},
|
||||
|
||||
hide_unhide_group_ledger(frm) {
|
||||
let doc = frm.doc;
|
||||
if (doc.is_group == 1) {
|
||||
frm.add_custom_button(__("Convert to Non-Group"), () => frm.events.convert_to_ledger(frm));
|
||||
frm.add_custom_button(__('Convert to Non-Group'),
|
||||
() => frm.events.convert_to_ledger(frm));
|
||||
} else if (doc.is_group == 0) {
|
||||
frm.add_custom_button(__("Convert to Group"), () => frm.events.convert_to_group(frm));
|
||||
frm.add_custom_button(__('Convert to Group'),
|
||||
() => frm.events.convert_to_group(frm));
|
||||
}
|
||||
},
|
||||
|
||||
convert_to_group(frm) {
|
||||
frm.call("convert_ledger_to_group").then((r) => {
|
||||
if (r.message === 1) {
|
||||
frm.call('convert_ledger_to_group').then(r => {
|
||||
if(r.message === 1) {
|
||||
frm.refresh();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
convert_to_ledger(frm) {
|
||||
frm.call("convert_group_to_ledger").then((r) => {
|
||||
if (r.message === 1) {
|
||||
frm.call('convert_group_to_ledger').then(r => {
|
||||
if(r.message === 1) {
|
||||
frm.refresh();
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Cost Center Number",
|
||||
"read_only_depends_on": "eval:!doc.__islocal"
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "parent_cost_center",
|
||||
@@ -125,7 +125,7 @@
|
||||
"idx": 1,
|
||||
"is_tree": 1,
|
||||
"links": [],
|
||||
"modified": "2024-04-24 10:55:54.083042",
|
||||
"modified": "2022-01-31 13:22:58.916273",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Cost Center",
|
||||
@@ -163,15 +163,6 @@
|
||||
{
|
||||
"read": 1,
|
||||
"role": "Purchase User"
|
||||
},
|
||||
{
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"report": 1,
|
||||
"role": "Employee",
|
||||
"select": 1,
|
||||
"share": 1
|
||||
}
|
||||
],
|
||||
"search_fields": "parent_cost_center, is_group",
|
||||
@@ -179,4 +170,4 @@
|
||||
"sort_field": "modified",
|
||||
"sort_order": "ASC",
|
||||
"states": []
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,9 @@ class CostCenter(NestedSet):
|
||||
def autoname(self):
|
||||
from erpnext.accounts.utils import get_autoname_with_number
|
||||
|
||||
self.name = get_autoname_with_number(self.cost_center_number, self.cost_center_name, self.company)
|
||||
self.name = get_autoname_with_number(
|
||||
self.cost_center_number, self.cost_center_name, self.company
|
||||
)
|
||||
|
||||
def validate(self):
|
||||
self.validate_mandatory()
|
||||
@@ -107,14 +109,14 @@ class CostCenter(NestedSet):
|
||||
new_cost_center = get_name_with_abbr(newdn, self.company)
|
||||
|
||||
# Validate properties before merging
|
||||
super().before_rename(olddn, new_cost_center, merge, "is_group")
|
||||
super(CostCenter, self).before_rename(olddn, new_cost_center, merge, "is_group")
|
||||
if not merge:
|
||||
new_cost_center = get_name_with_number(new_cost_center, self.cost_center_number)
|
||||
|
||||
return new_cost_center
|
||||
|
||||
def after_rename(self, olddn, newdn, merge=False):
|
||||
super().after_rename(olddn, newdn, merge)
|
||||
super(CostCenter, self).after_rename(olddn, newdn, merge)
|
||||
|
||||
if not merge:
|
||||
new_cost_center = frappe.db.get_value(
|
||||
|
||||
@@ -1,84 +1,54 @@
|
||||
frappe.treeview_settings["Cost Center"] = {
|
||||
breadcrumb: "Accounts",
|
||||
get_tree_root: false,
|
||||
filters: [
|
||||
{
|
||||
fieldname: "company",
|
||||
fieldtype: "Select",
|
||||
options: erpnext.utils.get_tree_options("company"),
|
||||
label: __("Company"),
|
||||
default: erpnext.utils.get_tree_default("company"),
|
||||
},
|
||||
],
|
||||
filters: [{
|
||||
fieldname: "company",
|
||||
fieldtype:"Select",
|
||||
options: erpnext.utils.get_tree_options("company"),
|
||||
label: __("Company"),
|
||||
default: erpnext.utils.get_tree_default("company")
|
||||
}],
|
||||
root_label: "Cost Centers",
|
||||
get_tree_nodes: "erpnext.accounts.utils.get_children",
|
||||
add_tree_node: "erpnext.accounts.utils.add_cc",
|
||||
menu_items: [
|
||||
get_tree_nodes: 'erpnext.accounts.utils.get_children',
|
||||
add_tree_node: 'erpnext.accounts.utils.add_cc',
|
||||
menu_items:[
|
||||
{
|
||||
label: __("New Company"),
|
||||
action: function () {
|
||||
frappe.new_doc("Company", true);
|
||||
},
|
||||
condition: 'frappe.boot.user.can_create.indexOf("Company") !== -1',
|
||||
},
|
||||
label: __('New Company'),
|
||||
action: function() { frappe.new_doc("Company", true) },
|
||||
condition: 'frappe.boot.user.can_create.indexOf("Company") !== -1'
|
||||
}
|
||||
],
|
||||
fields: [
|
||||
{ fieldtype: "Data", fieldname: "cost_center_name", label: __("New Cost Center Name"), reqd: true },
|
||||
{
|
||||
fieldtype: "Check",
|
||||
fieldname: "is_group",
|
||||
label: __("Is Group"),
|
||||
description: __(
|
||||
"Further cost centers can be made under Groups but entries can be made against non-Groups"
|
||||
),
|
||||
},
|
||||
{
|
||||
fieldtype: "Data",
|
||||
fieldname: "cost_center_number",
|
||||
label: __("Cost Center Number"),
|
||||
description: __(
|
||||
"Number of new Cost Center, it will be included in the cost center name as a prefix"
|
||||
),
|
||||
},
|
||||
fields:[
|
||||
{fieldtype:'Data', fieldname:'cost_center_name', label:__('New Cost Center Name'), reqd:true},
|
||||
{fieldtype:'Check', fieldname:'is_group', label:__('Is Group'),
|
||||
description:__('Further cost centers can be made under Groups but entries can be made against non-Groups')},
|
||||
{fieldtype:'Data', fieldname:'cost_center_number', label:__('Cost Center Number'),
|
||||
description: __("Number of new Cost Center, it will be included in the cost center name as a prefix")}
|
||||
],
|
||||
ignore_fields: ["parent_cost_center"],
|
||||
onload: function (treeview) {
|
||||
ignore_fields:["parent_cost_center"],
|
||||
onload: function(treeview) {
|
||||
function get_company() {
|
||||
return treeview.page.fields_dict.company.get_value();
|
||||
}
|
||||
|
||||
// tools
|
||||
treeview.page.add_inner_button(
|
||||
__("Chart of Accounts"),
|
||||
function () {
|
||||
frappe.set_route("Tree", "Account", { company: get_company() });
|
||||
},
|
||||
__("View")
|
||||
);
|
||||
treeview.page.add_inner_button(__("Chart of Accounts"), function() {
|
||||
frappe.set_route('Tree', 'Account', {company: get_company()});
|
||||
}, __('View'));
|
||||
|
||||
// make
|
||||
treeview.page.add_inner_button(
|
||||
__("Budget List"),
|
||||
function () {
|
||||
frappe.set_route("List", "Budget", { company: get_company() });
|
||||
},
|
||||
__("Budget")
|
||||
);
|
||||
treeview.page.add_inner_button(__("Budget List"), function() {
|
||||
frappe.set_route('List', 'Budget', {company: get_company()});
|
||||
}, __('Budget'));
|
||||
|
||||
treeview.page.add_inner_button(
|
||||
__("Monthly Distribution"),
|
||||
function () {
|
||||
frappe.set_route("List", "Monthly Distribution", { company: get_company() });
|
||||
},
|
||||
__("Budget")
|
||||
);
|
||||
treeview.page.add_inner_button(__("Monthly Distribution"), function() {
|
||||
frappe.set_route('List', 'Monthly Distribution', {company: get_company()});
|
||||
}, __('Budget'));
|
||||
|
||||
treeview.page.add_inner_button(
|
||||
__("Budget Variance Report"),
|
||||
function () {
|
||||
frappe.set_route("query-report", "Budget Variance Report", { company: get_company() });
|
||||
},
|
||||
__("Budget")
|
||||
);
|
||||
},
|
||||
};
|
||||
treeview.page.add_inner_button(__("Budget Variance Report"), function() {
|
||||
frappe.set_route('query-report', 'Budget Variance Report', {company: get_company()});
|
||||
}, __('Budget'));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ test_records = frappe.get_test_records("Cost Center")
|
||||
|
||||
class TestCostCenter(unittest.TestCase):
|
||||
def test_cost_center_creation_against_child_node(self):
|
||||
|
||||
if not frappe.db.get_value("Cost Center", {"name": "_Test Cost Center 2 - _TC"}):
|
||||
frappe.get_doc(test_records[1]).insert()
|
||||
|
||||
|
||||
@@ -1,24 +1,19 @@
|
||||
// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Cost Center Allocation", {
|
||||
setup: function (frm) {
|
||||
frm.set_query("main_cost_center", function () {
|
||||
return {
|
||||
filters: {
|
||||
company: frm.doc.company,
|
||||
is_group: 0,
|
||||
},
|
||||
};
|
||||
});
|
||||
frappe.ui.form.on('Cost Center Allocation', {
|
||||
setup: function(frm) {
|
||||
let filters = {"is_group": 0};
|
||||
if (frm.doc.company) {
|
||||
$.extend(filters, {
|
||||
"company": frm.doc.company
|
||||
});
|
||||
}
|
||||
|
||||
frm.set_query("cost_center", "allocation_percentages", function () {
|
||||
frm.set_query('main_cost_center', function() {
|
||||
return {
|
||||
filters: {
|
||||
company: frm.doc.company,
|
||||
is_group: 0,
|
||||
},
|
||||
filters: filters
|
||||
};
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
@@ -48,7 +48,7 @@ class CostCenterAllocation(Document):
|
||||
# end: auto-generated types
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
super(CostCenterAllocation, self).__init__(*args, **kwargs)
|
||||
self._skip_from_date_validation = False
|
||||
|
||||
def validate(self):
|
||||
@@ -63,7 +63,9 @@ class CostCenterAllocation(Document):
|
||||
total_percentage = sum([d.percentage for d in self.get("allocation_percentages", [])])
|
||||
|
||||
if total_percentage != 100:
|
||||
frappe.throw(_("Total percentage against cost centers should be 100"), WrongPercentageAllocation)
|
||||
frappe.throw(
|
||||
_("Total percentage against cost centers should be 100"), WrongPercentageAllocation
|
||||
)
|
||||
|
||||
def validate_from_date_based_on_existing_gle(self):
|
||||
# Check if GLE exists against the main cost center
|
||||
|
||||
@@ -1,41 +1,44 @@
|
||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Coupon Code", {
|
||||
setup: function (frm) {
|
||||
frm.set_query("pricing_rule", function () {
|
||||
frappe.ui.form.on('Coupon Code', {
|
||||
setup: function(frm) {
|
||||
frm.set_query("pricing_rule", function() {
|
||||
return {
|
||||
filters: [["Pricing Rule", "coupon_code_based", "=", "1"]],
|
||||
filters: [
|
||||
["Pricing Rule","coupon_code_based", "=", "1"]
|
||||
]
|
||||
};
|
||||
});
|
||||
},
|
||||
coupon_name: function (frm) {
|
||||
if (frm.doc.__islocal === 1) {
|
||||
coupon_name:function(frm){
|
||||
if (frm.doc.__islocal===1) {
|
||||
frm.trigger("make_coupon_code");
|
||||
}
|
||||
},
|
||||
coupon_type: function (frm) {
|
||||
if (frm.doc.__islocal === 1) {
|
||||
coupon_type:function(frm){
|
||||
if (frm.doc.__islocal===1) {
|
||||
frm.trigger("make_coupon_code");
|
||||
}
|
||||
},
|
||||
make_coupon_code: function (frm) {
|
||||
var coupon_name = frm.doc.coupon_name;
|
||||
make_coupon_code: function(frm) {
|
||||
var coupon_name=frm.doc.coupon_name;
|
||||
var coupon_code;
|
||||
if (frm.doc.coupon_type == "Gift Card") {
|
||||
coupon_code = Math.random().toString(12).substring(2, 12).toUpperCase();
|
||||
} else if (frm.doc.coupon_type == "Promotional") {
|
||||
coupon_name = coupon_name.replace(/\s/g, "");
|
||||
coupon_code = coupon_name.toUpperCase().slice(0, 8);
|
||||
if (frm.doc.coupon_type=='Gift Card') {
|
||||
coupon_code=Math.random().toString(12).substring(2, 12).toUpperCase();
|
||||
}
|
||||
frm.doc.coupon_code = coupon_code;
|
||||
frm.refresh_field("coupon_code");
|
||||
else if(frm.doc.coupon_type=='Promotional'){
|
||||
coupon_name=coupon_name.replace(/\s/g,'');
|
||||
coupon_code=coupon_name.toUpperCase().slice(0,8);
|
||||
}
|
||||
frm.doc.coupon_code=coupon_code;
|
||||
frm.refresh_field('coupon_code');
|
||||
},
|
||||
refresh: function (frm) {
|
||||
refresh: function(frm) {
|
||||
if (frm.doc.pricing_rule) {
|
||||
frm.add_custom_button(__("Add/Edit Coupon Conditions"), function () {
|
||||
frm.add_custom_button(__("Add/Edit Coupon Conditions"), function(){
|
||||
frappe.set_route("Form", "Pricing Rule", frm.doc.pricing_rule);
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,41 +1,28 @@
|
||||
// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Currency Exchange Settings", {
|
||||
service_provider: function (frm) {
|
||||
frm.call({
|
||||
method: "erpnext.accounts.doctype.currency_exchange_settings.currency_exchange_settings.get_api_endpoint",
|
||||
args: {
|
||||
service_provider: frm.doc.service_provider,
|
||||
use_http: frm.doc.use_http,
|
||||
},
|
||||
callback: function (r) {
|
||||
if (r && r.message) {
|
||||
if (frm.doc.service_provider == "exchangerate.host") {
|
||||
let result = ["result"];
|
||||
let params = {
|
||||
date: "{transaction_date}",
|
||||
from: "{from_currency}",
|
||||
to: "{to_currency}",
|
||||
};
|
||||
add_param(frm, r.message, params, result);
|
||||
} else if (frm.doc.service_provider == "frankfurter.app") {
|
||||
let result = ["rates", "{to_currency}"];
|
||||
let params = {
|
||||
base: "{from_currency}",
|
||||
symbols: "{to_currency}",
|
||||
};
|
||||
add_param(frm, r.message, params, result);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
use_http: function (frm) {
|
||||
frm.trigger("service_provider");
|
||||
},
|
||||
frappe.ui.form.on('Currency Exchange Settings', {
|
||||
service_provider: function(frm) {
|
||||
if (frm.doc.service_provider == "exchangerate.host") {
|
||||
let result = ['result'];
|
||||
let params = {
|
||||
date: '{transaction_date}',
|
||||
from: '{from_currency}',
|
||||
to: '{to_currency}'
|
||||
};
|
||||
add_param(frm, "https://api.exchangerate.host/convert", params, result);
|
||||
} else if (frm.doc.service_provider == "frankfurter.app") {
|
||||
let result = ['rates', '{to_currency}'];
|
||||
let params = {
|
||||
base: '{from_currency}',
|
||||
symbols: '{to_currency}'
|
||||
};
|
||||
add_param(frm, "https://frankfurter.app/{transaction_date}", params, result);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function add_param(frm, api, params, result) {
|
||||
var row;
|
||||
frm.clear_table("req_params");
|
||||
@@ -43,13 +30,13 @@ function add_param(frm, api, params, result) {
|
||||
|
||||
frm.doc.api_endpoint = api;
|
||||
|
||||
$.each(params, function (key, value) {
|
||||
$.each(params, function(key, value) {
|
||||
row = frm.add_child("req_params");
|
||||
row.key = key;
|
||||
row.value = value;
|
||||
});
|
||||
|
||||
$.each(result, function (key, value) {
|
||||
$.each(result, function(key, value) {
|
||||
row = frm.add_child("result_key");
|
||||
row.key = value;
|
||||
});
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
"disabled",
|
||||
"service_provider",
|
||||
"api_endpoint",
|
||||
"use_http",
|
||||
"access_key",
|
||||
"url",
|
||||
"column_break_3",
|
||||
@@ -92,19 +91,12 @@
|
||||
"fieldname": "access_key",
|
||||
"fieldtype": "Data",
|
||||
"label": "Access Key"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval: doc.service_provider != \"Custom\"",
|
||||
"fieldname": "use_http",
|
||||
"fieldtype": "Check",
|
||||
"label": "Use HTTP Protocol"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2024-03-18 08:32:26.895076",
|
||||
"modified": "2023-10-04 15:30:25.333860",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Currency Exchange Settings",
|
||||
|
||||
@@ -31,7 +31,6 @@ class CurrencyExchangeSettings(Document):
|
||||
result_key: DF.Table[CurrencyExchangeSettingsResult]
|
||||
service_provider: DF.Literal["frankfurter.app", "exchangerate.host", "Custom"]
|
||||
url: DF.Data | None
|
||||
use_http: DF.Check
|
||||
# end: auto-generated types
|
||||
|
||||
def validate(self):
|
||||
@@ -43,6 +42,7 @@ class CurrencyExchangeSettings(Document):
|
||||
|
||||
def set_parameters_and_result(self):
|
||||
if self.service_provider == "exchangerate.host":
|
||||
|
||||
if not self.access_key:
|
||||
frappe.throw(
|
||||
_("Access Key is required for Service Provider: {0}").format(
|
||||
@@ -53,7 +53,7 @@ class CurrencyExchangeSettings(Document):
|
||||
self.set("result_key", [])
|
||||
self.set("req_params", [])
|
||||
|
||||
self.api_endpoint = get_api_endpoint(self.service_provider, self.use_http)
|
||||
self.api_endpoint = "https://api.exchangerate.host/convert"
|
||||
self.append("result_key", {"key": "result"})
|
||||
self.append("req_params", {"key": "access_key", "value": self.access_key})
|
||||
self.append("req_params", {"key": "amount", "value": "1"})
|
||||
@@ -64,7 +64,7 @@ class CurrencyExchangeSettings(Document):
|
||||
self.set("result_key", [])
|
||||
self.set("req_params", [])
|
||||
|
||||
self.api_endpoint = get_api_endpoint(self.service_provider, self.use_http)
|
||||
self.api_endpoint = "https://frankfurter.app/{transaction_date}"
|
||||
self.append("result_key", {"key": "rates"})
|
||||
self.append("result_key", {"key": "{to_currency}"})
|
||||
self.append("req_params", {"key": "base", "value": "{from_currency}"})
|
||||
@@ -77,7 +77,9 @@ class CurrencyExchangeSettings(Document):
|
||||
transaction_date=nowdate(), to_currency="INR", from_currency="USD"
|
||||
)
|
||||
|
||||
api_url = self.api_endpoint.format(transaction_date=nowdate(), to_currency="INR", from_currency="USD")
|
||||
api_url = self.api_endpoint.format(
|
||||
transaction_date=nowdate(), to_currency="INR", from_currency="USD"
|
||||
)
|
||||
|
||||
try:
|
||||
response = requests.get(api_url, params=params)
|
||||
@@ -97,23 +99,7 @@ class CurrencyExchangeSettings(Document):
|
||||
]
|
||||
except Exception:
|
||||
frappe.throw(_("Invalid result key. Response:") + " " + response.text)
|
||||
if not isinstance(value, int | float):
|
||||
if not isinstance(value, (int, float)):
|
||||
frappe.throw(_("Returned exchange rate is neither integer not float."))
|
||||
|
||||
self.url = response.url
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_api_endpoint(service_provider: str | None = None, use_http: bool = False):
|
||||
if service_provider and service_provider in ["exchangerate.host", "frankfurter.app"]:
|
||||
if service_provider == "exchangerate.host":
|
||||
api = "api.exchangerate.host/convert"
|
||||
elif service_provider == "frankfurter.app":
|
||||
api = "frankfurter.app/{transaction_date}"
|
||||
|
||||
protocol = "https://"
|
||||
if use_http:
|
||||
protocol = "http://"
|
||||
|
||||
return protocol + api
|
||||
return None
|
||||
|
||||
@@ -10,7 +10,7 @@ frappe.ui.form.on("Dunning", {
|
||||
company: frm.doc.company,
|
||||
customer: frm.doc.customer,
|
||||
outstanding_amount: [">", 0],
|
||||
status: "Overdue",
|
||||
status: "Overdue"
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -19,16 +19,16 @@ frappe.ui.form.on("Dunning", {
|
||||
filters: {
|
||||
company: frm.doc.company,
|
||||
root_type: "Income",
|
||||
is_group: 0,
|
||||
},
|
||||
is_group: 0
|
||||
}
|
||||
};
|
||||
});
|
||||
frm.set_query("cost_center", () => {
|
||||
return {
|
||||
filters: {
|
||||
company: frm.doc.company,
|
||||
is_group: 0,
|
||||
},
|
||||
is_group: 0
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -51,8 +51,7 @@ frappe.ui.form.on("Dunning", {
|
||||
__("Payment"),
|
||||
function () {
|
||||
frm.events.make_payment_entry(frm);
|
||||
},
|
||||
__("Create")
|
||||
}, __("Create")
|
||||
);
|
||||
frm.page.set_inner_btn_group_as_primary(__("Create"));
|
||||
}
|
||||
@@ -70,7 +69,7 @@ frappe.ui.form.on("Dunning", {
|
||||
get_query_filters: {
|
||||
docstatus: 1,
|
||||
status: "Overdue",
|
||||
company: frm.doc.company,
|
||||
company: frm.doc.company
|
||||
},
|
||||
allow_child_item_selection: true,
|
||||
child_fieldname: "payment_schedule",
|
||||
@@ -79,12 +78,9 @@ frappe.ui.form.on("Dunning", {
|
||||
});
|
||||
}
|
||||
|
||||
frappe.dynamic_link = { doc: frm.doc, fieldname: "customer", doctype: "Customer" };
|
||||
frappe.dynamic_link = { doc: frm.doc, fieldname: 'customer', doctype: 'Customer' };
|
||||
|
||||
frm.toggle_display(
|
||||
"customer_name",
|
||||
frm.doc.customer_name && frm.doc.customer_name !== frm.doc.customer
|
||||
);
|
||||
frm.toggle_display("customer_name", (frm.doc.customer_name && frm.doc.customer_name !== frm.doc.customer));
|
||||
},
|
||||
// When multiple companies are set up. in case company name is changed set default company address
|
||||
company: function (frm) {
|
||||
@@ -94,8 +90,8 @@ frappe.ui.form.on("Dunning", {
|
||||
args: { name: frm.doc.company, existing_address: frm.doc.company_address || "" },
|
||||
debounce: 2000,
|
||||
callback: function (r) {
|
||||
frm.set_value("company_address", (r && r.message) || "");
|
||||
},
|
||||
frm.set_value("company_address", r && r.message || "");
|
||||
}
|
||||
});
|
||||
|
||||
if (frm.fields_dict.currency) {
|
||||
@@ -129,16 +125,16 @@ frappe.ui.form.on("Dunning", {
|
||||
transaction_date: frm.doc.posting_date,
|
||||
from_currency: frm.doc.currency,
|
||||
to_currency: company_currency,
|
||||
args: "for_selling",
|
||||
args: "for_selling"
|
||||
},
|
||||
freeze: true,
|
||||
freeze_message: __("Fetching exchange rates ..."),
|
||||
callback: function (r) {
|
||||
callback: function(r) {
|
||||
const exchange_rate = flt(r.message);
|
||||
if (exchange_rate != frm.doc.conversion_rate) {
|
||||
frm.set_value("conversion_rate", exchange_rate);
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
} else {
|
||||
frm.trigger("conversion_rate");
|
||||
@@ -170,7 +166,8 @@ frappe.ui.form.on("Dunning", {
|
||||
get_dunning_letter_text: function (frm) {
|
||||
if (frm.doc.dunning_type) {
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.dunning.dunning.get_dunning_letter_text",
|
||||
method:
|
||||
"erpnext.accounts.doctype.dunning.dunning.get_dunning_letter_text",
|
||||
args: {
|
||||
dunning_type: frm.doc.dunning_type,
|
||||
language: frm.doc.language,
|
||||
@@ -207,7 +204,10 @@ frappe.ui.form.on("Dunning", {
|
||||
calculate_overdue_days: function (frm) {
|
||||
frm.doc.overdue_payments.forEach((row) => {
|
||||
if (frm.doc.posting_date && row.due_date) {
|
||||
const overdue_days = moment(frm.doc.posting_date).diff(row.due_date, "days");
|
||||
const overdue_days = moment(frm.doc.posting_date).diff(
|
||||
row.due_date,
|
||||
"days"
|
||||
);
|
||||
frappe.model.set_value(row.doctype, row.name, "overdue_days", overdue_days);
|
||||
}
|
||||
});
|
||||
@@ -215,16 +215,15 @@ frappe.ui.form.on("Dunning", {
|
||||
calculate_interest: function (frm) {
|
||||
frm.doc.overdue_payments.forEach((row) => {
|
||||
const interest_per_day = frm.doc.rate_of_interest / 100 / 365;
|
||||
const interest = flt(
|
||||
interest_per_day * row.overdue_days * row.outstanding,
|
||||
precision("interest", row)
|
||||
);
|
||||
const interest = flt((interest_per_day * row.overdue_days * row.outstanding), precision("interest", row));
|
||||
frappe.model.set_value(row.doctype, row.name, "interest", interest);
|
||||
});
|
||||
},
|
||||
calculate_totals: function (frm) {
|
||||
const total_interest = frm.doc.overdue_payments.reduce((prev, cur) => prev + cur.interest, 0);
|
||||
const total_outstanding = frm.doc.overdue_payments.reduce((prev, cur) => prev + cur.outstanding, 0);
|
||||
const total_interest = frm.doc.overdue_payments
|
||||
.reduce((prev, cur) => prev + cur.interest, 0);
|
||||
const total_outstanding = frm.doc.overdue_payments
|
||||
.reduce((prev, cur) => prev + cur.outstanding, 0);
|
||||
const dunning_amount = total_interest + frm.doc.dunning_fee;
|
||||
const base_dunning_amount = dunning_amount * frm.doc.conversion_rate;
|
||||
const grand_total = total_outstanding + dunning_amount;
|
||||
@@ -241,7 +240,8 @@ frappe.ui.form.on("Dunning", {
|
||||
},
|
||||
make_payment_entry: function (frm) {
|
||||
return frappe.call({
|
||||
method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry",
|
||||
method:
|
||||
"erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry",
|
||||
args: {
|
||||
dt: frm.doc.doctype,
|
||||
dn: frm.doc.name,
|
||||
@@ -257,5 +257,5 @@ frappe.ui.form.on("Dunning", {
|
||||
frappe.ui.form.on("Overdue Payment", {
|
||||
interest: function (frm) {
|
||||
frm.trigger("calculate_totals");
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -139,10 +139,6 @@ class Dunning(AccountsController):
|
||||
)
|
||||
row.dunning_level = len(past_dunnings) + 1
|
||||
|
||||
def on_cancel(self):
|
||||
super().on_cancel()
|
||||
self.ignore_linked_doctypes = ["GL Entry"]
|
||||
|
||||
|
||||
def resolve_dunning(doc, state):
|
||||
"""
|
||||
|
||||
@@ -109,7 +109,9 @@ class TestDunning(FrappeTestCase):
|
||||
|
||||
def create_dunning(overdue_days, dunning_type_name=None):
|
||||
posting_date = add_days(today(), -1 * overdue_days)
|
||||
sales_invoice = create_sales_invoice_against_cost_center(posting_date=posting_date, qty=1, rate=100)
|
||||
sales_invoice = create_sales_invoice_against_cost_center(
|
||||
posting_date=posting_date, qty=1, rate=100
|
||||
)
|
||||
dunning = create_dunning_from_sales_invoice(sales_invoice.name)
|
||||
|
||||
if dunning_type_name:
|
||||
|
||||
@@ -1,79 +1,75 @@
|
||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Exchange Rate Revaluation", {
|
||||
setup: function (frm) {
|
||||
frm.set_query("party_type", "accounts", function () {
|
||||
frappe.ui.form.on('Exchange Rate Revaluation', {
|
||||
setup: function(frm) {
|
||||
frm.set_query("party_type", "accounts", function() {
|
||||
return {
|
||||
filters: {
|
||||
name: ["in", Object.keys(frappe.boot.party_account_types)],
|
||||
},
|
||||
"filters": {
|
||||
"name": ["in", Object.keys(frappe.boot.party_account_types)],
|
||||
}
|
||||
};
|
||||
});
|
||||
frm.set_query("account", "accounts", function (doc) {
|
||||
frm.set_query("account", "accounts", function(doc) {
|
||||
return {
|
||||
filters: {
|
||||
company: doc.company,
|
||||
},
|
||||
"filters": {
|
||||
"company": doc.company
|
||||
}
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
refresh: function (frm) {
|
||||
if (frm.doc.docstatus == 1) {
|
||||
refresh: function(frm) {
|
||||
if(frm.doc.docstatus==1) {
|
||||
frappe.call({
|
||||
method: "check_journal_entry_condition",
|
||||
method: 'check_journal_entry_condition',
|
||||
doc: frm.doc,
|
||||
callback: function (r) {
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
frm.add_custom_button(
|
||||
__("Journal Entries"),
|
||||
function () {
|
||||
return frm.events.make_jv(frm);
|
||||
},
|
||||
__("Create")
|
||||
);
|
||||
frm.add_custom_button(__('Journal Entries'), function() {
|
||||
return frm.events.make_jv(frm);
|
||||
}, __('Create'));
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
validate_rounding_loss: function (frm) {
|
||||
validate_rounding_loss: function(frm) {
|
||||
let allowance = frm.doc.rounding_loss_allowance;
|
||||
if (!(allowance >= 0 && allowance < 1)) {
|
||||
frappe.throw(__("Rounding Loss Allowance should be between 0 and 1"));
|
||||
}
|
||||
},
|
||||
|
||||
rounding_loss_allowance: function (frm) {
|
||||
rounding_loss_allowance: function(frm) {
|
||||
frm.events.validate_rounding_loss(frm);
|
||||
},
|
||||
|
||||
validate: function (frm) {
|
||||
validate: function(frm) {
|
||||
frm.events.validate_rounding_loss(frm);
|
||||
},
|
||||
|
||||
get_entries: function (frm, account) {
|
||||
get_entries: function(frm, account) {
|
||||
frappe.call({
|
||||
method: "get_accounts_data",
|
||||
doc: cur_frm.doc,
|
||||
account: account,
|
||||
callback: function (r) {
|
||||
callback: function(r){
|
||||
frappe.model.clear_table(frm.doc, "accounts");
|
||||
if (r.message) {
|
||||
if(r.message) {
|
||||
r.message.forEach((d) => {
|
||||
cur_frm.add_child("accounts", d);
|
||||
cur_frm.add_child("accounts",d);
|
||||
});
|
||||
frm.events.get_total_gain_loss(frm);
|
||||
refresh_field("accounts");
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
get_total_gain_loss: function (frm) {
|
||||
if (!(frm.doc.accounts && frm.doc.accounts.length)) return;
|
||||
get_total_gain_loss: function(frm) {
|
||||
if(!(frm.doc.accounts && frm.doc.accounts.length)) return;
|
||||
|
||||
let total_gain_loss = 0;
|
||||
frm.doc.accounts.forEach((d) => {
|
||||
@@ -84,7 +80,7 @@ frappe.ui.form.on("Exchange Rate Revaluation", {
|
||||
frm.refresh_fields();
|
||||
},
|
||||
|
||||
make_jv: function (frm) {
|
||||
make_jv : function(frm) {
|
||||
let revaluation_journal = null;
|
||||
let zero_balance_journal = null;
|
||||
frappe.call({
|
||||
@@ -92,68 +88,66 @@ frappe.ui.form.on("Exchange Rate Revaluation", {
|
||||
doc: frm.doc,
|
||||
freeze: true,
|
||||
freeze_message: "Making Journal Entries...",
|
||||
callback: function (r) {
|
||||
callback: function(r){
|
||||
if (r.message) {
|
||||
let response = r.message;
|
||||
if (response["revaluation_jv"] || response["zero_balance_jv"]) {
|
||||
if(response['revaluation_jv'] || response['zero_balance_jv']) {
|
||||
frappe.msgprint(__("Journals have been created"));
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Exchange Rate Revaluation Account", {
|
||||
new_exchange_rate: function (frm, cdt, cdn) {
|
||||
new_exchange_rate: function(frm, cdt, cdn) {
|
||||
var row = frappe.get_doc(cdt, cdn);
|
||||
row.new_balance_in_base_currency = flt(
|
||||
row.new_exchange_rate * flt(row.balance_in_account_currency),
|
||||
precision("new_balance_in_base_currency", row)
|
||||
);
|
||||
row.new_balance_in_base_currency = flt(row.new_exchange_rate * flt(row.balance_in_account_currency),
|
||||
precision("new_balance_in_base_currency", row));
|
||||
row.gain_loss = row.new_balance_in_base_currency - flt(row.balance_in_base_currency);
|
||||
refresh_field("accounts");
|
||||
frm.events.get_total_gain_loss(frm);
|
||||
},
|
||||
|
||||
account: function (frm, cdt, cdn) {
|
||||
account: function(frm, cdt, cdn) {
|
||||
var row = locals[cdt][cdn];
|
||||
if (row.account) {
|
||||
get_account_details(frm, cdt, cdn);
|
||||
}
|
||||
},
|
||||
|
||||
party: function (frm, cdt, cdn) {
|
||||
party: function(frm, cdt, cdn) {
|
||||
var row = locals[cdt][cdn];
|
||||
if (row.party && row.account) {
|
||||
get_account_details(frm, cdt, cdn);
|
||||
}
|
||||
},
|
||||
|
||||
accounts_remove: function (frm) {
|
||||
accounts_remove: function(frm) {
|
||||
frm.events.get_total_gain_loss(frm);
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
var get_account_details = function (frm, cdt, cdn) {
|
||||
var get_account_details = function(frm, cdt, cdn) {
|
||||
var row = frappe.get_doc(cdt, cdn);
|
||||
if (!frm.doc.company || !frm.doc.posting_date) {
|
||||
if(!frm.doc.company || !frm.doc.posting_date) {
|
||||
frappe.throw(__("Please select Company and Posting Date to getting entries"));
|
||||
}
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.exchange_rate_revaluation.exchange_rate_revaluation.get_account_details",
|
||||
args: {
|
||||
args:{
|
||||
account: row.account,
|
||||
company: frm.doc.company,
|
||||
posting_date: frm.doc.posting_date,
|
||||
party_type: row.party_type,
|
||||
party: row.party,
|
||||
rounding_loss_allowance: frm.doc.rounding_loss_allowance,
|
||||
rounding_loss_allowance: frm.doc.rounding_loss_allowance
|
||||
},
|
||||
callback: function (r) {
|
||||
callback: function(r){
|
||||
$.extend(row, r.message);
|
||||
refresh_field("accounts");
|
||||
frm.events.get_total_gain_loss(frm);
|
||||
},
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -44,7 +44,7 @@ class ExchangeRateRevaluation(Document):
|
||||
self.set_total_gain_loss()
|
||||
|
||||
def validate_rounding_loss_allowance(self):
|
||||
if not (self.rounding_loss_allowance >= 0 and self.rounding_loss_allowance < 1):
|
||||
if self.rounding_loss_allowance < 0 or self.rounding_loss_allowance >= 1:
|
||||
frappe.throw(_("Rounding Loss Allowance should be between 0 and 1"))
|
||||
|
||||
def set_total_gain_loss(self):
|
||||
@@ -268,6 +268,7 @@ class ExchangeRateRevaluation(Document):
|
||||
|
||||
# Handle Accounts with '0' balance in Account/Base Currency
|
||||
for d in [x for x in account_details if x.zero_balance]:
|
||||
|
||||
if d.balance != 0:
|
||||
current_exchange_rate = new_exchange_rate = 0
|
||||
|
||||
@@ -280,8 +281,7 @@ class ExchangeRateRevaluation(Document):
|
||||
new_balance_in_account_currency = 0
|
||||
|
||||
current_exchange_rate = (
|
||||
calculate_exchange_rate_using_last_gle(company, d.account, d.party_type, d.party)
|
||||
or 0.0
|
||||
calculate_exchange_rate_using_last_gle(company, d.account, d.party_type, d.party) or 0.0
|
||||
)
|
||||
|
||||
gain_loss = new_balance_in_account_currency - (
|
||||
@@ -335,7 +335,9 @@ class ExchangeRateRevaluation(Document):
|
||||
|
||||
revaluation_jv = self.make_jv_for_revaluation()
|
||||
if revaluation_jv:
|
||||
frappe.msgprint(f"Revaluation Journal: {get_link_to_form('Journal Entry', revaluation_jv.name)}")
|
||||
frappe.msgprint(
|
||||
f"Revaluation Journal: {get_link_to_form('Journal Entry', revaluation_jv.name)}"
|
||||
)
|
||||
|
||||
return {
|
||||
"revaluation_jv": revaluation_jv.name if revaluation_jv else None,
|
||||
@@ -392,8 +394,7 @@ class ExchangeRateRevaluation(Document):
|
||||
journal_account.update(
|
||||
{
|
||||
dr_or_cr: flt(
|
||||
abs(d.get("balance_in_account_currency")),
|
||||
d.precision("balance_in_account_currency"),
|
||||
abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")
|
||||
),
|
||||
reverse_dr_or_cr: 0,
|
||||
"debit": 0,
|
||||
@@ -519,9 +520,7 @@ class ExchangeRateRevaluation(Document):
|
||||
abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")
|
||||
),
|
||||
"cost_center": erpnext.get_default_cost_center(self.company),
|
||||
"exchange_rate": flt(
|
||||
d.get("current_exchange_rate"), d.precision("current_exchange_rate")
|
||||
),
|
||||
"exchange_rate": flt(d.get("current_exchange_rate"), d.precision("current_exchange_rate")),
|
||||
"reference_type": "Exchange Rate Revaluation",
|
||||
"reference_name": self.name,
|
||||
}
|
||||
@@ -599,7 +598,7 @@ def calculate_exchange_rate_using_last_gle(company, account, party_type, party):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_account_details(
|
||||
company, posting_date, account, party_type=None, party=None, rounding_loss_allowance: float | None = None
|
||||
company, posting_date, account, party_type=None, party=None, rounding_loss_allowance: float = None
|
||||
):
|
||||
if not (company and posting_date):
|
||||
frappe.throw(_("Company and Posting Date is mandatory"))
|
||||
@@ -612,7 +611,7 @@ def get_account_details(
|
||||
frappe.throw(_("Party Type and Party is mandatory for {0} account").format(account_type))
|
||||
|
||||
account_details = {}
|
||||
erpnext.get_company_currency(company)
|
||||
company_currency = erpnext.get_company_currency(company)
|
||||
|
||||
account_details = {
|
||||
"account_currency": account_currency,
|
||||
@@ -626,22 +625,24 @@ def get_account_details(
|
||||
rounding_loss_allowance=rounding_loss_allowance,
|
||||
)
|
||||
|
||||
if account_balance and (account_balance[0].balance or account_balance[0].balance_in_account_currency):
|
||||
if account_with_new_balance := ExchangeRateRevaluation.calculate_new_account_balance(
|
||||
if account_balance and (
|
||||
account_balance[0].balance or account_balance[0].balance_in_account_currency
|
||||
):
|
||||
account_with_new_balance = ExchangeRateRevaluation.calculate_new_account_balance(
|
||||
company, posting_date, account_balance
|
||||
):
|
||||
row = account_with_new_balance[0]
|
||||
account_details.update(
|
||||
{
|
||||
"balance_in_base_currency": row["balance_in_base_currency"],
|
||||
"balance_in_account_currency": row["balance_in_account_currency"],
|
||||
"current_exchange_rate": row["current_exchange_rate"],
|
||||
"new_exchange_rate": row["new_exchange_rate"],
|
||||
"new_balance_in_base_currency": row["new_balance_in_base_currency"],
|
||||
"new_balance_in_account_currency": row["new_balance_in_account_currency"],
|
||||
"zero_balance": row["zero_balance"],
|
||||
"gain_loss": row["gain_loss"],
|
||||
}
|
||||
)
|
||||
)
|
||||
row = account_with_new_balance[0]
|
||||
account_details.update(
|
||||
{
|
||||
"balance_in_base_currency": row["balance_in_base_currency"],
|
||||
"balance_in_account_currency": row["balance_in_account_currency"],
|
||||
"current_exchange_rate": row["current_exchange_rate"],
|
||||
"new_exchange_rate": row["new_exchange_rate"],
|
||||
"new_balance_in_base_currency": row["new_balance_in_base_currency"],
|
||||
"new_balance_in_account_currency": row["new_balance_in_account_currency"],
|
||||
"zero_balance": row["zero_balance"],
|
||||
"gain_loss": row["gain_loss"],
|
||||
}
|
||||
)
|
||||
|
||||
return account_details
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe import qb
|
||||
from frappe.tests.utils import FrappeTestCase, change_settings
|
||||
from frappe.utils import add_days, flt, today
|
||||
|
||||
from erpnext import get_default_cost_center
|
||||
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
|
||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
from erpnext.accounts.party import get_party_account
|
||||
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
|
||||
from erpnext.stock.doctype.item.test_item import create_item
|
||||
|
||||
|
||||
class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
|
||||
@@ -66,7 +73,9 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
|
||||
err.extend("accounts", accounts)
|
||||
row = err.accounts[0]
|
||||
row.new_exchange_rate = 85
|
||||
row.new_balance_in_base_currency = flt(row.new_exchange_rate * flt(row.balance_in_account_currency))
|
||||
row.new_balance_in_base_currency = flt(
|
||||
row.new_exchange_rate * flt(row.balance_in_account_currency)
|
||||
)
|
||||
row.gain_loss = row.new_balance_in_base_currency - flt(row.balance_in_base_currency)
|
||||
err.set_total_gain_loss()
|
||||
err = err.save().submit()
|
||||
@@ -118,9 +127,9 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
|
||||
pe.save().submit()
|
||||
|
||||
# Cancel the auto created gain/loss JE to simulate balance only in base currency
|
||||
je = frappe.db.get_all("Journal Entry Account", filters={"reference_name": si.name}, pluck="parent")[
|
||||
0
|
||||
]
|
||||
je = frappe.db.get_all(
|
||||
"Journal Entry Account", filters={"reference_name": si.name}, pluck="parent"
|
||||
)[0]
|
||||
frappe.get_doc("Journal Entry", je).cancel()
|
||||
|
||||
err = frappe.new_doc("Exchange Rate Revaluation")
|
||||
@@ -226,9 +235,9 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
|
||||
self.assertEqual(flt(acc.debit, precision), 0.0)
|
||||
self.assertEqual(flt(acc.credit, precision), 0.0)
|
||||
|
||||
row = next(x for x in je.accounts if x.account == self.debtors_usd)
|
||||
row = [x for x in je.accounts if x.account == self.debtors_usd][0]
|
||||
self.assertEqual(flt(row.credit_in_account_currency, precision), 5.0) # in USD
|
||||
row = next(x for x in je.accounts if x.account != self.debtors_usd)
|
||||
row = [x for x in je.accounts if x.account != self.debtors_usd][0]
|
||||
self.assertEqual(flt(row.debit_in_account_currency, precision), 421.06) # in INR
|
||||
|
||||
# total_debit and total_credit will be 0.0, as JV is posting only to account currency fields
|
||||
@@ -285,5 +294,5 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
|
||||
"new_balance_in_account_currency": 100.0,
|
||||
}
|
||||
|
||||
for key, _val in expected_data.items():
|
||||
for key, val in expected_data.items():
|
||||
self.assertEqual(expected_data.get(key), account_details.get(key))
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Finance Book", {
|
||||
refresh: function (frm) {},
|
||||
frappe.ui.form.on('Finance Book', {
|
||||
refresh: function(frm) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
frappe.ui.form.on("Fiscal Year", {
|
||||
onload: function (frm) {
|
||||
if (frm.doc.__islocal) {
|
||||
frm.set_value(
|
||||
"year_start_date",
|
||||
frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)
|
||||
);
|
||||
frappe.ui.form.on('Fiscal Year', {
|
||||
onload: function(frm) {
|
||||
if(frm.doc.__islocal) {
|
||||
frm.set_value("year_start_date",
|
||||
frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1));
|
||||
}
|
||||
},
|
||||
year_start_date: function (frm) {
|
||||
year_start_date: function(frm) {
|
||||
if (!frm.doc.is_short_year) {
|
||||
let year_end_date = frappe.datetime.add_days(
|
||||
frappe.datetime.add_months(frm.doc.year_start_date, 12),
|
||||
-1
|
||||
);
|
||||
let year_end_date =
|
||||
frappe.datetime.add_days(frappe.datetime.add_months(frm.doc.year_start_date, 12), -1);
|
||||
frm.set_value("year_end_date", year_end_date);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"allow_import": 1,
|
||||
"autoname": "field:year",
|
||||
"creation": "2013-01-22 16:50:25",
|
||||
"description": "Represents a Financial Year. All accounting entries and other major transactions are tracked against the Fiscal Year.",
|
||||
"description": "**Fiscal Year** represents a Financial Year. All accounting entries and other major transactions are tracked against **Fiscal Year**.",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"engine": "InnoDB",
|
||||
@@ -82,12 +82,11 @@
|
||||
"icon": "fa fa-calendar",
|
||||
"idx": 1,
|
||||
"links": [],
|
||||
"modified": "2024-01-30 12:35:38.645968",
|
||||
"modified": "2020-11-05 12:16:53.081573",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Fiscal Year",
|
||||
"naming_rule": "By fieldname",
|
||||
"owner": "Administrator",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
@@ -119,18 +118,9 @@
|
||||
{
|
||||
"read": 1,
|
||||
"role": "Employee"
|
||||
},
|
||||
{
|
||||
"read": 1,
|
||||
"role": "Accounts Manager"
|
||||
},
|
||||
{
|
||||
"read": 1,
|
||||
"role": "Stock Manager"
|
||||
}
|
||||
],
|
||||
"show_name_in_global_search": 1,
|
||||
"sort_field": "name",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
"sort_order": "DESC"
|
||||
}
|
||||
@@ -108,9 +108,9 @@ class FiscalYear(Document):
|
||||
|
||||
if overlap:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Year start date or end date is overlapping with {0}. To avoid please set company"
|
||||
).format(existing.name),
|
||||
_("Year start date or end date is overlapping with {0}. To avoid please set company").format(
|
||||
existing.name
|
||||
),
|
||||
frappe.NameError,
|
||||
)
|
||||
|
||||
@@ -126,9 +126,9 @@ def check_duplicate_fiscal_year(doc):
|
||||
not frappe.flags.in_test
|
||||
):
|
||||
frappe.throw(
|
||||
_(
|
||||
"Fiscal Year Start Date and Fiscal Year End Date are already set in Fiscal Year {0}"
|
||||
).format(fiscal_year)
|
||||
_("Fiscal Year Start Date and Fiscal Year End Date are already set in Fiscal Year {0}").format(
|
||||
fiscal_year
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ def test_record_generator():
|
||||
]
|
||||
|
||||
start = 2012
|
||||
end = now_datetime().year + 25
|
||||
end = now_datetime().year + 5
|
||||
for year in range(start, end):
|
||||
test_records.append(
|
||||
{
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("GL Entry", {
|
||||
refresh: function (frm) {
|
||||
frm.page.btn_secondary.hide();
|
||||
},
|
||||
frappe.ui.form.on('GL Entry', {
|
||||
refresh: function(frm) {
|
||||
frm.page.btn_secondary.hide()
|
||||
}
|
||||
});
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
"account_currency",
|
||||
"debit_in_account_currency",
|
||||
"credit_in_account_currency",
|
||||
"against_type",
|
||||
"against",
|
||||
"against_link",
|
||||
"against_voucher_type",
|
||||
"against_voucher",
|
||||
"voucher_type",
|
||||
@@ -129,6 +131,13 @@
|
||||
"label": "Credit Amount in Account Currency",
|
||||
"options": "account_currency"
|
||||
},
|
||||
{
|
||||
"fieldname": "against_type",
|
||||
"fieldtype": "Link",
|
||||
"in_filter": 1,
|
||||
"label": "Against Type",
|
||||
"options": "DocType"
|
||||
},
|
||||
{
|
||||
"fieldname": "against",
|
||||
"fieldtype": "Text",
|
||||
@@ -137,6 +146,13 @@
|
||||
"oldfieldname": "against",
|
||||
"oldfieldtype": "Text"
|
||||
},
|
||||
{
|
||||
"fieldname": "against_link",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"in_filter": 1,
|
||||
"label": "Against",
|
||||
"options": "against_type"
|
||||
},
|
||||
{
|
||||
"fieldname": "against_voucher_type",
|
||||
"fieldtype": "Link",
|
||||
|
||||
@@ -13,9 +13,16 @@ import erpnext
|
||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
|
||||
get_checks_for_pl_and_bs_accounts,
|
||||
)
|
||||
from erpnext.accounts.doctype.accounting_dimension_filter.accounting_dimension_filter import (
|
||||
get_dimension_filter_map,
|
||||
)
|
||||
from erpnext.accounts.party import validate_party_frozen_disabled, validate_party_gle_currency
|
||||
from erpnext.accounts.utils import get_account_currency, get_fiscal_year
|
||||
from erpnext.exceptions import InvalidAccountCurrency
|
||||
from erpnext.exceptions import (
|
||||
InvalidAccountCurrency,
|
||||
InvalidAccountDimensionError,
|
||||
MandatoryAccountDimensionError,
|
||||
)
|
||||
|
||||
exclude_from_linked_with = True
|
||||
|
||||
@@ -91,6 +98,7 @@ class GLEntry(Document):
|
||||
if not self.flags.from_repost and self.voucher_type != "Period Closing Voucher":
|
||||
self.validate_account_details(adv_adj)
|
||||
self.validate_dimensions_for_pl_and_bs()
|
||||
self.validate_allowed_dimensions()
|
||||
validate_balance_type(self.account, adv_adj)
|
||||
validate_frozen_account(self.account, adv_adj)
|
||||
|
||||
@@ -107,18 +115,13 @@ class GLEntry(Document):
|
||||
]:
|
||||
# Update outstanding amt on against voucher
|
||||
if (
|
||||
self.against_voucher_type
|
||||
in ["Journal Entry", "Sales Invoice", "Purchase Invoice", "Fees"]
|
||||
self.against_voucher_type in ["Journal Entry", "Sales Invoice", "Purchase Invoice", "Fees"]
|
||||
and self.against_voucher
|
||||
and self.flags.update_outstanding == "Yes"
|
||||
and not frappe.flags.is_reverse_depr_entry
|
||||
):
|
||||
update_outstanding_amt(
|
||||
self.account,
|
||||
self.party_type,
|
||||
self.party,
|
||||
self.against_voucher_type,
|
||||
self.against_voucher,
|
||||
self.account, self.party_type, self.party, self.against_voucher_type, self.against_voucher
|
||||
)
|
||||
|
||||
def check_mandatory(self):
|
||||
@@ -184,13 +187,12 @@ class GLEntry(Document):
|
||||
and self.company == dimension.company
|
||||
and dimension.mandatory_for_pl
|
||||
and not dimension.disabled
|
||||
and not self.is_cancelled
|
||||
):
|
||||
if not self.get(dimension.fieldname):
|
||||
frappe.throw(
|
||||
_(
|
||||
"Accounting Dimension <b>{0}</b> is required for 'Profit and Loss' account {1}."
|
||||
).format(dimension.label, self.account)
|
||||
_("Accounting Dimension <b>{0}</b> is required for 'Profit and Loss' account {1}.").format(
|
||||
dimension.label, self.account
|
||||
)
|
||||
)
|
||||
|
||||
if (
|
||||
@@ -198,15 +200,50 @@ class GLEntry(Document):
|
||||
and self.company == dimension.company
|
||||
and dimension.mandatory_for_bs
|
||||
and not dimension.disabled
|
||||
and not self.is_cancelled
|
||||
):
|
||||
if not self.get(dimension.fieldname):
|
||||
frappe.throw(
|
||||
_(
|
||||
"Accounting Dimension <b>{0}</b> is required for 'Balance Sheet' account {1}."
|
||||
).format(dimension.label, self.account)
|
||||
_("Accounting Dimension <b>{0}</b> is required for 'Balance Sheet' account {1}.").format(
|
||||
dimension.label, self.account
|
||||
)
|
||||
)
|
||||
|
||||
def validate_allowed_dimensions(self):
|
||||
dimension_filter_map = get_dimension_filter_map()
|
||||
for key, value in dimension_filter_map.items():
|
||||
dimension = key[0]
|
||||
account = key[1]
|
||||
|
||||
if self.account == account:
|
||||
if value["is_mandatory"] and not self.get(dimension):
|
||||
frappe.throw(
|
||||
_("{0} is mandatory for account {1}").format(
|
||||
frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)
|
||||
),
|
||||
MandatoryAccountDimensionError,
|
||||
)
|
||||
|
||||
if value["allow_or_restrict"] == "Allow":
|
||||
if self.get(dimension) and self.get(dimension) not in value["allowed_dimensions"]:
|
||||
frappe.throw(
|
||||
_("Invalid value {0} for {1} against account {2}").format(
|
||||
frappe.bold(self.get(dimension)),
|
||||
frappe.bold(frappe.unscrub(dimension)),
|
||||
frappe.bold(self.account),
|
||||
),
|
||||
InvalidAccountDimensionError,
|
||||
)
|
||||
else:
|
||||
if self.get(dimension) and self.get(dimension) in value["allowed_dimensions"]:
|
||||
frappe.throw(
|
||||
_("Invalid value {0} for {1} against account {2}").format(
|
||||
frappe.bold(self.get(dimension)),
|
||||
frappe.bold(frappe.unscrub(dimension)),
|
||||
frappe.bold(self.account),
|
||||
),
|
||||
InvalidAccountDimensionError,
|
||||
)
|
||||
|
||||
def check_pl_account(self):
|
||||
if (
|
||||
self.is_opening == "Yes"
|
||||
@@ -252,7 +289,9 @@ class GLEntry(Document):
|
||||
if not self.cost_center:
|
||||
return
|
||||
|
||||
is_group, company = frappe.get_cached_value("Cost Center", self.cost_center, ["is_group", "company"])
|
||||
is_group, company = frappe.get_cached_value(
|
||||
"Cost Center", self.cost_center, ["is_group", "company"]
|
||||
)
|
||||
|
||||
if company != self.company:
|
||||
frappe.throw(
|
||||
@@ -321,7 +360,7 @@ def update_outstanding_amt(
|
||||
account, party_type, party, against_voucher_type, against_voucher, on_cancel=False
|
||||
):
|
||||
if party_type and party:
|
||||
party_condition = " and party_type={} and party={}".format(
|
||||
party_condition = " and party_type={0} and party={1}".format(
|
||||
frappe.db.escape(party_type), frappe.db.escape(party)
|
||||
)
|
||||
else:
|
||||
@@ -329,19 +368,23 @@ def update_outstanding_amt(
|
||||
|
||||
if against_voucher_type == "Sales Invoice":
|
||||
party_account = frappe.db.get_value(against_voucher_type, against_voucher, "debit_to")
|
||||
account_condition = f"and account in ({frappe.db.escape(account)}, {frappe.db.escape(party_account)})"
|
||||
account_condition = "and account in ({0}, {1})".format(
|
||||
frappe.db.escape(account), frappe.db.escape(party_account)
|
||||
)
|
||||
else:
|
||||
account_condition = f" and account = {frappe.db.escape(account)}"
|
||||
account_condition = " and account = {0}".format(frappe.db.escape(account))
|
||||
|
||||
# get final outstanding amt
|
||||
bal = flt(
|
||||
frappe.db.sql(
|
||||
f"""
|
||||
"""
|
||||
select sum(debit_in_account_currency) - sum(credit_in_account_currency)
|
||||
from `tabGL Entry`
|
||||
where against_voucher_type=%s and against_voucher=%s
|
||||
and voucher_type != 'Invoice Discounting'
|
||||
{party_condition} {account_condition}""",
|
||||
{0} {1}""".format(
|
||||
party_condition, account_condition
|
||||
),
|
||||
(against_voucher_type, against_voucher),
|
||||
)[0][0]
|
||||
or 0.0
|
||||
@@ -352,10 +395,12 @@ def update_outstanding_amt(
|
||||
elif against_voucher_type == "Journal Entry":
|
||||
against_voucher_amount = flt(
|
||||
frappe.db.sql(
|
||||
f"""
|
||||
"""
|
||||
select sum(debit_in_account_currency) - sum(credit_in_account_currency)
|
||||
from `tabGL Entry` where voucher_type = 'Journal Entry' and voucher_no = %s
|
||||
and account = %s and (against_voucher is null or against_voucher='') {party_condition}""",
|
||||
and account = %s and (against_voucher is null or against_voucher='') {0}""".format(
|
||||
party_condition
|
||||
),
|
||||
(against_voucher, account),
|
||||
)[0][0]
|
||||
)
|
||||
@@ -374,9 +419,7 @@ def update_outstanding_amt(
|
||||
# Validation : Outstanding can not be negative for JV
|
||||
if bal < 0 and not on_cancel:
|
||||
frappe.throw(
|
||||
_("Outstanding for {0} cannot be less than zero ({1})").format(
|
||||
against_voucher, fmt_money(bal)
|
||||
)
|
||||
_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal))
|
||||
)
|
||||
|
||||
if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]:
|
||||
@@ -392,7 +435,9 @@ def update_outstanding_amt(
|
||||
def validate_frozen_account(account, adv_adj=None):
|
||||
frozen_account = frappe.get_cached_value("Account", account, "freeze_account")
|
||||
if frozen_account == "Yes" and not adv_adj:
|
||||
frozen_accounts_modifier = frappe.db.get_value("Accounts Settings", None, "frozen_accounts_modifier")
|
||||
frozen_accounts_modifier = frappe.db.get_single_value(
|
||||
"Accounts Settings", "frozen_accounts_modifier"
|
||||
)
|
||||
|
||||
if not frozen_accounts_modifier:
|
||||
frappe.throw(_("Account {0} is frozen").format(account))
|
||||
@@ -447,7 +492,7 @@ def rename_temporarily_named_docs(doctype):
|
||||
set_name_from_naming_options(frappe.get_meta(doctype).autoname, doc)
|
||||
newname = doc.name
|
||||
frappe.db.sql(
|
||||
f"UPDATE `tab{doctype}` SET name = %s, to_rename = 0 where name = %s",
|
||||
"UPDATE `tab{}` SET name = %s, to_rename = 0 where name = %s".format(doctype),
|
||||
(newname, oldname),
|
||||
auto_commit=True,
|
||||
)
|
||||
|
||||
@@ -14,7 +14,9 @@ from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journ
|
||||
class TestGLEntry(unittest.TestCase):
|
||||
def test_round_off_entry(self):
|
||||
frappe.db.set_value("Company", "_Test Company", "round_off_account", "_Test Write Off - _TC")
|
||||
frappe.db.set_value("Company", "_Test Company", "round_off_cost_center", "_Test Cost Center - _TC")
|
||||
frappe.db.set_value(
|
||||
"Company", "_Test Company", "round_off_cost_center", "_Test Cost Center - _TC"
|
||||
)
|
||||
|
||||
jv = make_journal_entry(
|
||||
"_Test Account Cost for Goods Sold - _TC",
|
||||
@@ -71,9 +73,7 @@ class TestGLEntry(unittest.TestCase):
|
||||
)
|
||||
self.assertTrue(all(entry.to_rename == 0 for entry in new_gl_entries))
|
||||
|
||||
self.assertTrue(
|
||||
all(new.name != old.name for new, old in zip(gl_entries, new_gl_entries, strict=False))
|
||||
)
|
||||
self.assertTrue(all(new.name != old.name for new, old in zip(gl_entries, new_gl_entries)))
|
||||
|
||||
new_naming_series_current_value = frappe.db.sql(
|
||||
"SELECT current from tabSeries where name = %s", naming_series
|
||||
|
||||
@@ -1,49 +1,47 @@
|
||||
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Invoice Discounting", {
|
||||
frappe.ui.form.on('Invoice Discounting', {
|
||||
setup: (frm) => {
|
||||
frm.set_query("sales_invoice", "invoices", (doc) => {
|
||||
return {
|
||||
filters: {
|
||||
docstatus: 1,
|
||||
company: doc.company,
|
||||
outstanding_amount: [">", 0],
|
||||
"filters": {
|
||||
"docstatus": 1,
|
||||
"company": doc.company,
|
||||
"outstanding_amount": [">", 0]
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
frm.events.filter_accounts("bank_account", frm, [["account_type", "=", "Bank"]]);
|
||||
frm.events.filter_accounts("bank_charges_account", frm, [["root_type", "=", "Expense"]]);
|
||||
frm.events.filter_accounts("short_term_loan", frm, [["root_type", "=", "Liability"]]);
|
||||
frm.events.filter_accounts("accounts_receivable_discounted", frm, [
|
||||
["account_type", "=", "Receivable"],
|
||||
]);
|
||||
frm.events.filter_accounts("accounts_receivable_discounted", frm, [["account_type", "=", "Receivable"]]);
|
||||
frm.events.filter_accounts("accounts_receivable_credit", frm, [["account_type", "=", "Receivable"]]);
|
||||
frm.events.filter_accounts("accounts_receivable_unpaid", frm, [["account_type", "=", "Receivable"]]);
|
||||
|
||||
},
|
||||
|
||||
filter_accounts: (fieldname, frm, addl_filters) => {
|
||||
let filters = [
|
||||
["company", "=", frm.doc.company],
|
||||
["is_group", "=", 0],
|
||||
["is_group", "=", 0]
|
||||
];
|
||||
if (addl_filters) {
|
||||
filters = $.merge(filters, addl_filters);
|
||||
if(addl_filters){
|
||||
filters = $.merge(filters , addl_filters);
|
||||
}
|
||||
|
||||
frm.set_query(fieldname, () => {
|
||||
return { filters: filters };
|
||||
});
|
||||
frm.set_query(fieldname, () => { return { "filters": filters }; });
|
||||
},
|
||||
|
||||
refresh_filters: (frm) => {
|
||||
let invoice_accounts = Object.keys(frm.doc.invoices).map(function (key) {
|
||||
refresh_filters: (frm) =>{
|
||||
let invoice_accounts = Object.keys(frm.doc.invoices).map(function(key) {
|
||||
return frm.doc.invoices[key].debit_to;
|
||||
});
|
||||
let filters = [
|
||||
["account_type", "=", "Receivable"],
|
||||
["name", "not in", invoice_accounts],
|
||||
["name", "not in", invoice_accounts]
|
||||
];
|
||||
frm.events.filter_accounts("accounts_receivable_credit", frm, filters);
|
||||
frm.events.filter_accounts("accounts_receivable_discounted", frm, filters);
|
||||
@@ -54,19 +52,19 @@ frappe.ui.form.on("Invoice Discounting", {
|
||||
frm.events.show_general_ledger(frm);
|
||||
|
||||
if (frm.doc.docstatus === 0) {
|
||||
frm.add_custom_button(__("Get Invoices"), function () {
|
||||
frm.add_custom_button(__('Get Invoices'), function() {
|
||||
frm.events.get_invoices(frm);
|
||||
});
|
||||
}
|
||||
|
||||
if (frm.doc.docstatus === 1 && frm.doc.status !== "Settled") {
|
||||
if (frm.doc.status == "Sanctioned") {
|
||||
frm.add_custom_button(__("Disburse Loan"), function () {
|
||||
frm.add_custom_button(__('Disburse Loan'), function() {
|
||||
frm.events.create_disbursement_entry(frm);
|
||||
}).addClass("btn-primary");
|
||||
}
|
||||
if (frm.doc.status == "Disbursed") {
|
||||
frm.add_custom_button(__("Close Loan"), function () {
|
||||
frm.add_custom_button(__('Close Loan'), function() {
|
||||
frm.events.close_loan(frm);
|
||||
}).addClass("btn-primary");
|
||||
}
|
||||
@@ -94,121 +92,119 @@ frappe.ui.form.on("Invoice Discounting", {
|
||||
|
||||
calculate_total_amount: (frm) => {
|
||||
let total_amount = 0.0;
|
||||
for (let row of frm.doc.invoices || []) {
|
||||
for (let row of (frm.doc.invoices || [])) {
|
||||
total_amount += flt(row.outstanding_amount);
|
||||
}
|
||||
frm.set_value("total_amount", total_amount);
|
||||
},
|
||||
get_invoices: (frm) => {
|
||||
var d = new frappe.ui.Dialog({
|
||||
title: __("Get Invoices based on Filters"),
|
||||
title: __('Get Invoices based on Filters'),
|
||||
fields: [
|
||||
{
|
||||
label: "Customer",
|
||||
fieldname: "customer",
|
||||
fieldtype: "Link",
|
||||
options: "Customer",
|
||||
"label": "Customer",
|
||||
"fieldname": "customer",
|
||||
"fieldtype": "Link",
|
||||
"options": "Customer"
|
||||
},
|
||||
{
|
||||
label: "From Date",
|
||||
fieldname: "from_date",
|
||||
fieldtype: "Date",
|
||||
"label": "From Date",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date"
|
||||
},
|
||||
{
|
||||
label: "To Date",
|
||||
fieldname: "to_date",
|
||||
fieldtype: "Date",
|
||||
"label": "To Date",
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date"
|
||||
},
|
||||
{
|
||||
fieldname: "col_break",
|
||||
fieldtype: "Column Break",
|
||||
"fieldname": "col_break",
|
||||
"fieldtype": "Column Break",
|
||||
},
|
||||
{
|
||||
label: "Min Amount",
|
||||
fieldname: "min_amount",
|
||||
fieldtype: "Currency",
|
||||
"label": "Min Amount",
|
||||
"fieldname": "min_amount",
|
||||
"fieldtype": "Currency"
|
||||
},
|
||||
{
|
||||
label: "Max Amount",
|
||||
fieldname: "max_amount",
|
||||
fieldtype: "Currency",
|
||||
},
|
||||
"label": "Max Amount",
|
||||
"fieldname": "max_amount",
|
||||
"fieldtype": "Currency"
|
||||
}
|
||||
],
|
||||
primary_action: function () {
|
||||
primary_action: function() {
|
||||
var data = d.get_values();
|
||||
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.invoice_discounting.invoice_discounting.get_invoices",
|
||||
args: {
|
||||
filters: data,
|
||||
filters: data
|
||||
},
|
||||
callback: function (r) {
|
||||
if (!r.exc) {
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
d.hide();
|
||||
$.each(r.message, function (i, v) {
|
||||
frm.doc.invoices = frm.doc.invoices.filter((row) => row.sales_invoice);
|
||||
$.each(r.message, function(i, v) {
|
||||
frm.doc.invoices = frm.doc.invoices.filter(row => row.sales_invoice);
|
||||
let row = frm.add_child("invoices");
|
||||
$.extend(row, v);
|
||||
frm.events.refresh_filters(frm);
|
||||
});
|
||||
refresh_field("invoices");
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
},
|
||||
primary_action_label: __("Get Invocies"),
|
||||
primary_action_label: __('Get Invocies')
|
||||
});
|
||||
d.show();
|
||||
},
|
||||
|
||||
create_disbursement_entry: (frm) => {
|
||||
frappe.call({
|
||||
method: "create_disbursement_entry",
|
||||
method:"create_disbursement_entry",
|
||||
doc: frm.doc,
|
||||
callback: function (r) {
|
||||
if (!r.exc) {
|
||||
callback: function(r) {
|
||||
if(!r.exc){
|
||||
var doclist = frappe.model.sync(r.message);
|
||||
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
close_loan: (frm) => {
|
||||
frappe.call({
|
||||
method: "close_loan",
|
||||
method:"close_loan",
|
||||
doc: frm.doc,
|
||||
callback: function (r) {
|
||||
if (!r.exc) {
|
||||
callback: function(r) {
|
||||
if(!r.exc){
|
||||
var doclist = frappe.model.sync(r.message);
|
||||
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
show_general_ledger: (frm) => {
|
||||
if (frm.doc.docstatus > 0) {
|
||||
cur_frm.add_custom_button(
|
||||
__("Accounting Ledger"),
|
||||
function () {
|
||||
frappe.route_options = {
|
||||
voucher_no: frm.doc.name,
|
||||
from_date: frm.doc.posting_date,
|
||||
to_date: moment(frm.doc.modified).format("YYYY-MM-DD"),
|
||||
company: frm.doc.company,
|
||||
group_by: "Group by Voucher (Consolidated)",
|
||||
show_cancelled_entries: frm.doc.docstatus === 2,
|
||||
};
|
||||
frappe.set_route("query-report", "General Ledger");
|
||||
},
|
||||
__("View")
|
||||
);
|
||||
if(frm.doc.docstatus > 0) {
|
||||
cur_frm.add_custom_button(__('Accounting Ledger'), function() {
|
||||
frappe.route_options = {
|
||||
voucher_no: frm.doc.name,
|
||||
from_date: frm.doc.posting_date,
|
||||
to_date: moment(frm.doc.modified).format('YYYY-MM-DD'),
|
||||
company: frm.doc.company,
|
||||
group_by: "Group by Voucher (Consolidated)",
|
||||
show_cancelled_entries: frm.doc.docstatus === 2
|
||||
};
|
||||
frappe.set_route("query-report", "General Ledger");
|
||||
}, __("View"));
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Discounted Invoice", {
|
||||
frappe.ui.form.on('Discounted Invoice', {
|
||||
sales_invoice: (frm) => {
|
||||
frm.events.calculate_total_amount(frm);
|
||||
frm.events.refresh_filters(frm);
|
||||
@@ -216,5 +212,5 @@ frappe.ui.form.on("Discounted Invoice", {
|
||||
invoices_remove: (frm) => {
|
||||
frm.events.calculate_total_amount(frm);
|
||||
frm.events.refresh_filters(frm);
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user