mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-10 00:19:00 +00:00
Compare commits
551 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
927d14e021 | ||
|
|
7bb5313190 | ||
|
|
1d6bb3953d | ||
|
|
78d0a11d09 | ||
|
|
ccf62886e4 | ||
|
|
40747e1b49 | ||
|
|
87f8d66b79 | ||
|
|
d075399ecd | ||
|
|
c4ff9c72dd | ||
|
|
97ccb0eec2 | ||
|
|
2382312d41 | ||
|
|
348d10cad8 | ||
|
|
47f030b1bf | ||
|
|
7e4f8fa04d | ||
|
|
652181600d | ||
|
|
e4f300c903 | ||
|
|
d839c46f10 | ||
|
|
6d45dfd6e8 | ||
|
|
5caa40857d | ||
|
|
78b7f69e0c | ||
|
|
8d9b58d801 | ||
|
|
a611cc5916 | ||
|
|
c6466c7d97 | ||
|
|
b35db7e819 | ||
|
|
44875482ac | ||
|
|
f06cc233a8 | ||
|
|
fdd93cba03 | ||
|
|
10f53f04d7 | ||
|
|
884dc28af6 | ||
|
|
a506930b63 | ||
|
|
f6836c1d89 | ||
|
|
3e2fe12c9d | ||
|
|
5b2ca9e133 | ||
|
|
f8dc7b32d5 | ||
|
|
0393cb5c43 | ||
|
|
4206b52bdc | ||
|
|
9e66c86d6f | ||
|
|
ec46b4d4c6 | ||
|
|
39c71e26b9 | ||
|
|
77285317c0 | ||
|
|
a95c09bef6 | ||
|
|
6b901d0c1a | ||
|
|
8a7ccd9874 | ||
|
|
2f2e748b7a | ||
|
|
549ab68eb0 | ||
|
|
0b9bf4d0ac | ||
|
|
1c5df5dede | ||
|
|
41d2357dc4 | ||
|
|
af79714f40 | ||
|
|
8a5f31da34 | ||
|
|
47a7e3422b | ||
|
|
9efb087e22 | ||
|
|
3d58dd6aa5 | ||
|
|
ac7966cd7e | ||
|
|
61ddb508d4 | ||
|
|
d973fb8823 | ||
|
|
a649414419 | ||
|
|
270f23b966 | ||
|
|
4d745a39e0 | ||
|
|
5e0b5cb283 | ||
|
|
69358cfd40 | ||
|
|
ad1ba2bb76 | ||
|
|
c44abd2f78 | ||
|
|
0bcada8f3c | ||
|
|
a6b110a7d3 | ||
|
|
b4ce43306b | ||
|
|
85a9f55fa7 | ||
|
|
596c0d2060 | ||
|
|
723421f375 | ||
|
|
f9297416f2 | ||
|
|
a8d2916e61 | ||
|
|
0f70f20ec5 | ||
|
|
73d9d8fec9 | ||
|
|
c4756e938f | ||
|
|
faa5d90f1b | ||
|
|
3b37a109c5 | ||
|
|
ab4aa53f52 | ||
|
|
3483426fe7 | ||
|
|
3039094737 | ||
|
|
e21602d432 | ||
|
|
6067ef3c49 | ||
|
|
b78128a7fe | ||
|
|
c8cbb87168 | ||
|
|
aed8aeb83a | ||
|
|
fa47445e11 | ||
|
|
0220acf100 | ||
|
|
0fe8f8e6d9 | ||
|
|
f87b766a26 | ||
|
|
2262e5d443 | ||
|
|
b9cad8d891 | ||
|
|
dafdc6035a | ||
|
|
c8369c10ff | ||
|
|
08b8ed5ca6 | ||
|
|
6cefdb40d1 | ||
|
|
3ad65ec57d | ||
|
|
364332bcfe | ||
|
|
9965f443ee | ||
|
|
d87d644dc2 | ||
|
|
eeab3128aa | ||
|
|
7b2cec8da2 | ||
|
|
6e3cbd8698 | ||
|
|
b5e1f846ce | ||
|
|
f06b31aab4 | ||
|
|
f8502317a9 | ||
|
|
9c7a33405d | ||
|
|
0ee46a624c | ||
|
|
5485d4c435 | ||
|
|
fb2d4884b3 | ||
|
|
3b38a9e418 | ||
|
|
437d3e37d6 | ||
|
|
1b9a55e766 | ||
|
|
a5353a8d04 | ||
|
|
e1bc0a1028 | ||
|
|
92116abd92 | ||
|
|
e5731b3d0f | ||
|
|
a36c4326c3 | ||
|
|
5a0a4bdcff | ||
|
|
d2dc889849 | ||
|
|
dbcdf7e225 | ||
|
|
7fdef64152 | ||
|
|
b2be301988 | ||
|
|
68db5a6792 | ||
|
|
f232965392 | ||
|
|
2ff7197752 | ||
|
|
1d3b5b0437 | ||
|
|
8280d32967 | ||
|
|
4a79072c71 | ||
|
|
721a00cd22 | ||
|
|
8ce7893bf0 | ||
|
|
0f87282aef | ||
|
|
de51217a80 | ||
|
|
bc01dafbba | ||
|
|
a985bfc29a | ||
|
|
08dbb13451 | ||
|
|
170dd37b86 | ||
|
|
b1744972e0 | ||
|
|
7aa993b14b | ||
|
|
ad89cb0ef3 | ||
|
|
a185b51f0d | ||
|
|
1ff81bca7d | ||
|
|
b02db3c3e8 | ||
|
|
d1d835347e | ||
|
|
ba245553f0 | ||
|
|
112902a9c4 | ||
|
|
3f4cdcc7a4 | ||
|
|
91f81b07d9 | ||
|
|
66e92041b9 | ||
|
|
f2dc89c7a0 | ||
|
|
10b226af10 | ||
|
|
115887a886 | ||
|
|
a1f5c22da7 | ||
|
|
712c01d5bd | ||
|
|
bd4e296336 | ||
|
|
b52d81ef6a | ||
|
|
15432c76a4 | ||
|
|
dd3cd6e2ba | ||
|
|
5889a29656 | ||
|
|
0e7ca6ada7 | ||
|
|
5a2ea528e8 | ||
|
|
8957093439 | ||
|
|
507c435dee | ||
|
|
8889edad72 | ||
|
|
e27d1aebd5 | ||
|
|
ef567eac37 | ||
|
|
f8e0a2204e | ||
|
|
baecc7fd31 | ||
|
|
529b465337 | ||
|
|
675e2670b2 | ||
|
|
3919fd10ad | ||
|
|
b4e61e19bb | ||
|
|
85be3ec796 | ||
|
|
aa3d6ed11e | ||
|
|
382735ad2f | ||
|
|
8d42b75e29 | ||
|
|
7e03e046ec | ||
|
|
bfddc8aba1 | ||
|
|
d0dafcc46f | ||
|
|
43a0161257 | ||
|
|
f8996b64f1 | ||
|
|
867443ea6d | ||
|
|
acdc230ef3 | ||
|
|
86232e4449 | ||
|
|
78c7f03731 | ||
|
|
65b99a5109 | ||
|
|
8ec290d5ec | ||
|
|
65a259eb81 | ||
|
|
47420578ae | ||
|
|
35d8cd9f10 | ||
|
|
1e55ac6a4f | ||
|
|
ce60dc3ea3 | ||
|
|
ff7a5d18d1 | ||
|
|
5a15dcc060 | ||
|
|
cec6c87e09 | ||
|
|
b2870b9426 | ||
|
|
e27600066c | ||
|
|
5b8c624b34 | ||
|
|
9e8531d68e | ||
|
|
ebeb137351 | ||
|
|
8c7d28a41a | ||
|
|
d90b80af86 | ||
|
|
b62e17f7f2 | ||
|
|
5169dc1566 | ||
|
|
d3afecd1d9 | ||
|
|
ec56984e12 | ||
|
|
1c26623f22 | ||
|
|
02cb668f22 | ||
|
|
bd749dce18 | ||
|
|
737a504a35 | ||
|
|
c719f842bc | ||
|
|
0d19139acf | ||
|
|
80973bb8de | ||
|
|
a9a7e84873 | ||
|
|
b23da53fee | ||
|
|
78776c42fe | ||
|
|
2d9908d22f | ||
|
|
3b0695e0ca | ||
|
|
a9695f04de | ||
|
|
a2398775cd | ||
|
|
f2544ec7f2 | ||
|
|
778d7b9cbd | ||
|
|
2a3c62e7f6 | ||
|
|
5c576e4365 | ||
|
|
c57753a234 | ||
|
|
7cabcdd9a2 | ||
|
|
2b0c845cb8 | ||
|
|
71e51714f2 | ||
|
|
d37a30fc47 | ||
|
|
62767ced3e | ||
|
|
cf71643685 | ||
|
|
d225f5993b | ||
|
|
3dc8941be7 | ||
|
|
dbebec8fcc | ||
|
|
4111965d2e | ||
|
|
3ac54a0ba6 | ||
|
|
143f007272 | ||
|
|
3fc10a8da1 | ||
|
|
ed1e28b8fa | ||
|
|
71145c699f | ||
|
|
ea6d90988f | ||
|
|
6efe4acb1d | ||
|
|
ad2365502f | ||
|
|
9c428ebfba | ||
|
|
b53e4404dc | ||
|
|
0bc849975c | ||
|
|
690139ca11 | ||
|
|
c0a21ad96a | ||
|
|
8c9fd62775 | ||
|
|
0be054ae0f | ||
|
|
912cf9d555 | ||
|
|
7fe0a2023a | ||
|
|
cf191c0483 | ||
|
|
bb8d9f2a57 | ||
|
|
fa4577d18a | ||
|
|
993521d365 | ||
|
|
3372b7411d | ||
|
|
5dbfeb8557 | ||
|
|
f5d2337176 | ||
|
|
67aa7b8735 | ||
|
|
aea67c9843 | ||
|
|
f64aaf5e1c | ||
|
|
00f6fd827e | ||
|
|
ea2cdf8c12 | ||
|
|
29729c8d31 | ||
|
|
536e24c6a1 | ||
|
|
f178dc3b26 | ||
|
|
bc2f311a41 | ||
|
|
5c28aba872 | ||
|
|
f800050b71 | ||
|
|
fea996303f | ||
|
|
833e017111 | ||
|
|
fb4f73605a | ||
|
|
94dfcce434 | ||
|
|
18e5bc1976 | ||
|
|
7e54e6acb8 | ||
|
|
0e52951db8 | ||
|
|
1b050e2dfd | ||
|
|
5674e19e71 | ||
|
|
c49735ed15 | ||
|
|
d1c467e49c | ||
|
|
e14df31c05 | ||
|
|
9ff9f21af0 | ||
|
|
eba361b27d | ||
|
|
cc960806a9 | ||
|
|
75c47d64ff | ||
|
|
300307b22f | ||
|
|
43ebcb5876 | ||
|
|
f254d8c5ee | ||
|
|
8811a8c9b6 | ||
|
|
4f1b88ee0f | ||
|
|
51a7aa9c76 | ||
|
|
33ce5ce048 | ||
|
|
b4a570242e | ||
|
|
481977d368 | ||
|
|
479b2bbbd9 | ||
|
|
4faf57869d | ||
|
|
33eb38cc1b | ||
|
|
c6f72f7c90 | ||
|
|
d082a12411 | ||
|
|
096de8a0d5 | ||
|
|
5638d63866 | ||
|
|
a621466219 | ||
|
|
6155114bfd | ||
|
|
2ca33d4f51 | ||
|
|
f1618af76c | ||
|
|
9f7098115b | ||
|
|
55b8b4e374 | ||
|
|
7cb8c51b2d | ||
|
|
311fe5b3dc | ||
|
|
269868130f | ||
|
|
736eed01c2 | ||
|
|
e014b898bd | ||
|
|
1eb31210e0 | ||
|
|
8d72aa99aa | ||
|
|
3a4503b7a1 | ||
|
|
01952bbc15 | ||
|
|
1ca6365dee | ||
|
|
a7a810a25f | ||
|
|
e62fba062e | ||
|
|
60b7acc9b0 | ||
|
|
d7ddb0c14e | ||
|
|
8d84ffb276 | ||
|
|
784c3333d7 | ||
|
|
2227f54e66 | ||
|
|
c55657dca5 | ||
|
|
b6b2c48340 | ||
|
|
582c86a7b6 | ||
|
|
76668e9004 | ||
|
|
d0426a6127 | ||
|
|
4252575e84 | ||
|
|
f6dcd8ac91 | ||
|
|
6a9fb51dc5 | ||
|
|
70b93e2cd4 | ||
|
|
f4493674f7 | ||
|
|
dd5a818d6a | ||
|
|
e78dfb5df6 | ||
|
|
429d0dcda0 | ||
|
|
eb174e1afa | ||
|
|
4a6b5367ca | ||
|
|
3675e616bc | ||
|
|
f9d3df9531 | ||
|
|
081d312a11 | ||
|
|
355e832b02 | ||
|
|
6b65e2b168 | ||
|
|
57041084d1 | ||
|
|
cf4c7b406a | ||
|
|
edb3cdbb59 | ||
|
|
55025d4dc9 | ||
|
|
6f7a5e525d | ||
|
|
342c77c851 | ||
|
|
94f4d81b70 | ||
|
|
74f31c6677 | ||
|
|
7589b9f822 | ||
|
|
b761169976 | ||
|
|
537c3a79ab | ||
|
|
0510988b3e | ||
|
|
79c389a48f | ||
|
|
239779dad0 | ||
|
|
37a7ab9d0e | ||
|
|
17ea15eb91 | ||
|
|
ddaf81051e | ||
|
|
d06f3882e9 | ||
|
|
523994c2cf | ||
|
|
00219ab7f0 | ||
|
|
0c4f1e5e67 | ||
|
|
29e3cfd60f | ||
|
|
95058eac21 | ||
|
|
4b963be8c4 | ||
|
|
a4434b1a50 | ||
|
|
d3dc241878 | ||
|
|
012e617c55 | ||
|
|
bee135c8c6 | ||
|
|
8a19cd1096 | ||
|
|
5b14ca081c | ||
|
|
4fbf03c760 | ||
|
|
503d1d3291 | ||
|
|
e2108d75dc | ||
|
|
9a009a1d1c | ||
|
|
61211486bc | ||
|
|
42fd7451bf | ||
|
|
70a1c1bdac | ||
|
|
eb6d74217c | ||
|
|
172a8f1cea | ||
|
|
164057fa5b | ||
|
|
3c21ac6bb6 | ||
|
|
ad2585e1b7 | ||
|
|
09fe604452 | ||
|
|
28d836cfea | ||
|
|
8005f350ae | ||
|
|
3168ae7dcc | ||
|
|
447b8dab96 | ||
|
|
6408949d43 | ||
|
|
5fe2272861 | ||
|
|
7165a5d1f8 | ||
|
|
2ec21fa9fb | ||
|
|
a162f560dd | ||
|
|
c3c168a70d | ||
|
|
b5ac3b8a53 | ||
|
|
30452a8bcd | ||
|
|
36676effd9 | ||
|
|
f31ff74a75 | ||
|
|
4298cc1cdb | ||
|
|
abe5a5ae30 | ||
|
|
9daaeaed07 | ||
|
|
926150bccb | ||
|
|
e46d10676e | ||
|
|
631e8bb9dd | ||
|
|
13d6f4b291 | ||
|
|
bc72fdff7a | ||
|
|
e8148112fc | ||
|
|
a1dd861096 | ||
|
|
aeb0b008fa | ||
|
|
4c711ed62c | ||
|
|
7620742299 | ||
|
|
8a5bd02662 | ||
|
|
565936442d | ||
|
|
a8e1c90035 | ||
|
|
a953b68a17 | ||
|
|
c9c48d3736 | ||
|
|
d1d679843e | ||
|
|
c2b7084ee8 | ||
|
|
07bbe8568f | ||
|
|
2a58b6c20b | ||
|
|
196bf2104f | ||
|
|
a33145e6d9 | ||
|
|
6161d3362d | ||
|
|
fac8ed0630 | ||
|
|
3279ad55f6 | ||
|
|
9bdb04d7ca | ||
|
|
89270ef2c0 | ||
|
|
c0ea006571 | ||
|
|
de3b79cce8 | ||
|
|
f326d5c9a1 | ||
|
|
e112ac2345 | ||
|
|
ad1719bbe4 | ||
|
|
f2c464dda8 | ||
|
|
2b64b38a6e | ||
|
|
dcbafecf58 | ||
|
|
20229b915c | ||
|
|
7a1a0fb5fb | ||
|
|
fddef7bd5c | ||
|
|
804854f051 | ||
|
|
bdb48f34ed | ||
|
|
986b5115da | ||
|
|
b37f4a157c | ||
|
|
f650b287ef | ||
|
|
1866cebd14 | ||
|
|
bb5bf06e0c | ||
|
|
ecf8e20462 | ||
|
|
5a0bc043aa | ||
|
|
a2c9c9505c | ||
|
|
f6060fa5f2 | ||
|
|
b4cf29391b | ||
|
|
a6a6f69e26 | ||
|
|
db38c02aee | ||
|
|
e107d430eb | ||
|
|
0c849ac0f3 | ||
|
|
6b86bb462b | ||
|
|
80cfc88abb | ||
|
|
3c425584a7 | ||
|
|
af561becc6 | ||
|
|
0ef08618c9 | ||
|
|
373d90f802 | ||
|
|
ceaedd0f95 | ||
|
|
fd7c4e9822 | ||
|
|
26878d907e | ||
|
|
ae3c21046b | ||
|
|
f01baaa56f | ||
|
|
ac534c64bc | ||
|
|
6e8e42ca7c | ||
|
|
7d3d99a9b8 | ||
|
|
9a858a9824 | ||
|
|
5792d6037c | ||
|
|
75310637da | ||
|
|
6fa4697efb | ||
|
|
63e83cc005 | ||
|
|
68d7d6e223 | ||
|
|
7ee97b6128 | ||
|
|
49fadcbaf2 | ||
|
|
e6ab86e185 | ||
|
|
35fe0a6cbd | ||
|
|
d287fe4dae | ||
|
|
683d987432 | ||
|
|
37d0c75f04 | ||
|
|
6940a6cd47 | ||
|
|
34867ced92 | ||
|
|
5c482e907e | ||
|
|
3ebc13f228 | ||
|
|
b5882aaa6c | ||
|
|
7543916da6 | ||
|
|
11d02c028f | ||
|
|
a72ef5eceb | ||
|
|
3e9ddf23e1 | ||
|
|
6fc6f385fe | ||
|
|
ba62173561 | ||
|
|
709f21692c | ||
|
|
4dd48f46f3 | ||
|
|
e59836e3da | ||
|
|
cc80be449d | ||
|
|
29433f2b1b | ||
|
|
bdeaf4880f | ||
|
|
53e3674924 | ||
|
|
6f2f8ae85e | ||
|
|
48474a2d4e | ||
|
|
3e6df7f0bb | ||
|
|
47bca7f651 | ||
|
|
fb59d770ba | ||
|
|
ad99934b4c | ||
|
|
2a0be68cb3 | ||
|
|
a7da7226ac | ||
|
|
505332e680 | ||
|
|
ada3330403 | ||
|
|
e0143e081c | ||
|
|
84d3d544ef | ||
|
|
30c9c56d6c | ||
|
|
dfe0a9002a | ||
|
|
3eea9e3967 | ||
|
|
69051c4197 | ||
|
|
5b5156356a | ||
|
|
fdd1422d31 | ||
|
|
c82253e479 | ||
|
|
b059c6f630 | ||
|
|
f03522ad9e | ||
|
|
42e62bd1b4 | ||
|
|
2ff9c385c3 | ||
|
|
804b80ac7b | ||
|
|
25eff13d35 | ||
|
|
415c90d9d0 | ||
|
|
97a49185c5 | ||
|
|
e68d4c897b | ||
|
|
76640cbad6 | ||
|
|
c2e5e569a0 | ||
|
|
d5384a110d | ||
|
|
4e9f1a08fe | ||
|
|
e20e38d530 | ||
|
|
b3dec74c6a | ||
|
|
cf77740ab1 | ||
|
|
f80be2d1f4 | ||
|
|
1bd60ac343 | ||
|
|
2fc2d9b8ad | ||
|
|
839b653f6b | ||
|
|
61cfa275f8 | ||
|
|
7319bcb577 | ||
|
|
3750a06d4f | ||
|
|
c79c8dae19 | ||
|
|
1e4e2c81e9 | ||
|
|
334106a6d0 | ||
|
|
eba2455ad3 | ||
|
|
63267d3616 | ||
|
|
5ed80a5ba9 | ||
|
|
98e5692a40 | ||
|
|
bc293cf251 |
14
.github/workflows/docker-release.yml
vendored
Normal file
14
.github/workflows/docker-release.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
name: Trigger Docker build on release
|
||||
on:
|
||||
release:
|
||||
types: [released]
|
||||
jobs:
|
||||
curl:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: alpine:latest
|
||||
steps:
|
||||
- name: curl
|
||||
run: |
|
||||
apk add curl bash
|
||||
curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token ${{ secrets.TRAVIS_CI_TOKEN }}" -d '{"request":{"branch":"master"}}' https://api.travis-ci.com/repo/frappe%2Ffrappe_docker/requests
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -13,4 +13,5 @@ erpnext/docs/current
|
||||
__pycache__
|
||||
*~
|
||||
.vscode/
|
||||
node_modules/
|
||||
node_modules/
|
||||
.idea/
|
||||
102
.travis.yml
102
.travis.yml
@@ -1,51 +1,81 @@
|
||||
language: python
|
||||
dist: trusty
|
||||
|
||||
python:
|
||||
- "2.7"
|
||||
- "3.6"
|
||||
language: python
|
||||
|
||||
env:
|
||||
- TEST_TYPE="Server Side Test"
|
||||
- TEST_TYPE="Patch Test"
|
||||
git:
|
||||
depth: 1
|
||||
|
||||
services:
|
||||
- mysql
|
||||
cache:
|
||||
- pip
|
||||
|
||||
addons:
|
||||
hosts: test_site
|
||||
mariadb: 10.3
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- name: "Python 2.7 Server Side Test"
|
||||
python: 2.7
|
||||
script: bench --site test_site run-tests --app erpnext --coverage
|
||||
|
||||
- name: "Python 3.6 Server Side Test"
|
||||
python: 3.6
|
||||
script: bench --site test_site run-tests --app erpnext --coverage
|
||||
|
||||
- name: "Python 2.7 Patch Test"
|
||||
python: 2.7
|
||||
before_script:
|
||||
- wget http://build.erpnext.com/20171108_190013_955977f8_database.sql.gz
|
||||
- bench --site test_site --force restore ~/frappe-bench/20171108_190013_955977f8_database.sql.gz
|
||||
script: bench --site test_site migrate
|
||||
|
||||
- name: "Python 3.6 Patch Test"
|
||||
python: 3.6
|
||||
before_script:
|
||||
- wget http://build.erpnext.com/20171108_190013_955977f8_database.sql.gz
|
||||
- bench --site test_site --force restore ~/frappe-bench/20171108_190013_955977f8_database.sql.gz
|
||||
script: bench --site test_site migrate
|
||||
|
||||
install:
|
||||
# fix mongodb travis error
|
||||
- sudo rm /etc/apt/sources.list.d/mongodb*.list
|
||||
- pip install flake8==3.3.0
|
||||
- flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics
|
||||
- sudo rm /etc/apt/sources.list.d/docker.list
|
||||
- sudo apt-get install hhvm && rm -rf /home/travis/.kiex/
|
||||
- sudo apt-get purge -y mysql-common mysql-server mysql-client
|
||||
- cd ~
|
||||
- nvm install 10
|
||||
- pip install python-coveralls
|
||||
- wget https://raw.githubusercontent.com/frappe/bench/master/playbooks/install.py
|
||||
- sudo python install.py --develop --user travis --without-bench-setup
|
||||
- sudo pip install -e ~/bench
|
||||
|
||||
- rm $TRAVIS_BUILD_DIR/.git/shallow
|
||||
- bash $TRAVIS_BUILD_DIR/travis/bench_init.sh
|
||||
- cp -r $TRAVIS_BUILD_DIR/test_sites/test_site ~/frappe-bench/sites/
|
||||
- git clone https://github.com/frappe/bench --depth 1
|
||||
- pip install -e ./bench
|
||||
|
||||
before_script:
|
||||
- mysql -u root -ptravis -e 'create database test_frappe'
|
||||
- echo "USE mysql;\nCREATE USER 'test_frappe'@'localhost' IDENTIFIED BY 'test_frappe';\nFLUSH PRIVILEGES;\n" | mysql -u root -ptravis
|
||||
- echo "USE mysql;\nGRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost';\n" | mysql -u root -ptravis
|
||||
- git clone https://github.com/frappe/frappe --branch $TRAVIS_BRANCH --depth 1
|
||||
- bench init --skip-assets --frappe-path ~/frappe --python $(which python) frappe-bench
|
||||
|
||||
- mkdir ~/frappe-bench/sites/test_site
|
||||
- cp -r $TRAVIS_BUILD_DIR/.travis/site_config.json ~/frappe-bench/sites/test_site/
|
||||
|
||||
- mysql -u root -e "SET GLOBAL character_set_server = 'utf8mb4'"
|
||||
- mysql -u root -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'"
|
||||
|
||||
- mysql -u root -e "CREATE DATABASE test_frappe"
|
||||
- mysql -u root -e "CREATE USER 'test_frappe'@'localhost' IDENTIFIED BY 'test_frappe'"
|
||||
- mysql -u root -e "GRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost'"
|
||||
|
||||
- mysql -u root -e "UPDATE mysql.user SET Password=PASSWORD('travis') WHERE User='root'"
|
||||
- mysql -u root -e "FLUSH PRIVILEGES"
|
||||
|
||||
- wget -O /tmp/wkhtmltox.tar.xz https://github.com/frappe/wkhtmltopdf/raw/master/wkhtmltox-0.12.3_linux-generic-amd64.tar.xz
|
||||
- tar -xf /tmp/wkhtmltox.tar.xz -C /tmp
|
||||
- sudo mv /tmp/wkhtmltox/bin/wkhtmltopdf /usr/local/bin/wkhtmltopdf
|
||||
- sudo chmod o+x /usr/local/bin/wkhtmltopdf
|
||||
|
||||
- cd ~/frappe-bench
|
||||
- bench get-app erpnext $TRAVIS_BUILD_DIR
|
||||
- bench use test_site
|
||||
- bench reinstall --mariadb-root-username root --mariadb-root-password travis --yes
|
||||
- bench scheduler disable
|
||||
- sed -i 's/9000/9001/g' sites/common_site_config.json
|
||||
- bench start &
|
||||
- sleep 10
|
||||
|
||||
script:
|
||||
- bash $TRAVIS_BUILD_DIR/travis/run-tests.sh
|
||||
- sed -i 's/watch:/# watch:/g' Procfile
|
||||
- sed -i 's/schedule:/# schedule:/g' Procfile
|
||||
- sed -i 's/socketio:/# socketio:/g' Procfile
|
||||
- sed -i 's/redis_socketio:/# redis_socketio:/g' Procfile
|
||||
|
||||
- bench get-app erpnext $TRAVIS_BUILD_DIR
|
||||
- bench start &
|
||||
- bench --site test_site reinstall --yes
|
||||
|
||||
after_script:
|
||||
- pip install coverage==4.5.4
|
||||
- pip install python-coveralls
|
||||
- coveralls -b apps/erpnext -d ../../sites/.coverage
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
"mail_login": "test@example.com",
|
||||
"mail_password": "test",
|
||||
"admin_password": "admin",
|
||||
"run_selenium_tests": 1,
|
||||
"host_name": "http://localhost:8000",
|
||||
"root_login": "root",
|
||||
"root_password": "travis",
|
||||
"host_name": "http://test_site:8000",
|
||||
"install_apps": ["erpnext"]
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<div align="center">
|
||||
<img src="https://github.com/frappe/design/blob/master/logos/erpnext-logo.svg" height="128">
|
||||
<img src="https://github.com/frappe/design/blob/master/logos/logo-2019/erpnext-logo.png" height="128">
|
||||
<h2>ERPNext</h2>
|
||||
<p align="center">
|
||||
<p>ERP made simple</p>
|
||||
|
||||
@@ -5,7 +5,7 @@ import frappe
|
||||
from erpnext.hooks import regional_overrides
|
||||
from frappe.utils import getdate
|
||||
|
||||
__version__ = '11.1.41'
|
||||
__version__ = '11.1.77'
|
||||
|
||||
def get_default_company(user=None):
|
||||
'''Get default company for user'''
|
||||
@@ -144,4 +144,4 @@ def is_member():
|
||||
last_membership = get_last_membership()
|
||||
if last_membership and getdate(last_membership.to_date) > getdate():
|
||||
return True
|
||||
return False
|
||||
return False
|
||||
|
||||
@@ -174,6 +174,8 @@ def make_gl_entries(doc, credit_account, debit_account, against,
|
||||
# GL Entry for crediting the amount in the deferred expense
|
||||
from erpnext.accounts.general_ledger import make_gl_entries
|
||||
|
||||
if amount == 0: return
|
||||
|
||||
gl_entries = []
|
||||
gl_entries.append(
|
||||
doc.get_gl_dict({
|
||||
|
||||
@@ -100,7 +100,10 @@ class Account(NestedSet):
|
||||
if ancestors:
|
||||
if frappe.get_value("Company", self.company, "allow_account_creation_against_child_company"):
|
||||
return
|
||||
frappe.throw(_("Please add the account to root level Company - %s" % ancestors[0]))
|
||||
|
||||
if not frappe.db.get_value("Account",
|
||||
{'account_name': self.account_name, 'company': ancestors[0]}, 'name'):
|
||||
frappe.throw(_("Please add the account to root level Company - %s" % ancestors[0]))
|
||||
else:
|
||||
descendants = get_descendants_of('Company', self.company)
|
||||
if not descendants: return
|
||||
@@ -114,21 +117,7 @@ class Account(NestedSet):
|
||||
|
||||
if not parent_acc_name_map: return
|
||||
|
||||
for company in descendants:
|
||||
if not parent_acc_name_map.get(company):
|
||||
frappe.throw(_("While creating account for child Company {0}, parent account {1} not found. Please create the parent account in corresponding COA")
|
||||
.format(company, parent_acc_name))
|
||||
|
||||
doc = frappe.copy_doc(self)
|
||||
doc.flags.ignore_root_company_validation = True
|
||||
doc.update({
|
||||
"company": company,
|
||||
"account_currency": None,
|
||||
"parent_account": parent_acc_name_map[company]
|
||||
})
|
||||
doc.save()
|
||||
frappe.msgprint(_("Account {0} is added in the child company {1}")
|
||||
.format(doc.name, company))
|
||||
self.create_account_for_child_company(parent_acc_name_map, descendants, parent_acc_name)
|
||||
|
||||
def validate_group_or_ledger(self):
|
||||
if self.get("__islocal"):
|
||||
@@ -170,6 +159,49 @@ class Account(NestedSet):
|
||||
if frappe.db.get_value("GL Entry", {"account": self.name}):
|
||||
frappe.throw(_("Currency can not be changed after making entries using some other currency"))
|
||||
|
||||
def create_account_for_child_company(self, parent_acc_name_map, descendants, parent_acc_name):
|
||||
for company in descendants:
|
||||
if not parent_acc_name_map.get(company):
|
||||
frappe.throw(_("While creating account for child Company {0}, parent account {1} not found. Please create the parent account in corresponding COA")
|
||||
.format(company, parent_acc_name))
|
||||
|
||||
filters = {
|
||||
"account_name": self.account_name,
|
||||
"company": company
|
||||
}
|
||||
|
||||
if self.account_number:
|
||||
filters["account_number"] = self.account_number
|
||||
|
||||
child_account = frappe.db.get_value("Account", filters, 'name')
|
||||
|
||||
if not child_account:
|
||||
doc = frappe.copy_doc(self)
|
||||
doc.flags.ignore_root_company_validation = True
|
||||
doc.update({
|
||||
"company": company,
|
||||
# parent account's currency should be passed down to child account's curreny
|
||||
# if it is None, it picks it up from default company currency, which might be unintended
|
||||
"account_currency": self.account_currency,
|
||||
"parent_account": parent_acc_name_map[company]
|
||||
})
|
||||
|
||||
doc.save()
|
||||
frappe.msgprint(_("Account {0} is added in the child company {1}")
|
||||
.format(doc.name, company))
|
||||
elif child_account:
|
||||
# update the parent company's value in child companies
|
||||
doc = frappe.get_doc("Account", child_account)
|
||||
parent_value_changed = False
|
||||
for field in ['account_type', 'account_currency',
|
||||
'freeze_account', 'balance_must_be']:
|
||||
if doc.get(field) != self.get(field):
|
||||
parent_value_changed = True
|
||||
doc.set(field, self.get(field))
|
||||
|
||||
if parent_value_changed:
|
||||
doc.save()
|
||||
|
||||
def convert_group_to_ledger(self):
|
||||
if self.check_if_child_exists():
|
||||
throw(_("Account with child nodes cannot be converted to ledger"))
|
||||
|
||||
@@ -121,7 +121,11 @@ frappe.treeview_settings["Account"] = {
|
||||
},
|
||||
onrender: function(node) {
|
||||
if(frappe.boot.user.can_read.indexOf("GL Entry") !== -1){
|
||||
var dr_or_cr = node.data.balance < 0 ? "Cr" : "Dr";
|
||||
|
||||
// show Dr if positive since balance is calculated as debit - credit else show Cr
|
||||
let balance = node.data.balance_in_account_currency || node.data.balance;
|
||||
let dr_or_cr = balance > 0 ? "Dr": "Cr";
|
||||
|
||||
if (node.data && node.data.balance!==undefined) {
|
||||
$('<span class="balance-area pull-right text-muted small">'
|
||||
+ (node.data.balance_in_account_currency ?
|
||||
|
||||
@@ -1,465 +1,466 @@
|
||||
{
|
||||
"country_code": "ae",
|
||||
"name": "U.A.E - Chart of Accounts",
|
||||
"country_code": "ae",
|
||||
"name": "U.A.E - Chart of Accounts",
|
||||
"tree": {
|
||||
"Assets": {
|
||||
"Current Assets": {
|
||||
"Accounts Receivable": {
|
||||
"Corporate Credit Cards": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
},
|
||||
"Other Receivable": {
|
||||
"Accrued Rebates Due from Suppliers": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Accured Income from Suppliers": {
|
||||
},
|
||||
"Accrued Income from Suppliers": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
},
|
||||
"Other Debtors": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
},
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
},
|
||||
"Post Dated Cheques Received": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
},
|
||||
"Staff Receivable": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
},
|
||||
"Trade Receivable": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
},
|
||||
"Trade in Opening Fees": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
},
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
},
|
||||
"Cash in Hand & Banks": {
|
||||
"Banks": {
|
||||
"Bank Margin On LC & LG": {},
|
||||
"Banks Blocked Deposits": {},
|
||||
"Banks Call Deposit Accounts": {},
|
||||
"Bank Margin On LC & LG": {},
|
||||
"Banks Blocked Deposits": {},
|
||||
"Banks Call Deposit Accounts": {},
|
||||
"Banks Current Accounts": {
|
||||
"account_type": "Bank"
|
||||
},
|
||||
},
|
||||
"account_type": "Bank"
|
||||
},
|
||||
},
|
||||
"Cash in Hand": {
|
||||
"Cash in Safe": {
|
||||
"Main Safe": {
|
||||
"account_type": "Cash"
|
||||
},
|
||||
},
|
||||
"Main Safe - Foreign Currency": {
|
||||
"account_type": "Cash"
|
||||
}
|
||||
},
|
||||
},
|
||||
"Petty Cash": {
|
||||
"Petty Cash - Admininistration": {
|
||||
"account_type": "Cash"
|
||||
},
|
||||
},
|
||||
"Petty Cash - Others": {
|
||||
"account_type": "Cash"
|
||||
}
|
||||
},
|
||||
},
|
||||
"account_type": "Cash"
|
||||
},
|
||||
},
|
||||
"Cash in Transit": {
|
||||
"Credit Cards": {
|
||||
"Gateway Credit Cards": {
|
||||
"account_type": "Bank"
|
||||
},
|
||||
},
|
||||
"Manual Visa & Master Cards": {
|
||||
"account_type": "Bank"
|
||||
},
|
||||
},
|
||||
"PayPal Account": {
|
||||
"account_type": "Bank"
|
||||
},
|
||||
},
|
||||
"Visa & Master Credit Cards": {
|
||||
"account_type": "Bank"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
"Inventory": {
|
||||
"Consigned Stock": {
|
||||
"Handling Difference in Inventory": {
|
||||
"account_type": "Stock Adjustment"
|
||||
},
|
||||
"Items Delivered to Customs on temprary Base": {}
|
||||
},
|
||||
"Handling Difference in Inventory": {},
|
||||
"Items Delivered to Customs on temporary Base": {}
|
||||
},
|
||||
"Stock in Hand": {
|
||||
"account_type": "Stock"
|
||||
}
|
||||
},
|
||||
"Perliminary and Preoperating Expenses": {
|
||||
},
|
||||
"Preliminary and Preoperating Expenses": {
|
||||
"Preoperating Expenses": {}
|
||||
},
|
||||
},
|
||||
"Prepayments & Deposits": {
|
||||
"Deposits": {
|
||||
"Deposit - Office Rent": {},
|
||||
"Deposit Others": {},
|
||||
"Deposit to Immigration (Visa)": {},
|
||||
"Deposit - Office Rent": {},
|
||||
"Deposit Others": {},
|
||||
"Deposit to Immigration (Visa)": {},
|
||||
"Deposits - Customs": {}
|
||||
},
|
||||
},
|
||||
"Prepaid Taxes": {
|
||||
"Sales Taxes Receivables": {},
|
||||
"Sales Taxes Receivables": {},
|
||||
"Withholding Tax Receivables": {}
|
||||
},
|
||||
},
|
||||
"Prepayments": {
|
||||
"Other Prepayments": {},
|
||||
"PrePaid Advertisement Expenses": {},
|
||||
"Prepaid Bank Guarantee": {},
|
||||
"Prepaid Consultancy Fees": {},
|
||||
"Prepaid Employees Housing": {},
|
||||
"Prepaid Finance charge for Loans": {},
|
||||
"Prepaid Legal Fees": {},
|
||||
"Prepaid License Fees": {},
|
||||
"Prepaid Life Insurance": {},
|
||||
"Prepaid Maintenance": {},
|
||||
"Prepaid Medical Insurance": {},
|
||||
"Prepaid Office Rent": {},
|
||||
"Prepaid Other Insurance": {},
|
||||
"Prepaid Schooling Fees": {},
|
||||
"Prepaid Site Hosting Fees": {},
|
||||
"Other Prepayments": {},
|
||||
"PrePaid Advertisement Expenses": {},
|
||||
"Prepaid Bank Guarantee": {},
|
||||
"Prepaid Consultancy Fees": {},
|
||||
"Prepaid Employees Housing": {},
|
||||
"Prepaid Finance charge for Loans": {},
|
||||
"Prepaid Legal Fees": {},
|
||||
"Prepaid License Fees": {},
|
||||
"Prepaid Life Insurance": {},
|
||||
"Prepaid Maintenance": {},
|
||||
"Prepaid Medical Insurance": {},
|
||||
"Prepaid Office Rent": {},
|
||||
"Prepaid Other Insurance": {},
|
||||
"Prepaid Schooling Fees": {},
|
||||
"Prepaid Site Hosting Fees": {},
|
||||
"Prepaid Sponsorship Fees": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
"Long Term Assets": {
|
||||
"Fixed Assets": {
|
||||
"Accumulated Depreciation": {
|
||||
"Acc. Depreciation of Motor Vehicles": {
|
||||
"account_type": "Accumulated Depreciation"
|
||||
},
|
||||
},
|
||||
"Acc. Deprn.Computer Hardware & Software": {
|
||||
"account_type": "Accumulated Depreciation"
|
||||
},
|
||||
},
|
||||
"Acc.Deprn.of Furniture & Office Equipment": {
|
||||
"account_type": "Accumulated Depreciation"
|
||||
},
|
||||
},
|
||||
"Amortisation on Leasehold Improvement": {
|
||||
"account_type": "Accumulated Depreciation"
|
||||
},
|
||||
},
|
||||
"account_type": "Accumulated Depreciation"
|
||||
},
|
||||
},
|
||||
"Fixed Assets (Cost Price)": {
|
||||
"Computer Hardware & Software": {
|
||||
"account_type": "Fixed Asset"
|
||||
},
|
||||
},
|
||||
"Furniture and Equipment": {
|
||||
"account_type": "Fixed Asset"
|
||||
},
|
||||
"Leasehold Improvement": {},
|
||||
"Motor Vehicules": {
|
||||
},
|
||||
"Leasehold Improvement": {},
|
||||
"Motor Vehicles": {
|
||||
"account_type": "Fixed Asset"
|
||||
},
|
||||
"Work In Progrees": {},
|
||||
},
|
||||
"Work In Progress": {},
|
||||
"account_type": "Fixed Asset"
|
||||
}
|
||||
},
|
||||
},
|
||||
"Intangible Assets": {
|
||||
"Computer Card Renewal": {},
|
||||
"Dispoal of Outlets": {},
|
||||
"Computer Card Renewal": {},
|
||||
"Disposal of Outlets": {},
|
||||
"Registration of Trademarks": {}
|
||||
},
|
||||
"Intercompany Accounts": {},
|
||||
},
|
||||
"Intercompany Accounts": {},
|
||||
"Investments": {
|
||||
"Investments in Subsidiaries": {}
|
||||
}
|
||||
},
|
||||
},
|
||||
"root_type": "Asset"
|
||||
},
|
||||
},
|
||||
"Closing And Temporary Accounts": {
|
||||
"Closing Accounts": {
|
||||
"Closing Account": {}
|
||||
},
|
||||
},
|
||||
"root_type": "Liability"
|
||||
},
|
||||
},
|
||||
"Expenses": {
|
||||
"Commercial Expenses": {
|
||||
"Consultancy Fees": {},
|
||||
"Consultancy Fees": {},
|
||||
"Provision for Doubtful Debts": {}
|
||||
},
|
||||
},
|
||||
"Cost of Sale": {
|
||||
"Cost Of Goods Sold": {
|
||||
"Cost Of Goods Sold I/C Sales": {},
|
||||
"Cost Of Goods Sold I/C Sales": {},
|
||||
"Cost of Goods Sold in Trading": {
|
||||
"account_type": "Cost of Goods Sold"
|
||||
},
|
||||
},
|
||||
"account_type": "Cost of Goods Sold"
|
||||
},
|
||||
},
|
||||
"Expenses Included In Valuation": {
|
||||
"account_type": "Expenses Included In Valuation"
|
||||
},
|
||||
"Stock Adjustment": {
|
||||
"account_type": "Stock Adjustment"
|
||||
}
|
||||
},
|
||||
},
|
||||
"Depreciation": {
|
||||
"Depreciation & Amortization": {
|
||||
"Amortization on Leasehold Improvement": {},
|
||||
"Amortization on Leasehold Improvement": {},
|
||||
"Depreciation Of Computer Hard & Soft": {
|
||||
"account_type": "Depreciation"
|
||||
},
|
||||
},
|
||||
"Depreciation Of Furniture & Office Equipment\n\t\t\t": {
|
||||
"account_type": "Depreciation"
|
||||
},
|
||||
},
|
||||
"Depreciation Of Motor Vehicles": {
|
||||
"account_type": "Depreciation"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
"Direct Expenses": {
|
||||
"Financial Charges": {
|
||||
"Air Miles Card Charges": {},
|
||||
"Amex Credit Cards Charges": {},
|
||||
"Bank Finance & Loan Charges": {},
|
||||
"Credit Card Charges": {},
|
||||
"Credit Card Swipe Charges": {},
|
||||
"Air Miles Card Charges": {},
|
||||
"Amex Credit Cards Charges": {},
|
||||
"Bank Finance & Loan Charges": {},
|
||||
"Credit Card Charges": {},
|
||||
"Credit Card Swipe Charges": {},
|
||||
"PayPal Charges": {}
|
||||
}
|
||||
},
|
||||
},
|
||||
"MISC Charges": {
|
||||
"Other Charges": {
|
||||
"Captial Loss": {
|
||||
"Disposal of Business Branch": {},
|
||||
"Loss On Fixed Assets Disposal": {},
|
||||
"Capital Loss": {
|
||||
"Disposal of Business Branch": {},
|
||||
"Loss On Fixed Assets Disposal": {},
|
||||
"Loss on Difference on Exchange": {}
|
||||
},
|
||||
},
|
||||
"Other Non Operating Exp": {
|
||||
"Other Non Operating Expenses": {}
|
||||
},
|
||||
},
|
||||
"Previous Year Adjustments": {
|
||||
"Previous Year Adjustments Account": {}
|
||||
},
|
||||
},
|
||||
"Royalty Fees": {
|
||||
"Royalty to Parent Co.": {}
|
||||
},
|
||||
},
|
||||
"Tax / Zakat Expenses": {
|
||||
"Income Tax": {
|
||||
"account_type": "Tax"
|
||||
},
|
||||
"Zakat": {},
|
||||
},
|
||||
"Zakat": {},
|
||||
"account_type": "Tax"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
"Share Resources": {
|
||||
"Share Resource Expenses Account": {}
|
||||
},
|
||||
},
|
||||
"Store Operating Expenses": {
|
||||
"Selling, General & Admin Expenses": {
|
||||
"Advertising Expenses": {
|
||||
"Other - Advertising Expenses": {}
|
||||
},
|
||||
},
|
||||
"Bank & Finance Charges": {
|
||||
"Other Bank Charges": {}
|
||||
},
|
||||
},
|
||||
"Communications": {
|
||||
"Courrier": {},
|
||||
"Others - Communication": {},
|
||||
"Telephone": {},
|
||||
"Courier": {},
|
||||
"Others - Communication": {},
|
||||
"Telephone": {},
|
||||
"Web Site Hosting Fees": {}
|
||||
},
|
||||
},
|
||||
"Office & Various Expenses": {
|
||||
"Cleaning": {},
|
||||
"Convoyance Expenses": {},
|
||||
"Gifts & Donations": {},
|
||||
"Insurance": {},
|
||||
"Kitchen and Buffet Expenses": {},
|
||||
"Maintenance": {},
|
||||
"Others - Office Various Expenses": {},
|
||||
"Security & Guard": {},
|
||||
"Stationary From Suppliers": {},
|
||||
"Stationary Out Of Stock": {},
|
||||
"Subscriptions": {},
|
||||
"Training": {},
|
||||
"Cleaning": {},
|
||||
"Conveyance Expenses": {},
|
||||
"Gifts & Donations": {},
|
||||
"Insurance": {},
|
||||
"Kitchen and Buffet Expenses": {},
|
||||
"Maintenance": {},
|
||||
"Others - Office Various Expenses": {},
|
||||
"Security & Guard": {},
|
||||
"Stationary From Suppliers": {},
|
||||
"Stationary Out Of Stock": {},
|
||||
"Subscriptions": {},
|
||||
"Training": {},
|
||||
"Vehicle Expenses": {}
|
||||
},
|
||||
},
|
||||
"Personnel Cost": {
|
||||
"Basic Salary": {},
|
||||
"End Of Service Indemnity": {},
|
||||
"Housing Allowance": {},
|
||||
"Leave Salary": {},
|
||||
"Leave Ticket": {},
|
||||
"Life Insurance": {},
|
||||
"Medical Insurance": {},
|
||||
"Personnel Cost Others": {},
|
||||
"Sales Commission": {},
|
||||
"Staff School Allowances": {},
|
||||
"Transportation Allowance": {},
|
||||
"Uniform": {},
|
||||
"Basic Salary": {},
|
||||
"End Of Service Indemnity": {},
|
||||
"Housing Allowance": {},
|
||||
"Leave Salary": {},
|
||||
"Leave Ticket": {},
|
||||
"Life Insurance": {},
|
||||
"Medical Insurance": {},
|
||||
"Personnel Cost Others": {},
|
||||
"Sales Commission": {},
|
||||
"Staff School Allowances": {},
|
||||
"Transportation Allowance": {},
|
||||
"Uniform": {},
|
||||
"Visa Expenses": {}
|
||||
},
|
||||
},
|
||||
"Professional & Legal Fees": {
|
||||
"Audit Fees": {},
|
||||
"Legal fees": {},
|
||||
"Others - Professional Fees": {},
|
||||
"Sponsorship Fees": {},
|
||||
"Audit Fees": {},
|
||||
"Legal fees": {},
|
||||
"Others - Professional Fees": {},
|
||||
"Sponsorship Fees": {},
|
||||
"Trade License Fees": {}
|
||||
},
|
||||
},
|
||||
"Provision & Write Off": {
|
||||
"Amortisation of Preoperating Expenses": {},
|
||||
"Cash Shortage": {},
|
||||
"Others - Provision & Write off": {},
|
||||
"Write Off Inventory": {},
|
||||
"Amortisation of Preoperating Expenses": {},
|
||||
"Cash Shortage": {},
|
||||
"Others - Provision & Write off": {},
|
||||
"Write Off Inventory": {},
|
||||
"Write Off Receivables & Payables": {}
|
||||
},
|
||||
},
|
||||
"Rent Expenses": {
|
||||
"Office Rent": {},
|
||||
"Office Rent": {},
|
||||
"Warehouse Rent": {}
|
||||
},
|
||||
},
|
||||
"Travel Expenses": {
|
||||
"Air tickets": {},
|
||||
"Hotel": {},
|
||||
"Meals": {},
|
||||
"Others": {},
|
||||
"Air tickets": {},
|
||||
"Hotel": {},
|
||||
"Meals": {},
|
||||
"Others": {},
|
||||
"Per Diem": {}
|
||||
},
|
||||
},
|
||||
"Utilities": {
|
||||
"Other Utility Cahrges": {},
|
||||
"Other Utility Cahrges": {},
|
||||
"Water & Electricity": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
"root_type": "Expense"
|
||||
},
|
||||
},
|
||||
"Liabilities": {
|
||||
"Current Liabilities": {
|
||||
"Accounts Payable": {
|
||||
"Payables": {
|
||||
"Advance Paybale to Suppliers": {
|
||||
"account_type": "Payable"
|
||||
},
|
||||
},
|
||||
"Consigned Payable": {
|
||||
"account_type": "Payable"
|
||||
},
|
||||
},
|
||||
"Other Payable": {
|
||||
"account_type": "Payable"
|
||||
},
|
||||
},
|
||||
"Post Dated Cheques Paid": {
|
||||
"account_type": "Payable"
|
||||
},
|
||||
"Staff Payable": {},
|
||||
},
|
||||
"Staff Payable": {},
|
||||
"Suppliers Price Protection": {
|
||||
"account_type": "Payable"
|
||||
},
|
||||
},
|
||||
"Trade Payable": {
|
||||
"account_type": "Payable"
|
||||
},
|
||||
},
|
||||
"account_type": "Payable"
|
||||
}
|
||||
},
|
||||
},
|
||||
"Accruals & Provisions": {
|
||||
"Accruals": {
|
||||
"Accrued Personnel Cost": {
|
||||
"Accrued - Commissions": {},
|
||||
"Accrued - Leave Salary": {},
|
||||
"Accrued - Leave Tickets": {},
|
||||
"Accrued - Salaries": {},
|
||||
"Accrued Other Personnel Cost": {},
|
||||
"Accrued Salaries Increment": {},
|
||||
"Accrued - Commissions": {},
|
||||
"Accrued - Leave Salary": {},
|
||||
"Accrued - Leave Tickets": {},
|
||||
"Accrued - Salaries": {},
|
||||
"Accrued Other Personnel Cost": {},
|
||||
"Accrued Salaries Increment": {},
|
||||
"Accrued-Staff Bonus": {}
|
||||
}
|
||||
},
|
||||
},
|
||||
"Accrued Expenses": {
|
||||
"Accrued Other Expenses": {
|
||||
"Accrued - Audit Fees": {},
|
||||
"Accrued - Office Rent": {},
|
||||
"Accrued - Sponsorship": {},
|
||||
"Accrued - Telephone": {},
|
||||
"Accrued - Utilities": {},
|
||||
"Accrued - Audit Fees": {},
|
||||
"Accrued - Office Rent": {},
|
||||
"Accrued - Sponsorship": {},
|
||||
"Accrued - Telephone": {},
|
||||
"Accrued - Utilities": {},
|
||||
"Accrued Others": {}
|
||||
}
|
||||
},
|
||||
},
|
||||
"Other Current Liabilities": {
|
||||
"Accrued Dubai Customs": {},
|
||||
"Deferred income": {},
|
||||
"Accrued Dubai Customs": {},
|
||||
"Deferred income": {},
|
||||
"Shipping & Handling": {}
|
||||
},
|
||||
},
|
||||
"Provisions": {
|
||||
"Tax Payables": {
|
||||
"Income Tax Payable": {},
|
||||
"Sales Tax Payable": {},
|
||||
"Income Tax Payable": {},
|
||||
"Sales Tax Payable": {},
|
||||
"Withholding Tax Payable": {}
|
||||
}
|
||||
},
|
||||
},
|
||||
"Short Term Loan": {}
|
||||
},
|
||||
},
|
||||
"Duties and Taxes": {
|
||||
"account_type": "Tax",
|
||||
"account_type": "Tax",
|
||||
"is_group": 1
|
||||
},
|
||||
},
|
||||
"Reservations & Credit Notes": {
|
||||
"Credit Notes": {
|
||||
"Credit Notes to Customers": {},
|
||||
"Credit Notes to Customers": {},
|
||||
"Reservations": {}
|
||||
}
|
||||
},
|
||||
},
|
||||
"Stock Liabilities": {
|
||||
"Stock Received But Not Billed": {
|
||||
"account_type": "Stock Received But Not Billed"
|
||||
}
|
||||
},
|
||||
},
|
||||
"Unearned Income": {}
|
||||
},
|
||||
},
|
||||
"Long Term Liabilities": {
|
||||
"Long Term Loans & Provisions": {}
|
||||
},
|
||||
},
|
||||
"root_type": "Liability"
|
||||
},
|
||||
},
|
||||
"Revenue": {
|
||||
"Direct Revenue": {
|
||||
"Other Direct Revenue": {
|
||||
"Other Revenue - Operating": {
|
||||
"Advertising Income": {},
|
||||
"Branding Income": {},
|
||||
"Early Setmt Margin from Suppliers": {},
|
||||
"Marketing Rebate from Suppliers": {},
|
||||
"Rebate from Suppliers": {},
|
||||
"Service Income": {},
|
||||
"Advertising Income": {},
|
||||
"Branding Income": {},
|
||||
"Early Setmt Margin from Suppliers": {},
|
||||
"Marketing Rebate from Suppliers": {},
|
||||
"Rebate from Suppliers": {},
|
||||
"Service Income": {},
|
||||
"Space Rental Income": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
"Indirect Revenue": {
|
||||
"Other Indirect Revenue": {
|
||||
"Capital Gain": {},
|
||||
"Excess In Till": {},
|
||||
"Gain On Difference Of Exchange": {},
|
||||
"Management Consultancy Fees": {},
|
||||
"Capital Gain": {},
|
||||
"Excess In Till": {},
|
||||
"Gain On Difference Of Exchange": {},
|
||||
"Management Consultancy Fees": {},
|
||||
"Other Income": {}
|
||||
},
|
||||
},
|
||||
"Other Revenue - Non Operating": {
|
||||
"Interest Revenue": {},
|
||||
"Interest from FD": {},
|
||||
"Products Listing Fees from Suppliers": {},
|
||||
"Interest Revenue": {},
|
||||
"Interest from FD": {},
|
||||
"Products Listing Fees from Suppliers": {},
|
||||
"Trade Opening Fees from suppliers": {}
|
||||
}
|
||||
},
|
||||
},
|
||||
"Sales": {
|
||||
"Sales from Other Regions": {
|
||||
"Sales from Other Region": {}
|
||||
},
|
||||
},
|
||||
"Sales of same region": {
|
||||
"Management Consultancy Fees 1": {},
|
||||
"Sales Account": {},
|
||||
"Management Consultancy Fees 1": {},
|
||||
"Sales Account": {},
|
||||
"Sales of I/C": {}
|
||||
}
|
||||
},
|
||||
},
|
||||
"root_type": "Income"
|
||||
},
|
||||
},
|
||||
"Share Holder Equity": {
|
||||
"Capital": {
|
||||
"Contributed Capital": {},
|
||||
"Share Capital": {},
|
||||
"Shareholders Current A/c": {},
|
||||
"Sub Ordinated Loan": {},
|
||||
"Contributed Capital": {},
|
||||
"Share Capital": {},
|
||||
"Shareholders Current A/c": {},
|
||||
"Sub Ordinated Loan": {},
|
||||
"Treasury Stocks": {}
|
||||
},
|
||||
},
|
||||
"Retained Earnings": {
|
||||
"Current Year Results": {},
|
||||
"Dividends Paid": {},
|
||||
"Current Year Results": {},
|
||||
"Dividends Paid": {},
|
||||
"Previous Years Results": {}
|
||||
},
|
||||
"account_type": "Equity",
|
||||
},
|
||||
"account_type": "Equity",
|
||||
"root_type": "Equity"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,39 +167,7 @@
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Status",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Open\nClosed",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@@ -273,7 +241,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-04-13 19:14:47.593753",
|
||||
"modified": "2019-08-01 19:14:47.593753",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Accounting Period",
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe import _
|
||||
|
||||
class OverlapError(frappe.ValidationError): pass
|
||||
|
||||
class AccountingPeriod(Document):
|
||||
def validate(self):
|
||||
@@ -16,7 +19,7 @@ class AccountingPeriod(Document):
|
||||
def autoname(self):
|
||||
company_abbr = frappe.get_cached_value('Company', self.company, "abbr")
|
||||
self.name = " - ".join([self.period_name, company_abbr])
|
||||
|
||||
|
||||
def validate_overlap(self):
|
||||
existing_accounting_period = frappe.db.sql("""select name from `tabAccounting Period`
|
||||
where (
|
||||
@@ -33,12 +36,13 @@ class AccountingPeriod(Document):
|
||||
}, as_dict=True)
|
||||
|
||||
if len(existing_accounting_period) > 0:
|
||||
frappe.throw("Accounting Period overlaps with {0}".format(existing_accounting_period[0].get("name")))
|
||||
frappe.throw(_("Accounting Period overlaps with {0}")
|
||||
.format(existing_accounting_period[0].get("name")), OverlapError)
|
||||
|
||||
def get_doctypes_for_closing(self):
|
||||
docs_for_closing = []
|
||||
#if not self.closed_documents or len(self.closed_documents) == 0:
|
||||
doctypes = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Payroll Entry", "Bank Reconciliation", "Asset", "Purchase Order", "Sales Order", "Leave Application", "Leave Allocation", "Stock Entry"]
|
||||
doctypes = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Payroll Entry", "Bank Reconciliation",
|
||||
"Asset", "Purchase Order", "Sales Order", "Leave Application", "Leave Allocation", "Stock Entry"]
|
||||
closed_doctypes = [{"document_type": doctype, "closed": 1} for doctype in doctypes]
|
||||
for closed_doctype in closed_doctypes:
|
||||
docs_for_closing.append(closed_doctype)
|
||||
@@ -51,4 +55,4 @@ class AccountingPeriod(Document):
|
||||
self.append('closed_documents', {
|
||||
"document_type": doctype_for_closing.document_type,
|
||||
"closed": doctype_for_closing.closed
|
||||
})
|
||||
})
|
||||
@@ -5,23 +5,42 @@ from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
from frappe.utils import nowdate, add_months
|
||||
from erpnext.accounts.general_ledger import ClosedAccountingPeriod
|
||||
from erpnext.accounts.doctype.accounting_period.accounting_period import OverlapError
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
|
||||
# class TestAccountingPeriod(unittest.TestCase):
|
||||
# def test_overlap(self):
|
||||
# ap1 = create_accounting_period({"start_date":"2018-04-01", "end_date":"2018-06-30", "company":"Wind Power LLC"})
|
||||
# ap1.save()
|
||||
# ap2 = create_accounting_period({"start_date":"2018-06-30", "end_date":"2018-07-10", "company":"Wind Power LLC"})
|
||||
# self.assertRaises(frappe.OverlapError, accounting_period_2.save())
|
||||
#
|
||||
# def tearDown(self):
|
||||
# pass
|
||||
#
|
||||
#
|
||||
# def create_accounting_period(**args):
|
||||
# accounting_period = frappe.new_doc("Accounting Period")
|
||||
# accounting_period.start_date = args.start_date or frappe.utils.datetime.date(2018, 4, 1)
|
||||
# accounting_period.end_date = args.end_date or frappe.utils.datetime.date(2018, 6, 30)
|
||||
# accounting_period.company = args.company
|
||||
# accounting_period.period_name = "_Test_Period_Name_1"
|
||||
#
|
||||
# return accounting_period
|
||||
class TestAccountingPeriod(unittest.TestCase):
|
||||
def test_overlap(self):
|
||||
ap1 = create_accounting_period(start_date = "2018-04-01",
|
||||
end_date = "2018-06-30", company = "Wind Power LLC")
|
||||
ap1.save()
|
||||
|
||||
ap2 = create_accounting_period(start_date = "2018-06-30",
|
||||
end_date = "2018-07-10", company = "Wind Power LLC", period_name = "Test Accounting Period 1")
|
||||
self.assertRaises(OverlapError, ap2.save)
|
||||
|
||||
def test_accounting_period(self):
|
||||
ap1 = create_accounting_period(period_name = "Test Accounting Period 2")
|
||||
ap1.save()
|
||||
|
||||
doc = create_sales_invoice(do_not_submit=1, cost_center = "_Test Company - _TC", warehouse = "Stores - _TC")
|
||||
self.assertRaises(ClosedAccountingPeriod, doc.submit)
|
||||
|
||||
def tearDown(self):
|
||||
for d in frappe.get_all("Accounting Period"):
|
||||
frappe.delete_doc("Accounting Period", d.name)
|
||||
|
||||
def create_accounting_period(**args):
|
||||
args = frappe._dict(args)
|
||||
|
||||
accounting_period = frappe.new_doc("Accounting Period")
|
||||
accounting_period.start_date = args.start_date or nowdate()
|
||||
accounting_period.end_date = args.end_date or add_months(nowdate(), 1)
|
||||
accounting_period.company = args.company or "_Test Company"
|
||||
accounting_period.period_name =args.period_name or "_Test_Period_Name_1"
|
||||
accounting_period.append("closed_documents", {
|
||||
"document_type": 'Sales Invoice', "closed": 1
|
||||
})
|
||||
|
||||
return accounting_period
|
||||
@@ -48,7 +48,10 @@ class BankAccount(Document):
|
||||
# Encode characters as numbers
|
||||
encoded = [encode_char(c) if ord(c) >= 65 and ord(c) <= 90 else c for c in flipped]
|
||||
|
||||
to_check = int(''.join(encoded))
|
||||
try:
|
||||
to_check = int(''.join(encoded))
|
||||
except ValueError:
|
||||
frappe.throw(_('IBAN is not valid'))
|
||||
|
||||
if to_check % 97 != 1:
|
||||
frappe.throw(_('IBAN is not valid'))
|
||||
|
||||
@@ -6,6 +6,7 @@ from __future__ import unicode_literals
|
||||
import frappe, json
|
||||
from frappe.model.document import Document
|
||||
from frappe import _
|
||||
from frappe.desk.search import sanitize_searchfield
|
||||
|
||||
class BankGuarantee(Document):
|
||||
def validate(self):
|
||||
@@ -22,5 +23,8 @@ class BankGuarantee(Document):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_vouchar_detials(column_list, doctype, docname):
|
||||
column_list = json.loads(column_list)
|
||||
for col in column_list:
|
||||
sanitize_searchfield(col)
|
||||
return frappe.db.sql(''' select {columns} from `tab{doctype}` where name=%s'''
|
||||
.format(columns=", ".join(json.loads(column_list)), doctype=doctype), docname, as_dict=1)[0]
|
||||
|
||||
@@ -48,7 +48,7 @@ class BankStatementTransactionEntry(Document):
|
||||
|
||||
def get_statement_headers(self):
|
||||
if not self.bank_settings:
|
||||
frappe.throw("Bank Data mapper doesn't exist")
|
||||
frappe.throw(_("Bank Data mapper doesn't exist"))
|
||||
mapper_doc = frappe.get_doc("Bank Statement Settings", self.bank_settings)
|
||||
headers = {entry.mapped_header:entry.stmt_header for entry in mapper_doc.header_items}
|
||||
return headers
|
||||
@@ -57,7 +57,7 @@ class BankStatementTransactionEntry(Document):
|
||||
if self.bank_statement is None: return
|
||||
filename = self.bank_statement.split("/")[-1]
|
||||
if (len(self.new_transaction_items + self.reconciled_transaction_items) > 0):
|
||||
frappe.throw("Transactions already retreived from the statement")
|
||||
frappe.throw(_("Transactions already retreived from the statement"))
|
||||
|
||||
date_format = frappe.get_value("Bank Statement Settings", self.bank_settings, "date_format")
|
||||
if (date_format is None):
|
||||
@@ -314,7 +314,7 @@ class BankStatementTransactionEntry(Document):
|
||||
try:
|
||||
reconcile_against_document(lst)
|
||||
except:
|
||||
frappe.throw("Exception occurred while reconciling {0}".format(payment.reference_name))
|
||||
frappe.throw(_("Exception occurred while reconciling {0}".format(payment.reference_name)))
|
||||
|
||||
def submit_payment_entries(self):
|
||||
for payment in self.new_transaction_items:
|
||||
@@ -414,7 +414,7 @@ def get_transaction_entries(filename, headers):
|
||||
elif (filename.lower().endswith("xls")):
|
||||
rows = get_rows_from_xls_file(filename)
|
||||
else:
|
||||
frappe.throw("Only .csv and .xlsx files are supported currently")
|
||||
frappe.throw(_("Only .csv and .xlsx files are supported currently"))
|
||||
|
||||
stmt_headers = headers.values()
|
||||
for row in rows:
|
||||
|
||||
@@ -45,12 +45,12 @@ class BankTransaction(StatusUpdater):
|
||||
def clear_linked_payment_entries(self):
|
||||
for payment_entry in self.payment_entries:
|
||||
allocated_amount = get_total_allocated_amount(payment_entry)
|
||||
paid_amount = get_paid_amount(payment_entry)
|
||||
paid_amount = get_paid_amount(payment_entry, self.currency)
|
||||
|
||||
if paid_amount and allocated_amount:
|
||||
if flt(allocated_amount[0]["allocated_amount"]) > flt(paid_amount):
|
||||
frappe.throw(_("The total allocated amount ({0}) is greated than the paid amount ({1}).".format(flt(allocated_amount[0]["allocated_amount"]), flt(paid_amount))))
|
||||
elif flt(allocated_amount[0]["allocated_amount"]) == flt(paid_amount):
|
||||
else:
|
||||
if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"]:
|
||||
self.clear_simple_entry(payment_entry)
|
||||
|
||||
@@ -80,9 +80,17 @@ def get_total_allocated_amount(payment_entry):
|
||||
AND
|
||||
bt.docstatus = 1""", (payment_entry.payment_document, payment_entry.payment_entry), as_dict=True)
|
||||
|
||||
def get_paid_amount(payment_entry):
|
||||
def get_paid_amount(payment_entry, currency):
|
||||
if payment_entry.payment_document in ["Payment Entry", "Sales Invoice", "Purchase Invoice"]:
|
||||
return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "paid_amount")
|
||||
|
||||
paid_amount_field = "paid_amount"
|
||||
if payment_entry.payment_document == 'Payment Entry':
|
||||
doc = frappe.get_doc("Payment Entry", payment_entry.payment_entry)
|
||||
paid_amount_field = ("base_paid_amount"
|
||||
if doc.paid_to_account_currency == currency else "paid_amount")
|
||||
|
||||
return frappe.db.get_value(payment_entry.payment_document,
|
||||
payment_entry.payment_entry, paid_amount_field)
|
||||
|
||||
elif payment_entry.payment_document == "Journal Entry":
|
||||
return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "total_credit")
|
||||
@@ -91,7 +99,7 @@ def get_paid_amount(payment_entry):
|
||||
return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "total_amount_reimbursed")
|
||||
|
||||
else:
|
||||
frappe.throw("Please reconcile {0}: {1} manually".format(payment_entry.payment_document, payment_entry.payment_entry))
|
||||
frappe.throw(_("Please reconcile {0}: {1} manually".format(payment_entry.payment_document, payment_entry.payment_entry)))
|
||||
|
||||
@frappe.whitelist()
|
||||
def unclear_reference_payment(doctype, docname):
|
||||
|
||||
@@ -21,9 +21,29 @@ frappe.ui.form.on('Exchange Rate Revaluation', {
|
||||
|
||||
refresh: function(frm) {
|
||||
if(frm.doc.docstatus==1) {
|
||||
frm.add_custom_button(__('Make Journal Entry'), function() {
|
||||
return frm.events.make_jv(frm);
|
||||
});
|
||||
frappe.db.get_value("Journal Entry Account", {
|
||||
'reference_type': 'Exchange Rate Revaluation',
|
||||
'reference_name': frm.doc.name,
|
||||
'docstatus': 1
|
||||
}, "sum(debit) as sum", (r) =>{
|
||||
let total_amt = 0;
|
||||
frm.doc.accounts.forEach(d=> {
|
||||
total_amt = total_amt + d['new_balance_in_base_currency'];
|
||||
});
|
||||
if(total_amt === r.sum) {
|
||||
frm.add_custom_button(__("Journal Entry"), function(){
|
||||
frappe.route_options = {
|
||||
'reference_type': 'Exchange Rate Revaluation',
|
||||
'reference_name': frm.doc.name
|
||||
};
|
||||
frappe.set_route("List", "Journal Entry");
|
||||
}, __("View"));
|
||||
} else {
|
||||
frm.add_custom_button(__('Create Journal Entry'), function() {
|
||||
return frm.events.make_jv(frm);
|
||||
});
|
||||
}
|
||||
}, 'Journal Entry');
|
||||
}
|
||||
},
|
||||
|
||||
@@ -39,8 +59,6 @@ frappe.ui.form.on('Exchange Rate Revaluation', {
|
||||
});
|
||||
frm.events.get_total_gain_loss(frm);
|
||||
refresh_field("accounts");
|
||||
} else {
|
||||
frappe.msgprint(__("No records found"));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -54,7 +72,7 @@ frappe.ui.form.on('Exchange Rate Revaluation', {
|
||||
d.gain_loss = flt(d.new_balance_in_base_currency, precision("new_balance_in_base_currency", d)) - flt(d.balance_in_base_currency, precision("balance_in_base_currency", d));
|
||||
total_gain_loss += flt(d.gain_loss, precision("gain_loss", d));
|
||||
});
|
||||
|
||||
|
||||
frm.set_value("total_gain_loss", flt(total_gain_loss, precision("total_gain_loss")));
|
||||
frm.refresh_fields();
|
||||
},
|
||||
|
||||
@@ -22,7 +22,7 @@ class ExchangeRateRevaluation(Document):
|
||||
- flt(d.balance_in_base_currency, d.precision("balance_in_base_currency"))
|
||||
total_gain_loss += flt(d.gain_loss, d.precision("gain_loss"))
|
||||
self.total_gain_loss = flt(total_gain_loss, self.precision("total_gain_loss"))
|
||||
|
||||
|
||||
def validate_mandatory(self):
|
||||
if not (self.company and self.posting_date):
|
||||
frappe.throw(_("Please select Company and Posting Date to getting entries"))
|
||||
@@ -33,8 +33,9 @@ class ExchangeRateRevaluation(Document):
|
||||
company_currency = erpnext.get_company_currency(self.company)
|
||||
precision = get_field_precision(frappe.get_meta("Exchange Rate Revaluation Account")
|
||||
.get_field("new_balance_in_base_currency"), company_currency)
|
||||
for d in self.get_accounts_from_gle():
|
||||
|
||||
|
||||
account_details = self.get_accounts_from_gle()
|
||||
for d in account_details:
|
||||
current_exchange_rate = d.balance / d.balance_in_account_currency \
|
||||
if d.balance_in_account_currency else 0
|
||||
new_exchange_rate = get_exchange_rate(d.account_currency, company_currency, self.posting_date)
|
||||
@@ -52,6 +53,10 @@ class ExchangeRateRevaluation(Document):
|
||||
"new_exchange_rate": new_exchange_rate,
|
||||
"new_balance_in_base_currency": new_balance_in_base_currency
|
||||
})
|
||||
|
||||
if not accounts:
|
||||
self.throw_invalid_response_message(account_details)
|
||||
|
||||
return accounts
|
||||
|
||||
def get_accounts_from_gle(self):
|
||||
@@ -83,11 +88,18 @@ class ExchangeRateRevaluation(Document):
|
||||
|
||||
return account_details
|
||||
|
||||
def throw_invalid_response_message(self, account_details):
|
||||
if account_details:
|
||||
message = _("No outstanding invoices require exchange rate revaluation")
|
||||
else:
|
||||
message = _("No outstanding invoices found")
|
||||
frappe.msgprint(message)
|
||||
|
||||
def make_jv_entry(self):
|
||||
if self.total_gain_loss == 0:
|
||||
return
|
||||
|
||||
unrealized_exchange_gain_loss_account = frappe.get_cached_value('Company', self.company,
|
||||
unrealized_exchange_gain_loss_account = frappe.get_cached_value('Company', self.company,
|
||||
"unrealized_exchange_gain_loss_account")
|
||||
if not unrealized_exchange_gain_loss_account:
|
||||
frappe.throw(_("Please set Unrealized Exchange Gain/Loss Account in Company {0}")
|
||||
|
||||
@@ -1,461 +1,475 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2018-04-13 18:30:06.110433",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2018-04-13 18:30:06.110433",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "account",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"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,
|
||||
"translatable": 0,
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "account",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "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,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "party_type",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Party Type",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "DocType",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "party_type",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Party Type",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "DocType",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "party",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Party",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "party_type",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "party",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Party",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "party_type",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_2",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_2",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "account_currency",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Account Currency",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Currency",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "account_currency",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Account Currency",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Currency",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "balance_in_account_currency",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Balance In Account Currency",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "balance_in_account_currency",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Balance In Account Currency",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "account_currency",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "balances",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "balances",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "current_exchange_rate",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Current Exchange Rate",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "current_exchange_rate",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Current Exchange Rate",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "balance_in_base_currency",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Balance In Base Currency",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "balance_in_base_currency",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Balance In Base Currency",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_9",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_9",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "new_exchange_rate",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "New Exchange Rate",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "new_exchange_rate",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "New Exchange Rate",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "new_balance_in_base_currency",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "New Balance In Base Currency",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "new_balance_in_base_currency",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "New Balance In Base Currency",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "gain_loss",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Gain/Loss",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "gain_loss",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Gain/Loss",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-01-07 16:52:07.327930",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Exchange Rate Revaluation Account",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-06-26 18:57:51.762345",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Exchange Rate Revaluation Account",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
||||
@@ -92,10 +92,10 @@ frappe.ui.form.on("Journal Entry", {
|
||||
multi_currency: function(frm) {
|
||||
erpnext.journal_entry.toggle_fields_based_on_currency(frm);
|
||||
},
|
||||
|
||||
|
||||
posting_date: function(frm) {
|
||||
if(!frm.doc.multi_currency || !frm.doc.posting_date) return;
|
||||
|
||||
|
||||
$.each(frm.doc.accounts || [], function(i, row) {
|
||||
erpnext.journal_entry.set_exchange_rate(frm, row.doctype, row.name);
|
||||
})
|
||||
@@ -167,7 +167,7 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
||||
filters: {
|
||||
'account': row.account
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
me.frm.set_query("reference_name", "accounts", function(doc, cdt, cdn) {
|
||||
@@ -234,7 +234,7 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
||||
|
||||
out.filters.push([jvd.reference_type, "per_billed", "<", 100]);
|
||||
}
|
||||
|
||||
|
||||
if(jvd.party_type && jvd.party) {
|
||||
var party_field = "";
|
||||
if(jvd.reference_type.indexOf("Sales")===0) {
|
||||
@@ -382,7 +382,7 @@ cur_frm.cscript.voucher_type = function(doc, cdt, cdn) {
|
||||
});
|
||||
refresh_field("accounts");
|
||||
}
|
||||
|
||||
|
||||
if((!(doc.accounts || []).length) || ((doc.accounts || []).length==1 && !doc.accounts[0].account)) {
|
||||
if(in_list(["Bank Entry", "Cash Entry"], doc.voucher_type)) {
|
||||
return frappe.call({
|
||||
@@ -390,7 +390,7 @@ cur_frm.cscript.voucher_type = function(doc, cdt, cdn) {
|
||||
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_default_bank_cash_account",
|
||||
args: {
|
||||
"account_type": (doc.voucher_type=="Bank Entry" ?
|
||||
"Bank" : (doc.voucher_type=="Cash" ? "Cash" : null)),
|
||||
"Bank" : (doc.voucher_type=="Cash Entry" ? "Cash" : null)),
|
||||
"company": doc.company
|
||||
},
|
||||
callback: function(r) {
|
||||
@@ -442,7 +442,7 @@ frappe.ui.form.on("Journal Entry Account", {
|
||||
account: function(frm, dt, dn) {
|
||||
erpnext.journal_entry.set_account_balance(frm, dt, dn);
|
||||
},
|
||||
|
||||
|
||||
debit_in_account_currency: function(frm, cdt, cdn) {
|
||||
erpnext.journal_entry.set_exchange_rate(frm, cdt, cdn);
|
||||
},
|
||||
|
||||
@@ -38,7 +38,10 @@ def get_loyalty_details(customer, loyalty_program, expiry_date=None, company=Non
|
||||
@frappe.whitelist()
|
||||
def get_loyalty_program_details_with_points(customer, loyalty_program=None, expiry_date=None, company=None, silent=False, include_expired_entry=False, current_transaction_amount=0):
|
||||
lp_details = get_loyalty_program_details(customer, loyalty_program, company=company, silent=silent)
|
||||
loyalty_program = frappe.get_doc("Loyalty Program", loyalty_program)
|
||||
loyalty_program_name = loyalty_program or lp_details.loyalty_program
|
||||
if not loyalty_program_name: return
|
||||
|
||||
loyalty_program = frappe.get_doc("Loyalty Program", loyalty_program_name)
|
||||
lp_details.update(get_loyalty_details(customer, loyalty_program.name, expiry_date, company, include_expired_entry))
|
||||
|
||||
tier_spent_level = sorted([d.as_dict() for d in loyalty_program.collection_rules],
|
||||
|
||||
@@ -294,7 +294,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
() => {
|
||||
frm.set_party_account_based_on_party = false;
|
||||
if (r.message.bank_account) {
|
||||
frm.set_value("bank_account", r.message.bank_account);
|
||||
frm.set_value("party_bank_account", r.message.bank_account);
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
@@ -1791,6 +1791,41 @@
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "Draft",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Status",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "\nDraft\nSubmitted\nCancelled",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
@@ -2205,7 +2240,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-03-27 17:39:54.163016",
|
||||
"modified": "2019-11-06 15:15:45.223497",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Entry",
|
||||
|
||||
@@ -60,6 +60,7 @@ class PaymentEntry(AccountsController):
|
||||
self.validate_duplicate_entry()
|
||||
self.validate_allocated_amount()
|
||||
self.ensure_supplier_is_not_blocked()
|
||||
self.set_status()
|
||||
|
||||
def on_submit(self):
|
||||
self.setup_party_account_field()
|
||||
@@ -69,6 +70,7 @@ class PaymentEntry(AccountsController):
|
||||
self.update_outstanding_amounts()
|
||||
self.update_advance_paid()
|
||||
self.update_expense_claim()
|
||||
self.set_status()
|
||||
|
||||
|
||||
def on_cancel(self):
|
||||
@@ -78,6 +80,7 @@ class PaymentEntry(AccountsController):
|
||||
self.update_advance_paid()
|
||||
self.update_expense_claim()
|
||||
self.delink_advance_entry_references()
|
||||
self.set_status()
|
||||
|
||||
def update_outstanding_amounts(self):
|
||||
self.set_missing_ref_details(force=True)
|
||||
@@ -274,6 +277,14 @@ class PaymentEntry(AccountsController):
|
||||
frappe.throw(_("Against Journal Entry {0} does not have any unmatched {1} entry")
|
||||
.format(d.reference_name, dr_or_cr))
|
||||
|
||||
def set_status(self):
|
||||
if self.docstatus == 2:
|
||||
self.status = 'Cancelled'
|
||||
elif self.docstatus == 1:
|
||||
self.status = 'Submitted'
|
||||
else:
|
||||
self.status = 'Draft'
|
||||
|
||||
def set_amounts(self):
|
||||
self.set_amounts_in_company_currency()
|
||||
self.set_total_allocated_amount()
|
||||
@@ -558,7 +569,7 @@ def get_outstanding_reference_documents(args):
|
||||
|
||||
# Get negative outstanding sales /purchase invoices
|
||||
negative_outstanding_invoices = []
|
||||
if args.get("party_type") not in ["Student", "Employee"] and not args.get("voucher_no"):
|
||||
if args.get("party_type") in ("Supplier", "Customer") and not args.get("voucher_no"):
|
||||
negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"),
|
||||
args.get("party"), args.get("party_account"), party_account_currency, company_currency)
|
||||
|
||||
@@ -589,7 +600,7 @@ def get_outstanding_reference_documents(args):
|
||||
|
||||
# Get all SO / PO which are not fully billed or aginst which full advance not paid
|
||||
orders_to_be_billed = []
|
||||
if (args.get("party_type") != "Student"):
|
||||
if (args.get("party_type") in ("Supplier", "Customer")):
|
||||
orders_to_be_billed = get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"),
|
||||
args.get("party"), party_account_currency, company_currency)
|
||||
|
||||
@@ -601,7 +612,7 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre
|
||||
voucher_type = 'Sales Order'
|
||||
elif party_type == "Supplier":
|
||||
voucher_type = 'Purchase Order'
|
||||
elif party_type == "Employee":
|
||||
else:
|
||||
voucher_type = None
|
||||
|
||||
# Add cost center condition
|
||||
@@ -613,13 +624,18 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre
|
||||
|
||||
orders = []
|
||||
if voucher_type:
|
||||
ref_field = "base_grand_total" if party_account_currency == company_currency else "grand_total"
|
||||
if party_account_currency == company_currency:
|
||||
grand_total_field = "base_grand_total"
|
||||
rounded_total_field = "base_rounded_total"
|
||||
else:
|
||||
grand_total_field = "grand_total"
|
||||
rounded_total_field = "rounded_total"
|
||||
|
||||
orders = frappe.db.sql("""
|
||||
select
|
||||
name as voucher_no,
|
||||
{ref_field} as invoice_amount,
|
||||
({ref_field} - advance_paid) as outstanding_amount,
|
||||
if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount,
|
||||
(if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) - advance_paid) as outstanding_amount,
|
||||
transaction_date as posting_date
|
||||
from
|
||||
`tab{voucher_type}`
|
||||
@@ -627,17 +643,18 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre
|
||||
{party_type} = %s
|
||||
and docstatus = 1
|
||||
and ifnull(status, "") != "Closed"
|
||||
and {ref_field} > advance_paid
|
||||
and if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) > advance_paid
|
||||
and abs(100 - per_billed) > 0.01
|
||||
{condition}
|
||||
order by
|
||||
transaction_date, name
|
||||
""".format(**{
|
||||
"ref_field": ref_field,
|
||||
"rounded_total_field": rounded_total_field,
|
||||
"grand_total_field": grand_total_field,
|
||||
"voucher_type": voucher_type,
|
||||
"party_type": scrub(party_type),
|
||||
"condition": condition
|
||||
}), party, as_dict=True)
|
||||
}), (party), as_dict=True)
|
||||
|
||||
order_list = []
|
||||
for d in orders:
|
||||
@@ -713,9 +730,23 @@ def get_party_details(company, party_type, party, date, cost_center=None):
|
||||
@frappe.whitelist()
|
||||
def get_account_details(account, date, cost_center=None):
|
||||
frappe.has_permission('Payment Entry', throw=True)
|
||||
|
||||
# to check if the passed account is accessible if the reference doctype is Payment Entry
|
||||
account_list = frappe.get_list('Account', {
|
||||
'name': account
|
||||
}, reference_doctype='Payment Entry', limit=1)
|
||||
|
||||
# There might be some user permissions which will allow account under certain doctypes
|
||||
# except for Payment Entry, only in such case we should throw permission error
|
||||
if not account_list:
|
||||
frappe.throw(_('Account: {0} is not permitted under Payment Entry').format(account))
|
||||
|
||||
account_balance = get_balance_on(account, date, cost_center=cost_center,
|
||||
ignore_account_permission=True)
|
||||
|
||||
return frappe._dict({
|
||||
"account_currency": get_account_currency(account),
|
||||
"account_balance": get_balance_on(account, date, cost_center=cost_center),
|
||||
"account_balance": account_balance,
|
||||
"account_type": frappe.db.get_value("Account", account, "account_type")
|
||||
})
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
cur_frm.add_fetch("payment_gateway", "payment_account", "payment_account")
|
||||
cur_frm.add_fetch("payment_gateway", "payment_gateway", "payment_gateway")
|
||||
cur_frm.add_fetch("payment_gateway", "message", "message")
|
||||
cur_frm.add_fetch("payment_gateway", "payment_url_message", "payment_url_message")
|
||||
cur_frm.add_fetch("payment_gateway_account", "payment_account", "payment_account")
|
||||
cur_frm.add_fetch("payment_gateway_account", "payment_gateway", "payment_gateway")
|
||||
cur_frm.add_fetch("payment_gateway_account", "message", "message")
|
||||
|
||||
frappe.ui.form.on("Payment Request", "onload", function(frm, dt, dn){
|
||||
if (frm.doc.reference_doctype) {
|
||||
|
||||
@@ -157,7 +157,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
||||
can_change_release_date: function(date) {
|
||||
const diff = frappe.datetime.get_diff(date, frappe.datetime.nowdate());
|
||||
if (diff < 0) {
|
||||
frappe.throw('New release date should be in the future');
|
||||
frappe.throw(__('New release date should be in the future'));
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
|
||||
@@ -2454,7 +2454,7 @@
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "other_charges_calculation",
|
||||
"fieldtype": "Text",
|
||||
"fieldtype": "Long Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
@@ -4903,7 +4903,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2019-04-22 12:45:49.728359",
|
||||
"modified": "2020-04-16 19:04:18.599264",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice",
|
||||
|
||||
@@ -250,7 +250,7 @@ class PurchaseInvoice(BuyingController):
|
||||
def set_against_expense_account(self):
|
||||
against_accounts = []
|
||||
for item in self.get("items"):
|
||||
if item.expense_account not in against_accounts:
|
||||
if item.expense_account and (item.expense_account not in against_accounts):
|
||||
against_accounts.append(item.expense_account)
|
||||
|
||||
self.against_expense_account = ",".join(against_accounts)
|
||||
@@ -336,6 +336,7 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
if not self.is_return:
|
||||
self.update_against_document_in_jv()
|
||||
self.update_billing_status_for_zero_amount_refdoc("Purchase Receipt")
|
||||
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
||||
self.update_billing_status_in_pr()
|
||||
|
||||
@@ -362,7 +363,7 @@ class PurchaseInvoice(BuyingController):
|
||||
update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes"
|
||||
|
||||
make_gl_entries(gl_entries, cancel=(self.docstatus == 2),
|
||||
update_outstanding=update_outstanding, merge_entries=False)
|
||||
update_outstanding=update_outstanding, merge_entries=False, from_repost=from_repost)
|
||||
|
||||
if update_outstanding == "No":
|
||||
update_outstanding_amt(self.credit_to, "Supplier", self.supplier,
|
||||
@@ -484,9 +485,13 @@ class PurchaseInvoice(BuyingController):
|
||||
"credit": flt(item.rm_supp_cost)
|
||||
}, warehouse_account[self.supplier_warehouse]["account_currency"]))
|
||||
elif not item.is_fixed_asset or (item.is_fixed_asset and is_cwip_accounting_disabled()):
|
||||
|
||||
expense_account = (item.expense_account
|
||||
if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account)
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": item.expense_account if not item.enable_deferred_expense else item.deferred_expense_account,
|
||||
"account": expense_account,
|
||||
"against": self.supplier,
|
||||
"debit": flt(item.base_net_amount, item.precision("base_net_amount")),
|
||||
"debit_in_account_currency": (flt(item.base_net_amount,
|
||||
@@ -745,7 +750,11 @@ class PurchaseInvoice(BuyingController):
|
||||
)
|
||||
|
||||
def make_gle_for_rounding_adjustment(self, gl_entries):
|
||||
if self.rounding_adjustment:
|
||||
# if rounding adjustment in small and conversion rate is also small then
|
||||
# base_rounding_adjustment may become zero due to small precision
|
||||
# eg: rounding_adjustment = 0.01 and exchange rate = 0.05 and precision of base_rounding_adjustment is 2
|
||||
# then base_rounding_adjustment becomes zero and error is thrown in GL Entry
|
||||
if self.rounding_adjustment and self.base_rounding_adjustment:
|
||||
round_off_account, round_off_cost_center = \
|
||||
get_round_off_account_and_cost_center(self.company)
|
||||
|
||||
@@ -772,6 +781,7 @@ class PurchaseInvoice(BuyingController):
|
||||
if frappe.db.get_single_value('Accounts Settings', 'unlink_payment_on_cancellation_of_invoice'):
|
||||
unlink_ref_doc_from_payment_entries(self)
|
||||
|
||||
self.update_billing_status_for_zero_amount_refdoc("Purchase Receipt")
|
||||
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
||||
self.update_billing_status_in_pr()
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ frappe.listview_settings['Purchase Invoice'] = {
|
||||
} else if(frappe.datetime.get_diff(doc.due_date) < 0) {
|
||||
return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<,Today"];
|
||||
} else {
|
||||
return [__("Unpaid"), "orange", "outstanding_amount,>,0|due,>=,Today"];
|
||||
return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>=,Today"];
|
||||
}
|
||||
} else if(cint(doc.is_return)) {
|
||||
return [__("Return"), "darkgrey", "is_return,=,Yes"];
|
||||
|
||||
@@ -20,11 +20,13 @@ test_dependencies = ["Item", "Cost Center", "Payment Term", "Payment Terms Templ
|
||||
test_ignore = ["Serial No"]
|
||||
|
||||
class TestPurchaseInvoice(unittest.TestCase):
|
||||
def setUp(self):
|
||||
@classmethod
|
||||
def setUpClass(self):
|
||||
unlink_payment_on_cancel_of_invoice()
|
||||
frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)
|
||||
|
||||
def tearDown(self):
|
||||
@classmethod
|
||||
def tearDownClass(self):
|
||||
unlink_payment_on_cancel_of_invoice(0)
|
||||
|
||||
def test_gl_entries_without_perpetual_inventory(self):
|
||||
@@ -91,6 +93,7 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
pi_doc = frappe.get_doc('Purchase Invoice', pi_doc.name)
|
||||
|
||||
self.assertRaises(frappe.LinkExistsError, pi_doc.cancel)
|
||||
unlink_payment_on_cancel_of_invoice()
|
||||
|
||||
def test_purchase_invoice_for_blocked_supplier(self):
|
||||
supplier = frappe.get_doc('Supplier', '_Test Supplier')
|
||||
|
||||
@@ -41,6 +41,8 @@ def get_pos_data():
|
||||
items_list = get_items_list(pos_profile, doc.company)
|
||||
customers = get_customers_list(pos_profile)
|
||||
|
||||
doc.plc_conversion_rate = update_plc_conversion_rate(doc, pos_profile)
|
||||
|
||||
return {
|
||||
'doc': doc,
|
||||
'default_customer': pos_profile.get('customer'),
|
||||
@@ -53,7 +55,7 @@ def get_pos_data():
|
||||
'batch_no_data': get_batch_no_data(),
|
||||
'barcode_data': get_barcode_data(items_list),
|
||||
'tax_data': get_item_tax_data(),
|
||||
'price_list_data': get_price_list_data(doc.selling_price_list),
|
||||
'price_list_data': get_price_list_data(doc.selling_price_list, doc.plc_conversion_rate),
|
||||
'customer_wise_price_list': get_customer_wise_price_list(),
|
||||
'bin_data': get_bin_data(pos_profile),
|
||||
'pricing_rules': get_pricing_rule_data(doc),
|
||||
@@ -62,6 +64,15 @@ def get_pos_data():
|
||||
'meta': get_meta()
|
||||
}
|
||||
|
||||
def update_plc_conversion_rate(doc, pos_profile):
|
||||
conversion_rate = 1.0
|
||||
|
||||
price_list_currency = frappe.get_cached_value("Price List", doc.selling_price_list, "currency")
|
||||
if pos_profile.get("currency") != price_list_currency:
|
||||
conversion_rate = get_exchange_rate(price_list_currency,
|
||||
pos_profile.get("currency"), nowdate(), args="for_selling") or 1.0
|
||||
|
||||
return conversion_rate
|
||||
|
||||
def get_meta():
|
||||
doctype_meta = {
|
||||
@@ -317,14 +328,14 @@ def get_item_tax_data():
|
||||
return itemwise_tax
|
||||
|
||||
|
||||
def get_price_list_data(selling_price_list):
|
||||
def get_price_list_data(selling_price_list, conversion_rate):
|
||||
itemwise_price_list = {}
|
||||
price_lists = frappe.db.sql("""Select ifnull(price_list_rate, 0) as price_list_rate,
|
||||
item_code from `tabItem Price` ip where price_list = %(price_list)s""",
|
||||
{'price_list': selling_price_list}, as_dict=1)
|
||||
|
||||
for item in price_lists:
|
||||
itemwise_price_list[item.item_code] = item.price_list_rate
|
||||
itemwise_price_list[item.item_code] = item.price_list_rate * conversion_rate
|
||||
|
||||
return itemwise_price_list
|
||||
|
||||
@@ -432,7 +443,6 @@ def get_customer_id(doc, customer=None):
|
||||
|
||||
return cust_id
|
||||
|
||||
|
||||
def make_customer_and_address(customers):
|
||||
customers_list = []
|
||||
for customer, data in iteritems(customers):
|
||||
@@ -449,8 +459,11 @@ def make_customer_and_address(customers):
|
||||
frappe.db.commit()
|
||||
return customers_list
|
||||
|
||||
|
||||
def add_customer(data):
|
||||
customer = data.get('full_name') or data.get('customer')
|
||||
if frappe.db.exists("Customer", customer.strip()):
|
||||
return customer.strip()
|
||||
|
||||
customer_doc = frappe.new_doc('Customer')
|
||||
customer_doc.customer_name = data.get('full_name') or data.get('customer')
|
||||
customer_doc.customer_pos_id = data.get('customer_pos_id')
|
||||
@@ -462,21 +475,18 @@ def add_customer(data):
|
||||
frappe.db.commit()
|
||||
return customer_doc.name
|
||||
|
||||
|
||||
def get_territory(data):
|
||||
if data.get('territory'):
|
||||
return data.get('territory')
|
||||
|
||||
return frappe.db.get_single_value('Selling Settings','territory') or _('All Territories')
|
||||
|
||||
|
||||
def get_customer_group(data):
|
||||
if data.get('customer_group'):
|
||||
return data.get('customer_group')
|
||||
|
||||
return frappe.db.get_single_value('Selling Settings', 'customer_group') or frappe.db.get_value('Customer Group', {'is_group': 0}, 'name')
|
||||
|
||||
|
||||
def make_contact(args, customer):
|
||||
if args.get('email_id') or args.get('phone'):
|
||||
name = frappe.db.get_value('Dynamic Link',
|
||||
@@ -502,7 +512,6 @@ def make_contact(args, customer):
|
||||
doc.flags.ignore_mandatory = True
|
||||
doc.save(ignore_permissions=True)
|
||||
|
||||
|
||||
def make_address(args, customer):
|
||||
if not args.get('address_line1'):
|
||||
return
|
||||
@@ -517,7 +526,10 @@ def make_address(args, customer):
|
||||
address = frappe.get_doc('Address', name)
|
||||
else:
|
||||
address = frappe.new_doc('Address')
|
||||
address.country = frappe.get_cached_value('Company', args.get('company'), 'country')
|
||||
if args.get('company'):
|
||||
address.country = frappe.get_cached_value('Company',
|
||||
args.get('company'), 'country')
|
||||
|
||||
address.append('links', {
|
||||
'link_doctype': 'Customer',
|
||||
'link_name': customer
|
||||
@@ -529,7 +541,6 @@ def make_address(args, customer):
|
||||
address.flags.ignore_mandatory = True
|
||||
address.save(ignore_permissions=True)
|
||||
|
||||
|
||||
def make_email_queue(email_queue):
|
||||
name_list = []
|
||||
for key, data in iteritems(email_queue):
|
||||
@@ -546,7 +557,6 @@ def make_email_queue(email_queue):
|
||||
|
||||
return name_list
|
||||
|
||||
|
||||
def validate_item(doc):
|
||||
for item in doc.get('items'):
|
||||
if not frappe.db.exists('Item', item.get('item_code')):
|
||||
@@ -565,7 +575,6 @@ def validate_item(doc):
|
||||
item_doc.save(ignore_permissions=True)
|
||||
frappe.db.commit()
|
||||
|
||||
|
||||
def submit_invoice(si_doc, name, doc, name_list):
|
||||
try:
|
||||
si_doc.insert()
|
||||
@@ -581,7 +590,6 @@ def submit_invoice(si_doc, name, doc, name_list):
|
||||
|
||||
return name_list
|
||||
|
||||
|
||||
def save_invoice(doc, name, name_list):
|
||||
try:
|
||||
if not frappe.db.exists('Sales Invoice', {'offline_pos_name': name}):
|
||||
|
||||
@@ -44,6 +44,10 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
|
||||
this.frm.toggle_reqd("due_date", !this.frm.doc.is_return);
|
||||
|
||||
if (this.frm.doc.is_return) {
|
||||
this.frm.return_print_format = "Sales Invoice Return";
|
||||
}
|
||||
|
||||
this.show_general_ledger();
|
||||
|
||||
if(doc.update_stock) this.show_stock_ledger();
|
||||
@@ -131,16 +135,24 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
},
|
||||
|
||||
set_default_print_format: function() {
|
||||
// set default print format to POS type
|
||||
// set default print format to POS type or Credit Note
|
||||
if(cur_frm.doc.is_pos) {
|
||||
if(cur_frm.pos_print_format) {
|
||||
cur_frm.meta._default_print_format = cur_frm.meta.default_print_format;
|
||||
cur_frm.meta.default_print_format = cur_frm.pos_print_format;
|
||||
}
|
||||
} else if(cur_frm.doc.is_return && !cur_frm.meta.default_print_format) {
|
||||
if(cur_frm.return_print_format) {
|
||||
cur_frm.meta._default_print_format = cur_frm.meta.default_print_format;
|
||||
cur_frm.meta.default_print_format = cur_frm.return_print_format;
|
||||
}
|
||||
} else {
|
||||
if(cur_frm.meta._default_print_format) {
|
||||
cur_frm.meta.default_print_format = cur_frm.meta._default_print_format;
|
||||
cur_frm.meta._default_print_format = null;
|
||||
} else if(in_list([cur_frm.pos_print_format, cur_frm.return_print_format], cur_frm.meta.default_print_format)) {
|
||||
cur_frm.meta.default_print_format = null;
|
||||
cur_frm.meta._default_print_format = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -174,9 +186,13 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
method: "erpnext.selling.doctype.quotation.quotation.make_sales_invoice",
|
||||
source_doctype: "Quotation",
|
||||
target: me.frm,
|
||||
setters: {
|
||||
customer: me.frm.doc.customer || undefined,
|
||||
},
|
||||
setters: [{
|
||||
fieldtype: 'Link',
|
||||
label: __('Customer'),
|
||||
options: 'Customer',
|
||||
fieldname: 'party_name',
|
||||
default: me.frm.doc.customer,
|
||||
}],
|
||||
get_query_filters: {
|
||||
docstatus: 1,
|
||||
status: ["!=", "Lost"],
|
||||
|
||||
@@ -2544,7 +2544,7 @@
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "other_charges_calculation",
|
||||
"fieldtype": "Text",
|
||||
"fieldtype": "Long Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
@@ -5816,7 +5816,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2019-04-22 12:45:41.109345",
|
||||
"modified": "2020-04-16 19:05:00.498772",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice",
|
||||
|
||||
@@ -78,6 +78,7 @@ class SalesInvoice(SellingController):
|
||||
self.so_dn_required()
|
||||
|
||||
self.validate_proj_cust()
|
||||
self.validate_pos_return()
|
||||
self.validate_with_previous_doc()
|
||||
self.validate_uom_is_integer("stock_uom", "stock_qty")
|
||||
self.validate_uom_is_integer("uom", "qty")
|
||||
@@ -166,6 +167,7 @@ class SalesInvoice(SellingController):
|
||||
self.make_gl_entries()
|
||||
|
||||
if not self.is_return:
|
||||
self.update_billing_status_for_zero_amount_refdoc("Delivery Note")
|
||||
self.update_billing_status_for_zero_amount_refdoc("Sales Order")
|
||||
self.check_credit_limit()
|
||||
|
||||
@@ -198,6 +200,16 @@ class SalesInvoice(SellingController):
|
||||
if "Healthcare" in active_domains:
|
||||
manage_invoice_submit_cancel(self, "on_submit")
|
||||
|
||||
def validate_pos_return(self):
|
||||
|
||||
if self.is_pos and self.is_return:
|
||||
total_amount_in_payments = 0
|
||||
for payment in self.payments:
|
||||
total_amount_in_payments += payment.amount
|
||||
invoice_total = self.rounded_total or self.grand_total
|
||||
if flt(total_amount_in_payments, self.precision("grand_total")) < invoice_total:
|
||||
frappe.throw(_("Total payments amount can't be greater than {}".format(-invoice_total)))
|
||||
|
||||
def validate_pos_paid_amount(self):
|
||||
if len(self.payments) == 0 and self.is_pos:
|
||||
frappe.throw(_("At least one mode of payment is required for POS invoice."))
|
||||
@@ -222,6 +234,7 @@ class SalesInvoice(SellingController):
|
||||
self.update_billing_status_in_dn()
|
||||
|
||||
if not self.is_return:
|
||||
self.update_billing_status_for_zero_amount_refdoc("Delivery Note")
|
||||
self.update_billing_status_for_zero_amount_refdoc("Sales Order")
|
||||
self.update_serial_no(in_cancel=True)
|
||||
|
||||
@@ -337,7 +350,7 @@ class SalesInvoice(SellingController):
|
||||
timesheet.calculate_percentage_billed()
|
||||
timesheet.flags.ignore_validate_update_after_submit = True
|
||||
timesheet.set_status()
|
||||
timesheet.save()
|
||||
timesheet.save(ignore_permissions=True)
|
||||
|
||||
def update_time_sheet_detail(self, timesheet, args, sales_invoice):
|
||||
for data in timesheet.time_logs:
|
||||
@@ -397,14 +410,18 @@ class SalesInvoice(SellingController):
|
||||
if pos.get('account_for_change_amount'):
|
||||
self.account_for_change_amount = pos.get('account_for_change_amount')
|
||||
|
||||
for fieldname in ('territory', 'naming_series', 'currency', 'taxes_and_charges', 'letter_head', 'tc_name',
|
||||
'company', 'select_print_heading', 'cash_bank_account', 'company_address',
|
||||
'write_off_account', 'write_off_cost_center', 'apply_discount_on', 'cost_center'):
|
||||
for fieldname in ('territory', 'naming_series', 'currency', 'letter_head', 'tc_name',
|
||||
'company', 'select_print_heading', 'cash_bank_account', 'write_off_account',
|
||||
'write_off_cost_center', 'apply_discount_on', 'cost_center'):
|
||||
if (not for_validate) or (for_validate and not self.get(fieldname)):
|
||||
self.set(fieldname, pos.get(fieldname))
|
||||
|
||||
customer_price_list = frappe.get_value("Customer", self.customer, 'default_price_list')
|
||||
|
||||
for field in ['taxes_and_charges', 'company_address']:
|
||||
if pos.get(field):
|
||||
self.set(field, pos.get(field))
|
||||
|
||||
if not customer_price_list:
|
||||
self.set('selling_price_list', pos.get('selling_price_list'))
|
||||
|
||||
@@ -682,7 +699,7 @@ class SalesInvoice(SellingController):
|
||||
cint(self.redeem_loyalty_points)) else "Yes"
|
||||
|
||||
make_gl_entries(gl_entries, cancel=(self.docstatus == 2),
|
||||
update_outstanding=update_outstanding, merge_entries=False)
|
||||
update_outstanding=update_outstanding, merge_entries=False, from_repost=from_repost)
|
||||
|
||||
if update_outstanding == "No":
|
||||
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
|
||||
@@ -770,7 +787,14 @@ class SalesInvoice(SellingController):
|
||||
if item.is_fixed_asset:
|
||||
asset = frappe.get_doc("Asset", item.asset)
|
||||
|
||||
fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(asset, item.base_net_amount)
|
||||
if (len(asset.finance_books) > 1 and not item.finance_book
|
||||
and asset.finance_books[0].finance_book):
|
||||
frappe.throw(_("Select finance book for the item {0} at row {1}")
|
||||
.format(item.item_code, item.idx))
|
||||
|
||||
fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(asset,
|
||||
item.base_net_amount, item.finance_book)
|
||||
|
||||
for gle in fixed_asset_gl_entries:
|
||||
gle["against"] = self.customer
|
||||
gl_entries.append(self.get_gl_dict(gle))
|
||||
@@ -778,10 +802,13 @@ class SalesInvoice(SellingController):
|
||||
asset.db_set("disposal_date", self.posting_date)
|
||||
asset.set_status("Sold" if self.docstatus==1 else None)
|
||||
else:
|
||||
account_currency = get_account_currency(item.income_account)
|
||||
income_account = (item.income_account
|
||||
if (not item.enable_deferred_revenue or self.is_return) else item.deferred_revenue_account)
|
||||
|
||||
account_currency = get_account_currency(income_account)
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": item.income_account if not item.enable_deferred_revenue else item.deferred_revenue_account,
|
||||
"account": income_account,
|
||||
"against": self.customer,
|
||||
"credit": flt(item.base_net_amount, item.precision("base_net_amount")),
|
||||
"credit_in_account_currency": (flt(item.base_net_amount, item.precision("base_net_amount"))
|
||||
@@ -917,7 +944,7 @@ class SalesInvoice(SellingController):
|
||||
)
|
||||
|
||||
def make_gle_for_rounding_adjustment(self, gl_entries):
|
||||
if flt(self.rounding_adjustment, self.precision("rounding_adjustment")):
|
||||
if flt(self.rounding_adjustment, self.precision("rounding_adjustment")) and self.base_rounding_adjustment:
|
||||
round_off_account, round_off_cost_center = \
|
||||
get_round_off_account_and_cost_center(self.company)
|
||||
|
||||
@@ -965,10 +992,8 @@ class SalesInvoice(SellingController):
|
||||
continue
|
||||
|
||||
for serial_no in item.serial_no.split("\n"):
|
||||
if serial_no and frappe.db.exists('Serial No', serial_no):
|
||||
sno = frappe.get_doc('Serial No', serial_no)
|
||||
sno.sales_invoice = invoice
|
||||
sno.db_update()
|
||||
if serial_no and frappe.db.get_value('Serial No', serial_no, 'item_code') == item.item_code:
|
||||
frappe.db.set_value('Serial No', serial_no, 'sales_invoice', invoice)
|
||||
|
||||
def validate_serial_numbers(self):
|
||||
"""
|
||||
@@ -1014,12 +1039,18 @@ class SalesInvoice(SellingController):
|
||||
continue
|
||||
|
||||
for serial_no in item.serial_no.split("\n"):
|
||||
sales_invoice = frappe.db.get_value("Serial No", serial_no, "sales_invoice")
|
||||
if sales_invoice and self.name != sales_invoice:
|
||||
sales_invoice_company = frappe.db.get_value("Sales Invoice", sales_invoice, "company")
|
||||
serial_no_details = frappe.db.get_value("Serial No", serial_no,
|
||||
["sales_invoice", "item_code"], as_dict=1)
|
||||
|
||||
if not serial_no_details:
|
||||
continue
|
||||
|
||||
if serial_no_details.sales_invoice and serial_no_details.item_code == item.item_code \
|
||||
and self.name != serial_no_details.sales_invoice:
|
||||
sales_invoice_company = frappe.db.get_value("Sales Invoice", serial_no_details.sales_invoice, "company")
|
||||
if sales_invoice_company == self.company:
|
||||
frappe.throw(_("Serial Number: {0} is already referenced in Sales Invoice: {1}"
|
||||
.format(serial_no, sales_invoice)))
|
||||
.format(serial_no, serial_no_details.sales_invoice)))
|
||||
|
||||
def update_project(self):
|
||||
if self.project:
|
||||
|
||||
@@ -28,10 +28,12 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
w.submit()
|
||||
return w
|
||||
|
||||
def setUp(self):
|
||||
@classmethod
|
||||
def setUpClass(self):
|
||||
unlink_payment_on_cancel_of_invoice()
|
||||
|
||||
def tearDown(self):
|
||||
@classmethod
|
||||
def tearDownClass(self):
|
||||
unlink_payment_on_cancel_of_invoice(0)
|
||||
|
||||
def test_timestamp_change(self):
|
||||
@@ -134,6 +136,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
unlink_payment_on_cancel_of_invoice(0)
|
||||
si = frappe.get_doc('Sales Invoice', si.name)
|
||||
self.assertRaises(frappe.LinkExistsError, si.cancel)
|
||||
unlink_payment_on_cancel_of_invoice()
|
||||
|
||||
def test_sales_invoice_calculation_export_currency(self):
|
||||
si = frappe.copy_doc(test_records[2])
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "barcode",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
@@ -52,6 +53,7 @@
|
||||
"bold": 1,
|
||||
"collapsible": 0,
|
||||
"columns": 4,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -86,6 +88,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "col_break1",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@@ -116,6 +119,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "item_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
@@ -149,6 +153,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "customer_item_code",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
@@ -180,6 +185,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "section_break_6",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@@ -212,6 +218,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Text Editor",
|
||||
"hidden": 0,
|
||||
@@ -247,6 +254,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_8",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@@ -278,6 +286,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "image_view",
|
||||
"fieldtype": "Image",
|
||||
"hidden": 0,
|
||||
@@ -311,6 +320,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "image",
|
||||
"fieldtype": "Attach",
|
||||
"hidden": 1,
|
||||
@@ -343,6 +353,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "quantity_and_rate",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@@ -374,6 +385,7 @@
|
||||
"bold": 1,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "qty",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
@@ -407,6 +419,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "stock_uom",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -439,6 +452,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "col_break2",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@@ -469,6 +483,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "uom",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -502,6 +517,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "conversion_factor",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
@@ -534,6 +550,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "stock_qty",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
@@ -566,6 +583,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "section_break_17",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@@ -597,6 +615,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "price_list_rate",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@@ -631,6 +650,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "base_price_list_rate",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@@ -665,6 +685,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "discount_and_margin",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@@ -698,6 +719,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "price_list_rate",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "margin_type",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
@@ -732,6 +754,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.margin_type && doc.price_list_rate",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "margin_rate_or_amount",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
@@ -765,6 +788,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.margin_type && doc.price_list_rate && doc.margin_rate_or_amount",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "rate_with_margin",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@@ -798,6 +822,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_19",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@@ -830,6 +855,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "price_list_rate",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "discount_percentage",
|
||||
"fieldtype": "Percent",
|
||||
"hidden": 0,
|
||||
@@ -865,6 +891,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "discount_percentage",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "discount_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@@ -899,6 +926,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.margin_type && doc.price_list_rate && doc.margin_rate_or_amount",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "base_rate_with_margin",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@@ -932,6 +960,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "section_break1",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@@ -962,6 +991,7 @@
|
||||
"bold": 1,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "rate",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@@ -996,6 +1026,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@@ -1030,6 +1061,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "col_break3",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@@ -1060,6 +1092,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "base_rate",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@@ -1094,6 +1127,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "base_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@@ -1128,6 +1162,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "pricing_rule",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -1160,6 +1195,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "section_break_21",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@@ -1191,6 +1227,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "net_rate",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@@ -1224,6 +1261,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "net_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@@ -1257,6 +1295,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_24",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@@ -1288,6 +1327,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "base_net_rate",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@@ -1321,6 +1361,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "base_net_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@@ -1355,6 +1396,7 @@
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "eval:doc.delivered_by_supplier==1",
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "drop_ship",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@@ -1387,6 +1429,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "delivered_by_supplier",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
@@ -1419,6 +1462,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "accounting",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@@ -1450,6 +1494,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "income_account",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -1486,6 +1531,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "expense_account",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -1519,6 +1565,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "col_break4",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@@ -1550,6 +1597,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": ":Company",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "cost_center",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -1586,6 +1634,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "deferred_revenue",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@@ -1619,6 +1668,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "enable_deferred_revenue",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "deferred_revenue_account",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -1653,6 +1703,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "enable_deferred_revenue",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "service_stop_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
@@ -1686,6 +1737,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "0",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "enable_deferred_revenue",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
@@ -1718,6 +1770,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_50",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@@ -1750,6 +1803,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "enable_deferred_revenue",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "service_start_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
@@ -1783,6 +1837,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "enable_deferred_revenue",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "service_end_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
@@ -1815,6 +1870,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "section_break_18",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@@ -1847,6 +1903,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "weight_per_unit",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
@@ -1880,6 +1937,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "total_weight",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
@@ -1912,6 +1970,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_21",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@@ -1943,6 +2002,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "weight_uom",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -1978,6 +2038,7 @@
|
||||
"collapsible_depends_on": "eval:doc.serial_no || doc.batch_no",
|
||||
"columns": 0,
|
||||
"depends_on": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "warehouse_and_reference",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@@ -2009,6 +2070,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "warehouse",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -2043,6 +2105,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "target_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
@@ -2077,6 +2140,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:!doc.__islocal",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "quality_inspection",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -2110,6 +2174,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "batch_no",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -2142,6 +2207,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "col_break5",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@@ -2172,6 +2238,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "allow_zero_valuation_rate",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
@@ -2204,6 +2271,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "serial_no",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
@@ -2238,6 +2306,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "item_group",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
@@ -2272,6 +2341,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "brand",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
@@ -2305,6 +2375,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "item_tax_rate",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
@@ -2338,6 +2409,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "actual_batch_qty",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
@@ -2372,6 +2444,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "actual_qty",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
@@ -2405,6 +2478,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "edit_references",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@@ -2437,6 +2511,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "sales_order",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -2471,6 +2546,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "so_detail",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
@@ -2504,6 +2580,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_74",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@@ -2535,6 +2612,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "delivery_note",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -2569,6 +2647,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "dn_detail",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
@@ -2602,6 +2681,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "delivered_qty",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
@@ -2635,6 +2715,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "is_fixed_asset",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
@@ -2667,6 +2748,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "asset",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -2700,6 +2782,42 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "asset",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "finance_book",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Finance Book",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Finance Book",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "section_break_54",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@@ -2731,6 +2849,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "page_break",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
@@ -2766,7 +2885,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-02-18 18:59:52.223628",
|
||||
"modified": "2019-06-28 17:04:25.870346",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice Item",
|
||||
|
||||
@@ -16,7 +16,7 @@ frappe.ui.form.on('Share Transfer', {
|
||||
};
|
||||
};
|
||||
});
|
||||
if (frm.doc.docstatus == 1) {
|
||||
if (frm.doc.docstatus == 1 && frm.doc.equity_or_liability_account && frm.doc.asset_account ) {
|
||||
frm.add_custom_button(__('Make Journal Entry'), function () {
|
||||
erpnext.share_transfer.make_jv(frm);
|
||||
});
|
||||
@@ -92,6 +92,7 @@ erpnext.share_transfer.make_jv = function (frm) {
|
||||
debit_applicant_type = "Shareholder";
|
||||
debit_applicant = frm.doc.from_shareholder;
|
||||
}
|
||||
|
||||
frappe.call({
|
||||
args: {
|
||||
"company": frm.doc.company,
|
||||
|
||||
@@ -292,11 +292,14 @@ def make_jv_entry( company, account, amount, payment_account,\
|
||||
"party_type": debit_applicant_type,
|
||||
"party": debit_applicant,
|
||||
})
|
||||
|
||||
account_amt_list.append({
|
||||
"account": payment_account,
|
||||
"credit_in_account_currency": amount,
|
||||
"party_type": credit_applicant_type,
|
||||
"party": credit_applicant,
|
||||
})
|
||||
|
||||
journal_entry.set("accounts", account_amt_list)
|
||||
|
||||
return journal_entry.as_dict()
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from erpnext.utilities.product import get_price
|
||||
|
||||
@@ -13,7 +14,7 @@ class SubscriptionPlan(Document):
|
||||
|
||||
def validate_interval_count(self):
|
||||
if self.billing_interval_count < 1:
|
||||
frappe.throw('Billing Interval Count cannot be less than 1')
|
||||
frappe.throw(_('Billing Interval Count cannot be less than 1'))
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_plan_rate(plan, quantity=1, customer=None):
|
||||
@@ -26,7 +27,7 @@ def get_plan_rate(plan, quantity=1, customer=None):
|
||||
customer_group = frappe.db.get_value("Customer", customer, "customer_group")
|
||||
else:
|
||||
customer_group = None
|
||||
|
||||
|
||||
price = get_price(item_code=plan.item, price_list=plan.price_list, customer_group=customer_group, company=None, qty=quantity)
|
||||
if not price:
|
||||
return 0
|
||||
|
||||
@@ -9,11 +9,13 @@ from frappe.model.meta import get_field_precision
|
||||
from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
|
||||
|
||||
|
||||
class ClosedAccountingPeriod(frappe.ValidationError): pass
|
||||
class StockAccountInvalidTransaction(frappe.ValidationError): pass
|
||||
|
||||
def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, update_outstanding='Yes', from_repost=False):
|
||||
if gl_map:
|
||||
if not cancel:
|
||||
validate_accounting_period(gl_map)
|
||||
gl_map = process_gl_map(gl_map, merge_entries)
|
||||
if gl_map and len(gl_map) > 1:
|
||||
save_entries(gl_map, adv_adj, update_outstanding, from_repost)
|
||||
@@ -22,6 +24,27 @@ def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, upd
|
||||
else:
|
||||
delete_gl_entries(gl_map, adv_adj=adv_adj, update_outstanding=update_outstanding)
|
||||
|
||||
def validate_accounting_period(gl_map):
|
||||
accounting_periods = frappe.db.sql(""" SELECT
|
||||
ap.name as name
|
||||
FROM
|
||||
`tabAccounting Period` ap, `tabClosed Document` cd
|
||||
WHERE
|
||||
ap.name = cd.parent
|
||||
AND ap.company = %(company)s
|
||||
AND cd.closed = 1
|
||||
AND cd.document_type = %(voucher_type)s
|
||||
AND %(date)s between ap.start_date and ap.end_date
|
||||
""", {
|
||||
'date': gl_map[0].posting_date,
|
||||
'company': gl_map[0].company,
|
||||
'voucher_type': gl_map[0].voucher_type
|
||||
}, as_dict=1)
|
||||
|
||||
if accounting_periods:
|
||||
frappe.throw(_("You can't create accounting entries in the closed accounting period {0}")
|
||||
.format(accounting_periods[0].name), ClosedAccountingPeriod)
|
||||
|
||||
def process_gl_map(gl_map, merge_entries=True):
|
||||
if merge_entries:
|
||||
gl_map = merge_similar_entries(gl_map)
|
||||
|
||||
@@ -124,8 +124,6 @@ def check_matching_amount(bank_account, company, transaction):
|
||||
'txt': '%%%s%%' % amount
|
||||
}, as_dict=True)
|
||||
|
||||
frappe.errprint(journal_entries)
|
||||
|
||||
if transaction.credit > 0:
|
||||
sales_invoices = frappe.db.sql("""
|
||||
SELECT
|
||||
|
||||
@@ -1625,7 +1625,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
setTimeout(function () {
|
||||
w.print();
|
||||
w.close();
|
||||
}, 1000)
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
submit_invoice: function () {
|
||||
@@ -1682,6 +1682,12 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
$(this.wrapper).find('.pos-bill').css('pointer-events', pointer_events);
|
||||
$(this.wrapper).find('.pos-items-section').css('pointer-events', pointer_events);
|
||||
this.set_primary_action();
|
||||
|
||||
$(this.wrapper).find('#pos-item-disc').prop('disabled',
|
||||
this.pos_profile_data.allow_user_to_edit_discount ? false : true);
|
||||
|
||||
$(this.wrapper).find('#pos-item-price').prop('disabled',
|
||||
this.pos_profile_data.allow_user_to_edit_rate ? false : true);
|
||||
},
|
||||
|
||||
create_invoice: function () {
|
||||
@@ -1699,13 +1705,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
if (this.frm.doc.offline_pos_name
|
||||
&& in_list(existing_pos_list, this.frm.doc.offline_pos_name)) {
|
||||
this.update_invoice()
|
||||
//to retrieve and set the default payment
|
||||
invoice_data[this.frm.doc.offline_pos_name] = this.frm.doc;
|
||||
invoice_data[this.frm.doc.offline_pos_name].payments[0].amount = this.frm.doc.net_total
|
||||
invoice_data[this.frm.doc.offline_pos_name].payments[0].base_amount = this.frm.doc.net_total
|
||||
|
||||
this.frm.doc.paid_amount = this.frm.doc.net_total
|
||||
this.frm.doc.outstanding_amount = 0
|
||||
} else if(!this.frm.doc.offline_pos_name) {
|
||||
this.frm.doc.offline_pos_name = frappe.datetime.now_datetime();
|
||||
this.frm.doc.posting_date = frappe.datetime.get_today();
|
||||
@@ -1762,6 +1761,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
this.si_docs = this.get_submitted_invoice() || [];
|
||||
this.email_queue_list = this.get_email_queue() || {};
|
||||
this.customers_list = this.get_customers_details() || {};
|
||||
|
||||
if(this.customer_doc) {
|
||||
this.freeze = this.customer_doc.display
|
||||
}
|
||||
@@ -1906,7 +1906,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
serial_no = me.item_serial_no[key][0];
|
||||
}
|
||||
|
||||
if (this.items[0].has_serial_no && serial_no == "") {
|
||||
if (this.items && this.items[0].has_serial_no && serial_no == "") {
|
||||
this.refresh();
|
||||
frappe.throw(__(repl("Error: Serial no is mandatory for item %(item)s", {
|
||||
'item': this.items[0].item_code
|
||||
|
||||
@@ -53,7 +53,7 @@ def _get_party_details(party=None, account=None, party_type="Customer", company=
|
||||
set_other_values(out, party, party_type)
|
||||
set_price_list(out, party, party_type, price_list, pos_profile)
|
||||
|
||||
out["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company, out.customer_group, out.supplier_type)
|
||||
out["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company, out.customer_group, out.supplier_group)
|
||||
|
||||
if fetch_payment_terms_template:
|
||||
out["payment_terms_template"] = get_pyt_term_template(party.name, party_type, company)
|
||||
@@ -155,7 +155,7 @@ def set_price_list(out, party, party_type, given_price_list, pos=None):
|
||||
# price list
|
||||
price_list = get_permitted_documents('Price List')
|
||||
|
||||
if price_list:
|
||||
if price_list and len(price_list) == 1:
|
||||
price_list = price_list[0]
|
||||
elif pos and party_type == 'Customer':
|
||||
customer_price_list = frappe.get_value('Customer', party.name, 'default_price_list')
|
||||
@@ -450,7 +450,9 @@ def get_timeline_data(doctype, name):
|
||||
# fetch and append data from Activity Log
|
||||
data += frappe.db.sql("""select {fields}
|
||||
from `tabActivity Log`
|
||||
where reference_doctype="{doctype}" and reference_name="{name}"
|
||||
where (reference_doctype="{doctype}" and reference_name="{name}")
|
||||
or (timeline_doctype in ("{doctype}") and timeline_name="{name}")
|
||||
or (reference_doctype in ("Quotation", "Opportunity") and timeline_name="{name}")
|
||||
and status!='Success' and creation > {after}
|
||||
{group_by} order by creation desc
|
||||
""".format(doctype=frappe.db.escape(doctype), name=frappe.db.escape(name), fields=fields,
|
||||
@@ -586,4 +588,4 @@ def get_partywise_advanced_payment_amount(party_type, posting_date = None):
|
||||
.format(("credit") if party_type == "Customer" else "debit", cond) , party_type)
|
||||
|
||||
if data:
|
||||
return frappe._dict(data)
|
||||
return frappe._dict(data)
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
{%- from "templates/print_formats/standard_macros.html" import add_header, render_field, print_value, fieldmeta,
|
||||
get_width, get_align_class -%}
|
||||
|
||||
{%- macro render_currency(df, doc) -%}
|
||||
<div class="row {% if df.bold %}important{% endif %} data-field">
|
||||
<div class="col-xs-{{ "9" if df.fieldtype=="Check" else "5" }}
|
||||
{%- if doc.align_labels_right %} text-right{%- endif -%}">
|
||||
<label>{{ _(df.label) }}</label>
|
||||
</div>
|
||||
<div class="col-xs-{{ "3" if df.fieldtype=="Check" else "7" }} value">
|
||||
{% if doc.get(df.fieldname) != None -%}
|
||||
{{ frappe.utils.fmt_money((doc[df.fieldname])|abs, currency=doc.currency) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{%- endmacro -%}
|
||||
|
||||
{%- macro render_taxes(df, doc) -%}
|
||||
{%- set data = doc.get(df.fieldname)[df.start:df.end] -%}
|
||||
<div class="row">
|
||||
<div class="col-xs-6"></div>
|
||||
<div class="col-xs-6">
|
||||
{%- for charge in data -%}
|
||||
{%- if (charge.tax_amount or doc.flags.print_taxes_with_zero_amount) and (not charge.included_in_print_rate or doc.flags.show_inclusive_tax_in_print) -%}
|
||||
<div class="row">
|
||||
<div class="col-xs-5 {%- if doc.align_labels_right %} text-right{%- endif -%}">
|
||||
<label>{{ charge.get_formatted("description") }}</label></div>
|
||||
<div class="col-xs-7 text-right">
|
||||
{{ frappe.utils.fmt_money((charge.tax_amount)|abs, currency=doc.currency) }}
|
||||
</div>
|
||||
</div>
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
</div>
|
||||
</div>
|
||||
{%- endmacro -%}
|
||||
|
||||
{%- macro render_table(df, doc) -%}
|
||||
{%- set table_meta = frappe.get_meta(df.options) -%}
|
||||
{%- set data = doc.get(df.fieldname)[df.start:df.end] -%}
|
||||
{%- if doc.print_templates and
|
||||
doc.print_templates.get(df.fieldname) -%}
|
||||
{% include doc.print_templates[df.fieldname] %}
|
||||
{%- else -%}
|
||||
{%- if data -%}
|
||||
{%- set visible_columns = get_visible_columns(doc.get(df.fieldname),
|
||||
table_meta, df) -%}
|
||||
<div {{ fieldmeta(df) }}>
|
||||
<table class="table table-bordered table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 40px" class="table-sr">{{ _("Sr") }}</th>
|
||||
{% for tdf in visible_columns %}
|
||||
{% if (data and not data[0].flags.compact_item_print) or tdf.fieldname in doc.get(df.fieldname)[0].flags.compact_item_fields %}
|
||||
<th style="width: {{ get_width(tdf) }};" class="{{ get_align_class(tdf) }}" {{ fieldmeta(df) }}>
|
||||
{{ _(tdf.label) }}</th>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for d in data %}
|
||||
<tr>
|
||||
<td class="table-sr">{{ d.idx }}</td>
|
||||
{% for tdf in visible_columns %}
|
||||
{% if not d.flags.compact_item_print or tdf.fieldname in doc.get(df.fieldname)[0].flags.compact_item_fields %}
|
||||
<td class="{{ get_align_class(tdf) }}" {{ fieldmeta(df) }}>
|
||||
{% if tdf.fieldname == 'qty' %}
|
||||
<div class="value">{{ (d[tdf.fieldname])|abs }}</div></td>
|
||||
{% elif tdf.fieldtype == 'Currency' %}
|
||||
<div class="value">{{ frappe.utils.fmt_money((d[tdf.fieldname])|abs, currency=doc.currency) }}</div></td>
|
||||
{% else %}
|
||||
<div class="value">{{ print_value(tdf, d, doc, visible_columns) }}</div></td>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{%- endif -%}
|
||||
{%- endif -%}
|
||||
{%- endmacro -%}
|
||||
|
||||
{% for page in layout %}
|
||||
<div class="page-break">
|
||||
<div {% if print_settings.repeat_header_footer %} id="header-html" class="hidden-pdf" {% endif %}>
|
||||
{{ add_header(loop.index, layout|len, doc, letter_head, no_letterhead, footer, print_settings) }}
|
||||
</div>
|
||||
|
||||
{% if print_settings.repeat_header_footer %}
|
||||
<div id="footer-html" class="visible-pdf">
|
||||
{% if not no_letterhead and footer %}
|
||||
<div class="letter-head-footer">
|
||||
{{ footer }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<p class="text-center small page-number visible-pdf">
|
||||
{{ _("Page {0} of {1}").format('<span class="page"></span>', '<span class="topage"></span>') }}
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% for section in page %}
|
||||
<div class="row section-break">
|
||||
{% if section.columns.fields %}
|
||||
{%- if doc.print_line_breaks and loop.index != 1 -%}<hr>{%- endif -%}
|
||||
{%- if doc.print_section_headings and section.label and section.has_data -%}
|
||||
<h4 class='col-sm-12'>{{ _(section.label) }}</h4>
|
||||
{% endif %}
|
||||
{%- endif -%}
|
||||
{% for column in section.columns %}
|
||||
<div class="col-xs-{{ (12 / section.columns|len)|int }} column-break">
|
||||
{% for df in column.fields %}
|
||||
{% if df.fieldname == 'taxes' %}
|
||||
{{ render_taxes(df, doc) }}
|
||||
{% elif df.fieldtype == 'Currency' %}
|
||||
{{ render_currency(df, doc) }}
|
||||
{% elif df.fieldtype =='Table' %}
|
||||
{{ render_table(df, doc)}}
|
||||
{% elif doc[df.fieldname] and df.fieldname != 'total_qty' %}
|
||||
{{ render_field(df, doc) }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"align_labels_right": 1,
|
||||
"creation": "2019-07-24 20:13:30.259953",
|
||||
"custom_format": 0,
|
||||
"default_print_language": "en-US",
|
||||
"disabled": 0,
|
||||
"doc_type": "Sales Invoice",
|
||||
"docstatus": 0,
|
||||
"doctype": "Print Format",
|
||||
"font": "Default",
|
||||
"html": "",
|
||||
"idx": 0,
|
||||
"line_breaks": 1,
|
||||
"modified": "2019-07-24 20:13:30.259953",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice Return",
|
||||
"owner": "Administrator",
|
||||
"print_format_builder": 0,
|
||||
"print_format_type": "Jinja",
|
||||
"raw_printing": 0,
|
||||
"show_section_headings": 1,
|
||||
"standard": "Yes"
|
||||
}
|
||||
@@ -73,6 +73,12 @@ frappe.query_reports["Accounts Payable"] = {
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname":"payment_terms_template",
|
||||
"label": __("Payment Terms Template"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Payment Terms Template"
|
||||
},
|
||||
{
|
||||
"fieldname":"supplier_group",
|
||||
"label": __("Supplier Group"),
|
||||
|
||||
@@ -63,6 +63,12 @@ frappe.query_reports["Accounts Payable Summary"] = {
|
||||
"fieldtype": "Link",
|
||||
"options": "Supplier"
|
||||
},
|
||||
{
|
||||
"fieldname":"payment_terms_template",
|
||||
"label": __("Payment Terms Template"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Payment Terms Template"
|
||||
},
|
||||
{
|
||||
"fieldname":"supplier_group",
|
||||
"label": __("Supplier Group"),
|
||||
|
||||
@@ -1,275 +1,269 @@
|
||||
<style>
|
||||
.print-format {
|
||||
padding: 4mm;
|
||||
font-size: 8.0pt !important;
|
||||
}
|
||||
.print-format td {
|
||||
vertical-align:middle !important;
|
||||
}
|
||||
</style>
|
||||
.print-format {
|
||||
padding: 4mm;
|
||||
font-size: 8.0pt !important;
|
||||
}
|
||||
.print-format td {
|
||||
vertical-align:middle !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<h2 class="text-center" style="margin-top:0">{%= __(report.report_name) %}</h2>
|
||||
<h4 class="text-center">
|
||||
{% if (filters.customer_name) { %}
|
||||
{%= filters.customer_name %}
|
||||
{% } else { %}
|
||||
{%= filters.customer || filters.supplier %}
|
||||
{% } %}
|
||||
</h4>
|
||||
<h6 class="text-center">
|
||||
{% if (filters.tax_id) { %}
|
||||
{%= __("Tax Id: ")%} {%= filters.tax_id %}
|
||||
<h2 class="text-center" style="margin-top:0">{%= __(report.report_name) %}</h2>
|
||||
<h4 class="text-center">
|
||||
{% if (filters.customer_name) { %}
|
||||
{%= filters.customer_name %}
|
||||
{% } else { %}
|
||||
{%= filters.customer || filters.supplier %}
|
||||
{% } %}
|
||||
</h6>
|
||||
<h5 class="text-center">
|
||||
{%= __(filters.ageing_based_on) %}
|
||||
{%= __("Until") %}
|
||||
{%= frappe.datetime.str_to_user(filters.report_date) %}
|
||||
</h5>
|
||||
</h4>
|
||||
<h6 class="text-center">
|
||||
{% if (filters.tax_id) { %}
|
||||
{%= __("Tax Id: ")%} {%= filters.tax_id %}
|
||||
{% } %}
|
||||
</h6>
|
||||
<h5 class="text-center">
|
||||
{%= __(filters.ageing_based_on) %}
|
||||
{%= __("Until") %}
|
||||
{%= frappe.datetime.str_to_user(filters.report_date) %}
|
||||
</h5>
|
||||
|
||||
<div class="clearfix">
|
||||
<div class="pull-left">
|
||||
{% if(filters.payment_terms) { %}
|
||||
<strong>{%= __("Payment Terms") %}:</strong> {%= filters.payment_terms %}
|
||||
{% } %}
|
||||
<div class="clearfix">
|
||||
<div class="pull-left">
|
||||
{% if(filters.payment_terms) { %}
|
||||
<strong>{%= __("Payment Terms") %}:</strong> {%= filters.payment_terms %}
|
||||
{% } %}
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
{% if(filters.credit_limit) { %}
|
||||
<strong>{%= __("Credit Limit") %}:</strong> {%= format_currency(filters.credit_limit) %}
|
||||
{% } %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
{% if(filters.credit_limit) { %}
|
||||
<strong>{%= __("Credit Limit") %}:</strong> {%= format_currency(filters.credit_limit) %}
|
||||
|
||||
{% if(filters.show_future_payments) { %}
|
||||
{% var balance_row = data.slice(-1).pop();
|
||||
var range1 = report.columns[11].label;
|
||||
var range2 = report.columns[12].label;
|
||||
var range3 = report.columns[13].label;
|
||||
var range4 = report.columns[14].label;
|
||||
var range5 = report.columns[15].label;
|
||||
%}
|
||||
{% if(balance_row) { %}
|
||||
<table class="table table-bordered table-condensed">
|
||||
<caption class="text-right">(Amount in {%= data[0]["currency"] || "" %})</caption>
|
||||
<colgroup>
|
||||
<col style="width: 30mm;">
|
||||
<col style="width: 18mm;">
|
||||
<col style="width: 18mm;">
|
||||
<col style="width: 18mm;">
|
||||
<col style="width: 18mm;">
|
||||
<col style="width: 18mm;">
|
||||
<col style="width: 18mm;">
|
||||
<col style="width: 18mm;">
|
||||
</colgroup>
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{%= __(" ") %}</th>
|
||||
<th>{%= __(range1) %}</th>
|
||||
<th>{%= __(range2) %}</th>
|
||||
<th>{%= __(range3) %}</th>
|
||||
<th>{%= __(range4) %}</th>
|
||||
<th>{%= __(range5) %}</th>
|
||||
<th>{%= __("Total") %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{%= __("Total Outstanding") %}</td>
|
||||
<td class="text-right">{%= format_number(balance_row["range1"], null, 2) %}</td>
|
||||
<td class="text-right">{%= format_currency(balance_row["range2"]) %}</td>
|
||||
<td class="text-right">{%= format_currency(balance_row["range3"]) %}</td>
|
||||
<td class="text-right">{%= format_currency(balance_row["range4"]) %}</td>
|
||||
<td class="text-right">{%= format_currency(balance_row["range5"]) %}</td>
|
||||
<td class="text-right">
|
||||
{%= format_currency(flt(balance_row["outstanding"]), data[data.length-1]["currency"]) %}
|
||||
</td>
|
||||
</tr>
|
||||
<td>{%= __("Future Payments") %}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td class="text-right">
|
||||
{%= format_currency(flt(balance_row[("future_amount")]), data[data.length-1]["currency"]) %}
|
||||
</td>
|
||||
<tr class="cvs-footer">
|
||||
<th class="text-left">{%= __("Cheques Required") %}</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th class="text-right">
|
||||
{%= format_currency(flt(balance_row["outstanding"] - balance_row[("future_amount")]), data[data.length-1]["currency"]) %}</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
{% } %}
|
||||
{% } %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if(filters.show_pdc_in_print) { %}
|
||||
{% var balance_row = data.slice(-1).pop();
|
||||
var range1 = report.columns[11].label;
|
||||
var range2 = report.columns[12].label;
|
||||
var range3 = report.columns[13].label;
|
||||
var range4 = report.columns[14].label;
|
||||
var range5 = report.columns[15].label;
|
||||
var range6 = report.columns[16].label;
|
||||
%}
|
||||
{% if(balance_row) { %}
|
||||
<table class="table table-bordered table-condensed">
|
||||
<caption class="text-right">(Amount in {%= data[0][__("currency")] || "" %})</caption>
|
||||
<colgroup>
|
||||
<col style="width: 30mm;">
|
||||
<col style="width: 18mm;">
|
||||
<col style="width: 18mm;">
|
||||
<col style="width: 18mm;">
|
||||
<col style="width: 18mm;">
|
||||
<col style="width: 18mm;">
|
||||
<col style="width: 18mm;">
|
||||
<col style="width: 18mm;">
|
||||
<col style="width: 18mm;">
|
||||
</colgroup>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{%= __(" ") %}</th>
|
||||
<th>{%= __(range1) %}</th>
|
||||
<th>{%= __(range2) %}</th>
|
||||
<th>{%= __(range3) %}</th>
|
||||
<th>{%= __(range4) %}</th>
|
||||
<th>{%= __(range5) %}</th>
|
||||
<th>{%= __(range6) %}</th>
|
||||
<th>{%= __("Total") %}</th>
|
||||
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
|
||||
<th style="width: 10%">{%= __("Date") %}</th>
|
||||
<th style="width: 4%">{%= __("Age (Days)") %}</th>
|
||||
|
||||
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %}
|
||||
<th style="width: 14%">{%= __("Reference") %}</th>
|
||||
<th style="width: 10%">{%= __("Sales Person") %}</th>
|
||||
{% } else { %}
|
||||
<th style="width: 24%">{%= __("Reference") %}</th>
|
||||
{% } %}
|
||||
{% if(!filters.show_future_payments) { %}
|
||||
<th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
|
||||
{% } %}
|
||||
<th style="width: 10%; text-align: right">{%= __("Invoiced Amount") %}</th>
|
||||
{% if(!filters.show_future_payments) { %}
|
||||
<th style="width: 10%; text-align: right">{%= __("Paid Amount") %}</th>
|
||||
<th style="width: 10%; text-align: right">{%= report.report_name === "Accounts Receivable" ? __('Credit Note') : __('Debit Note') %}</th>
|
||||
{% } %}
|
||||
<th style="width: 10%; text-align: right">{%= __("Outstanding Amount") %}</th>
|
||||
{% if(filters.show_future_payments) { %}
|
||||
{% if(report.report_name === "Accounts Receivable") { %}
|
||||
<th style="width: 12%">{%= __("Customer LPO No.") %}</th>
|
||||
{% } %}
|
||||
<th style="width: 10%">{%= __("Future Payment Ref") %}</th>
|
||||
<th style="width: 10%">{%= __("Future Payment Amount") %}</th>
|
||||
<th style="width: 10%">{%= __("Remaining Balance") %}</th>
|
||||
{% } %}
|
||||
{% } else { %}
|
||||
<th style="width: 40%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
|
||||
<th style="width: 15%">{%= __("Total Invoiced Amount") %}</th>
|
||||
<th style="width: 15%">{%= __("Total Paid Amount") %}</th>
|
||||
<th style="width: 15%">{%= report.report_name === "Accounts Receivable Summary" ? __('Credit Note Amount') : __('Debit Note Amount') %}</th>
|
||||
<th style="width: 15%">{%= __("Total Outstanding Amount") %}</th>
|
||||
{% } %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{%= __("Total Outstanding") %}</td>
|
||||
<td class="text-right">{%= format_number(balance_row[range1], null, 2) %}</td>
|
||||
<td class="text-right">{%= format_currency(balance_row[range2]) %}</td>
|
||||
<td class="text-right">{%= format_currency(balance_row[range3]) %}</td>
|
||||
<td class="text-right">{%= format_currency(balance_row[range4]) %}</td>
|
||||
<td class="text-right">{%= format_currency(balance_row[range5]) %}</td>
|
||||
<td class="text-right">{%= format_currency(balance_row[range6]) %}</td>
|
||||
<td class="text-right">
|
||||
{%= format_currency(flt(balance_row[("outstanding_amount")]), data[data.length-1]["currency"]) %}
|
||||
</td>
|
||||
</tr>
|
||||
<td>{%= __("PDC/LC") %}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td class="text-right">
|
||||
{%= format_currency(flt(balance_row[("pdc/lc_amount")]), data[data.length-1]["currency"]) %}
|
||||
</td>
|
||||
<tr class="cvs-footer">
|
||||
<th class="text-left">{%= __("Cheques Required") %}</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th class="text-right">
|
||||
{%= format_currency(flt(balance_row[("outstanding_amount")]-balance_row[("pdc/lc_amount")]), data[data.length-1]["currency"]) %}</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
{% } %}
|
||||
{% } %}
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
|
||||
<th style="width: 10%">{%= __("Date") %}</th>
|
||||
<th style="width: 4%">{%= __("Age (Days)") %}</th>
|
||||
|
||||
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %}
|
||||
<th style="width: 14%">{%= __("Reference") %}</th>
|
||||
<th style="width: 10%">{%= __("Sales Person") %}</th>
|
||||
{% } else { %}
|
||||
<th style="width: 24%">{%= __("Reference") %}</th>
|
||||
{% } %}
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
<th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
|
||||
{% } %}
|
||||
<th style="width: 10%; text-align: right">{%= __("Invoiced Amount") %}</th>
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
<th style="width: 10%; text-align: right">{%= __("Paid Amount") %}</th>
|
||||
<th style="width: 10%; text-align: right">{%= report.report_name === "Accounts Receivable" ? __('Credit Note') : __('Debit Note') %}</th>
|
||||
{% } %}
|
||||
<th style="width: 10%; text-align: right">{%= __("Outstanding Amount") %}</th>
|
||||
{% if(filters.show_pdc_in_print) { %}
|
||||
{% if(report.report_name === "Accounts Receivable") { %}
|
||||
<th style="width: 12%">{%= __("Customer LPO No.") %}</th>
|
||||
{% } %}
|
||||
<th style="width: 10%">{%= __("PDC/LC Ref") %}</th>
|
||||
<th style="width: 10%">{%= __("PDC/LC Amount") %}</th>
|
||||
<th style="width: 10%">{%= __("Remaining Balance") %}</th>
|
||||
{% } %}
|
||||
{% } else { %}
|
||||
<th style="width: 40%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
|
||||
<th style="width: 15%">{%= __("Total Invoiced Amount") %}</th>
|
||||
<th style="width: 15%">{%= __("Total Paid Amount") %}</th>
|
||||
<th style="width: 15%">{%= report.report_name === "Accounts Receivable Summary" ? __('Credit Note Amount') : __('Debit Note Amount') %}</th>
|
||||
<th style="width: 15%">{%= __("Total Outstanding Amount") %}</th>
|
||||
{% } %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for(var i=0, l=data.length; i<l; i++) { %}
|
||||
<tr>
|
||||
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
|
||||
{% if(data[i][__("Customer")] || data[i][__("Supplier")]) { %}
|
||||
<td>{%= frappe.datetime.str_to_user(data[i]["posting_date"]) %}</td>
|
||||
<td style="text-align: right">{%= data[i][__("Age (Days)")] %}</td>
|
||||
<td>
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
{%= data[i]["voucher_type"] %}
|
||||
<br>
|
||||
{% } %}
|
||||
{%= data[i]["voucher_no"] %}
|
||||
</td>
|
||||
|
||||
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %}
|
||||
<td>{%= data[i]["sales_person"] %}</td>
|
||||
{% } %}
|
||||
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
<td>
|
||||
{% if(!(filters.customer || filters.supplier)) { %}
|
||||
{%= data[i][__("Customer")] || data[i][__("Supplier")] %}
|
||||
{% if(data[i][__("Customer Name")] && data[i][__("Customer Name")] != data[i][__("Customer")]) { %}
|
||||
<br> {%= data[i][__("Customer Name")] %}
|
||||
{% } else if(data[i][__("Supplier Name")] != data[i][__("Supplier")]) { %}
|
||||
<br> {%= data[i][__("Supplier Name")] %}
|
||||
{% for(var i=0, l=data.length; i<l; i++) { %}
|
||||
<tr>
|
||||
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
|
||||
{% if(data[i]["party"]) { %}
|
||||
<td>{%= frappe.datetime.str_to_user(data[i]["posting_date"]) %}</td>
|
||||
<td style="text-align: right">{%= data[i]["age"] %}</td>
|
||||
<td>
|
||||
{% if(!filters.show_future_payments) { %}
|
||||
{%= data[i]["voucher_type"] %}
|
||||
<br>
|
||||
{% } %}
|
||||
{%= data[i]["voucher_no"] %}
|
||||
</td>
|
||||
|
||||
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %}
|
||||
<td>{%= data[i]["sales_person"] %}</td>
|
||||
{% } %}
|
||||
<div>
|
||||
{% if data[i][__("Remarks")] %}
|
||||
{%= __("Remarks") %}:
|
||||
{%= data[i][__("Remarks")] %}
|
||||
{% } %}
|
||||
</div>
|
||||
</td>
|
||||
{% } %}
|
||||
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["invoiced_amount"], data[i]["currency"]) %}</td>
|
||||
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["paid_amount"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">
|
||||
{%= report.report_name === "Accounts Receivable" ? format_currency(data[i]["credit_note"], data[i]["currency"]) : format_currency(data[i]["debit_note"], data[i]["currency"]) %}</td>
|
||||
{% } %}
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["outstanding_amount"], data[i]["currency"]) %}</td>
|
||||
|
||||
{% if(filters.show_pdc_in_print) { %}
|
||||
{% if(report.report_name === "Accounts Receivable") { %}
|
||||
<td style="text-align: right">
|
||||
{%= data[i]["po_no"] %}</td>
|
||||
{% } %}
|
||||
<td style="text-align: right">{%= data[i][("pdc/lc_ref")] %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i][("pdc/lc_amount")], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i][("remaining_balance")], data[i]["currency"]) %}</td>
|
||||
{% } %}
|
||||
{% } else { %}
|
||||
<td></td>
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
<td></td>
|
||||
{% } %}
|
||||
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %}
|
||||
<td></td>
|
||||
{% } %}
|
||||
<td></td>
|
||||
<td style="text-align: right"><b>{%= __("Total") %}</b></td>
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["invoiced_amount"], data[i]["currency"] ) %}</td>
|
||||
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["paid_amount"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= report.report_name === "Accounts Receivable" ? format_currency(data[i]["credit_note"], data[i]["currency"]) : format_currency(data[i]["debit_note"], data[i]["currency"]) %} </td>
|
||||
{% } %}
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["outstanding_amount"], data[i]["currency"]) %}</td>
|
||||
|
||||
{% if(filters.show_pdc_in_print) { %}
|
||||
{% if(report.report_name === "Accounts Receivable") { %}
|
||||
<td style="text-align: right">
|
||||
{%= data[i][__("Customer LPO")] %}</td>
|
||||
{% } %}
|
||||
<td style="text-align: right">{%= data[i][("pdc/lc_ref")] %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i][("pdc/lc_amount")], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i][("remaining_balance")], data[i]["currency"]) %}</td>
|
||||
{% } %}
|
||||
{% } %}
|
||||
{% } else { %}
|
||||
{% if(data[i][__("Customer")] || data[i][__("Supplier")]|| " ") { %}
|
||||
{% if((data[i][__("Customer")] || data[i][__("Supplier")]) != __("'Total'")) { %}
|
||||
{% if(!filters.show_future_payments) { %}
|
||||
<td>
|
||||
{% if(!(filters.customer || filters.supplier)) { %}
|
||||
{%= data[i][__("Customer")] || data[i][__("Supplier")] %}
|
||||
{% if(data[i][__("Customer Name")] && data[i][__("Customer Name")] != data[i][__("Customer")]) { %}
|
||||
<br> {%= data[i][__("Customer Name")] %}
|
||||
{% } else if(data[i][__("Supplier Name")] != data[i][__("Supplier")]) { %}
|
||||
<br> {%= data[i][__("Supplier Name")] %}
|
||||
{%= data[i]["party"] %}
|
||||
{% if(data[i]["customer_name"] && data[i]["customer_name"] != data[i]["party"]) { %}
|
||||
<br> {%= data[i]["customer_name"] %}
|
||||
{% } else if(data[i]["supplier_name"] != data[i]["party"]) { %}
|
||||
<br> {%= data[i]["supplier_name"] %}
|
||||
{% } %}
|
||||
{% } %}
|
||||
<br>{%= __("Remarks") %}:
|
||||
{%= data[i][__("Remarks")] %}
|
||||
<div>
|
||||
{% if data[i]["remarks"] %}
|
||||
{%= __("Remarks") %}:
|
||||
{%= data[i]["remarks"] %}
|
||||
{% } %}
|
||||
</div>
|
||||
</td>
|
||||
{% } %}
|
||||
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["invoiced"], data[i]["currency"]) %}</td>
|
||||
|
||||
{% if(!filters.show_future_payments) { %}
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["paid"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %}</td>
|
||||
{% } %}
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["outstanding"], data[i]["currency"]) %}</td>
|
||||
|
||||
{% if(filters.show_future_payments) { %}
|
||||
{% if(report.report_name === "Accounts Receivable") { %}
|
||||
<td style="text-align: right">
|
||||
{%= data[i]["po_no"] %}</td>
|
||||
{% } %}
|
||||
<td style="text-align: right">{%= data[i]["future_ref"] %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["future_amount"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["remaining_balance"], data[i]["currency"]) %}</td>
|
||||
{% } %}
|
||||
{% } else { %}
|
||||
<td><b>{%= __("Total") %}</b></td>
|
||||
<td></td>
|
||||
{% if(!filters.show_future_payments) { %}
|
||||
<td></td>
|
||||
{% } %}
|
||||
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %}
|
||||
<td></td>
|
||||
{% } %}
|
||||
<td></td>
|
||||
<td style="text-align: right"><b>{%= __("Total") %}</b></td>
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["invoiced"], data[i]["currency"] ) %}</td>
|
||||
|
||||
{% if(!filters.show_future_payments) { %}
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["paid"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %} </td>
|
||||
{% } %}
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["outstanding"], data[i]["currency"]) %}</td>
|
||||
|
||||
{% if(filters.show_future_payments) { %}
|
||||
{% if(report.report_name === "Accounts Receivable") { %}
|
||||
<td style="text-align: right">
|
||||
{%= data[i]["po_no"] %}</td>
|
||||
{% } %}
|
||||
<td style="text-align: right">{%= data[i]["future_ref"] %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["future_amount"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["remaining_balance"], data[i]["currency"]) %}</td>
|
||||
{% } %}
|
||||
{% } %}
|
||||
{% } else { %}
|
||||
{% if(data[i]["party"]|| " ") { %}
|
||||
{% if((data[i]["party"]) != __("'Total'")) { %}
|
||||
<td>
|
||||
{% if(!(filters.customer || filters.supplier)) { %}
|
||||
{%= data[i]["party"] %}
|
||||
{% if(data[i]["customer_name"] && data[i]["customer_name"] != data[i]["party"]) { %}
|
||||
<br> {%= data[i]["customer_name"] %}
|
||||
{% } else if(data[i]["supplier_name"] != data[i]["party"]) { %}
|
||||
<br> {%= data[i]["supplier_name"] %}
|
||||
{% } %}
|
||||
{% } %}
|
||||
<br>{%= __("Remarks") %}:
|
||||
{%= data[i]["remarks"] %}
|
||||
</td>
|
||||
{% } else { %}
|
||||
<td><b>{%= __("Total") %}</b></td>
|
||||
{% } %}
|
||||
<td style="text-align: right">{%= format_currency(data[i]["invoiced"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["paid"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["outstanding"], data[i]["currency"]) %}</td>
|
||||
{% } %}
|
||||
<td style="text-align: right">{%= format_currency(data[i][("total_invoiced_amt")], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i][("total_paid_amt")], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= report.report_name === "Accounts Receivable Summary" ? format_currency(data[i][__("credit_note_amt")], data[i]["currency"]) : format_currency(data[i][__("debit_note_amt")], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i][("total_outstanding_amt")], data[i]["currency"]) %}</td>
|
||||
{% } %}
|
||||
</tr>
|
||||
{% } %}
|
||||
</tr>
|
||||
{% } %}
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="text-right text-muted">{{ __("Printed On ") }}{%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}</p>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="text-right text-muted">{{ __("Printed On ") }}{%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}</p>
|
||||
|
||||
@@ -115,13 +115,18 @@ frappe.query_reports["Accounts Receivable"] = {
|
||||
"fieldtype": "Check",
|
||||
},
|
||||
{
|
||||
"fieldname":"show_pdc_in_print",
|
||||
"label": __("Show PDC in Print"),
|
||||
"fieldname":"show_future_payments",
|
||||
"label": __("Show Future Payments"),
|
||||
"fieldtype": "Check",
|
||||
},
|
||||
{
|
||||
"fieldname":"show_sales_person_in_print",
|
||||
"label": __("Show Sales Person in Print"),
|
||||
"fieldname":"show_delivery_notes",
|
||||
"label": __("Show Delivery Notes"),
|
||||
"fieldtype": "Check",
|
||||
},
|
||||
{
|
||||
"fieldname":"show_sales_person",
|
||||
"label": __("Show Sales Person"),
|
||||
"fieldtype": "Check",
|
||||
},
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,33 +14,44 @@ class TestAccountsReceivable(unittest.TestCase):
|
||||
|
||||
filters = {
|
||||
'company': '_Test Company 2',
|
||||
'based_on_payment_terms': 1
|
||||
'based_on_payment_terms': 1,
|
||||
'report_date': today(),
|
||||
'range1': 30,
|
||||
'range2': 60,
|
||||
'range3': 90,
|
||||
'range4': 120
|
||||
}
|
||||
|
||||
# check invoice grand total and invoiced column's value for 3 payment terms
|
||||
name = make_sales_invoice()
|
||||
report = execute(filters)
|
||||
|
||||
expected_data = [[100,30], [100,50], [100,20]]
|
||||
expected_data = [[100, 30], [100, 50], [100, 20]]
|
||||
|
||||
self.assertEqual(expected_data[0], report[1][0][7:9])
|
||||
self.assertEqual(expected_data[1], report[1][1][7:9])
|
||||
self.assertEqual(expected_data[2], report[1][2][7:9])
|
||||
for i in range(3):
|
||||
row = report[1][i-1]
|
||||
self.assertEqual(expected_data[i-1], [row.invoice_grand_total, row.invoiced])
|
||||
|
||||
# check invoice grand total, invoiced, paid and outstanding column's value after payment
|
||||
make_payment(name)
|
||||
report = execute(filters)
|
||||
|
||||
expected_data_after_payment = [[100,50], [100,20]]
|
||||
expected_data_after_payment = [[100, 50, 10, 40], [100, 20, 0, 20]]
|
||||
|
||||
self.assertEqual(expected_data_after_payment[0], report[1][0][7:9])
|
||||
self.assertEqual(expected_data_after_payment[1], report[1][1][7:9])
|
||||
for i in range(2):
|
||||
row = report[1][i-1]
|
||||
self.assertEqual(expected_data_after_payment[i-1],
|
||||
[row.invoice_grand_total, row.invoiced, row.paid, row.outstanding])
|
||||
|
||||
# check invoice grand total, invoiced, paid and outstanding column's value after credit note
|
||||
make_credit_note(name)
|
||||
report = execute(filters)
|
||||
|
||||
expected_data_after_credit_note = [[100,100,30,100,-30]]
|
||||
|
||||
self.assertEqual(expected_data_after_credit_note[0], report[1][0][7:12])
|
||||
expected_data_after_credit_note = [100, 0, 0, 40, -40]
|
||||
|
||||
row = report[1][0]
|
||||
self.assertEqual(expected_data_after_credit_note,
|
||||
[row.invoice_grand_total, row.invoiced, row.paid, row.credit_note, row.outstanding])
|
||||
|
||||
def make_sales_invoice():
|
||||
frappe.set_user("Administrator")
|
||||
@@ -64,7 +75,7 @@ def make_sales_invoice():
|
||||
return si.name
|
||||
|
||||
def make_payment(docname):
|
||||
pe = get_payment_entry("Sales Invoice", docname, bank_account="Cash - _TC2", party_amount=30)
|
||||
pe = get_payment_entry("Sales Invoice", docname, bank_account="Cash - _TC2", party_amount=40)
|
||||
pe.paid_from = "Debtors - _TC2"
|
||||
pe.insert()
|
||||
pe.submit()
|
||||
|
||||
@@ -3,236 +3,11 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _, scrub
|
||||
from frappe.utils import flt
|
||||
from frappe import _
|
||||
from frappe.utils import flt, cint
|
||||
from erpnext.accounts.party import get_partywise_advanced_payment_amount
|
||||
from erpnext.accounts.report.accounts_receivable.accounts_receivable import ReceivablePayableReport
|
||||
|
||||
from six import iteritems
|
||||
from six.moves import zip
|
||||
|
||||
class AccountsReceivableSummary(ReceivablePayableReport):
|
||||
def run(self, args):
|
||||
party_naming_by = frappe.db.get_value(args.get("naming_by")[0], None, args.get("naming_by")[1])
|
||||
return self.get_columns(party_naming_by, args), self.get_data(party_naming_by, args)
|
||||
|
||||
def get_columns(self, party_naming_by, args):
|
||||
columns = [_(args.get("party_type")) + ":Link/" + args.get("party_type") + ":200"]
|
||||
|
||||
if party_naming_by == "Naming Series":
|
||||
columns += [ args.get("party_type") + " Name::140"]
|
||||
|
||||
credit_debit_label = "Credit Note Amt" if args.get('party_type') == 'Customer' else "Debit Note Amt"
|
||||
|
||||
columns += [{
|
||||
"label": _("Advance Amount"),
|
||||
"fieldname": "advance_amount",
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 100
|
||||
},{
|
||||
"label": _("Total Invoiced Amt"),
|
||||
"fieldname": "total_invoiced_amt",
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Total Paid Amt"),
|
||||
"fieldname": "total_paid_amt",
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 100
|
||||
}]
|
||||
|
||||
columns += [
|
||||
{
|
||||
"label": _(credit_debit_label),
|
||||
"fieldname": scrub(credit_debit_label),
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 140
|
||||
},
|
||||
{
|
||||
"label": _("Total Outstanding Amt"),
|
||||
"fieldname": "total_outstanding_amt",
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 160
|
||||
},
|
||||
{
|
||||
"label": _("0-" + str(self.filters.range1)),
|
||||
"fieldname": scrub("0-" + str(self.filters.range1)),
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 160
|
||||
},
|
||||
{
|
||||
"label": _(str(self.filters.range1) + "-" + str(self.filters.range2)),
|
||||
"fieldname": scrub(str(self.filters.range1) + "-" + str(self.filters.range2)),
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 160
|
||||
},
|
||||
{
|
||||
"label": _(str(self.filters.range2) + "-" + str(self.filters.range3)),
|
||||
"fieldname": scrub(str(self.filters.range2) + "-" + str(self.filters.range3)),
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 160
|
||||
},
|
||||
{
|
||||
"label": _(str(self.filters.range3) + "-" + str(self.filters.range4)),
|
||||
"fieldname": scrub(str(self.filters.range3) + "-" + str(self.filters.range4)),
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 160
|
||||
},
|
||||
{
|
||||
"label": _(str(self.filters.range4) + _("-Above")),
|
||||
"fieldname": scrub(str(self.filters.range4) + _("-Above")),
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 160
|
||||
}
|
||||
]
|
||||
|
||||
if args.get("party_type") == "Customer":
|
||||
columns += [{
|
||||
"label": _("Territory"),
|
||||
"fieldname": "territory",
|
||||
"fieldtype": "Link",
|
||||
"options": "Territory",
|
||||
"width": 80
|
||||
},
|
||||
{
|
||||
"label": _("Customer Group"),
|
||||
"fieldname": "customer_group",
|
||||
"fieldtype": "Link",
|
||||
"options": "Customer Group",
|
||||
"width": 80
|
||||
},
|
||||
{
|
||||
"label": _("Sales Person"),
|
||||
"fieldtype": "Data",
|
||||
"fieldname": "sales_person",
|
||||
"width": 120,
|
||||
}]
|
||||
|
||||
if args.get("party_type") == "Supplier":
|
||||
columns += [{
|
||||
"label": _("Supplier Group"),
|
||||
"fieldname": "supplier_group",
|
||||
"fieldtype": "Link",
|
||||
"options": "Supplier Group",
|
||||
"width": 80
|
||||
}]
|
||||
|
||||
columns.append({
|
||||
"fieldname": "currency",
|
||||
"label": _("Currency"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Currency",
|
||||
"width": 80
|
||||
})
|
||||
|
||||
return columns
|
||||
|
||||
def get_data(self, party_naming_by, args):
|
||||
data = []
|
||||
|
||||
partywise_total = self.get_partywise_total(party_naming_by, args)
|
||||
|
||||
partywise_advance_amount = get_partywise_advanced_payment_amount(args.get("party_type"),
|
||||
self.filters.get("report_date")) or {}
|
||||
for party, party_dict in iteritems(partywise_total):
|
||||
row = [party]
|
||||
|
||||
if party_naming_by == "Naming Series":
|
||||
row += [self.get_party_name(args.get("party_type"), party)]
|
||||
|
||||
row += [partywise_advance_amount.get(party, 0)]
|
||||
|
||||
paid_amt = 0
|
||||
if party_dict.paid_amt > 0:
|
||||
paid_amt = flt(party_dict.paid_amt - partywise_advance_amount.get(party, 0))
|
||||
|
||||
row += [
|
||||
party_dict.invoiced_amt, paid_amt, party_dict.credit_amt, party_dict.outstanding_amt,
|
||||
party_dict.range1, party_dict.range2, party_dict.range3, party_dict.range4, party_dict.range5
|
||||
]
|
||||
|
||||
if args.get("party_type") == "Customer":
|
||||
row += [self.get_territory(party), self.get_customer_group(party), ", ".join(set(party_dict.sales_person))]
|
||||
if args.get("party_type") == "Supplier":
|
||||
row += [self.get_supplier_group(party)]
|
||||
|
||||
row.append(party_dict.currency)
|
||||
data.append(row)
|
||||
|
||||
return data
|
||||
|
||||
def get_partywise_total(self, party_naming_by, args):
|
||||
party_total = frappe._dict()
|
||||
for d in self.get_voucherwise_data(party_naming_by, args):
|
||||
party_total.setdefault(d.party,
|
||||
frappe._dict({
|
||||
"invoiced_amt": 0,
|
||||
"paid_amt": 0,
|
||||
"credit_amt": 0,
|
||||
"outstanding_amt": 0,
|
||||
"range1": 0,
|
||||
"range2": 0,
|
||||
"range3": 0,
|
||||
"range4": 0,
|
||||
"range5": 0,
|
||||
"sales_person": []
|
||||
})
|
||||
)
|
||||
for k in list(party_total[d.party]):
|
||||
if k not in ["currency", "sales_person"]:
|
||||
party_total[d.party][k] += flt(d.get(k, 0))
|
||||
|
||||
party_total[d.party].currency = d.currency
|
||||
|
||||
if d.sales_person:
|
||||
party_total[d.party].sales_person.append(d.sales_person)
|
||||
|
||||
return party_total
|
||||
|
||||
def get_voucherwise_data(self, party_naming_by, args):
|
||||
voucherwise_data = ReceivablePayableReport(self.filters).run(args)[1]
|
||||
|
||||
cols = ["posting_date", "party"]
|
||||
|
||||
if party_naming_by == "Naming Series":
|
||||
cols += ["party_name"]
|
||||
|
||||
if args.get("party_type") == 'Customer':
|
||||
cols += ["contact"]
|
||||
|
||||
cols += ["voucher_type", "voucher_no", "due_date"]
|
||||
|
||||
if args.get("party_type") == "Supplier":
|
||||
cols += ["bill_no", "bill_date"]
|
||||
|
||||
cols += ["invoiced_amt", "paid_amt", "credit_amt",
|
||||
"outstanding_amt", "age", "range1", "range2", "range3", "range4", "range5", "currency", "pdc/lc_date", "pdc/lc_ref",
|
||||
"pdc/lc_amount"]
|
||||
|
||||
if args.get("party_type") == "Supplier":
|
||||
cols += ["supplier_group", "remarks"]
|
||||
if args.get("party_type") == "Customer":
|
||||
cols += ["po_no", "do_no", "territory", "customer_group", "sales_person", "remarks"]
|
||||
|
||||
return self.make_data_dict(cols, voucherwise_data)
|
||||
|
||||
def make_data_dict(self, cols, data):
|
||||
data_dict = []
|
||||
for d in data:
|
||||
data_dict.append(frappe._dict(zip(cols, d)))
|
||||
|
||||
return data_dict
|
||||
|
||||
def execute(filters=None):
|
||||
args = {
|
||||
@@ -241,3 +16,123 @@ def execute(filters=None):
|
||||
}
|
||||
|
||||
return AccountsReceivableSummary(filters).run(args)
|
||||
|
||||
class AccountsReceivableSummary(ReceivablePayableReport):
|
||||
def run(self, args):
|
||||
self.party_type = args.get('party_type')
|
||||
self.party_naming_by = frappe.db.get_value(args.get("naming_by")[0], None, args.get("naming_by")[1])
|
||||
self.get_columns()
|
||||
self.get_data(args)
|
||||
return self.columns, self.data
|
||||
|
||||
def get_data(self, args):
|
||||
self.data = []
|
||||
|
||||
self.receivables = ReceivablePayableReport(self.filters).run(args)[1]
|
||||
|
||||
self.get_party_total(args)
|
||||
|
||||
party_advance_amount = get_partywise_advanced_payment_amount(self.party_type,
|
||||
self.filters.report_date) or {}
|
||||
|
||||
for party, party_dict in iteritems(self.party_total):
|
||||
if party_dict.outstanding == 0:
|
||||
continue
|
||||
|
||||
row = frappe._dict()
|
||||
|
||||
row.party = party
|
||||
if self.party_naming_by == "Naming Series":
|
||||
row.party_name = frappe.get_cached_value(self.party_type,
|
||||
party, frappe.scrub(self.party_type) + "_name")
|
||||
|
||||
row.update(party_dict)
|
||||
|
||||
# Advance against party
|
||||
row.advance = party_advance_amount.get(party, 0)
|
||||
|
||||
# In AR/AP, advance shown in paid columns,
|
||||
# but in summary report advance shown in separate column
|
||||
row.paid -= row.advance
|
||||
|
||||
self.data.append(row)
|
||||
|
||||
def get_party_total(self, args):
|
||||
self.party_total = frappe._dict()
|
||||
|
||||
for d in self.receivables:
|
||||
self.init_party_total(d)
|
||||
|
||||
# Add all amount columns
|
||||
for k in list(self.party_total[d.party]):
|
||||
if k not in ["currency", "sales_person"]:
|
||||
|
||||
self.party_total[d.party][k] += d.get(k, 0.0)
|
||||
|
||||
# set territory, customer_group, sales person etc
|
||||
self.set_party_details(d)
|
||||
|
||||
def init_party_total(self, row):
|
||||
self.party_total.setdefault(row.party, frappe._dict({
|
||||
"invoiced": 0.0,
|
||||
"paid": 0.0,
|
||||
"credit_note": 0.0,
|
||||
"outstanding": 0.0,
|
||||
"range1": 0.0,
|
||||
"range2": 0.0,
|
||||
"range3": 0.0,
|
||||
"range4": 0.0,
|
||||
"range5": 0.0,
|
||||
"sales_person": []
|
||||
}))
|
||||
|
||||
def set_party_details(self, row):
|
||||
self.party_total[row.party].currency = row.currency
|
||||
|
||||
for key in ('territory', 'customer_group', 'supplier_group'):
|
||||
if row.get(key):
|
||||
self.party_total[row.party][key] = row.get(key)
|
||||
|
||||
if row.sales_person:
|
||||
self.party_total[row.party].sales_person.append(row.sales_person)
|
||||
|
||||
def get_columns(self):
|
||||
self.columns = []
|
||||
self.add_column(label=_(self.party_type), fieldname='party',
|
||||
fieldtype='Link', options=self.party_type, width=180)
|
||||
|
||||
if self.party_naming_by == "Naming Series":
|
||||
self.add_column(_('{0} Name').format(self.party_type),
|
||||
fieldname = 'party_name', fieldtype='Data')
|
||||
|
||||
credit_debit_label = "Credit Note" if self.party_type == 'Customer' else "Debit Note"
|
||||
|
||||
self.add_column(_('Advance Amount'), fieldname='advance')
|
||||
self.add_column(_('Invoiced Amount'), fieldname='invoiced')
|
||||
self.add_column(_('Paid Amount'), fieldname='paid')
|
||||
self.add_column(_(credit_debit_label), fieldname='credit_note')
|
||||
self.add_column(_('Outstanding Amount'), fieldname='outstanding')
|
||||
|
||||
self.setup_ageing_columns()
|
||||
|
||||
if self.party_type == "Customer":
|
||||
self.add_column(label=_('Territory'), fieldname='territory', fieldtype='Link',
|
||||
options='Territory')
|
||||
self.add_column(label=_('Customer Group'), fieldname='customer_group', fieldtype='Link',
|
||||
options='Customer Group')
|
||||
if self.filters.show_sales_person:
|
||||
self.add_column(label=_('Sales Person'), fieldname='sales_person', fieldtype='Data')
|
||||
else:
|
||||
self.add_column(label=_('Supplier Group'), fieldname='supplier_group', fieldtype='Link',
|
||||
options='Supplier Group')
|
||||
|
||||
self.add_column(label=_('Currency'), fieldname='currency', fieldtype='Link',
|
||||
options='Currency', width=80)
|
||||
|
||||
def setup_ageing_columns(self):
|
||||
for i, label in enumerate(["0-{range1}".format(range1=self.filters["range1"]),
|
||||
"{range1}-{range2}".format(range1=cint(self.filters["range1"])+ 1, range2=self.filters["range2"]),
|
||||
"{range2}-{range3}".format(range2=cint(self.filters["range2"])+ 1, range3=self.filters["range3"]),
|
||||
"{range3}-{range4}".format(range3=cint(self.filters["range3"])+ 1, range4=self.filters["range4"]),
|
||||
"{range4}-{above}".format(range4=cint(self.filters["range4"])+ 1, above=_("Above"))]):
|
||||
self.add_column(label=label, fieldname='range' + str(i+1))
|
||||
|
||||
@@ -4,126 +4,141 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import formatdate, getdate, flt, add_days
|
||||
from frappe.utils import formatdate, flt, add_days
|
||||
|
||||
|
||||
def execute(filters=None):
|
||||
filters.day_before_from_date = add_days(filters.from_date, -1)
|
||||
columns, data = get_columns(filters), get_data(filters)
|
||||
return columns, data
|
||||
|
||||
|
||||
|
||||
def get_data(filters):
|
||||
data = []
|
||||
|
||||
|
||||
asset_categories = get_asset_categories(filters)
|
||||
assets = get_assets(filters)
|
||||
asset_costs = get_asset_costs(assets, filters)
|
||||
asset_depreciations = get_accumulated_depreciations(assets, filters)
|
||||
|
||||
|
||||
for asset_category in asset_categories:
|
||||
row = frappe._dict()
|
||||
row.asset_category = asset_category
|
||||
row.update(asset_costs.get(asset_category))
|
||||
# row.asset_category = asset_category
|
||||
row.update(asset_category)
|
||||
|
||||
row.cost_as_on_to_date = (flt(row.cost_as_on_from_date) + flt(row.cost_of_new_purchase) -
|
||||
flt(row.cost_of_sold_asset) - flt(row.cost_of_scrapped_asset))
|
||||
|
||||
row.update(next(asset for asset in assets if asset["asset_category"] == asset_category.get("asset_category", "")))
|
||||
row.accumulated_depreciation_as_on_to_date = (flt(row.accumulated_depreciation_as_on_from_date) +
|
||||
flt(row.depreciation_amount_during_the_period) - flt(row.depreciation_eliminated))
|
||||
|
||||
row.net_asset_value_as_on_from_date = (flt(row.cost_as_on_from_date) -
|
||||
flt(row.accumulated_depreciation_as_on_from_date))
|
||||
|
||||
row.net_asset_value_as_on_to_date = (flt(row.cost_as_on_to_date) -
|
||||
flt(row.accumulated_depreciation_as_on_to_date))
|
||||
|
||||
row.cost_as_on_to_date = (flt(row.cost_as_on_from_date) + flt(row.cost_of_new_purchase)
|
||||
- flt(row.cost_of_sold_asset) - flt(row.cost_of_scrapped_asset))
|
||||
|
||||
row.update(asset_depreciations.get(asset_category))
|
||||
row.accumulated_depreciation_as_on_to_date = (flt(row.accumulated_depreciation_as_on_from_date) +
|
||||
flt(row.depreciation_amount_during_the_period) - flt(row.depreciation_eliminated))
|
||||
|
||||
row.net_asset_value_as_on_from_date = (flt(row.cost_as_on_from_date) -
|
||||
flt(row.accumulated_depreciation_as_on_from_date))
|
||||
|
||||
row.net_asset_value_as_on_to_date = (flt(row.cost_as_on_to_date) -
|
||||
flt(row.accumulated_depreciation_as_on_to_date))
|
||||
|
||||
data.append(row)
|
||||
|
||||
|
||||
return data
|
||||
|
||||
|
||||
|
||||
def get_asset_categories(filters):
|
||||
return frappe.db.sql_list("""
|
||||
select distinct asset_category from `tabAsset`
|
||||
where docstatus=1 and company=%s and purchase_date <= %s
|
||||
""", (filters.company, filters.to_date))
|
||||
|
||||
return frappe.db.sql("""
|
||||
SELECT asset_category,
|
||||
ifnull(sum(case when purchase_date < %(from_date)s then
|
||||
case when ifnull(disposal_date, 0) = 0 or disposal_date >= %(from_date)s then
|
||||
gross_purchase_amount
|
||||
else
|
||||
0
|
||||
end
|
||||
else
|
||||
0
|
||||
end), 0) as cost_as_on_from_date,
|
||||
ifnull(sum(case when purchase_date >= %(from_date)s then
|
||||
gross_purchase_amount
|
||||
else
|
||||
0
|
||||
end), 0) as cost_of_new_purchase,
|
||||
ifnull(sum(case when ifnull(disposal_date, 0) != 0
|
||||
and disposal_date >= %(from_date)s
|
||||
and disposal_date <= %(to_date)s then
|
||||
case when status = "Sold" then
|
||||
gross_purchase_amount
|
||||
else
|
||||
0
|
||||
end
|
||||
else
|
||||
0
|
||||
end), 0) as cost_of_sold_asset,
|
||||
ifnull(sum(case when ifnull(disposal_date, 0) != 0
|
||||
and disposal_date >= %(from_date)s
|
||||
and disposal_date <= %(to_date)s then
|
||||
case when status = "Scrapped" then
|
||||
gross_purchase_amount
|
||||
else
|
||||
0
|
||||
end
|
||||
else
|
||||
0
|
||||
end), 0) as cost_of_scrapped_asset
|
||||
from `tabAsset`
|
||||
where docstatus=1 and company=%(company)s and purchase_date <= %(to_date)s
|
||||
group by asset_category
|
||||
""", {"to_date": filters.to_date, "from_date": filters.from_date, "company": filters.company}, as_dict=1)
|
||||
|
||||
|
||||
def get_assets(filters):
|
||||
return frappe.db.sql("""
|
||||
select name, asset_category, purchase_date, gross_purchase_amount, disposal_date, status
|
||||
from `tabAsset`
|
||||
where docstatus=1 and company=%s and purchase_date <= %s""",
|
||||
(filters.company, filters.to_date), as_dict=1)
|
||||
|
||||
def get_asset_costs(assets, filters):
|
||||
asset_costs = frappe._dict()
|
||||
for d in assets:
|
||||
asset_costs.setdefault(d.asset_category, frappe._dict({
|
||||
"cost_as_on_from_date": 0,
|
||||
"cost_of_new_purchase": 0,
|
||||
"cost_of_sold_asset": 0,
|
||||
"cost_of_scrapped_asset": 0
|
||||
}))
|
||||
|
||||
costs = asset_costs[d.asset_category]
|
||||
|
||||
if getdate(d.purchase_date) < getdate(filters.from_date):
|
||||
if not d.disposal_date or getdate(d.disposal_date) >= getdate(filters.from_date):
|
||||
costs.cost_as_on_from_date += flt(d.gross_purchase_amount)
|
||||
else:
|
||||
costs.cost_of_new_purchase += flt(d.gross_purchase_amount)
|
||||
|
||||
if d.disposal_date and getdate(d.disposal_date) >= getdate(filters.from_date) \
|
||||
and getdate(d.disposal_date) <= getdate(filters.to_date):
|
||||
if d.status == "Sold":
|
||||
costs.cost_of_sold_asset += flt(d.gross_purchase_amount)
|
||||
elif d.status == "Scrapped":
|
||||
costs.cost_of_scrapped_asset += flt(d.gross_purchase_amount)
|
||||
|
||||
return asset_costs
|
||||
|
||||
def get_accumulated_depreciations(assets, filters):
|
||||
asset_depreciations = frappe._dict()
|
||||
for d in assets:
|
||||
asset = frappe.get_doc("Asset", d.name)
|
||||
|
||||
if d.asset_category in asset_depreciations:
|
||||
asset_depreciations[d.asset_category]['accumulated_depreciation_as_on_from_date'] += asset.opening_accumulated_depreciation
|
||||
else:
|
||||
asset_depreciations.setdefault(d.asset_category, frappe._dict({
|
||||
"accumulated_depreciation_as_on_from_date": asset.opening_accumulated_depreciation,
|
||||
"depreciation_amount_during_the_period": 0,
|
||||
"depreciation_eliminated_during_the_period": 0
|
||||
}))
|
||||
SELECT results.asset_category,
|
||||
sum(results.accumulated_depreciation_as_on_from_date) as accumulated_depreciation_as_on_from_date,
|
||||
sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period,
|
||||
sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period
|
||||
from (SELECT a.asset_category,
|
||||
ifnull(sum(a.opening_accumulated_depreciation +
|
||||
case when ds.schedule_date < %(from_date)s and
|
||||
(ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then
|
||||
ds.depreciation_amount
|
||||
else
|
||||
0
|
||||
end), 0) as accumulated_depreciation_as_on_from_date,
|
||||
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s
|
||||
and a.disposal_date <= %(to_date)s and ds.schedule_date <= a.disposal_date then
|
||||
ds.depreciation_amount
|
||||
else
|
||||
0
|
||||
end), 0) as depreciation_eliminated_during_the_period,
|
||||
|
||||
depr = asset_depreciations[d.asset_category]
|
||||
ifnull(sum(case when ds.schedule_date >= %(from_date)s and ds.schedule_date <= %(to_date)s
|
||||
and (ifnull(a.disposal_date, 0) = 0 or ds.schedule_date <= a.disposal_date) then
|
||||
ds.depreciation_amount
|
||||
else
|
||||
0
|
||||
end), 0) as depreciation_amount_during_the_period
|
||||
from `tabAsset` a, `tabDepreciation Schedule` ds
|
||||
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent
|
||||
group by a.asset_category
|
||||
union
|
||||
SELECT a.asset_category,
|
||||
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
|
||||
and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) then
|
||||
0
|
||||
else
|
||||
a.opening_accumulated_depreciation
|
||||
end), 0) as accumulated_depreciation_as_on_from_date,
|
||||
ifnull(sum(case when a.disposal_date >= %(from_date)s and a.disposal_date <= %(to_date)s then
|
||||
a.opening_accumulated_depreciation
|
||||
else
|
||||
0
|
||||
end), 0) as depreciation_eliminated_during_the_period,
|
||||
0 as depreciation_amount_during_the_period
|
||||
from `tabAsset` a
|
||||
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s
|
||||
and not exists(select * from `tabDepreciation Schedule` ds where a.name = ds.parent)
|
||||
group by a.asset_category) as results
|
||||
group by results.asset_category
|
||||
""", {"to_date": filters.to_date, "from_date": filters.from_date, "company": filters.company}, as_dict=1)
|
||||
|
||||
if not asset.schedules: # if no schedule,
|
||||
if asset.disposal_date:
|
||||
# and disposal is NOT within the period, then opening accumulated depreciation not included
|
||||
if getdate(asset.disposal_date) < getdate(filters.from_date) or getdate(asset.disposal_date) > getdate(filters.to_date):
|
||||
asset_depreciations[d.asset_category]['accumulated_depreciation_as_on_from_date'] = 0
|
||||
|
||||
# if no schedule, and disposal is within period, accumulated dep is the amount eliminated
|
||||
if getdate(asset.disposal_date) >= getdate(filters.from_date) and getdate(asset.disposal_date) <= getdate(filters.to_date):
|
||||
depr.depreciation_eliminated_during_the_period += asset.opening_accumulated_depreciation
|
||||
|
||||
for schedule in asset.get("schedules"):
|
||||
if getdate(schedule.schedule_date) < getdate(filters.from_date):
|
||||
if not asset.disposal_date or getdate(asset.disposal_date) >= getdate(filters.from_date):
|
||||
depr.accumulated_depreciation_as_on_from_date += flt(schedule.depreciation_amount)
|
||||
elif getdate(schedule.schedule_date) <= getdate(filters.to_date):
|
||||
if not asset.disposal_date:
|
||||
depr.depreciation_amount_during_the_period += flt(schedule.depreciation_amount)
|
||||
else:
|
||||
if getdate(schedule.schedule_date) <= getdate(asset.disposal_date):
|
||||
depr.depreciation_amount_during_the_period += flt(schedule.depreciation_amount)
|
||||
|
||||
if asset.disposal_date and getdate(asset.disposal_date) >= getdate(filters.from_date) and getdate(asset.disposal_date) <= getdate(filters.to_date):
|
||||
if getdate(schedule.schedule_date) <= getdate(asset.disposal_date):
|
||||
depr.depreciation_eliminated_during_the_period += flt(schedule.depreciation_amount)
|
||||
|
||||
return asset_depreciations
|
||||
|
||||
def get_columns(filters):
|
||||
return [
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
||||
frappe.query_reports["Balance Sheet"] = erpnext.financial_statements;
|
||||
frappe.query_reports["Balance Sheet"] = $.extend({}, erpnext.financial_statements);
|
||||
|
||||
frappe.query_reports["Balance Sheet"]["filters"].push({
|
||||
"fieldname": "accumulated_values",
|
||||
|
||||
@@ -135,11 +135,11 @@ def get_chart_data(filters, columns, asset, liability, equity):
|
||||
|
||||
datasets = []
|
||||
if asset_data:
|
||||
datasets.append({'name':'Assets', 'values': asset_data})
|
||||
datasets.append({'name':_('Assets'), 'values': asset_data})
|
||||
if liability_data:
|
||||
datasets.append({'name':'Liabilities', 'values': liability_data})
|
||||
datasets.append({'name':_('Liabilities'), 'values': liability_data})
|
||||
if equity_data:
|
||||
datasets.append({'name':'Equity', 'values': equity_data})
|
||||
datasets.append({'name':_('Equity'), 'values': equity_data})
|
||||
|
||||
chart = {
|
||||
"data": {
|
||||
@@ -153,4 +153,4 @@ def get_chart_data(filters, columns, asset, liability, equity):
|
||||
else:
|
||||
chart["type"] = "line"
|
||||
|
||||
return chart
|
||||
return chart
|
||||
|
||||
@@ -12,11 +12,11 @@ def execute(filters=None):
|
||||
columns = get_columns()
|
||||
|
||||
if not filters.get("account"): return columns, []
|
||||
|
||||
|
||||
account_currency = frappe.db.get_value("Account", filters.account, "account_currency")
|
||||
|
||||
data = get_entries(filters)
|
||||
|
||||
|
||||
from erpnext.accounts.utils import get_balance_on
|
||||
balance_as_per_system = get_balance_on(filters["account"], filters["report_date"])
|
||||
|
||||
@@ -24,7 +24,7 @@ def execute(filters=None):
|
||||
for d in data:
|
||||
total_debit += flt(d.debit)
|
||||
total_credit += flt(d.credit)
|
||||
|
||||
|
||||
amounts_not_reflected_in_system = get_amounts_not_reflected_in_system(filters)
|
||||
|
||||
bank_bal = flt(balance_as_per_system) - flt(total_debit) + flt(total_credit) \
|
||||
@@ -39,7 +39,7 @@ def execute(filters=None):
|
||||
"credit": total_credit,
|
||||
"account_currency": account_currency
|
||||
},
|
||||
get_balance_row(_("Cheques and Deposits incorrectly cleared"), amounts_not_reflected_in_system,
|
||||
get_balance_row(_("Cheques and Deposits incorrectly cleared"), amounts_not_reflected_in_system,
|
||||
account_currency),
|
||||
{},
|
||||
get_balance_row(_("Calculated Bank Statement balance"), bank_bal, account_currency)
|
||||
@@ -55,9 +55,15 @@ def get_columns():
|
||||
"fieldtype": "Date",
|
||||
"width": 90
|
||||
},
|
||||
{
|
||||
"fieldname": "payment_document",
|
||||
"label": _("Payment Document Type"),
|
||||
"fieldtype": "Data",
|
||||
"width": 220
|
||||
},
|
||||
{
|
||||
"fieldname": "payment_entry",
|
||||
"label": _("Payment Entry"),
|
||||
"label": _("Payment Document"),
|
||||
"fieldtype": "Dynamic Link",
|
||||
"options": "payment_document",
|
||||
"width": 220
|
||||
@@ -100,7 +106,7 @@ def get_columns():
|
||||
"label": _("Clearance Date"),
|
||||
"fieldtype": "Date",
|
||||
"width": 110
|
||||
},
|
||||
},
|
||||
{
|
||||
"fieldname": "account_currency",
|
||||
"label": _("Currency"),
|
||||
@@ -112,9 +118,9 @@ def get_columns():
|
||||
|
||||
def get_entries(filters):
|
||||
journal_entries = frappe.db.sql("""
|
||||
select "Journal Entry" as payment_document, jv.posting_date,
|
||||
jv.name as payment_entry, jvd.debit_in_account_currency as debit,
|
||||
jvd.credit_in_account_currency as credit, jvd.against_account,
|
||||
select "Journal Entry" as payment_document, jv.posting_date,
|
||||
jv.name as payment_entry, jvd.debit_in_account_currency as debit,
|
||||
jvd.credit_in_account_currency as credit, jvd.against_account,
|
||||
jv.cheque_no as reference_no, jv.cheque_date as ref_date, jv.clearance_date, jvd.account_currency
|
||||
from
|
||||
`tabJournal Entry Account` jvd, `tabJournal Entry` jv
|
||||
@@ -122,13 +128,13 @@ def get_entries(filters):
|
||||
and jvd.account = %(account)s and jv.posting_date <= %(report_date)s
|
||||
and ifnull(jv.clearance_date, '4000-01-01') > %(report_date)s
|
||||
and ifnull(jv.is_opening, 'No') = 'No'""", filters, as_dict=1)
|
||||
|
||||
|
||||
payment_entries = frappe.db.sql("""
|
||||
select
|
||||
"Payment Entry" as payment_document, name as payment_entry,
|
||||
reference_no, reference_date as ref_date,
|
||||
if(paid_to=%(account)s, received_amount, 0) as debit,
|
||||
if(paid_from=%(account)s, paid_amount, 0) as credit,
|
||||
select
|
||||
"Payment Entry" as payment_document, name as payment_entry,
|
||||
reference_no, reference_date as ref_date,
|
||||
if(paid_to=%(account)s, received_amount, 0) as debit,
|
||||
if(paid_from=%(account)s, paid_amount, 0) as credit,
|
||||
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
|
||||
from `tabPayment Entry`
|
||||
@@ -156,25 +162,25 @@ def get_entries(filters):
|
||||
|
||||
return sorted(list(payment_entries)+list(journal_entries+list(pos_entries)),
|
||||
key=lambda k: k['posting_date'] or getdate(nowdate()))
|
||||
|
||||
|
||||
def get_amounts_not_reflected_in_system(filters):
|
||||
je_amount = frappe.db.sql("""
|
||||
select sum(jvd.debit_in_account_currency - jvd.credit_in_account_currency)
|
||||
from `tabJournal Entry Account` jvd, `tabJournal Entry` jv
|
||||
where jvd.parent = jv.name and jv.docstatus=1 and jvd.account=%(account)s
|
||||
and jv.posting_date > %(report_date)s and jv.clearance_date <= %(report_date)s
|
||||
and jv.posting_date > %(report_date)s and jv.clearance_date <= %(report_date)s
|
||||
and ifnull(jv.is_opening, 'No') = 'No' """, filters)
|
||||
|
||||
je_amount = flt(je_amount[0][0]) if je_amount else 0.0
|
||||
|
||||
|
||||
pe_amount = frappe.db.sql("""
|
||||
select sum(if(paid_from=%(account)s, paid_amount, received_amount))
|
||||
from `tabPayment Entry`
|
||||
where (paid_from=%(account)s or paid_to=%(account)s) and docstatus=1
|
||||
where (paid_from=%(account)s or paid_to=%(account)s) and docstatus=1
|
||||
and posting_date > %(report_date)s and clearance_date <= %(report_date)s""", filters)
|
||||
|
||||
pe_amount = flt(pe_amount[0][0]) if pe_amount else 0.0
|
||||
|
||||
|
||||
return je_amount + pe_amount
|
||||
|
||||
def get_balance_row(label, amount, account_currency):
|
||||
|
||||
@@ -130,7 +130,7 @@ def get_cash_flow_data(fiscal_year, companies, filters):
|
||||
section_data.append(net_profit_loss)
|
||||
|
||||
for account in cash_flow_account['account_types']:
|
||||
account_data = get_account_type_based_data(account['account_type'], companies, fiscal_year)
|
||||
account_data = get_account_type_based_data(account['account_type'], companies, fiscal_year, filters)
|
||||
account_data.update({
|
||||
"account_name": account['label'],
|
||||
"account": account['label'],
|
||||
@@ -148,12 +148,12 @@ def get_cash_flow_data(fiscal_year, companies, filters):
|
||||
|
||||
return data
|
||||
|
||||
def get_account_type_based_data(account_type, companies, fiscal_year):
|
||||
def get_account_type_based_data(account_type, companies, fiscal_year, filters):
|
||||
data = {}
|
||||
total = 0
|
||||
for company in companies:
|
||||
amount = get_account_type_based_gl_data(company,
|
||||
fiscal_year.year_start_date, fiscal_year.year_end_date, account_type)
|
||||
fiscal_year.year_start_date, fiscal_year.year_end_date, account_type, filters)
|
||||
|
||||
if amount and account_type == "Depreciation":
|
||||
amount *= -1
|
||||
|
||||
@@ -286,14 +286,14 @@ class PartyLedgerSummaryReport(object):
|
||||
|
||||
if parties and accounts:
|
||||
if len(parties) == 1:
|
||||
party = parties.keys()[0]
|
||||
party = list(parties.keys())[0]
|
||||
for account, amount in iteritems(accounts):
|
||||
self.party_adjustment_accounts.add(account)
|
||||
self.party_adjustment_details.setdefault(party, {})
|
||||
self.party_adjustment_details[party].setdefault(account, 0)
|
||||
self.party_adjustment_details[party][account] += amount
|
||||
elif len(accounts) == 1 and not has_irrelevant_entry:
|
||||
account = accounts.keys()[0]
|
||||
account = list(accounts.keys())[0]
|
||||
self.party_adjustment_accounts.add(account)
|
||||
for party, amount in iteritems(parties):
|
||||
self.party_adjustment_details.setdefault(party, {})
|
||||
|
||||
@@ -407,9 +407,12 @@ def get_cost_centers_with_children(cost_centers):
|
||||
|
||||
all_cost_centers = []
|
||||
for d in cost_centers:
|
||||
lft, rgt = frappe.db.get_value("Cost Center", d, ["lft", "rgt"])
|
||||
children = frappe.get_all("Cost Center", filters={"lft": [">=", lft], "rgt": ["<=", rgt]})
|
||||
all_cost_centers += [c.name for c in children]
|
||||
if frappe.db.exists("Cost Center", d):
|
||||
lft, rgt = frappe.db.get_value("Cost Center", d, ["lft", "rgt"])
|
||||
children = frappe.get_all("Cost Center", filters={"lft": [">=", lft], "rgt": ["<=", rgt]})
|
||||
all_cost_centers += [c.name for c in children]
|
||||
else:
|
||||
frappe.throw(_("Cost Center: {0} does not exist".format(d)))
|
||||
|
||||
return list(set(all_cost_centers))
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ from frappe import _, _dict
|
||||
from erpnext.accounts.utils import get_account_currency
|
||||
from erpnext.accounts.report.financial_statements import get_cost_centers_with_children
|
||||
from six import iteritems
|
||||
from collections import OrderedDict
|
||||
|
||||
def execute(filters=None):
|
||||
if not filters:
|
||||
@@ -120,19 +121,11 @@ def get_gl_entries(filters):
|
||||
select_fields = """, debit, credit, debit_in_account_currency,
|
||||
credit_in_account_currency """
|
||||
|
||||
group_by_statement = ''
|
||||
order_by_statement = "order by posting_date, account"
|
||||
|
||||
if filters.get("group_by") == _("Group by Voucher"):
|
||||
order_by_statement = "order by posting_date, voucher_type, voucher_no"
|
||||
|
||||
if filters.get("group_by") == _("Group by Voucher (Consolidated)"):
|
||||
group_by_statement = "group by voucher_type, voucher_no, account, cost_center"
|
||||
|
||||
select_fields = """, sum(debit) as debit, sum(credit) as credit,
|
||||
sum(debit_in_account_currency) as debit_in_account_currency,
|
||||
sum(credit_in_account_currency) as credit_in_account_currency"""
|
||||
|
||||
if filters.get("include_default_book_entries"):
|
||||
filters['company_fb'] = frappe.db.get_value("Company",
|
||||
filters.get("company"), 'default_finance_book')
|
||||
@@ -145,11 +138,10 @@ def get_gl_entries(filters):
|
||||
against_voucher_type, against_voucher, account_currency,
|
||||
remarks, against, is_opening {select_fields}
|
||||
from `tabGL Entry`
|
||||
where company=%(company)s {conditions} {group_by_statement}
|
||||
where company=%(company)s {conditions}
|
||||
{order_by_statement}
|
||||
""".format(
|
||||
select_fields=select_fields, conditions=get_conditions(filters),
|
||||
group_by_statement=group_by_statement,
|
||||
order_by_statement=order_by_statement
|
||||
),
|
||||
filters, as_dict=1)
|
||||
@@ -186,7 +178,8 @@ def get_conditions(filters):
|
||||
if not (filters.get("account") or filters.get("party") or
|
||||
filters.get("group_by") in ["Group by Account", "Group by Party"]):
|
||||
conditions.append("posting_date >=%(from_date)s")
|
||||
conditions.append("posting_date <=%(to_date)s")
|
||||
|
||||
conditions.append("(posting_date <=%(to_date)s or is_opening = 'Yes')")
|
||||
|
||||
if filters.get("project"):
|
||||
conditions.append("project in %(project)s")
|
||||
@@ -269,7 +262,7 @@ def group_by_field(group_by):
|
||||
return 'voucher_no'
|
||||
|
||||
def initialize_gle_map(gl_entries, filters):
|
||||
gle_map = frappe._dict()
|
||||
gle_map = OrderedDict()
|
||||
group_by = group_by_field(filters.get('group_by'))
|
||||
|
||||
for gle in gl_entries:
|
||||
@@ -280,6 +273,7 @@ def initialize_gle_map(gl_entries, filters):
|
||||
def get_accountwise_gle(filters, gl_entries, gle_map):
|
||||
totals = get_totals_dict()
|
||||
entries = []
|
||||
consolidated_gle = OrderedDict()
|
||||
group_by = group_by_field(filters.get('group_by'))
|
||||
|
||||
def update_value_in_dict(data, key, gle):
|
||||
@@ -305,11 +299,19 @@ def get_accountwise_gle(filters, gl_entries, gle_map):
|
||||
if filters.get("group_by") != _('Group by Voucher (Consolidated)'):
|
||||
gle_map[gle.get(group_by)].entries.append(gle)
|
||||
else:
|
||||
entries.append(gle)
|
||||
key = (gle.get("voucher_type"), gle.get("voucher_no"),
|
||||
gle.get("account"), gle.get("cost_center"))
|
||||
if key not in consolidated_gle:
|
||||
consolidated_gle.setdefault(key, gle)
|
||||
else:
|
||||
update_value_in_dict(consolidated_gle, key, gle)
|
||||
|
||||
update_value_in_dict(gle_map[gle.get(group_by)].totals, 'closing', gle)
|
||||
update_value_in_dict(totals, 'closing', gle)
|
||||
|
||||
for key, value in consolidated_gle.items():
|
||||
entries.append(value)
|
||||
|
||||
return totals, entries
|
||||
|
||||
def get_result_as_list(data, filters):
|
||||
|
||||
@@ -154,28 +154,31 @@ class GrossProfitGenerator(object):
|
||||
def get_average_rate_based_on_group_by(self):
|
||||
# sum buying / selling totals for group
|
||||
for key in list(self.grouped):
|
||||
for i, row in enumerate(self.grouped[key]):
|
||||
if row.parent in self.returned_invoices \
|
||||
and row.item_code in self.returned_invoices[row.parent]:
|
||||
returned_item_rows = self.returned_invoices[row.parent][row.item_code]
|
||||
for returned_item_row in returned_item_rows:
|
||||
row.qty += returned_item_row.qty
|
||||
row.base_amount += flt(returned_item_row.base_amount, self.currency_precision)
|
||||
row.buying_amount = flt(row.qty * row.buying_rate, self.currency_precision)
|
||||
|
||||
if i==0:
|
||||
new_row = row
|
||||
elif self.filters.get("group_by") != "Invoice":
|
||||
new_row.qty += row.qty
|
||||
new_row.buying_amount += flt(row.buying_amount, self.currency_precision)
|
||||
new_row.base_amount += flt(row.base_amount, self.currency_precision)
|
||||
|
||||
if self.filters.get("group_by") == "Invoice" and (row.qty or row.base_amount):
|
||||
self.grouped_data_based_on_group_by(row)
|
||||
|
||||
if self.filters.get("group_by") != "Invoice":
|
||||
for i, row in enumerate(self.grouped[key]):
|
||||
if i==0:
|
||||
new_row = row
|
||||
else:
|
||||
new_row.qty += row.qty
|
||||
new_row.buying_amount += flt(row.buying_amount, self.currency_precision)
|
||||
new_row.base_amount += flt(row.base_amount, self.currency_precision)
|
||||
new_row = self.set_average_rate(new_row)
|
||||
self.grouped_data.append(new_row)
|
||||
else:
|
||||
for i, row in enumerate(self.grouped[key]):
|
||||
if row.parent in self.returned_invoices \
|
||||
and row.item_code in self.returned_invoices[row.parent]:
|
||||
returned_item_rows = self.returned_invoices[row.parent][row.item_code]
|
||||
for returned_item_row in returned_item_rows:
|
||||
row.qty += returned_item_row.qty
|
||||
row.base_amount += flt(returned_item_row.base_amount, self.currency_precision)
|
||||
row.buying_amount = flt(row.qty * row.buying_rate, self.currency_precision)
|
||||
if row.qty or row.base_amount:
|
||||
row = self.set_average_rate(row)
|
||||
self.grouped_data.append(row)
|
||||
self.grouped_data_based_on_group_by(new_row)
|
||||
|
||||
def grouped_data_based_on_group_by(self, row):
|
||||
row = self.set_average_rate(row)
|
||||
self.grouped_data.append(row)
|
||||
|
||||
def set_average_rate(self, new_row):
|
||||
new_row.gross_profit = flt(new_row.base_amount - new_row.buying_amount, self.currency_precision)
|
||||
@@ -204,10 +207,7 @@ class GrossProfitGenerator(object):
|
||||
.setdefault(inv.item_code, []).append(inv)
|
||||
|
||||
def skip_row(self, row, product_bundles):
|
||||
if self.filters.get("group_by") != "Invoice":
|
||||
if not row.get(scrub(self.filters.get("group_by", ""))):
|
||||
return True
|
||||
elif row.get("is_return") == 1:
|
||||
if row.get("is_return") == 1:
|
||||
return True
|
||||
|
||||
def get_buying_amount_from_product_bundle(self, row, product_bundle):
|
||||
|
||||
@@ -17,7 +17,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
filters.update({"from_date": filters.get("date_range") and filters.get("date_range")[0], "to_date": filters.get("date_range") and filters.get("date_range")[1]})
|
||||
columns = get_columns(additional_table_columns)
|
||||
|
||||
company_currency = erpnext.get_company_currency(filters.get('company'))
|
||||
company_currency = frappe.get_cached_value('Company', filters.get("company"), "default_currency")
|
||||
|
||||
item_list = get_items(filters, additional_query_columns)
|
||||
if item_list:
|
||||
|
||||
@@ -27,8 +27,8 @@ frappe.query_reports["Payment Period Based On Invoice Date"] = {
|
||||
fieldname:"payment_type",
|
||||
label: __("Payment Type"),
|
||||
fieldtype: "Select",
|
||||
options: "Incoming\nOutgoing",
|
||||
default: "Incoming"
|
||||
options: __("Incoming")+"\n"+__("Outgoing"),
|
||||
default: __("Incoming")
|
||||
},
|
||||
{
|
||||
"fieldname":"party_type",
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from erpnext.accounts.report.accounts_receivable.accounts_receivable import get_ageing_data
|
||||
from erpnext.accounts.report.accounts_receivable.accounts_receivable import ReceivablePayableReport
|
||||
from frappe.utils import getdate, flt
|
||||
|
||||
def execute(filters=None):
|
||||
if not filters: filters = {}
|
||||
if not filters:
|
||||
filters = {}
|
||||
|
||||
validate_filters(filters)
|
||||
|
||||
columns = get_columns(filters)
|
||||
@@ -19,18 +21,29 @@ def execute(filters=None):
|
||||
for d in entries:
|
||||
invoice = invoice_details.get(d.against_voucher) or frappe._dict()
|
||||
|
||||
if d.reference_type=="Purchase Invoice":
|
||||
if d.reference_type == "Purchase Invoice":
|
||||
payment_amount = flt(d.debit) or -1 * flt(d.credit)
|
||||
|
||||
else:
|
||||
payment_amount = flt(d.credit) or -1 * flt(d.debit)
|
||||
|
||||
row = [d.voucher_type, d.voucher_no, d.party_type, d.party, d.posting_date, d.against_voucher,
|
||||
invoice.posting_date, invoice.due_date, d.debit, d.credit, d.remarks]
|
||||
d.update({
|
||||
"range1": 0,
|
||||
"range2": 0,
|
||||
"range3": 0,
|
||||
"range4": 0,
|
||||
"outstanding": payment_amount
|
||||
})
|
||||
|
||||
if d.against_voucher:
|
||||
row += get_ageing_data(30, 60, 90, 120, d.posting_date, invoice.posting_date, payment_amount)
|
||||
else:
|
||||
row += ["", "", "", "", ""]
|
||||
ReceivablePayableReport(filters).get_ageing_data(invoice.posting_date, d)
|
||||
|
||||
row = [
|
||||
d.voucher_type, d.voucher_no, d.party_type, d.party, d.posting_date, d.against_voucher,
|
||||
invoice.posting_date, invoice.due_date, d.debit, d.credit, d.remarks,
|
||||
d.age, d.range1, d.range2, d.range3, d.range4
|
||||
]
|
||||
|
||||
if invoice.due_date:
|
||||
row.append((getdate(d.posting_date) - getdate(invoice.due_date)).days or 0)
|
||||
|
||||
@@ -39,8 +52,8 @@ def execute(filters=None):
|
||||
return columns, data
|
||||
|
||||
def validate_filters(filters):
|
||||
if (filters.get("payment_type") == "Incoming" and filters.get("party_type") == "Supplier") or \
|
||||
(filters.get("payment_type") == "Outgoing" and filters.get("party_type") == "Customer"):
|
||||
if (filters.get("payment_type") == _("Incoming") and filters.get("party_type") == "Supplier") or \
|
||||
(filters.get("payment_type") == _("Outgoing") and filters.get("party_type") == "Customer"):
|
||||
frappe.throw(_("{0} payment entries can not be filtered by {1}")\
|
||||
.format(filters.payment_type, filters.party_type))
|
||||
|
||||
@@ -51,7 +64,7 @@ def get_columns(filters):
|
||||
_("Party Type") + "::100",
|
||||
_("Party") + ":Dynamic Link/Party Type:140",
|
||||
_("Posting Date") + ":Date:100",
|
||||
_("Invoice") + (":Link/Purchase Invoice:130" if filters.get("payment_type") == "Outgoing" else ":Link/Sales Invoice:130"),
|
||||
_("Invoice") + (":Link/Purchase Invoice:130" if filters.get("payment_type") == _("Outgoing") else ":Link/Sales Invoice:130"),
|
||||
_("Invoice Posting Date") + ":Date:130",
|
||||
_("Payment Due Date") + ":Date:130",
|
||||
_("Debit") + ":Currency:120",
|
||||
@@ -69,7 +82,7 @@ def get_conditions(filters):
|
||||
conditions = []
|
||||
|
||||
if not filters.party_type:
|
||||
if filters.payment_type == "Outgoing":
|
||||
if filters.payment_type == _("Outgoing"):
|
||||
filters.party_type = "Supplier"
|
||||
else:
|
||||
filters.party_type = "Customer"
|
||||
@@ -101,7 +114,7 @@ def get_entries(filters):
|
||||
|
||||
def get_invoice_posting_date_map(filters):
|
||||
invoice_details = {}
|
||||
dt = "Sales Invoice" if filters.get("payment_type") == "Incoming" else "Purchase Invoice"
|
||||
dt = "Sales Invoice" if filters.get("payment_type") == _("Incoming") else "Purchase Invoice"
|
||||
for t in frappe.db.sql("select name, posting_date, due_date from `tab{0}`".format(dt), as_dict=1):
|
||||
invoice_details[t.name] = t
|
||||
|
||||
|
||||
@@ -93,4 +93,6 @@ def get_chart_data(filters, columns, income, expense, net_profit_loss):
|
||||
else:
|
||||
chart["type"] = "line"
|
||||
|
||||
chart["fieldtype"] = "Currency"
|
||||
|
||||
return chart
|
||||
@@ -4,6 +4,7 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.utils import flt
|
||||
from frappe.model.meta import get_field_precision
|
||||
from frappe import msgprint, _
|
||||
|
||||
def execute(filters=None):
|
||||
@@ -67,7 +68,8 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No
|
||||
total_tax = 0
|
||||
for tax_acc in tax_accounts:
|
||||
if tax_acc not in income_accounts:
|
||||
tax_amount = flt(invoice_tax_map.get(inv.name, {}).get(tax_acc))
|
||||
tax_amount_precision = get_field_precision(frappe.get_meta("Sales Taxes and Charges").get_field("tax_amount"), currency=company_currency) or 2
|
||||
tax_amount = flt(invoice_tax_map.get(inv.name, {}).get(tax_acc), tax_amount_precision)
|
||||
total_tax += tax_amount
|
||||
row.append(tax_amount)
|
||||
|
||||
|
||||
@@ -5,9 +5,8 @@
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"letter_head": "Capital Traders",
|
||||
"modified": "2018-12-12 05:10:02.987274",
|
||||
"is_standard": "Yes",
|
||||
"modified": "2019-02-12 05:10:02.987274",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Supplier Ledger Summary",
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
"doctype": "Report",
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"letter_head": "Gadgets International",
|
||||
"modified": "2018-08-21 11:25:00.551823",
|
||||
"modified": "2018-09-21 11:25:00.551823",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "TDS Computation Summary",
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
"doctype": "Report",
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"letter_head": "Gadgets International",
|
||||
"modified": "2018-08-21 11:33:40.804532",
|
||||
"modified": "2019-09-24 13:46:16.473711",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "TDS Payable Monthly",
|
||||
|
||||
@@ -75,8 +75,7 @@ def get_data(filters):
|
||||
accumulate_values_into_parents(accounts, accounts_by_name)
|
||||
|
||||
data = prepare_data(accounts, filters, total_row, parent_children_map, company_currency)
|
||||
data = filter_out_zero_value_rows(data, parent_children_map,
|
||||
show_zero_values=filters.get("show_zero_values"))
|
||||
data = filter_out_zero_value_rows(data, parent_children_map, show_zero_values=filters.get("show_zero_values"))
|
||||
|
||||
return data
|
||||
|
||||
@@ -175,33 +174,11 @@ def calculate_values(accounts, gl_entries_by_account, opening_balances, filters,
|
||||
|
||||
d["closing_debit"] = d["opening_debit"] + d["debit"]
|
||||
d["closing_credit"] = d["opening_credit"] + d["credit"]
|
||||
total_row["debit"] += d["debit"]
|
||||
total_row["credit"] += d["credit"]
|
||||
|
||||
if d["root_type"] == "Asset" or d["root_type"] == "Equity" or d["root_type"] == "Expense":
|
||||
d["opening_debit"] -= d["opening_credit"]
|
||||
d["closing_debit"] -= d["closing_credit"]
|
||||
prepare_opening_closing(d)
|
||||
|
||||
# For opening
|
||||
check_opening_closing_has_negative_value(d, "opening_debit", "opening_credit")
|
||||
|
||||
# For closing
|
||||
check_opening_closing_has_negative_value(d, "closing_debit", "closing_credit")
|
||||
|
||||
if d["root_type"] == "Liability" or d["root_type"] == "Income":
|
||||
d["opening_credit"] -= d["opening_debit"]
|
||||
d["closing_credit"] -= d["closing_debit"]
|
||||
|
||||
# For opening
|
||||
check_opening_closing_has_negative_value(d, "opening_credit", "opening_debit")
|
||||
|
||||
# For closing
|
||||
check_opening_closing_has_negative_value(d, "closing_credit", "closing_debit")
|
||||
|
||||
total_row["opening_debit"] += d["opening_debit"]
|
||||
total_row["closing_debit"] += d["closing_debit"]
|
||||
total_row["opening_credit"] += d["opening_credit"]
|
||||
total_row["closing_credit"] += d["closing_credit"]
|
||||
for field in value_fields:
|
||||
total_row[field] += d[field]
|
||||
|
||||
return total_row
|
||||
|
||||
@@ -215,6 +192,10 @@ def prepare_data(accounts, filters, total_row, parent_children_map, company_curr
|
||||
data = []
|
||||
|
||||
for d in accounts:
|
||||
# Prepare opening closing for group account
|
||||
if parent_children_map.get(d.account):
|
||||
prepare_opening_closing(d)
|
||||
|
||||
has_value = False
|
||||
row = {
|
||||
"account": d.name,
|
||||
@@ -301,11 +282,16 @@ def get_columns():
|
||||
}
|
||||
]
|
||||
|
||||
def check_opening_closing_has_negative_value(d, dr_or_cr, switch_to_column):
|
||||
# If opening debit has negetive value then move it to opening credit and vice versa.
|
||||
def prepare_opening_closing(row):
|
||||
dr_or_cr = "debit" if row["root_type"] in ["Asset", "Equity", "Expense"] else "credit"
|
||||
reverse_dr_or_cr = "credit" if dr_or_cr == "debit" else "debit"
|
||||
|
||||
if d[dr_or_cr] < 0:
|
||||
d[switch_to_column] = abs(d[dr_or_cr])
|
||||
d[dr_or_cr] = 0.0
|
||||
else:
|
||||
d[switch_to_column] = 0.0
|
||||
for col_type in ["opening", "closing"]:
|
||||
valid_col = col_type + "_" + dr_or_cr
|
||||
reverse_col = col_type + "_" + reverse_dr_or_cr
|
||||
row[valid_col] -= row[reverse_col]
|
||||
if row[valid_col] < 0:
|
||||
row[reverse_col] = abs(row[valid_col])
|
||||
row[valid_col] = 0.0
|
||||
else:
|
||||
row[reverse_col] = 0.0
|
||||
@@ -18,14 +18,17 @@ def execute(filters=None):
|
||||
return columns, data
|
||||
|
||||
def get_data(filters, show_party_name):
|
||||
party_name_field = "{0}_name".format(frappe.scrub(filters.get('party_type')))
|
||||
if filters.get('party_type') in ('Customer', 'Supplier', 'Employee', 'Member'):
|
||||
party_name_field = "{0}_name".format(frappe.scrub(filters.get('party_type')))
|
||||
if filters.get('party_type') == 'Student':
|
||||
party_name_field = 'first_name'
|
||||
elif filters.get('party_type') == 'Shareholder':
|
||||
party_name_field = 'title'
|
||||
else:
|
||||
party_name_field = 'name'
|
||||
|
||||
party_filters = {"name": filters.get("party")} if filters.get("party") else {}
|
||||
parties = frappe.get_all(filters.get("party_type"), fields = ["name", party_name_field],
|
||||
parties = frappe.get_all(filters.get("party_type"), fields = ["name", party_name_field],
|
||||
filters = party_filters, order_by="name")
|
||||
company_currency = frappe.get_cached_value('Company', filters.company, "default_currency")
|
||||
opening_balances = get_opening_balances(filters)
|
||||
@@ -70,7 +73,7 @@ def get_data(filters, show_party_name):
|
||||
# totals
|
||||
for col in total_row:
|
||||
total_row[col] += row.get(col)
|
||||
|
||||
|
||||
row.update({
|
||||
"currency": company_currency
|
||||
})
|
||||
@@ -78,7 +81,7 @@ def get_data(filters, show_party_name):
|
||||
has_value = False
|
||||
if (opening_debit or opening_credit or debit or credit or closing_debit or closing_credit):
|
||||
has_value =True
|
||||
|
||||
|
||||
if cint(filters.show_zero_values) or has_value:
|
||||
data.append(row)
|
||||
|
||||
@@ -94,9 +97,9 @@ def get_data(filters, show_party_name):
|
||||
|
||||
def get_opening_balances(filters):
|
||||
gle = frappe.db.sql("""
|
||||
select party, sum(debit) as opening_debit, sum(credit) as opening_credit
|
||||
select party, sum(debit) as opening_debit, sum(credit) as opening_credit
|
||||
from `tabGL Entry`
|
||||
where company=%(company)s
|
||||
where company=%(company)s
|
||||
and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != ''
|
||||
and (posting_date < %(from_date)s or ifnull(is_opening, 'No') = 'Yes')
|
||||
group by party""", {
|
||||
@@ -114,11 +117,11 @@ def get_opening_balances(filters):
|
||||
|
||||
def get_balances_within_period(filters):
|
||||
gle = frappe.db.sql("""
|
||||
select party, sum(debit) as debit, sum(credit) as credit
|
||||
select party, sum(debit) as debit, sum(credit) as credit
|
||||
from `tabGL Entry`
|
||||
where company=%(company)s
|
||||
where company=%(company)s
|
||||
and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != ''
|
||||
and posting_date >= %(from_date)s and posting_date <= %(to_date)s
|
||||
and posting_date >= %(from_date)s and posting_date <= %(to_date)s
|
||||
and ifnull(is_opening, 'No') = 'No'
|
||||
group by party""", {
|
||||
"company": filters.company,
|
||||
|
||||
@@ -84,7 +84,8 @@ def validate_fiscal_year(date, fiscal_year, company, label="Date", doc=None):
|
||||
throw(_("{0} '{1}' not in Fiscal Year {2}").format(label, formatdate(date), fiscal_year))
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_balance_on(account=None, date=None, party_type=None, party=None, company=None, in_account_currency=True, cost_center=None):
|
||||
def get_balance_on(account=None, date=None, party_type=None, party=None, company=None,
|
||||
in_account_currency=True, cost_center=None, ignore_account_permission=False):
|
||||
if not account and frappe.form_dict.get("account"):
|
||||
account = frappe.form_dict.get("account")
|
||||
if not date and frappe.form_dict.get("date"):
|
||||
@@ -104,6 +105,9 @@ def get_balance_on(account=None, date=None, party_type=None, party=None, company
|
||||
# get balance of all entries that exist
|
||||
date = nowdate()
|
||||
|
||||
if account:
|
||||
acc = frappe.get_doc("Account", account)
|
||||
|
||||
try:
|
||||
year_start_date = get_fiscal_year(date, verbose=0)[1]
|
||||
except FiscalYearError:
|
||||
@@ -118,7 +122,12 @@ def get_balance_on(account=None, date=None, party_type=None, party=None, company
|
||||
|
||||
allow_cost_center_in_entry_of_bs_account = get_allow_cost_center_in_entry_of_bs_account()
|
||||
|
||||
if cost_center and allow_cost_center_in_entry_of_bs_account:
|
||||
if account:
|
||||
report_type = acc.report_type
|
||||
else:
|
||||
report_type = ""
|
||||
|
||||
if cost_center and (allow_cost_center_in_entry_of_bs_account or report_type =='Profit and Loss'):
|
||||
cc = frappe.get_doc("Cost Center", cost_center)
|
||||
if cc.is_group:
|
||||
cond.append(""" exists (
|
||||
@@ -132,20 +141,14 @@ def get_balance_on(account=None, date=None, party_type=None, party=None, company
|
||||
|
||||
if account:
|
||||
|
||||
acc = frappe.get_doc("Account", account)
|
||||
|
||||
if not frappe.flags.ignore_account_permission:
|
||||
if not (frappe.flags.ignore_account_permission
|
||||
or ignore_account_permission):
|
||||
acc.check_permission("read")
|
||||
|
||||
|
||||
if not allow_cost_center_in_entry_of_bs_account and acc.report_type == 'Profit and Loss':
|
||||
if report_type == 'Profit and Loss':
|
||||
# for pl accounts, get balance within a fiscal year
|
||||
cond.append("posting_date >= '%s' and voucher_type != 'Period Closing Voucher'" \
|
||||
% year_start_date)
|
||||
elif allow_cost_center_in_entry_of_bs_account:
|
||||
# for all accounts, get balance within a fiscal year if maintain cost center in balance account is checked
|
||||
cond.append("posting_date >= '%s' and voucher_type != 'Period Closing Voucher'" \
|
||||
% year_start_date)
|
||||
# different filter for group and ledger - improved performance
|
||||
if acc.is_group:
|
||||
cond.append("""exists (
|
||||
@@ -732,7 +735,7 @@ def get_children(doctype, parent, company, is_root=False):
|
||||
filters.append(['company', '=', company])
|
||||
|
||||
else:
|
||||
fields += ['account_currency'] if doctype == 'Account' else []
|
||||
fields += ['root_type', 'account_currency'] if doctype == 'Account' else []
|
||||
fields += [parent_fieldname + ' as parent']
|
||||
|
||||
acc = frappe.get_list(doctype, fields=fields, filters=filters)
|
||||
|
||||
@@ -303,14 +303,17 @@ frappe.ui.form.on('Asset', {
|
||||
},
|
||||
|
||||
set_depreciation_rate: function(frm, row) {
|
||||
if (row.total_number_of_depreciations && row.frequency_of_depreciation) {
|
||||
if (row.total_number_of_depreciations && row.frequency_of_depreciation
|
||||
&& row.expected_value_after_useful_life) {
|
||||
frappe.call({
|
||||
method: "get_depreciation_rate",
|
||||
doc: frm.doc,
|
||||
args: row,
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
frappe.model.set_value(row.doctype, row.name, "rate_of_depreciation", r.message);
|
||||
frappe.flags.dont_change_rate = true;
|
||||
frappe.model.set_value(row.doctype, row.name,
|
||||
"rate_of_depreciation", flt(r.message, precision("rate_of_depreciation", row)));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -338,6 +341,14 @@ frappe.ui.form.on('Asset Finance Book', {
|
||||
total_number_of_depreciations: function(frm, cdt, cdn) {
|
||||
const row = locals[cdt][cdn];
|
||||
frm.events.set_depreciation_rate(frm, row);
|
||||
},
|
||||
|
||||
rate_of_depreciation: function(frm, cdt, cdn) {
|
||||
if(!frappe.flags.dont_change_rate) {
|
||||
frappe.model.set_value(cdt, cdn, "expected_value_after_useful_life", 0);
|
||||
}
|
||||
|
||||
frappe.flags.dont_change_rate = false;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ from __future__ import unicode_literals
|
||||
import frappe, erpnext, math, json
|
||||
from frappe import _
|
||||
from six import string_types
|
||||
from frappe.utils import flt, add_months, cint, nowdate, getdate, today, date_diff
|
||||
from frappe.utils import flt, add_months, cint, nowdate, getdate, today, date_diff, add_days
|
||||
from frappe.model.document import Document
|
||||
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
|
||||
from erpnext.assets.doctype.asset.depreciation \
|
||||
@@ -101,97 +101,88 @@ class Asset(AccountsController):
|
||||
|
||||
def set_depreciation_rate(self):
|
||||
for d in self.get("finance_books"):
|
||||
d.rate_of_depreciation = self.get_depreciation_rate(d, on_validate=True)
|
||||
d.rate_of_depreciation = flt(self.get_depreciation_rate(d, on_validate=True),
|
||||
d.precision("rate_of_depreciation"))
|
||||
|
||||
def make_depreciation_schedule(self):
|
||||
depreciation_method = [d.depreciation_method for d in self.finance_books]
|
||||
|
||||
if 'Manual' not in depreciation_method:
|
||||
if 'Manual' not in [d.depreciation_method for d in self.finance_books]:
|
||||
self.schedules = []
|
||||
|
||||
if not self.get("schedules") and self.available_for_use_date:
|
||||
total_depreciations = sum([d.total_number_of_depreciations for d in self.get('finance_books')])
|
||||
if self.get("schedules") or not self.available_for_use_date:
|
||||
return
|
||||
|
||||
for d in self.get('finance_books'):
|
||||
self.validate_asset_finance_books(d)
|
||||
for d in self.get('finance_books'):
|
||||
self.validate_asset_finance_books(d)
|
||||
|
||||
value_after_depreciation = (flt(self.gross_purchase_amount) -
|
||||
flt(self.opening_accumulated_depreciation))
|
||||
value_after_depreciation = (flt(self.gross_purchase_amount) -
|
||||
flt(self.opening_accumulated_depreciation))
|
||||
|
||||
d.value_after_depreciation = value_after_depreciation
|
||||
d.value_after_depreciation = value_after_depreciation
|
||||
|
||||
no_of_depreciations = cint(d.total_number_of_depreciations - 1) - cint(self.number_of_depreciations_booked)
|
||||
end_date = add_months(d.depreciation_start_date,
|
||||
no_of_depreciations * cint(d.frequency_of_depreciation))
|
||||
number_of_pending_depreciations = cint(d.total_number_of_depreciations) - \
|
||||
cint(self.number_of_depreciations_booked)
|
||||
|
||||
total_days = date_diff(end_date, self.available_for_use_date)
|
||||
rate_per_day = (value_after_depreciation - d.get("expected_value_after_useful_life")) / total_days
|
||||
has_pro_rata = self.check_is_pro_rata(d)
|
||||
|
||||
number_of_pending_depreciations = cint(d.total_number_of_depreciations) - \
|
||||
cint(self.number_of_depreciations_booked)
|
||||
if has_pro_rata:
|
||||
number_of_pending_depreciations += 1
|
||||
|
||||
from_date = self.available_for_use_date
|
||||
if number_of_pending_depreciations:
|
||||
next_depr_date = getdate(add_months(self.available_for_use_date,
|
||||
number_of_pending_depreciations * 12))
|
||||
if (cint(frappe.db.get_value("Asset Settings", None, "schedule_based_on_fiscal_year")) == 1
|
||||
and getdate(d.depreciation_start_date) < next_depr_date):
|
||||
skip_row = False
|
||||
for n in range(number_of_pending_depreciations):
|
||||
# If depreciation is already completed (for double declining balance)
|
||||
if skip_row: continue
|
||||
|
||||
number_of_pending_depreciations += 1
|
||||
for n in range(number_of_pending_depreciations):
|
||||
if n == list(range(number_of_pending_depreciations))[-1]:
|
||||
schedule_date = add_months(self.available_for_use_date, n * 12)
|
||||
previous_scheduled_date = add_months(d.depreciation_start_date, (n-1) * 12)
|
||||
depreciation_amount = \
|
||||
self.get_depreciation_amount_prorata_temporis(value_after_depreciation,
|
||||
d, previous_scheduled_date, schedule_date)
|
||||
depreciation_amount = self.get_depreciation_amount(value_after_depreciation,
|
||||
d.total_number_of_depreciations, d)
|
||||
|
||||
elif n == list(range(number_of_pending_depreciations))[0]:
|
||||
schedule_date = d.depreciation_start_date
|
||||
depreciation_amount = \
|
||||
self.get_depreciation_amount_prorata_temporis(value_after_depreciation,
|
||||
d, self.available_for_use_date, schedule_date)
|
||||
if not has_pro_rata or n < cint(number_of_pending_depreciations) - 1:
|
||||
schedule_date = add_months(d.depreciation_start_date,
|
||||
n * cint(d.frequency_of_depreciation))
|
||||
|
||||
else:
|
||||
schedule_date = add_months(d.depreciation_start_date, n * 12)
|
||||
depreciation_amount = \
|
||||
self.get_depreciation_amount_prorata_temporis(value_after_depreciation, d)
|
||||
# For first row
|
||||
if has_pro_rata and n==0:
|
||||
depreciation_amount, days = get_pro_rata_amt(d, depreciation_amount,
|
||||
self.available_for_use_date, d.depreciation_start_date)
|
||||
# For last row
|
||||
elif has_pro_rata and n == cint(number_of_pending_depreciations) - 1:
|
||||
to_date = add_months(self.available_for_use_date,
|
||||
n * cint(d.frequency_of_depreciation))
|
||||
|
||||
if value_after_depreciation != 0:
|
||||
value_after_depreciation -= flt(depreciation_amount)
|
||||
depreciation_amount, days = get_pro_rata_amt(d,
|
||||
depreciation_amount, schedule_date, to_date)
|
||||
|
||||
self.append("schedules", {
|
||||
"schedule_date": schedule_date,
|
||||
"depreciation_amount": depreciation_amount,
|
||||
"depreciation_method": d.depreciation_method,
|
||||
"finance_book": d.finance_book,
|
||||
"finance_book_id": d.idx
|
||||
})
|
||||
else:
|
||||
for n in range(number_of_pending_depreciations):
|
||||
schedule_date = add_months(d.depreciation_start_date,
|
||||
n * cint(d.frequency_of_depreciation))
|
||||
schedule_date = add_days(schedule_date, days)
|
||||
|
||||
if d.depreciation_method in ("Straight Line", "Manual"):
|
||||
days = date_diff(schedule_date, from_date)
|
||||
if n == 0: days += 1
|
||||
if not depreciation_amount: continue
|
||||
value_after_depreciation -= flt(depreciation_amount,
|
||||
self.precision("gross_purchase_amount"))
|
||||
|
||||
depreciation_amount = days * rate_per_day
|
||||
from_date = schedule_date
|
||||
else:
|
||||
depreciation_amount = self.get_depreciation_amount(value_after_depreciation,
|
||||
d.total_number_of_depreciations, d)
|
||||
# Adjust depreciation amount in the last period based on the expected value after useful life
|
||||
if d.expected_value_after_useful_life and ((n == cint(number_of_pending_depreciations) - 1
|
||||
and value_after_depreciation != d.expected_value_after_useful_life)
|
||||
or value_after_depreciation < d.expected_value_after_useful_life):
|
||||
depreciation_amount += (value_after_depreciation - d.expected_value_after_useful_life)
|
||||
skip_row = True
|
||||
|
||||
if depreciation_amount:
|
||||
value_after_depreciation -= flt(depreciation_amount)
|
||||
if depreciation_amount > 0:
|
||||
self.append("schedules", {
|
||||
"schedule_date": schedule_date,
|
||||
"depreciation_amount": depreciation_amount,
|
||||
"depreciation_method": d.depreciation_method,
|
||||
"finance_book": d.finance_book,
|
||||
"finance_book_id": d.idx
|
||||
})
|
||||
|
||||
self.append("schedules", {
|
||||
"schedule_date": schedule_date,
|
||||
"depreciation_amount": depreciation_amount,
|
||||
"depreciation_method": d.depreciation_method,
|
||||
"finance_book": d.finance_book,
|
||||
"finance_book_id": d.idx
|
||||
})
|
||||
def check_is_pro_rata(self, row):
|
||||
has_pro_rata = False
|
||||
|
||||
days = date_diff(row.depreciation_start_date, self.available_for_use_date) + 1
|
||||
total_days = get_total_days(row.depreciation_start_date, row.frequency_of_depreciation)
|
||||
|
||||
if days < total_days:
|
||||
has_pro_rata = True
|
||||
|
||||
return has_pro_rata
|
||||
|
||||
def validate_asset_finance_books(self, row):
|
||||
if flt(row.expected_value_after_useful_life) >= flt(self.gross_purchase_amount):
|
||||
@@ -261,31 +252,14 @@ class Asset(AccountsController):
|
||||
return flt(self.get('finance_books')[cint(idx)-1].value_after_depreciation)
|
||||
|
||||
def get_depreciation_amount(self, depreciable_value, total_number_of_depreciations, row):
|
||||
if row.depreciation_method in ["Straight Line", "Manual"]:
|
||||
amt = (flt(self.gross_purchase_amount) - flt(row.expected_value_after_useful_life) -
|
||||
flt(self.opening_accumulated_depreciation))
|
||||
|
||||
depreciation_amount = amt * row.rate_of_depreciation
|
||||
else:
|
||||
depreciation_amount = flt(depreciable_value) * (flt(row.rate_of_depreciation) / 100)
|
||||
value_after_depreciation = flt(depreciable_value) - depreciation_amount
|
||||
if value_after_depreciation < flt(row.expected_value_after_useful_life):
|
||||
depreciation_amount = flt(depreciable_value) - flt(row.expected_value_after_useful_life)
|
||||
|
||||
return depreciation_amount
|
||||
|
||||
def get_depreciation_amount_prorata_temporis(self, depreciable_value, row, start_date=None, end_date=None):
|
||||
if start_date and end_date:
|
||||
prorata_temporis = min(abs(flt(date_diff(str(end_date), str(start_date)))) / flt(frappe.db.get_value("Asset Settings", None, "number_of_days_in_fiscal_year")), 1)
|
||||
else:
|
||||
prorata_temporis = 1
|
||||
precision = self.precision("gross_purchase_amount")
|
||||
|
||||
if row.depreciation_method in ("Straight Line", "Manual"):
|
||||
depreciation_amount = (flt(row.value_after_depreciation) -
|
||||
flt(row.expected_value_after_useful_life)) / (cint(row.total_number_of_depreciations) -
|
||||
cint(self.number_of_depreciations_booked)) * prorata_temporis
|
||||
cint(self.number_of_depreciations_booked))
|
||||
else:
|
||||
depreciation_amount = self.get_depreciation_amount(depreciable_value, row.total_number_of_depreciations, row)
|
||||
depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100), precision)
|
||||
|
||||
return depreciation_amount
|
||||
|
||||
@@ -301,20 +275,17 @@ class Asset(AccountsController):
|
||||
flt(accumulated_depreciation_after_full_schedule),
|
||||
self.precision('gross_purchase_amount'))
|
||||
|
||||
if row.expected_value_after_useful_life < asset_value_after_full_schedule:
|
||||
if (row.expected_value_after_useful_life and
|
||||
row.expected_value_after_useful_life < asset_value_after_full_schedule):
|
||||
frappe.throw(_("Depreciation Row {0}: Expected value after useful life must be greater than or equal to {1}")
|
||||
.format(row.idx, asset_value_after_full_schedule))
|
||||
elif not row.expected_value_after_useful_life:
|
||||
row.expected_value_after_useful_life = asset_value_after_full_schedule
|
||||
|
||||
def validate_cancellation(self):
|
||||
if self.status not in ("Submitted", "Partially Depreciated", "Fully Depreciated"):
|
||||
frappe.throw(_("Asset cannot be cancelled, as it is already {0}").format(self.status))
|
||||
|
||||
if self.purchase_invoice:
|
||||
frappe.throw(_("Please cancel Purchase Invoice {0} first").format(self.purchase_invoice))
|
||||
|
||||
if self.purchase_receipt:
|
||||
frappe.throw(_("Please cancel Purchase Receipt {0} first").format(self.purchase_receipt))
|
||||
|
||||
def delete_depreciation_entries(self):
|
||||
for d in self.get("schedules"):
|
||||
if d.journal_entry:
|
||||
@@ -388,7 +359,8 @@ class Asset(AccountsController):
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
||||
"posting_date": self.available_for_use_date,
|
||||
"credit": self.purchase_receipt_amount,
|
||||
"credit_in_account_currency": self.purchase_receipt_amount
|
||||
"credit_in_account_currency": self.purchase_receipt_amount,
|
||||
"cost_center": self.cost_center
|
||||
}))
|
||||
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
@@ -397,7 +369,8 @@ class Asset(AccountsController):
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
||||
"posting_date": self.available_for_use_date,
|
||||
"debit": self.purchase_receipt_amount,
|
||||
"debit_in_account_currency": self.purchase_receipt_amount
|
||||
"debit_in_account_currency": self.purchase_receipt_amount,
|
||||
"cost_center": self.cost_center
|
||||
}))
|
||||
|
||||
if gl_entries:
|
||||
@@ -410,15 +383,7 @@ class Asset(AccountsController):
|
||||
if isinstance(args, string_types):
|
||||
args = json.loads(args)
|
||||
|
||||
number_of_depreciations_booked = 0
|
||||
if self.is_existing_asset:
|
||||
number_of_depreciations_booked = self.number_of_depreciations_booked
|
||||
|
||||
float_precision = cint(frappe.db.get_default("float_precision")) or 2
|
||||
tot_no_of_depreciation = flt(args.get("total_number_of_depreciations")) - flt(number_of_depreciations_booked)
|
||||
|
||||
if args.get("depreciation_method") in ["Straight Line", "Manual"]:
|
||||
return 1.0 / tot_no_of_depreciation
|
||||
|
||||
if args.get("depreciation_method") == 'Double Declining Balance':
|
||||
return 200.0 / args.get("total_number_of_depreciations")
|
||||
@@ -598,3 +563,15 @@ def make_journal_entry(asset_name):
|
||||
|
||||
def is_cwip_accounting_disabled():
|
||||
return cint(frappe.db.get_single_value("Asset Settings", "disable_cwip_accounting"))
|
||||
|
||||
def get_pro_rata_amt(row, depreciation_amount, from_date, to_date):
|
||||
days = date_diff(to_date, from_date)
|
||||
total_days = get_total_days(to_date, row.frequency_of_depreciation)
|
||||
|
||||
return (depreciation_amount * flt(days)) / flt(total_days), days
|
||||
|
||||
def get_total_days(date, frequency):
|
||||
period_start_date = add_months(date,
|
||||
cint(frequency) * -1)
|
||||
|
||||
return date_diff(date, period_start_date)
|
||||
@@ -36,7 +36,7 @@ def make_depreciation_entry(asset_name, date=None):
|
||||
fixed_asset_account, accumulated_depreciation_account, depreciation_expense_account = \
|
||||
get_depreciation_accounts(asset)
|
||||
|
||||
depreciation_cost_center, depreciation_series = frappe.get_cached_value('Company', asset.company,
|
||||
depreciation_cost_center, depreciation_series = frappe.get_cached_value('Company', asset.company,
|
||||
["depreciation_cost_center", "series_for_depreciation_entry"])
|
||||
|
||||
depreciation_cost_center = asset.cost_center or depreciation_cost_center
|
||||
@@ -70,7 +70,7 @@ def make_depreciation_entry(asset_name, date=None):
|
||||
je.submit()
|
||||
|
||||
d.db_set("journal_entry", je.name)
|
||||
|
||||
|
||||
idx = cint(d.finance_book_id)
|
||||
finance_books = asset.get('finance_books')[idx - 1]
|
||||
finance_books.value_after_depreciation -= d.depreciation_amount
|
||||
@@ -88,15 +88,15 @@ def get_depreciation_accounts(asset):
|
||||
fieldname = ['fixed_asset_account', 'accumulated_depreciation_account',
|
||||
'depreciation_expense_account'], as_dict=1)
|
||||
|
||||
if accounts:
|
||||
if accounts:
|
||||
fixed_asset_account = accounts.fixed_asset_account
|
||||
accumulated_depreciation_account = accounts.accumulated_depreciation_account
|
||||
depreciation_expense_account = accounts.depreciation_expense_account
|
||||
|
||||
|
||||
if not accumulated_depreciation_account or not depreciation_expense_account:
|
||||
accounts = frappe.get_cached_value('Company', asset.company,
|
||||
accounts = frappe.get_cached_value('Company', asset.company,
|
||||
["accumulated_depreciation_account", "depreciation_expense_account"])
|
||||
|
||||
|
||||
if not accumulated_depreciation_account:
|
||||
accumulated_depreciation_account = accounts[0]
|
||||
if not depreciation_expense_account:
|
||||
@@ -135,11 +135,11 @@ def scrap_asset(asset_name):
|
||||
|
||||
je.flags.ignore_permissions = True
|
||||
je.submit()
|
||||
|
||||
|
||||
frappe.db.set_value("Asset", asset_name, "disposal_date", today())
|
||||
frappe.db.set_value("Asset", asset_name, "journal_entry_for_scrap", je.name)
|
||||
asset.set_status("Scrapped")
|
||||
|
||||
|
||||
frappe.msgprint(_("Asset scrapped via Journal Entry {0}").format(je.name))
|
||||
|
||||
@frappe.whitelist()
|
||||
@@ -147,21 +147,29 @@ def restore_asset(asset_name):
|
||||
asset = frappe.get_doc("Asset", asset_name)
|
||||
|
||||
je = asset.journal_entry_for_scrap
|
||||
|
||||
|
||||
asset.db_set("disposal_date", None)
|
||||
asset.db_set("journal_entry_for_scrap", None)
|
||||
|
||||
|
||||
frappe.get_doc("Journal Entry", je).cancel()
|
||||
|
||||
asset.set_status()
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_gl_entries_on_asset_disposal(asset, selling_amount=0):
|
||||
def get_gl_entries_on_asset_disposal(asset, selling_amount=0, finance_book=None):
|
||||
fixed_asset_account, accumulated_depr_account, depr_expense_account = get_depreciation_accounts(asset)
|
||||
disposal_account, depreciation_cost_center = get_disposal_account_and_cost_center(asset.company)
|
||||
depreciation_cost_center = asset.cost_center or depreciation_cost_center
|
||||
|
||||
accumulated_depr_amount = flt(asset.gross_purchase_amount) - flt(asset.value_after_depreciation)
|
||||
idx = 1
|
||||
if finance_book:
|
||||
for d in asset.finance_books:
|
||||
if d.finance_book == finance_book:
|
||||
idx = d.idx
|
||||
break
|
||||
|
||||
value_after_depreciation = asset.finance_books[idx - 1].value_after_depreciation
|
||||
accumulated_depr_amount = flt(asset.gross_purchase_amount) - flt(value_after_depreciation)
|
||||
|
||||
gl_entries = [
|
||||
{
|
||||
@@ -176,7 +184,7 @@ def get_gl_entries_on_asset_disposal(asset, selling_amount=0):
|
||||
}
|
||||
]
|
||||
|
||||
profit_amount = flt(selling_amount) - flt(asset.value_after_depreciation)
|
||||
profit_amount = flt(selling_amount) - flt(value_after_depreciation)
|
||||
if profit_amount:
|
||||
debit_or_credit = "debit" if profit_amount < 0 else "credit"
|
||||
gl_entries.append({
|
||||
@@ -190,7 +198,7 @@ def get_gl_entries_on_asset_disposal(asset, selling_amount=0):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_disposal_account_and_cost_center(company):
|
||||
disposal_account, depreciation_cost_center = frappe.get_cached_value('Company', company,
|
||||
disposal_account, depreciation_cost_center = frappe.get_cached_value('Company', company,
|
||||
["disposal_account", "depreciation_cost_center"])
|
||||
|
||||
if not disposal_account:
|
||||
|
||||
@@ -88,23 +88,23 @@ class TestAsset(unittest.TestCase):
|
||||
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
|
||||
asset = frappe.get_doc('Asset', asset_name)
|
||||
asset.calculate_depreciation = 1
|
||||
asset.available_for_use_date = '2020-06-06'
|
||||
asset.purchase_date = '2020-06-06'
|
||||
asset.available_for_use_date = '2030-01-01'
|
||||
asset.purchase_date = '2030-01-01'
|
||||
|
||||
asset.append("finance_books", {
|
||||
"expected_value_after_useful_life": 10000,
|
||||
"next_depreciation_date": "2020-12-31",
|
||||
"depreciation_method": "Straight Line",
|
||||
"total_number_of_depreciations": 3,
|
||||
"frequency_of_depreciation": 10,
|
||||
"depreciation_start_date": "2020-06-06"
|
||||
"frequency_of_depreciation": 12,
|
||||
"depreciation_start_date": "2030-12-31"
|
||||
})
|
||||
asset.save()
|
||||
|
||||
self.assertEqual(asset.status, "Draft")
|
||||
expected_schedules = [
|
||||
["2020-06-06", 147.54, 147.54],
|
||||
["2021-04-06", 44852.46, 45000.0],
|
||||
["2022-02-06", 45000.0, 90000.00]
|
||||
["2030-12-31", 30000.00, 30000.00],
|
||||
["2031-12-31", 30000.00, 60000.00],
|
||||
["2032-12-31", 30000.00, 90000.00]
|
||||
]
|
||||
|
||||
schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
|
||||
@@ -118,20 +118,21 @@ class TestAsset(unittest.TestCase):
|
||||
asset.calculate_depreciation = 1
|
||||
asset.number_of_depreciations_booked = 1
|
||||
asset.opening_accumulated_depreciation = 40000
|
||||
asset.available_for_use_date = "2030-06-06"
|
||||
asset.append("finance_books", {
|
||||
"expected_value_after_useful_life": 10000,
|
||||
"next_depreciation_date": "2020-12-31",
|
||||
"depreciation_method": "Straight Line",
|
||||
"total_number_of_depreciations": 3,
|
||||
"frequency_of_depreciation": 10,
|
||||
"depreciation_start_date": "2020-06-06"
|
||||
"frequency_of_depreciation": 12,
|
||||
"depreciation_start_date": "2030-12-31"
|
||||
})
|
||||
asset.insert()
|
||||
self.assertEqual(asset.status, "Draft")
|
||||
asset.save()
|
||||
expected_schedules = [
|
||||
["2020-06-06", 164.47, 40164.47],
|
||||
["2021-04-06", 49835.53, 90000.00]
|
||||
["2030-12-31", 14246.58, 54246.58],
|
||||
["2031-12-31", 25000.00, 79246.58],
|
||||
["2032-06-06", 10753.42, 90000.00]
|
||||
]
|
||||
schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount]
|
||||
for d in asset.get("schedules")]
|
||||
@@ -145,24 +146,23 @@ class TestAsset(unittest.TestCase):
|
||||
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
|
||||
asset = frappe.get_doc('Asset', asset_name)
|
||||
asset.calculate_depreciation = 1
|
||||
asset.available_for_use_date = '2020-06-06'
|
||||
asset.purchase_date = '2020-06-06'
|
||||
asset.available_for_use_date = '2030-01-01'
|
||||
asset.purchase_date = '2030-01-01'
|
||||
asset.append("finance_books", {
|
||||
"expected_value_after_useful_life": 10000,
|
||||
"next_depreciation_date": "2020-12-31",
|
||||
"depreciation_method": "Double Declining Balance",
|
||||
"total_number_of_depreciations": 3,
|
||||
"frequency_of_depreciation": 10,
|
||||
"depreciation_start_date": "2020-06-06"
|
||||
"frequency_of_depreciation": 12,
|
||||
"depreciation_start_date": '2030-12-31'
|
||||
})
|
||||
asset.insert()
|
||||
self.assertEqual(asset.status, "Draft")
|
||||
asset.save()
|
||||
|
||||
expected_schedules = [
|
||||
["2020-06-06", 66666.67, 66666.67],
|
||||
["2021-04-06", 22222.22, 88888.89],
|
||||
["2022-02-06", 1111.11, 90000.0]
|
||||
['2030-12-31', 66667.00, 66667.00],
|
||||
['2031-12-31', 22222.11, 88889.11],
|
||||
['2032-12-31', 1110.89, 90000.0]
|
||||
]
|
||||
|
||||
schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
|
||||
@@ -177,23 +177,21 @@ class TestAsset(unittest.TestCase):
|
||||
asset.is_existing_asset = 1
|
||||
asset.number_of_depreciations_booked = 1
|
||||
asset.opening_accumulated_depreciation = 50000
|
||||
asset.available_for_use_date = '2030-01-01'
|
||||
asset.purchase_date = '2029-11-30'
|
||||
asset.append("finance_books", {
|
||||
"expected_value_after_useful_life": 10000,
|
||||
"next_depreciation_date": "2020-12-31",
|
||||
"depreciation_method": "Double Declining Balance",
|
||||
"total_number_of_depreciations": 3,
|
||||
"frequency_of_depreciation": 10,
|
||||
"depreciation_start_date": "2020-06-06"
|
||||
"frequency_of_depreciation": 12,
|
||||
"depreciation_start_date": "2030-12-31"
|
||||
})
|
||||
asset.insert()
|
||||
self.assertEqual(asset.status, "Draft")
|
||||
asset.save()
|
||||
|
||||
asset.save()
|
||||
|
||||
expected_schedules = [
|
||||
["2020-06-06", 33333.33, 83333.33],
|
||||
["2021-04-06", 6666.67, 90000.0]
|
||||
["2030-12-31", 33333.50, 83333.50],
|
||||
["2031-12-31", 6666.50, 90000.0]
|
||||
]
|
||||
|
||||
schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
|
||||
@@ -209,25 +207,25 @@ class TestAsset(unittest.TestCase):
|
||||
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
|
||||
asset = frappe.get_doc('Asset', asset_name)
|
||||
asset.calculate_depreciation = 1
|
||||
asset.purchase_date = '2020-01-30'
|
||||
asset.purchase_date = '2030-01-30'
|
||||
asset.is_existing_asset = 0
|
||||
asset.available_for_use_date = "2020-01-30"
|
||||
asset.available_for_use_date = "2030-01-30"
|
||||
asset.append("finance_books", {
|
||||
"expected_value_after_useful_life": 10000,
|
||||
"depreciation_method": "Straight Line",
|
||||
"total_number_of_depreciations": 3,
|
||||
"frequency_of_depreciation": 10,
|
||||
"depreciation_start_date": "2020-12-31"
|
||||
"frequency_of_depreciation": 12,
|
||||
"depreciation_start_date": "2030-12-31"
|
||||
})
|
||||
|
||||
asset.insert()
|
||||
asset.save()
|
||||
|
||||
expected_schedules = [
|
||||
["2020-12-31", 28000.0, 28000.0],
|
||||
["2021-12-31", 30000.0, 58000.0],
|
||||
["2022-12-31", 30000.0, 88000.0],
|
||||
["2023-01-30", 2000.0, 90000.0]
|
||||
["2030-12-31", 27534.25, 27534.25],
|
||||
["2031-12-31", 30000.0, 57534.25],
|
||||
["2032-12-31", 30000.0, 87534.25],
|
||||
["2033-01-30", 2465.75, 90000.0]
|
||||
]
|
||||
|
||||
schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
|
||||
@@ -266,8 +264,8 @@ class TestAsset(unittest.TestCase):
|
||||
self.assertEqual(asset.get("schedules")[0].journal_entry[:4], "DEPR")
|
||||
|
||||
expected_gle = (
|
||||
("_Test Accumulated Depreciations - _TC", 0.0, 32129.24),
|
||||
("_Test Depreciations - _TC", 32129.24, 0.0)
|
||||
("_Test Accumulated Depreciations - _TC", 0.0, 30000.0),
|
||||
("_Test Depreciations - _TC", 30000.0, 0.0)
|
||||
)
|
||||
|
||||
gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
|
||||
@@ -277,15 +275,15 @@ class TestAsset(unittest.TestCase):
|
||||
self.assertEqual(gle, expected_gle)
|
||||
self.assertEqual(asset.get("value_after_depreciation"), 0)
|
||||
|
||||
def test_depreciation_entry_for_wdv(self):
|
||||
def test_depreciation_entry_for_wdv_without_pro_rata(self):
|
||||
pr = make_purchase_receipt(item_code="Macbook Pro",
|
||||
qty=1, rate=8000.0, location="Test Location")
|
||||
|
||||
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
|
||||
asset = frappe.get_doc('Asset', asset_name)
|
||||
asset.calculate_depreciation = 1
|
||||
asset.available_for_use_date = '2030-06-06'
|
||||
asset.purchase_date = '2030-06-06'
|
||||
asset.available_for_use_date = '2030-01-01'
|
||||
asset.purchase_date = '2030-01-01'
|
||||
asset.append("finance_books", {
|
||||
"expected_value_after_useful_life": 1000,
|
||||
"depreciation_method": "Written Down Value",
|
||||
@@ -298,9 +296,41 @@ class TestAsset(unittest.TestCase):
|
||||
self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
|
||||
|
||||
expected_schedules = [
|
||||
["2030-12-31", 4000.0, 4000.0],
|
||||
["2031-12-31", 2000.0, 6000.0],
|
||||
["2032-12-31", 1000.0, 7000.0],
|
||||
["2030-12-31", 4000.00, 4000.00],
|
||||
["2031-12-31", 2000.00, 6000.00],
|
||||
["2032-12-31", 1000.00, 7000.0],
|
||||
]
|
||||
|
||||
schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
|
||||
for d in asset.get("schedules")]
|
||||
|
||||
self.assertEqual(schedules, expected_schedules)
|
||||
|
||||
def test_pro_rata_depreciation_entry_for_wdv(self):
|
||||
pr = make_purchase_receipt(item_code="Macbook Pro",
|
||||
qty=1, rate=8000.0, location="Test Location")
|
||||
|
||||
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
|
||||
asset = frappe.get_doc('Asset', asset_name)
|
||||
asset.calculate_depreciation = 1
|
||||
asset.available_for_use_date = '2030-06-06'
|
||||
asset.purchase_date = '2030-01-01'
|
||||
asset.append("finance_books", {
|
||||
"expected_value_after_useful_life": 1000,
|
||||
"depreciation_method": "Written Down Value",
|
||||
"total_number_of_depreciations": 3,
|
||||
"frequency_of_depreciation": 12,
|
||||
"depreciation_start_date": "2030-12-31"
|
||||
})
|
||||
asset.save(ignore_permissions=True)
|
||||
|
||||
self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
|
||||
|
||||
expected_schedules = [
|
||||
["2030-12-31", 2279.45, 2279.45],
|
||||
["2031-12-31", 2860.28, 5139.73],
|
||||
["2032-12-31", 1430.14, 6569.87],
|
||||
["2033-06-06", 430.13, 7000.0],
|
||||
]
|
||||
|
||||
schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
|
||||
@@ -346,18 +376,19 @@ class TestAsset(unittest.TestCase):
|
||||
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
|
||||
asset = frappe.get_doc('Asset', asset_name)
|
||||
asset.calculate_depreciation = 1
|
||||
asset.available_for_use_date = '2020-06-06'
|
||||
asset.purchase_date = '2020-06-06'
|
||||
asset.available_for_use_date = nowdate()
|
||||
asset.purchase_date = nowdate()
|
||||
asset.append("finance_books", {
|
||||
"expected_value_after_useful_life": 10000,
|
||||
"depreciation_method": "Straight Line",
|
||||
"total_number_of_depreciations": 3,
|
||||
"frequency_of_depreciation": 10,
|
||||
"depreciation_start_date": "2020-06-06"
|
||||
"depreciation_start_date": nowdate()
|
||||
})
|
||||
asset.insert()
|
||||
asset.submit()
|
||||
post_depreciation_entries(date="2021-01-01")
|
||||
|
||||
post_depreciation_entries(date=add_months(nowdate(), 10))
|
||||
|
||||
scrap_asset(asset.name)
|
||||
|
||||
@@ -366,8 +397,9 @@ class TestAsset(unittest.TestCase):
|
||||
self.assertTrue(asset.journal_entry_for_scrap)
|
||||
|
||||
expected_gle = (
|
||||
("_Test Accumulated Depreciations - _TC", 100000.0, 0.0),
|
||||
("_Test Fixed Asset - _TC", 0.0, 100000.0)
|
||||
("_Test Accumulated Depreciations - _TC", 30000.0, 0.0),
|
||||
("_Test Fixed Asset - _TC", 0.0, 100000.0),
|
||||
("_Test Gain/Loss on Asset Disposal - _TC", 70000.0, 0.0)
|
||||
)
|
||||
|
||||
gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
|
||||
@@ -411,9 +443,9 @@ class TestAsset(unittest.TestCase):
|
||||
self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Sold")
|
||||
|
||||
expected_gle = (
|
||||
("_Test Accumulated Depreciations - _TC", 100000.0, 0.0),
|
||||
("_Test Accumulated Depreciations - _TC", 20392.16, 0.0),
|
||||
("_Test Fixed Asset - _TC", 0.0, 100000.0),
|
||||
("_Test Gain/Loss on Asset Disposal - _TC", 0, 25000.0),
|
||||
("_Test Gain/Loss on Asset Disposal - _TC", 54607.84, 0.0),
|
||||
("Debtors - _TC", 25000.0, 0.0)
|
||||
)
|
||||
|
||||
|
||||
@@ -56,8 +56,7 @@ def assign_tasks(asset_maintenance_name, assign_to_member, maintenance_task, nex
|
||||
def calculate_next_due_date(periodicity, start_date = None, end_date = None, last_completion_date = None, next_due_date = None):
|
||||
if not start_date and not last_completion_date:
|
||||
start_date = frappe.utils.now()
|
||||
|
||||
if last_completion_date and (last_completion_date > start_date or not start_date):
|
||||
if last_completion_date and ((start_date and last_completion_date > start_date) or not start_date):
|
||||
start_date = last_completion_date
|
||||
if periodicity == 'Daily':
|
||||
next_due_date = add_days(start_date, 1)
|
||||
@@ -115,4 +114,4 @@ def get_maintenance_log(asset_name):
|
||||
select maintenance_status, count(asset_name) as count, asset_name
|
||||
from `tabAsset Maintenance Log`
|
||||
where asset_name=%s group by maintenance_status""",
|
||||
(asset_name), as_dict=1)
|
||||
(asset_name), as_dict=1)
|
||||
|
||||
@@ -46,75 +46,6 @@
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "schedule_based_on_fiscal_year",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Calculate Prorated Depreciation Schedule Based on Fiscal Year",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "360",
|
||||
"depends_on": "eval:doc.schedule_based_on_fiscal_year",
|
||||
"description": "This value is used for pro-rata temporis calculation",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "number_of_days_in_fiscal_year",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Number of Days in Fiscal Year",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
@@ -159,7 +90,7 @@
|
||||
"issingle": 1,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-03-08 10:44:41.924547",
|
||||
"modified": "2019-05-26 18:31:19.930563",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Assets",
|
||||
"name": "Asset Settings",
|
||||
|
||||
@@ -10,7 +10,8 @@ frappe.ui.form.on("Purchase Order", {
|
||||
frm.custom_make_buttons = {
|
||||
'Purchase Receipt': 'Receipt',
|
||||
'Purchase Invoice': 'Invoice',
|
||||
'Stock Entry': 'Material to Supplier'
|
||||
'Stock Entry': 'Material to Supplier',
|
||||
'Payment Entry': 'Payment'
|
||||
}
|
||||
|
||||
frm.set_query("reserve_warehouse", "supplied_items", function() {
|
||||
|
||||
@@ -2176,7 +2176,7 @@
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "other_charges_calculation",
|
||||
"fieldtype": "Text",
|
||||
"fieldtype": "Long Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
@@ -3948,7 +3948,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-06-24 20:55:03.466766",
|
||||
"modified": "2020-04-16 18:54:54.840653",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Purchase Order",
|
||||
|
||||
@@ -472,7 +472,7 @@ def make_rm_stock_entry(purchase_order, rm_items):
|
||||
'from_warehouse': rm_item_data["warehouse"],
|
||||
'stock_uom': rm_item_data["stock_uom"],
|
||||
'main_item_code': rm_item_data["item_code"],
|
||||
'allow_alternative_item': item_wh[rm_item_code].get('allow_alternative_item')
|
||||
'allow_alternative_item': item_wh.get(rm_item_code, {}).get('allow_alternative_item')
|
||||
}
|
||||
}
|
||||
stock_entry.add_to_stock_entry_detail(items_dict)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1395,7 +1395,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "other_charges_calculation",
|
||||
"fieldtype": "Text",
|
||||
"fieldtype": "Long Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
@@ -2845,7 +2845,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2019-01-07 16:52:01.505553",
|
||||
"modified": "2020-04-16 19:05:41.924303",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Supplier Quotation",
|
||||
|
||||
@@ -138,7 +138,7 @@ def refresh_scorecards():
|
||||
# Check to see if any new scorecard periods are created
|
||||
if make_all_scorecards(sc.name) > 0:
|
||||
# Save the scorecard to update the score and standings
|
||||
sc.save()
|
||||
frappe.get_doc('Supplier Scorecard', sc.name).save()
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
||||
@@ -30,7 +30,9 @@ def update_last_purchase_rate(doc, is_submit):
|
||||
# for it to be considered for latest purchase rate
|
||||
if flt(d.conversion_factor):
|
||||
last_purchase_rate = flt(d.base_rate) / flt(d.conversion_factor)
|
||||
else:
|
||||
# Check if item code is present
|
||||
# Conversion factor should not be mandatory for non itemized items
|
||||
elif d.item_code:
|
||||
frappe.throw(_("UOM Conversion factor is required in row {0}").format(d.idx))
|
||||
|
||||
# update last purchsae rate
|
||||
@@ -84,13 +86,13 @@ def get_linked_material_requests(items):
|
||||
items = json.loads(items)
|
||||
mr_list = []
|
||||
for item in items:
|
||||
material_request = frappe.db.sql("""SELECT distinct mr.name AS mr_name,
|
||||
(mr_item.qty - mr_item.ordered_qty) AS qty,
|
||||
material_request = frappe.db.sql("""SELECT distinct mr.name AS mr_name,
|
||||
(mr_item.qty - mr_item.ordered_qty) AS qty,
|
||||
mr_item.item_code AS item_code,
|
||||
mr_item.name AS mr_item
|
||||
mr_item.name AS mr_item
|
||||
FROM `tabMaterial Request` mr, `tabMaterial Request Item` mr_item
|
||||
WHERE mr.name = mr_item.parent
|
||||
AND mr_item.item_code = %(item)s
|
||||
AND mr_item.item_code = %(item)s
|
||||
AND mr.material_request_type = 'Purchase'
|
||||
AND mr.per_ordered < 99.99
|
||||
AND mr.docstatus = 1
|
||||
@@ -98,6 +100,6 @@ def get_linked_material_requests(items):
|
||||
ORDER BY mr_item.item_code ASC""",{"item": item}, as_dict=1)
|
||||
if material_request:
|
||||
mr_list.append(material_request)
|
||||
|
||||
|
||||
return mr_list
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@ def get_data():
|
||||
"type": "report",
|
||||
"is_query_report": True,
|
||||
"name": "Address And Contacts",
|
||||
"label": "Supplier Addresses And Contacts",
|
||||
"label": _("Supplier Addresses And Contacts"),
|
||||
"doctype": "Address",
|
||||
"route_options": {
|
||||
"party_type": "Supplier"
|
||||
|
||||
@@ -58,7 +58,9 @@ class AccountsController(TransactionBase):
|
||||
|
||||
def validate(self):
|
||||
|
||||
self.validate_qty_is_not_zero()
|
||||
if not self.get('is_return'):
|
||||
self.validate_qty_is_not_zero()
|
||||
|
||||
if self.get("_action") and self._action != "update_after_submit":
|
||||
self.set_missing_values(for_validate=True)
|
||||
|
||||
@@ -85,7 +87,7 @@ class AccountsController(TransactionBase):
|
||||
self.validate_currency()
|
||||
|
||||
if self.doctype == 'Purchase Invoice':
|
||||
self.validate_paid_amount()
|
||||
self.calculate_paid_amount()
|
||||
|
||||
if self.doctype in ['Purchase Invoice', 'Sales Invoice']:
|
||||
pos_check_field = "is_pos" if self.doctype=="Sales Invoice" else "is_paid"
|
||||
@@ -129,22 +131,23 @@ class AccountsController(TransactionBase):
|
||||
else:
|
||||
df.set("print_hide", 1)
|
||||
|
||||
def validate_paid_amount(self):
|
||||
def calculate_paid_amount(self):
|
||||
if hasattr(self, "is_pos") or hasattr(self, "is_paid"):
|
||||
is_paid = self.get("is_pos") or self.get("is_paid")
|
||||
if cint(is_paid) == 1:
|
||||
if flt(self.paid_amount) == 0 and flt(self.outstanding_amount) > 0:
|
||||
if self.cash_bank_account:
|
||||
self.paid_amount = flt(flt(self.outstanding_amount), self.precision("paid_amount"))
|
||||
self.base_paid_amount = flt(self.paid_amount * self.conversion_rate,
|
||||
self.precision("base_paid_amount"))
|
||||
else:
|
||||
# show message that the amount is not paid
|
||||
self.paid_amount = 0
|
||||
frappe.throw(
|
||||
_("Note: Payment Entry will not be created since 'Cash or Bank Account' was not specified"))
|
||||
else:
|
||||
frappe.db.set(self, 'paid_amount', 0)
|
||||
|
||||
if is_paid:
|
||||
if not self.cash_bank_account:
|
||||
# show message that the amount is not paid
|
||||
frappe.throw(_("Note: Payment Entry will not be created since 'Cash or Bank Account' was not specified"))
|
||||
|
||||
if cint(self.is_return) and (self.grand_total > self.paid_amount):
|
||||
self.paid_amount = flt(flt(self.grand_total), self.precision("paid_amount"))
|
||||
|
||||
elif not flt(self.paid_amount) and flt(self.outstanding_amount) > 0:
|
||||
self.paid_amount = flt(flt(self.outstanding_amount), self.precision("paid_amount"))
|
||||
|
||||
self.base_paid_amount = flt(self.paid_amount * self.conversion_rate,
|
||||
self.precision("base_paid_amount"))
|
||||
|
||||
def set_missing_values(self, for_validate=False):
|
||||
if frappe.flags.in_test:
|
||||
@@ -372,9 +375,10 @@ class AccountsController(TransactionBase):
|
||||
return gl_dict
|
||||
|
||||
def validate_qty_is_not_zero(self):
|
||||
for item in self.items:
|
||||
if not item.qty:
|
||||
frappe.throw("Item quantity can not be zero")
|
||||
if self.doctype != "Purchase Receipt":
|
||||
for item in self.items:
|
||||
if not item.qty:
|
||||
frappe.throw(_("Item quantity can not be zero"))
|
||||
|
||||
def validate_account_currency(self, account, account_currency=None):
|
||||
valid_currency = [self.company_currency]
|
||||
@@ -825,7 +829,7 @@ class AccountsController(TransactionBase):
|
||||
|
||||
if self.doctype in ("Sales Invoice", "Purchase Invoice"):
|
||||
grand_total = grand_total - flt(self.write_off_amount)
|
||||
if total != grand_total:
|
||||
if total != flt(grand_total, self.precision("grand_total")):
|
||||
frappe.throw(_("Total Payment Amount in Payment Schedule must be equal to Grand / Rounded Total"))
|
||||
|
||||
def is_rounded_total_disabled(self):
|
||||
@@ -1094,6 +1098,8 @@ def get_supplier_block_status(party_name):
|
||||
@frappe.whitelist()
|
||||
def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name):
|
||||
data = json.loads(trans_items)
|
||||
sales_doctypes = ['Sales Order', 'Sales Invoice', 'Delivery Note', 'Quotation']
|
||||
|
||||
for d in data:
|
||||
child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname"))
|
||||
|
||||
@@ -1110,6 +1116,28 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name):
|
||||
.format(child_item.idx, child_item.item_code))
|
||||
else:
|
||||
child_item.rate = flt(d.get("rate"))
|
||||
if flt(child_item.price_list_rate):
|
||||
if flt(child_item.rate) > flt(child_item.price_list_rate):
|
||||
# if rate is greater than price_list_rate, set margin
|
||||
# or set discount
|
||||
child_item.discount_percentage = 0
|
||||
|
||||
if parent_doctype in sales_doctypes:
|
||||
child_item.margin_type = "Amount"
|
||||
child_item.margin_rate_or_amount = flt(child_item.rate - child_item.price_list_rate,
|
||||
child_item.precision("margin_rate_or_amount"))
|
||||
child_item.rate_with_margin = child_item.rate
|
||||
else:
|
||||
child_item.discount_percentage = flt((1 - flt(child_item.rate) / flt(child_item.price_list_rate)) * 100.0,
|
||||
child_item.precision("discount_percentage"))
|
||||
child_item.discount_amount = flt(
|
||||
child_item.price_list_rate) - flt(child_item.rate)
|
||||
|
||||
if parent_doctype in sales_doctypes:
|
||||
child_item.margin_type = ""
|
||||
child_item.margin_rate_or_amount = 0
|
||||
child_item.rate_with_margin = 0
|
||||
|
||||
child_item.flags.ignore_validate_update_after_submit = True
|
||||
child_item.save()
|
||||
|
||||
|
||||
@@ -337,7 +337,7 @@ class BuyingController(StockController):
|
||||
if self.doctype in ["Purchase Receipt", "Purchase Invoice"]:
|
||||
rm.consumed_qty = required_qty
|
||||
rm.description = bom_item.description
|
||||
if item.batch_no and not rm.batch_no:
|
||||
if item.batch_no and frappe.db.get_value("Item", rm.rm_item_code, "has_batch_no") and not rm.batch_no:
|
||||
rm.batch_no = item.batch_no
|
||||
|
||||
# get raw materials rate
|
||||
@@ -395,7 +395,9 @@ class BuyingController(StockController):
|
||||
def set_qty_as_per_stock_uom(self):
|
||||
for d in self.get("items"):
|
||||
if d.meta.get_field("stock_qty"):
|
||||
if not d.conversion_factor:
|
||||
# Check if item code is present
|
||||
# Conversion factor should not be mandatory for non itemized items
|
||||
if not d.conversion_factor and d.item_code:
|
||||
frappe.throw(_("Row {0}: Conversion Factor is mandatory").format(d.idx))
|
||||
d.stock_qty = flt(d.qty) * flt(d.conversion_factor)
|
||||
|
||||
@@ -513,10 +515,15 @@ class BuyingController(StockController):
|
||||
for d in self.get('supplied_items'):
|
||||
# negative quantity is passed, as raw material qty has to be decreased
|
||||
# when PR is submitted and it has to be increased when PR is cancelled
|
||||
incoming_rate = 0
|
||||
if self.is_return and self.return_against and self.docstatus==1:
|
||||
incoming_rate = self.get_incoming_rate_for_sales_return(d.rm_item_code, self.return_against)
|
||||
|
||||
sl_entries.append(self.get_sl_entries(d, {
|
||||
"item_code": d.rm_item_code,
|
||||
"warehouse": self.supplier_warehouse,
|
||||
"actual_qty": -1*flt(d.consumed_qty),
|
||||
"incoming_rate": incoming_rate
|
||||
}))
|
||||
|
||||
def on_submit(self):
|
||||
@@ -715,7 +722,7 @@ def get_items_from_bom(item_code, bom, exploded_item=1):
|
||||
where
|
||||
t2.parent = t1.name and t1.item = %s
|
||||
and t1.docstatus = 1 and t1.is_active = 1 and t1.name = %s
|
||||
and t2.item_code = t3.name and t3.is_stock_item = 1""".format(doctype),
|
||||
and t2.item_code = t3.name""".format(doctype),
|
||||
(item_code, bom), as_dict=1)
|
||||
|
||||
if not bom_items:
|
||||
|
||||
@@ -292,6 +292,7 @@ def copy_attributes_to_variant(item, variant):
|
||||
if not variant.description:
|
||||
variant.description = ""
|
||||
|
||||
else:
|
||||
if item.variant_based_on=='Item Attribute':
|
||||
if variant.attributes:
|
||||
attributes_description = item.description + " "
|
||||
@@ -299,7 +300,7 @@ def copy_attributes_to_variant(item, variant):
|
||||
attributes_description += "<div>" + d.attribute + ": " + cstr(d.attribute_value) + "</div>"
|
||||
|
||||
if attributes_description not in variant.description:
|
||||
variant.description += attributes_description
|
||||
variant.description = attributes_description
|
||||
|
||||
def make_variant_item_code(template_item_code, template_item_name, variant):
|
||||
"""Uses template's item code and abbreviations to make variant's item code"""
|
||||
|
||||
@@ -152,6 +152,24 @@ def tax_account_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False):
|
||||
conditions = []
|
||||
|
||||
#Get searchfields from meta and use in Item Link field query
|
||||
meta = frappe.get_meta("Item", cached=True)
|
||||
searchfields = meta.get_search_fields()
|
||||
|
||||
if "description" in searchfields:
|
||||
searchfields.remove("description")
|
||||
|
||||
columns = ''
|
||||
extra_searchfields = [field for field in searchfields
|
||||
if field not in ["name", "item_group", "description"]]
|
||||
|
||||
if extra_searchfields:
|
||||
columns = ", " + ", ".join(extra_searchfields)
|
||||
|
||||
searchfields = searchfields + [field for field in[searchfield or "name", "item_code", "item_group", "item_name"]
|
||||
if not field in searchfields]
|
||||
searchfields = " or ".join([field + " like %(txt)s" for field in searchfields])
|
||||
|
||||
description_cond = ''
|
||||
if frappe.db.count('Item', cache=True) < 50000:
|
||||
# scan description only if items are less than 50000
|
||||
@@ -162,17 +180,14 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
|
||||
concat(substr(tabItem.item_name, 1, 40), "..."), item_name) as item_name,
|
||||
tabItem.item_group,
|
||||
if(length(tabItem.description) > 40, \
|
||||
concat(substr(tabItem.description, 1, 40), "..."), description) as decription
|
||||
concat(substr(tabItem.description, 1, 40), "..."), description) as description
|
||||
{columns}
|
||||
from tabItem
|
||||
where tabItem.docstatus < 2
|
||||
and tabItem.has_variants=0
|
||||
and tabItem.disabled=0
|
||||
and (tabItem.end_of_life > %(today)s or ifnull(tabItem.end_of_life, '0000-00-00')='0000-00-00')
|
||||
and (tabItem.`{key}` LIKE %(txt)s
|
||||
or tabItem.item_code LIKE %(txt)s
|
||||
or tabItem.item_group LIKE %(txt)s
|
||||
or tabItem.item_name LIKE %(txt)s
|
||||
or tabItem.item_code IN (select parent from `tabItem Barcode` where barcode LIKE %(txt)s)
|
||||
and ({scond} or tabItem.item_code IN (select parent from `tabItem Barcode` where barcode LIKE %(txt)s)
|
||||
{description_cond})
|
||||
{fcond} {mcond}
|
||||
order by
|
||||
@@ -182,6 +197,8 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
|
||||
name, item_name
|
||||
limit %(start)s, %(page_len)s """.format(
|
||||
key=searchfield,
|
||||
columns=columns,
|
||||
scond=searchfields,
|
||||
fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'),
|
||||
mcond=get_match_cond(doctype).replace('%', '%%'),
|
||||
description_cond = description_cond),
|
||||
@@ -206,11 +223,11 @@ def bom(doctype, txt, searchfield, start, page_len, filters):
|
||||
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
|
||||
idx desc, name
|
||||
limit %(start)s, %(page_len)s """.format(
|
||||
fcond=get_filters_cond(doctype, filters, conditions),
|
||||
fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'),
|
||||
mcond=get_match_cond(doctype),
|
||||
key=frappe.db.escape(searchfield)),
|
||||
{
|
||||
'txt': "%%%s%%" % frappe.db.escape(txt),
|
||||
'txt': "%"+frappe.db.escape(txt)+"%",
|
||||
'_txt': txt.replace("%", ""),
|
||||
'start': start or 0,
|
||||
'page_len': page_len or 20
|
||||
@@ -280,30 +297,40 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
|
||||
"page_len": page_len
|
||||
}
|
||||
|
||||
if args.get('warehouse'):
|
||||
batch_nos = frappe.db.sql("""select sle.batch_no, round(sum(sle.actual_qty),2), sle.stock_uom, concat('MFG-',batch.manufacturing_date), concat('EXP-',batch.expiry_date)
|
||||
from `tabStock Ledger Entry` sle
|
||||
INNER JOIN `tabBatch` batch on sle.batch_no = batch.name
|
||||
where
|
||||
batch.disabled = 0
|
||||
and sle.item_code = %(item_code)s
|
||||
and sle.warehouse = %(warehouse)s
|
||||
and (sle.batch_no like %(txt)s
|
||||
or batch.manufacturing_date like %(txt)s)
|
||||
and batch.docstatus < 2
|
||||
{0}
|
||||
{match_conditions}
|
||||
group by batch_no having sum(sle.actual_qty) > 0
|
||||
order by batch.expiry_date, sle.batch_no desc
|
||||
limit %(start)s, %(page_len)s""".format(cond, match_conditions=get_match_cond(doctype)), args)
|
||||
having_clause = "having sum(sle.actual_qty) > 0"
|
||||
if filters.get("is_return"):
|
||||
having_clause = ""
|
||||
|
||||
if args.get('warehouse'):
|
||||
batch_nos = frappe.db.sql("""select sle.batch_no, round(sum(sle.actual_qty),2), sle.stock_uom,
|
||||
concat('MFG-',batch.manufacturing_date), concat('EXP-',batch.expiry_date)
|
||||
from `tabStock Ledger Entry` sle
|
||||
INNER JOIN `tabBatch` batch on sle.batch_no = batch.name
|
||||
where
|
||||
batch.disabled = 0
|
||||
and sle.item_code = %(item_code)s
|
||||
and sle.warehouse = %(warehouse)s
|
||||
and (sle.batch_no like %(txt)s
|
||||
or batch.expiry_date like %(txt)s
|
||||
or batch.manufacturing_date like %(txt)s)
|
||||
and batch.docstatus < 2
|
||||
{cond}
|
||||
{match_conditions}
|
||||
group by batch_no {having_clause}
|
||||
order by batch.expiry_date, sle.batch_no desc
|
||||
limit %(start)s, %(page_len)s""".format(
|
||||
cond=cond,
|
||||
match_conditions=get_match_cond(doctype),
|
||||
having_clause = having_clause
|
||||
), args)
|
||||
|
||||
if batch_nos:
|
||||
return batch_nos
|
||||
else:
|
||||
return frappe.db.sql("""select name, concat('MFG-', manufacturing_date), concat('EXP-',expiry_date) from `tabBatch` batch
|
||||
where batch.disabled = 0
|
||||
and item = %(item_code)s
|
||||
and (name like %(txt)s
|
||||
or expiry_date like %(txt)s
|
||||
or manufacturing_date like %(txt)s)
|
||||
and docstatus < 2
|
||||
{0}
|
||||
@@ -372,7 +399,7 @@ def get_expense_account(doctype, txt, searchfield, start, page_len, filters):
|
||||
|
||||
return frappe.db.sql("""select tabAccount.name from `tabAccount`
|
||||
where (tabAccount.report_type = "Profit and Loss"
|
||||
or tabAccount.account_type in ("Expense Account", "Fixed Asset", "Temporary", "Asset Received But Not Billed"))
|
||||
or tabAccount.account_type in ("Expense Account", "Fixed Asset", "Temporary", "Asset Received But Not Billed", "Capital Work in Progress"))
|
||||
and tabAccount.is_group=0
|
||||
and tabAccount.docstatus!=2
|
||||
and tabAccount.{key} LIKE %(txt)s
|
||||
|
||||
@@ -18,34 +18,31 @@ def validate_return(doc):
|
||||
validate_returned_items(doc)
|
||||
|
||||
def validate_return_against(doc):
|
||||
filters = {"doctype": doc.doctype, "docstatus": 1, "company": doc.company}
|
||||
if doc.meta.get_field("customer") and doc.customer:
|
||||
filters["customer"] = doc.customer
|
||||
elif doc.meta.get_field("supplier") and doc.supplier:
|
||||
filters["supplier"] = doc.supplier
|
||||
|
||||
if not frappe.db.exists(filters):
|
||||
if not frappe.db.exists(doc.doctype, doc.return_against):
|
||||
frappe.throw(_("Invalid {0}: {1}")
|
||||
.format(doc.meta.get_label("return_against"), doc.return_against))
|
||||
else:
|
||||
ref_doc = frappe.get_doc(doc.doctype, doc.return_against)
|
||||
|
||||
# validate posting date time
|
||||
return_posting_datetime = "%s %s" % (doc.posting_date, doc.get("posting_time") or "00:00:00")
|
||||
ref_posting_datetime = "%s %s" % (ref_doc.posting_date, ref_doc.get("posting_time") or "00:00:00")
|
||||
party_type = "customer" if doc.doctype in ("Sales Invoice", "Delivery Note") else "supplier"
|
||||
|
||||
if get_datetime(return_posting_datetime) < get_datetime(ref_posting_datetime):
|
||||
frappe.throw(_("Posting timestamp must be after {0}").format(format_datetime(ref_posting_datetime)))
|
||||
if ref_doc.company == doc.company and ref_doc.get(party_type) == doc.get(party_type) and ref_doc.docstatus == 1:
|
||||
# validate posting date time
|
||||
return_posting_datetime = "%s %s" % (doc.posting_date, doc.get("posting_time") or "00:00:00")
|
||||
ref_posting_datetime = "%s %s" % (ref_doc.posting_date, ref_doc.get("posting_time") or "00:00:00")
|
||||
|
||||
# validate same exchange rate
|
||||
if doc.conversion_rate != ref_doc.conversion_rate:
|
||||
frappe.throw(_("Exchange Rate must be same as {0} {1} ({2})")
|
||||
.format(doc.doctype, doc.return_against, ref_doc.conversion_rate))
|
||||
if get_datetime(return_posting_datetime) < get_datetime(ref_posting_datetime):
|
||||
frappe.throw(_("Posting timestamp must be after {0}").format(format_datetime(ref_posting_datetime)))
|
||||
|
||||
# validate update stock
|
||||
if doc.doctype == "Sales Invoice" and doc.update_stock and not ref_doc.update_stock:
|
||||
frappe.throw(_("'Update Stock' can not be checked because items are not delivered via {0}")
|
||||
.format(doc.return_against))
|
||||
# validate same exchange rate
|
||||
if doc.conversion_rate != ref_doc.conversion_rate:
|
||||
frappe.throw(_("Exchange Rate must be same as {0} {1} ({2})")
|
||||
.format(doc.doctype, doc.return_against, ref_doc.conversion_rate))
|
||||
|
||||
# validate update stock
|
||||
if doc.doctype == "Sales Invoice" and doc.update_stock and not ref_doc.update_stock:
|
||||
frappe.throw(_("'Update Stock' can not be checked because items are not delivered via {0}")
|
||||
.format(doc.return_against))
|
||||
|
||||
def validate_returned_items(doc):
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
@@ -75,7 +72,7 @@ def validate_returned_items(doc):
|
||||
|
||||
items_returned = False
|
||||
for d in doc.get("items"):
|
||||
if flt(d.qty) < 0 or d.get('received_qty') < 0:
|
||||
if d.item_code and (flt(d.qty) < 0 or flt(d.get('received_qty')) < 0):
|
||||
if d.item_code not in valid_items:
|
||||
frappe.throw(_("Row # {0}: Returned Item {1} does not exists in {2} {3}")
|
||||
.format(d.idx, d.item_code, doc.doctype, doc.return_against))
|
||||
@@ -107,6 +104,9 @@ def validate_returned_items(doc):
|
||||
|
||||
items_returned = True
|
||||
|
||||
elif d.item_name:
|
||||
items_returned = True
|
||||
|
||||
if not items_returned:
|
||||
frappe.throw(_("Atleast one item should be entered with negative quantity in return document"))
|
||||
|
||||
@@ -246,6 +246,8 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
||||
elif doc.doctype == 'Purchase Invoice':
|
||||
doc.paid_amount = -1 * source.paid_amount
|
||||
doc.base_paid_amount = -1 * source.base_paid_amount
|
||||
doc.payment_terms_template = ''
|
||||
doc.payment_schedule = []
|
||||
|
||||
if doc.get("is_return") and hasattr(doc, "packed_items"):
|
||||
for d in doc.get("packed_items"):
|
||||
|
||||
@@ -45,6 +45,7 @@ class SellingController(StockController):
|
||||
self.set_gross_profit()
|
||||
set_default_income_account_for_item(self)
|
||||
self.set_customer_address()
|
||||
self.validate_for_duplicate_items()
|
||||
|
||||
def set_missing_values(self, for_validate=False):
|
||||
|
||||
@@ -55,14 +56,28 @@ class SellingController(StockController):
|
||||
self.set_price_list_and_item_details(for_validate=for_validate)
|
||||
|
||||
def set_missing_lead_customer_details(self):
|
||||
customer, lead = None, None
|
||||
if getattr(self, "customer", None):
|
||||
customer = self.customer
|
||||
elif self.doctype == "Opportunity" and self.party_name:
|
||||
if self.opportunity_from == "Customer":
|
||||
customer = self.party_name
|
||||
else:
|
||||
lead = self.party_name
|
||||
elif self.doctype == "Quotation" and self.party_name:
|
||||
if self.quotation_to == "Customer":
|
||||
customer = self.party_name
|
||||
else:
|
||||
lead = self.party_name
|
||||
|
||||
if customer:
|
||||
from erpnext.accounts.party import _get_party_details
|
||||
fetch_payment_terms_template = False
|
||||
if (self.get("__islocal") or
|
||||
self.company != frappe.db.get_value(self.doctype, self.name, 'company')):
|
||||
fetch_payment_terms_template = True
|
||||
|
||||
party_details = _get_party_details(self.customer,
|
||||
party_details = _get_party_details(customer,
|
||||
ignore_permissions=self.flags.ignore_permissions,
|
||||
doctype=self.doctype, company=self.company,
|
||||
fetch_payment_terms_template=fetch_payment_terms_template,
|
||||
@@ -71,10 +86,9 @@ class SellingController(StockController):
|
||||
party_details.pop("sales_team")
|
||||
self.update_if_missing(party_details)
|
||||
|
||||
elif getattr(self, "lead", None):
|
||||
elif lead:
|
||||
from erpnext.crm.doctype.lead.lead import get_lead_details
|
||||
self.update_if_missing(get_lead_details(
|
||||
self.lead,
|
||||
self.update_if_missing(get_lead_details(lead,
|
||||
posting_date=self.get('transaction_date') or self.get('posting_date'),
|
||||
company=self.company))
|
||||
|
||||
@@ -292,8 +306,9 @@ class SellingController(StockController):
|
||||
if flt(d.conversion_factor)==0.0:
|
||||
d.conversion_factor = get_conversion_factor(d.item_code, d.uom).get("conversion_factor") or 1.0
|
||||
return_rate = 0
|
||||
if cint(self.is_return) and self.return_against and self.docstatus==1:
|
||||
return_rate = self.get_incoming_rate_for_sales_return(d.item_code, self.return_against)
|
||||
if cint(self.is_return) and self.docstatus==1:
|
||||
return_rate = self.get_incoming_rate_for_sales_return(d.item_code,
|
||||
d.warehouse, self.return_against)
|
||||
|
||||
# On cancellation or if return entry submission, make stock ledger entry for
|
||||
# target warehouse first, to update serial no values properly
|
||||
@@ -368,6 +383,34 @@ class SellingController(StockController):
|
||||
if self.get(address_field):
|
||||
self.set(address_display_field, get_address_display(self.get(address_field)))
|
||||
|
||||
def validate_for_duplicate_items(self):
|
||||
check_list, chk_dupl_itm = [], []
|
||||
if cint(frappe.db.get_single_value("Selling Settings", "allow_multiple_items")):
|
||||
return
|
||||
|
||||
for d in self.get('items'):
|
||||
if self.doctype == "Sales Invoice":
|
||||
e = [d.item_code, d.description, d.warehouse, d.sales_order or d.delivery_note, d.batch_no or '']
|
||||
f = [d.item_code, d.description, d.sales_order or d.delivery_note]
|
||||
elif self.doctype == "Delivery Note":
|
||||
e = [d.item_code, d.description, d.warehouse, d.against_sales_order or d.against_sales_invoice, d.batch_no or '']
|
||||
f = [d.item_code, d.description, d.against_sales_order or d.against_sales_invoice]
|
||||
elif self.doctype in ["Sales Order", "Quotation"]:
|
||||
e = [d.item_code, d.description, d.warehouse, '']
|
||||
f = [d.item_code, d.description]
|
||||
|
||||
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1:
|
||||
if e in check_list:
|
||||
frappe.throw(_("Note: Item {0} entered multiple times").format(d.item_code))
|
||||
else:
|
||||
check_list.append(e)
|
||||
else:
|
||||
if f in chk_dupl_itm:
|
||||
frappe.throw(_("Note: Item {0} entered multiple times").format(d.item_code))
|
||||
else:
|
||||
chk_dupl_itm.append(f)
|
||||
|
||||
|
||||
def validate_items(self):
|
||||
# validate items to see if they have is_sales_item enabled
|
||||
from erpnext.controllers.buying_controller import validate_item_type
|
||||
|
||||
@@ -38,7 +38,6 @@ status_map = {
|
||||
["To Bill", "eval:self.per_delivered == 100 and self.per_billed < 100 and self.docstatus == 1"],
|
||||
["To Deliver", "eval:self.per_delivered < 100 and self.per_billed == 100 and self.docstatus == 1"],
|
||||
["Completed", "eval:self.per_delivered == 100 and self.per_billed == 100 and self.docstatus == 1"],
|
||||
["Completed", "eval:self.order_type == 'Maintenance' and self.per_billed == 100 and self.docstatus == 1"],
|
||||
["Cancelled", "eval:self.docstatus==2"],
|
||||
["Closed", "eval:self.status=='Closed'"],
|
||||
],
|
||||
@@ -94,7 +93,8 @@ status_map = {
|
||||
["Partially Ordered", "eval:self.status != 'Stopped' and self.per_ordered < 100 and self.per_ordered > 0 and self.docstatus == 1"],
|
||||
["Ordered", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Purchase'"],
|
||||
["Transferred", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Material Transfer'"],
|
||||
["Issued", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Material Issue'"]
|
||||
["Issued", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Material Issue'"],
|
||||
["Manufactured", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Manufacture'"]
|
||||
],
|
||||
"Bank Transaction": [
|
||||
["Unreconciled", "eval:self.docstatus == 1 and self.unallocated_amount>0"],
|
||||
|
||||
@@ -54,6 +54,7 @@ class StockController(AccountsController):
|
||||
gl_list = []
|
||||
warehouse_with_no_account = []
|
||||
|
||||
precision = frappe.get_precision('GL Entry', 'debit_in_account_currency')
|
||||
for item_row in voucher_details:
|
||||
sle_list = sle_map.get(item_row.name)
|
||||
if sle_list:
|
||||
@@ -79,7 +80,7 @@ class StockController(AccountsController):
|
||||
"against": item_row.expense_account,
|
||||
"cost_center": item_row.cost_center,
|
||||
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||
"debit": flt(sle.stock_value_difference, 2),
|
||||
"debit": flt(sle.stock_value_difference, precision),
|
||||
"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No",
|
||||
}, warehouse_account[sle.warehouse]["account_currency"]))
|
||||
|
||||
@@ -89,7 +90,7 @@ class StockController(AccountsController):
|
||||
"against": warehouse_account[sle.warehouse]["account"],
|
||||
"cost_center": item_row.cost_center,
|
||||
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||
"credit": flt(sle.stock_value_difference, 2),
|
||||
"credit": flt(sle.stock_value_difference, precision),
|
||||
"project": item_row.get("project") or self.get("project"),
|
||||
"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No"
|
||||
}))
|
||||
@@ -299,7 +300,7 @@ class StockController(AccountsController):
|
||||
|
||||
return serialized_items
|
||||
|
||||
def get_incoming_rate_for_sales_return(self, item_code, against_document):
|
||||
def get_incoming_rate_for_sales_return(self, item_code, warehouse, against_document):
|
||||
incoming_rate = 0.0
|
||||
if against_document and item_code:
|
||||
incoming_rate = frappe.db.sql("""select abs(stock_value_difference / actual_qty)
|
||||
@@ -308,6 +309,9 @@ class StockController(AccountsController):
|
||||
and item_code = %s limit 1""",
|
||||
(self.doctype, against_document, item_code))
|
||||
incoming_rate = incoming_rate[0][0] if incoming_rate else 0.0
|
||||
else:
|
||||
incoming_rate = get_valuation_rate(item_code, warehouse,
|
||||
self.doctype, against_document, company=self.company, currency=self.currency)
|
||||
|
||||
return incoming_rate
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@ class calculate_taxes_and_totals(object):
|
||||
self.calculate()
|
||||
|
||||
def calculate(self):
|
||||
if not len(self.doc.get("items")):
|
||||
return
|
||||
|
||||
self.discount_amount_applied = False
|
||||
self._calculate()
|
||||
|
||||
@@ -73,7 +76,12 @@ class calculate_taxes_and_totals(object):
|
||||
item.discount_amount = item.price_list_rate - item.rate
|
||||
|
||||
item.net_rate = item.rate
|
||||
item.amount = flt(item.rate * item.qty, item.precision("amount"))
|
||||
|
||||
if not item.qty and self.doc.get("is_return"):
|
||||
item.amount = flt(-1 * item.rate, item.precision("amount"))
|
||||
else:
|
||||
item.amount = flt(item.rate * item.qty, item.precision("amount"))
|
||||
|
||||
item.net_amount = item.amount
|
||||
|
||||
self._set_in_company_currency(item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"])
|
||||
@@ -297,11 +305,19 @@ class calculate_taxes_and_totals(object):
|
||||
last_tax = self.doc.get("taxes")[-1]
|
||||
non_inclusive_tax_amount = sum([flt(d.tax_amount_after_discount_amount)
|
||||
for d in self.doc.get("taxes") if not d.included_in_print_rate])
|
||||
|
||||
diff = self.doc.total + non_inclusive_tax_amount \
|
||||
- flt(last_tax.total, last_tax.precision("total"))
|
||||
|
||||
# If discount amount applied, deduct the discount amount
|
||||
# because self.doc.total is always without discount, but last_tax.total is after discount
|
||||
if self.discount_amount_applied and self.doc.discount_amount:
|
||||
diff -= flt(self.doc.discount_amount)
|
||||
|
||||
diff = flt(diff, self.doc.precision("rounding_adjustment"))
|
||||
|
||||
if diff and abs(diff) <= (5.0 / 10**last_tax.precision("tax_amount")):
|
||||
self.doc.rounding_adjustment = flt(flt(self.doc.rounding_adjustment) +
|
||||
flt(diff), self.doc.precision("rounding_adjustment"))
|
||||
self.doc.rounding_adjustment = diff
|
||||
|
||||
def calculate_totals(self):
|
||||
self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment) \
|
||||
@@ -326,7 +342,7 @@ class calculate_taxes_and_totals(object):
|
||||
|
||||
self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
|
||||
|
||||
self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate) \
|
||||
self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate, self.doc.precision("base_grand_total")) \
|
||||
if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted) \
|
||||
else self.doc.base_net_total
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ class TestMapper(unittest.TestCase):
|
||||
qtn = frappe.get_doc({
|
||||
"doctype": "Quotation",
|
||||
"quotation_to": "Customer",
|
||||
"customer": customer,
|
||||
"party_name": customer,
|
||||
"order_type": "Sales",
|
||||
"transaction_date" : nowdate(),
|
||||
"valid_till" : add_months(nowdate(), 1)
|
||||
|
||||
@@ -39,7 +39,6 @@ def validate_filters(filters):
|
||||
frappe.throw(_("'Based On' and 'Group By' can not be same"))
|
||||
|
||||
def get_data(filters, conditions):
|
||||
|
||||
data = []
|
||||
inc, cond= '',''
|
||||
query_details = conditions["based_on_select"] + conditions["period_wise_select"]
|
||||
@@ -47,13 +46,17 @@ def get_data(filters, conditions):
|
||||
posting_date = 't1.transaction_date'
|
||||
if conditions.get('trans') in ['Sales Invoice', 'Purchase Invoice', 'Purchase Receipt', 'Delivery Note']:
|
||||
posting_date = 't1.posting_date'
|
||||
if filters.period_based_on:
|
||||
posting_date = 't1.'+filters.period_based_on
|
||||
|
||||
if conditions["based_on_select"] in ["t1.project,", "t2.project,"]:
|
||||
cond = ' and '+ conditions["based_on_select"][:-1] +' IS Not NULL'
|
||||
|
||||
if conditions.get('trans') in ['Sales Order', 'Purchase Order']:
|
||||
cond += " and t1.status != 'Closed'"
|
||||
|
||||
if conditions.get('trans') == 'Quotation' and filters.get("group_by") == 'Customer':
|
||||
cond += " and t1.quotation_to = 'Customer'"
|
||||
|
||||
year_start_date, year_end_date = frappe.db.get_value("Fiscal Year",
|
||||
filters.get('fiscal_year'), ["year_start_date", "year_end_date"])
|
||||
|
||||
@@ -64,7 +67,7 @@ def get_data(filters, conditions):
|
||||
if filters.get("group_by") == 'Item':
|
||||
sel_col = 't2.item_code'
|
||||
elif filters.get("group_by") == 'Customer':
|
||||
sel_col = 't1.customer'
|
||||
sel_col = 't1.party_name' if conditions.get('trans') == 'Quotation' else 't1.customer'
|
||||
elif filters.get("group_by") == 'Supplier':
|
||||
sel_col = 't1.supplier'
|
||||
|
||||
@@ -225,7 +228,7 @@ def based_wise_columns_query(based_on, trans):
|
||||
elif based_on == "Customer":
|
||||
based_on_details["based_on_cols"] = ["Customer:Link/Customer:120", "Territory:Link/Territory:120"]
|
||||
based_on_details["based_on_select"] = "t1.customer_name, t1.territory, "
|
||||
based_on_details["based_on_group_by"] = 't1.customer'
|
||||
based_on_details["based_on_group_by"] = 't1.party_name' if trans == 'Quotation' else 't1.customer'
|
||||
based_on_details["addl_tables"] = ''
|
||||
|
||||
elif based_on == "Customer Group":
|
||||
|
||||
@@ -21,42 +21,45 @@ def get_list_context(context=None):
|
||||
|
||||
def get_transaction_list(doctype, txt=None, filters=None, limit_start=0, limit_page_length=20, order_by="modified"):
|
||||
user = frappe.session.user
|
||||
key = None
|
||||
ignore_permissions = False
|
||||
|
||||
if not filters: filters = []
|
||||
|
||||
if doctype == 'Supplier Quotation':
|
||||
filters.append((doctype, "docstatus", "<", 2))
|
||||
filters.append((doctype, 'docstatus', '<', 2))
|
||||
else:
|
||||
filters.append((doctype, "docstatus", "=", 1))
|
||||
filters.append((doctype, 'docstatus', '=', 1))
|
||||
|
||||
if (user != "Guest" and is_website_user()) or doctype == 'Request for Quotation':
|
||||
if (user != 'Guest' and is_website_user()) or doctype == 'Request for Quotation':
|
||||
parties_doctype = 'Request for Quotation Supplier' if doctype == 'Request for Quotation' else doctype
|
||||
# find party for this contact
|
||||
customers, suppliers = get_customers_suppliers(parties_doctype, user)
|
||||
|
||||
if not customers and not suppliers: return []
|
||||
|
||||
key, parties = get_party_details(customers, suppliers)
|
||||
|
||||
if doctype == 'Request for Quotation':
|
||||
return rfq_transaction_list(parties_doctype, doctype, parties, limit_start, limit_page_length)
|
||||
|
||||
filters.append((doctype, key, "in", parties))
|
||||
|
||||
if key:
|
||||
return post_process(doctype, get_list_for_transactions(doctype, txt,
|
||||
filters=filters, fields="name",limit_start=limit_start,
|
||||
limit_page_length=limit_page_length,ignore_permissions=True,
|
||||
order_by="modified desc"))
|
||||
if customers:
|
||||
if doctype == 'Quotation':
|
||||
filters.append(('quotation_to', '=', 'Customer'))
|
||||
filters.append(('party_name', 'in', customers))
|
||||
else:
|
||||
filters.append(('customer', 'in', customers))
|
||||
elif suppliers:
|
||||
filters.append(('supplier', 'in', suppliers))
|
||||
else:
|
||||
return []
|
||||
|
||||
return post_process(doctype, get_list_for_transactions(doctype, txt, filters, limit_start, limit_page_length,
|
||||
fields="name", order_by="modified desc"))
|
||||
if doctype == 'Request for Quotation':
|
||||
parties = customers or suppliers
|
||||
return rfq_transaction_list(parties_doctype, doctype, parties, limit_start, limit_page_length)
|
||||
|
||||
# Since customers and supplier do not have direct access to internal doctypes
|
||||
ignore_permissions = True
|
||||
|
||||
transactions = get_list_for_transactions(doctype, txt, filters, limit_start, limit_page_length,
|
||||
fields='name', ignore_permissions=ignore_permissions, order_by='modified desc')
|
||||
|
||||
return post_process(doctype, transactions)
|
||||
|
||||
def get_list_for_transactions(doctype, txt, filters, limit_start, limit_page_length=20,
|
||||
ignore_permissions=False,fields=None, order_by=None):
|
||||
ignore_permissions=False, fields=None, order_by=None):
|
||||
""" Get List of transactions like Invoices, Orders """
|
||||
from frappe.www.list import get_list
|
||||
meta = frappe.get_meta(doctype)
|
||||
@@ -77,22 +80,12 @@ def get_list_for_transactions(doctype, txt, filters, limit_start, limit_page_len
|
||||
|
||||
if or_filters:
|
||||
for r in frappe.get_list(doctype, fields=fields,filters=filters, or_filters=or_filters,
|
||||
limit_start=limit_start, limit_page_length=limit_page_length,
|
||||
limit_start=limit_start, limit_page_length=limit_page_length,
|
||||
ignore_permissions=ignore_permissions, order_by=order_by):
|
||||
data.append(r)
|
||||
|
||||
return data
|
||||
|
||||
def get_party_details(customers, suppliers):
|
||||
if customers:
|
||||
key, parties = "customer", customers
|
||||
elif suppliers:
|
||||
key, parties = "supplier", suppliers
|
||||
else:
|
||||
key, parties = "customer", []
|
||||
|
||||
return key, parties
|
||||
|
||||
def rfq_transaction_list(parties_doctype, doctype, parties, limit_start, limit_page_length):
|
||||
data = frappe.db.sql("""select distinct parent as name, supplier from `tab{doctype}`
|
||||
where supplier = '{supplier}' and docstatus=1 order by modified desc limit {start}, {len}""".
|
||||
@@ -130,38 +123,56 @@ def get_customers_suppliers(doctype, user):
|
||||
suppliers = []
|
||||
meta = frappe.get_meta(doctype)
|
||||
|
||||
customer_field_name = get_customer_field_name(doctype)
|
||||
|
||||
has_customer_field = meta.has_field(customer_field_name)
|
||||
has_supplier_field = meta.has_field('supplier')
|
||||
|
||||
if has_common(["Supplier", "Customer"], frappe.get_roles(user)):
|
||||
contacts = frappe.db.sql("""
|
||||
select
|
||||
select
|
||||
`tabContact`.email_id,
|
||||
`tabDynamic Link`.link_doctype,
|
||||
`tabDynamic Link`.link_name
|
||||
from
|
||||
from
|
||||
`tabContact`, `tabDynamic Link`
|
||||
where
|
||||
`tabContact`.name=`tabDynamic Link`.parent and `tabContact`.email_id =%s
|
||||
""", user, as_dict=1)
|
||||
customers = [c.link_name for c in contacts if c.link_doctype == 'Customer'] \
|
||||
if meta.get_field("customer") else None
|
||||
suppliers = [c.link_name for c in contacts if c.link_doctype == 'Supplier'] \
|
||||
if meta.get_field("supplier") else None
|
||||
customers = [c.link_name for c in contacts if c.link_doctype == 'Customer']
|
||||
suppliers = [c.link_name for c in contacts if c.link_doctype == 'Supplier']
|
||||
elif frappe.has_permission(doctype, 'read', user=user):
|
||||
customers = [customer.name for customer in frappe.get_list("Customer")] \
|
||||
if meta.get_field("customer") else None
|
||||
suppliers = [supplier.name for supplier in frappe.get_list("Customer")] \
|
||||
if meta.get_field("supplier") else None
|
||||
customer_list = frappe.get_list("Customer")
|
||||
customers = suppliers = [customer.name for customer in customer_list]
|
||||
|
||||
return customers, suppliers
|
||||
return customers if has_customer_field else None, \
|
||||
suppliers if has_supplier_field else None
|
||||
|
||||
def has_website_permission(doc, ptype, user, verbose=False):
|
||||
doctype = doc.doctype
|
||||
customers, suppliers = get_customers_suppliers(doctype, user)
|
||||
if customers:
|
||||
return frappe.get_all(doctype, filters=[(doctype, "customer", "in", customers),
|
||||
(doctype, "name", "=", doc.name)]) and True or False
|
||||
return frappe.db.exists(doctype, get_customer_filter(doc, customers))
|
||||
elif suppliers:
|
||||
fieldname = 'suppliers' if doctype == 'Request for Quotation' else 'supplier'
|
||||
return frappe.get_all(doctype, filters=[(doctype, fieldname, "in", suppliers),
|
||||
(doctype, "name", "=", doc.name)]) and True or False
|
||||
return frappe.db.exists(doctype, filters={
|
||||
'name': doc.name,
|
||||
fieldname: ["in", suppliers]
|
||||
})
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_customer_filter(doc, customers):
|
||||
doctype = doc.doctype
|
||||
filters = frappe._dict()
|
||||
filters.name = doc.name
|
||||
filters[get_customer_field_name(doctype)] = ['in', customers]
|
||||
if doctype == 'Quotation':
|
||||
filters.quotation_to = 'Customer'
|
||||
return filters
|
||||
|
||||
def get_customer_field_name(doctype):
|
||||
if doctype == 'Quotation':
|
||||
return 'party_name'
|
||||
else:
|
||||
return 'customer'
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user