Compare commits
1582 Commits
v15.43.2
...
skip_enque
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b189fc46b0 | ||
|
|
58e1548640 | ||
|
|
b660d98f20 | ||
|
|
dee49e6078 | ||
|
|
f4d69f16c0 | ||
|
|
c5b94df188 | ||
|
|
0b100782e2 | ||
|
|
ec74a5e566 | ||
|
|
2c16036ef3 | ||
|
|
1f4c86136f | ||
|
|
b0fcb014c1 | ||
|
|
65c8605b88 | ||
|
|
4c6d28a299 | ||
|
|
962b51d6d5 | ||
|
|
9702a65a0f | ||
|
|
d69c593f93 | ||
|
|
1e09f584d5 | ||
|
|
0d6afda6b8 | ||
|
|
ac1961b687 | ||
|
|
c0a1f298a1 | ||
|
|
55b9664115 | ||
|
|
68baa3612a | ||
|
|
01856a6e9d | ||
|
|
65a2f3d12c | ||
|
|
d0e0b66b2f | ||
|
|
4b38139b44 | ||
|
|
898c6e30eb | ||
|
|
cef6291311 | ||
|
|
a93d14b3f7 | ||
|
|
48da952fd7 | ||
|
|
e178ffc3c1 | ||
|
|
1eaa386657 | ||
|
|
171b8af3ee | ||
|
|
031465f5ec | ||
|
|
f44a841af7 | ||
|
|
28ac53a704 | ||
|
|
bdc0175fe3 | ||
|
|
e58c2dd43d | ||
|
|
092999f2a6 | ||
|
|
e52c4c8f22 | ||
|
|
d9a0494fc3 | ||
|
|
cb2529cec8 | ||
|
|
158112896e | ||
|
|
9d9b83362a | ||
|
|
9904a9868c | ||
|
|
34051bc04f | ||
|
|
8e2f9787c1 | ||
|
|
6379238893 | ||
|
|
031b99f373 | ||
|
|
51909077bd | ||
|
|
d0140412cd | ||
|
|
e7d707797a | ||
|
|
f2f5c1a0eb | ||
|
|
ceee93e0e8 | ||
|
|
ed95d41a51 | ||
|
|
eaac02655b | ||
|
|
72ac56b6c4 | ||
|
|
e017421708 | ||
|
|
13b05aa7fb | ||
|
|
a5232d9c10 | ||
|
|
8a5078b826 | ||
|
|
5b67631d40 | ||
|
|
0aa72f841d | ||
|
|
b8dac84a90 | ||
|
|
79c492cc4b | ||
|
|
a8eb5e5c85 | ||
|
|
863abc3ee5 | ||
|
|
93c2762f7b | ||
|
|
c067ac16a7 | ||
|
|
5e736f0d06 | ||
|
|
983f71975a | ||
|
|
b235f48067 | ||
|
|
9d3614597c | ||
|
|
3c19186654 | ||
|
|
646e9ca0dd | ||
|
|
3327599c9d | ||
|
|
dd70fb5f7e | ||
|
|
ac2b53bf15 | ||
|
|
5c47087f20 | ||
|
|
29f91a7919 | ||
|
|
f6fdfd2c01 | ||
|
|
24dcd64c16 | ||
|
|
9dcdd5b5ef | ||
|
|
fea20db262 | ||
|
|
043d9e3986 | ||
|
|
dde4d9e53c | ||
|
|
36a0569df2 | ||
|
|
f8ba560394 | ||
|
|
93170a56a9 | ||
|
|
6f5815e44f | ||
|
|
0c52eb913b | ||
|
|
519682bb0e | ||
|
|
694c17487d | ||
|
|
5b8b40c13b | ||
|
|
ceeb8fc9e5 | ||
|
|
b193fafe49 | ||
|
|
c310da7b29 | ||
|
|
d6b29d1cd7 | ||
|
|
303433c0ae | ||
|
|
20fa3da950 | ||
|
|
bc9c480246 | ||
|
|
8aa2b7c183 | ||
|
|
08caa7cfa1 | ||
|
|
5885978fc2 | ||
|
|
122373fcd3 | ||
|
|
1f01ff3487 | ||
|
|
c9e2f03a3a | ||
|
|
924b08e395 | ||
|
|
461fb183fc | ||
|
|
c9ff769d28 | ||
|
|
3658c4754f | ||
|
|
9c8d103d8a | ||
|
|
6d40844894 | ||
|
|
81dbfe189e | ||
|
|
75f8464724 | ||
|
|
e240ccd305 | ||
|
|
3c3c57c674 | ||
|
|
c42444ab3b | ||
|
|
bf6e32a960 | ||
|
|
9f6535472d | ||
|
|
7cb07425b1 | ||
|
|
4c9048fb39 | ||
|
|
f4222be027 | ||
|
|
44ed52c5cf | ||
|
|
8aaddb0f9e | ||
|
|
635174f1ce | ||
|
|
406793a6ff | ||
|
|
7566c1ee78 | ||
|
|
97c3e27c60 | ||
|
|
467c0898e9 | ||
|
|
26ec697a0b | ||
|
|
3294282880 | ||
|
|
a823f16dff | ||
|
|
339698d172 | ||
|
|
dd23ddcd6c | ||
|
|
20eff568b1 | ||
|
|
3634c4c284 | ||
|
|
2e0aaf3521 | ||
|
|
6249965605 | ||
|
|
a9f8af2fd1 | ||
|
|
6d43d46fbc | ||
|
|
4288713abe | ||
|
|
b03c683898 | ||
|
|
c5050c935b | ||
|
|
e2d16955dd | ||
|
|
5a3b133d65 | ||
|
|
146c5b3e16 | ||
|
|
c1e1fd8829 | ||
|
|
da184d709b | ||
|
|
864d7ae04c | ||
|
|
0b04d04da3 | ||
|
|
6707425baa | ||
|
|
6cfb8fe439 | ||
|
|
45b25e09c1 | ||
|
|
38e88db2c9 | ||
|
|
87df7ff717 | ||
|
|
4c061d61fa | ||
|
|
a73ba2c0d2 | ||
|
|
ecd83b12ab | ||
|
|
408ea0432b | ||
|
|
11f4cb914a | ||
|
|
66bf6e4041 | ||
|
|
f37f7ca5c3 | ||
|
|
85471533e9 | ||
|
|
4b24fcd221 | ||
|
|
68a23730f3 | ||
|
|
17f85de6fb | ||
|
|
8e7d47b3a7 | ||
|
|
133f8bd92a | ||
|
|
d4264f7ba1 | ||
|
|
fa1113f912 | ||
|
|
e8ae4ed61d | ||
|
|
a4cbfabe0e | ||
|
|
9cb83d2198 | ||
|
|
e25b4e24df | ||
|
|
4f23d88f62 | ||
|
|
0bbf45cd8b | ||
|
|
2ff06af154 | ||
|
|
9c0755d3de | ||
|
|
701671b2bd | ||
|
|
4d7d7dac01 | ||
|
|
f04676aaed | ||
|
|
0d260faa00 | ||
|
|
44538bd02a | ||
|
|
75230ece9a | ||
|
|
f99f7fd2cf | ||
|
|
cc83af0dd4 | ||
|
|
028d31a6f9 | ||
|
|
3f1d008741 | ||
|
|
0b3ecd24e2 | ||
|
|
506fefa186 | ||
|
|
a995e87567 | ||
|
|
fa4ea7c96d | ||
|
|
dc9115a586 | ||
|
|
f3bd53193d | ||
|
|
8a702a6338 | ||
|
|
8360339119 | ||
|
|
799794cafb | ||
|
|
2fb0499923 | ||
|
|
230a7d8d53 | ||
|
|
b1dfa2537b | ||
|
|
1a8f7f9403 | ||
|
|
d0df5df4a6 | ||
|
|
6239fd704b | ||
|
|
b124081065 | ||
|
|
4bef6707ec | ||
|
|
76023f1fdc | ||
|
|
f1f8f59bdb | ||
|
|
24ba26fef3 | ||
|
|
e6949d71f6 | ||
|
|
e3bd8d10b0 | ||
|
|
d80ca523a4 | ||
|
|
22291d32e6 | ||
|
|
5df5851798 | ||
|
|
1d1cb86c75 | ||
|
|
1c2a7801b7 | ||
|
|
55e66db315 | ||
|
|
094ecc1f62 | ||
|
|
2b1e0d3bd0 | ||
|
|
d97b6d38ef | ||
|
|
b966c06a4f | ||
|
|
2ee642fb2c | ||
|
|
4b1c851da1 | ||
|
|
39067c7614 | ||
|
|
aa1c69dd7a | ||
|
|
e5824fc3f1 | ||
|
|
d59caf08e6 | ||
|
|
186cc3d748 | ||
|
|
a2a8a8f2e0 | ||
|
|
17452b7693 | ||
|
|
8c85404191 | ||
|
|
159a123dc7 | ||
|
|
1568af2a7b | ||
|
|
1745371cd6 | ||
|
|
ecdc4c8e9b | ||
|
|
5cf0759b0c | ||
|
|
61a29eb5fb | ||
|
|
8bb83e267c | ||
|
|
781bdd2ec9 | ||
|
|
2d78dba66f | ||
|
|
2c8e4c1ab3 | ||
|
|
22a187c3be | ||
|
|
2f676ced5c | ||
|
|
e9251c3775 | ||
|
|
294f562fb9 | ||
|
|
81e82b0595 | ||
|
|
a8ebc94a36 | ||
|
|
33efe0d12d | ||
|
|
31a8c3bdc4 | ||
|
|
0549535603 | ||
|
|
50f54d983d | ||
|
|
146147316d | ||
|
|
ab9d6576d0 | ||
|
|
32ccf3524a | ||
|
|
d6d91257b6 | ||
|
|
40ce0c851d | ||
|
|
76b57a4338 | ||
|
|
0e5b4e5f07 | ||
|
|
fb330d1b5a | ||
|
|
9636458ae1 | ||
|
|
4671f65cbd | ||
|
|
9383f5da94 | ||
|
|
01650120d4 | ||
|
|
943dcb6dea | ||
|
|
3a26823c62 | ||
|
|
e55611497b | ||
|
|
798a0510e6 | ||
|
|
e38b46300c | ||
|
|
41f58476e1 | ||
|
|
56e5611337 | ||
|
|
2e509f69d4 | ||
|
|
ee14faaa39 | ||
|
|
ae4ebcd987 | ||
|
|
1bfbbfe393 | ||
|
|
1fa6233377 | ||
|
|
c1e869f040 | ||
|
|
3b671d5875 | ||
|
|
c5770f2ecc | ||
|
|
ff3ca50a4b | ||
|
|
cc96d2b50c | ||
|
|
4d614c1589 | ||
|
|
27d6c8b6d5 | ||
|
|
61ded697a7 | ||
|
|
5ce5c352e4 | ||
|
|
6e6c818084 | ||
|
|
b834ed10d6 | ||
|
|
25c2b79864 | ||
|
|
7a04f0f7ba | ||
|
|
b70f3de16b | ||
|
|
955098c4c0 | ||
|
|
675a0b810f | ||
|
|
d7e4a6be13 | ||
|
|
617d923f0d | ||
|
|
407045a1de | ||
|
|
d73e42fc41 | ||
|
|
9fafc83632 | ||
|
|
c81d597ca5 | ||
|
|
50cff656b4 | ||
|
|
d9a72c1e61 | ||
|
|
322cdbaccf | ||
|
|
a9a2ec81de | ||
|
|
93259cab1d | ||
|
|
7efb5a8cb5 | ||
|
|
c18ff5bd25 | ||
|
|
f7face43cd | ||
|
|
2caa2d677c | ||
|
|
6d87cfeb8d | ||
|
|
2693fcb446 | ||
|
|
7c6a5a0f23 | ||
|
|
1e15a3cc15 | ||
|
|
d78a1e7814 | ||
|
|
12affa70cf | ||
|
|
78483e2ee6 | ||
|
|
e9fe10c6f1 | ||
|
|
518b06c8eb | ||
|
|
27b557bef9 | ||
|
|
beff566c82 | ||
|
|
affca3a519 | ||
|
|
c077eda64e | ||
|
|
772f540bef | ||
|
|
951023f434 | ||
|
|
5e71d6ac4e | ||
|
|
7475233fa6 | ||
|
|
0c9572bb48 | ||
|
|
6b8f046fb4 | ||
|
|
1ff473b615 | ||
|
|
cfd1666181 | ||
|
|
0c6c650c08 | ||
|
|
31f586f716 | ||
|
|
6fea9d6dfe | ||
|
|
212d656d85 | ||
|
|
9a705169fd | ||
|
|
00a915b741 | ||
|
|
ae7be84d87 | ||
|
|
f8a9554bbd | ||
|
|
d86186ec47 | ||
|
|
3fd43cd6bb | ||
|
|
12a4f2761c | ||
|
|
e9dfb45fca | ||
|
|
b14886b227 | ||
|
|
2e49423d3f | ||
|
|
ddecbeba75 | ||
|
|
2e5d716408 | ||
|
|
bdca718103 | ||
|
|
8bdc760733 | ||
|
|
6f2fae1b61 | ||
|
|
91b913b5bb | ||
|
|
5cf47ae5f9 | ||
|
|
a673220feb | ||
|
|
4e182b89ce | ||
|
|
68c997aa06 | ||
|
|
866df9f1c7 | ||
|
|
079cd30b9c | ||
|
|
49cb11c1f3 | ||
|
|
efade9b9ae | ||
|
|
2a46799188 | ||
|
|
50d56db0c2 | ||
|
|
16404110a8 | ||
|
|
6a63a8997d | ||
|
|
88ff945e40 | ||
|
|
8f6c23cb53 | ||
|
|
4c197c8dbd | ||
|
|
f2891229ab | ||
|
|
2486b646a1 | ||
|
|
8fdc244e16 | ||
|
|
5be868c7f6 | ||
|
|
67d828dab3 | ||
|
|
7b37389115 | ||
|
|
0de4197c88 | ||
|
|
92649de5c6 | ||
|
|
3e59c66806 | ||
|
|
fc677811b7 | ||
|
|
19ff10dfeb | ||
|
|
4173203382 | ||
|
|
dbd4dae3d9 | ||
|
|
99b839d2b6 | ||
|
|
30cc65d2b7 | ||
|
|
722ee53fb1 | ||
|
|
6173b34b10 | ||
|
|
f7a3af7473 | ||
|
|
d491036f2d | ||
|
|
d2057588dd | ||
|
|
2bdfdeeb9a | ||
|
|
be074a2972 | ||
|
|
2b3cc5ba2d | ||
|
|
06f48c678b | ||
|
|
53b44ccf29 | ||
|
|
69db569ca5 | ||
|
|
7f8303a493 | ||
|
|
dfda5ad673 | ||
|
|
1e89c1c875 | ||
|
|
d1fb90edff | ||
|
|
4f215f1b70 | ||
|
|
3f383d81bd | ||
|
|
c0a1188067 | ||
|
|
ca02dfa652 | ||
|
|
7d3240ae3a | ||
|
|
93681cfa24 | ||
|
|
4f1e729b7c | ||
|
|
735576ab27 | ||
|
|
23199a3271 | ||
|
|
64cb1153de | ||
|
|
9fcd89d456 | ||
|
|
4a4c4ba21b | ||
|
|
030d35628d | ||
|
|
a552df8a9f | ||
|
|
b127aa308e | ||
|
|
02028becb3 | ||
|
|
764f3422a0 | ||
|
|
236c7e1e95 | ||
|
|
9f1ddeb4e4 | ||
|
|
b046d980ad | ||
|
|
806696a003 | ||
|
|
77b044f1a6 | ||
|
|
7a6a789199 | ||
|
|
252fae68df | ||
|
|
e5a5b6afc8 | ||
|
|
ffd38362d5 | ||
|
|
7a7a213285 | ||
|
|
34ec2f8a2b | ||
|
|
4832175341 | ||
|
|
3c7e7a76f0 | ||
|
|
3815f07c33 | ||
|
|
ebaa5d3add | ||
|
|
0d7dd93284 | ||
|
|
9b4e757b0b | ||
|
|
8de03ef836 | ||
|
|
bd6a4ca1d7 | ||
|
|
7d8aa469d7 | ||
|
|
b1aef01a1f | ||
|
|
c88ce55242 | ||
|
|
1a670ff266 | ||
|
|
1a686cb66d | ||
|
|
31592b8f3a | ||
|
|
f8675817e2 | ||
|
|
236b73565e | ||
|
|
72614fe9e1 | ||
|
|
60809ced85 | ||
|
|
aaf83da3e9 | ||
|
|
4c20a4710b | ||
|
|
52814724eb | ||
|
|
fc0d2aeeff | ||
|
|
7c2cb70387 | ||
|
|
ec0f17ca8b | ||
|
|
f8bbb0619c | ||
|
|
b17e632a85 | ||
|
|
13aae34e9c | ||
|
|
6d8949adea | ||
|
|
faab225126 | ||
|
|
63ffce58cc | ||
|
|
5d94f0bde5 | ||
|
|
b4393bc03d | ||
|
|
da0ad3bc00 | ||
|
|
9fc5c0cc58 | ||
|
|
ebc8230d45 | ||
|
|
e9526b112d | ||
|
|
a6afe50a92 | ||
|
|
273bc1b1e3 | ||
|
|
c40719caa5 | ||
|
|
b2d9380596 | ||
|
|
fcf4687c52 | ||
|
|
cbd443a78a | ||
|
|
6148fb024b | ||
|
|
9678d050a4 | ||
|
|
d468accb02 | ||
|
|
a50808a077 | ||
|
|
125847c69f | ||
|
|
4ca63c07f6 | ||
|
|
35067282cf | ||
|
|
c44eb432a5 | ||
|
|
871a1f4565 | ||
|
|
99b94af49f | ||
|
|
e9bc63aacf | ||
|
|
ba5a7c8cd8 | ||
|
|
527cfcd87f | ||
|
|
ef7aefeb45 | ||
|
|
45aea56198 | ||
|
|
daf954057f | ||
|
|
d7e6b83e64 | ||
|
|
80dc5a7b1c | ||
|
|
0f881bc90a | ||
|
|
6efa92de70 | ||
|
|
2adb710751 | ||
|
|
97f69986ff | ||
|
|
5df40661d2 | ||
|
|
889d67bcee | ||
|
|
8b91287034 | ||
|
|
1148ed1566 | ||
|
|
e3c44231ab | ||
|
|
188ff8cde7 | ||
|
|
3aa17fa0d6 | ||
|
|
4f3aeaefc1 | ||
|
|
120bfdf33d | ||
|
|
73625a2622 | ||
|
|
9f2b62dd1c | ||
|
|
60f52adc90 | ||
|
|
7eefedfb11 | ||
|
|
af80d253db | ||
|
|
ab939cc6e8 | ||
|
|
ca9413bc64 | ||
|
|
bfe42fdccb | ||
|
|
ce2dd28a25 | ||
|
|
96dcfba65a | ||
|
|
6e4d4a55cd | ||
|
|
3349dde5e2 | ||
|
|
4eefb445a7 | ||
|
|
ed927f102e | ||
|
|
82026f780d | ||
|
|
e5aeab7e7e | ||
|
|
32738637ce | ||
|
|
80f5026208 | ||
|
|
00619342e1 | ||
|
|
b719585a2f | ||
|
|
4970b5d5bc | ||
|
|
bdd382bdfd | ||
|
|
92a5cda61a | ||
|
|
60b26ad8b2 | ||
|
|
efe9f6656f | ||
|
|
22bd6a54b2 | ||
|
|
a36b6cb102 | ||
|
|
d91813c277 | ||
|
|
0ec17590ae | ||
|
|
2154502955 | ||
|
|
a4ddf93492 | ||
|
|
9c5a79209e | ||
|
|
5dc22e1811 | ||
|
|
ad8475cb8b | ||
|
|
ff60ec85b8 | ||
|
|
c1fe4bcc64 | ||
|
|
20576e0f47 | ||
|
|
20e0acc20a | ||
|
|
cfb3d87267 | ||
|
|
1cde804c77 | ||
|
|
1387b0ba7f | ||
|
|
2e03af7ac4 | ||
|
|
749c735627 | ||
|
|
aef87cced7 | ||
|
|
6bb2d9195f | ||
|
|
28ebc4cfee | ||
|
|
ad5906916d | ||
|
|
114f2b4326 | ||
|
|
a27a4db3de | ||
|
|
b4354cbc8d | ||
|
|
02fde73545 | ||
|
|
19975dcb7b | ||
|
|
641c3de0ca | ||
|
|
cf6b52e543 | ||
|
|
9500254861 | ||
|
|
13e5578bc6 | ||
|
|
24137ff54f | ||
|
|
9a13842751 | ||
|
|
13b4ddec63 | ||
|
|
39c8507dc2 | ||
|
|
0b1cc7fad1 | ||
|
|
8bc1efcf8b | ||
|
|
1a67d7d95f | ||
|
|
4dad4b50fb | ||
|
|
0f9734ae37 | ||
|
|
a43ee34bd5 | ||
|
|
e6f599b32d | ||
|
|
8cf6ff69c0 | ||
|
|
2249b7c793 | ||
|
|
591de1338b | ||
|
|
2c95cd206b | ||
|
|
8ee6dbc1e2 | ||
|
|
beb169cf75 | ||
|
|
b40d3b0a05 | ||
|
|
93ff84bf56 | ||
|
|
498c9c7955 | ||
|
|
073f2fa302 | ||
|
|
9339a8b57f | ||
|
|
fbeaf2b398 | ||
|
|
7c0e180fd9 | ||
|
|
1710e10b31 | ||
|
|
8ae7ca7f14 | ||
|
|
5fc29ac913 | ||
|
|
41d9225bd1 | ||
|
|
53f6cfb216 | ||
|
|
6827edb2c5 | ||
|
|
0b525f9d87 | ||
|
|
c7509d8ebf | ||
|
|
566876ae7a | ||
|
|
2820a0ac0a | ||
|
|
116ff8241c | ||
|
|
89623aba57 | ||
|
|
f567af49a6 | ||
|
|
1aecb578e6 | ||
|
|
a87bb21246 | ||
|
|
ec5c0deb0e | ||
|
|
3b14c59133 | ||
|
|
98e2b6575d | ||
|
|
9947bae60e | ||
|
|
402a1b91f9 | ||
|
|
3b07700ef6 | ||
|
|
d3aff000d9 | ||
|
|
c648090b5d | ||
|
|
d0687788b5 | ||
|
|
3c46abca6c | ||
|
|
cdd0acc672 | ||
|
|
c20241fcb5 | ||
|
|
ac81323fec | ||
|
|
6b5fa2c673 | ||
|
|
e4755778ae | ||
|
|
6c8f52b26f | ||
|
|
0c47396785 | ||
|
|
935622bde8 | ||
|
|
ea33f902e7 | ||
|
|
ea1624fba5 | ||
|
|
f31c6f52e8 | ||
|
|
3036a6afdc | ||
|
|
e67ed4fb2d | ||
|
|
f32a870a58 | ||
|
|
e69f9ddf8b | ||
|
|
2bcd032a44 | ||
|
|
33bffe8201 | ||
|
|
8039dc5194 | ||
|
|
2a195d457e | ||
|
|
951e8e3a73 | ||
|
|
268731aec4 | ||
|
|
7b3f9386d7 | ||
|
|
d9e2427aa8 | ||
|
|
eb0bc5c6ad | ||
|
|
fde6fadf4d | ||
|
|
432a14c84c | ||
|
|
813b7a96fb | ||
|
|
bbdf98a8f0 | ||
|
|
d96a777edd | ||
|
|
c67b0a3a64 | ||
|
|
7cc324e31e | ||
|
|
f057dc6867 | ||
|
|
e53931e27a | ||
|
|
6f9fe6a792 | ||
|
|
38c5ecf007 | ||
|
|
c1922ea5de | ||
|
|
54bcbd0bc6 | ||
|
|
9f27ac142b | ||
|
|
2ef0596df2 | ||
|
|
3ff562cb8a | ||
|
|
8fdf0ca2d3 | ||
|
|
01f507ebcb | ||
|
|
e1ba5878a3 | ||
|
|
6dceb25fb2 | ||
|
|
2178fdc65a | ||
|
|
eabf706f37 | ||
|
|
5b4c5d59d8 | ||
|
|
28626cd7c0 | ||
|
|
58343e5b7c | ||
|
|
b897225136 | ||
|
|
bb18ae82cf | ||
|
|
8e1f6c8149 | ||
|
|
74ceb6da5e | ||
|
|
e0ad52b500 | ||
|
|
8252d92e6a | ||
|
|
b15795392b | ||
|
|
274c65c451 | ||
|
|
1b4fbbb115 | ||
|
|
f95403d1dc | ||
|
|
53bf44d2b8 | ||
|
|
2d2ff7cf52 | ||
|
|
d32a407e08 | ||
|
|
5e0d017497 | ||
|
|
ce7d05aa1f | ||
|
|
ec07b42ea2 | ||
|
|
3b861798e6 | ||
|
|
135e19d0aa | ||
|
|
b498094a97 | ||
|
|
bb421c8b07 | ||
|
|
b5be17c6df | ||
|
|
2b93be1139 | ||
|
|
bd464197c4 | ||
|
|
62bbcbc7ef | ||
|
|
96530b9c0b | ||
|
|
0cbcc55d9a | ||
|
|
6b5c54bcbc | ||
|
|
c2dde04aa2 | ||
|
|
8d2c78867e | ||
|
|
c14986f9e6 | ||
|
|
62a6945bd2 | ||
|
|
be8bb235dc | ||
|
|
498bf15ecd | ||
|
|
210d7711b4 | ||
|
|
cf988434e9 | ||
|
|
466625213b | ||
|
|
9ba6ff67d5 | ||
|
|
eab22eb282 | ||
|
|
8548eae368 | ||
|
|
7e198ccb21 | ||
|
|
3d54fd8389 | ||
|
|
d319caa2ee | ||
|
|
d0ea598cdf | ||
|
|
f01f6d50b5 | ||
|
|
6de8c18f98 | ||
|
|
5923b48ede | ||
|
|
7d264696f3 | ||
|
|
b96c063c93 | ||
|
|
e9d36242ce | ||
|
|
2bad706dcf | ||
|
|
7fa3e82ac7 | ||
|
|
7ddbfa10c9 | ||
|
|
4bd437b59d | ||
|
|
fca8028e3c | ||
|
|
cd293a5173 | ||
|
|
b73507abe0 | ||
|
|
07b605a287 | ||
|
|
54383cfb55 | ||
|
|
5d33bbaff0 | ||
|
|
4cc3c1b765 | ||
|
|
4d56f725fe | ||
|
|
c34f09c503 | ||
|
|
fe43dab4d7 | ||
|
|
b69cdeb4a6 | ||
|
|
15dc5c7e99 | ||
|
|
e05bf9d32a | ||
|
|
3fb7886418 | ||
|
|
bae7c64964 | ||
|
|
68c0e188e8 | ||
|
|
e912e9597d | ||
|
|
610d4f5cb6 | ||
|
|
cdd5441435 | ||
|
|
60329ade9e | ||
|
|
f09e2130a1 | ||
|
|
0819675fce | ||
|
|
784b6dcfea | ||
|
|
245effcccd | ||
|
|
0d01bd8a5a | ||
|
|
57b6a98703 | ||
|
|
e1b0fffd0c | ||
|
|
c2f88f29dc | ||
|
|
277dade9f9 | ||
|
|
e2cf1ea73e | ||
|
|
df409d80e0 | ||
|
|
5b1571879c | ||
|
|
bb839b2924 | ||
|
|
5bc2035bd0 | ||
|
|
9e973476b2 | ||
|
|
5e2669f4b6 | ||
|
|
0f1be03faf | ||
|
|
351ee5b8fe | ||
|
|
e84c9f7c51 | ||
|
|
300aaa39fe | ||
|
|
7da9ffa3bd | ||
|
|
b21da472f6 | ||
|
|
4415212a2d | ||
|
|
6401908f41 | ||
|
|
beee98da6d | ||
|
|
60c33ac3e6 | ||
|
|
73ecf51a27 | ||
|
|
0d3a77dce9 | ||
|
|
1bc74bde29 | ||
|
|
12c40ef2e4 | ||
|
|
b250a21a2b | ||
|
|
4e28b5a199 | ||
|
|
739434b727 | ||
|
|
9ea963bfe9 | ||
|
|
f52d7c7665 | ||
|
|
6e5484ea03 | ||
|
|
d048644327 | ||
|
|
1a9e091d12 | ||
|
|
1b0d9643cd | ||
|
|
92bc962f60 | ||
|
|
a9576f0cf6 | ||
|
|
4f8a16848f | ||
|
|
bacf2b7431 | ||
|
|
34d8bc4701 | ||
|
|
510fdf7bf6 | ||
|
|
0346f47c1d | ||
|
|
026824880d | ||
|
|
3f9693b31f | ||
|
|
877cc7255d | ||
|
|
70abedc57a | ||
|
|
0c6de4ecb2 | ||
|
|
a517125d64 | ||
|
|
b7f283b2f0 | ||
|
|
cd37fd790b | ||
|
|
f983e09f92 | ||
|
|
3b4b2275de | ||
|
|
c86deceaba | ||
|
|
b5340c5ec0 | ||
|
|
10074e9980 | ||
|
|
bbee9b5637 | ||
|
|
915d864166 | ||
|
|
8555617295 | ||
|
|
b71b0d5997 | ||
|
|
37767738b0 | ||
|
|
a117ef3cb8 | ||
|
|
33b631e395 | ||
|
|
517bedeb7e | ||
|
|
0a95b38166 | ||
|
|
9b1c22250f | ||
|
|
43fed29514 | ||
|
|
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 | ||
|
|
f73685f4f6 | ||
|
|
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 | ||
|
|
c61925598a | ||
|
|
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 | ||
|
|
a8949174c8 | ||
|
|
906ac093e3 | ||
|
|
8ddc26eb2e | ||
|
|
ca0c3eb184 | ||
|
|
0465c9aabb | ||
|
|
8e0a7a8dbc | ||
|
|
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 | ||
|
|
e824cd012b | ||
|
|
8d5045ef4c | ||
|
|
083da7d8a4 | ||
|
|
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 | ||
|
|
383a4b132e | ||
|
|
9006c9b747 | ||
|
|
6518582ed3 | ||
|
|
5c308a4f9a | ||
|
|
ac91030b31 | ||
|
|
9680edfcc3 | ||
|
|
56e991b7f4 | ||
|
|
9903049c7a | ||
|
|
f34ffc2062 | ||
|
|
bfaa93b0ca | ||
|
|
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 | ||
|
|
1662a4c9c3 | ||
|
|
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 | ||
|
|
c2f0fadb6e | ||
|
|
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 | ||
|
|
c78fdaae90 | ||
|
|
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 | ||
|
|
8b21ca2db9 | ||
|
|
fed8cf4196 | ||
|
|
528ed9c986 | ||
|
|
e2cccc4005 | ||
|
|
569d0961f2 | ||
|
|
de46fa8e66 | ||
|
|
bfa0c0981c | ||
|
|
c77c296b8b | ||
|
|
9ab55a5bd8 | ||
|
|
e4d657e6fd | ||
|
|
f4c8d63fe0 | ||
|
|
d94f45531a | ||
|
|
4d38bd8517 | ||
|
|
c99e7ca999 | ||
|
|
aba3065573 | ||
|
|
e95dce6d8d | ||
|
|
35d92abe73 | ||
|
|
28445058ef | ||
|
|
aab5737ff3 | ||
|
|
e97af14ff4 | ||
|
|
c809e61103 | ||
|
|
115f024260 | ||
|
|
952e8cf60c | ||
|
|
fcfdb9b566 | ||
|
|
ff1dc72d74 | ||
|
|
b3486b43c4 | ||
|
|
f705bf2efe | ||
|
|
6e1565c32c | ||
|
|
4ea43ebc5d | ||
|
|
f292a0cc4c | ||
|
|
4c5a83d6cf | ||
|
|
82774f89b1 | ||
|
|
19b220f39c | ||
|
|
725a7f90e9 |
@@ -30,5 +30,8 @@ b147b85e6ac19a9220cd1e2958a6ebd99373283a
|
|||||||
# bulk format python code with black
|
# bulk format python code with black
|
||||||
baec607ff5905b1c67531096a9cf50ec7ff00a5d
|
baec607ff5905b1c67531096a9cf50ec7ff00a5d
|
||||||
|
|
||||||
# ruff
|
# bulk refactor with sourcery
|
||||||
960ef14b7a68cfec9e309ec12845f521cb6a721c
|
eb9ee3f79b94e594fc6dfa4f6514580e125eee8c
|
||||||
|
|
||||||
|
# js formatting
|
||||||
|
ec74a5e56617bbd76ac402451468fd4668af543d
|
||||||
|
|||||||
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!')
|
||||||
40
.github/helper/update_pot_file.sh
vendored
Normal file
40
.github/helper/update_pot_file.sh
vendored
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
cd ~ || exit
|
||||||
|
|
||||||
|
echo "Setting Up Bench..."
|
||||||
|
|
||||||
|
pip install frappe-bench
|
||||||
|
bench -v init frappe-bench --skip-assets --skip-redis-config-generation --python "$(which python)"
|
||||||
|
cd ./frappe-bench || exit
|
||||||
|
|
||||||
|
echo "Get ERPNext..."
|
||||||
|
bench get-app --skip-assets erpnext "${GITHUB_WORKSPACE}"
|
||||||
|
|
||||||
|
echo "Generating POT file..."
|
||||||
|
bench generate-pot-file --app erpnext
|
||||||
|
|
||||||
|
cd ./apps/erpnext || exit
|
||||||
|
|
||||||
|
echo "Configuring git user..."
|
||||||
|
git config user.email "developers@erpnext.com"
|
||||||
|
git config user.name "frappe-pr-bot"
|
||||||
|
|
||||||
|
echo "Setting the correct git remote..."
|
||||||
|
# Here, the git remote is a local file path by default. Let's change it to the upstream repo.
|
||||||
|
git remote set-url upstream https://github.com/frappe/erpnext.git
|
||||||
|
|
||||||
|
echo "Creating a new branch..."
|
||||||
|
isodate=$(date -u +"%Y-%m-%d")
|
||||||
|
branch_name="pot_${BASE_BRANCH}_${isodate}"
|
||||||
|
git checkout -b "${branch_name}"
|
||||||
|
|
||||||
|
echo "Commiting changes..."
|
||||||
|
git add .
|
||||||
|
git commit -m "chore: update POT file"
|
||||||
|
|
||||||
|
gh auth setup-git
|
||||||
|
git push -u upstream "${branch_name}"
|
||||||
|
|
||||||
|
echo "Creating a PR..."
|
||||||
|
gh pr create --fill --base "${BASE_BRANCH}" --head "${branch_name}" -R frappe/erpnext
|
||||||
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}}"
|
||||||
38
.github/workflows/generate-pot-file.yml
vendored
Normal file
38
.github/workflows/generate-pot-file.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# This workflow is agnostic to branches. Only maintain on develop branch.
|
||||||
|
# To add/remove branches just modify the matrix.
|
||||||
|
|
||||||
|
name: Regenerate POT file (translatable strings)
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# 9:30 UTC => 3 PM IST Sunday
|
||||||
|
- cron: "30 9 * * 0"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
regeneratee-pot-file:
|
||||||
|
name: Release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
branch: ["develop"]
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ matrix.branch }}
|
||||||
|
|
||||||
|
- name: Setup Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: "3.12"
|
||||||
|
|
||||||
|
- name: Run script to update POT file
|
||||||
|
run: |
|
||||||
|
bash ${GITHUB_WORKSPACE}/.github/helper/update_pot_file.sh
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
BASE_BRANCH: ${{ matrix.branch }}
|
||||||
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 }}
|
||||||
12
.github/workflows/linters.yml
vendored
12
.github/workflows/linters.yml
vendored
@@ -20,6 +20,18 @@ jobs:
|
|||||||
- name: Install and Run Pre-commit
|
- name: Install and Run Pre-commit
|
||||||
uses: pre-commit/action@v3.0.0
|
uses: pre-commit/action@v3.0.0
|
||||||
|
|
||||||
|
semgrep:
|
||||||
|
name: semgrep
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Python 3.10
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.10'
|
||||||
|
cache: pip
|
||||||
|
|
||||||
- name: Download Semgrep rules
|
- name: Download Semgrep rules
|
||||||
run: git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules
|
run: git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules
|
||||||
|
|
||||||
|
|||||||
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 14
|
||||||
|
update_to_version 15
|
||||||
|
|
||||||
echo "Updating to latest version"
|
echo "Updating to latest version"
|
||||||
git -C "apps/frappe" checkout -q -f "${GITHUB_BASE_REF:-${GITHUB_REF##*/}}"
|
git -C "apps/frappe" checkout -q -f "${GITHUB_BASE_REF:-${GITHUB_REF##*/}}"
|
||||||
|
|||||||
22
.github/workflows/patch_faux.yml
vendored
Normal file
22
.github/workflows/patch_faux.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# Tests are skipped for these files but github doesn't allow "passing" hence this is required.
|
||||||
|
|
||||||
|
name: Skipped Patch Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- "**.js"
|
||||||
|
- "**.css"
|
||||||
|
- "**.md"
|
||||||
|
- "**.html"
|
||||||
|
- "**.csv"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
name: Patch Test
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Pass skipped tests unconditionally
|
||||||
|
run: "echo Skipped"
|
||||||
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@@ -2,23 +2,21 @@ name: Generate Semantic Release
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- version-15
|
- version-13
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
name: Release
|
name: Release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Entire Repository
|
- name: Checkout Entire Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
|
|
||||||
- name: Setup dependencies
|
- name: Setup dependencies
|
||||||
run: |
|
run: |
|
||||||
npm install @semantic-release/git @semantic-release/exec --no-save
|
npm install @semantic-release/git @semantic-release/exec --no-save
|
||||||
|
|||||||
24
.github/workflows/server-tests-mariadb-faux.yml
vendored
Normal file
24
.github/workflows/server-tests-mariadb-faux.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Tests are skipped for these files but github doesn't allow "passing" hence this is required.
|
||||||
|
|
||||||
|
name: Skipped Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- "**.js"
|
||||||
|
- "**.css"
|
||||||
|
- "**.md"
|
||||||
|
- "**.html"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
container: [1, 2, 3, 4]
|
||||||
|
|
||||||
|
name: Python Unit Tests
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Pass skipped tests unconditionally
|
||||||
|
run: "echo Skipped"
|
||||||
34
.github/workflows/server-tests-mariadb.yml
vendored
34
.github/workflows/server-tests-mariadb.yml
vendored
@@ -31,6 +31,9 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
|
env:
|
||||||
|
NODE_ENV: "production"
|
||||||
|
WITH_COVERAGE: ${{ github.event_name != 'pull_request' }}
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
@@ -120,9 +123,36 @@ jobs:
|
|||||||
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 --total-builds 4 --build-number ${{ matrix.container }}'
|
||||||
env:
|
env:
|
||||||
TYPE: server
|
TYPE: server
|
||||||
CI_BUILD_ID: ${{ github.run_id }}
|
CAPTURE_COVERAGE: ${{ github.event_name != 'pull_request' }}
|
||||||
ORCHESTRATOR_URL: http://test-orchestrator.frappe.io
|
|
||||||
|
|
||||||
- name: Show bench output
|
- name: Show bench output
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
run: cat ~/frappe-bench/bench_start.log || true
|
run: cat ~/frappe-bench/bench_start.log || true
|
||||||
|
|
||||||
|
- name: Upload coverage data
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
with:
|
||||||
|
name: coverage-${{ matrix.container }}
|
||||||
|
path: /home/runner/frappe-bench/sites/coverage.xml
|
||||||
|
|
||||||
|
coverage:
|
||||||
|
name: Coverage Wrap Up
|
||||||
|
needs: test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event_name != 'pull_request' }}
|
||||||
|
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
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
fail_ci_if_error: true
|
||||||
|
verbose: true
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,7 +2,6 @@
|
|||||||
*.py~
|
*.py~
|
||||||
.DS_Store
|
.DS_Store
|
||||||
conf.py
|
conf.py
|
||||||
locale
|
|
||||||
latest_updates.json
|
latest_updates.json
|
||||||
.wnf-lang-status
|
.wnf-lang-status
|
||||||
*.egg-info
|
*.egg-info
|
||||||
|
|||||||
46
.mergify.yml
46
.mergify.yml
@@ -17,6 +17,7 @@ pull_request_rules:
|
|||||||
- base=version-12
|
- base=version-12
|
||||||
- base=version-14
|
- base=version-14
|
||||||
- base=version-15
|
- base=version-15
|
||||||
|
- base=version-16
|
||||||
actions:
|
actions:
|
||||||
close:
|
close:
|
||||||
comment:
|
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.
|
@{{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
|
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
|
- name: backport to develop
|
||||||
conditions:
|
conditions:
|
||||||
- label="backport develop"
|
- label="backport develop"
|
||||||
@@ -54,13 +45,13 @@ pull_request_rules:
|
|||||||
assignees:
|
assignees:
|
||||||
- "{{ author }}"
|
- "{{ author }}"
|
||||||
|
|
||||||
- name: backport to version-14-pre-release
|
- name: backport to version-15-hotfix
|
||||||
conditions:
|
conditions:
|
||||||
- label="backport version-14-pre-release"
|
- label="backport version-15-hotfix"
|
||||||
actions:
|
actions:
|
||||||
backport:
|
backport:
|
||||||
branches:
|
branches:
|
||||||
- version-14-pre-release
|
- version-15-hotfix
|
||||||
assignees:
|
assignees:
|
||||||
- "{{ author }}"
|
- "{{ author }}"
|
||||||
|
|
||||||
@@ -74,35 +65,6 @@ pull_request_rules:
|
|||||||
assignees:
|
assignees:
|
||||||
- "{{ author }}"
|
- "{{ 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
|
- name: Automatic merge on CI success and review
|
||||||
conditions:
|
conditions:
|
||||||
|
|||||||
@@ -55,18 +55,29 @@ repos:
|
|||||||
erpnext/templates/includes/.*
|
erpnext/templates/includes/.*
|
||||||
)$
|
)$
|
||||||
|
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/PyCQA/flake8
|
||||||
rev: v0.2.0
|
rev: 6.0.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: flake8
|
||||||
name: "Run ruff import sorter"
|
additional_dependencies: [
|
||||||
args: ["--select=I", "--fix"]
|
'flake8-bugbear',
|
||||||
|
'flake8-tuple',
|
||||||
|
]
|
||||||
|
args: ['--config', '.github/helper/.flake8_strict']
|
||||||
|
exclude: ".*setup.py$"
|
||||||
|
|
||||||
- id: ruff
|
- repo: https://github.com/adityahase/black
|
||||||
name: "Run ruff linter"
|
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$"
|
||||||
|
|
||||||
- id: ruff-format
|
|
||||||
name: "Run ruff formatter"
|
|
||||||
|
|
||||||
ci:
|
ci:
|
||||||
autoupdate_schedule: weekly
|
autoupdate_schedule: weekly
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"branches": ["version-15"],
|
"branches": ["version-13"],
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"@semantic-release/commit-analyzer", {
|
"@semantic-release/commit-analyzer", {
|
||||||
"preset": "angular",
|
"preset": "angular",
|
||||||
@@ -21,4 +21,4 @@
|
|||||||
],
|
],
|
||||||
"@semantic-release/github"
|
"@semantic-release/github"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
16
CODEOWNERS
16
CODEOWNERS
@@ -4,21 +4,21 @@
|
|||||||
# the repo. Unless a later match takes precedence,
|
# the repo. Unless a later match takes precedence,
|
||||||
|
|
||||||
erpnext/accounts/ @deepeshgarg007 @ruthra-kumar
|
erpnext/accounts/ @deepeshgarg007 @ruthra-kumar
|
||||||
erpnext/assets/ @khushi8112 @deepeshgarg007
|
erpnext/assets/ @anandbaburajan @deepeshgarg007
|
||||||
erpnext/regional @deepeshgarg007 @ruthra-kumar
|
erpnext/regional @deepeshgarg007 @ruthra-kumar
|
||||||
erpnext/selling @deepeshgarg007 @ruthra-kumar
|
erpnext/selling @deepeshgarg007 @ruthra-kumar
|
||||||
erpnext/support/ @deepeshgarg007
|
erpnext/support/ @deepeshgarg007
|
||||||
pos*
|
pos*
|
||||||
|
|
||||||
erpnext/buying/ @rohitwaghchaure
|
erpnext/buying/ @rohitwaghchaure @s-aga-r
|
||||||
erpnext/maintenance/ @rohitwaghchaure
|
erpnext/maintenance/ @rohitwaghchaure @s-aga-r
|
||||||
erpnext/manufacturing/ @rohitwaghchaure
|
erpnext/manufacturing/ @rohitwaghchaure @s-aga-r
|
||||||
erpnext/quality_management/ @rohitwaghchaure
|
erpnext/quality_management/ @rohitwaghchaure @s-aga-r
|
||||||
erpnext/stock/ @rohitwaghchaure
|
erpnext/stock/ @rohitwaghchaure @s-aga-r
|
||||||
erpnext/subcontracting @rohitwaghchaure
|
erpnext/subcontracting @rohitwaghchaure @s-aga-r
|
||||||
|
|
||||||
erpnext/controllers/ @deepeshgarg007 @rohitwaghchaure
|
erpnext/controllers/ @deepeshgarg007 @rohitwaghchaure
|
||||||
erpnext/patches/ @deepeshgarg007
|
erpnext/patches/ @deepeshgarg007
|
||||||
|
|
||||||
.github/ @deepeshgarg007
|
.github/ @deepeshgarg007
|
||||||
pyproject.toml @akhilnarang
|
pyproject.toml @phot0n
|
||||||
|
|||||||
@@ -7,8 +7,7 @@
|
|||||||
<p>ERP made simple</p>
|
<p>ERP made simple</p>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
[](https://github.com/frappe/erpnext/actions/workflows/server-tests.yml)
|
[](https://github.com/frappe/erpnext/actions/workflows/server-tests-mariadb.yml)
|
||||||
[](https://github.com/erpnext/erpnext_ui_tests/actions/workflows/ui-tests.yml)
|
|
||||||
[](https://www.codetriage.com/frappe/erpnext)
|
[](https://www.codetriage.com/frappe/erpnext)
|
||||||
[](https://codecov.io/gh/frappe/erpnext)
|
[](https://codecov.io/gh/frappe/erpnext)
|
||||||
[](https://hub.docker.com/r/frappe/erpnext-worker)
|
[](https://hub.docker.com/r/frappe/erpnext-worker)
|
||||||
@@ -73,8 +72,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. [Issue Guidelines](https://github.com/frappe/erpnext/wiki/Issue-Guidelines)
|
||||||
1. [Report Security Vulnerabilities](https://erpnext.com/security)
|
1. [Report Security Vulnerabilities](https://erpnext.com/security)
|
||||||
1. [Pull Request Requirements](https://github.com/frappe/erpnext/wiki/Contribution-Guidelines)
|
1. [Pull Request Requirements](https://github.com/frappe/erpnext/wiki/Contribution-Guidelines)
|
||||||
1. [Translations](https://translate.erpnext.com)
|
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
4
crowdin.yml
Normal file
4
crowdin.yml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
files:
|
||||||
|
- source: /erpnext/locale/main.pot
|
||||||
|
translation: /erpnext/locale/%two_letters_code%.po
|
||||||
|
pull_request_title: "chore: sync translations from crowdin"
|
||||||
@@ -2,9 +2,8 @@ import functools
|
|||||||
import inspect
|
import inspect
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils.user import is_website_user
|
|
||||||
|
|
||||||
__version__ = "15.43.2"
|
__version__ = "16.0.0-dev"
|
||||||
|
|
||||||
|
|
||||||
def get_default_company(user=None):
|
def get_default_company(user=None):
|
||||||
@@ -38,7 +37,9 @@ def get_default_cost_center(company):
|
|||||||
if not frappe.flags.company_cost_center:
|
if not frappe.flags.company_cost_center:
|
||||||
frappe.flags.company_cost_center = {}
|
frappe.flags.company_cost_center = {}
|
||||||
if company not in 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]
|
return frappe.flags.company_cost_center[company]
|
||||||
|
|
||||||
|
|
||||||
@@ -150,13 +151,3 @@ def allow_regional(fn):
|
|||||||
return frappe.get_attr(overrides[function_path][-1])(*args, **kwargs)
|
return frappe.get_attr(overrides[function_path][-1])(*args, **kwargs)
|
||||||
|
|
||||||
return caller
|
return caller
|
||||||
|
|
||||||
|
|
||||||
def check_app_permission():
|
|
||||||
if frappe.session.user == "Administrator":
|
|
||||||
return True
|
|
||||||
|
|
||||||
if is_website_user():
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|||||||
@@ -11,14 +11,14 @@ class ERPNextAddress(Address):
|
|||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_reference()
|
self.validate_reference()
|
||||||
self.update_compnay_address()
|
self.update_compnay_address()
|
||||||
super().validate()
|
super(ERPNextAddress, self).validate()
|
||||||
|
|
||||||
def link_address(self):
|
def link_address(self):
|
||||||
"""Link address based on owner"""
|
"""Link address based on owner"""
|
||||||
if self.is_your_company_address:
|
if self.is_your_company_address:
|
||||||
return
|
return
|
||||||
|
|
||||||
return super().link_address()
|
return super(ERPNextAddress, self).link_address()
|
||||||
|
|
||||||
def update_compnay_address(self):
|
def update_compnay_address(self):
|
||||||
for link in self.get("links"):
|
for link in self.get("links"):
|
||||||
@@ -26,11 +26,11 @@ class ERPNextAddress(Address):
|
|||||||
self.is_your_company_address = 1
|
self.is_your_company_address = 1
|
||||||
|
|
||||||
def validate_reference(self):
|
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(
|
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"),
|
title=_("Company Not Linked"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ def get(
|
|||||||
filters = frappe.parse_json(filters) or frappe.parse_json(chart.filters_json)
|
filters = frappe.parse_json(filters) or frappe.parse_json(chart.filters_json)
|
||||||
|
|
||||||
account = filters.get("account")
|
account = filters.get("account")
|
||||||
filters.get("company")
|
company = filters.get("company")
|
||||||
|
|
||||||
if not account and chart_name:
|
if not account and chart_name:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
@@ -83,6 +83,7 @@ def build_result(account, dates, gl_entries):
|
|||||||
|
|
||||||
# get balances in debit
|
# get balances in debit
|
||||||
for entry in gl_entries:
|
for entry in gl_entries:
|
||||||
|
|
||||||
# entry date is after the current pointer, so move the pointer forward
|
# entry date is after the current pointer, so move the pointer forward
|
||||||
while getdate(entry.posting_date) > result[date_index][0]:
|
while getdate(entry.posting_date) > result[date_index][0]:
|
||||||
date_index += 1
|
date_index += 1
|
||||||
@@ -132,6 +133,8 @@ def get_dates_from_timegrain(from_date, to_date, timegrain):
|
|||||||
|
|
||||||
dates = [get_period_ending(from_date, timegrain)]
|
dates = [get_period_ending(from_date, timegrain)]
|
||||||
while getdate(dates[-1]) < getdate(to_date):
|
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)
|
dates.append(date)
|
||||||
return dates
|
return dates
|
||||||
|
|||||||
@@ -24,10 +24,14 @@ from erpnext.accounts.utils import get_account_currency
|
|||||||
def validate_service_stop_date(doc):
|
def validate_service_stop_date(doc):
|
||||||
"""Validates service_stop_date for Purchase Invoice and Sales Invoice"""
|
"""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_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:
|
for d in old_doc:
|
||||||
old_stop_dates[d.name] = d.service_stop_date or ""
|
old_stop_dates[d.name] = d.service_stop_date or ""
|
||||||
@@ -58,14 +62,16 @@ def build_conditions(process_type, account, company):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if account:
|
if account:
|
||||||
conditions += f"AND {deferred_account}={frappe.db.escape(account)}"
|
conditions += "AND %s='%s'" % (deferred_account, account)
|
||||||
elif company:
|
elif company:
|
||||||
conditions += f"AND p.company = {frappe.db.escape(company)}"
|
conditions += f"AND p.company = {frappe.db.escape(company)}"
|
||||||
|
|
||||||
return conditions
|
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
|
# 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:
|
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
|
# check for the purchase invoice for which GL entries has to be done
|
||||||
invoices = frappe.db.sql_list(
|
invoices = frappe.db.sql_list(
|
||||||
f"""
|
"""
|
||||||
select distinct item.parent
|
select distinct item.parent
|
||||||
from `tabPurchase Invoice Item` item, `tabPurchase Invoice` p
|
from `tabPurchase Invoice Item` item, `tabPurchase Invoice` p
|
||||||
where item.service_start_date<=%s and item.service_end_date>=%s
|
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.enable_deferred_expense = 1 and item.parent=p.name
|
||||||
and item.docstatus = 1 and ifnull(item.amount, 0) > 0
|
and item.docstatus = 1 and ifnull(item.amount, 0) > 0
|
||||||
{conditions}
|
{0}
|
||||||
""",
|
""".format(
|
||||||
|
conditions
|
||||||
|
),
|
||||||
(end_date, start_date),
|
(end_date, start_date),
|
||||||
) # nosec
|
) # nosec
|
||||||
|
|
||||||
@@ -95,7 +103,9 @@ def convert_deferred_expense_to_expense(deferred_process, start_date=None, end_d
|
|||||||
send_mail(deferred_process)
|
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
|
# 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:
|
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
|
# check for the sales invoice for which GL entries has to be done
|
||||||
invoices = frappe.db.sql_list(
|
invoices = frappe.db.sql_list(
|
||||||
f"""
|
"""
|
||||||
select distinct item.parent
|
select distinct item.parent
|
||||||
from `tabSales Invoice Item` item, `tabSales Invoice` p
|
from `tabSales Invoice Item` item, `tabSales Invoice` p
|
||||||
where item.service_start_date<=%s and item.service_end_date>=%s
|
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.enable_deferred_revenue = 1 and item.parent=p.name
|
||||||
and item.docstatus = 1 and ifnull(item.amount, 0) > 0
|
and item.docstatus = 1 and ifnull(item.amount, 0) > 0
|
||||||
{conditions}
|
{0}
|
||||||
""",
|
""".format(
|
||||||
|
conditions
|
||||||
|
),
|
||||||
(end_date, start_date),
|
(end_date, start_date),
|
||||||
) # nosec
|
) # nosec
|
||||||
|
|
||||||
@@ -220,7 +232,7 @@ def calculate_monthly_amount(
|
|||||||
if amount + already_booked_amount_in_account_currency > item.net_amount:
|
if amount + already_booked_amount_in_account_currency > item.net_amount:
|
||||||
amount = item.net_amount - already_booked_amount_in_account_currency
|
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(
|
partial_month = flt(date_diff(end_date, start_date)) / flt(
|
||||||
date_diff(get_last_day(end_date), get_first_day(start_date))
|
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(
|
already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(
|
||||||
doc, item
|
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:
|
if account_currency == doc.company_currency:
|
||||||
amount = base_amount
|
amount = base_amount
|
||||||
else:
|
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:
|
if account_currency == doc.company_currency:
|
||||||
amount = base_amount
|
amount = base_amount
|
||||||
else:
|
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:
|
else:
|
||||||
already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(
|
already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(
|
||||||
doc, item
|
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:
|
if account_currency == doc.company_currency:
|
||||||
amount = base_amount
|
amount = base_amount
|
||||||
else:
|
else:
|
||||||
@@ -278,22 +296,26 @@ def get_already_booked_amount(doc, item):
|
|||||||
|
|
||||||
gl_entries_details = frappe.db.sql(
|
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
|
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
|
and is_cancelled = 0
|
||||||
group by voucher_detail_no
|
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),
|
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
|
||||||
as_dict=True,
|
as_dict=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
journal_entry_details = frappe.db.sql(
|
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
|
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
|
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
|
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),
|
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
|
||||||
as_dict=True,
|
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):
|
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")
|
accounts_frozen_upto = frappe.db.get_single_value("Accounts Settings", "acc_frozen_upto")
|
||||||
|
|
||||||
@@ -360,45 +384,45 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if not amount:
|
if not amount:
|
||||||
prev_posting_date = end_date
|
return
|
||||||
else:
|
|
||||||
gl_posting_date = end_date
|
|
||||||
prev_posting_date = None
|
|
||||||
# check if books nor frozen till endate:
|
|
||||||
if accounts_frozen_upto and getdate(end_date) <= getdate(accounts_frozen_upto):
|
|
||||||
gl_posting_date = get_last_day(add_days(accounts_frozen_upto, 1))
|
|
||||||
prev_posting_date = end_date
|
|
||||||
|
|
||||||
if via_journal_entry:
|
gl_posting_date = end_date
|
||||||
book_revenue_via_journal_entry(
|
prev_posting_date = None
|
||||||
doc,
|
# check if books nor frozen till endate:
|
||||||
credit_account,
|
if accounts_frozen_upto and getdate(end_date) <= getdate(accounts_frozen_upto):
|
||||||
debit_account,
|
gl_posting_date = get_last_day(add_days(accounts_frozen_upto, 1))
|
||||||
amount,
|
prev_posting_date = end_date
|
||||||
base_amount,
|
|
||||||
gl_posting_date,
|
if via_journal_entry:
|
||||||
project,
|
book_revenue_via_journal_entry(
|
||||||
account_currency,
|
doc,
|
||||||
item.cost_center,
|
credit_account,
|
||||||
item,
|
debit_account,
|
||||||
deferred_process,
|
amount,
|
||||||
submit_journal_entry,
|
base_amount,
|
||||||
)
|
gl_posting_date,
|
||||||
else:
|
project,
|
||||||
make_gl_entries(
|
account_currency,
|
||||||
doc,
|
item.cost_center,
|
||||||
credit_account,
|
item,
|
||||||
debit_account,
|
deferred_process,
|
||||||
against,
|
submit_journal_entry,
|
||||||
amount,
|
)
|
||||||
base_amount,
|
else:
|
||||||
gl_posting_date,
|
make_gl_entries(
|
||||||
project,
|
doc,
|
||||||
account_currency,
|
credit_account,
|
||||||
item.cost_center,
|
debit_account,
|
||||||
item,
|
against,
|
||||||
deferred_process,
|
amount,
|
||||||
)
|
base_amount,
|
||||||
|
gl_posting_date,
|
||||||
|
project,
|
||||||
|
account_currency,
|
||||||
|
item.cost_center,
|
||||||
|
item,
|
||||||
|
deferred_process,
|
||||||
|
)
|
||||||
|
|
||||||
# Returned in case of any errors because it tries to submit the same record again and again in case of errors
|
# Returned in case of any errors because it tries to submit the same record again and again in case of errors
|
||||||
if frappe.flags.deferred_accounting_error:
|
if frappe.flags.deferred_accounting_error:
|
||||||
@@ -416,7 +440,9 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
|
|||||||
via_journal_entry = cint(
|
via_journal_entry = cint(
|
||||||
frappe.db.get_singles_value("Accounts Settings", "book_deferred_entries_via_journal_entry")
|
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(
|
book_deferred_entries_based_on = frappe.db.get_singles_value(
|
||||||
"Accounts Settings", "book_deferred_entries_based_on"
|
"Accounts Settings", "book_deferred_entries_based_on"
|
||||||
)
|
)
|
||||||
@@ -436,7 +462,9 @@ def process_deferred_accounting(posting_date=None):
|
|||||||
posting_date = today()
|
posting_date = today()
|
||||||
|
|
||||||
if not cint(
|
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
|
return
|
||||||
|
|
||||||
@@ -559,13 +587,16 @@ def book_revenue_via_journal_entry(
|
|||||||
deferred_process=None,
|
deferred_process=None,
|
||||||
submit="No",
|
submit="No",
|
||||||
):
|
):
|
||||||
|
|
||||||
if amount == 0:
|
if amount == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
journal_entry = frappe.new_doc("Journal Entry")
|
journal_entry = frappe.new_doc("Journal Entry")
|
||||||
journal_entry.posting_date = posting_date
|
journal_entry.posting_date = posting_date
|
||||||
journal_entry.company = doc.company
|
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
|
journal_entry.process_deferred_accounting = deferred_process
|
||||||
|
|
||||||
debit_entry = {
|
debit_entry = {
|
||||||
@@ -614,6 +645,7 @@ def book_revenue_via_journal_entry(
|
|||||||
|
|
||||||
|
|
||||||
def get_deferred_booking_accounts(doctype, voucher_detail_no, dr_or_cr):
|
def get_deferred_booking_accounts(doctype, voucher_detail_no, dr_or_cr):
|
||||||
|
|
||||||
if doctype == "Sales Invoice":
|
if doctype == "Sales Invoice":
|
||||||
credit_account, debit_account = frappe.db.get_value(
|
credit_account, debit_account = frappe.db.get_value(
|
||||||
"Sales Invoice Item",
|
"Sales Invoice Item",
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ frappe.ui.form.on("Account", {
|
|||||||
// hide fields if group
|
// hide fields if group
|
||||||
frm.toggle_display(["tax_rate"], cint(frm.doc.is_group) == 0);
|
frm.toggle_display(["tax_rate"], cint(frm.doc.is_group) == 0);
|
||||||
|
|
||||||
frm.toggle_enable(["is_group", "company", "account_number"], frm.is_new());
|
// disable fields
|
||||||
|
frm.toggle_enable(["is_group", "company"], false);
|
||||||
|
|
||||||
if (cint(frm.doc.is_group) == 0) {
|
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);
|
||||||
|
|||||||
@@ -55,7 +55,8 @@
|
|||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Account Number"
|
"label": "Account Number",
|
||||||
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
@@ -71,6 +72,7 @@
|
|||||||
"oldfieldname": "company",
|
"oldfieldname": "company",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Company",
|
"options": "Company",
|
||||||
|
"read_only": 1,
|
||||||
"remember_last_selected_value": 1,
|
"remember_last_selected_value": 1,
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
@@ -106,6 +108,7 @@
|
|||||||
"fieldname": "parent_account",
|
"fieldname": "parent_account",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"ignore_user_permissions": 1,
|
"ignore_user_permissions": 1,
|
||||||
|
"in_preview": 1,
|
||||||
"label": "Parent Account",
|
"label": "Parent Account",
|
||||||
"oldfieldname": "parent_account",
|
"oldfieldname": "parent_account",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
@@ -121,8 +124,7 @@
|
|||||||
"label": "Account Type",
|
"label": "Account Type",
|
||||||
"oldfieldname": "account_type",
|
"oldfieldname": "account_type",
|
||||||
"oldfieldtype": "Select",
|
"oldfieldtype": "Select",
|
||||||
"options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nCurrent Asset\nCurrent Liability\nDepreciation\nDirect Expense\nDirect Income\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nIndirect Expense\nIndirect Income\nLiability\nPayable\nReceivable\nRound Off\nRound Off for Opening\nStock\nStock Adjustment\nStock Received But Not Billed\nService Received But Not Billed\nTax\nTemporary",
|
"options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nCurrent Asset\nCurrent Liability\nDepreciation\nDirect Expense\nDirect Income\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nIndirect Expense\nIndirect Income\nLiability\nPayable\nReceivable\nRound Off\nStock\nStock Adjustment\nStock Received But Not Billed\nService Received But Not Billed\nTax\nTemporary"
|
||||||
"search_index": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Rate at which this tax is applied",
|
"description": "Rate at which this tax is applied",
|
||||||
@@ -191,7 +193,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_tree": 1,
|
"is_tree": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-08-19 15:19:11.095045",
|
"modified": "2024-01-10 04:57:33.681676",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Account",
|
"name": "Account",
|
||||||
@@ -248,6 +250,7 @@
|
|||||||
],
|
],
|
||||||
"search_fields": "account_number",
|
"search_fields": "account_number",
|
||||||
"show_name_in_global_search": 1,
|
"show_name_in_global_search": 1,
|
||||||
|
"show_preview_popup": 1,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "ASC",
|
"sort_order": "ASC",
|
||||||
"states": [],
|
"states": [],
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ class Account(NestedSet):
|
|||||||
"Payable",
|
"Payable",
|
||||||
"Receivable",
|
"Receivable",
|
||||||
"Round Off",
|
"Round Off",
|
||||||
"Round Off for Opening",
|
|
||||||
"Stock",
|
"Stock",
|
||||||
"Stock Adjustment",
|
"Stock Adjustment",
|
||||||
"Stock Received But Not Billed",
|
"Stock Received But Not Billed",
|
||||||
@@ -89,11 +88,11 @@ class Account(NestedSet):
|
|||||||
if frappe.local.flags.ignore_update_nsm:
|
if frappe.local.flags.ignore_update_nsm:
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
super().on_update()
|
super(Account, self).on_update()
|
||||||
|
|
||||||
def onload(self):
|
def onload(self):
|
||||||
frozen_accounts_modifier = frappe.db.get_value(
|
frozen_accounts_modifier = frappe.db.get_single_value(
|
||||||
"Accounts Settings", "Accounts Settings", "frozen_accounts_modifier"
|
"Accounts Settings", "frozen_accounts_modifier"
|
||||||
)
|
)
|
||||||
if not frozen_accounts_modifier or frozen_accounts_modifier in frappe.get_roles():
|
if not frozen_accounts_modifier or frozen_accounts_modifier in frappe.get_roles():
|
||||||
self.set_onload("can_freeze_account", True)
|
self.set_onload("can_freeze_account", True)
|
||||||
@@ -104,12 +103,14 @@ class Account(NestedSet):
|
|||||||
self.name = get_autoname_with_number(self.account_number, self.account_name, self.company)
|
self.name = get_autoname_with_number(self.account_number, self.account_name, self.company)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
from erpnext.accounts.utils import validate_field_number
|
||||||
|
|
||||||
if frappe.local.flags.allow_unverified_charts:
|
if frappe.local.flags.allow_unverified_charts:
|
||||||
return
|
return
|
||||||
self.validate_parent()
|
self.validate_parent()
|
||||||
self.validate_parent_child_account_type()
|
self.validate_parent_child_account_type()
|
||||||
self.validate_root_details()
|
self.validate_root_details()
|
||||||
self.validate_account_number()
|
validate_field_number("Account", self.name, self.account_number, self.company, "account_number")
|
||||||
self.validate_group_or_ledger()
|
self.validate_group_or_ledger()
|
||||||
self.set_root_and_report_type()
|
self.set_root_and_report_type()
|
||||||
self.validate_mandatory()
|
self.validate_mandatory()
|
||||||
@@ -201,7 +202,7 @@ class Account(NestedSet):
|
|||||||
msg = _(
|
msg = _(
|
||||||
"There are ledger entries against this account. Changing {0} to non-{1} in live system will cause incorrect output in 'Accounts {2}' report"
|
"There are ledger entries against this account. Changing {0} to non-{1} in live system will cause incorrect output in 'Accounts {2}' report"
|
||||||
).format(
|
).format(
|
||||||
frappe.bold(_("Account Type")), doc_before_save.account_type, doc_before_save.account_type
|
frappe.bold("Account Type"), doc_before_save.account_type, doc_before_save.account_type
|
||||||
)
|
)
|
||||||
frappe.msgprint(msg)
|
frappe.msgprint(msg)
|
||||||
self.add_comment("Comment", msg)
|
self.add_comment("Comment", msg)
|
||||||
@@ -217,7 +218,9 @@ class Account(NestedSet):
|
|||||||
|
|
||||||
def validate_root_company_and_sync_account_to_children(self):
|
def validate_root_company_and_sync_account_to_children(self):
|
||||||
# ignore validation while creating new compnay or while syncing to child companies
|
# ignore validation while creating new compnay or while syncing to child companies
|
||||||
if frappe.local.flags.ignore_root_company_validation or self.flags.ignore_root_company_validation:
|
if (
|
||||||
|
frappe.local.flags.ignore_root_company_validation or self.flags.ignore_root_company_validation
|
||||||
|
):
|
||||||
return
|
return
|
||||||
ancestors = get_root_company(self.company)
|
ancestors = get_root_company(self.company)
|
||||||
if ancestors:
|
if ancestors:
|
||||||
@@ -310,22 +313,6 @@ class Account(NestedSet):
|
|||||||
if frappe.db.get_value("GL Entry", {"account": self.name}):
|
if frappe.db.get_value("GL Entry", {"account": self.name}):
|
||||||
frappe.throw(_("Currency can not be changed after making entries using some other currency"))
|
frappe.throw(_("Currency can not be changed after making entries using some other currency"))
|
||||||
|
|
||||||
def validate_account_number(self, account_number=None):
|
|
||||||
if not account_number:
|
|
||||||
account_number = self.account_number
|
|
||||||
|
|
||||||
if account_number:
|
|
||||||
account_with_same_number = frappe.db.get_value(
|
|
||||||
"Account",
|
|
||||||
{"account_number": account_number, "company": self.company, "name": ["!=", self.name]},
|
|
||||||
)
|
|
||||||
if account_with_same_number:
|
|
||||||
frappe.throw(
|
|
||||||
_("Account Number {0} already used in account {1}").format(
|
|
||||||
account_number, account_with_same_number
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def create_account_for_child_company(self, parent_acc_name_map, descendants, parent_acc_name):
|
def create_account_for_child_company(self, parent_acc_name_map, descendants, parent_acc_name):
|
||||||
for company in descendants:
|
for company in descendants:
|
||||||
company_bold = frappe.bold(company)
|
company_bold = frappe.bold(company)
|
||||||
@@ -431,7 +418,7 @@ class Account(NestedSet):
|
|||||||
if self.check_gle_exists():
|
if self.check_gle_exists():
|
||||||
throw(_("Account with existing transaction can not be deleted"))
|
throw(_("Account with existing transaction can not be deleted"))
|
||||||
|
|
||||||
super().on_trash(True)
|
super(Account, self).on_trash(True)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@@ -439,8 +426,9 @@ class Account(NestedSet):
|
|||||||
def get_parent_account(doctype, txt, searchfield, start, page_len, filters):
|
def get_parent_account(doctype, txt, searchfield, start, page_len, filters):
|
||||||
return frappe.db.sql(
|
return frappe.db.sql(
|
||||||
"""select name from tabAccount
|
"""select name from tabAccount
|
||||||
where is_group = 1 and docstatus != 2 and company = {}
|
where is_group = 1 and docstatus != 2 and company = %s
|
||||||
and {} like {} order by name limit {} offset {}""".format("%s", searchfield, "%s", "%s", "%s"),
|
and %s like %s order by name limit %s offset %s"""
|
||||||
|
% ("%s", searchfield, "%s", "%s", "%s"),
|
||||||
(filters["company"], "%%%s%%" % txt, page_len, start),
|
(filters["company"], "%%%s%%" % txt, page_len, start),
|
||||||
as_list=1,
|
as_list=1,
|
||||||
)
|
)
|
||||||
@@ -479,6 +467,19 @@ def get_account_autoname(account_number, account_name, company):
|
|||||||
return " - ".join(parts)
|
return " - ".join(parts)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_account_number(name, account_number, company):
|
||||||
|
if account_number:
|
||||||
|
account_with_same_number = frappe.db.get_value(
|
||||||
|
"Account", {"account_number": account_number, "company": company, "name": ["!=", name]}
|
||||||
|
)
|
||||||
|
if account_with_same_number:
|
||||||
|
frappe.throw(
|
||||||
|
_("Account Number {0} already used in account {1}").format(
|
||||||
|
account_number, account_with_same_number
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def update_account_number(name, account_name, account_number=None, from_descendant=False):
|
def update_account_number(name, account_name, account_number=None, from_descendant=False):
|
||||||
account = frappe.get_cached_doc("Account", name)
|
account = frappe.get_cached_doc("Account", name)
|
||||||
@@ -519,7 +520,7 @@ def update_account_number(name, account_name, account_number=None, from_descenda
|
|||||||
|
|
||||||
frappe.throw(message, title=_("Rename Not Allowed"))
|
frappe.throw(message, title=_("Rename Not Allowed"))
|
||||||
|
|
||||||
account.validate_account_number(account_number)
|
validate_account_number(name, account_number, account.company)
|
||||||
if account_number:
|
if account_number:
|
||||||
frappe.db.set_value("Account", name, "account_number", account_number.strip())
|
frappe.db.set_value("Account", name, "account_number", account_number.strip())
|
||||||
else:
|
else:
|
||||||
@@ -593,5 +594,7 @@ def sync_update_account_number_in_child(
|
|||||||
if old_acc_number:
|
if old_acc_number:
|
||||||
filters["account_number"] = 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)
|
update_account_number(d["name"], account_name, account_number, from_descendant=True)
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ frappe.treeview_settings["Account"] = {
|
|||||||
|
|
||||||
// show Dr if positive since balance is calculated as debit - credit else show Cr
|
// show Dr if positive since balance is calculated as debit - credit else show Cr
|
||||||
const balance = account.balance_in_account_currency || account.balance;
|
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);
|
const format = (value, currency) => format_currency(Math.abs(value), currency);
|
||||||
|
|
||||||
if (account.balance !== undefined) {
|
if (account.balance !== undefined) {
|
||||||
@@ -222,7 +222,7 @@ frappe.treeview_settings["Account"] = {
|
|||||||
"General Ledger",
|
"General Ledger",
|
||||||
"Balance Sheet",
|
"Balance Sheet",
|
||||||
"Profit and Loss Statement",
|
"Profit and Loss Statement",
|
||||||
"Cash Flow",
|
"Cash Flow Statement",
|
||||||
"Accounts Payable",
|
"Accounts Payable",
|
||||||
"Accounts Receivable",
|
"Accounts Receivable",
|
||||||
]) {
|
]) {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ def create_charts(
|
|||||||
"tax_rate",
|
"tax_rate",
|
||||||
"account_currency",
|
"account_currency",
|
||||||
]:
|
]:
|
||||||
|
|
||||||
account_number = cstr(child.get("account_number")).strip()
|
account_number = cstr(child.get("account_number")).strip()
|
||||||
account_name, account_name_in_db = add_suffix_if_duplicate(
|
account_name, account_name_in_db = add_suffix_if_duplicate(
|
||||||
account_name, account_number, accounts
|
account_name, account_number, accounts
|
||||||
@@ -38,9 +39,7 @@ def create_charts(
|
|||||||
|
|
||||||
is_group = identify_is_group(child)
|
is_group = identify_is_group(child)
|
||||||
report_type = (
|
report_type = (
|
||||||
"Balance Sheet"
|
"Balance Sheet" if root_type in ["Asset", "Liability", "Equity"] else "Profit and Loss"
|
||||||
if root_type in ["Asset", "Liability", "Equity"]
|
|
||||||
else "Profit and Loss"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
account = frappe.get_doc(
|
account = frappe.get_doc(
|
||||||
@@ -75,7 +74,7 @@ def create_charts(
|
|||||||
# after all accounts are already inserted.
|
# after all accounts are already inserted.
|
||||||
frappe.local.flags.ignore_update_nsm = True
|
frappe.local.flags.ignore_update_nsm = True
|
||||||
_import_accounts(chart, None, None, root_account=True)
|
_import_accounts(chart, None, None, root_account=True)
|
||||||
rebuild_tree("Account", "parent_account")
|
rebuild_tree("Account")
|
||||||
frappe.local.flags.ignore_update_nsm = False
|
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):
|
for fname in os.listdir(path):
|
||||||
fname = frappe.as_unicode(fname)
|
fname = frappe.as_unicode(fname)
|
||||||
if fname.endswith(".json"):
|
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()
|
chart = f.read()
|
||||||
if chart and json.loads(chart).get("name") == chart_template:
|
if chart and json.loads(chart).get("name") == chart_template:
|
||||||
return json.loads(chart).get("tree")
|
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):
|
for fname in os.listdir(path):
|
||||||
fname = frappe.as_unicode(fname)
|
fname = frappe.as_unicode(fname)
|
||||||
if (fname.startswith(country_code) or fname.startswith(country)) and fname.endswith(".json"):
|
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())
|
_get_chart_name(f.read())
|
||||||
|
|
||||||
# if more than one charts, returned then add the standard
|
# 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
|
tree[child.account_name]["account_type"] = child.account_type
|
||||||
if child.tax_rate:
|
if child.tax_rate:
|
||||||
tree[child.account_name]["tax_rate"] = 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:
|
if not parent:
|
||||||
tree[child.account_name]["root_type"] = child.root_type
|
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):
|
def _get_account_names(account_master):
|
||||||
for account_name, child in account_master.items():
|
for account_name, child in account_master.items():
|
||||||
if account_name not in [
|
if account_name not in ["account_number", "account_type", "root_type", "is_group", "tax_rate"]:
|
||||||
"account_number",
|
|
||||||
"account_type",
|
|
||||||
"root_type",
|
|
||||||
"is_group",
|
|
||||||
"tax_rate",
|
|
||||||
]:
|
|
||||||
accounts.append(account_name)
|
accounts.append(account_name)
|
||||||
|
|
||||||
_get_account_names(child)
|
_get_account_names(child)
|
||||||
|
|||||||
@@ -0,0 +1,531 @@
|
|||||||
|
{
|
||||||
|
"country_code": "tr",
|
||||||
|
"name": "Turkey - Tek D\u00fczen Hesap Plan\u0131",
|
||||||
|
"tree": {
|
||||||
|
"Duran Varl\u0131klar": {
|
||||||
|
"Di\u011fer Alacaklar": {
|
||||||
|
"Ba\u011fl\u0131 Ortakl\u0131klardan Alacaklar": {},
|
||||||
|
"Di\u011fer Alacak Senetleri Reeskontu(-)": {},
|
||||||
|
"Di\u011fer \u00c7e\u015fitli Alacaklar": {},
|
||||||
|
"Ortaklardan Alacaklar": {},
|
||||||
|
"Personelden Alacaklar": {},
|
||||||
|
"\u0130\u015ftiraklerden Alacaklar": {},
|
||||||
|
"\u015e\u00fcpheli Di\u011fer Alacaklar Kar\u015f\u0131l\u0131\u011f\u0131(-)": {}
|
||||||
|
},
|
||||||
|
"Di\u011fer Duran Varl\u0131klar": {
|
||||||
|
"Birikmi\u015f Amortismanlar(-)": {},
|
||||||
|
"Di\u011fer KDV": {},
|
||||||
|
"Di\u011fer \u00c7e\u015fitli Duran Varl\u0131klar": {},
|
||||||
|
"Elden \u00c7\u0131kar\u0131lacak Stoklar Ve Maddi Duran Varl\u0131klar": {},
|
||||||
|
"Gelecek Y\u0131llar \u0130htiyac\u0131 Stoklar": {},
|
||||||
|
"Gelecek Y\u0131llarda \u0130ndirilecek KDV": {},
|
||||||
|
"Pe\u015fin \u00d6denen Vergi Ve Fonlar": {},
|
||||||
|
"Stok De\u011fer D\u00fc\u015f\u00fckl\u00fc\u011f\u00fc Kar\u015f\u0131l\u0131\u011f\u0131(-)": {}
|
||||||
|
},
|
||||||
|
"Gelecek Y\u0131llara Ait Giderler ve Gelir Tahakkuklar\u0131": {
|
||||||
|
"Gelecek Y\u0131llara Ait Giderler": {},
|
||||||
|
"Gelir Tahakkuklar\u0131": {}
|
||||||
|
},
|
||||||
|
"Maddi Duran Varl\u0131klar": {
|
||||||
|
"Arazi Ve Arsalar": {},
|
||||||
|
"Binalar": {},
|
||||||
|
"Birikmi\u015f Amortismanlar(-)": {},
|
||||||
|
"Demirba\u015flar": {},
|
||||||
|
"Di\u011fer Maddi Duran Varl\u0131klar": {},
|
||||||
|
"Ta\u015f\u0131tlar": {},
|
||||||
|
"Tesis, Makine Ve Cihazlar": {},
|
||||||
|
"Verilen Avanslar": {},
|
||||||
|
"Yap\u0131lmakta Olan Yat\u0131r\u0131mlar": {},
|
||||||
|
"Yer Alt\u0131 Ve Yer \u00dcst\u00fc D\u00fczenleri": {}
|
||||||
|
},
|
||||||
|
"Maddi Olmayan Duran Varl\u0131klar": {
|
||||||
|
"Ara\u015ft\u0131rma Ve Geli\u015ftirme Giderleri": {},
|
||||||
|
"Birikmi\u015f Amortismanlar(-)": {},
|
||||||
|
"Di\u011fer Maddi Olmayan Duran Varl\u0131klar": {},
|
||||||
|
"Haklar": {},
|
||||||
|
"Kurulu\u015f Ve \u00d6rg\u00fctlenme Giderleri": {},
|
||||||
|
"Verilen Avanslar": {},
|
||||||
|
"\u00d6zel Maliyetler": {},
|
||||||
|
"\u015eerefiye": {}
|
||||||
|
},
|
||||||
|
"Mali Duran Varl\u0131klar": {
|
||||||
|
"Ba\u011fl\u0131 Menkul K\u0131ymetler": {},
|
||||||
|
"Ba\u011fl\u0131 Menkul K\u0131ymetler De\u011fer D\u00fc\u015f\u00fckl\u00fc\u011f\u00fc Kar\u015f\u0131l\u0131\u011f\u0131(-)": {},
|
||||||
|
"Ba\u011fl\u0131 Ortakl\u0131klar": {},
|
||||||
|
"Ba\u011fl\u0131 Ortakl\u0131klar Sermaye Paylar\u0131 De\u011fer D\u00fc\u015f\u00fckl\u00fc\u011f\u00fc Kar\u015f\u0131l\u0131\u011f\u0131(-)": {},
|
||||||
|
"Ba\u011fl\u0131 Ortakl\u0131klara Sermaye Taahh\u00fctleri(-)": {},
|
||||||
|
"Di\u011fer Mali Duran Varl\u0131klar": {},
|
||||||
|
"Di\u011fer Mali Duran Varl\u0131klar Kar\u015f\u0131l\u0131\u011f\u0131(-)": {},
|
||||||
|
"\u0130\u015ftirakler": {},
|
||||||
|
"\u0130\u015ftirakler Sermaye Paylar\u0131 De\u011fer D\u00fc\u015f\u00fckl\u00fc\u011f\u00fc Kar\u015f\u0131l\u0131\u011f\u0131(-)": {},
|
||||||
|
"\u0130\u015ftiraklere Sermaye Taahh\u00fctleri(-)": {}
|
||||||
|
},
|
||||||
|
"Ticari Alacaklar": {
|
||||||
|
"Alacak Senetleri": {},
|
||||||
|
"Alacak Senetleri Reeskontu(-)": {},
|
||||||
|
"Al\u0131c\u0131lar": {},
|
||||||
|
"Kazaqn\u0131lmam\u0131\u015f Finansal Kiralama Faiz Gelirleri(-)": {},
|
||||||
|
"Verilen Depozito Ve Teminatlar": {},
|
||||||
|
"\u015e\u00fcpheli Ticari Alacaklar Kar\u015f\u0131l\u0131\u011f\u0131(-)": {}
|
||||||
|
},
|
||||||
|
"root_type": "",
|
||||||
|
"\u00d6zel T\u00fckenmeye Tabi Varl\u0131klar": {
|
||||||
|
"Arama Giderleri": {},
|
||||||
|
"Birikmi\u015f T\u00fckenme Paylar\u0131(-)": {},
|
||||||
|
"Di\u011fer \u00d6zel T\u00fckenmeye Tabi Varl\u0131klar": {},
|
||||||
|
"Haz\u0131rl\u0131k Ve Geli\u015ftirme Giderleri": {},
|
||||||
|
"Verilen Avanslar": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"D\u00f6nen Varl\u0131klar": {
|
||||||
|
"Di\u011fer Alacaklar": {
|
||||||
|
"Ba\u011fl\u0131 Ortakl\u0131klardan Alacaklar": {},
|
||||||
|
"Di\u011fer Alacak Senetleri Reeskontu(-)": {},
|
||||||
|
"Di\u011fer \u00c7e\u015fitli Alacaklar": {},
|
||||||
|
"Ortaklardan Alacaklar": {},
|
||||||
|
"Personelden Alacaklar": {},
|
||||||
|
"\u0130\u015ftiraklerden Alacaklar": {},
|
||||||
|
"\u015e\u00fcpheli Di\u011fer Alacaklar": {},
|
||||||
|
"\u015e\u00fcpheli Di\u011fer Alacaklar Kar\u015f\u0131l\u0131\u011f\u0131(-)": {}
|
||||||
|
},
|
||||||
|
"Di\u011fer D\u00f6nen Varl\u0131klar": {
|
||||||
|
"Devreden KDV": {},
|
||||||
|
"Di\u011fer D\u00f6nen Varl\u0131klar Kar\u015f\u0131l\u0131\u011f\u0131(-)": {},
|
||||||
|
"Di\u011fer KDV": {},
|
||||||
|
"Di\u011fer \u00c7e\u015fitli D\u00f6nen Varl\u0131klar": {},
|
||||||
|
"Personel Avanslar\u0131": {},
|
||||||
|
"Pe\u015fin \u00d6denen Vergiler Ve Fonlar": {},
|
||||||
|
"Say\u0131m Ve Tesell\u00fcm Noksanlar\u0131": {},
|
||||||
|
"\u0130ndirilecek KDV": {},
|
||||||
|
"\u0130\u015f Avanslar\u0131": {}
|
||||||
|
},
|
||||||
|
"Gelecek Aylara Ait Giderler ve Gelir Tahakkuklar\u0131": {
|
||||||
|
"Gelecek Aylara Ait Giderler": {},
|
||||||
|
"Gelir Tahakkuklar\u0131": {}
|
||||||
|
},
|
||||||
|
"Haz\u0131r De\u011ferler": {
|
||||||
|
"Al\u0131nan \u00c7ekler": {},
|
||||||
|
"Bankalar": {
|
||||||
|
"account_type": "Bank"
|
||||||
|
},
|
||||||
|
"Di\u011fer Haz\u0131r De\u011ferler": {},
|
||||||
|
"Kasa": {
|
||||||
|
"account_type": "Cash"
|
||||||
|
},
|
||||||
|
"Verilen \u00c7ekler ve \u00d6deme Emirleri(-)": {}
|
||||||
|
},
|
||||||
|
"Menkul K\u0131ymetler": {
|
||||||
|
"Di\u011fer Menkul K\u0131ymetler": {},
|
||||||
|
"Hisse Senetleri": {},
|
||||||
|
"Kamu Kesimi Tahvil, Senet ve Bonolar\u0131": {},
|
||||||
|
"Menkul K\u0131ymetler De\u011fer D\u00fc\u015f\u00fckl\u00fc\u011f\u00fc Kar\u015f\u0131l\u0131\u011f\u0131(-)": {},
|
||||||
|
"\u00d6zel Kesim Tahvil Senet Ve Bonolar\u0131": {}
|
||||||
|
},
|
||||||
|
"Stoklar": {
|
||||||
|
"Mamuller": {},
|
||||||
|
"Stok De\u011fer D\u00fc\u015f\u00fckl\u00fc\u011f\u00fc Kar\u015f\u0131l\u0131\u011f\u0131(-)": {},
|
||||||
|
"Ticari Mallar": {},
|
||||||
|
"Verilen Sipari\u015f Avanslar\u0131": {},
|
||||||
|
"Yar\u0131 Mamuller": {},
|
||||||
|
"\u0130lk Madde Malzeme": {}
|
||||||
|
},
|
||||||
|
"Ticari Alacaklar": {
|
||||||
|
"Alacak Senetleri": {},
|
||||||
|
"Alacak Senetleri Reeskontu(-)": {},
|
||||||
|
"Al\u0131c\u0131lar": {},
|
||||||
|
"Di\u011fer Ticari Alacaklar": {},
|
||||||
|
"Kazan\u0131lmam\u0131\u015f Finansal Kiralama Faiz Gelirleri(-)": {},
|
||||||
|
"Verilen Depozito ve Teminatlar": {},
|
||||||
|
"\u015e\u00fcpheli Ticari Alacaklar": {},
|
||||||
|
"\u015e\u00fcpheli Ticari Alacaklar Kar\u015f\u0131l\u0131\u011f\u0131": {}
|
||||||
|
},
|
||||||
|
"Y\u0131llara Yayg\u0131n \u0130n\u015faat ve Onar\u0131m Maliyetleri": {
|
||||||
|
"Ta\u015feronlara Verilen Avanslar": {},
|
||||||
|
"Y\u0131llara Yayg\u0131n \u0130n\u015faat Ve Onar\u0131m Maliyetleri": {}
|
||||||
|
},
|
||||||
|
"root_type": ""
|
||||||
|
},
|
||||||
|
"Gelir Tablosu Hesaplar\u0131": {
|
||||||
|
"Br\u00fct Sat\u0131\u015flar": {
|
||||||
|
"Di\u011fer Gelirler": {},
|
||||||
|
"Yurt D\u0131\u015f\u0131 Sat\u0131\u015flar": {},
|
||||||
|
"Yurt \u0130\u00e7i Sat\u0131\u015flar": {}
|
||||||
|
},
|
||||||
|
"Di\u011fer Faaliyetlerden Olu\u015fan Gelir ve K\u00e2rlar": {
|
||||||
|
"Ba\u011fl\u0131 Ortakl\u0131klardan Temett\u00fc Gelirleri": {},
|
||||||
|
"Di\u011fer Ola\u011fan Gelir Ve K\u00e2rlar": {},
|
||||||
|
"Enflasyon D\u00fczeltme K\u00e2rlar\u0131": {},
|
||||||
|
"Faiz Gelirleri": {},
|
||||||
|
"Kambiyo K\u00e2rlar\u0131": {},
|
||||||
|
"Komisyon Gelirleri": {},
|
||||||
|
"Konusu Kalmayan Kar\u015f\u0131l\u0131klar": {},
|
||||||
|
"Menkul K\u0131ymet Sat\u0131\u015f K\u00e2rlar\u0131": {},
|
||||||
|
"Reeskont Faiz Gelirleri": {},
|
||||||
|
"\u0130\u015ftiraklerden Temett\u00fc Gelirleri": {}
|
||||||
|
},
|
||||||
|
"Di\u011fer Faaliyetlerden Olu\u015fan Gider ve Zararlar (-)": {
|
||||||
|
"Di\u011fer Ola\u011fan Gider Ve Zararlar(-)": {},
|
||||||
|
"Enflasyon D\u00fczeltmesi Zararlar\u0131(-)": {},
|
||||||
|
"Kambiyo Zararlar\u0131(-)": {},
|
||||||
|
"Kar\u015f\u0131l\u0131k Giderleri(-)": {},
|
||||||
|
"Komisyon Giderleri(-)": {},
|
||||||
|
"Menkul K\u0131ymet Sat\u0131\u015f Zararlar\u0131(-)": {},
|
||||||
|
"Reeskont Faiz Giderleri(-)": {}
|
||||||
|
},
|
||||||
|
"D\u00f6nem Net K\u00e2r\u0131 Ve Zarar\u0131": {
|
||||||
|
"D\u00f6nem K\u00e2r\u0131 Vergi Ve Di\u011fer Yasal Y\u00fck\u00fcml\u00fcl\u00fck Kar\u015f\u0131l\u0131klar\u0131(-)": {},
|
||||||
|
"D\u00f6nem K\u00e2r\u0131 Veya Zarar\u0131": {},
|
||||||
|
"D\u00f6nem Net K\u00e2r\u0131 Veya Zarar\u0131": {},
|
||||||
|
"Enflasyon D\u00fczeltme Hesab\u0131": {},
|
||||||
|
"Y\u0131llara Yayg\u0131n \u0130n\u015faat Ve Enflasyon D\u00fczeltme Hesab\u0131": {}
|
||||||
|
},
|
||||||
|
"Faaliyet Giderleri(-)": {
|
||||||
|
"Ara\u015ft\u0131rma Ve Geli\u015ftirme Giderleri(-)": {},
|
||||||
|
"Genel Y\u00f6netim Giderleri(-)": {},
|
||||||
|
"Pazarlama Sat\u0131\u015f Ve Da\u011f\u0131t\u0131m Giderleri(-)": {}
|
||||||
|
},
|
||||||
|
"Finansman Giderleri": {
|
||||||
|
"K\u0131sa Vadeli Bor\u00e7lanma Giderleri(-)": {},
|
||||||
|
"Uzun Vadeli Bor\u00e7lanma Giderleri(-)": {}
|
||||||
|
},
|
||||||
|
"Ola\u011fan D\u0131\u015f\u0131 Gelir Ve K\u00e2rlar": {
|
||||||
|
"Di\u011fer Ola\u011fan D\u0131\u015f\u0131 Gelir Ve K\u00e2rlar": {},
|
||||||
|
"\u00d6nceki D\u00f6nem Gelir Ve K\u00e2rlar\u0131": {}
|
||||||
|
},
|
||||||
|
"Ola\u011fan D\u0131\u015f\u0131 Gider Ve Zaralar(-)": {
|
||||||
|
"Di\u011fer Ola\u011fan D\u0131\u015f\u0131 Gider Ve Zararlar(-)": {},
|
||||||
|
"\u00c7al\u0131\u015fmayan K\u0131s\u0131m Gider Ve Zararlar\u0131(-)": {},
|
||||||
|
"\u00d6nceki D\u00f6nem Gider Ve Zararlar\u0131(-)": {}
|
||||||
|
},
|
||||||
|
"Sat\u0131\u015f \u0130ndirimleri (-)": {
|
||||||
|
"Di\u011fer \u0130ndirimler": {},
|
||||||
|
"Sat\u0131\u015f \u0130ndirimleri(-)": {},
|
||||||
|
"Sat\u0131\u015ftan \u0130adeler(-)": {}
|
||||||
|
},
|
||||||
|
"Sat\u0131\u015flar\u0131n Maliyeti(-)": {
|
||||||
|
"Di\u011fer Sat\u0131\u015flar\u0131n Maliyeti(-)": {},
|
||||||
|
"Sat\u0131lan Hizmet Maliyeti(-)": {},
|
||||||
|
"Sat\u0131lan Mamuller Maliyeti(-)": {},
|
||||||
|
"Sat\u0131lan Ticari Mallar Maliyeti(-)": {}
|
||||||
|
},
|
||||||
|
"root_type": ""
|
||||||
|
},
|
||||||
|
"K\u0131sa Vadeli Yabanc\u0131 Kaynaklar": {
|
||||||
|
"Al\u0131nan Avanslar": {
|
||||||
|
"Al\u0131nan Di\u011fer Avanslar": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Al\u0131nan Sipari\u015f Avanslar\u0131": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Bor\u00e7 ve Gider Kar\u015f\u0131l\u0131klar\u0131": {
|
||||||
|
"Di\u011fer Bor\u00e7 Ve Gider Kar\u015f\u0131l\u0131klar\u0131": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"D\u00f6nem K\u00e2r\u0131 Vergi Ve Di\u011fer Yasal Y\u00fck\u00fcml\u00fcl\u00fck Kar\u015f\u0131l\u0131klar\u0131": {
|
||||||
|
"account_type": "Tax"
|
||||||
|
},
|
||||||
|
"D\u00f6nem K\u00e2r\u0131n\u0131n Pe\u015fin \u00d6denen Vergi Ve Di\u011fer Y\u00fck\u00fcml\u00fcl\u00fckler(-)": {
|
||||||
|
"account_type": "Tax"
|
||||||
|
},
|
||||||
|
"K\u0131dem Tazminat\u0131 Kar\u015f\u0131l\u0131\u011f\u0131": {},
|
||||||
|
"Maliyet Giderleri Kar\u015f\u0131l\u0131\u011f\u0131": {},
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Di\u011fer Bor\u00e7lar": {
|
||||||
|
"Ba\u011fl\u0131 Ortakl\u0131klara Bor\u00e7lar": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Di\u011fer Bor\u00e7 Senetleri Reeskontu(-)": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Di\u011fer \u00c7e\u015fitli Bor\u00e7lar": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Ortaklara Bor\u00e7lar": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Personele Bor\u00e7lar": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"account_type": "Payable",
|
||||||
|
"\u0130\u015ftiraklere Bor\u00e7lar": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Di\u011fer K\u0131sa Vadeli Yabanc\u0131 Kaynaklar": {
|
||||||
|
"Di\u011fer KDV": {
|
||||||
|
"account_type": "Tax"
|
||||||
|
},
|
||||||
|
"Di\u011fer \u00c7e\u015fitli Yabanc\u0131 Kaynaklar": {},
|
||||||
|
"Hesaplanan KDV": {
|
||||||
|
"account_type": "Tax"
|
||||||
|
},
|
||||||
|
"Merkez Ve \u015eubeler Cari Hesab\u0131": {},
|
||||||
|
"Say\u0131m Ve Tesell\u00fcm Fazlalar\u0131": {},
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Gelecek Aylara Ait Gelirler Ve Gider Tahakkuklar\u0131": {
|
||||||
|
"Gelecek Aylara Ait Gelirler": {},
|
||||||
|
"Gider Tahakkuklar\u0131": {}
|
||||||
|
},
|
||||||
|
"Mali Bor\u00e7lar": {
|
||||||
|
"Banka Kredileri": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Di\u011fer Mali Bor\u00e7lar": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Ertelenmi\u015f Finansal Kiralama Bor\u00e7lanma Maliyetleri(-)": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Finansal Kiralama \u0130\u015flemlerinden Bor\u00e7lar": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Menkul K\u0131ymetler \u0130hra\u00e7 Fark\u0131(-)": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Tahvil Anapara Bor\u00e7, Taksit Ve Faizleri": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Uzun Vadeli Kredilerin Anapara Taksitleri Ve Faizleri": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"account_type": "Payable",
|
||||||
|
"\u00c7\u0131kar\u0131lan Bonolar Ve Senetler": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"\u00c7\u0131kar\u0131lm\u0131\u015f Di\u011fer Menkul K\u0131ymetler": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Ticari Bor\u00e7lar": {
|
||||||
|
"Al\u0131nan Depozito Ve Teminatlar": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Bor\u00e7 Senetleri": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Bor\u00e7 Senetleri Reeskontu(-)": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Di\u011fer Ticari Bor\u00e7lar": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Sat\u0131c\u0131lar": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Y\u0131llara Yayg\u0131n \u0130n\u015faat Ve Onar\u0131m Hakedi\u015fleri": {
|
||||||
|
"350 Y\u0131llara Yayg\u0131n \u0130n\u015faat Ve Onar\u0131m Hakedi\u015fleri Bedelleri": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"root_type": "",
|
||||||
|
"\u00d6denecek Vergi ve Di\u011fer Y\u00fck\u00fcml\u00fcl\u00fckler": {
|
||||||
|
"Vadesi Ge\u00e7mi\u015f, Ertelenmi\u015f Veya Taksitlendirilmi\u015f Vergi Ve Di\u011fer Y\u00fck\u00fcml\u00fcl\u00fckler": {
|
||||||
|
"account_type": "Tax"
|
||||||
|
},
|
||||||
|
"account_type": "Tax",
|
||||||
|
"\u00d6denecek Di\u011fer Y\u00fck\u00fcml\u00fcl\u00fckler": {
|
||||||
|
"account_type": "Tax"
|
||||||
|
},
|
||||||
|
"\u00d6denecek Sosyal G\u00fcvenl\u00fck Kesintileri": {
|
||||||
|
"account_type": "Tax"
|
||||||
|
},
|
||||||
|
"\u00d6denecek Vergi Ve Fonlar": {
|
||||||
|
"account_type": "Tax"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Maliyet Hesaplar\u0131": {
|
||||||
|
"Ara\u015ft\u0131rma Ve Geli\u015ftirme Giderleri": {},
|
||||||
|
"Direkt \u0130lk Madde Ve Malzeme Giderleri": {
|
||||||
|
"Direk \u0130lk Madde Ve Malzeme Giderleri Hesab\u0131": {},
|
||||||
|
"Direkt \u0130lk Madde Ve Malzeme Fiyat Fark\u0131": {},
|
||||||
|
"Direkt \u0130lk Madde Ve Malzeme Miktar Fark\u0131": {},
|
||||||
|
"Direkt \u0130lk Madde Ve Malzeme Yans\u0131tma Hesab\u0131": {}
|
||||||
|
},
|
||||||
|
"Direkt \u0130\u015f\u00e7ilik Giderleri": {
|
||||||
|
"Direkt \u0130\u015f\u00e7ilik Giderleri": {},
|
||||||
|
"Direkt \u0130\u015f\u00e7ilik Giderleri Yans\u0131tma Hesab\u0131": {},
|
||||||
|
"Direkt \u0130\u015f\u00e7ilik S\u00fcre Farklar\u0131": {},
|
||||||
|
"Direkt \u0130\u015f\u00e7ilik \u00dccret Farklar\u0131": {}
|
||||||
|
},
|
||||||
|
"Finansman Giderleri": {
|
||||||
|
"Finansman Giderleri": {},
|
||||||
|
"Finansman Giderleri Fark Hesab\u0131": {},
|
||||||
|
"Finansman Giderleri Yans\u0131tma Hesab\u0131": {}
|
||||||
|
},
|
||||||
|
"Genel Y\u00f6netim Giderleri": {
|
||||||
|
"Genel Y\u00f6netim Gider Farklar\u0131 Hesab\u0131": {},
|
||||||
|
"Genel Y\u00f6netim Giderleri": {},
|
||||||
|
"Genel Y\u00f6netim Giderleri Yans\u0131tma Hesab\u0131": {}
|
||||||
|
},
|
||||||
|
"Genel \u00dcretim Giderleri": {
|
||||||
|
"Genel \u00dcretim Giderleri": {},
|
||||||
|
"Genel \u00dcretim Giderleri B\u00fct\u00e7e Farklar\u0131": {},
|
||||||
|
"Genel \u00dcretim Giderleri Kapasite Farklar\u0131": {},
|
||||||
|
"Genel \u00dcretim Giderleri Verimlilik Giderleri": {},
|
||||||
|
"Genel \u00dcretim Giderleri Yans\u0131tma Hesab\u0131": {}
|
||||||
|
},
|
||||||
|
"Hizmet \u00dcretim Maliyeti": {
|
||||||
|
"Hizmet \u00dcretim Maliyeti": {},
|
||||||
|
"Hizmet \u00dcretim Maliyeti Fark Hesaplar\u0131": {},
|
||||||
|
"Hizmet \u00dcretim Maliyeti Yans\u0131tma Hesab\u0131": {}
|
||||||
|
},
|
||||||
|
"Maliyet Muhasebesi Ba\u011flant\u0131 Hesaplar\u0131": {
|
||||||
|
"Maliyet Muhasebesi Ba\u011flant\u0131 Hesab\u0131": {},
|
||||||
|
"Maliyet Muhasebesi Yans\u0131tma Hesab\u0131": {}
|
||||||
|
},
|
||||||
|
"Pazarlama, Sat\u0131\u015f Ve Da\u011f\u0131t\u0131m Giderleri": {
|
||||||
|
"Atra\u015ft\u0131rma Ve Geli\u015ftirme Giderleri": {},
|
||||||
|
"Pazarlama Sat\u0131\u015f Ve Dag\u0131t\u0131m Giderleri Yans\u0131tma Hesab\u0131": {},
|
||||||
|
"Pazarlama Sat\u0131\u015f Ve Da\u011f\u0131t\u0131m Giderleri Fark Hesab\u0131": {}
|
||||||
|
},
|
||||||
|
"root_type": ""
|
||||||
|
},
|
||||||
|
"Naz\u0131m Hesaplar": {
|
||||||
|
"root_type": ""
|
||||||
|
},
|
||||||
|
"Serbest Hesaplar": {
|
||||||
|
"root_type": ""
|
||||||
|
},
|
||||||
|
"Uzun Vadeli Yabanc\u0131 Kaynaklar": {
|
||||||
|
"Al\u0131nan Avanslar": {
|
||||||
|
"Al\u0131nan Di\u011fer Avanslar": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Al\u0131nan Sipari\u015f Avanslar\u0131": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Bor\u00e7 Ve Gider Kar\u015f\u0131l\u0131klar\u0131": {
|
||||||
|
"Di\u011fer Bor\u00e7 Ve Gider Kar\u015f\u0131l\u0131klar\u0131": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"K\u0131dem Tazminat\u0131 Kar\u015f\u0131l\u0131\u011f\u0131": {},
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Di\u011fer Bor\u00e7lar": {
|
||||||
|
"Ba\u011fl\u0131 Ortakl\u0131klara Bor\u00e7lar": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Di\u011fer Bor\u00e7 Senetleri Reeskontu(-)": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Di\u011fer \u00c7e\u015fitli Bor\u00e7lar": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Kamuya Olan Ertelenmi\u015f Veya Taksitlendirilmi\u015f Bor\u00e7lar": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Ortaklara Bor\u00e7lar": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"account_type": "Payable",
|
||||||
|
"\u0130\u015ftiraklere Bor\u00e7lar": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Di\u011fer Uzun Vadeli Yabanc\u0131 Kaynaklar": {
|
||||||
|
"Di\u011fer \u00c7e\u015fitli Uzun Vadeli Yabanc\u0131 Kaynaklar": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Gelecek Y\u0131llara Ertelenmi\u015f Veya Terkin Edilecek KDV": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Tesise Kat\u0131lma Paylar\u0131": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Gelecek Y\u0131llara Ait Gelirler Ve Gider Tahakkuklar\u0131": {
|
||||||
|
"Gelecek Y\u0131llara Ait Gelirler": {},
|
||||||
|
"Gider Tahakkuklar\u0131": {}
|
||||||
|
},
|
||||||
|
"Mali Bor\u00e7lar": {
|
||||||
|
"Banka Kredileri": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Di\u011fer Mali Bor\u00e7lar": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Ertelenmi\u015f Finansal Kiralama Bor\u00e7lanma Maliyetleri(-)": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Finansal Kiralama \u0130\u015flemlerinden Bor\u00e7lar": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Menkul K\u0131ymetler \u0130hra\u00e7 Fark\u0131(-)": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"account_type": "Payable",
|
||||||
|
"\u00c7\u0131kar\u0131lm\u0131\u015f Di\u011fer Menkul K\u0131ymetler": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"\u00c7\u0131kar\u0131lm\u0131\u015f Tahviller": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Ticari Bor\u00e7lar": {
|
||||||
|
"Al\u0131nan Depozito Ve Teminatlar": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Bor\u00e7 Senetleri": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Bor\u00e7 Senetleri Reeskontu(-)": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Di\u011fer Ticari Bor\u00e7lar": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"Sat\u0131c\u0131lar": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"account_type": "Payable"
|
||||||
|
},
|
||||||
|
"root_type": ""
|
||||||
|
},
|
||||||
|
"\u00d6z Kaynaklar": {
|
||||||
|
"D\u00f6nem Net K\u00e2r\u0131 (Zarar\u0131)": {
|
||||||
|
"D\u00f6nem Net K\u00e2r\u0131": {},
|
||||||
|
"D\u00f6nem Net Zarar\u0131(-)": {}
|
||||||
|
},
|
||||||
|
"Ge\u00e7mi\u015f Y\u0131llar K\u00e2rlar\u0131": {
|
||||||
|
"Ge\u00e7mi\u015f Y\u0131llar K\u00e2rlar\u0131": {}
|
||||||
|
},
|
||||||
|
"Ge\u00e7mi\u015f Y\u0131llar Zararlar\u0131(-)": {
|
||||||
|
"Ge\u00e7mi\u015f Y\u0131llar Zararlar\u0131(-)": {}
|
||||||
|
},
|
||||||
|
"K\u00e2r Yedekleri": {
|
||||||
|
"Di\u011fer K\u00e2r Yedekleri": {},
|
||||||
|
"Ola\u011fan\u00fcst\u00fc Yedekler": {},
|
||||||
|
"Stat\u00fc Yedekleri": {},
|
||||||
|
"Yasal Yedekler": {},
|
||||||
|
"\u00d6zel Fonlar": {}
|
||||||
|
},
|
||||||
|
"Sermaye Yedekleri": {
|
||||||
|
"Di\u011fer Sermaye Yedekleri": {},
|
||||||
|
"Hisse Senedi \u0130ptal K\u00e2rlar\u0131": {},
|
||||||
|
"Hisse Senetleri \u0130hra\u00e7 Primleri": {},
|
||||||
|
"Maddi Duran Varl\u0131k Yeniden De\u011ferlenme Art\u0131\u015flar\u0131": {},
|
||||||
|
"Maliyet Art\u0131\u015flar\u0131 Fonu": {},
|
||||||
|
"\u0130\u015ftirakler Yeniden De\u011ferleme Art\u0131\u015flar\u0131": {}
|
||||||
|
},
|
||||||
|
"root_type": "",
|
||||||
|
"\u00d6denmi\u015f Sermaye": {
|
||||||
|
"Sermaye": {},
|
||||||
|
"\u00d6denmi\u015f Sermaye(-)": {
|
||||||
|
"account_type": "Payable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1525,8 +1525,7 @@
|
|||||||
"41-Clients et comptes rattach\u00e9s (PASSIF)": {
|
"41-Clients et comptes rattach\u00e9s (PASSIF)": {
|
||||||
"Clients cr\u00e9diteurs": {
|
"Clients cr\u00e9diteurs": {
|
||||||
"Clients - Avances et acomptes re\u00e7us sur commandes": {
|
"Clients - Avances et acomptes re\u00e7us sur commandes": {
|
||||||
"account_number": "4191",
|
"account_number": "4191"
|
||||||
"account_type": "Income Account"
|
|
||||||
},
|
},
|
||||||
"Clients - Dettes pour emballages et mat\u00e9riels consign\u00e9s": {
|
"Clients - Dettes pour emballages et mat\u00e9riels consign\u00e9s": {
|
||||||
"account_number": "4196"
|
"account_number": "4196"
|
||||||
@@ -3142,4 +3141,4 @@
|
|||||||
"account_number": "7"
|
"account_number": "7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1653,4 +1653,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,16 +36,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Fixed Assets": {
|
"Fixed Assets": {
|
||||||
"Capital Equipments": {
|
"Capital Equipment": {
|
||||||
"account_type": "Fixed Asset"
|
"account_type": "Fixed Asset"
|
||||||
},
|
},
|
||||||
"Electronic Equipments": {
|
"Electronic Equipment": {
|
||||||
"account_type": "Fixed Asset"
|
"account_type": "Fixed Asset"
|
||||||
},
|
},
|
||||||
"Furnitures and Fixtures": {
|
"Furniture and Fixtures": {
|
||||||
"account_type": "Fixed Asset"
|
"account_type": "Fixed Asset"
|
||||||
},
|
},
|
||||||
"Office Equipments": {
|
"Office Equipment": {
|
||||||
"account_type": "Fixed Asset"
|
"account_type": "Fixed Asset"
|
||||||
},
|
},
|
||||||
"Plants and Machineries": {
|
"Plants and Machineries": {
|
||||||
@@ -109,8 +109,7 @@
|
|||||||
"Utility Expenses": {},
|
"Utility Expenses": {},
|
||||||
"Write Off": {},
|
"Write Off": {},
|
||||||
"Exchange Gain/Loss": {},
|
"Exchange Gain/Loss": {},
|
||||||
"Gain/Loss on Asset Disposal": {},
|
"Gain/Loss on Asset Disposal": {}
|
||||||
"Impairment": {}
|
|
||||||
},
|
},
|
||||||
"root_type": "Expense"
|
"root_type": "Expense"
|
||||||
},
|
},
|
||||||
@@ -133,8 +132,7 @@
|
|||||||
"Source of Funds (Liabilities)": {
|
"Source of Funds (Liabilities)": {
|
||||||
"Capital Account": {
|
"Capital Account": {
|
||||||
"Reserves and Surplus": {},
|
"Reserves and Surplus": {},
|
||||||
"Shareholders Funds": {},
|
"Shareholders Funds": {}
|
||||||
"Revaluation Surplus": {}
|
|
||||||
},
|
},
|
||||||
"Current Liabilities": {
|
"Current Liabilities": {
|
||||||
"Accounts Payable": {
|
"Accounts Payable": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"country_code": "ni",
|
"country_code": "ni",
|
||||||
"name": "Nicaragua - Catalogo de Cuentas",
|
"name": "Nicaragua - Catálogo de Cuentas",
|
||||||
"tree": {
|
"tree": {
|
||||||
"Activo": {
|
"Activo": {
|
||||||
"Activo Corriente": {
|
"Activo Corriente": {
|
||||||
@@ -491,4 +491,4 @@
|
|||||||
"root_type": "Liability"
|
"root_type": "Liability"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,13 +23,13 @@ def get():
|
|||||||
_("Tax Assets"): {"is_group": 1},
|
_("Tax Assets"): {"is_group": 1},
|
||||||
},
|
},
|
||||||
_("Fixed Assets"): {
|
_("Fixed Assets"): {
|
||||||
_("Capital Equipments"): {"account_type": "Fixed Asset"},
|
_("Capital Equipment"): {"account_type": "Fixed Asset"},
|
||||||
_("Electronic Equipments"): {"account_type": "Fixed Asset"},
|
_("Electronic Equipment"): {"account_type": "Fixed Asset"},
|
||||||
_("Furnitures and Fixtures"): {"account_type": "Fixed Asset"},
|
_("Furniture and Fixtures"): {"account_type": "Fixed Asset"},
|
||||||
_("Office Equipments"): {"account_type": "Fixed Asset"},
|
_("Office Equipment"): {"account_type": "Fixed Asset"},
|
||||||
_("Plants and Machineries"): {"account_type": "Fixed Asset"},
|
_("Plants and Machineries"): {"account_type": "Fixed Asset"},
|
||||||
_("Buildings"): {"account_type": "Fixed Asset"},
|
_("Buildings"): {"account_type": "Fixed Asset"},
|
||||||
_("Softwares"): {"account_type": "Fixed Asset"},
|
_("Software"): {"account_type": "Fixed Asset"},
|
||||||
_("Accumulated Depreciation"): {"account_type": "Accumulated Depreciation"},
|
_("Accumulated Depreciation"): {"account_type": "Accumulated Depreciation"},
|
||||||
_("CWIP Account"): {
|
_("CWIP Account"): {
|
||||||
"account_type": "Capital Work in Progress",
|
"account_type": "Capital Work in Progress",
|
||||||
@@ -72,7 +72,6 @@ def get():
|
|||||||
_("Write Off"): {},
|
_("Write Off"): {},
|
||||||
_("Exchange Gain/Loss"): {},
|
_("Exchange Gain/Loss"): {},
|
||||||
_("Gain/Loss on Asset Disposal"): {},
|
_("Gain/Loss on Asset Disposal"): {},
|
||||||
_("Impairment"): {},
|
|
||||||
},
|
},
|
||||||
"root_type": "Expense",
|
"root_type": "Expense",
|
||||||
},
|
},
|
||||||
@@ -105,7 +104,6 @@ def get():
|
|||||||
_("Dividends Paid"): {"account_type": "Equity"},
|
_("Dividends Paid"): {"account_type": "Equity"},
|
||||||
_("Opening Balance Equity"): {"account_type": "Equity"},
|
_("Opening Balance Equity"): {"account_type": "Equity"},
|
||||||
_("Retained Earnings"): {"account_type": "Equity"},
|
_("Retained Earnings"): {"account_type": "Equity"},
|
||||||
_("Revaluation Surplus"): {"account_type": "Equity"},
|
|
||||||
"root_type": "Equity",
|
"root_type": "Equity",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,13 +36,13 @@ def get():
|
|||||||
"account_number": "1100-1600",
|
"account_number": "1100-1600",
|
||||||
},
|
},
|
||||||
_("Fixed Assets"): {
|
_("Fixed Assets"): {
|
||||||
_("Capital Equipments"): {"account_type": "Fixed Asset", "account_number": "1710"},
|
_("Capital Equipment"): {"account_type": "Fixed Asset", "account_number": "1710"},
|
||||||
_("Electronic Equipments"): {"account_type": "Fixed Asset", "account_number": "1720"},
|
_("Electronic Equipment"): {"account_type": "Fixed Asset", "account_number": "1720"},
|
||||||
_("Furnitures and Fixtures"): {"account_type": "Fixed Asset", "account_number": "1730"},
|
_("Furniture and Fixtures"): {"account_type": "Fixed Asset", "account_number": "1730"},
|
||||||
_("Office Equipments"): {"account_type": "Fixed Asset", "account_number": "1740"},
|
_("Office Equipment"): {"account_type": "Fixed Asset", "account_number": "1740"},
|
||||||
_("Plants and Machineries"): {"account_type": "Fixed Asset", "account_number": "1750"},
|
_("Plants and Machineries"): {"account_type": "Fixed Asset", "account_number": "1750"},
|
||||||
_("Buildings"): {"account_type": "Fixed Asset", "account_number": "1760"},
|
_("Buildings"): {"account_type": "Fixed Asset", "account_number": "1760"},
|
||||||
_("Softwares"): {"account_type": "Fixed Asset", "account_number": "1770"},
|
_("Software"): {"account_type": "Fixed Asset", "account_number": "1770"},
|
||||||
_("Accumulated Depreciation"): {
|
_("Accumulated Depreciation"): {
|
||||||
"account_type": "Accumulated Depreciation",
|
"account_type": "Accumulated Depreciation",
|
||||||
"account_number": "1780",
|
"account_number": "1780",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -120,7 +120,7 @@ class TestAccount(unittest.TestCase):
|
|||||||
InvalidAccountMergeError,
|
InvalidAccountMergeError,
|
||||||
merge_account,
|
merge_account,
|
||||||
"Capital Stock - _TC",
|
"Capital Stock - _TC",
|
||||||
"Softwares - _TC",
|
"Software - _TC",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Raise error as currency doesn't match
|
# Raise error as currency doesn't match
|
||||||
@@ -261,20 +261,28 @@ class TestAccount(unittest.TestCase):
|
|||||||
acc.insert()
|
acc.insert()
|
||||||
|
|
||||||
self.assertTrue(
|
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(
|
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
|
# Try renaming child company account
|
||||||
acc_tc_5 = frappe.db.get_value(
|
acc_tc_5 = frappe.db.get_value(
|
||||||
"Account", {"account_name": "Test Group Account", "company": "_Test Company 5"}
|
"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
|
# 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")
|
update_account_number(acc_tc_5, "Test Modified Account")
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
@@ -283,7 +291,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 = [
|
to_delete = [
|
||||||
"Test Group Account - _TC3",
|
"Test Group Account - _TC3",
|
||||||
@@ -308,7 +318,9 @@ class TestAccount(unittest.TestCase):
|
|||||||
self.assertEqual(acc.account_currency, "INR")
|
self.assertEqual(acc.account_currency, "INR")
|
||||||
|
|
||||||
# Make a JV against this account
|
# 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"
|
acc.account_currency = "USD"
|
||||||
self.assertRaises(frappe.ValidationError, acc.save)
|
self.assertRaises(frappe.ValidationError, acc.save)
|
||||||
|
|||||||
@@ -40,12 +40,16 @@ class AccountClosingBalance(Document):
|
|||||||
def make_closing_entries(closing_entries, voucher_name, company, closing_date):
|
def make_closing_entries(closing_entries, voucher_name, company, closing_date):
|
||||||
accounting_dimensions = get_accounting_dimensions()
|
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
|
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 = frappe.new_doc("Account Closing Balance")
|
||||||
cle.update(value)
|
cle.update(value)
|
||||||
cle.update(value["dimensions"])
|
cle.update(value["dimensions"])
|
||||||
@@ -113,9 +117,9 @@ def get_previous_closing_entries(company, closing_date, accounting_dimensions):
|
|||||||
entries = []
|
entries = []
|
||||||
last_period_closing_voucher = frappe.db.get_all(
|
last_period_closing_voucher = frappe.db.get_all(
|
||||||
"Period Closing Voucher",
|
"Period Closing Voucher",
|
||||||
filters={"docstatus": 1, "company": company, "period_end_date": ("<", closing_date)},
|
filters={"docstatus": 1, "company": company, "posting_date": ("<", closing_date)},
|
||||||
fields=["name"],
|
fields=["name"],
|
||||||
order_by="period_end_date desc",
|
order_by="posting_date desc",
|
||||||
limit=1,
|
limit=1,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -57,12 +57,9 @@ frappe.ui.form.on("Accounting Dimension", {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
label: function (frm) {
|
|
||||||
frm.set_value("fieldname", frm.doc.label.replace(/ /g, "_").replace(/-/g, "_").toLowerCase());
|
|
||||||
},
|
|
||||||
|
|
||||||
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(
|
frappe.db.get_value(
|
||||||
"Accounting Dimension",
|
"Accounting Dimension",
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import json
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _, scrub
|
from frappe import _, scrub
|
||||||
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
|
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
|
||||||
from frappe.database.schema import validate_column_name
|
|
||||||
from frappe.model import core_doctypes_list
|
from frappe.model import core_doctypes_list
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import cstr
|
from frappe.utils import cstr
|
||||||
@@ -41,8 +40,7 @@ class AccountingDimension(Document):
|
|||||||
self.set_fieldname_and_label()
|
self.set_fieldname_and_label()
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
if self.document_type in (
|
if self.document_type in core_doctypes_list + (
|
||||||
*core_doctypes_list,
|
|
||||||
"Accounting Dimension",
|
"Accounting Dimension",
|
||||||
"Project",
|
"Project",
|
||||||
"Cost Center",
|
"Cost Center",
|
||||||
@@ -50,10 +48,13 @@ class AccountingDimension(Document):
|
|||||||
"Company",
|
"Company",
|
||||||
"Account",
|
"Account",
|
||||||
):
|
):
|
||||||
|
|
||||||
msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type)
|
msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type)
|
||||||
frappe.throw(msg)
|
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():
|
if exists and self.is_new():
|
||||||
frappe.throw(_("Document Type already used as a dimension"))
|
frappe.throw(_("Document Type already used as a dimension"))
|
||||||
@@ -61,7 +62,6 @@ class AccountingDimension(Document):
|
|||||||
if not self.is_new():
|
if not self.is_new():
|
||||||
self.validate_document_type_change()
|
self.validate_document_type_change()
|
||||||
|
|
||||||
validate_column_name(self.fieldname)
|
|
||||||
self.validate_dimension_defaults()
|
self.validate_dimension_defaults()
|
||||||
|
|
||||||
def validate_document_type_change(self):
|
def validate_document_type_change(self):
|
||||||
@@ -113,6 +113,7 @@ def make_dimension_in_accounting_doctypes(doc, doclist=None):
|
|||||||
repostable_doctypes = get_allowed_types_from_settings()
|
repostable_doctypes = get_allowed_types_from_settings()
|
||||||
|
|
||||||
for doctype in doclist:
|
for doctype in doclist:
|
||||||
|
|
||||||
if (doc_count + 1) % 2 == 0:
|
if (doc_count + 1) % 2 == 0:
|
||||||
insert_after_field = "dimension_col_break"
|
insert_after_field = "dimension_col_break"
|
||||||
else:
|
else:
|
||||||
@@ -147,7 +148,7 @@ def add_dimension_to_budget_doctype(df, doc):
|
|||||||
df.update(
|
df.update(
|
||||||
{
|
{
|
||||||
"insert_after": "cost_center",
|
"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),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -181,17 +182,19 @@ def delete_accounting_dimension(doc):
|
|||||||
frappe.db.sql(
|
frappe.db.sql(
|
||||||
"""
|
"""
|
||||||
DELETE FROM `tabCustom Field`
|
DELETE FROM `tabCustom Field`
|
||||||
WHERE fieldname = {}
|
WHERE fieldname = %s
|
||||||
AND dt IN ({})""".format("%s", ", ".join(["%s"] * len(doclist))), # nosec
|
AND dt IN (%s)"""
|
||||||
tuple([doc.fieldname, *doclist]),
|
% ("%s", ", ".join(["%s"] * len(doclist))), # nosec
|
||||||
|
tuple([doc.fieldname] + doclist),
|
||||||
)
|
)
|
||||||
|
|
||||||
frappe.db.sql(
|
frappe.db.sql(
|
||||||
"""
|
"""
|
||||||
DELETE FROM `tabProperty Setter`
|
DELETE FROM `tabProperty Setter`
|
||||||
WHERE field_name = {}
|
WHERE field_name = %s
|
||||||
AND doc_type IN ({})""".format("%s", ", ".join(["%s"] * len(doclist))), # nosec
|
AND doc_type IN (%s)"""
|
||||||
tuple([doc.fieldname, *doclist]),
|
% ("%s", ", ".join(["%s"] * len(doclist))), # nosec
|
||||||
|
tuple([doc.fieldname] + doclist),
|
||||||
)
|
)
|
||||||
|
|
||||||
budget_against_property = frappe.get_doc("Property Setter", "Budget-budget_against-options")
|
budget_against_property = frappe.get_doc("Property Setter", "Budget-budget_against-options")
|
||||||
@@ -240,6 +243,7 @@ def get_doctypes_with_dimensions():
|
|||||||
|
|
||||||
|
|
||||||
def get_accounting_dimensions(as_list=True, filters=None):
|
def get_accounting_dimensions(as_list=True, filters=None):
|
||||||
|
|
||||||
if not filters:
|
if not filters:
|
||||||
filters = {"disabled": 0}
|
filters = {"disabled": 0}
|
||||||
|
|
||||||
@@ -257,19 +261,18 @@ def get_accounting_dimensions(as_list=True, filters=None):
|
|||||||
|
|
||||||
|
|
||||||
def get_checks_for_pl_and_bs_accounts():
|
def get_checks_for_pl_and_bs_accounts():
|
||||||
if frappe.flags.accounting_dimensions_details is None:
|
dimensions = frappe.db.sql(
|
||||||
# nosemgrep
|
"""SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs
|
||||||
frappe.flags.accounting_dimensions_details = frappe.db.sql(
|
FROM `tabAccounting Dimension`p ,`tabAccounting Dimension Detail` c
|
||||||
"""SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs
|
WHERE p.name = c.parent""",
|
||||||
FROM `tabAccounting Dimension`p ,`tabAccounting Dimension Detail` c
|
as_dict=1,
|
||||||
WHERE p.name = c.parent""",
|
)
|
||||||
as_dict=1,
|
|
||||||
)
|
|
||||||
|
|
||||||
return frappe.flags.accounting_dimensions_details
|
return dimensions
|
||||||
|
|
||||||
|
|
||||||
def get_dimension_with_children(doctype, dimensions):
|
def get_dimension_with_children(doctype, dimensions):
|
||||||
|
|
||||||
if isinstance(dimensions, str):
|
if isinstance(dimensions, str):
|
||||||
dimensions = [dimensions]
|
dimensions = [dimensions]
|
||||||
|
|
||||||
@@ -277,7 +280,9 @@ def get_dimension_with_children(doctype, dimensions):
|
|||||||
|
|
||||||
for dimension in dimensions:
|
for dimension in dimensions:
|
||||||
lft, rgt = frappe.db.get_value(doctype, dimension, ["lft", "rgt"])
|
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]
|
all_dimensions += [c.name for c in children]
|
||||||
|
|
||||||
return all_dimensions
|
return all_dimensions
|
||||||
@@ -285,10 +290,14 @@ def get_dimension_with_children(doctype, dimensions):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_dimensions(with_cost_center_and_project=False):
|
def get_dimensions(with_cost_center_and_project=False):
|
||||||
|
|
||||||
c = frappe.qb.DocType("Accounting Dimension Detail")
|
c = frappe.qb.DocType("Accounting Dimension Detail")
|
||||||
p = frappe.qb.DocType("Accounting Dimension")
|
p = frappe.qb.DocType("Accounting Dimension")
|
||||||
dimension_filters = (
|
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 = (
|
default_dimensions = (
|
||||||
frappe.qb.from_(c)
|
frappe.qb.from_(c)
|
||||||
|
|||||||
@@ -78,8 +78,6 @@ class TestAccountingDimension(unittest.TestCase):
|
|||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
disable_dimension()
|
disable_dimension()
|
||||||
frappe.flags.accounting_dimensions_details = None
|
|
||||||
frappe.flags.dimension_filter_map = None
|
|
||||||
|
|
||||||
|
|
||||||
def create_dimension():
|
def create_dimension():
|
||||||
|
|||||||
@@ -66,40 +66,37 @@ class AccountingDimensionFilter(Document):
|
|||||||
|
|
||||||
|
|
||||||
def get_dimension_filter_map():
|
def get_dimension_filter_map():
|
||||||
if not frappe.flags.get("dimension_filter_map"):
|
filters = frappe.db.sql(
|
||||||
# nosemgrep
|
"""
|
||||||
filters = frappe.db.sql(
|
SELECT
|
||||||
"""
|
a.applicable_on_account, d.dimension_value, p.accounting_dimension,
|
||||||
SELECT
|
p.allow_or_restrict, a.is_mandatory
|
||||||
a.applicable_on_account, d.dimension_value, p.accounting_dimension,
|
FROM
|
||||||
p.allow_or_restrict, a.is_mandatory
|
`tabApplicable On Account` a,
|
||||||
FROM
|
`tabAccounting Dimension Filter` p
|
||||||
`tabApplicable On Account` a,
|
LEFT JOIN `tabAllowed Dimension` d ON d.parent = p.name
|
||||||
`tabAccounting Dimension Filter` p
|
WHERE
|
||||||
LEFT JOIN `tabAllowed Dimension` d ON d.parent = p.name
|
p.name = a.parent
|
||||||
WHERE
|
AND p.disabled = 0
|
||||||
p.name = a.parent
|
""",
|
||||||
AND p.disabled = 0
|
as_dict=1,
|
||||||
""",
|
)
|
||||||
as_dict=1,
|
|
||||||
|
dimension_filter_map = {}
|
||||||
|
|
||||||
|
for f in filters:
|
||||||
|
f.fieldname = scrub(f.accounting_dimension)
|
||||||
|
|
||||||
|
build_map(
|
||||||
|
dimension_filter_map,
|
||||||
|
f.fieldname,
|
||||||
|
f.applicable_on_account,
|
||||||
|
f.dimension_value,
|
||||||
|
f.allow_or_restrict,
|
||||||
|
f.is_mandatory,
|
||||||
)
|
)
|
||||||
|
|
||||||
dimension_filter_map = {}
|
return dimension_filter_map
|
||||||
|
|
||||||
for f in filters:
|
|
||||||
f.fieldname = scrub(f.accounting_dimension)
|
|
||||||
|
|
||||||
build_map(
|
|
||||||
dimension_filter_map,
|
|
||||||
f.fieldname,
|
|
||||||
f.applicable_on_account,
|
|
||||||
f.dimension_value,
|
|
||||||
f.allow_or_restrict,
|
|
||||||
f.is_mandatory,
|
|
||||||
)
|
|
||||||
frappe.flags.dimension_filter_map = dimension_filter_map
|
|
||||||
|
|
||||||
return frappe.flags.dimension_filter_map
|
|
||||||
|
|
||||||
|
|
||||||
def build_map(map_object, dimension, account, filter_value, allow_or_restrict, is_mandatory):
|
def build_map(map_object, dimension, account, filter_value, allow_or_restrict, is_mandatory):
|
||||||
|
|||||||
@@ -47,8 +47,6 @@ class TestAccountingDimensionFilter(unittest.TestCase):
|
|||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
disable_dimension_filter()
|
disable_dimension_filter()
|
||||||
disable_dimension()
|
disable_dimension()
|
||||||
frappe.flags.accounting_dimensions_details = None
|
|
||||||
frappe.flags.dimension_filter_map = None
|
|
||||||
|
|
||||||
for si in self.invoice_list:
|
for si in self.invoice_list:
|
||||||
si.load_from_db()
|
si.load_from_db()
|
||||||
@@ -57,7 +55,9 @@ class TestAccountingDimensionFilter(unittest.TestCase):
|
|||||||
|
|
||||||
|
|
||||||
def create_accounting_dimension_filter():
|
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(
|
frappe.get_doc(
|
||||||
{
|
{
|
||||||
"doctype": "Accounting Dimension Filter",
|
"doctype": "Accounting Dimension Filter",
|
||||||
|
|||||||
@@ -84,10 +84,7 @@ class AccountingPeriod(Document):
|
|||||||
for doctype_for_closing in self.get_doctypes_for_closing():
|
for doctype_for_closing in self.get_doctypes_for_closing():
|
||||||
self.append(
|
self.append(
|
||||||
"closed_documents",
|
"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,
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -101,8 +98,6 @@ def validate_accounting_period_on_doc_save(doc, method=None):
|
|||||||
date = doc.available_for_use_date
|
date = doc.available_for_use_date
|
||||||
elif doc.doctype == "Asset Repair":
|
elif doc.doctype == "Asset Repair":
|
||||||
date = doc.completion_date
|
date = doc.completion_date
|
||||||
elif doc.doctype == "Period Closing Voucher":
|
|
||||||
date = doc.period_end_date
|
|
||||||
else:
|
else:
|
||||||
date = doc.posting_date
|
date = doc.posting_date
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ class TestAccountingPeriod(unittest.TestCase):
|
|||||||
ap1 = create_accounting_period(period_name="Test Accounting Period 2")
|
ap1 = create_accounting_period(period_name="Test Accounting Period 2")
|
||||||
ap1.save()
|
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)
|
self.assertRaises(ClosedAccountingPeriod, doc.save)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
|
|||||||
@@ -3,23 +3,4 @@
|
|||||||
|
|
||||||
frappe.ui.form.on("Accounts Settings", {
|
frappe.ui.form.on("Accounts Settings", {
|
||||||
refresh: function (frm) {},
|
refresh: function (frm) {},
|
||||||
enable_immutable_ledger: function (frm) {
|
|
||||||
if (!frm.doc.enable_immutable_ledger) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let msg = __("Enabling this will change the way how cancelled transactions are handled.");
|
|
||||||
msg += " ";
|
|
||||||
msg += __("Please enable only if the understand the effects of enabling this.");
|
|
||||||
msg += "<br>";
|
|
||||||
msg += "Do you still want to enable immutable ledger?";
|
|
||||||
|
|
||||||
frappe.confirm(
|
|
||||||
msg,
|
|
||||||
() => {},
|
|
||||||
() => {
|
|
||||||
frm.set_value("enable_immutable_ledger", 0);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
"unlink_advance_payment_on_cancelation_of_order",
|
"unlink_advance_payment_on_cancelation_of_order",
|
||||||
"column_break_13",
|
"column_break_13",
|
||||||
"delete_linked_ledger_entries",
|
"delete_linked_ledger_entries",
|
||||||
"enable_immutable_ledger",
|
|
||||||
"invoicing_features_section",
|
"invoicing_features_section",
|
||||||
"check_supplier_invoice_uniqueness",
|
"check_supplier_invoice_uniqueness",
|
||||||
"automatically_fetch_payment_terms",
|
"automatically_fetch_payment_terms",
|
||||||
@@ -55,8 +54,6 @@
|
|||||||
"post_change_gl_entries",
|
"post_change_gl_entries",
|
||||||
"assets_tab",
|
"assets_tab",
|
||||||
"asset_settings_section",
|
"asset_settings_section",
|
||||||
"calculate_depr_using_total_days",
|
|
||||||
"column_break_gjcc",
|
|
||||||
"book_asset_depreciation_entry_automatically",
|
"book_asset_depreciation_entry_automatically",
|
||||||
"closing_settings_tab",
|
"closing_settings_tab",
|
||||||
"period_closing_settings_section",
|
"period_closing_settings_section",
|
||||||
@@ -73,9 +70,7 @@
|
|||||||
"remarks_section",
|
"remarks_section",
|
||||||
"general_ledger_remarks_length",
|
"general_ledger_remarks_length",
|
||||||
"column_break_lvjk",
|
"column_break_lvjk",
|
||||||
"receivable_payable_remarks_length",
|
"receivable_payable_remarks_length"
|
||||||
"payment_request_settings",
|
|
||||||
"create_pr_in_draft_status"
|
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -110,7 +105,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"description": "Enabling this ensures each Purchase Invoice has a unique value in Supplier Invoice No. field within a particular fiscal year",
|
"description": "Enabling ensure each Purchase Invoice has a unique value in Supplier Invoice No. field",
|
||||||
"fieldname": "check_supplier_invoice_uniqueness",
|
"fieldname": "check_supplier_invoice_uniqueness",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Check Supplier Invoice Number Uniqueness"
|
"label": "Check Supplier Invoice Number Uniqueness"
|
||||||
@@ -459,36 +454,6 @@
|
|||||||
"fieldname": "remarks_section",
|
"fieldname": "remarks_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Remarks Column Length"
|
"label": "Remarks Column Length"
|
||||||
},
|
|
||||||
{
|
|
||||||
"default": "0",
|
|
||||||
"description": "On enabling this cancellation entries will be posted on the actual cancellation date and reports will consider cancelled entries as well",
|
|
||||||
"fieldname": "enable_immutable_ledger",
|
|
||||||
"fieldtype": "Check",
|
|
||||||
"label": "Enable Immutable Ledger"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "column_break_gjcc",
|
|
||||||
"fieldtype": "Column Break"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"default": "0",
|
|
||||||
"description": "Enable this option to calculate daily depreciation by considering the total number of days in the entire depreciation period, (including leap years) while using daily pro-rata based depreciation",
|
|
||||||
"fieldname": "calculate_depr_using_total_days",
|
|
||||||
"fieldtype": "Check",
|
|
||||||
"label": "Calculate daily depreciation using total days in depreciation period"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "Payment Request created from Sales Order or Purchase Order will be in Draft status. When disabled document will be in unsaved state.",
|
|
||||||
"fieldname": "payment_request_settings",
|
|
||||||
"fieldtype": "Tab Break",
|
|
||||||
"label": "Payment Request"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"default": "1",
|
|
||||||
"fieldname": "create_pr_in_draft_status",
|
|
||||||
"fieldtype": "Check",
|
|
||||||
"label": "Create in Draft Status"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "icon-cog",
|
"icon": "icon-cog",
|
||||||
@@ -496,7 +461,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-07-26 06:48:52.714630",
|
"modified": "2024-01-30 14:04:26.553554",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Accounts Settings",
|
"name": "Accounts Settings",
|
||||||
|
|||||||
@@ -33,15 +33,12 @@ class AccountsSettings(Document):
|
|||||||
book_deferred_entries_based_on: DF.Literal["Days", "Months"]
|
book_deferred_entries_based_on: DF.Literal["Days", "Months"]
|
||||||
book_deferred_entries_via_journal_entry: DF.Check
|
book_deferred_entries_via_journal_entry: DF.Check
|
||||||
book_tax_discount_loss: DF.Check
|
book_tax_discount_loss: DF.Check
|
||||||
calculate_depr_using_total_days: DF.Check
|
|
||||||
check_supplier_invoice_uniqueness: DF.Check
|
check_supplier_invoice_uniqueness: DF.Check
|
||||||
create_pr_in_draft_status: DF.Check
|
|
||||||
credit_controller: DF.Link | None
|
credit_controller: DF.Link | None
|
||||||
delete_linked_ledger_entries: DF.Check
|
delete_linked_ledger_entries: DF.Check
|
||||||
determine_address_tax_category_from: DF.Literal["Billing Address", "Shipping Address"]
|
determine_address_tax_category_from: DF.Literal["Billing Address", "Shipping Address"]
|
||||||
enable_common_party_accounting: DF.Check
|
enable_common_party_accounting: DF.Check
|
||||||
enable_fuzzy_matching: DF.Check
|
enable_fuzzy_matching: DF.Check
|
||||||
enable_immutable_ledger: DF.Check
|
|
||||||
enable_party_matching: DF.Check
|
enable_party_matching: DF.Check
|
||||||
frozen_accounts_modifier: DF.Link | None
|
frozen_accounts_modifier: DF.Link | None
|
||||||
general_ledger_remarks_length: DF.Int
|
general_ledger_remarks_length: DF.Int
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
|
|
||||||
// For license information, please see license.txt
|
|
||||||
|
|
||||||
// frappe.ui.form.on("Advance Payment Ledger Entry", {
|
|
||||||
// refresh(frm) {
|
|
||||||
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
{
|
|
||||||
"actions": [],
|
|
||||||
"allow_rename": 1,
|
|
||||||
"creation": "2024-10-16 16:57:12.085072",
|
|
||||||
"doctype": "DocType",
|
|
||||||
"engine": "InnoDB",
|
|
||||||
"field_order": [
|
|
||||||
"company",
|
|
||||||
"voucher_type",
|
|
||||||
"voucher_no",
|
|
||||||
"against_voucher_type",
|
|
||||||
"against_voucher_no",
|
|
||||||
"amount",
|
|
||||||
"currency",
|
|
||||||
"event"
|
|
||||||
],
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"fieldname": "voucher_type",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"label": "Voucher Type",
|
|
||||||
"options": "DocType",
|
|
||||||
"read_only": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "voucher_no",
|
|
||||||
"fieldtype": "Dynamic Link",
|
|
||||||
"label": "Voucher No",
|
|
||||||
"options": "voucher_type",
|
|
||||||
"read_only": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "against_voucher_type",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"label": "Against Voucher Type",
|
|
||||||
"options": "DocType",
|
|
||||||
"read_only": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "against_voucher_no",
|
|
||||||
"fieldtype": "Dynamic Link",
|
|
||||||
"label": "Against Voucher No",
|
|
||||||
"options": "against_voucher_type",
|
|
||||||
"read_only": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "amount",
|
|
||||||
"fieldtype": "Currency",
|
|
||||||
"label": "Amount",
|
|
||||||
"read_only": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "currency",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"label": "Currency",
|
|
||||||
"options": "Currency",
|
|
||||||
"read_only": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "event",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"label": "Event",
|
|
||||||
"read_only": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "company",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"label": "Company",
|
|
||||||
"options": "Company",
|
|
||||||
"read_only": 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"in_create": 1,
|
|
||||||
"index_web_pages_for_search": 1,
|
|
||||||
"links": [],
|
|
||||||
"modified": "2024-11-05 10:31:28.736671",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "Accounts",
|
|
||||||
"name": "Advance Payment Ledger Entry",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [
|
|
||||||
{
|
|
||||||
"email": 1,
|
|
||||||
"export": 1,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Accounts User",
|
|
||||||
"share": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"email": 1,
|
|
||||||
"export": 1,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Accounts Manager",
|
|
||||||
"share": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"email": 1,
|
|
||||||
"export": 1,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Auditor",
|
|
||||||
"share": 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"sort_field": "creation",
|
|
||||||
"sort_order": "DESC",
|
|
||||||
"states": []
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
|
|
||||||
# For license information, please see license.txt
|
|
||||||
|
|
||||||
# import frappe
|
|
||||||
from frappe.model.document import Document
|
|
||||||
|
|
||||||
|
|
||||||
class AdvancePaymentLedgerEntry(Document):
|
|
||||||
# begin: auto-generated types
|
|
||||||
# This code is auto-generated. Do not modify anything in this block.
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from frappe.types import DF
|
|
||||||
|
|
||||||
against_voucher_no: DF.DynamicLink | None
|
|
||||||
against_voucher_type: DF.Link | None
|
|
||||||
amount: DF.Currency
|
|
||||||
company: DF.Link | None
|
|
||||||
currency: DF.Link | None
|
|
||||||
event: DF.Data | None
|
|
||||||
voucher_no: DF.DynamicLink | None
|
|
||||||
voucher_type: DF.Link | None
|
|
||||||
# end: auto-generated types
|
|
||||||
|
|
||||||
pass
|
|
||||||
@@ -1,222 +0,0 @@
|
|||||||
# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors
|
|
||||||
# See license.txt
|
|
||||||
|
|
||||||
import frappe
|
|
||||||
from frappe.tests.utils import FrappeTestCase
|
|
||||||
from frappe.utils import nowdate, today
|
|
||||||
|
|
||||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
|
|
||||||
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
|
|
||||||
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
|
|
||||||
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
|
||||||
|
|
||||||
|
|
||||||
class TestAdvancePaymentLedgerEntry(AccountsTestMixin, FrappeTestCase):
|
|
||||||
"""
|
|
||||||
Integration tests for AdvancePaymentLedgerEntry.
|
|
||||||
Use this class for testing interactions between multiple components.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.create_company()
|
|
||||||
self.create_usd_receivable_account()
|
|
||||||
self.create_usd_payable_account()
|
|
||||||
self.create_item()
|
|
||||||
self.clear_old_entries()
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
frappe.db.rollback()
|
|
||||||
|
|
||||||
def create_sales_order(self, qty=1, rate=100, currency="INR", do_not_submit=False):
|
|
||||||
"""
|
|
||||||
Helper method
|
|
||||||
"""
|
|
||||||
so = make_sales_order(
|
|
||||||
company=self.company,
|
|
||||||
customer=self.customer,
|
|
||||||
currency=currency,
|
|
||||||
item=self.item,
|
|
||||||
qty=qty,
|
|
||||||
rate=rate,
|
|
||||||
transaction_date=today(),
|
|
||||||
do_not_submit=do_not_submit,
|
|
||||||
)
|
|
||||||
return so
|
|
||||||
|
|
||||||
def create_purchase_order(self, qty=1, rate=100, currency="INR", do_not_submit=False):
|
|
||||||
"""
|
|
||||||
Helper method
|
|
||||||
"""
|
|
||||||
po = create_purchase_order(
|
|
||||||
company=self.company,
|
|
||||||
customer=self.supplier,
|
|
||||||
currency=currency,
|
|
||||||
item=self.item,
|
|
||||||
qty=qty,
|
|
||||||
rate=rate,
|
|
||||||
transaction_date=today(),
|
|
||||||
do_not_submit=do_not_submit,
|
|
||||||
)
|
|
||||||
return po
|
|
||||||
|
|
||||||
def test_so_advance_paid_and_currency_with_payment(self):
|
|
||||||
self.create_customer("_Test USD Customer", "USD")
|
|
||||||
|
|
||||||
so = self.create_sales_order(currency="USD", do_not_submit=True)
|
|
||||||
so.conversion_rate = 80
|
|
||||||
so.submit()
|
|
||||||
|
|
||||||
pe_exchange_rate = 85
|
|
||||||
pe = get_payment_entry(so.doctype, so.name, bank_account=self.cash)
|
|
||||||
pe.reference_no = "1"
|
|
||||||
pe.reference_date = nowdate()
|
|
||||||
pe.paid_from = self.debtors_usd
|
|
||||||
pe.paid_from_account_currency = "USD"
|
|
||||||
pe.source_exchange_rate = pe_exchange_rate
|
|
||||||
pe.paid_amount = so.grand_total
|
|
||||||
pe.received_amount = pe_exchange_rate * pe.paid_amount
|
|
||||||
pe.references[0].outstanding_amount = 100
|
|
||||||
pe.references[0].total_amount = 100
|
|
||||||
pe.references[0].allocated_amount = 100
|
|
||||||
pe.save().submit()
|
|
||||||
|
|
||||||
so.reload()
|
|
||||||
self.assertEqual(so.advance_paid, 100)
|
|
||||||
self.assertEqual(so.party_account_currency, "USD")
|
|
||||||
|
|
||||||
# cancel advance payment
|
|
||||||
pe.reload()
|
|
||||||
pe.cancel()
|
|
||||||
|
|
||||||
so.reload()
|
|
||||||
self.assertEqual(so.advance_paid, 0)
|
|
||||||
self.assertEqual(so.party_account_currency, "USD")
|
|
||||||
|
|
||||||
def test_so_advance_paid_and_currency_with_journal(self):
|
|
||||||
self.create_customer("_Test USD Customer", "USD")
|
|
||||||
|
|
||||||
so = self.create_sales_order(currency="USD", do_not_submit=True)
|
|
||||||
so.conversion_rate = 80
|
|
||||||
so.submit()
|
|
||||||
|
|
||||||
je_exchange_rate = 85
|
|
||||||
je = frappe.get_doc(
|
|
||||||
{
|
|
||||||
"doctype": "Journal Entry",
|
|
||||||
"company": self.company,
|
|
||||||
"voucher_type": "Journal Entry",
|
|
||||||
"posting_date": so.transaction_date,
|
|
||||||
"multi_currency": True,
|
|
||||||
"accounts": [
|
|
||||||
{
|
|
||||||
"account": self.debtors_usd,
|
|
||||||
"party_type": "Customer",
|
|
||||||
"party": so.customer,
|
|
||||||
"credit": 8500,
|
|
||||||
"credit_in_account_currency": 100,
|
|
||||||
"is_advance": "Yes",
|
|
||||||
"reference_type": so.doctype,
|
|
||||||
"reference_name": so.name,
|
|
||||||
"exchange_rate": je_exchange_rate,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"account": self.cash,
|
|
||||||
"debit": 8500,
|
|
||||||
"debit_in_account_currency": 8500,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
je.save().submit()
|
|
||||||
so.reload()
|
|
||||||
self.assertEqual(so.advance_paid, 100)
|
|
||||||
self.assertEqual(so.party_account_currency, "USD")
|
|
||||||
|
|
||||||
# cancel advance payment
|
|
||||||
je.reload()
|
|
||||||
je.cancel()
|
|
||||||
|
|
||||||
so.reload()
|
|
||||||
self.assertEqual(so.advance_paid, 0)
|
|
||||||
self.assertEqual(so.party_account_currency, "USD")
|
|
||||||
|
|
||||||
def test_po_advance_paid_and_currency_with_payment(self):
|
|
||||||
self.create_supplier("_Test USD Supplier", "USD")
|
|
||||||
|
|
||||||
po = self.create_purchase_order(currency="USD", do_not_submit=True)
|
|
||||||
po.conversion_rate = 80
|
|
||||||
po.submit()
|
|
||||||
|
|
||||||
pe_exchange_rate = 85
|
|
||||||
pe = get_payment_entry(po.doctype, po.name, bank_account=self.cash)
|
|
||||||
pe.reference_no = "1"
|
|
||||||
pe.reference_date = nowdate()
|
|
||||||
pe.paid_to = self.creditors_usd
|
|
||||||
pe.paid_to_account_currency = "USD"
|
|
||||||
pe.target_exchange_rate = pe_exchange_rate
|
|
||||||
pe.received_amount = po.grand_total
|
|
||||||
pe.paid_amount = pe_exchange_rate * pe.received_amount
|
|
||||||
pe.references[0].outstanding_amount = 100
|
|
||||||
pe.references[0].total_amount = 100
|
|
||||||
pe.references[0].allocated_amount = 100
|
|
||||||
pe.save().submit()
|
|
||||||
|
|
||||||
po.reload()
|
|
||||||
self.assertEqual(po.advance_paid, 100)
|
|
||||||
self.assertEqual(po.party_account_currency, "USD")
|
|
||||||
|
|
||||||
# cancel advance payment
|
|
||||||
pe.reload()
|
|
||||||
pe.cancel()
|
|
||||||
|
|
||||||
po.reload()
|
|
||||||
self.assertEqual(po.advance_paid, 0)
|
|
||||||
self.assertEqual(po.party_account_currency, "USD")
|
|
||||||
|
|
||||||
def test_po_advance_paid_and_currency_with_journal(self):
|
|
||||||
self.create_supplier("_Test USD Supplier", "USD")
|
|
||||||
|
|
||||||
po = self.create_purchase_order(currency="USD", do_not_submit=True)
|
|
||||||
po.conversion_rate = 80
|
|
||||||
po.submit()
|
|
||||||
|
|
||||||
je_exchange_rate = 85
|
|
||||||
je = frappe.get_doc(
|
|
||||||
{
|
|
||||||
"doctype": "Journal Entry",
|
|
||||||
"company": self.company,
|
|
||||||
"voucher_type": "Journal Entry",
|
|
||||||
"posting_date": po.transaction_date,
|
|
||||||
"multi_currency": True,
|
|
||||||
"accounts": [
|
|
||||||
{
|
|
||||||
"account": self.creditors_usd,
|
|
||||||
"party_type": "Supplier",
|
|
||||||
"party": po.supplier,
|
|
||||||
"debit": 8500,
|
|
||||||
"debit_in_account_currency": 100,
|
|
||||||
"is_advance": "Yes",
|
|
||||||
"reference_type": po.doctype,
|
|
||||||
"reference_name": po.name,
|
|
||||||
"exchange_rate": je_exchange_rate,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"account": self.cash,
|
|
||||||
"credit": 8500,
|
|
||||||
"credit_in_account_currency": 8500,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
je.save().submit()
|
|
||||||
po.reload()
|
|
||||||
self.assertEqual(po.advance_paid, 100)
|
|
||||||
self.assertEqual(po.party_account_currency, "USD")
|
|
||||||
|
|
||||||
# cancel advance payment
|
|
||||||
je.reload()
|
|
||||||
je.cancel()
|
|
||||||
|
|
||||||
po.reload()
|
|
||||||
self.assertEqual(po.advance_paid, 0)
|
|
||||||
self.assertEqual(po.party_account_currency, "USD")
|
|
||||||
@@ -208,49 +208,8 @@
|
|||||||
"label": "Disabled"
|
"label": "Disabled"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"links": [
|
"links": [],
|
||||||
{
|
"modified": "2023-09-22 21:31:34.763977",
|
||||||
"group": "Transactions",
|
|
||||||
"link_doctype": "Payment Request",
|
|
||||||
"link_fieldname": "bank_account"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "Transactions",
|
|
||||||
"link_doctype": "Payment Order",
|
|
||||||
"link_fieldname": "bank_account"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "Transactions",
|
|
||||||
"link_doctype": "Bank Guarantee",
|
|
||||||
"link_fieldname": "bank_account"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "Transactions",
|
|
||||||
"link_doctype": "Bank Transaction",
|
|
||||||
"link_fieldname": "bank_account"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "Accounting",
|
|
||||||
"link_doctype": "Payment Entry",
|
|
||||||
"link_fieldname": "bank_account"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "Accounting",
|
|
||||||
"link_doctype": "Journal Entry",
|
|
||||||
"link_fieldname": "bank_account"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "Party",
|
|
||||||
"link_doctype": "Customer",
|
|
||||||
"link_fieldname": "default_bank_account"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "Party",
|
|
||||||
"link_doctype": "Supplier",
|
|
||||||
"link_fieldname": "default_bank_account"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"modified": "2024-10-30 09:41:14.113414",
|
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Bank Account",
|
"name": "Bank Account",
|
||||||
@@ -287,4 +246,4 @@
|
|||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"states": [],
|
"states": [],
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,6 @@ class BankAccount(Document):
|
|||||||
self.validate_company()
|
self.validate_company()
|
||||||
self.validate_iban()
|
self.validate_iban()
|
||||||
self.validate_account()
|
self.validate_account()
|
||||||
self.update_default_bank_account()
|
|
||||||
|
|
||||||
def validate_account(self):
|
def validate_account(self):
|
||||||
if self.account:
|
if self.account:
|
||||||
@@ -70,7 +69,7 @@ class BankAccount(Document):
|
|||||||
|
|
||||||
def validate_company(self):
|
def validate_company(self):
|
||||||
if self.is_company_account and not self.company:
|
if self.is_company_account and not self.company:
|
||||||
frappe.throw(_("Company is manadatory for company account"))
|
frappe.throw(_("Company is mandatory for company account"))
|
||||||
|
|
||||||
def validate_iban(self):
|
def validate_iban(self):
|
||||||
"""
|
"""
|
||||||
@@ -101,51 +100,19 @@ class BankAccount(Document):
|
|||||||
if to_check % 97 != 1:
|
if to_check % 97 != 1:
|
||||||
frappe.throw(_("IBAN is not valid"))
|
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()
|
@frappe.whitelist()
|
||||||
def make_bank_account(doctype, docname):
|
def make_bank_account(doctype, docname):
|
||||||
doc = frappe.new_doc("Bank Account")
|
doc = frappe.new_doc("Bank Account")
|
||||||
doc.party_type = doctype
|
doc.party_type = doctype
|
||||||
doc.party = docname
|
doc.party = docname
|
||||||
|
doc.is_default = 1
|
||||||
|
|
||||||
return doc
|
return doc
|
||||||
|
|
||||||
|
|
||||||
def get_party_bank_account(party_type, party):
|
def get_party_bank_account(party_type, party):
|
||||||
return frappe.db.get_value(
|
return frappe.db.get_value(party_type, party, "default_bank_account")
|
||||||
"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
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
from frappe import _
|
||||||
|
|
||||||
|
|
||||||
|
def get_data():
|
||||||
|
return {
|
||||||
|
"fieldname": "bank_account",
|
||||||
|
"non_standard_fieldnames": {
|
||||||
|
"Customer": "default_bank_account",
|
||||||
|
"Supplier": "default_bank_account",
|
||||||
|
},
|
||||||
|
"transactions": [
|
||||||
|
{
|
||||||
|
"label": _("Payments"),
|
||||||
|
"items": ["Payment Entry", "Payment Request", "Payment Order", "Payroll Entry"],
|
||||||
|
},
|
||||||
|
{"label": _("Party"), "items": ["Customer", "Supplier"]},
|
||||||
|
{"items": ["Bank Guarantee"]},
|
||||||
|
{"items": ["Journal Entry"]},
|
||||||
|
],
|
||||||
|
}
|
||||||
@@ -37,11 +37,11 @@ class TestBankAccount(unittest.TestCase):
|
|||||||
try:
|
try:
|
||||||
bank_account.validate_iban()
|
bank_account.validate_iban()
|
||||||
except ValidationError:
|
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)
|
self.fail(msg=msg)
|
||||||
|
|
||||||
for not_iban in invalid_ibans:
|
for not_iban in invalid_ibans:
|
||||||
bank_account.iban = not_iban
|
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):
|
with self.assertRaises(ValidationError, msg=msg):
|
||||||
bank_account.validate_iban()
|
bank_account.validate_iban()
|
||||||
|
|||||||
@@ -38,11 +38,6 @@ frappe.ui.form.on("Bank Clearance", {
|
|||||||
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");
|
||||||
if (frm.doc.payment_entries.length) {
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
update_clearance_date: function (frm) {
|
update_clearance_date: function (frm) {
|
||||||
@@ -50,7 +45,13 @@ frappe.ui.form.on("Bank Clearance", {
|
|||||||
method: "update_clearance_date",
|
method: "update_clearance_date",
|
||||||
doc: frm.doc,
|
doc: frm.doc,
|
||||||
callback: function (r, rt) {
|
callback: function (r, rt) {
|
||||||
frm.refresh();
|
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");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -59,8 +60,17 @@ frappe.ui.form.on("Bank Clearance", {
|
|||||||
return frappe.call({
|
return frappe.call({
|
||||||
method: "get_payment_entries",
|
method: "get_payment_entries",
|
||||||
doc: frm.doc,
|
doc: frm.doc,
|
||||||
callback: function () {
|
callback: function (r, rt) {
|
||||||
frm.refresh();
|
frm.refresh_field("payment_entries");
|
||||||
|
|
||||||
|
if (frm.doc.payment_entries.length) {
|
||||||
|
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");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import frappe
|
|||||||
from frappe import _, msgprint
|
from frappe import _, msgprint
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.query_builder.custom import ConstantColumn
|
from frappe.query_builder.custom import ConstantColumn
|
||||||
from frappe.utils import flt, fmt_money, get_link_to_form, getdate
|
from frappe.utils import flt, fmt_money, getdate
|
||||||
from pypika import Order
|
from pypika import Order
|
||||||
|
|
||||||
import erpnext
|
import erpnext
|
||||||
@@ -96,11 +96,8 @@ class BankClearance(Document):
|
|||||||
|
|
||||||
if d.cheque_date and getdate(d.clearance_date) < getdate(d.cheque_date):
|
if d.cheque_date and getdate(d.clearance_date) < getdate(d.cheque_date):
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Row #{0}: For {1} Clearance date {2} cannot be before Cheque Date {3}").format(
|
_("Row #{0}: Clearance date {1} cannot be before Cheque Date {2}").format(
|
||||||
d.idx,
|
d.idx, d.clearance_date, d.cheque_date
|
||||||
get_link_to_form(d.payment_document, d.payment_entry),
|
|
||||||
d.clearance_date,
|
|
||||||
d.cheque_date,
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -108,18 +105,8 @@ class BankClearance(Document):
|
|||||||
if not d.clearance_date:
|
if not d.clearance_date:
|
||||||
d.clearance_date = None
|
d.clearance_date = None
|
||||||
|
|
||||||
if d.payment_document == "Sales Invoice":
|
payment_entry = frappe.get_doc(d.payment_document, d.payment_entry)
|
||||||
frappe.db.set_value(
|
payment_entry.db_set("clearance_date", d.clearance_date)
|
||||||
"Sales Invoice Payment",
|
|
||||||
{"parent": d.payment_entry, "account": self.get("account"), "amount": [">", 0]},
|
|
||||||
"clearance_date",
|
|
||||||
d.clearance_date,
|
|
||||||
)
|
|
||||||
|
|
||||||
else:
|
|
||||||
frappe.db.set_value(
|
|
||||||
d.payment_document, d.payment_entry, "clearance_date", d.clearance_date
|
|
||||||
)
|
|
||||||
|
|
||||||
clearance_date_updated = True
|
clearance_date_updated = True
|
||||||
|
|
||||||
@@ -140,7 +127,7 @@ def get_payment_entries_for_bank_clearance(
|
|||||||
condition = "and (clearance_date IS NULL or clearance_date='0000-00-00')"
|
condition = "and (clearance_date IS NULL or clearance_date='0000-00-00')"
|
||||||
|
|
||||||
journal_entries = frappe.db.sql(
|
journal_entries = frappe.db.sql(
|
||||||
f"""
|
"""
|
||||||
select
|
select
|
||||||
"Journal Entry" as payment_document, t1.name as payment_entry,
|
"Journal Entry" as payment_document, t1.name as payment_entry,
|
||||||
t1.cheque_no as cheque_number, t1.cheque_date,
|
t1.cheque_no as cheque_number, t1.cheque_date,
|
||||||
@@ -154,7 +141,9 @@ def get_payment_entries_for_bank_clearance(
|
|||||||
and ifnull(t1.is_opening, 'No') = 'No' {condition}
|
and ifnull(t1.is_opening, 'No') = 'No' {condition}
|
||||||
group by t2.account, t1.name
|
group by t2.account, t1.name
|
||||||
order by t1.posting_date ASC, t1.name DESC
|
order by t1.posting_date ASC, t1.name DESC
|
||||||
""",
|
""".format(
|
||||||
|
condition=condition
|
||||||
|
),
|
||||||
{"account": account, "from": from_date, "to": to_date},
|
{"account": account, "from": from_date, "to": to_date},
|
||||||
as_dict=1,
|
as_dict=1,
|
||||||
)
|
)
|
||||||
@@ -163,12 +152,12 @@ def get_payment_entries_for_bank_clearance(
|
|||||||
condition += "and bank_account = %(bank_account)s"
|
condition += "and bank_account = %(bank_account)s"
|
||||||
|
|
||||||
payment_entries = frappe.db.sql(
|
payment_entries = frappe.db.sql(
|
||||||
f"""
|
"""
|
||||||
select
|
select
|
||||||
"Payment Entry" as payment_document, name as payment_entry,
|
"Payment Entry" as payment_document, name as payment_entry,
|
||||||
reference_no as cheque_number, reference_date as cheque_date,
|
reference_no as cheque_number, reference_date as cheque_date,
|
||||||
if(paid_from=%(account)s, paid_amount + total_taxes_and_charges, 0) as credit,
|
if(paid_from=%(account)s, paid_amount + total_taxes_and_charges, 0) as credit,
|
||||||
if(paid_from=%(account)s, 0, received_amount + total_taxes_and_charges) as debit,
|
if(paid_from=%(account)s, 0, received_amount) as debit,
|
||||||
posting_date, ifnull(party,if(paid_from=%(account)s,paid_to,paid_from)) as against_account, clearance_date,
|
posting_date, ifnull(party,if(paid_from=%(account)s,paid_to,paid_from)) as against_account, clearance_date,
|
||||||
if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency
|
if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency
|
||||||
from `tabPayment Entry`
|
from `tabPayment Entry`
|
||||||
@@ -178,7 +167,9 @@ def get_payment_entries_for_bank_clearance(
|
|||||||
{condition}
|
{condition}
|
||||||
order by
|
order by
|
||||||
posting_date ASC, name DESC
|
posting_date ASC, name DESC
|
||||||
""",
|
""".format(
|
||||||
|
condition=condition
|
||||||
|
),
|
||||||
{
|
{
|
||||||
"account": account,
|
"account": account,
|
||||||
"from": from_date,
|
"from": from_date,
|
||||||
@@ -248,7 +239,10 @@ def get_payment_entries_for_bank_clearance(
|
|||||||
).run(as_dict=True)
|
).run(as_dict=True)
|
||||||
|
|
||||||
entries = (
|
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
|
return entries
|
||||||
|
|||||||
@@ -6,29 +6,16 @@ import unittest
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import add_months, getdate
|
from frappe.utils import add_months, getdate
|
||||||
|
|
||||||
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
|
|
||||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
|
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
|
||||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
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.stock.doctype.item.test_item import create_item
|
|
||||||
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
|
||||||
from erpnext.tests.utils import if_lending_app_installed, if_lending_app_not_installed
|
from erpnext.tests.utils import if_lending_app_installed, if_lending_app_not_installed
|
||||||
|
|
||||||
|
|
||||||
class TestBankClearance(unittest.TestCase):
|
class TestBankClearance(unittest.TestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
create_warehouse(
|
|
||||||
warehouse_name="_Test Warehouse",
|
|
||||||
properties={"parent_warehouse": "All Warehouses - _TC"},
|
|
||||||
company="_Test Company",
|
|
||||||
)
|
|
||||||
create_item("_Test Item")
|
|
||||||
create_cost_center(cost_center_name="_Test Cost Center", company="_Test Company")
|
|
||||||
|
|
||||||
clear_payment_entries()
|
clear_payment_entries()
|
||||||
clear_loan_transactions()
|
clear_loan_transactions()
|
||||||
clear_pos_sales_invoices()
|
|
||||||
make_bank_account()
|
make_bank_account()
|
||||||
add_transactions()
|
add_transactions()
|
||||||
|
|
||||||
@@ -81,7 +68,9 @@ class TestBankClearance(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
loan.submit()
|
loan.submit()
|
||||||
make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=getdate())
|
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.save()
|
||||||
repayment_entry.submit()
|
repayment_entry.submit()
|
||||||
|
|
||||||
@@ -96,41 +85,11 @@ class TestBankClearance(unittest.TestCase):
|
|||||||
bank_clearance.get_payment_entries()
|
bank_clearance.get_payment_entries()
|
||||||
self.assertEqual(len(bank_clearance.payment_entries), 3)
|
self.assertEqual(len(bank_clearance.payment_entries), 3)
|
||||||
|
|
||||||
def test_update_clearance_date_on_si(self):
|
|
||||||
sales_invoice = make_pos_sales_invoice()
|
|
||||||
|
|
||||||
date = getdate()
|
|
||||||
bank_clearance = frappe.get_doc("Bank Clearance")
|
|
||||||
bank_clearance.account = "_Test Bank Clearance - _TC"
|
|
||||||
bank_clearance.from_date = add_months(date, -1)
|
|
||||||
bank_clearance.to_date = date
|
|
||||||
bank_clearance.include_pos_transactions = 1
|
|
||||||
bank_clearance.get_payment_entries()
|
|
||||||
|
|
||||||
self.assertNotEqual(len(bank_clearance.payment_entries), 0)
|
|
||||||
for payment in bank_clearance.payment_entries:
|
|
||||||
if payment.payment_entry == sales_invoice.name:
|
|
||||||
payment.clearance_date = date
|
|
||||||
|
|
||||||
bank_clearance.update_clearance_date()
|
|
||||||
|
|
||||||
si_clearance_date = frappe.db.get_value(
|
|
||||||
"Sales Invoice Payment",
|
|
||||||
{"parent": sales_invoice.name, "account": bank_clearance.account},
|
|
||||||
"clearance_date",
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(si_clearance_date, date)
|
|
||||||
|
|
||||||
|
|
||||||
def clear_payment_entries():
|
def clear_payment_entries():
|
||||||
frappe.db.delete("Payment Entry")
|
frappe.db.delete("Payment Entry")
|
||||||
|
|
||||||
|
|
||||||
def clear_pos_sales_invoices():
|
|
||||||
frappe.db.delete("Sales Invoice", {"is_pos": 1})
|
|
||||||
|
|
||||||
|
|
||||||
@if_lending_app_installed
|
@if_lending_app_installed
|
||||||
def clear_loan_transactions():
|
def clear_loan_transactions():
|
||||||
for dt in [
|
for dt in [
|
||||||
@@ -158,45 +117,9 @@ def add_transactions():
|
|||||||
|
|
||||||
|
|
||||||
def make_payment_entry():
|
def make_payment_entry():
|
||||||
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
|
pi = make_purchase_invoice(supplier="_Test Supplier", qty=1, rate=690)
|
||||||
|
|
||||||
supplier = create_supplier(supplier_name="_Test Supplier")
|
|
||||||
pi = make_purchase_invoice(
|
|
||||||
supplier=supplier,
|
|
||||||
supplier_warehouse="_Test Warehouse - _TC",
|
|
||||||
expense_account="Cost of Goods Sold - _TC",
|
|
||||||
uom="Nos",
|
|
||||||
qty=1,
|
|
||||||
rate=690,
|
|
||||||
)
|
|
||||||
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank Clearance - _TC")
|
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank Clearance - _TC")
|
||||||
pe.reference_no = "Conrad Oct 18"
|
pe.reference_no = "Conrad Oct 18"
|
||||||
pe.reference_date = "2018-10-24"
|
pe.reference_date = "2018-10-24"
|
||||||
pe.insert()
|
pe.insert()
|
||||||
pe.submit()
|
pe.submit()
|
||||||
|
|
||||||
|
|
||||||
def make_pos_sales_invoice():
|
|
||||||
from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import (
|
|
||||||
make_customer,
|
|
||||||
)
|
|
||||||
|
|
||||||
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": "_Test Bank Clearance - _TC"}
|
|
||||||
)
|
|
||||||
mode_of_payment.save()
|
|
||||||
|
|
||||||
customer = make_customer(customer="_Test Customer")
|
|
||||||
|
|
||||||
si = create_sales_invoice(customer=customer, item="_Test Item", is_pos=1, qty=1, rate=1000, do_not_save=1)
|
|
||||||
si.set("payments", [])
|
|
||||||
si.append(
|
|
||||||
"payments", {"mode_of_payment": "Cash", "account": "_Test Bank Clearance - _TC", "amount": 1000}
|
|
||||||
)
|
|
||||||
si.insert()
|
|
||||||
si.submit()
|
|
||||||
|
|
||||||
return si
|
|
||||||
|
|||||||
@@ -48,11 +48,11 @@ class BankGuarantee(Document):
|
|||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
if not self.bank_guarantee_number:
|
if not self.bank_guarantee_number:
|
||||||
frappe.throw(_("Enter the Bank Guarantee Number before submittting."))
|
frappe.throw(_("Enter the Bank Guarantee Number before submitting."))
|
||||||
if not self.name_of_beneficiary:
|
if not self.name_of_beneficiary:
|
||||||
frappe.throw(_("Enter the name of the Beneficiary before submittting."))
|
frappe.throw(_("Enter the name of the Beneficiary before submitting."))
|
||||||
if not self.bank:
|
if not self.bank:
|
||||||
frappe.throw(_("Enter the name of the bank or lending institution before submittting."))
|
frappe.throw(_("Enter the name of the bank or lending institution before submitting."))
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
|
|||||||
@@ -59,10 +59,6 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
|||||||
);
|
);
|
||||||
|
|
||||||
frm.add_custom_button(__("Auto Reconcile"), function () {
|
frm.add_custom_button(__("Auto Reconcile"), function () {
|
||||||
if (!frm.doc.bank_account) {
|
|
||||||
frappe.msgprint(__("Please select Bank Account"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.auto_reconcile_vouchers",
|
method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.auto_reconcile_vouchers",
|
||||||
args: {
|
args: {
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "company",
|
"fieldname": "company",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"ignore_user_permissions": 1,
|
|
||||||
"label": "Company",
|
"label": "Company",
|
||||||
"options": "Company"
|
"options": "Company"
|
||||||
},
|
},
|
||||||
@@ -119,7 +118,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-04-28 14:40:50.910884",
|
"modified": "2023-03-07 11:02:24.535714",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Bank Reconciliation Tool",
|
"name": "Bank Reconciliation Tool",
|
||||||
@@ -140,4 +139,4 @@
|
|||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"states": []
|
"states": []
|
||||||
}
|
}
|
||||||
@@ -9,6 +9,7 @@ from frappe import _
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.query_builder.custom import ConstantColumn
|
from frappe.query_builder.custom import ConstantColumn
|
||||||
from frappe.utils import cint, flt
|
from frappe.utils import cint, flt
|
||||||
|
from pypika.terms import Parameter
|
||||||
|
|
||||||
from erpnext import get_default_cost_center
|
from erpnext import get_default_cost_center
|
||||||
from erpnext.accounts.doctype.bank_transaction.bank_transaction import get_total_allocated_amount
|
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):
|
def get_account_balance(bank_account, till_date):
|
||||||
# returns account balance till the specified date
|
# returns account balance till the specified date
|
||||||
account = frappe.db.get_value("Bank Account", bank_account, "account")
|
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)
|
data = get_entries(filters)
|
||||||
|
|
||||||
balance_as_per_system = get_balance_on(filters["account"], filters["report_date"])
|
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)
|
amounts_not_reflected_in_system = get_amounts_not_reflected_in_system(filters)
|
||||||
|
|
||||||
bank_bal = (
|
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
|
return bank_bal
|
||||||
@@ -495,26 +501,14 @@ def check_matching(
|
|||||||
bank_account,
|
bank_account,
|
||||||
company,
|
company,
|
||||||
transaction,
|
transaction,
|
||||||
document_types=None,
|
document_types,
|
||||||
from_date=None,
|
from_date,
|
||||||
to_date=None,
|
to_date,
|
||||||
filter_by_reference_date=None,
|
filter_by_reference_date,
|
||||||
from_reference_date=None,
|
from_reference_date,
|
||||||
to_reference_date=None,
|
to_reference_date,
|
||||||
):
|
):
|
||||||
exact_match = True if "exact_match" in document_types else False
|
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(
|
queries = get_queries(
|
||||||
bank_account,
|
bank_account,
|
||||||
company,
|
company,
|
||||||
@@ -526,28 +520,37 @@ def check_matching(
|
|||||||
from_reference_date,
|
from_reference_date,
|
||||||
to_reference_date,
|
to_reference_date,
|
||||||
exact_match,
|
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 = []
|
matching_vouchers = []
|
||||||
for query in queries:
|
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(
|
def get_queries(
|
||||||
bank_account,
|
bank_account,
|
||||||
company,
|
company,
|
||||||
transaction,
|
transaction,
|
||||||
document_types=None,
|
document_types,
|
||||||
from_date=None,
|
from_date,
|
||||||
to_date=None,
|
to_date,
|
||||||
filter_by_reference_date=None,
|
filter_by_reference_date,
|
||||||
from_reference_date=None,
|
from_reference_date,
|
||||||
to_reference_date=None,
|
to_reference_date,
|
||||||
exact_match=None,
|
exact_match,
|
||||||
common_filters=None,
|
|
||||||
):
|
):
|
||||||
# get queries to get matching vouchers
|
# get queries to get matching vouchers
|
||||||
account_from_to = "paid_to" if transaction.deposit > 0.0 else "paid_from"
|
account_from_to = "paid_to" if transaction.deposit > 0.0 else "paid_from"
|
||||||
@@ -568,7 +571,6 @@ def get_queries(
|
|||||||
filter_by_reference_date,
|
filter_by_reference_date,
|
||||||
from_reference_date,
|
from_reference_date,
|
||||||
to_reference_date,
|
to_reference_date,
|
||||||
common_filters,
|
|
||||||
)
|
)
|
||||||
or []
|
or []
|
||||||
)
|
)
|
||||||
@@ -580,15 +582,14 @@ def get_matching_queries(
|
|||||||
bank_account,
|
bank_account,
|
||||||
company,
|
company,
|
||||||
transaction,
|
transaction,
|
||||||
document_types=None,
|
document_types,
|
||||||
exact_match=None,
|
exact_match,
|
||||||
account_from_to=None,
|
account_from_to,
|
||||||
from_date=None,
|
from_date,
|
||||||
to_date=None,
|
to_date,
|
||||||
filter_by_reference_date=None,
|
filter_by_reference_date,
|
||||||
from_reference_date=None,
|
from_reference_date,
|
||||||
to_reference_date=None,
|
to_reference_date,
|
||||||
common_filters=None,
|
|
||||||
):
|
):
|
||||||
queries = []
|
queries = []
|
||||||
currency = get_account_currency(bank_account)
|
currency = get_account_currency(bank_account)
|
||||||
@@ -603,7 +604,6 @@ def get_matching_queries(
|
|||||||
filter_by_reference_date,
|
filter_by_reference_date,
|
||||||
from_reference_date,
|
from_reference_date,
|
||||||
to_reference_date,
|
to_reference_date,
|
||||||
common_filters,
|
|
||||||
)
|
)
|
||||||
queries.append(query)
|
queries.append(query)
|
||||||
|
|
||||||
@@ -616,17 +616,16 @@ def get_matching_queries(
|
|||||||
filter_by_reference_date,
|
filter_by_reference_date,
|
||||||
from_reference_date,
|
from_reference_date,
|
||||||
to_reference_date,
|
to_reference_date,
|
||||||
common_filters,
|
|
||||||
)
|
)
|
||||||
queries.append(query)
|
queries.append(query)
|
||||||
|
|
||||||
if transaction.deposit > 0.0 and "sales_invoice" in document_types:
|
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)
|
queries.append(query)
|
||||||
|
|
||||||
if transaction.withdrawal > 0.0:
|
if transaction.withdrawal > 0.0:
|
||||||
if "purchase_invoice" in document_types:
|
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)
|
queries.append(query)
|
||||||
|
|
||||||
if "bank_transaction" in document_types:
|
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_rank = frappe.qb.terms.Case().when(amount_equality, 1).else_(0)
|
||||||
amount_condition = amount_equality if exact_match else getattr(bt, field) > 0.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 = (
|
unallocated_rank = (
|
||||||
frappe.qb.terms.Case().when(bt.unallocated_amount == transaction.unallocated_amount, 1).else_(0)
|
frappe.qb.terms.Case().when(bt.unallocated_amount == transaction.unallocated_amount, 1).else_(0)
|
||||||
)
|
)
|
||||||
|
|
||||||
party_condition = (
|
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)
|
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(amount_condition)
|
||||||
.where(bt.docstatus == 1)
|
.where(bt.docstatus == 1)
|
||||||
)
|
)
|
||||||
return query
|
return str(query)
|
||||||
|
|
||||||
|
|
||||||
def get_pe_matching_query(
|
def get_pe_matching_query(
|
||||||
@@ -689,7 +692,6 @@ def get_pe_matching_query(
|
|||||||
filter_by_reference_date,
|
filter_by_reference_date,
|
||||||
from_reference_date,
|
from_reference_date,
|
||||||
to_reference_date,
|
to_reference_date,
|
||||||
common_filters,
|
|
||||||
):
|
):
|
||||||
# get matching payment entries query
|
# get matching payment entries query
|
||||||
to_from = "to" if transaction.deposit > 0.0 else "from"
|
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
|
amount_condition = amount_equality if exact_match else pe.paid_amount > 0.0
|
||||||
|
|
||||||
party_condition = (
|
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)
|
party_rank = frappe.qb.terms.Case().when(party_condition, 1).else_(0)
|
||||||
|
|
||||||
@@ -719,7 +723,7 @@ def get_pe_matching_query(
|
|||||||
(ref_rank + amount_rank + party_rank + 1).as_("rank"),
|
(ref_rank + amount_rank + party_rank + 1).as_("rank"),
|
||||||
ConstantColumn("Payment Entry").as_("doctype"),
|
ConstantColumn("Payment Entry").as_("doctype"),
|
||||||
pe.name,
|
pe.name,
|
||||||
pe.paid_amount_after_tax.as_("paid_amount"),
|
pe.paid_amount,
|
||||||
pe.reference_no,
|
pe.reference_no,
|
||||||
pe.reference_date,
|
pe.reference_date,
|
||||||
pe.party,
|
pe.party,
|
||||||
@@ -730,16 +734,16 @@ def get_pe_matching_query(
|
|||||||
.where(pe.docstatus == 1)
|
.where(pe.docstatus == 1)
|
||||||
.where(pe.payment_type.isin([payment_type, "Internal Transfer"]))
|
.where(pe.payment_type.isin([payment_type, "Internal Transfer"]))
|
||||||
.where(pe.clearance_date.isnull())
|
.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(amount_condition)
|
||||||
.where(filter_by_date)
|
.where(filter_by_date)
|
||||||
.orderby(pe.reference_date if cint(filter_by_reference_date) else pe.posting_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)
|
query = query.where(ref_condition)
|
||||||
|
|
||||||
return query
|
return str(query)
|
||||||
|
|
||||||
|
|
||||||
def get_je_matching_query(
|
def get_je_matching_query(
|
||||||
@@ -750,7 +754,6 @@ def get_je_matching_query(
|
|||||||
filter_by_reference_date,
|
filter_by_reference_date,
|
||||||
from_reference_date,
|
from_reference_date,
|
||||||
to_reference_date,
|
to_reference_date,
|
||||||
common_filters,
|
|
||||||
):
|
):
|
||||||
# get matching journal entry query
|
# get matching journal entry query
|
||||||
# We have mapping at the bank level
|
# We have mapping at the bank level
|
||||||
@@ -790,29 +793,29 @@ def get_je_matching_query(
|
|||||||
.where(je.docstatus == 1)
|
.where(je.docstatus == 1)
|
||||||
.where(je.voucher_type != "Opening Entry")
|
.where(je.voucher_type != "Opening Entry")
|
||||||
.where(je.clearance_date.isnull())
|
.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(amount_equality if exact_match else getattr(jea, amount_field) > 0.0)
|
||||||
.where(je.docstatus == 1)
|
.where(je.docstatus == 1)
|
||||||
.where(filter_by_date)
|
.where(filter_by_date)
|
||||||
.orderby(je.cheque_date if cint(filter_by_reference_date) else je.posting_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)
|
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
|
# get matching sales invoice query
|
||||||
si = frappe.qb.DocType("Sales Invoice")
|
si = frappe.qb.DocType("Sales Invoice")
|
||||||
sip = frappe.qb.DocType("Sales Invoice Payment")
|
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_rank = frappe.qb.terms.Case().when(amount_equality, 1).else_(0)
|
||||||
amount_condition = amount_equality if exact_match else sip.amount > 0.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)
|
party_rank = frappe.qb.terms.Case().when(party_condition, 1).else_(0)
|
||||||
|
|
||||||
query = (
|
query = (
|
||||||
@@ -833,23 +836,23 @@ def get_si_matching_query(exact_match, currency, common_filters):
|
|||||||
)
|
)
|
||||||
.where(si.docstatus == 1)
|
.where(si.docstatus == 1)
|
||||||
.where(sip.clearance_date.isnull())
|
.where(sip.clearance_date.isnull())
|
||||||
.where(sip.account == common_filters.bank_account)
|
.where(sip.account == Parameter("%(bank_account)s"))
|
||||||
.where(amount_condition)
|
.where(amount_condition)
|
||||||
.where(si.currency == currency)
|
.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)
|
# get matching purchase invoice query when they are also used as payment entries (is_paid)
|
||||||
purchase_invoice = frappe.qb.DocType("Purchase Invoice")
|
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_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
|
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)
|
party_rank = frappe.qb.terms.Case().when(party_condition, 1).else_(0)
|
||||||
|
|
||||||
query = (
|
query = (
|
||||||
@@ -869,9 +872,9 @@ def get_pi_matching_query(exact_match, currency, common_filters):
|
|||||||
.where(purchase_invoice.docstatus == 1)
|
.where(purchase_invoice.docstatus == 1)
|
||||||
.where(purchase_invoice.is_paid == 1)
|
.where(purchase_invoice.is_paid == 1)
|
||||||
.where(purchase_invoice.clearance_date.isnull())
|
.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(amount_condition)
|
||||||
.where(purchase_invoice.currency == currency)
|
.where(purchase_invoice.currency == currency)
|
||||||
)
|
)
|
||||||
|
|
||||||
return query
|
return str(query)
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# See license.txt
|
# See license.txt
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import qb
|
from frappe import qb
|
||||||
from frappe.tests.utils import FrappeTestCase
|
from frappe.tests.utils import FrappeTestCase, change_settings
|
||||||
from frappe.utils import add_days, today
|
from frappe.utils import add_days, flt, getdate, today
|
||||||
|
|
||||||
from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import (
|
from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import (
|
||||||
auto_reconcile_vouchers,
|
auto_reconcile_vouchers,
|
||||||
@@ -21,7 +22,7 @@ class TestBankReconciliationTool(AccountsTestMixin, FrappeTestCase):
|
|||||||
self.create_customer()
|
self.create_customer()
|
||||||
self.clear_old_entries()
|
self.clear_old_entries()
|
||||||
bank_dt = qb.DocType("Bank")
|
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()
|
self.create_bank_account()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
|
|||||||
@@ -2,6 +2,16 @@
|
|||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on("Bank Statement Import", {
|
frappe.ui.form.on("Bank Statement Import", {
|
||||||
|
onload(frm) {
|
||||||
|
frm.set_query("bank_account", function (doc) {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
company: doc.company,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
setup(frm) {
|
setup(frm) {
|
||||||
frappe.realtime.on("data_import_refresh", ({ data_import }) => {
|
frappe.realtime.on("data_import_refresh", ({ data_import }) => {
|
||||||
frm.import_in_progress = false;
|
frm.import_in_progress = false;
|
||||||
@@ -120,66 +130,52 @@ frappe.ui.form.on("Bank Statement Import", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
show_import_status(frm) {
|
show_import_status(frm) {
|
||||||
if (frm.doc.status == "Pending") return;
|
let import_log = JSON.parse(frm.doc.statement_import_log || "[]");
|
||||||
|
let successful_records = import_log.filter((log) => log.success);
|
||||||
|
let failed_records = import_log.filter((log) => !log.success);
|
||||||
|
if (successful_records.length === 0) return;
|
||||||
|
|
||||||
frappe.call({
|
let message;
|
||||||
method: "erpnext.accounts.doctype.bank_statement_import.bank_statement_import.get_import_status",
|
if (failed_records.length === 0) {
|
||||||
args: {
|
let message_args = [successful_records.length];
|
||||||
docname: frm.doc.name,
|
if (frm.doc.import_type === "Insert New Records") {
|
||||||
},
|
message =
|
||||||
callback: function (r) {
|
successful_records.length > 1
|
||||||
let successful_records = cint(r.message.success);
|
? __("Successfully imported {0} records.", message_args)
|
||||||
let failed_records = cint(r.message.failed);
|
: __("Successfully imported {0} record.", message_args);
|
||||||
let total_records = cint(r.message.total_records);
|
} else {
|
||||||
|
message =
|
||||||
if (!total_records) {
|
successful_records.length > 1
|
||||||
return;
|
? __("Successfully updated {0} records.", message_args)
|
||||||
}
|
: __("Successfully updated {0} record.", message_args);
|
||||||
|
}
|
||||||
let message;
|
} else {
|
||||||
if (failed_records === 0) {
|
let message_args = [successful_records.length, import_log.length];
|
||||||
let message_args = [successful_records];
|
if (frm.doc.import_type === "Insert New Records") {
|
||||||
if (frm.doc.import_type === "Insert New Records") {
|
message =
|
||||||
message =
|
successful_records.length > 1
|
||||||
successful_records > 1
|
? __(
|
||||||
? __("Successfully imported {0} records.", message_args)
|
"Successfully imported {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.",
|
||||||
: __("Successfully imported {0} record.", message_args);
|
message_args
|
||||||
} else {
|
)
|
||||||
message =
|
: __(
|
||||||
successful_records > 1
|
"Successfully imported {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.",
|
||||||
? __("Successfully updated {0} records.", message_args)
|
message_args
|
||||||
: __("Successfully updated {0} record.", message_args);
|
);
|
||||||
}
|
} else {
|
||||||
} else {
|
message =
|
||||||
let message_args = [successful_records, total_records];
|
successful_records.length > 1
|
||||||
if (frm.doc.import_type === "Insert New Records") {
|
? __(
|
||||||
message =
|
"Successfully updated {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.",
|
||||||
successful_records > 1
|
message_args
|
||||||
? __(
|
)
|
||||||
"Successfully imported {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 imported {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.",
|
}
|
||||||
message_args
|
}
|
||||||
);
|
frm.dashboard.set_headline(message);
|
||||||
} else {
|
|
||||||
message =
|
|
||||||
successful_records > 1
|
|
||||||
? __(
|
|
||||||
"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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
frm.dashboard.set_headline(message);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
show_report_error_button(frm) {
|
show_report_error_button(frm) {
|
||||||
@@ -301,7 +297,7 @@ frappe.ui.form.on("Bank Statement Import", {
|
|||||||
// method: 'frappe.core.doctype.data_import.data_import.get_preview_from_template',
|
// method: 'frappe.core.doctype.data_import.data_import.get_preview_from_template',
|
||||||
|
|
||||||
show_import_preview(frm, preview_data) {
|
show_import_preview(frm, preview_data) {
|
||||||
let import_log = preview_data.import_log;
|
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.preview_data = preview_data;
|
||||||
@@ -340,15 +336,6 @@ frappe.ui.form.on("Bank Statement Import", {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
export_import_log(frm) {
|
|
||||||
open_url_post(
|
|
||||||
"/api/method/erpnext.accounts.doctype.bank_statement_import.bank_statement_import.download_import_log",
|
|
||||||
{
|
|
||||||
data_import_name: frm.doc.name,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
show_import_warnings(frm, preview_data) {
|
show_import_warnings(frm, preview_data) {
|
||||||
let columns = preview_data.columns;
|
let columns = preview_data.columns;
|
||||||
let warnings = JSON.parse(frm.doc.template_warnings || "[]");
|
let warnings = JSON.parse(frm.doc.template_warnings || "[]");
|
||||||
@@ -424,50 +411,49 @@ frappe.ui.form.on("Bank Statement Import", {
|
|||||||
frm.trigger("show_import_log");
|
frm.trigger("show_import_log");
|
||||||
},
|
},
|
||||||
|
|
||||||
render_import_log(frm) {
|
show_import_log(frm) {
|
||||||
frappe.call({
|
let import_log = JSON.parse(frm.doc.statement_import_log || "[]");
|
||||||
method: "erpnext.accounts.doctype.bank_statement_import.bank_statement_import.get_import_logs",
|
let logs = import_log;
|
||||||
args: {
|
frm.toggle_display("import_log", false);
|
||||||
docname: frm.doc.name,
|
frm.toggle_display("import_log_section", logs.length > 0);
|
||||||
},
|
|
||||||
callback: function (r) {
|
|
||||||
let logs = r.message;
|
|
||||||
|
|
||||||
if (logs.length === 0) return;
|
if (logs.length === 0) {
|
||||||
|
frm.get_field("import_log_preview").$wrapper.empty();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
frm.toggle_display("import_log_section", true);
|
let rows = logs
|
||||||
|
.map((log) => {
|
||||||
let rows = logs
|
let html = "";
|
||||||
.map((log) => {
|
if (log.success) {
|
||||||
let html = "";
|
if (frm.doc.import_type === "Insert New Records") {
|
||||||
if (log.success) {
|
html = __("Successfully imported {0}", [
|
||||||
if (frm.doc.import_type === "Insert New Records") {
|
`<span class="underline">${frappe.utils.get_form_link(
|
||||||
html = __("Successfully imported {0}", [
|
frm.doc.reference_doctype,
|
||||||
`<span class="underline">${frappe.utils.get_form_link(
|
log.docname,
|
||||||
frm.doc.reference_doctype,
|
true
|
||||||
log.docname,
|
)}<span>`,
|
||||||
true
|
]);
|
||||||
)}<span>`,
|
} else {
|
||||||
]);
|
html = __("Successfully updated {0}", [
|
||||||
} else {
|
`<span class="underline">${frappe.utils.get_form_link(
|
||||||
html = __("Successfully updated {0}", [
|
frm.doc.reference_doctype,
|
||||||
`<span class="underline">${frappe.utils.get_form_link(
|
log.docname,
|
||||||
frm.doc.reference_doctype,
|
true
|
||||||
log.docname,
|
)}<span>`,
|
||||||
true
|
]);
|
||||||
)}<span>`,
|
}
|
||||||
]);
|
} else {
|
||||||
}
|
let messages = log.messages
|
||||||
} else {
|
.map(JSON.parse)
|
||||||
let messages = JSON.parse(log.messages || "[]")
|
.map((m) => {
|
||||||
.map((m) => {
|
let title = m.title ? `<strong>${m.title}</strong>` : "";
|
||||||
let title = m.title ? `<strong>${m.title}</strong>` : "";
|
let message = m.message ? `<div>${m.message}</div>` : "";
|
||||||
let message = m.message ? `<div>${m.message}</div>` : "";
|
return title + message;
|
||||||
return title + message;
|
})
|
||||||
})
|
.join("");
|
||||||
.join("");
|
let id = frappe.dom.get_unique_id();
|
||||||
let id = frappe.dom.get_unique_id();
|
html = `${messages}
|
||||||
html = `${messages}
|
|
||||||
<button class="btn btn-default btn-xs" type="button" data-toggle="collapse" data-target="#${id}" aria-expanded="false" aria-controls="${id}" style="margin-top: 15px;">
|
<button class="btn btn-default btn-xs" type="button" data-toggle="collapse" data-target="#${id}" aria-expanded="false" aria-controls="${id}" style="margin-top: 15px;">
|
||||||
${__("Show Traceback")}
|
${__("Show Traceback")}
|
||||||
</button>
|
</button>
|
||||||
@@ -476,16 +462,16 @@ frappe.ui.form.on("Bank Statement Import", {
|
|||||||
<pre>${log.exception}</pre>
|
<pre>${log.exception}</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
let indicator_color = log.success ? "green" : "red";
|
let indicator_color = log.success ? "green" : "red";
|
||||||
let title = log.success ? __("Success") : __("Failure");
|
let title = log.success ? __("Success") : __("Failure");
|
||||||
|
|
||||||
if (frm.doc.show_failed_logs && log.success) {
|
if (frm.doc.show_failed_logs && log.success) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<tr>
|
return `<tr>
|
||||||
<td>${JSON.parse(log.row_indexes).join(", ")}</td>
|
<td>${log.row_indexes.join(", ")}</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="indicator ${indicator_color}">${title}</div>
|
<div class="indicator ${indicator_color}">${title}</div>
|
||||||
</td>
|
</td>
|
||||||
@@ -493,16 +479,16 @@ frappe.ui.form.on("Bank Statement Import", {
|
|||||||
${html}
|
${html}
|
||||||
</td>
|
</td>
|
||||||
</tr>`;
|
</tr>`;
|
||||||
})
|
})
|
||||||
.join("");
|
.join("");
|
||||||
|
|
||||||
if (!rows && frm.doc.show_failed_logs) {
|
if (!rows && frm.doc.show_failed_logs) {
|
||||||
rows = `<tr><td class="text-center text-muted" colspan=3>
|
rows = `<tr><td class="text-center text-muted" colspan=3>
|
||||||
${__("No failed logs")}
|
${__("No failed logs")}
|
||||||
</td></tr>`;
|
</td></tr>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
frm.get_field("import_log_preview").$wrapper.html(`
|
frm.get_field("import_log_preview").$wrapper.html(`
|
||||||
<table class="table table-bordered">
|
<table class="table table-bordered">
|
||||||
<tr class="text-muted">
|
<tr class="text-muted">
|
||||||
<th width="10%">${__("Row Number")}</th>
|
<th width="10%">${__("Row Number")}</th>
|
||||||
@@ -512,34 +498,5 @@ frappe.ui.form.on("Bank Statement Import", {
|
|||||||
${rows}
|
${rows}
|
||||||
</table>
|
</table>
|
||||||
`);
|
`);
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
show_import_log(frm) {
|
|
||||||
frm.toggle_display("import_log_section", false);
|
|
||||||
|
|
||||||
if (frm.is_new() || frm.import_in_progress) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
frappe.call({
|
|
||||||
method: "frappe.client.get_count",
|
|
||||||
args: {
|
|
||||||
doctype: "Data Import Log",
|
|
||||||
filters: {
|
|
||||||
data_import: frm.doc.name,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
callback: function (r) {
|
|
||||||
let count = r.message;
|
|
||||||
if (count < 5000) {
|
|
||||||
frm.trigger("render_import_log");
|
|
||||||
} else {
|
|
||||||
frm.toggle_display("import_log_section", false);
|
|
||||||
frm.add_custom_button(__("Export Import Log"), () => frm.trigger("export_import_log"));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -11,8 +11,6 @@
|
|||||||
"bank_account",
|
"bank_account",
|
||||||
"bank",
|
"bank",
|
||||||
"column_break_4",
|
"column_break_4",
|
||||||
"custom_delimiters",
|
|
||||||
"delimiter_options",
|
|
||||||
"google_sheets_url",
|
"google_sheets_url",
|
||||||
"refresh_google_sheet",
|
"refresh_google_sheet",
|
||||||
"html_5",
|
"html_5",
|
||||||
@@ -26,6 +24,7 @@
|
|||||||
"section_import_preview",
|
"section_import_preview",
|
||||||
"import_preview",
|
"import_preview",
|
||||||
"import_log_section",
|
"import_log_section",
|
||||||
|
"statement_import_log",
|
||||||
"show_failed_logs",
|
"show_failed_logs",
|
||||||
"import_log_preview",
|
"import_log_preview",
|
||||||
"reference_doctype",
|
"reference_doctype",
|
||||||
@@ -195,23 +194,15 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"fieldname": "statement_import_log",
|
||||||
"fieldname": "custom_delimiters",
|
"fieldtype": "Code",
|
||||||
"fieldtype": "Check",
|
"label": "Statement Import Log",
|
||||||
"label": "Custom delimiters"
|
"options": "JSON"
|
||||||
},
|
|
||||||
{
|
|
||||||
"default": ",;\\t|",
|
|
||||||
"depends_on": "custom_delimiters",
|
|
||||||
"description": "If your CSV uses a different delimiter, add that character here, ensuring no spaces or additional characters are included.",
|
|
||||||
"fieldname": "delimiter_options",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"label": "Delimiter options"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hide_toolbar": 1,
|
"hide_toolbar": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-06-25 17:32:07.658250",
|
"modified": "2022-09-07 11:11:40.293317",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Bank Statement Import",
|
"name": "Bank Statement Import",
|
||||||
|
|||||||
@@ -31,14 +31,13 @@ class BankStatementImport(DataImport):
|
|||||||
bank: DF.Link | None
|
bank: DF.Link | None
|
||||||
bank_account: DF.Link
|
bank_account: DF.Link
|
||||||
company: DF.Link
|
company: DF.Link
|
||||||
custom_delimiters: DF.Check
|
|
||||||
delimiter_options: DF.Data | None
|
|
||||||
google_sheets_url: DF.Data | None
|
google_sheets_url: DF.Data | None
|
||||||
import_file: DF.Attach | None
|
import_file: DF.Attach | None
|
||||||
import_type: DF.Literal["", "Insert New Records", "Update Existing Records"]
|
import_type: DF.Literal["", "Insert New Records", "Update Existing Records"]
|
||||||
mute_emails: DF.Check
|
mute_emails: DF.Check
|
||||||
reference_doctype: DF.Link
|
reference_doctype: DF.Link
|
||||||
show_failed_logs: DF.Check
|
show_failed_logs: DF.Check
|
||||||
|
statement_import_log: DF.Code | None
|
||||||
status: DF.Literal["Pending", "Success", "Partial Success", "Error"]
|
status: DF.Literal["Pending", "Success", "Partial Success", "Error"]
|
||||||
submit_after_import: DF.Check
|
submit_after_import: DF.Check
|
||||||
template_options: DF.Code | None
|
template_options: DF.Code | None
|
||||||
@@ -46,7 +45,7 @@ class BankStatementImport(DataImport):
|
|||||||
# end: auto-generated types
|
# end: auto-generated types
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super(BankStatementImport, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
doc_before_save = self.get_doc_before_save()
|
doc_before_save = self.get_doc_before_save()
|
||||||
@@ -55,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.import_file != self.import_file)
|
||||||
or (doc_before_save and doc_before_save.google_sheets_url != self.google_sheets_url)
|
or (doc_before_save and doc_before_save.google_sheets_url != self.google_sheets_url)
|
||||||
):
|
):
|
||||||
|
|
||||||
template_options_dict = {}
|
template_options_dict = {}
|
||||||
column_to_field_map = {}
|
column_to_field_map = {}
|
||||||
bank = frappe.get_doc("Bank", self.bank)
|
bank = frappe.get_doc("Bank", self.bank)
|
||||||
@@ -69,6 +69,7 @@ class BankStatementImport(DataImport):
|
|||||||
self.validate_google_sheets_url()
|
self.validate_google_sheets_url()
|
||||||
|
|
||||||
def start_import(self):
|
def start_import(self):
|
||||||
|
|
||||||
preview = frappe.get_doc("Bank Statement Import", self.name).get_preview_from_template(
|
preview = frappe.get_doc("Bank Statement Import", self.name).get_preview_from_template(
|
||||||
self.import_file, self.google_sheets_url
|
self.import_file, self.google_sheets_url
|
||||||
)
|
)
|
||||||
@@ -79,7 +80,8 @@ class BankStatementImport(DataImport):
|
|||||||
from frappe.utils.background_jobs import is_job_enqueued
|
from frappe.utils.background_jobs import is_job_enqueued
|
||||||
from frappe.utils.scheduler import is_scheduler_inactive
|
from frappe.utils.scheduler import is_scheduler_inactive
|
||||||
|
|
||||||
if is_scheduler_inactive() and not frappe.flags.in_test:
|
run_now = frappe.flags.in_test or frappe.conf.developer_mode
|
||||||
|
if is_scheduler_inactive() and not run_now:
|
||||||
frappe.throw(_("Scheduler is inactive. Cannot import data."), title=_("Scheduler Inactive"))
|
frappe.throw(_("Scheduler is inactive. Cannot import data."), title=_("Scheduler Inactive"))
|
||||||
|
|
||||||
job_id = f"bank_statement_import::{self.name}"
|
job_id = f"bank_statement_import::{self.name}"
|
||||||
@@ -96,7 +98,7 @@ class BankStatementImport(DataImport):
|
|||||||
google_sheets_url=self.google_sheets_url,
|
google_sheets_url=self.google_sheets_url,
|
||||||
bank=self.bank,
|
bank=self.bank,
|
||||||
template_options=self.template_options,
|
template_options=self.template_options,
|
||||||
now=frappe.conf.developer_mode or frappe.flags.in_test,
|
now=run_now,
|
||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -121,15 +123,10 @@ def download_errored_template(data_import_name):
|
|||||||
data_import.export_errored_rows()
|
data_import.export_errored_rows()
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def download_import_log(data_import_name):
|
|
||||||
return frappe.get_doc("Bank Statement Import", data_import_name).download_import_log()
|
|
||||||
|
|
||||||
|
|
||||||
def parse_data_from_template(raw_data):
|
def parse_data_from_template(raw_data):
|
||||||
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):
|
if all(v in INVALID_VALUES for v in row):
|
||||||
# empty row
|
# empty row
|
||||||
continue
|
continue
|
||||||
@@ -139,7 +136,9 @@ def parse_data_from_template(raw_data):
|
|||||||
return 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"""
|
"""This method runs in background job"""
|
||||||
|
|
||||||
update_mapping_db(bank, template_options)
|
update_mapping_db(bank, template_options)
|
||||||
@@ -150,9 +149,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")
|
import_file = ImportFile("Bank Transaction", file=file, import_type="Insert New Records")
|
||||||
|
|
||||||
data = parse_data_from_template(import_file.raw_data)
|
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:
|
if import_file_path:
|
||||||
add_bank_account(data, bank_account)
|
add_bank_account(data, bank_account)
|
||||||
@@ -247,47 +243,6 @@ def write_xlsx(data, sheet_name, wb=None, column_widths=None, file_path=None):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def get_import_status(docname):
|
|
||||||
import_status = {}
|
|
||||||
|
|
||||||
data_import = frappe.get_doc("Bank Statement Import", docname)
|
|
||||||
import_status["status"] = data_import.status
|
|
||||||
|
|
||||||
logs = frappe.get_all(
|
|
||||||
"Data Import Log",
|
|
||||||
fields=["count(*) as count", "success"],
|
|
||||||
filters={"data_import": docname},
|
|
||||||
group_by="success",
|
|
||||||
)
|
|
||||||
|
|
||||||
total_payload_count = 0
|
|
||||||
|
|
||||||
for log in logs:
|
|
||||||
total_payload_count += log.get("count", 0)
|
|
||||||
if log.get("success"):
|
|
||||||
import_status["success"] = log.get("count")
|
|
||||||
else:
|
|
||||||
import_status["failed"] = log.get("count")
|
|
||||||
|
|
||||||
import_status["total_records"] = total_payload_count
|
|
||||||
|
|
||||||
return import_status
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def get_import_logs(docname: str):
|
|
||||||
frappe.has_permission("Bank Statement Import")
|
|
||||||
|
|
||||||
return frappe.get_all(
|
|
||||||
"Data Import Log",
|
|
||||||
fields=["success", "docname", "messages", "exception", "row_indexes"],
|
|
||||||
filters={"data_import": docname},
|
|
||||||
limit_page_length=5000,
|
|
||||||
order_by="log_index",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def upload_bank_statement(**args):
|
def upload_bank_statement(**args):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
from typing import Tuple, Union
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import flt
|
from frappe.utils import flt
|
||||||
from rapidfuzz import fuzz, process
|
from rapidfuzz import fuzz, process
|
||||||
@@ -17,7 +19,7 @@ class AutoMatchParty:
|
|||||||
def get(self, key):
|
def get(self, key):
|
||||||
return self.__dict__.get(key, None)
|
return self.__dict__.get(key, None)
|
||||||
|
|
||||||
def match(self) -> tuple | None:
|
def match(self) -> Union[Tuple, None]:
|
||||||
result = None
|
result = None
|
||||||
result = AutoMatchbyAccountIBAN(
|
result = AutoMatchbyAccountIBAN(
|
||||||
bank_party_account_number=self.bank_party_account_number,
|
bank_party_account_number=self.bank_party_account_number,
|
||||||
@@ -48,7 +50,7 @@ class AutoMatchbyAccountIBAN:
|
|||||||
result = self.match_account_in_party()
|
result = self.match_account_in_party()
|
||||||
return result
|
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"""
|
"""Check if there is a IBAN/Account No. match in Customer/Supplier/Employee"""
|
||||||
result = None
|
result = None
|
||||||
parties = get_parties_in_order(self.deposit)
|
parties = get_parties_in_order(self.deposit)
|
||||||
@@ -68,9 +70,6 @@ class AutoMatchbyAccountIBAN:
|
|||||||
party, or_filters=or_filters, pluck="name", limit_page_length=1
|
party, or_filters=or_filters, pluck="name", limit_page_length=1
|
||||||
)
|
)
|
||||||
|
|
||||||
if "bank_ac_no" in or_filters:
|
|
||||||
or_filters["bank_account_no"] = or_filters.pop("bank_ac_no")
|
|
||||||
|
|
||||||
if party_result:
|
if party_result:
|
||||||
result = (
|
result = (
|
||||||
party,
|
party,
|
||||||
@@ -98,7 +97,7 @@ class AutoMatchbyPartyNameDescription:
|
|||||||
def get(self, key):
|
def get(self, key):
|
||||||
return self.__dict__.get(key, None)
|
return self.__dict__.get(key, None)
|
||||||
|
|
||||||
def match(self) -> tuple | None:
|
def match(self) -> Union[Tuple, None]:
|
||||||
# fuzzy search by customer/supplier & employee
|
# fuzzy search by customer/supplier & employee
|
||||||
if not (self.bank_party_name or self.description):
|
if not (self.bank_party_name or self.description):
|
||||||
return None
|
return None
|
||||||
@@ -106,14 +105,15 @@ class AutoMatchbyPartyNameDescription:
|
|||||||
result = self.match_party_name_desc_in_party()
|
result = self.match_party_name_desc_in_party()
|
||||||
return result
|
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"""
|
"""Fuzzy search party name and/or description against parties in the system"""
|
||||||
result = None
|
result = None
|
||||||
parties = get_parties_in_order(self.deposit)
|
parties = get_parties_in_order(self.deposit)
|
||||||
|
|
||||||
for party in parties:
|
for party in parties:
|
||||||
filters = {"status": "Active"} if party == "Employee" else {"disabled": 0}
|
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"]:
|
for field in ["bank_party_name", "description"]:
|
||||||
if not self.get(field):
|
if not self.get(field):
|
||||||
@@ -130,9 +130,13 @@ class AutoMatchbyPartyNameDescription:
|
|||||||
|
|
||||||
return result
|
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
|
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)
|
party_name, skip = self.process_fuzzy_result(result)
|
||||||
|
|
||||||
if not party_name:
|
if not party_name:
|
||||||
@@ -143,21 +147,21 @@ class AutoMatchbyPartyNameDescription:
|
|||||||
party_name,
|
party_name,
|
||||||
), skip
|
), 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.
|
If there are multiple valid close matches return None as result may be faulty.
|
||||||
Return the result only if one accurate match stands out.
|
Return the result only if one accurate match stands out.
|
||||||
|
|
||||||
Returns: Result, Skip (whether or not to discontinue matching)
|
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):
|
if not result or not len(result):
|
||||||
return None, False
|
return None, False
|
||||||
|
|
||||||
first_result = result[0]
|
first_result = result[0]
|
||||||
if len(result) == 1:
|
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]
|
second_result = result[1]
|
||||||
if first_result[SCORE] > CUTOFF:
|
if first_result[SCORE] > CUTOFF:
|
||||||
@@ -166,7 +170,7 @@ class AutoMatchbyPartyNameDescription:
|
|||||||
if first_result[SCORE] == second_result[SCORE]:
|
if first_result[SCORE] == second_result[SCORE]:
|
||||||
return None, True
|
return None, True
|
||||||
|
|
||||||
return first_result[PARTY], True
|
return first_result[PARTY_ID], True
|
||||||
else:
|
else:
|
||||||
return None, False
|
return None, False
|
||||||
|
|
||||||
|
|||||||
@@ -56,19 +56,17 @@ class BankTransaction(Document):
|
|||||||
Bank Transaction should be on the same currency as the Bank Account.
|
Bank Transaction should be on the same currency as the Bank Account.
|
||||||
"""
|
"""
|
||||||
if self.currency and self.bank_account:
|
if self.currency and self.bank_account:
|
||||||
if account := frappe.get_cached_value("Bank Account", self.bank_account, "account"):
|
account = frappe.get_cached_value("Bank Account", self.bank_account, "account")
|
||||||
account_currency = frappe.get_cached_value("Account", account, "account_currency")
|
account_currency = frappe.get_cached_value("Account", account, "account_currency")
|
||||||
|
|
||||||
if self.currency != account_currency:
|
if self.currency != account_currency:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_(
|
_(
|
||||||
"Transaction currency: {0} cannot be different from Bank Account({1}) currency: {2}"
|
"Transaction currency: {0} cannot be different from Bank Account({1}) currency: {2}"
|
||||||
).format(
|
).format(
|
||||||
frappe.bold(self.currency),
|
frappe.bold(self.currency), frappe.bold(self.bank_account), frappe.bold(account_currency)
|
||||||
frappe.bold(self.bank_account),
|
|
||||||
frappe.bold(account_currency),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def set_status(self):
|
def set_status(self):
|
||||||
if self.docstatus == 2:
|
if self.docstatus == 2:
|
||||||
@@ -182,7 +180,7 @@ class BankTransaction(Document):
|
|||||||
frappe.throw(_("Voucher {0} is over-allocated by {1}").format(unallocated_amount))
|
frappe.throw(_("Voucher {0} is over-allocated by {1}").format(unallocated_amount))
|
||||||
|
|
||||||
for payment_entry in to_remove:
|
for payment_entry in to_remove:
|
||||||
self.remove(payment_entry)
|
self.remove(to_remove)
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def remove_payment_entries(self):
|
def remove_payment_entries(self):
|
||||||
@@ -237,7 +235,9 @@ def get_clearance_details(transaction, payment_entry):
|
|||||||
"""
|
"""
|
||||||
gl_bank_account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
|
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)
|
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(
|
unallocated_amount = min(
|
||||||
transaction.unallocated_amount,
|
transaction.unallocated_amount,
|
||||||
@@ -332,6 +332,7 @@ def get_total_allocated_amount(doctype, docname):
|
|||||||
|
|
||||||
def get_paid_amount(payment_entry, currency, gl_bank_account):
|
def get_paid_amount(payment_entry, currency, gl_bank_account):
|
||||||
if payment_entry.payment_document in ["Payment Entry", "Sales Invoice", "Purchase Invoice"]:
|
if payment_entry.payment_document in ["Payment Entry", "Sales Invoice", "Purchase Invoice"]:
|
||||||
|
|
||||||
paid_amount_field = "paid_amount"
|
paid_amount_field = "paid_amount"
|
||||||
if payment_entry.payment_document == "Payment Entry":
|
if payment_entry.payment_document == "Payment Entry":
|
||||||
doc = frappe.get_doc("Payment Entry", payment_entry.payment_entry)
|
doc = frappe.get_doc("Payment Entry", payment_entry.payment_entry)
|
||||||
@@ -370,7 +371,9 @@ def get_paid_amount(payment_entry, currency, gl_bank_account):
|
|||||||
)
|
)
|
||||||
|
|
||||||
elif payment_entry.payment_document == "Loan Repayment":
|
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":
|
elif payment_entry.payment_document == "Bank Transaction":
|
||||||
dep, wth = frappe.db.get_value(
|
dep, wth = frappe.db.get_value(
|
||||||
@@ -380,7 +383,9 @@ def get_paid_amount(payment_entry, currency, gl_bank_account):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
frappe.throw(
|
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
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -18,12 +18,12 @@ def upload_bank_statement():
|
|||||||
fcontent = frappe.local.uploaded_file
|
fcontent = frappe.local.uploaded_file
|
||||||
fname = frappe.local.uploaded_filename
|
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
|
from frappe.utils.csvutils import read_csv_content
|
||||||
|
|
||||||
rows = read_csv_content(fcontent, False)
|
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
|
from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file
|
||||||
|
|
||||||
rows = read_xlsx_file_from_attached_file(fcontent=fcontent)
|
rows = read_xlsx_file_from_attached_file(fcontent=fcontent)
|
||||||
|
|||||||
@@ -436,7 +436,9 @@ def add_vouchers(gl_account="_Test Bank - _TC"):
|
|||||||
|
|
||||||
mode_of_payment = frappe.get_doc({"doctype": "Mode of Payment", "name": "Cash"})
|
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"}):
|
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})
|
mode_of_payment.append("accounts", {"company": "_Test Company", "default_account": gl_account})
|
||||||
mode_of_payment.save()
|
mode_of_payment.save()
|
||||||
|
|
||||||
|
|||||||
@@ -138,10 +138,11 @@ class BisectAccountingStatements(Document):
|
|||||||
|
|
||||||
# set root as current node
|
# set root as current node
|
||||||
root = frappe.db.get_all("Bisect Nodes", filters={"root": ["is", "not set"]})[0]
|
root = frappe.db.get_all("Bisect Nodes", filters={"root": ["is", "not set"]})[0]
|
||||||
self.get_report_summary()
|
|
||||||
self.current_node = root.name
|
self.current_node = root.name
|
||||||
self.current_from_date = self.from_date
|
self.current_from_date = self.from_date
|
||||||
self.current_to_date = self.to_date
|
self.current_to_date = self.to_date
|
||||||
|
|
||||||
|
self.get_report_summary()
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
def get_report_summary(self):
|
def get_report_summary(self):
|
||||||
|
|||||||
@@ -70,11 +70,10 @@ class Budget(Document):
|
|||||||
select
|
select
|
||||||
b.name, ba.account from `tabBudget` b, `tabBudget Account` ba
|
b.name, ba.account from `tabBudget` b, `tabBudget Account` ba
|
||||||
where
|
where
|
||||||
ba.parent = b.name and b.docstatus < 2 and b.company = {} and {}={} and
|
ba.parent = b.name and b.docstatus < 2 and b.company = %s and %s=%s and
|
||||||
b.fiscal_year={} and b.name != {} and ba.account in ({}) """.format(
|
b.fiscal_year=%s and b.name != %s and ba.account in (%s) """
|
||||||
"%s", budget_against_field, "%s", "%s", "%s", ",".join(["%s"] * len(accounts))
|
% ("%s", budget_against_field, "%s", "%s", "%s", ",".join(["%s"] * len(accounts))),
|
||||||
),
|
(self.company, budget_against, self.fiscal_year, self.name) + tuple(accounts),
|
||||||
(self.company, budget_against, self.fiscal_year, self.name, *tuple(accounts)),
|
|
||||||
as_dict=1,
|
as_dict=1,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -97,14 +96,12 @@ class Budget(Document):
|
|||||||
if account_details.is_group:
|
if account_details.is_group:
|
||||||
frappe.throw(_("Budget cannot be assigned against Group Account {0}").format(d.account))
|
frappe.throw(_("Budget cannot be assigned against Group Account {0}").format(d.account))
|
||||||
elif account_details.company != self.company:
|
elif account_details.company != self.company:
|
||||||
frappe.throw(
|
frappe.throw(_("Account {0} does not belongs to company {1}").format(d.account, self.company))
|
||||||
_("Account {0} does not belongs to company {1}").format(d.account, self.company)
|
|
||||||
)
|
|
||||||
elif account_details.report_type != "Profit and Loss":
|
elif account_details.report_type != "Profit and Loss":
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_(
|
_("Budget cannot be assigned against {0}, as it's not an Income or Expense account").format(
|
||||||
"Budget cannot be assigned against {0}, as it's not an Income or Expense account"
|
d.account
|
||||||
).format(d.account)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if d.account in account_list:
|
if d.account in account_list:
|
||||||
@@ -142,8 +139,6 @@ class Budget(Document):
|
|||||||
|
|
||||||
def validate_expense_against_budget(args, expense_amount=0):
|
def validate_expense_against_budget(args, expense_amount=0):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
if not frappe.get_all("Budget", limit=1):
|
|
||||||
return
|
|
||||||
|
|
||||||
if args.get("company") and not args.fiscal_year:
|
if args.get("company") and not args.fiscal_year:
|
||||||
args.fiscal_year = get_fiscal_year(args.get("posting_date"), company=args.get("company"))[0]
|
args.fiscal_year = get_fiscal_year(args.get("posting_date"), company=args.get("company"))[0]
|
||||||
@@ -151,9 +146,6 @@ def validate_expense_against_budget(args, expense_amount=0):
|
|||||||
"Company", args.get("company"), "exception_budget_approver_role"
|
"Company", args.get("company"), "exception_budget_approver_role"
|
||||||
)
|
)
|
||||||
|
|
||||||
if not frappe.get_cached_value("Budget", {"fiscal_year": args.fiscal_year, "company": args.company}): # nosec
|
|
||||||
return
|
|
||||||
|
|
||||||
if not args.account:
|
if not args.account:
|
||||||
args.account = args.get("expense_account")
|
args.account = args.get("expense_account")
|
||||||
|
|
||||||
@@ -180,26 +172,32 @@ def validate_expense_against_budget(args, expense_amount=0):
|
|||||||
if (
|
if (
|
||||||
args.get(budget_against)
|
args.get(budget_against)
|
||||||
and args.account
|
and args.account
|
||||||
and (frappe.get_cached_value("Account", args.account, "root_type") == "Expense")
|
and frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"})
|
||||||
):
|
):
|
||||||
|
|
||||||
doctype = dimension.get("document_type")
|
doctype = dimension.get("document_type")
|
||||||
|
|
||||||
if frappe.get_cached_value("DocType", doctype, "is_tree"):
|
if frappe.get_cached_value("DocType", doctype, "is_tree"):
|
||||||
lft, rgt = frappe.get_cached_value(doctype, args.get(budget_against), ["lft", "rgt"])
|
lft, rgt = frappe.db.get_value(doctype, args.get(budget_against), ["lft", "rgt"])
|
||||||
condition = f"""and exists(select name from `tab{doctype}`
|
condition = """and exists(select name from `tab%s`
|
||||||
where lft<={lft} and rgt>={rgt} and name=b.{budget_against})""" # nosec
|
where lft<=%s and rgt>=%s and name=b.%s)""" % (
|
||||||
|
doctype,
|
||||||
|
lft,
|
||||||
|
rgt,
|
||||||
|
budget_against,
|
||||||
|
) # nosec
|
||||||
args.is_tree = True
|
args.is_tree = True
|
||||||
else:
|
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.is_tree = False
|
||||||
|
|
||||||
args.budget_against_field = budget_against
|
args.budget_against_field = budget_against
|
||||||
args.budget_against_doctype = doctype
|
args.budget_against_doctype = doctype
|
||||||
|
|
||||||
budget_records = frappe.db.sql(
|
budget_records = frappe.db.sql(
|
||||||
f"""
|
"""
|
||||||
select
|
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(b.applicable_on_material_request, 0) as for_material_request,
|
||||||
ifnull(applicable_on_purchase_order, 0) as for_purchase_order,
|
ifnull(applicable_on_purchase_order, 0) as for_purchase_order,
|
||||||
ifnull(applicable_on_booking_actual_expenses,0) as for_actual_expenses,
|
ifnull(applicable_on_booking_actual_expenses,0) as for_actual_expenses,
|
||||||
@@ -212,7 +210,9 @@ def validate_expense_against_budget(args, expense_amount=0):
|
|||||||
b.name=ba.parent and b.fiscal_year=%s
|
b.name=ba.parent and b.fiscal_year=%s
|
||||||
and ba.account=%s and b.docstatus=1
|
and ba.account=%s and b.docstatus=1
|
||||||
{condition}
|
{condition}
|
||||||
""",
|
""".format(
|
||||||
|
condition=condition, budget_against_field=budget_against
|
||||||
|
),
|
||||||
(args.fiscal_year, args.account),
|
(args.fiscal_year, args.account),
|
||||||
as_dict=True,
|
as_dict=True,
|
||||||
) # nosec
|
) # nosec
|
||||||
@@ -224,18 +224,12 @@ def validate_expense_against_budget(args, expense_amount=0):
|
|||||||
def validate_budget_records(args, budget_records, expense_amount):
|
def validate_budget_records(args, budget_records, expense_amount):
|
||||||
for budget in budget_records:
|
for budget in budget_records:
|
||||||
if flt(budget.budget_amount):
|
if flt(budget.budget_amount):
|
||||||
|
amount = expense_amount or get_amount(args, budget)
|
||||||
yearly_action, monthly_action = get_actions(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"):
|
if yearly_action in ("Stop", "Warn"):
|
||||||
compare_expense_with_budget(
|
compare_expense_with_budget(
|
||||||
args,
|
args, flt(budget.budget_amount), _("Annual"), yearly_action, budget.budget_against, amount
|
||||||
flt(budget.budget_amount),
|
|
||||||
_("Annual"),
|
|
||||||
yearly_action,
|
|
||||||
budget.budget_against,
|
|
||||||
expense_amount,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if monthly_action in ["Stop", "Warn"]:
|
if monthly_action in ["Stop", "Warn"]:
|
||||||
@@ -246,32 +240,18 @@ def validate_budget_records(args, budget_records, expense_amount):
|
|||||||
args["month_end_date"] = get_last_day(args.posting_date)
|
args["month_end_date"] = get_last_day(args.posting_date)
|
||||||
|
|
||||||
compare_expense_with_budget(
|
compare_expense_with_budget(
|
||||||
args,
|
args, budget_amount, _("Accumulated Monthly"), monthly_action, budget.budget_against, amount
|
||||||
budget_amount,
|
|
||||||
_("Accumulated Monthly"),
|
|
||||||
monthly_action,
|
|
||||||
budget.budget_against,
|
|
||||||
expense_amount,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def compare_expense_with_budget(args, budget_amount, action_for, action, budget_against, amount=0):
|
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
|
actual_expense = get_actual_expense(args)
|
||||||
if not amount:
|
total_expense = actual_expense + 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
|
|
||||||
|
|
||||||
if total_expense > budget_amount:
|
if total_expense > budget_amount:
|
||||||
if args.actual_expense > budget_amount:
|
if actual_expense > budget_amount:
|
||||||
error_tense = _("is already")
|
error_tense = _("is already")
|
||||||
diff = args.actual_expense - budget_amount
|
diff = actual_expense - budget_amount
|
||||||
else:
|
else:
|
||||||
error_tense = _("will be")
|
error_tense = _("will be")
|
||||||
diff = total_expense - budget_amount
|
diff = total_expense - budget_amount
|
||||||
@@ -288,10 +268,9 @@ def compare_expense_with_budget(args, budget_amount, action_for, action, budget_
|
|||||||
frappe.bold(fmt_money(diff, currency=currency)),
|
frappe.bold(fmt_money(diff, currency=currency)),
|
||||||
)
|
)
|
||||||
|
|
||||||
msg += get_expense_breakup(args, currency, budget_against)
|
if (
|
||||||
|
frappe.flags.exception_approver_role
|
||||||
if frappe.flags.exception_approver_role and frappe.flags.exception_approver_role in frappe.get_roles(
|
and frappe.flags.exception_approver_role in frappe.get_roles(frappe.session.user)
|
||||||
frappe.session.user
|
|
||||||
):
|
):
|
||||||
action = "Warn"
|
action = "Warn"
|
||||||
|
|
||||||
@@ -301,83 +280,6 @@ def compare_expense_with_budget(args, budget_amount, action_for, action, budget_
|
|||||||
frappe.msgprint(msg, indicator="orange", title=_("Budget Exceeded"))
|
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):
|
def get_actions(args, budget):
|
||||||
yearly_action = budget.action_if_annual_budget_exceeded
|
yearly_action = budget.action_if_annual_budget_exceeded
|
||||||
monthly_action = budget.action_if_accumulated_monthly_budget_exceeded
|
monthly_action = budget.action_if_accumulated_monthly_budget_exceeded
|
||||||
@@ -393,15 +295,31 @@ def get_actions(args, budget):
|
|||||||
return yearly_action, monthly_action
|
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")
|
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(
|
data = frappe.db.sql(
|
||||||
""" select ifnull((sum(child.stock_qty - child.ordered_qty) * rate), 0) as amount
|
""" 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
|
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
|
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),
|
parent.material_request_type = 'Purchase' and parent.status != 'Stopped'""".format(
|
||||||
|
condition
|
||||||
|
),
|
||||||
item_code,
|
item_code,
|
||||||
as_list=1,
|
as_list=1,
|
||||||
)
|
)
|
||||||
@@ -409,15 +327,17 @@ def get_requested_amount(args):
|
|||||||
return data[0][0] if data else 0
|
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")
|
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(
|
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
|
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
|
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,
|
item_code,
|
||||||
as_list=1,
|
as_list=1,
|
||||||
)
|
)
|
||||||
@@ -425,12 +345,12 @@ def get_ordered_amount(args):
|
|||||||
return data[0][0] if data else 0
|
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)
|
condition = "expense_account = '%s'" % (args.expense_account)
|
||||||
budget_against_field = args.get("budget_against_field")
|
budget_against_field = args.get("budget_against_field")
|
||||||
|
|
||||||
if budget_against_field and args.get(budget_against_field):
|
if budget_against_field and args.get(budget_against_field):
|
||||||
condition += 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"):
|
if args.get("fiscal_year"):
|
||||||
date_field = "schedule_date" if for_doc == "Material Request" else "transaction_date"
|
date_field = "schedule_date" if for_doc == "Material Request" else "transaction_date"
|
||||||
@@ -438,8 +358,12 @@ def get_other_condition(args, for_doc):
|
|||||||
"Fiscal Year", args.get("fiscal_year"), ["year_start_date", "year_end_date"]
|
"Fiscal Year", args.get("fiscal_year"), ["year_start_date", "year_end_date"]
|
||||||
)
|
)
|
||||||
|
|
||||||
condition += f""" and parent.{date_field}
|
condition += """ and parent.%s
|
||||||
between '{start_date}' and '{end_date}' """
|
between '%s' and '%s' """ % (
|
||||||
|
date_field,
|
||||||
|
start_date,
|
||||||
|
end_date,
|
||||||
|
)
|
||||||
|
|
||||||
return condition
|
return condition
|
||||||
|
|
||||||
@@ -458,17 +382,21 @@ def get_actual_expense(args):
|
|||||||
|
|
||||||
args.update(lft_rgt)
|
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
|
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:
|
else:
|
||||||
condition2 = f"""and exists(select name from `tab{args.budget_against_doctype}`
|
condition2 = """and exists(select name from `tab{doctype}`
|
||||||
where name=gle.{budget_against_field} and
|
where name=gle.{budget_against} and
|
||||||
gle.{budget_against_field} = %({budget_against_field})s)"""
|
gle.{budget_against} = %({budget_against})s)""".format(
|
||||||
|
doctype=args.budget_against_doctype, budget_against=budget_against_field
|
||||||
|
)
|
||||||
|
|
||||||
amount = flt(
|
amount = flt(
|
||||||
frappe.db.sql(
|
frappe.db.sql(
|
||||||
f"""
|
"""
|
||||||
select sum(gle.debit) - sum(gle.credit)
|
select sum(gle.debit) - sum(gle.credit)
|
||||||
from `tabGL Entry` gle
|
from `tabGL Entry` gle
|
||||||
where
|
where
|
||||||
@@ -479,7 +407,9 @@ def get_actual_expense(args):
|
|||||||
and gle.company=%(company)s
|
and gle.company=%(company)s
|
||||||
and gle.docstatus=1
|
and gle.docstatus=1
|
||||||
{condition2}
|
{condition2}
|
||||||
""",
|
""".format(
|
||||||
|
condition1=condition1, condition2=condition2
|
||||||
|
),
|
||||||
(args),
|
(args),
|
||||||
)[0][0]
|
)[0][0]
|
||||||
) # nosec
|
) # nosec
|
||||||
|
|||||||
@@ -41,7 +41,9 @@ class TestBudget(unittest.TestCase):
|
|||||||
|
|
||||||
budget = make_budget(budget_against="Cost Center")
|
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(
|
jv = make_journal_entry(
|
||||||
"_Test Account Cost for Goods Sold - _TC",
|
"_Test Account Cost for Goods Sold - _TC",
|
||||||
@@ -61,7 +63,9 @@ class TestBudget(unittest.TestCase):
|
|||||||
|
|
||||||
budget = make_budget(budget_against="Cost Center")
|
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(
|
jv = make_journal_entry(
|
||||||
"_Test Account Cost for Goods Sold - _TC",
|
"_Test Account Cost for Goods Sold - _TC",
|
||||||
@@ -93,7 +97,9 @@ class TestBudget(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
fiscal_year = get_fiscal_year(nowdate())[0]
|
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)
|
frappe.db.set_value("Budget", budget.name, "fiscal_year", fiscal_year)
|
||||||
|
|
||||||
mr = frappe.get_doc(
|
mr = frappe.get_doc(
|
||||||
@@ -132,7 +138,9 @@ class TestBudget(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
fiscal_year = get_fiscal_year(nowdate())[0]
|
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)
|
frappe.db.set_value("Budget", budget.name, "fiscal_year", fiscal_year)
|
||||||
|
|
||||||
po = create_purchase_order(transaction_date=nowdate(), do_not_submit=True)
|
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")
|
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"})
|
project = frappe.get_value("Project", {"project_name": "_Test Project"})
|
||||||
|
|
||||||
@@ -213,7 +223,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
if month > 9:
|
if month > 9:
|
||||||
month = 9
|
month = 9
|
||||||
|
|
||||||
for _i in range(month + 1):
|
for i in range(month + 1):
|
||||||
jv = make_journal_entry(
|
jv = make_journal_entry(
|
||||||
"_Test Account Cost for Goods Sold - _TC",
|
"_Test Account Cost for Goods Sold - _TC",
|
||||||
"_Test Bank - _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.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)
|
self.assertRaises(BudgetError, jv.cancel)
|
||||||
|
|
||||||
@@ -243,7 +255,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
month = 9
|
month = 9
|
||||||
|
|
||||||
project = frappe.get_value("Project", {"project_name": "_Test Project"})
|
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(
|
jv = make_journal_entry(
|
||||||
"_Test Account Cost for Goods Sold - _TC",
|
"_Test Account Cost for Goods Sold - _TC",
|
||||||
"_Test Bank - _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.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)
|
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")
|
set_total_expense_zero(nowdate(), "cost_center", "_Test Cost Center 2 - _TC")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Cost Center", cost_center="_Test Company - _TC")
|
budget = make_budget(budget_against="Cost Center", cost_center="_Test Company - _TC")
|
||||||
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
frappe.db.set_value(
|
||||||
|
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
||||||
|
)
|
||||||
|
|
||||||
jv = make_journal_entry(
|
jv = make_journal_entry(
|
||||||
"_Test Account Cost for Goods Sold - _TC",
|
"_Test Account Cost for Goods Sold - _TC",
|
||||||
@@ -300,7 +316,9 @@ class TestBudget(unittest.TestCase):
|
|||||||
).insert(ignore_permissions=True)
|
).insert(ignore_permissions=True)
|
||||||
|
|
||||||
budget = make_budget(budget_against="Cost Center", cost_center=cost_center)
|
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(
|
jv = make_journal_entry(
|
||||||
"_Test Account Cost for Goods Sold - _TC",
|
"_Test Account Cost for Goods Sold - _TC",
|
||||||
@@ -405,11 +423,13 @@ def make_budget(**args):
|
|||||||
fiscal_year = get_fiscal_year(nowdate())[0]
|
fiscal_year = get_fiscal_year(nowdate())[0]
|
||||||
|
|
||||||
if budget_against == "Project":
|
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)})
|
budget_list = frappe.get_all("Budget", fields=["name"], filters={"name": ("like", project_name)})
|
||||||
else:
|
else:
|
||||||
cost_center_name = "{}%".format(cost_center or "_Test Cost Center - _TC/" + fiscal_year)
|
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)})
|
budget_list = frappe.get_all(
|
||||||
|
"Budget", fields=["name"], filters={"name": ("like", cost_center_name)}
|
||||||
|
)
|
||||||
for d in budget_list:
|
for d in budget_list:
|
||||||
frappe.db.sql("delete from `tabBudget` where name = %(name)s", d)
|
frappe.db.sql("delete from `tabBudget` where name = %(name)s", d)
|
||||||
frappe.db.sql("delete from `tabBudget Account` where parent = %(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_annual_budget_exceeded = "Stop"
|
||||||
budget.action_if_accumulated_monthly_budget_exceeded = "Ignore"
|
budget.action_if_accumulated_monthly_budget_exceeded = "Ignore"
|
||||||
budget.budget_against = budget_against
|
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:
|
if args.applicable_on_material_request:
|
||||||
budget.applicable_on_material_request = 1
|
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 = (
|
budget.action_if_accumulated_monthly_budget_exceeded_on_mr = (
|
||||||
args.action_if_accumulated_monthly_budget_exceeded_on_mr or "Warn"
|
args.action_if_accumulated_monthly_budget_exceeded_on_mr or "Warn"
|
||||||
)
|
)
|
||||||
|
|
||||||
if args.applicable_on_purchase_order:
|
if args.applicable_on_purchase_order:
|
||||||
budget.applicable_on_purchase_order = 1
|
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 = (
|
budget.action_if_accumulated_monthly_budget_exceeded_on_po = (
|
||||||
args.action_if_accumulated_monthly_budget_exceeded_on_po or "Warn"
|
args.action_if_accumulated_monthly_budget_exceeded_on_po or "Warn"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,42 +1,94 @@
|
|||||||
{
|
{
|
||||||
"actions": [],
|
"allow_copy": 0,
|
||||||
"creation": "2016-05-16 11:54:09.286135",
|
"allow_import": 0,
|
||||||
"doctype": "DocType",
|
"allow_rename": 0,
|
||||||
"editable_grid": 1,
|
"beta": 0,
|
||||||
"engine": "InnoDB",
|
"creation": "2016-05-16 11:54:09.286135",
|
||||||
"field_order": [
|
"custom": 0,
|
||||||
"account",
|
"docstatus": 0,
|
||||||
"budget_amount"
|
"doctype": "DocType",
|
||||||
],
|
"document_type": "",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"fieldname": "account",
|
"allow_on_submit": 0,
|
||||||
"fieldtype": "Link",
|
"bold": 0,
|
||||||
"in_list_view": 1,
|
"collapsible": 0,
|
||||||
"label": "Account",
|
"columns": 0,
|
||||||
"options": "Account",
|
"fieldname": "account",
|
||||||
"reqd": 1,
|
"fieldtype": "Link",
|
||||||
"search_index": 1
|
"hidden": 0,
|
||||||
},
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Account",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Account",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "budget_amount",
|
"allow_on_submit": 0,
|
||||||
"fieldtype": "Currency",
|
"bold": 0,
|
||||||
"in_list_view": 1,
|
"collapsible": 0,
|
||||||
"label": "Budget Amount",
|
"columns": 0,
|
||||||
"options": "Company:company:default_currency",
|
"fieldname": "budget_amount",
|
||||||
"reqd": 1
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Budget Amount",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Company:company:default_currency",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"istable": 1,
|
"hide_heading": 0,
|
||||||
"links": [],
|
"hide_toolbar": 0,
|
||||||
"modified": "2024-03-04 15:43:27.016947",
|
"idx": 0,
|
||||||
"modified_by": "Administrator",
|
"image_view": 0,
|
||||||
"module": "Accounts",
|
"in_create": 0,
|
||||||
"name": "Budget Account",
|
|
||||||
"owner": "Administrator",
|
"is_submittable": 0,
|
||||||
"permissions": [],
|
"issingle": 0,
|
||||||
"quick_entry": 1,
|
"istable": 1,
|
||||||
"sort_field": "modified",
|
"max_attachments": 0,
|
||||||
"sort_order": "DESC",
|
"modified": "2017-01-02 17:02:53.339420",
|
||||||
"states": []
|
"modified_by": "Administrator",
|
||||||
|
"module": "Accounts",
|
||||||
|
"name": "Budget Account",
|
||||||
|
"name_case": "",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"quick_entry": 1,
|
||||||
|
"read_only": 0,
|
||||||
|
"read_only_onload": 0,
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_seen": 0
|
||||||
}
|
}
|
||||||
@@ -38,7 +38,9 @@ class ChartofAccountsImporter(Document):
|
|||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
if self.import_file:
|
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):
|
def validate_columns(data):
|
||||||
@@ -47,11 +49,9 @@ def validate_columns(data):
|
|||||||
|
|
||||||
no_of_columns = max([len(d) for d in data])
|
no_of_columns = max([len(d) for d in data])
|
||||||
|
|
||||||
if no_of_columns != 8:
|
if no_of_columns > 8:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_(
|
_("More columns found than expected. Please compare the uploaded file with standard template"),
|
||||||
"Columns are not according to template. Please compare the uploaded file with standard template"
|
|
||||||
),
|
|
||||||
title=(_("Wrong Template")),
|
title=(_("Wrong Template")),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ def generate_data_from_csv(file_doc, as_dict=False):
|
|||||||
file_path = file_doc.get_full_path()
|
file_path = file_doc.get_full_path()
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
with open(file_path) as in_file:
|
with open(file_path, "r") as in_file:
|
||||||
csv_reader = list(csv.reader(in_file))
|
csv_reader = list(csv.reader(in_file))
|
||||||
headers = csv_reader[0]
|
headers = csv_reader[0]
|
||||||
del csv_reader[0] # delete top row and headers row
|
del csv_reader[0] # delete top row and headers row
|
||||||
@@ -215,10 +215,10 @@ def build_forest(data):
|
|||||||
for row in data:
|
for row in data:
|
||||||
account_name, parent_account, account_number, parent_account_number = row[0:4]
|
account_name, parent_account, account_number, parent_account_number = row[0:4]
|
||||||
if account_number:
|
if account_number:
|
||||||
account_name = f"{account_number} - {account_name}"
|
account_name = "{} - {}".format(account_number, account_name)
|
||||||
if parent_account_number:
|
if parent_account_number:
|
||||||
parent_account_number = cstr(parent_account_number).strip()
|
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:
|
if parent_account == account_name == child:
|
||||||
return [parent_account]
|
return [parent_account]
|
||||||
@@ -230,7 +230,7 @@ def build_forest(data):
|
|||||||
frappe.bold(parent_account)
|
frappe.bold(parent_account)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return [child, *parent_account_list]
|
return [child] + parent_account_list
|
||||||
|
|
||||||
charts_map, paths = {}, []
|
charts_map, paths = {}, []
|
||||||
|
|
||||||
@@ -250,12 +250,12 @@ def build_forest(data):
|
|||||||
) = i
|
) = i
|
||||||
|
|
||||||
if not account_name:
|
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
|
name = account_name
|
||||||
if account_number:
|
if account_number:
|
||||||
account_number = cstr(account_number).strip()
|
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] = {}
|
||||||
charts_map[account_name]["account_name"] = name
|
charts_map[account_name]["account_name"] = name
|
||||||
@@ -352,9 +352,9 @@ def get_template(template_type, company):
|
|||||||
|
|
||||||
def get_sample_template(writer, company):
|
def get_sample_template(writer, company):
|
||||||
currency = frappe.db.get_value("Company", company, "default_currency")
|
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:
|
for row in f:
|
||||||
row = [*row.strip().split(","), currency]
|
row = row.strip().split(",") + [currency]
|
||||||
writer.writerow(row)
|
writer.writerow(row)
|
||||||
|
|
||||||
return writer
|
return writer
|
||||||
@@ -463,7 +463,7 @@ def unset_existing_data(company):
|
|||||||
"Purchase Taxes and Charges Template",
|
"Purchase Taxes and Charges Template",
|
||||||
]:
|
]:
|
||||||
frappe.db.sql(
|
frappe.db.sql(
|
||||||
f'''delete from `tab{doctype}` where `company`="%s"''' % (company) # nosec
|
'''delete from `tab{0}` where `company`="%s"'''.format(doctype) % (company) # nosec
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -66,71 +66,71 @@ def create_or_update_cheque_print_format(template_name):
|
|||||||
|
|
||||||
cheque_print.html = """
|
cheque_print.html = """
|
||||||
<style>
|
<style>
|
||||||
.print-format {{
|
.print-format {
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
}}
|
}
|
||||||
@media screen {{
|
@media screen {
|
||||||
.print-format {{
|
.print-format {
|
||||||
padding: 0in;
|
padding: 0in;
|
||||||
}}
|
}
|
||||||
}}
|
}
|
||||||
</style>
|
</style>
|
||||||
<div style="position: relative; top:{starting_position_from_top_edge}cm">
|
<div style="position: relative; top:%(starting_position_from_top_edge)scm">
|
||||||
<div style="width:{cheque_width}cm;height:{cheque_height}cm;">
|
<div style="width:%(cheque_width)scm;height:%(cheque_height)scm;">
|
||||||
<span style="top:{acc_pay_dist_from_top_edge}cm; left:{acc_pay_dist_from_left_edge}cm;
|
<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;">
|
border-bottom: solid 1px;border-top:solid 1px; width:2cm;text-align: center; position: absolute;">
|
||||||
{message_to_show}
|
%(message_to_show)s
|
||||||
</span>
|
</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;">
|
position: absolute;">
|
||||||
{{{{ frappe.utils.formatdate(doc.reference_date) or '' }}}}
|
{{ frappe.utils.formatdate(doc.reference_date) or '' }}
|
||||||
</span>
|
</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;">
|
position: absolute; min-width: 6cm;">
|
||||||
{{{{ doc.account_no or '' }}}}
|
{{ doc.account_no or '' }}
|
||||||
</span>
|
</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;">
|
position: absolute; min-width: 6cm;">
|
||||||
{{{{doc.party_name}}}}
|
{{doc.party_name}}
|
||||||
</span>
|
</span>
|
||||||
<span style="top:{amt_in_words_from_top_edge}cm; left:{amt_in_words_from_left_edge}cm;
|
<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}cm;
|
position: absolute; display: block; width: %(amt_in_word_width)scm;
|
||||||
line-height:{amt_in_words_line_spacing}cm; word-wrap: break-word;">
|
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)}}}}
|
{{frappe.utils.money_in_words(doc.base_paid_amount or doc.base_received_amount)}}
|
||||||
</span>
|
</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;">
|
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>
|
||||||
<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;">
|
position: absolute; min-width: 6cm;">
|
||||||
{{{{doc.company}}}}
|
{{doc.company}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>""".format(
|
</div>""" % {
|
||||||
starting_position_from_top_edge=doc.starting_position_from_top_edge
|
"starting_position_from_top_edge": doc.starting_position_from_top_edge
|
||||||
if doc.cheque_size == "A4"
|
if doc.cheque_size == "A4"
|
||||||
else 0.0,
|
else 0.0,
|
||||||
cheque_width=doc.cheque_width,
|
"cheque_width": doc.cheque_width,
|
||||||
cheque_height=doc.cheque_height,
|
"cheque_height": doc.cheque_height,
|
||||||
acc_pay_dist_from_top_edge=doc.acc_pay_dist_from_top_edge,
|
"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,
|
"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"),
|
"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_top_edge": doc.date_dist_from_top_edge,
|
||||||
date_dist_from_left_edge=doc.date_dist_from_left_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_top_edge": doc.acc_no_dist_from_top_edge,
|
||||||
acc_no_dist_from_left_edge=doc.acc_no_dist_from_left_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_top_edge": doc.payer_name_from_top_edge,
|
||||||
payer_name_from_left_edge=doc.payer_name_from_left_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_top_edge": doc.amt_in_words_from_top_edge,
|
||||||
amt_in_words_from_left_edge=doc.amt_in_words_from_left_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_word_width": doc.amt_in_word_width,
|
||||||
amt_in_words_line_spacing=doc.amt_in_words_line_spacing,
|
"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_top_edge": doc.amt_in_figures_from_top_edge,
|
||||||
amt_in_figures_from_left_edge=doc.amt_in_figures_from_left_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_top_edge": doc.signatory_from_top_edge,
|
||||||
signatory_from_left_edge=doc.signatory_from_left_edge,
|
"signatory_from_left_edge": doc.signatory_from_left_edge,
|
||||||
)
|
}
|
||||||
|
|
||||||
cheque_print.save(ignore_permissions=True)
|
cheque_print.save(ignore_permissions=True)
|
||||||
|
|
||||||
|
|||||||
@@ -125,7 +125,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_tree": 1,
|
"is_tree": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-04-24 10:55:54.083042",
|
"modified": "2022-01-31 13:22:58.916273",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Cost Center",
|
"name": "Cost Center",
|
||||||
@@ -163,15 +163,6 @@
|
|||||||
{
|
{
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"role": "Purchase User"
|
"role": "Purchase User"
|
||||||
},
|
|
||||||
{
|
|
||||||
"email": 1,
|
|
||||||
"export": 1,
|
|
||||||
"print": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Employee",
|
|
||||||
"select": 1,
|
|
||||||
"share": 1
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"search_fields": "parent_cost_center, is_group",
|
"search_fields": "parent_cost_center, is_group",
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ class CostCenter(NestedSet):
|
|||||||
def autoname(self):
|
def autoname(self):
|
||||||
from erpnext.accounts.utils import get_autoname_with_number
|
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):
|
def validate(self):
|
||||||
self.validate_mandatory()
|
self.validate_mandatory()
|
||||||
@@ -107,14 +109,14 @@ class CostCenter(NestedSet):
|
|||||||
new_cost_center = get_name_with_abbr(newdn, self.company)
|
new_cost_center = get_name_with_abbr(newdn, self.company)
|
||||||
|
|
||||||
# Validate properties before merging
|
# 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:
|
if not merge:
|
||||||
new_cost_center = get_name_with_number(new_cost_center, self.cost_center_number)
|
new_cost_center = get_name_with_number(new_cost_center, self.cost_center_number)
|
||||||
|
|
||||||
return new_cost_center
|
return new_cost_center
|
||||||
|
|
||||||
def after_rename(self, olddn, newdn, merge=False):
|
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:
|
if not merge:
|
||||||
new_cost_center = frappe.db.get_value(
|
new_cost_center = frappe.db.get_value(
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ test_records = frappe.get_test_records("Cost Center")
|
|||||||
|
|
||||||
class TestCostCenter(unittest.TestCase):
|
class TestCostCenter(unittest.TestCase):
|
||||||
def test_cost_center_creation_against_child_node(self):
|
def test_cost_center_creation_against_child_node(self):
|
||||||
|
|
||||||
if not frappe.db.get_value("Cost Center", {"name": "_Test Cost Center 2 - _TC"}):
|
if not frappe.db.get_value("Cost Center", {"name": "_Test Cost Center 2 - _TC"}):
|
||||||
frappe.get_doc(test_records[1]).insert()
|
frappe.get_doc(test_records[1]).insert()
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import add_days, flt, format_date, getdate
|
from frappe.utils import add_days, format_date, getdate
|
||||||
|
|
||||||
|
|
||||||
class MainCostCenterCantBeChild(frappe.ValidationError):
|
class MainCostCenterCantBeChild(frappe.ValidationError):
|
||||||
@@ -48,7 +48,7 @@ class CostCenterAllocation(Document):
|
|||||||
# end: auto-generated types
|
# end: auto-generated types
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super(CostCenterAllocation, self).__init__(*args, **kwargs)
|
||||||
self._skip_from_date_validation = False
|
self._skip_from_date_validation = False
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
@@ -60,10 +60,12 @@ class CostCenterAllocation(Document):
|
|||||||
self.validate_child_cost_centers()
|
self.validate_child_cost_centers()
|
||||||
|
|
||||||
def validate_total_allocation_percentage(self):
|
def validate_total_allocation_percentage(self):
|
||||||
total_percentage = sum([flt(d.percentage) for d in self.get("allocation_percentages", [])])
|
total_percentage = sum([d.percentage for d in self.get("allocation_percentages", [])])
|
||||||
|
|
||||||
if total_percentage != 100:
|
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):
|
def validate_from_date_based_on_existing_gle(self):
|
||||||
# Check if GLE exists against the main cost center
|
# Check if GLE exists against the main cost center
|
||||||
|
|||||||
@@ -22,10 +22,8 @@ class TestCostCenterAllocation(unittest.TestCase):
|
|||||||
cost_centers = [
|
cost_centers = [
|
||||||
"Main Cost Center 1",
|
"Main Cost Center 1",
|
||||||
"Main Cost Center 2",
|
"Main Cost Center 2",
|
||||||
"Main Cost Center 3",
|
|
||||||
"Sub Cost Center 1",
|
"Sub Cost Center 1",
|
||||||
"Sub Cost Center 2",
|
"Sub Cost Center 2",
|
||||||
"Sub Cost Center 3",
|
|
||||||
]
|
]
|
||||||
for cc in cost_centers:
|
for cc in cost_centers:
|
||||||
create_cost_center(cost_center_name=cc, company="_Test Company")
|
create_cost_center(cost_center_name=cc, company="_Test Company")
|
||||||
@@ -38,7 +36,7 @@ class TestCostCenterAllocation(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
jv = make_journal_entry(
|
jv = make_journal_entry(
|
||||||
"Cash - _TC", "Sales - _TC", 100, cost_center="Main Cost Center 1 - _TC", submit=True
|
"_Test Cash - _TC", "Sales - _TC", 100, cost_center="Main Cost Center 1 - _TC", submit=True
|
||||||
)
|
)
|
||||||
|
|
||||||
expected_values = [["Sub Cost Center 1 - _TC", 0.0, 60], ["Sub Cost Center 2 - _TC", 0.0, 40]]
|
expected_values = [["Sub Cost Center 1 - _TC", 0.0, 60], ["Sub Cost Center 2 - _TC", 0.0, 40]]
|
||||||
@@ -122,7 +120,7 @@ class TestCostCenterAllocation(unittest.TestCase):
|
|||||||
def test_valid_from_based_on_existing_gle(self):
|
def test_valid_from_based_on_existing_gle(self):
|
||||||
# GLE posted against Sub Cost Center 1 on today
|
# GLE posted against Sub Cost Center 1 on today
|
||||||
jv = make_journal_entry(
|
jv = make_journal_entry(
|
||||||
"Cash - _TC",
|
"_Test Cash - _TC",
|
||||||
"Sales - _TC",
|
"Sales - _TC",
|
||||||
100,
|
100,
|
||||||
cost_center="Main Cost Center 1 - _TC",
|
cost_center="Main Cost Center 1 - _TC",
|
||||||
@@ -143,53 +141,6 @@ class TestCostCenterAllocation(unittest.TestCase):
|
|||||||
|
|
||||||
jv.cancel()
|
jv.cancel()
|
||||||
|
|
||||||
def test_multiple_cost_center_allocation_on_same_main_cost_center(self):
|
|
||||||
coa1 = create_cost_center_allocation(
|
|
||||||
"_Test Company",
|
|
||||||
"Main Cost Center 3 - _TC",
|
|
||||||
{"Sub Cost Center 1 - _TC": 30, "Sub Cost Center 2 - _TC": 30, "Sub Cost Center 3 - _TC": 40},
|
|
||||||
valid_from=add_days(today(), -5),
|
|
||||||
)
|
|
||||||
|
|
||||||
coa2 = create_cost_center_allocation(
|
|
||||||
"_Test Company",
|
|
||||||
"Main Cost Center 3 - _TC",
|
|
||||||
{"Sub Cost Center 1 - _TC": 50, "Sub Cost Center 2 - _TC": 50},
|
|
||||||
valid_from=add_days(today(), -1),
|
|
||||||
)
|
|
||||||
|
|
||||||
jv = make_journal_entry(
|
|
||||||
"Cash - _TC",
|
|
||||||
"Sales - _TC",
|
|
||||||
100,
|
|
||||||
cost_center="Main Cost Center 3 - _TC",
|
|
||||||
posting_date=today(),
|
|
||||||
submit=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
expected_values = {"Sub Cost Center 1 - _TC": 50, "Sub Cost Center 2 - _TC": 50}
|
|
||||||
|
|
||||||
gle = frappe.qb.DocType("GL Entry")
|
|
||||||
gl_entries = (
|
|
||||||
frappe.qb.from_(gle)
|
|
||||||
.select(gle.cost_center, gle.debit, gle.credit)
|
|
||||||
.where(gle.voucher_type == "Journal Entry")
|
|
||||||
.where(gle.voucher_no == jv.name)
|
|
||||||
.where(gle.account == "Sales - _TC")
|
|
||||||
.orderby(gle.cost_center)
|
|
||||||
).run(as_dict=1)
|
|
||||||
|
|
||||||
self.assertTrue(gl_entries)
|
|
||||||
|
|
||||||
for gle in gl_entries:
|
|
||||||
self.assertTrue(gle.cost_center in expected_values)
|
|
||||||
self.assertEqual(gle.debit, 0)
|
|
||||||
self.assertEqual(gle.credit, expected_values[gle.cost_center])
|
|
||||||
|
|
||||||
coa1.cancel()
|
|
||||||
coa2.cancel()
|
|
||||||
jv.cancel()
|
|
||||||
|
|
||||||
|
|
||||||
def create_cost_center_allocation(
|
def create_cost_center_allocation(
|
||||||
company,
|
company,
|
||||||
|
|||||||
@@ -80,7 +80,7 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "valid_upto",
|
"fieldname": "valid_upto",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "Valid Upto"
|
"label": "Valid Up To"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval: doc.coupon_type == \"Promotional\"",
|
"depends_on": "eval: doc.coupon_type == \"Promotional\"",
|
||||||
@@ -115,7 +115,7 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2019-10-19 14:48:14.602481",
|
"modified": "2024-01-24 02:20:26.145996",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Coupon Code",
|
"name": "Coupon Code",
|
||||||
|
|||||||
@@ -3,36 +3,22 @@
|
|||||||
|
|
||||||
frappe.ui.form.on("Currency Exchange Settings", {
|
frappe.ui.form.on("Currency Exchange Settings", {
|
||||||
service_provider: function (frm) {
|
service_provider: function (frm) {
|
||||||
frm.call({
|
if (frm.doc.service_provider == "exchangerate.host") {
|
||||||
method: "erpnext.accounts.doctype.currency_exchange_settings.currency_exchange_settings.get_api_endpoint",
|
let result = ["result"];
|
||||||
args: {
|
let params = {
|
||||||
service_provider: frm.doc.service_provider,
|
date: "{transaction_date}",
|
||||||
use_http: frm.doc.use_http,
|
from: "{from_currency}",
|
||||||
},
|
to: "{to_currency}",
|
||||||
callback: function (r) {
|
};
|
||||||
if (r && r.message) {
|
add_param(frm, "https://api.exchangerate.host/convert", params, result);
|
||||||
if (frm.doc.service_provider == "exchangerate.host") {
|
} else if (frm.doc.service_provider == "frankfurter.app") {
|
||||||
let result = ["result"];
|
let result = ["rates", "{to_currency}"];
|
||||||
let params = {
|
let params = {
|
||||||
date: "{transaction_date}",
|
base: "{from_currency}",
|
||||||
from: "{from_currency}",
|
symbols: "{to_currency}",
|
||||||
to: "{to_currency}",
|
};
|
||||||
};
|
add_param(frm, "https://frankfurter.app/{transaction_date}", params, result);
|
||||||
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");
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
"disabled",
|
"disabled",
|
||||||
"service_provider",
|
"service_provider",
|
||||||
"api_endpoint",
|
"api_endpoint",
|
||||||
"use_http",
|
|
||||||
"access_key",
|
"access_key",
|
||||||
"url",
|
"url",
|
||||||
"column_break_3",
|
"column_break_3",
|
||||||
@@ -92,19 +91,12 @@
|
|||||||
"fieldname": "access_key",
|
"fieldname": "access_key",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Access Key"
|
"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,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-03-18 08:32:26.895076",
|
"modified": "2023-10-04 15:30:25.333860",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Currency Exchange Settings",
|
"name": "Currency Exchange Settings",
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ class CurrencyExchangeSettings(Document):
|
|||||||
result_key: DF.Table[CurrencyExchangeSettingsResult]
|
result_key: DF.Table[CurrencyExchangeSettingsResult]
|
||||||
service_provider: DF.Literal["frankfurter.app", "exchangerate.host", "Custom"]
|
service_provider: DF.Literal["frankfurter.app", "exchangerate.host", "Custom"]
|
||||||
url: DF.Data | None
|
url: DF.Data | None
|
||||||
use_http: DF.Check
|
|
||||||
# end: auto-generated types
|
# end: auto-generated types
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
@@ -43,6 +42,7 @@ class CurrencyExchangeSettings(Document):
|
|||||||
|
|
||||||
def set_parameters_and_result(self):
|
def set_parameters_and_result(self):
|
||||||
if self.service_provider == "exchangerate.host":
|
if self.service_provider == "exchangerate.host":
|
||||||
|
|
||||||
if not self.access_key:
|
if not self.access_key:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Access Key is required for Service Provider: {0}").format(
|
_("Access Key is required for Service Provider: {0}").format(
|
||||||
@@ -53,7 +53,7 @@ class CurrencyExchangeSettings(Document):
|
|||||||
self.set("result_key", [])
|
self.set("result_key", [])
|
||||||
self.set("req_params", [])
|
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("result_key", {"key": "result"})
|
||||||
self.append("req_params", {"key": "access_key", "value": self.access_key})
|
self.append("req_params", {"key": "access_key", "value": self.access_key})
|
||||||
self.append("req_params", {"key": "amount", "value": "1"})
|
self.append("req_params", {"key": "amount", "value": "1"})
|
||||||
@@ -64,7 +64,7 @@ class CurrencyExchangeSettings(Document):
|
|||||||
self.set("result_key", [])
|
self.set("result_key", [])
|
||||||
self.set("req_params", [])
|
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": "rates"})
|
||||||
self.append("result_key", {"key": "{to_currency}"})
|
self.append("result_key", {"key": "{to_currency}"})
|
||||||
self.append("req_params", {"key": "base", "value": "{from_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"
|
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:
|
try:
|
||||||
response = requests.get(api_url, params=params)
|
response = requests.get(api_url, params=params)
|
||||||
@@ -97,23 +99,7 @@ class CurrencyExchangeSettings(Document):
|
|||||||
]
|
]
|
||||||
except Exception:
|
except Exception:
|
||||||
frappe.throw(_("Invalid result key. Response:") + " " + response.text)
|
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."))
|
frappe.throw(_("Returned exchange rate is neither integer not float."))
|
||||||
|
|
||||||
self.url = response.url
|
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 = "api.frankfurter.app/{transaction_date}"
|
|
||||||
|
|
||||||
protocol = "https://"
|
|
||||||
if use_http:
|
|
||||||
protocol = "http://"
|
|
||||||
|
|
||||||
return protocol + api
|
|
||||||
return None
|
|
||||||
|
|||||||
@@ -85,7 +85,14 @@ class Dunning(AccountsController):
|
|||||||
frappe.throw(
|
frappe.throw(
|
||||||
_(
|
_(
|
||||||
"The currency of invoice {} ({}) is different from the currency of this dunning ({})."
|
"The currency of invoice {} ({}) is different from the currency of this dunning ({})."
|
||||||
).format(row.sales_invoice, invoice_currency, self.currency)
|
).format(
|
||||||
|
frappe.get_desk_link(
|
||||||
|
"Sales Invoice",
|
||||||
|
row.sales_invoice,
|
||||||
|
),
|
||||||
|
invoice_currency,
|
||||||
|
self.currency,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def validate_overdue_payments(self):
|
def validate_overdue_payments(self):
|
||||||
@@ -139,22 +146,6 @@ class Dunning(AccountsController):
|
|||||||
)
|
)
|
||||||
row.dunning_level = len(past_dunnings) + 1
|
row.dunning_level = len(past_dunnings) + 1
|
||||||
|
|
||||||
def on_cancel(self):
|
|
||||||
super().on_cancel()
|
|
||||||
self.ignore_linked_doctypes = [
|
|
||||||
"GL Entry",
|
|
||||||
"Stock Ledger Entry",
|
|
||||||
"Repost Item Valuation",
|
|
||||||
"Repost Payment Ledger",
|
|
||||||
"Repost Payment Ledger Items",
|
|
||||||
"Repost Accounting Ledger",
|
|
||||||
"Repost Accounting Ledger Items",
|
|
||||||
"Unreconcile Payment",
|
|
||||||
"Unreconcile Payment Entries",
|
|
||||||
"Payment Ledger Entry",
|
|
||||||
"Serial and Batch Bundle",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def resolve_dunning(doc, state):
|
def resolve_dunning(doc, state):
|
||||||
"""
|
"""
|
||||||
@@ -210,31 +201,19 @@ def get_linked_dunnings_as_per_state(sales_invoice, state):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_dunning_letter_text(dunning_type: str, doc: str | dict, language: str | None = None) -> dict:
|
def get_dunning_letter_text(dunning_type, doc, language=None):
|
||||||
DOCTYPE = "Dunning Letter Text"
|
|
||||||
FIELDS = ["body_text", "closing_text", "language"]
|
|
||||||
|
|
||||||
if isinstance(doc, str):
|
if isinstance(doc, str):
|
||||||
doc = json.loads(doc)
|
doc = json.loads(doc)
|
||||||
|
|
||||||
if not language:
|
|
||||||
language = doc.get("language")
|
|
||||||
|
|
||||||
if language:
|
if language:
|
||||||
letter_text = frappe.db.get_value(
|
filters = {"parent": dunning_type, "language": language}
|
||||||
DOCTYPE, {"parent": dunning_type, "language": language}, FIELDS, as_dict=1
|
else:
|
||||||
)
|
filters = {"parent": dunning_type, "is_default_language": 1}
|
||||||
|
letter_text = frappe.db.get_value(
|
||||||
if not letter_text:
|
"Dunning Letter Text", filters, ["body_text", "closing_text", "language"], as_dict=1
|
||||||
letter_text = frappe.db.get_value(
|
)
|
||||||
DOCTYPE, {"parent": dunning_type, "is_default_language": 1}, FIELDS, as_dict=1
|
if letter_text:
|
||||||
)
|
return {
|
||||||
|
"body_text": frappe.render_template(letter_text.body_text, doc),
|
||||||
if not letter_text:
|
"closing_text": frappe.render_template(letter_text.closing_text, doc),
|
||||||
return {}
|
"language": letter_text.language,
|
||||||
|
}
|
||||||
return {
|
|
||||||
"body_text": frappe.render_template(letter_text.body_text, doc),
|
|
||||||
"closing_text": frappe.render_template(letter_text.closing_text, doc),
|
|
||||||
"language": letter_text.language,
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -109,7 +109,9 @@ class TestDunning(FrappeTestCase):
|
|||||||
|
|
||||||
def create_dunning(overdue_days, dunning_type_name=None):
|
def create_dunning(overdue_days, dunning_type_name=None):
|
||||||
posting_date = add_days(today(), -1 * overdue_days)
|
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)
|
dunning = create_dunning_from_sales_invoice(sales_invoice.name)
|
||||||
|
|
||||||
if dunning_type_name:
|
if dunning_type_name:
|
||||||
|
|||||||
@@ -85,16 +85,18 @@ frappe.ui.form.on("Exchange Rate Revaluation", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
make_jv: function (frm) {
|
make_jv: function (frm) {
|
||||||
|
let revaluation_journal = null;
|
||||||
|
let zero_balance_journal = null;
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "make_jv_entries",
|
method: "make_jv_entries",
|
||||||
doc: frm.doc,
|
doc: frm.doc,
|
||||||
freeze: true,
|
freeze: true,
|
||||||
freeze_message: __("Creating Journal Entries..."),
|
freeze_message: "Making Journal Entries...",
|
||||||
callback: function (r) {
|
callback: function (r) {
|
||||||
if (r.message) {
|
if (r.message) {
|
||||||
let response = r.message;
|
let response = r.message;
|
||||||
if (response["revaluation_jv"] || response["zero_balance_jv"]) {
|
if (response["revaluation_jv"] || response["zero_balance_jv"]) {
|
||||||
frappe.msgprint(__("Journal entries have been created"));
|
frappe.msgprint(__("Journals have been created"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class ExchangeRateRevaluation(Document):
|
|||||||
self.set_total_gain_loss()
|
self.set_total_gain_loss()
|
||||||
|
|
||||||
def validate_rounding_loss_allowance(self):
|
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"))
|
frappe.throw(_("Rounding Loss Allowance should be between 0 and 1"))
|
||||||
|
|
||||||
def set_total_gain_loss(self):
|
def set_total_gain_loss(self):
|
||||||
@@ -74,21 +74,6 @@ class ExchangeRateRevaluation(Document):
|
|||||||
if not (self.company and self.posting_date):
|
if not (self.company and self.posting_date):
|
||||||
frappe.throw(_("Please select Company and Posting Date to getting entries"))
|
frappe.throw(_("Please select Company and Posting Date to getting entries"))
|
||||||
|
|
||||||
def before_submit(self):
|
|
||||||
self.remove_accounts_without_gain_loss()
|
|
||||||
|
|
||||||
def remove_accounts_without_gain_loss(self):
|
|
||||||
self.accounts = [account for account in self.accounts if account.gain_loss]
|
|
||||||
|
|
||||||
if not self.accounts:
|
|
||||||
frappe.throw(_("At least one account with exchange gain or loss is required"))
|
|
||||||
|
|
||||||
frappe.msgprint(
|
|
||||||
_("Removing rows without exchange gain or loss"),
|
|
||||||
alert=True,
|
|
||||||
indicator="yellow",
|
|
||||||
)
|
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.ignore_linked_doctypes = "GL Entry"
|
self.ignore_linked_doctypes = "GL Entry"
|
||||||
|
|
||||||
@@ -263,26 +248,27 @@ class ExchangeRateRevaluation(Document):
|
|||||||
new_exchange_rate = get_exchange_rate(d.account_currency, company_currency, posting_date)
|
new_exchange_rate = get_exchange_rate(d.account_currency, company_currency, posting_date)
|
||||||
new_balance_in_base_currency = flt(d.balance_in_account_currency * new_exchange_rate)
|
new_balance_in_base_currency = flt(d.balance_in_account_currency * new_exchange_rate)
|
||||||
gain_loss = flt(new_balance_in_base_currency, precision) - flt(d.balance, precision)
|
gain_loss = flt(new_balance_in_base_currency, precision) - flt(d.balance, precision)
|
||||||
|
if gain_loss:
|
||||||
accounts.append(
|
accounts.append(
|
||||||
{
|
{
|
||||||
"account": d.account,
|
"account": d.account,
|
||||||
"party_type": d.party_type,
|
"party_type": d.party_type,
|
||||||
"party": d.party,
|
"party": d.party,
|
||||||
"account_currency": d.account_currency,
|
"account_currency": d.account_currency,
|
||||||
"balance_in_base_currency": d.balance,
|
"balance_in_base_currency": d.balance,
|
||||||
"balance_in_account_currency": d.balance_in_account_currency,
|
"balance_in_account_currency": d.balance_in_account_currency,
|
||||||
"zero_balance": d.zero_balance,
|
"zero_balance": d.zero_balance,
|
||||||
"current_exchange_rate": current_exchange_rate,
|
"current_exchange_rate": current_exchange_rate,
|
||||||
"new_exchange_rate": new_exchange_rate,
|
"new_exchange_rate": new_exchange_rate,
|
||||||
"new_balance_in_base_currency": new_balance_in_base_currency,
|
"new_balance_in_base_currency": new_balance_in_base_currency,
|
||||||
"new_balance_in_account_currency": d.balance_in_account_currency,
|
"new_balance_in_account_currency": d.balance_in_account_currency,
|
||||||
"gain_loss": gain_loss,
|
"gain_loss": gain_loss,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Handle Accounts with '0' balance in Account/Base Currency
|
# Handle Accounts with '0' balance in Account/Base Currency
|
||||||
for d in [x for x in account_details if x.zero_balance]:
|
for d in [x for x in account_details if x.zero_balance]:
|
||||||
|
|
||||||
if d.balance != 0:
|
if d.balance != 0:
|
||||||
current_exchange_rate = new_exchange_rate = 0
|
current_exchange_rate = new_exchange_rate = 0
|
||||||
|
|
||||||
@@ -295,30 +281,30 @@ class ExchangeRateRevaluation(Document):
|
|||||||
new_balance_in_account_currency = 0
|
new_balance_in_account_currency = 0
|
||||||
|
|
||||||
current_exchange_rate = (
|
current_exchange_rate = (
|
||||||
calculate_exchange_rate_using_last_gle(company, d.account, d.party_type, d.party)
|
calculate_exchange_rate_using_last_gle(company, d.account, d.party_type, d.party) or 0.0
|
||||||
or 0.0
|
|
||||||
)
|
)
|
||||||
|
|
||||||
gain_loss = new_balance_in_account_currency - (
|
gain_loss = new_balance_in_account_currency - (
|
||||||
current_exchange_rate * d.balance_in_account_currency
|
current_exchange_rate * d.balance_in_account_currency
|
||||||
)
|
)
|
||||||
|
|
||||||
accounts.append(
|
if gain_loss:
|
||||||
{
|
accounts.append(
|
||||||
"account": d.account,
|
{
|
||||||
"party_type": d.party_type,
|
"account": d.account,
|
||||||
"party": d.party,
|
"party_type": d.party_type,
|
||||||
"account_currency": d.account_currency,
|
"party": d.party,
|
||||||
"balance_in_base_currency": d.balance,
|
"account_currency": d.account_currency,
|
||||||
"balance_in_account_currency": d.balance_in_account_currency,
|
"balance_in_base_currency": d.balance,
|
||||||
"zero_balance": d.zero_balance,
|
"balance_in_account_currency": d.balance_in_account_currency,
|
||||||
"current_exchange_rate": current_exchange_rate,
|
"zero_balance": d.zero_balance,
|
||||||
"new_exchange_rate": new_exchange_rate,
|
"current_exchange_rate": current_exchange_rate,
|
||||||
"new_balance_in_base_currency": new_balance_in_base_currency,
|
"new_exchange_rate": new_exchange_rate,
|
||||||
"new_balance_in_account_currency": new_balance_in_account_currency,
|
"new_balance_in_base_currency": new_balance_in_base_currency,
|
||||||
"gain_loss": gain_loss,
|
"new_balance_in_account_currency": new_balance_in_account_currency,
|
||||||
}
|
"gain_loss": gain_loss,
|
||||||
)
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return accounts
|
return accounts
|
||||||
|
|
||||||
@@ -349,7 +335,9 @@ class ExchangeRateRevaluation(Document):
|
|||||||
|
|
||||||
revaluation_jv = self.make_jv_for_revaluation()
|
revaluation_jv = self.make_jv_for_revaluation()
|
||||||
if revaluation_jv:
|
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 {
|
return {
|
||||||
"revaluation_jv": revaluation_jv.name if revaluation_jv else None,
|
"revaluation_jv": revaluation_jv.name if revaluation_jv else None,
|
||||||
@@ -406,8 +394,7 @@ class ExchangeRateRevaluation(Document):
|
|||||||
journal_account.update(
|
journal_account.update(
|
||||||
{
|
{
|
||||||
dr_or_cr: flt(
|
dr_or_cr: flt(
|
||||||
abs(d.get("balance_in_account_currency")),
|
abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")
|
||||||
d.precision("balance_in_account_currency"),
|
|
||||||
),
|
),
|
||||||
reverse_dr_or_cr: 0,
|
reverse_dr_or_cr: 0,
|
||||||
"debit": 0,
|
"debit": 0,
|
||||||
@@ -533,9 +520,7 @@ class ExchangeRateRevaluation(Document):
|
|||||||
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")
|
||||||
),
|
),
|
||||||
"cost_center": erpnext.get_default_cost_center(self.company),
|
"cost_center": erpnext.get_default_cost_center(self.company),
|
||||||
"exchange_rate": flt(
|
"exchange_rate": flt(d.get("current_exchange_rate"), d.precision("current_exchange_rate")),
|
||||||
d.get("current_exchange_rate"), d.precision("current_exchange_rate")
|
|
||||||
),
|
|
||||||
"reference_type": "Exchange Rate Revaluation",
|
"reference_type": "Exchange Rate Revaluation",
|
||||||
"reference_name": self.name,
|
"reference_name": self.name,
|
||||||
}
|
}
|
||||||
@@ -613,7 +598,7 @@ def calculate_exchange_rate_using_last_gle(company, account, party_type, party):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_account_details(
|
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):
|
if not (company and posting_date):
|
||||||
frappe.throw(_("Company and Posting Date is mandatory"))
|
frappe.throw(_("Company and Posting Date is mandatory"))
|
||||||
@@ -626,7 +611,7 @@ def get_account_details(
|
|||||||
frappe.throw(_("Party Type and Party is mandatory for {0} account").format(account_type))
|
frappe.throw(_("Party Type and Party is mandatory for {0} account").format(account_type))
|
||||||
|
|
||||||
account_details = {}
|
account_details = {}
|
||||||
erpnext.get_company_currency(company)
|
company_currency = erpnext.get_company_currency(company)
|
||||||
|
|
||||||
account_details = {
|
account_details = {
|
||||||
"account_currency": account_currency,
|
"account_currency": account_currency,
|
||||||
@@ -640,22 +625,24 @@ def get_account_details(
|
|||||||
rounding_loss_allowance=rounding_loss_allowance,
|
rounding_loss_allowance=rounding_loss_allowance,
|
||||||
)
|
)
|
||||||
|
|
||||||
if account_balance and (account_balance[0].balance or account_balance[0].balance_in_account_currency):
|
if account_balance and (
|
||||||
if account_with_new_balance := ExchangeRateRevaluation.calculate_new_account_balance(
|
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
|
company, posting_date, account_balance
|
||||||
):
|
)
|
||||||
row = account_with_new_balance[0]
|
row = account_with_new_balance[0]
|
||||||
account_details.update(
|
account_details.update(
|
||||||
{
|
{
|
||||||
"balance_in_base_currency": row["balance_in_base_currency"],
|
"balance_in_base_currency": row["balance_in_base_currency"],
|
||||||
"balance_in_account_currency": row["balance_in_account_currency"],
|
"balance_in_account_currency": row["balance_in_account_currency"],
|
||||||
"current_exchange_rate": row["current_exchange_rate"],
|
"current_exchange_rate": row["current_exchange_rate"],
|
||||||
"new_exchange_rate": row["new_exchange_rate"],
|
"new_exchange_rate": row["new_exchange_rate"],
|
||||||
"new_balance_in_base_currency": row["new_balance_in_base_currency"],
|
"new_balance_in_base_currency": row["new_balance_in_base_currency"],
|
||||||
"new_balance_in_account_currency": row["new_balance_in_account_currency"],
|
"new_balance_in_account_currency": row["new_balance_in_account_currency"],
|
||||||
"zero_balance": row["zero_balance"],
|
"zero_balance": row["zero_balance"],
|
||||||
"gain_loss": row["gain_loss"],
|
"gain_loss": row["gain_loss"],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return account_details
|
return account_details
|
||||||
|
|||||||
@@ -1,14 +1,21 @@
|
|||||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# See license.txt
|
# See license.txt
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
from frappe import qb
|
||||||
from frappe.tests.utils import FrappeTestCase, change_settings
|
from frappe.tests.utils import FrappeTestCase, change_settings
|
||||||
from frappe.utils import add_days, flt, today
|
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.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.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.accounts.test.accounts_mixin import AccountsTestMixin
|
||||||
|
from erpnext.stock.doctype.item.test_item import create_item
|
||||||
|
|
||||||
|
|
||||||
class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
|
class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
|
||||||
@@ -66,7 +73,9 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
|
|||||||
err.extend("accounts", accounts)
|
err.extend("accounts", accounts)
|
||||||
row = err.accounts[0]
|
row = err.accounts[0]
|
||||||
row.new_exchange_rate = 85
|
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)
|
row.gain_loss = row.new_balance_in_base_currency - flt(row.balance_in_base_currency)
|
||||||
err.set_total_gain_loss()
|
err.set_total_gain_loss()
|
||||||
err = err.save().submit()
|
err = err.save().submit()
|
||||||
@@ -118,9 +127,9 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
|
|||||||
pe.save().submit()
|
pe.save().submit()
|
||||||
|
|
||||||
# Cancel the auto created gain/loss JE to simulate balance only in base currency
|
# 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")[
|
je = frappe.db.get_all(
|
||||||
0
|
"Journal Entry Account", filters={"reference_name": si.name}, pluck="parent"
|
||||||
]
|
)[0]
|
||||||
frappe.get_doc("Journal Entry", je).cancel()
|
frappe.get_doc("Journal Entry", je).cancel()
|
||||||
|
|
||||||
err = frappe.new_doc("Exchange Rate Revaluation")
|
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.debit, precision), 0.0)
|
||||||
self.assertEqual(flt(acc.credit, 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
|
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
|
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
|
# 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,
|
"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))
|
self.assertEqual(expected_data.get(key), account_details.get(key))
|
||||||
|
|||||||
@@ -4,7 +4,10 @@
|
|||||||
frappe.ui.form.on("Fiscal Year", {
|
frappe.ui.form.on("Fiscal Year", {
|
||||||
onload: function (frm) {
|
onload: function (frm) {
|
||||||
if (frm.doc.__islocal) {
|
if (frm.doc.__islocal) {
|
||||||
frm.set_value("year_start_date", frappe.datetime.year_start());
|
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) {
|
||||||
|
|||||||
@@ -72,22 +72,22 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"description": "More/Less than 12 months.",
|
"description": "Less than 12 months.",
|
||||||
"fieldname": "is_short_year",
|
"fieldname": "is_short_year",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Is Short/Long Year",
|
"label": "Is Short Year",
|
||||||
"set_only_once": 1
|
"set_only_once": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-calendar",
|
"icon": "fa fa-calendar",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-05-27 17:29:55.560840",
|
"modified": "2024-01-30 12:35:38.645968",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Fiscal Year",
|
"name": "Fiscal Year",
|
||||||
"naming_rule": "By fieldname",
|
"naming_rule": "By fieldname",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"create": 1,
|
"create": 1,
|
||||||
@@ -127,10 +127,6 @@
|
|||||||
{
|
{
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"role": "Stock Manager"
|
"role": "Stock Manager"
|
||||||
},
|
|
||||||
{
|
|
||||||
"read": 1,
|
|
||||||
"role": "Auditor"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"show_name_in_global_search": 1,
|
"show_name_in_global_search": 1,
|
||||||
|
|||||||
@@ -108,9 +108,9 @@ class FiscalYear(Document):
|
|||||||
|
|
||||||
if overlap:
|
if overlap:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_(
|
_("Year start date or end date is overlapping with {0}. To avoid please set company").format(
|
||||||
"Year start date or end date is overlapping with {0}. To avoid please set company"
|
existing.name
|
||||||
).format(existing.name),
|
),
|
||||||
frappe.NameError,
|
frappe.NameError,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -126,9 +126,9 @@ def check_duplicate_fiscal_year(doc):
|
|||||||
not frappe.flags.in_test
|
not frappe.flags.in_test
|
||||||
):
|
):
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_(
|
_("Fiscal Year Start Date and Fiscal Year End Date are already set in Fiscal Year {0}").format(
|
||||||
"Fiscal Year Start Date and Fiscal Year End Date are already set in Fiscal Year {0}"
|
fiscal_year
|
||||||
).format(fiscal_year)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,50 +6,38 @@
|
|||||||
"document_type": "Document",
|
"document_type": "Document",
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"dates_section",
|
|
||||||
"posting_date",
|
"posting_date",
|
||||||
"transaction_date",
|
"transaction_date",
|
||||||
"column_break_avko",
|
|
||||||
"fiscal_year",
|
|
||||||
"due_date",
|
|
||||||
"account_details_section",
|
|
||||||
"account",
|
"account",
|
||||||
"account_currency",
|
|
||||||
"column_break_ifvf",
|
|
||||||
"against",
|
|
||||||
"party_type",
|
"party_type",
|
||||||
"party",
|
"party",
|
||||||
"transaction_details_section",
|
"cost_center",
|
||||||
"voucher_type",
|
"debit",
|
||||||
"voucher_no",
|
"credit",
|
||||||
"voucher_subtype",
|
"account_currency",
|
||||||
"transaction_currency",
|
"debit_in_account_currency",
|
||||||
"column_break_dpsx",
|
"credit_in_account_currency",
|
||||||
|
"against",
|
||||||
"against_voucher_type",
|
"against_voucher_type",
|
||||||
"against_voucher",
|
"against_voucher",
|
||||||
|
"voucher_type",
|
||||||
|
"voucher_subtype",
|
||||||
|
"voucher_no",
|
||||||
"voucher_detail_no",
|
"voucher_detail_no",
|
||||||
"transaction_exchange_rate",
|
|
||||||
"amounts_section",
|
|
||||||
"debit_in_account_currency",
|
|
||||||
"debit",
|
|
||||||
"debit_in_transaction_currency",
|
|
||||||
"column_break_bm1w",
|
|
||||||
"credit_in_account_currency",
|
|
||||||
"credit",
|
|
||||||
"credit_in_transaction_currency",
|
|
||||||
"dimensions_section",
|
|
||||||
"cost_center",
|
|
||||||
"column_break_lmnm",
|
|
||||||
"project",
|
"project",
|
||||||
"more_info_section",
|
"remarks",
|
||||||
"finance_book",
|
|
||||||
"company",
|
|
||||||
"is_opening",
|
"is_opening",
|
||||||
"is_advance",
|
"is_advance",
|
||||||
"column_break_8abq",
|
"fiscal_year",
|
||||||
|
"company",
|
||||||
|
"finance_book",
|
||||||
"to_rename",
|
"to_rename",
|
||||||
|
"due_date",
|
||||||
"is_cancelled",
|
"is_cancelled",
|
||||||
"remarks"
|
"transaction_currency",
|
||||||
|
"debit_in_transaction_currency",
|
||||||
|
"credit_in_transaction_currency",
|
||||||
|
"transaction_exchange_rate"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -191,8 +179,7 @@
|
|||||||
"fieldname": "voucher_detail_no",
|
"fieldname": "voucher_detail_no",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Voucher Detail No",
|
"label": "Voucher Detail No",
|
||||||
"read_only": 1,
|
"read_only": 1
|
||||||
"search_index": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "project",
|
"fieldname": "project",
|
||||||
@@ -297,67 +284,13 @@
|
|||||||
"fieldname": "voucher_subtype",
|
"fieldname": "voucher_subtype",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"label": "Voucher Subtype"
|
"label": "Voucher Subtype"
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "dates_section",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"label": "Dates"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "column_break_avko",
|
|
||||||
"fieldtype": "Column Break"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "account_details_section",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"label": "Account Details"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "column_break_ifvf",
|
|
||||||
"fieldtype": "Column Break"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "transaction_details_section",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"label": "Transaction Details"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "amounts_section",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"label": "Amounts"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "column_break_dpsx",
|
|
||||||
"fieldtype": "Column Break"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "more_info_section",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"label": "More Info"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "column_break_bm1w",
|
|
||||||
"fieldtype": "Column Break"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "dimensions_section",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"label": "Dimensions"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "column_break_lmnm",
|
|
||||||
"fieldtype": "Column Break"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "column_break_8abq",
|
|
||||||
"fieldtype": "Column Break"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-list",
|
"icon": "fa fa-list",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"in_create": 1,
|
"in_create": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-08-22 13:03:39.997475",
|
"modified": "2023-09-26 12:03:23.031733",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "GL Entry",
|
"name": "GL Entry",
|
||||||
@@ -389,7 +322,7 @@
|
|||||||
],
|
],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"search_fields": "voucher_no,account,posting_date,against_voucher",
|
"search_fields": "voucher_no,account,posting_date,against_voucher",
|
||||||
"sort_field": "creation",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"states": []
|
"states": []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ class GLEntry(Document):
|
|||||||
account: DF.Link | None
|
account: DF.Link | None
|
||||||
account_currency: DF.Link | None
|
account_currency: DF.Link | None
|
||||||
against: DF.Text | None
|
against: DF.Text | None
|
||||||
|
against_link: DF.DynamicLink | None
|
||||||
|
against_type: DF.Link | None
|
||||||
against_voucher: DF.DynamicLink | None
|
against_voucher: DF.DynamicLink | None
|
||||||
against_voucher_type: DF.Link | None
|
against_voucher_type: DF.Link | None
|
||||||
company: DF.Link | None
|
company: DF.Link | None
|
||||||
@@ -105,18 +107,13 @@ class GLEntry(Document):
|
|||||||
]:
|
]:
|
||||||
# Update outstanding amt on against voucher
|
# Update outstanding amt on against voucher
|
||||||
if (
|
if (
|
||||||
self.against_voucher_type
|
self.against_voucher_type in ["Journal Entry", "Sales Invoice", "Purchase Invoice", "Fees"]
|
||||||
in ["Journal Entry", "Sales Invoice", "Purchase Invoice", "Fees"]
|
|
||||||
and self.against_voucher
|
and self.against_voucher
|
||||||
and self.flags.update_outstanding == "Yes"
|
and self.flags.update_outstanding == "Yes"
|
||||||
and not frappe.flags.is_reverse_depr_entry
|
and not frappe.flags.is_reverse_depr_entry
|
||||||
):
|
):
|
||||||
update_outstanding_amt(
|
update_outstanding_amt(
|
||||||
self.account,
|
self.account, self.party_type, self.party, self.against_voucher_type, self.against_voucher
|
||||||
self.party_type,
|
|
||||||
self.party,
|
|
||||||
self.against_voucher_type,
|
|
||||||
self.against_voucher,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def check_mandatory(self):
|
def check_mandatory(self):
|
||||||
@@ -182,13 +179,12 @@ class GLEntry(Document):
|
|||||||
and self.company == dimension.company
|
and self.company == dimension.company
|
||||||
and dimension.mandatory_for_pl
|
and dimension.mandatory_for_pl
|
||||||
and not dimension.disabled
|
and not dimension.disabled
|
||||||
and not self.is_cancelled
|
|
||||||
):
|
):
|
||||||
if not self.get(dimension.fieldname):
|
if not self.get(dimension.fieldname):
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_(
|
_("Accounting Dimension <b>{0}</b> is required for 'Profit and Loss' account {1}.").format(
|
||||||
"Accounting Dimension <b>{0}</b> is required for 'Profit and Loss' account {1}."
|
dimension.label, self.account
|
||||||
).format(dimension.label, self.account)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -196,13 +192,12 @@ class GLEntry(Document):
|
|||||||
and self.company == dimension.company
|
and self.company == dimension.company
|
||||||
and dimension.mandatory_for_bs
|
and dimension.mandatory_for_bs
|
||||||
and not dimension.disabled
|
and not dimension.disabled
|
||||||
and not self.is_cancelled
|
|
||||||
):
|
):
|
||||||
if not self.get(dimension.fieldname):
|
if not self.get(dimension.fieldname):
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_(
|
_("Accounting Dimension <b>{0}</b> is required for 'Balance Sheet' account {1}.").format(
|
||||||
"Accounting Dimension <b>{0}</b> is required for 'Balance Sheet' account {1}."
|
dimension.label, self.account
|
||||||
).format(dimension.label, self.account)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def check_pl_account(self):
|
def check_pl_account(self):
|
||||||
@@ -250,7 +245,9 @@ class GLEntry(Document):
|
|||||||
if not self.cost_center:
|
if not self.cost_center:
|
||||||
return
|
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:
|
if company != self.company:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
@@ -319,27 +316,31 @@ def update_outstanding_amt(
|
|||||||
account, party_type, party, against_voucher_type, against_voucher, on_cancel=False
|
account, party_type, party, against_voucher_type, against_voucher, on_cancel=False
|
||||||
):
|
):
|
||||||
if party_type and party:
|
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)
|
frappe.db.escape(party_type), frappe.db.escape(party)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
party_condition = ""
|
party_condition = ""
|
||||||
|
|
||||||
if against_voucher_type == "Sales Invoice":
|
if against_voucher_type == "Sales Invoice":
|
||||||
party_account = frappe.get_cached_value(against_voucher_type, against_voucher, "debit_to")
|
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:
|
else:
|
||||||
account_condition = f" and account = {frappe.db.escape(account)}"
|
account_condition = " and account = {0}".format(frappe.db.escape(account))
|
||||||
|
|
||||||
# get final outstanding amt
|
# get final outstanding amt
|
||||||
bal = flt(
|
bal = flt(
|
||||||
frappe.db.sql(
|
frappe.db.sql(
|
||||||
f"""
|
"""
|
||||||
select sum(debit_in_account_currency) - sum(credit_in_account_currency)
|
select sum(debit_in_account_currency) - sum(credit_in_account_currency)
|
||||||
from `tabGL Entry`
|
from `tabGL Entry`
|
||||||
where against_voucher_type=%s and against_voucher=%s
|
where against_voucher_type=%s and against_voucher=%s
|
||||||
and voucher_type != 'Invoice Discounting'
|
and voucher_type != 'Invoice Discounting'
|
||||||
{party_condition} {account_condition}""",
|
{0} {1}""".format(
|
||||||
|
party_condition, account_condition
|
||||||
|
),
|
||||||
(against_voucher_type, against_voucher),
|
(against_voucher_type, against_voucher),
|
||||||
)[0][0]
|
)[0][0]
|
||||||
or 0.0
|
or 0.0
|
||||||
@@ -350,10 +351,12 @@ def update_outstanding_amt(
|
|||||||
elif against_voucher_type == "Journal Entry":
|
elif against_voucher_type == "Journal Entry":
|
||||||
against_voucher_amount = flt(
|
against_voucher_amount = flt(
|
||||||
frappe.db.sql(
|
frappe.db.sql(
|
||||||
f"""
|
"""
|
||||||
select sum(debit_in_account_currency) - sum(credit_in_account_currency)
|
select sum(debit_in_account_currency) - sum(credit_in_account_currency)
|
||||||
from `tabGL Entry` where voucher_type = 'Journal Entry' and voucher_no = %s
|
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),
|
(against_voucher, account),
|
||||||
)[0][0]
|
)[0][0]
|
||||||
)
|
)
|
||||||
@@ -372,9 +375,7 @@ def update_outstanding_amt(
|
|||||||
# Validation : Outstanding can not be negative for JV
|
# Validation : Outstanding can not be negative for JV
|
||||||
if bal < 0 and not on_cancel:
|
if bal < 0 and not on_cancel:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Outstanding for {0} cannot be less than zero ({1})").format(
|
_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal))
|
||||||
against_voucher, fmt_money(bal)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]:
|
if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]:
|
||||||
@@ -390,8 +391,8 @@ def update_outstanding_amt(
|
|||||||
def validate_frozen_account(account, adv_adj=None):
|
def validate_frozen_account(account, adv_adj=None):
|
||||||
frozen_account = frappe.get_cached_value("Account", account, "freeze_account")
|
frozen_account = frappe.get_cached_value("Account", account, "freeze_account")
|
||||||
if frozen_account == "Yes" and not adv_adj:
|
if frozen_account == "Yes" and not adv_adj:
|
||||||
frozen_accounts_modifier = frappe.get_cached_value(
|
frozen_accounts_modifier = frappe.db.get_single_value(
|
||||||
"Accounts Settings", None, "frozen_accounts_modifier"
|
"Accounts Settings", "frozen_accounts_modifier"
|
||||||
)
|
)
|
||||||
|
|
||||||
if not frozen_accounts_modifier:
|
if not frozen_accounts_modifier:
|
||||||
@@ -430,9 +431,8 @@ def update_against_account(voucher_type, voucher_no):
|
|||||||
|
|
||||||
|
|
||||||
def on_doctype_update():
|
def on_doctype_update():
|
||||||
|
frappe.db.add_index("GL Entry", ["against_voucher_type", "against_voucher"])
|
||||||
frappe.db.add_index("GL Entry", ["voucher_type", "voucher_no"])
|
frappe.db.add_index("GL Entry", ["voucher_type", "voucher_no"])
|
||||||
frappe.db.add_index("GL Entry", ["posting_date", "company"])
|
|
||||||
frappe.db.add_index("GL Entry", ["party_type", "party"])
|
|
||||||
|
|
||||||
|
|
||||||
def rename_gle_sle_docs():
|
def rename_gle_sle_docs():
|
||||||
@@ -448,7 +448,7 @@ def rename_temporarily_named_docs(doctype):
|
|||||||
set_name_from_naming_options(frappe.get_meta(doctype).autoname, doc)
|
set_name_from_naming_options(frappe.get_meta(doctype).autoname, doc)
|
||||||
newname = doc.name
|
newname = doc.name
|
||||||
frappe.db.sql(
|
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),
|
(newname, oldname),
|
||||||
auto_commit=True,
|
auto_commit=True,
|
||||||
)
|
)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user