Compare commits
473 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eda4265dbc | ||
|
|
a9eae0b424 | ||
|
|
c3fc490d53 | ||
|
|
108e935744 | ||
|
|
eac82039e3 | ||
|
|
e0a8f2d859 | ||
|
|
fcca772383 | ||
|
|
c5fb88c1cd | ||
|
|
aacb0a702e | ||
|
|
f3a9d788bf | ||
|
|
6203cf7f04 | ||
|
|
4ec5d5594b | ||
|
|
a3cb828ed3 | ||
|
|
5e13e0c316 | ||
|
|
bfeb7c4a57 | ||
|
|
43f627a83a | ||
|
|
d0b3a3734e | ||
|
|
94365fa035 | ||
|
|
e0ce9407cb | ||
|
|
dea3fb0d64 | ||
|
|
1dae1c40b9 | ||
|
|
fd654a06e8 | ||
|
|
5bf1f89da7 | ||
|
|
b6c8c7436f | ||
|
|
1dc8ff5220 | ||
|
|
06d81822fd | ||
|
|
0c883500bb | ||
|
|
4ce020f521 | ||
|
|
11cb9de10a | ||
|
|
f9b63dd36a | ||
|
|
282695f536 | ||
|
|
809abdf295 | ||
|
|
79dc136d50 | ||
|
|
2407d87b0d | ||
|
|
276053b35f | ||
|
|
dc4c2733f4 | ||
|
|
f6f2fc4079 | ||
|
|
68b5eaa82f | ||
|
|
238fd68803 | ||
|
|
22a4b82bd2 | ||
|
|
f456eb6254 | ||
|
|
408a8e804c | ||
|
|
e0b39b860f | ||
|
|
fde7febf0d | ||
|
|
eebf194468 | ||
|
|
6f701b3b6a | ||
|
|
8147f74e4e | ||
|
|
dc65dc3778 | ||
|
|
a7fb9216c9 | ||
|
|
81b98261ae | ||
|
|
c20072e940 | ||
|
|
e4c2ebfc57 | ||
|
|
587c061550 | ||
|
|
40a9f6f8e9 | ||
|
|
f0b0464cce | ||
|
|
4e803af0e6 | ||
|
|
4ccd8d3326 | ||
|
|
9768467d53 | ||
|
|
59de1e23bc | ||
|
|
f19b1e0c6b | ||
|
|
d33a3a295f | ||
|
|
f749302d25 | ||
|
|
9c47efb592 | ||
|
|
ea61046e8d | ||
|
|
3f671ea60f | ||
|
|
c1a7c3b08b | ||
|
|
d1225661d2 | ||
|
|
302eee9406 | ||
|
|
b434464b1c | ||
|
|
aabeb38c15 | ||
|
|
fe93ea56b6 | ||
|
|
b301603740 | ||
|
|
d7ba759844 | ||
|
|
8efe58bd3c | ||
|
|
6609938483 | ||
|
|
84662f2db5 | ||
|
|
da975f5a76 | ||
|
|
f34c96bf7a | ||
|
|
0f96f8e68f | ||
|
|
991962b6fd | ||
|
|
c481e27d88 | ||
|
|
56e04e0727 | ||
|
|
2244ac4d52 | ||
|
|
024b537b7f | ||
|
|
e4ee5c3f1c | ||
|
|
4b3d99d39a | ||
|
|
47a10f5a39 | ||
|
|
ecb39a5b63 | ||
|
|
39c8c9e7b0 | ||
|
|
4e7cc93af9 | ||
|
|
f8cb1a916e | ||
|
|
ab2e75e98e | ||
|
|
354892b1c6 | ||
|
|
abe69afd69 | ||
|
|
c72a89aaf8 | ||
|
|
873d98be2d | ||
|
|
f1d06b02e7 | ||
|
|
675276b802 | ||
|
|
3ed3a2d176 | ||
|
|
9e95e780da | ||
|
|
de58657537 | ||
|
|
015fa7a1d1 | ||
|
|
1ff3a6cdb8 | ||
|
|
7620efc9ad | ||
|
|
7778b480fd | ||
|
|
a17d7cea34 | ||
|
|
a9cafcb8ae | ||
|
|
bdfd0d1ff9 | ||
|
|
afc8b1a087 | ||
|
|
6c1773025b | ||
|
|
1b7d66fab6 | ||
|
|
e24365f1f4 | ||
|
|
fd334bf451 | ||
|
|
5515b1ea7f | ||
|
|
96d67f5153 | ||
|
|
8a00319962 | ||
|
|
c6136e4801 | ||
|
|
0938b5dec6 | ||
|
|
88f8fcb32e | ||
|
|
f40ce616a7 | ||
|
|
b02788b915 | ||
|
|
b094ee45d7 | ||
|
|
de0c87757a | ||
|
|
5604f987f2 | ||
|
|
9d14f0f36a | ||
|
|
a74468b353 | ||
|
|
35f81b24f1 | ||
|
|
40431cbf89 | ||
|
|
5529f14aaf | ||
|
|
54c31b498b | ||
|
|
899dba9022 | ||
|
|
7d8fa8089a | ||
|
|
ac86c5b6d1 | ||
|
|
ecc3f312b9 | ||
|
|
b65b5f43a7 | ||
|
|
c3270d7504 | ||
|
|
ea909ace01 | ||
|
|
f64d11da3c | ||
|
|
95225be93d | ||
|
|
e5d169b8d2 | ||
|
|
da4e3fb366 | ||
|
|
13ce150149 | ||
|
|
a04489a72b | ||
|
|
35cd88b09b | ||
|
|
ff56566506 | ||
|
|
01ad94bfad | ||
|
|
cfe3c54ca0 | ||
|
|
2f6a20a93a | ||
|
|
5d1543f241 | ||
|
|
c56650c773 | ||
|
|
cb11f27558 | ||
|
|
98be98816c | ||
|
|
ca2c297f72 | ||
|
|
387e1e21cb | ||
|
|
d6e49150a8 | ||
|
|
49a2729663 | ||
|
|
06baf20edd | ||
|
|
13553c2bf0 | ||
|
|
f638c1acd5 | ||
|
|
e4c659386a | ||
|
|
cd7a1661c6 | ||
|
|
96962e2101 | ||
|
|
7eedebc970 | ||
|
|
437e34accb | ||
|
|
0f2137be18 | ||
|
|
f87a622ef0 | ||
|
|
07722b835c | ||
|
|
3cd7a45c1b | ||
|
|
90b5174256 | ||
|
|
bffad26226 | ||
|
|
a578f3e23a | ||
|
|
870dd43268 | ||
|
|
ad0bd4ca92 | ||
|
|
eba480e15a | ||
|
|
4b83403a63 | ||
|
|
22c9e42f4d | ||
|
|
6ddc487fb6 | ||
|
|
3a34cadcb2 | ||
|
|
b71d1a4c7f | ||
|
|
bef80bab0d | ||
|
|
190210394c | ||
|
|
5cafcf66b0 | ||
|
|
d71e50c9fc | ||
|
|
35ebe1bf78 | ||
|
|
b14cc0417d | ||
|
|
399a3097e8 | ||
|
|
c8f5c3cdbe | ||
|
|
e83d506319 | ||
|
|
690bcd7b66 | ||
|
|
f18d285eab | ||
|
|
7887ccb441 | ||
|
|
d57b57a21d | ||
|
|
9fc0a8cbf8 | ||
|
|
5cc0531d27 | ||
|
|
0e5cdc8495 | ||
|
|
b80d892eab | ||
|
|
185af03fb2 | ||
|
|
abcbbc63d8 | ||
|
|
6871c74ce1 | ||
|
|
d0a44ca85c | ||
|
|
83db3e3ddb | ||
|
|
cc11045fd3 | ||
|
|
1b5afe737f | ||
|
|
3d65d9602e | ||
|
|
9440d080d4 | ||
|
|
9269c86339 | ||
|
|
7c82d616c9 | ||
|
|
f227379d2f | ||
|
|
eba88919c1 | ||
|
|
3408432b50 | ||
|
|
a0949158b7 | ||
|
|
ea91d2aaf1 | ||
|
|
de992abf83 | ||
|
|
5d8635a8dc | ||
|
|
7e911bae95 | ||
|
|
0676cf6d3f | ||
|
|
b74ae7aa31 | ||
|
|
0ad7db3bbd | ||
|
|
db74e316d2 | ||
|
|
1897360e4b | ||
|
|
e7fb957415 | ||
|
|
2f4567fa3c | ||
|
|
0a7abc188e | ||
|
|
5eeef7f065 | ||
|
|
7d36875d6f | ||
|
|
2fa718705a | ||
|
|
b1fdbf2335 | ||
|
|
2277922313 | ||
|
|
3e1029309c | ||
|
|
aa5deaa070 | ||
|
|
6b5d51ca22 | ||
|
|
2c114b5bb5 | ||
|
|
bd38a79e5e | ||
|
|
ccd9fd3e94 | ||
|
|
4215b3afc3 | ||
|
|
f60f111afe | ||
|
|
6fe0a3cee3 | ||
|
|
f004077734 | ||
|
|
6c5cfd2148 | ||
|
|
6c1011df92 | ||
|
|
1582a0ce78 | ||
|
|
b5b821363d | ||
|
|
399afc87ef | ||
|
|
8c842af172 | ||
|
|
bf836277f9 | ||
|
|
10d1806d81 | ||
|
|
bc41ce95fc | ||
|
|
4948d336c4 | ||
|
|
be2527d93d | ||
|
|
7f5bb1c8aa | ||
|
|
a8d40e4409 | ||
|
|
e9b4686fec | ||
|
|
5086ef2499 | ||
|
|
8a55d9a795 | ||
|
|
1caca80203 | ||
|
|
9114c26857 | ||
|
|
941a965af4 | ||
|
|
4acd431b92 | ||
|
|
fad0d566f9 | ||
|
|
e2d4079363 | ||
|
|
b9ce1f590b | ||
|
|
36463ed790 | ||
|
|
3c8838816d | ||
|
|
11e50a8eee | ||
|
|
9de4c60bd6 | ||
|
|
5288bdeabb | ||
|
|
f5804438bb | ||
|
|
6e06357dc5 | ||
|
|
28913b97b0 | ||
|
|
c59cd46391 | ||
|
|
dda239fd49 | ||
|
|
b86a6ce26a | ||
|
|
ee212e7bb5 | ||
|
|
4952c7b3b5 | ||
|
|
4e61536f50 | ||
|
|
f2886f152b | ||
|
|
b5c56f6cea | ||
|
|
80e95388f5 | ||
|
|
daed0b655a | ||
|
|
fbb994c731 | ||
|
|
10b155a486 | ||
|
|
ce6eda709b | ||
|
|
14e1d20df3 | ||
|
|
da8a02d56e | ||
|
|
16343683d9 | ||
|
|
2bdc017aff | ||
|
|
16e05c321c | ||
|
|
23d7919865 | ||
|
|
38265efc39 | ||
|
|
2d414706dc | ||
|
|
d85e4b0d46 | ||
|
|
45d03af8f6 | ||
|
|
73a3a2a131 | ||
|
|
73804580d4 | ||
|
|
259f9779db | ||
|
|
14e0e58a7d | ||
|
|
5eb373f7b5 | ||
|
|
4595c30a7b | ||
|
|
fc2dd44694 | ||
|
|
053c54017e | ||
|
|
af473d78f2 | ||
|
|
26d096aa87 | ||
|
|
476c613ba6 | ||
|
|
2348a5f592 | ||
|
|
9d8d045c66 | ||
|
|
63914dd55b | ||
|
|
93a8042f08 | ||
|
|
b2f32dac1b | ||
|
|
577a3acaac | ||
|
|
5e46ce8a50 | ||
|
|
3b7342b7b5 | ||
|
|
531077e504 | ||
|
|
147918ed66 | ||
|
|
9bbfca9226 | ||
|
|
f1a07ff105 | ||
|
|
fb6e434315 | ||
|
|
3811d96feb | ||
|
|
1017615d02 | ||
|
|
056d627f46 | ||
|
|
663bfeacf4 | ||
|
|
e918ebd721 | ||
|
|
0dc18f0102 | ||
|
|
8ddc882a66 | ||
|
|
49365d0982 | ||
|
|
50c29c7d0f | ||
|
|
bf492122f8 | ||
|
|
38d0ed9f3a | ||
|
|
248a65b37d | ||
|
|
78f86e9385 | ||
|
|
d9f3e0c275 | ||
|
|
c40451ee2e | ||
|
|
6644406185 | ||
|
|
b3a962e121 | ||
|
|
8a28ccfa2f | ||
|
|
0a75fa09ef | ||
|
|
17a16eeaf4 | ||
|
|
3d3f0bcf54 | ||
|
|
bc8b20ae3c | ||
|
|
daf344e5fd | ||
|
|
7916792f99 | ||
|
|
27e37e68b2 | ||
|
|
a538f8a24a | ||
|
|
70ec88b733 | ||
|
|
4f0e5db216 | ||
|
|
79ed124939 | ||
|
|
c0a3cd603b | ||
|
|
7c6f990cf9 | ||
|
|
7820b171d3 | ||
|
|
0cf4cc283c | ||
|
|
9a4b173b88 | ||
|
|
6d83454237 | ||
|
|
ff231b5e62 | ||
|
|
4d74216147 | ||
|
|
b7e5ad0a31 | ||
|
|
e435592d64 | ||
|
|
7ddde8dc3a | ||
|
|
fce2881de6 | ||
|
|
e96e83d557 | ||
|
|
1b2944e871 | ||
|
|
074e73a0dd | ||
|
|
8923801881 | ||
|
|
cfafe93391 | ||
|
|
6c48ef781b | ||
|
|
adeb976a1b | ||
|
|
bb19b91ef9 | ||
|
|
bfa7f171bd | ||
|
|
b96c014daf | ||
|
|
e0c83e22d9 | ||
|
|
099ad0f5e1 | ||
|
|
cf9746dd84 | ||
|
|
b70712dbba | ||
|
|
4c057fe693 | ||
|
|
ee8ff51d60 | ||
|
|
9974b16c32 | ||
|
|
9e5f319d80 | ||
|
|
4f614b4030 | ||
|
|
3d458e973e | ||
|
|
3a19a71262 | ||
|
|
29d1a1c593 | ||
|
|
3b90de558f | ||
|
|
af7e31acb3 | ||
|
|
a2c9d35efb | ||
|
|
4c058f4056 | ||
|
|
ad67b84d43 | ||
|
|
4e81e4065b | ||
|
|
81332789cb | ||
|
|
82d7c0c9eb | ||
|
|
d60235e239 | ||
|
|
9b50b0a762 | ||
|
|
21e14c4c98 | ||
|
|
16edacebc7 | ||
|
|
75027b4d54 | ||
|
|
b0bd99266d | ||
|
|
b9e04815f8 | ||
|
|
18ccc27b1b | ||
|
|
95f1fe92e2 | ||
|
|
b783f519ee | ||
|
|
996a1010cb | ||
|
|
32a9dfd983 | ||
|
|
1394509343 | ||
|
|
0f31c36b2c | ||
|
|
14ac8f71b7 | ||
|
|
6a92d51383 | ||
|
|
7c831c3fe5 | ||
|
|
778ff463af | ||
|
|
70a31d5402 | ||
|
|
48f5fa69f3 | ||
|
|
83a2d12cd2 | ||
|
|
e539297e53 | ||
|
|
bbc3d015a3 | ||
|
|
e4475c635d | ||
|
|
8370cb3e71 | ||
|
|
4073880ecf | ||
|
|
763c7a56dc | ||
|
|
556fbc487d | ||
|
|
54938d431c | ||
|
|
31c61e7eae | ||
|
|
a23151b138 | ||
|
|
11b75759d2 | ||
|
|
a2c562fea4 | ||
|
|
8950abc36f | ||
|
|
27c7226d97 | ||
|
|
1db294e837 | ||
|
|
16e943f120 | ||
|
|
9d610214cf | ||
|
|
c25681c36c | ||
|
|
1aea75261d | ||
|
|
5cd20e3d24 | ||
|
|
9968c7ef70 | ||
|
|
ca260618d8 | ||
|
|
30c5b41398 | ||
|
|
f37d4337a4 | ||
|
|
93c2adb2cd | ||
|
|
525ab0a925 | ||
|
|
a92c6886cd | ||
|
|
01d171756b | ||
|
|
930e7f5578 | ||
|
|
d14e15d432 | ||
|
|
4d3a18890b | ||
|
|
65bef02607 | ||
|
|
cb9331ddca | ||
|
|
ac0c3fb696 | ||
|
|
a844fb78bb | ||
|
|
6d02939256 | ||
|
|
e3b63f2d9c | ||
|
|
1c440b3b31 | ||
|
|
c5a0ef8399 | ||
|
|
951da0ca3d | ||
|
|
351be9ca42 | ||
|
|
4bab0a2f54 | ||
|
|
97478b746d | ||
|
|
61a591944b | ||
|
|
8e7ca41817 | ||
|
|
84e08fd534 | ||
|
|
b0e56fa1b5 | ||
|
|
e7c48ed3b0 | ||
|
|
c432270ca8 | ||
|
|
6a7f75c3e3 | ||
|
|
e9882e12ba | ||
|
|
3df10429db | ||
|
|
eee569bb20 | ||
|
|
2e07305616 | ||
|
|
2f9c36a588 | ||
|
|
907494c5d4 | ||
|
|
b14401c320 | ||
|
|
4d2520ea53 | ||
|
|
cece0c7ffe | ||
|
|
5e34383e25 | ||
|
|
b2a3f2d386 | ||
|
|
8e39ee79b0 | ||
|
|
6b679c45df | ||
|
|
e3d2643f2b | ||
|
|
fc33d5a75b |
@@ -14,8 +14,11 @@ install:
|
||||
- sudo apt-get update
|
||||
- sudo apt-get purge -y mysql-common
|
||||
- sudo apt-get install mariadb-server mariadb-common libmariadbclient-dev
|
||||
- wget http://downloads.sourceforge.net/project/wkhtmltopdf/0.12.1/wkhtmltox-0.12.1_linux-precise-amd64.deb
|
||||
- sudo dpkg -i wkhtmltox-0.12.1_linux-precise-amd64.deb
|
||||
- ./ci/fix-mariadb.sh
|
||||
|
||||
- sudo apt-get install xfonts-75dpi xfonts-base -y
|
||||
- wget http://downloads.sourceforge.net/project/wkhtmltopdf/0.12.2.1/wkhtmltox-0.12.2.1_linux-precise-amd64.deb
|
||||
- sudo dpkg -i wkhtmltox-0.12.2.1_linux-precise-amd64.deb
|
||||
- CFLAGS=-O0 pip install git+https://github.com/frappe/frappe.git@develop
|
||||
- CFLAGS=-O0 pip install --editable .
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# Contributing to Frappe / ERPNext
|
||||
|
||||
### Update 16-Sep-14
|
||||
|
||||
Please send pull requests to branch v5.0
|
||||
|
||||
## Reporting issues
|
||||
|
||||
We only accept issues that are bug reports or feature requests. Bugs must be isolated and reproducible problems. Please read the following guidelines before opening any issue.
|
||||
|
||||
29
README.md
29
README.md
@@ -1,34 +1,33 @@
|
||||
# ERPNext - Open Source ERP for small, medium sized businesses [](https://travis-ci.org/frappe/erpnext)
|
||||
# ERPNext - Open source ERP for small and medium-size business [](https://travis-ci.org/frappe/erpnext)
|
||||
|
||||
[](https://gitter.im/frappe/erpnext?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
[https://erpnext.com](https://erpnext.com)
|
||||
|
||||
Includes Accounting, Inventory, CRM, Sales, Purchase, Projects, HRMS. Built on Python / MariaDB.
|
||||
Includes: Accounting, Inventory, CRM, Sales, Purchase, Projects, HRMS. Requires MariaDB.
|
||||
|
||||
ERPNext is built on [frappe](https://github.com/frappe/frappe)
|
||||
ERPNext is built on the [Frappe](https://github.com/frappe/frappe) Framework, a full-stack web app framework in Python & Javascript.
|
||||
|
||||
- [User Guide](http://erpnext.org/user-guide.html)
|
||||
- [User Guide](https://erpnext.com/user-guide)
|
||||
- [Getting Help](http://erpnext.org/getting-help.html)
|
||||
- [Developer Forum](http://groups.google.com/group/erpnext-developer-forum)
|
||||
- [User Forum](http://groups.google.com/group/erpnext-user-forum)
|
||||
- [Discussion Forum](https://discuss.frappe.io/)
|
||||
|
||||
---
|
||||
|
||||
### Install
|
||||
### Full Install
|
||||
|
||||
Use the bench, https://github.com/frappe/bench
|
||||
The Easy Way install script for bench will install all dependencies (e.g. MariaDB). See https://github.com/frappe/bench
|
||||
|
||||
### Admin Login
|
||||
New passwords will be created for the ERPNext "Administrator" user, the MariaDB root user, and the frappe user (the script displays the passwords and saves them to ~/frappe_passwords.txt).
|
||||
|
||||
1. go to "/login"
|
||||
1. Administrator user name: "Administrator"
|
||||
1. Administrator passowrd "admin"
|
||||
### Virtual Image
|
||||
|
||||
### Download and Install
|
||||
|
||||
##### Virtual Image:
|
||||
You can download a virtual image to run ERPNext in a virtual machine on your local system.
|
||||
|
||||
- [ERPNext Download](http://erpnext.com/download)
|
||||
|
||||
System and user credentials are listed on the download page.
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
11
ci/fix-mariadb.sh
Executable file
11
ci/fix-mariadb.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
# stolen from http://cgit.drupalcode.org/octopus/commit/?id=db4f837
|
||||
includedir=`mysql_config --variable=pkgincludedir`
|
||||
thiscwd=`pwd`
|
||||
_THIS_DB_VERSION=`mysql -V 2>&1 | tr -d "\n" | cut -d" " -f6 | awk '{ print $1}' | cut -d"-" -f1 | awk '{ print $1}' | sed "s/[\,']//g"`
|
||||
if [ "$_THIS_DB_VERSION" = "5.5.40" ] && [ ! -e "$includedir-$_THIS_DB_VERSION-fixed.log" ] ; then
|
||||
cd $includedir
|
||||
sudo patch -p1 < $thiscwd/ci/my_config.h.patch &> /dev/null
|
||||
sudo touch $includedir-$_THIS_DB_VERSION-fixed.log
|
||||
fi
|
||||
22
ci/my_config.h.patch
Normal file
22
ci/my_config.h.patch
Normal file
@@ -0,0 +1,22 @@
|
||||
diff -burp a/my_config.h b/my_config.h
|
||||
--- a/my_config.h 2014-10-09 19:32:46.000000000 -0400
|
||||
+++ b/my_config.h 2014-10-09 19:35:12.000000000 -0400
|
||||
@@ -641,17 +641,4 @@
|
||||
#define SIZEOF_TIME_T 8
|
||||
/* #undef TIME_T_UNSIGNED */
|
||||
|
||||
-/*
|
||||
- stat structure (from <sys/stat.h>) is conditionally defined
|
||||
- to have different layout and size depending on the defined macros.
|
||||
- The correct macro is defined in my_config.h, which means it MUST be
|
||||
- included first (or at least before <features.h> - so, practically,
|
||||
- before including any system headers).
|
||||
-
|
||||
- __GLIBC__ is defined in <features.h>
|
||||
-*/
|
||||
-#ifdef __GLIBC__
|
||||
-#error <my_config.h> MUST be included first!
|
||||
-#endif
|
||||
-
|
||||
#endif
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = '4.3.0'
|
||||
__version__ = '4.21.4'
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.utils import flt, cstr, cint, getdate, add_days, formatdate
|
||||
from frappe.utils import flt, cstr, cint, getdate
|
||||
from frappe import msgprint, throw, _
|
||||
from frappe.model.document import Document
|
||||
|
||||
@@ -169,24 +169,14 @@ class Account(Document):
|
||||
|
||||
def validate_due_date(self, posting_date, due_date):
|
||||
credit_days = (self.credit_days or frappe.db.get_value("Company", self.company, "credit_days"))
|
||||
if credit_days is None:
|
||||
return
|
||||
|
||||
posting_date, due_date = getdate(posting_date), getdate(due_date)
|
||||
diff = (due_date - posting_date).days
|
||||
|
||||
if diff < 0:
|
||||
frappe.throw(_("Due Date cannot be before Posting Date"))
|
||||
elif diff > credit_days:
|
||||
is_credit_controller = frappe.db.get_value("Accounts Settings", None,
|
||||
"credit_controller") in frappe.user.get_roles()
|
||||
|
||||
if is_credit_controller:
|
||||
msgprint(_("Note: Due Date exceeds the allowed credit days by {0} day(s)").format(
|
||||
diff - credit_days))
|
||||
else:
|
||||
max_due_date = formatdate(add_days(posting_date, credit_days))
|
||||
frappe.throw(_("Due Date cannot be after {0}").format(max_due_date))
|
||||
elif credit_days is not None and diff > credit_days:
|
||||
msgprint(_("Note: Due Date exceeds the allowed credit days by {0} day(s)").format(diff - credit_days))
|
||||
|
||||
def validate_trash(self):
|
||||
"""checks gl entries and if child exists"""
|
||||
|
||||
@@ -17,15 +17,19 @@ class CForm(Document):
|
||||
inv = frappe.db.sql("""select c_form_applicable, c_form_no from
|
||||
`tabSales Invoice` where name = %s and docstatus = 1""", d.invoice_no)
|
||||
|
||||
if inv[0][0] != 'Yes':
|
||||
if inv and inv[0][0] != 'Yes':
|
||||
frappe.throw("C-form is not applicable for Invoice: %s" % d.invoice_no)
|
||||
|
||||
elif inv[0][1] and inv[0][1] != self.name:
|
||||
elif inv and inv[0][1] and inv[0][1] != self.name:
|
||||
frappe.throw("""Invoice %s is tagged in another C-form: %s.
|
||||
If you want to change C-form no for this invoice,
|
||||
please remove invoice no from the previous c-form and then try again""" %
|
||||
(d.invoice_no, inv[0][1]))
|
||||
|
||||
elif not inv:
|
||||
frappe.throw("Row %s: Invoice %s is invalid, it might be cancelled / does not exist. \
|
||||
Please enter a valid Invoice" % d.idx, d.invoice_no)
|
||||
|
||||
def on_update(self):
|
||||
""" Update C-Form No on invoices"""
|
||||
self.set_total_invoiced_amount()
|
||||
|
||||
@@ -25,7 +25,8 @@ class GLEntry(Document):
|
||||
validate_balance_type(self.account, adv_adj)
|
||||
|
||||
# Update outstanding amt on against voucher
|
||||
if self.against_voucher and update_outstanding == 'Yes':
|
||||
if self.against_voucher_type in ['Journal Voucher', 'Sales Invoice', 'Purchase Invoice'] \
|
||||
and self.against_voucher and update_outstanding == 'Yes':
|
||||
update_outstanding_amt(self.account, self.against_voucher_type,
|
||||
self.against_voucher)
|
||||
|
||||
@@ -123,6 +124,10 @@ def update_outstanding_amt(account, against_voucher_type, against_voucher, on_ca
|
||||
from `tabGL Entry` where voucher_type = 'Journal Voucher' and voucher_no = %s
|
||||
and account = %s and ifnull(against_voucher, '') = ''""",
|
||||
(against_voucher, account))[0][0])
|
||||
if not against_voucher_amount:
|
||||
frappe.throw(_("Against Journal Voucher {0} is already adjusted against some other voucher")
|
||||
.format(against_voucher))
|
||||
|
||||
bal = against_voucher_amount + bal
|
||||
if against_voucher_amount < 0:
|
||||
bal = -bal
|
||||
|
||||
@@ -213,10 +213,11 @@ cur_frm.cscript.voucher_type = function(doc, cdt, cdn) {
|
||||
return;
|
||||
|
||||
var update_jv_details = function(doc, r) {
|
||||
$.each(r.message, function(i, d) {
|
||||
var jvdetail = frappe.model.add_child(doc, "Journal Voucher Detail", "entries");
|
||||
jvdetail.account = d.account;
|
||||
jvdetail.balance = d.balance;
|
||||
var jvdetail = frappe.model.add_child(doc, "Journal Voucher Detail", "entries");
|
||||
$.each(r, function(i, d) {
|
||||
var row = frappe.model.add_child(doc, "Journal Voucher Detail", "entries");
|
||||
row.account = d.account;
|
||||
row.balance = d.balance;
|
||||
});
|
||||
refresh_field("entries");
|
||||
}
|
||||
@@ -231,7 +232,7 @@ cur_frm.cscript.voucher_type = function(doc, cdt, cdn) {
|
||||
},
|
||||
callback: function(r) {
|
||||
if(r.message) {
|
||||
update_jv_details(doc, r);
|
||||
update_jv_details(doc, [r.message]);
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -245,7 +246,7 @@ cur_frm.cscript.voucher_type = function(doc, cdt, cdn) {
|
||||
callback: function(r) {
|
||||
frappe.model.clear_table(doc, "entries");
|
||||
if(r.message) {
|
||||
update_jv_details(doc, r);
|
||||
update_jv_details(doc, r.message);
|
||||
}
|
||||
cur_frm.set_value("is_opening", "Yes")
|
||||
}
|
||||
|
||||
@@ -339,6 +339,7 @@
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "letter_head",
|
||||
"fieldtype": "Link",
|
||||
"label": "Letter Head",
|
||||
@@ -446,7 +447,7 @@
|
||||
"icon": "icon-file-text",
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"modified": "2014-08-14 01:37:14.822939",
|
||||
"modified": "2014-09-09 05:35:31.217863",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Journal Voucher",
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
from frappe.utils import cint, cstr, flt, fmt_money, formatdate, getdate
|
||||
from frappe import msgprint, _
|
||||
from frappe.utils import cstr, flt, fmt_money, formatdate, getdate
|
||||
from frappe import msgprint, _, scrub
|
||||
from erpnext.setup.utils import get_company_currency
|
||||
|
||||
from erpnext.controllers.accounts_controller import AccountsController
|
||||
@@ -13,10 +13,6 @@ from erpnext.controllers.accounts_controller import AccountsController
|
||||
class JournalVoucher(AccountsController):
|
||||
def __init__(self, arg1, arg2=None):
|
||||
super(JournalVoucher, self).__init__(arg1, arg2)
|
||||
self.master_type = {}
|
||||
self.credit_days_for = {}
|
||||
self.credit_days_global = -1
|
||||
self.is_approving_authority = -1
|
||||
|
||||
def validate(self):
|
||||
if not self.is_opening:
|
||||
@@ -35,18 +31,35 @@ class JournalVoucher(AccountsController):
|
||||
self.create_remarks()
|
||||
self.set_aging_date()
|
||||
self.set_print_format_fields()
|
||||
self.validate_against_sales_order()
|
||||
self.validate_against_purchase_order()
|
||||
|
||||
def on_submit(self):
|
||||
if self.voucher_type in ['Bank Voucher', 'Contra Voucher', 'Journal Entry']:
|
||||
self.check_credit_days()
|
||||
self.check_reference_date()
|
||||
self.make_gl_entries()
|
||||
self.check_credit_limit()
|
||||
self.update_advance_paid()
|
||||
|
||||
def update_advance_paid(self):
|
||||
advance_paid = frappe._dict()
|
||||
for d in self.get("entries"):
|
||||
if d.is_advance:
|
||||
if d.against_sales_order:
|
||||
advance_paid.setdefault("Sales Order", []).append(d.against_sales_order)
|
||||
elif d.against_purchase_order:
|
||||
advance_paid.setdefault("Purchase Order", []).append(d.against_purchase_order)
|
||||
|
||||
for voucher_type, order_list in advance_paid.items():
|
||||
for voucher_no in list(set(order_list)):
|
||||
frappe.get_doc(voucher_type, voucher_no).set_total_advance_paid()
|
||||
|
||||
def on_cancel(self):
|
||||
from erpnext.accounts.utils import remove_against_link_from_jv
|
||||
remove_against_link_from_jv(self.doctype, self.name, "against_jv")
|
||||
|
||||
self.make_gl_entries(1)
|
||||
self.update_advance_paid()
|
||||
|
||||
def validate_cheque_info(self):
|
||||
if self.voucher_type in ['Bank Voucher']:
|
||||
@@ -59,25 +72,36 @@ class JournalVoucher(AccountsController):
|
||||
|
||||
def validate_entries_for_advance(self):
|
||||
for d in self.get('entries'):
|
||||
if not d.is_advance and not d.against_voucher and \
|
||||
not d.against_invoice and not d.against_jv:
|
||||
if not (d.against_voucher and d.against_invoice and d.against_jv):
|
||||
master_type = frappe.db.get_value("Account", d.account, "master_type")
|
||||
if (master_type == 'Customer' and flt(d.credit) > 0) or \
|
||||
(master_type == 'Supplier' and flt(d.debit) > 0):
|
||||
msgprint(_("Please check 'Is Advance' against Account {0} if this is an advance entry.").format(d.account))
|
||||
if not d.is_advance:
|
||||
msgprint(_("Row {0}: Please check 'Is Advance' against Account {1} if this is an advance entry.").format(d.idx, d.account))
|
||||
elif (d.against_sales_order or d.against_purchase_order) and d.is_advance != "Yes":
|
||||
frappe.throw(_("Row {0}: Payment against Sales/Purchase Order should always be marked as advance").format(d.idx))
|
||||
|
||||
def validate_against_jv(self):
|
||||
for d in self.get('entries'):
|
||||
if d.against_jv:
|
||||
account_root_type = frappe.db.get_value("Account", d.account, "root_type")
|
||||
if account_root_type == "Asset" and flt(d.debit) > 0:
|
||||
frappe.throw(_("For {0}, only credit entries can be linked against another debit entry")
|
||||
.format(d.account))
|
||||
elif account_root_type == "Liability" and flt(d.credit) > 0:
|
||||
frappe.throw(_("For {0}, only debit entries can be linked against another credit entry")
|
||||
.format(d.account))
|
||||
|
||||
if d.against_jv == self.name:
|
||||
frappe.throw(_("You can not enter current voucher in 'Against Journal Voucher' column"))
|
||||
|
||||
against_entries = frappe.db.sql("""select * from `tabJournal Voucher Detail`
|
||||
where account = %s and docstatus = 1 and parent = %s
|
||||
and ifnull(against_jv, '') = ''""", (d.account, d.against_jv), as_dict=True)
|
||||
and ifnull(against_jv, '') = '' and ifnull(against_invoice, '') = ''
|
||||
and ifnull(against_voucher, '') = ''""", (d.account, d.against_jv), as_dict=True)
|
||||
|
||||
if not against_entries:
|
||||
frappe.throw(_("Journal Voucher {0} does not have account {1} or already matched")
|
||||
frappe.throw(_("Journal Voucher {0} does not have account {1} or already matched against other voucher")
|
||||
.format(d.against_jv, d.account))
|
||||
else:
|
||||
dr_or_cr = "debit" if d.credit > 0 else "credit"
|
||||
@@ -90,24 +114,89 @@ class JournalVoucher(AccountsController):
|
||||
.format(d.against_jv, dr_or_cr))
|
||||
|
||||
def validate_against_sales_invoice(self):
|
||||
for d in self.get("entries"):
|
||||
if d.against_invoice:
|
||||
if d.debit > 0:
|
||||
frappe.throw(_("Row {0}: Debit entry can not be linked with a Sales Invoice")
|
||||
.format(d.idx))
|
||||
if frappe.db.get_value("Sales Invoice", d.against_invoice, "debit_to") != d.account:
|
||||
frappe.throw(_("Row {0}: Account does not match with \
|
||||
Sales Invoice Debit To account").format(d.idx, d.account))
|
||||
payment_against_voucher = self.validate_account_in_against_voucher("against_invoice", "Sales Invoice")
|
||||
self.validate_against_invoice_fields("Sales Invoice", payment_against_voucher)
|
||||
|
||||
def validate_against_purchase_invoice(self):
|
||||
payment_against_voucher = self.validate_account_in_against_voucher("against_voucher", "Purchase Invoice")
|
||||
self.validate_against_invoice_fields("Purchase Invoice", payment_against_voucher)
|
||||
|
||||
def validate_against_sales_order(self):
|
||||
payment_against_voucher = self.validate_account_in_against_voucher("against_sales_order", "Sales Order")
|
||||
self.validate_against_order_fields("Sales Order", payment_against_voucher)
|
||||
|
||||
def validate_against_purchase_order(self):
|
||||
payment_against_voucher = self.validate_account_in_against_voucher("against_purchase_order", "Purchase Order")
|
||||
self.validate_against_order_fields("Purchase Order", payment_against_voucher)
|
||||
|
||||
def validate_account_in_against_voucher(self, against_field, doctype):
|
||||
payment_against_voucher = frappe._dict()
|
||||
field_dict = {'Sales Invoice': "Debit To",
|
||||
'Purchase Invoice': "Credit To",
|
||||
'Sales Order': "Customer",
|
||||
'Purchase Order': "Supplier"
|
||||
}
|
||||
|
||||
for d in self.get("entries"):
|
||||
if d.against_voucher:
|
||||
if flt(d.credit) > 0:
|
||||
frappe.throw(_("Row {0}: Credit entry can not be linked with a Purchase Invoice")
|
||||
.format(d.idx))
|
||||
if frappe.db.get_value("Purchase Invoice", d.against_voucher, "credit_to") != d.account:
|
||||
frappe.throw(_("Row {0}: Account does not match with \
|
||||
Purchase Invoice Credit To account").format(d.idx, d.account))
|
||||
if d.get(against_field):
|
||||
dr_or_cr = "credit" if against_field in ["against_invoice", "against_sales_order"] \
|
||||
else "debit"
|
||||
if against_field in ["against_invoice", "against_sales_order"] \
|
||||
and flt(d.debit) > 0:
|
||||
frappe.throw(_("Row {0}: Debit entry can not be linked with a {1}").format(d.idx, doctype))
|
||||
|
||||
if against_field in ["against_voucher", "against_purchase_order"] \
|
||||
and flt(d.credit) > 0:
|
||||
frappe.throw(_("Row {0}: Credit entry can not be linked with a {1}").format(d.idx, doctype))
|
||||
|
||||
voucher_account = frappe.db.get_value(doctype, d.get(against_field), \
|
||||
scrub(field_dict.get(doctype)))
|
||||
|
||||
account_master_name = frappe.db.get_value("Account", d.account, "master_name")
|
||||
|
||||
if against_field in ["against_invoice", "against_voucher"] \
|
||||
and voucher_account != d.account:
|
||||
frappe.throw(_("Row {0}: Account {1} does not match with {2} {3} account") \
|
||||
.format(d.idx, d.account, doctype, field_dict.get(doctype)))
|
||||
|
||||
if against_field in ["against_sales_order", "against_purchase_order"]:
|
||||
if voucher_account != account_master_name:
|
||||
frappe.throw(_("Row {0}: Account {1} does not match with {2} {3} Name") \
|
||||
.format(d.idx, d.account, doctype, field_dict.get(doctype)))
|
||||
elif d.is_advance == "Yes":
|
||||
payment_against_voucher.setdefault(d.get(against_field), []).append(flt(d.get(dr_or_cr)))
|
||||
|
||||
return payment_against_voucher
|
||||
|
||||
def validate_against_invoice_fields(self, doctype, payment_against_voucher):
|
||||
for voucher_no, payment_list in payment_against_voucher.items():
|
||||
voucher_properties = frappe.db.get_value(doctype, voucher_no,
|
||||
["docstatus", "outstanding_amount"])
|
||||
|
||||
if voucher_properties[0] != 1:
|
||||
frappe.throw(_("{0} {1} is not submitted").format(doctype, voucher_no))
|
||||
|
||||
if flt(voucher_properties[1]) < flt(sum(payment_list)):
|
||||
frappe.throw(_("Payment against {0} {1} cannot be greater \
|
||||
than Outstanding Amount {2}").format(doctype, voucher_no, voucher_properties[1]))
|
||||
|
||||
def validate_against_order_fields(self, doctype, payment_against_voucher):
|
||||
for voucher_no, payment_list in payment_against_voucher.items():
|
||||
voucher_properties = frappe.db.get_value(doctype, voucher_no,
|
||||
["docstatus", "per_billed", "status", "advance_paid", "grand_total"])
|
||||
|
||||
if voucher_properties[0] != 1:
|
||||
frappe.throw(_("{0} {1} is not submitted").format(doctype, voucher_no))
|
||||
|
||||
if flt(voucher_properties[1]) >= 100:
|
||||
frappe.throw(_("{0} {1} is fully billed").format(doctype, voucher_no))
|
||||
|
||||
if cstr(voucher_properties[2]) == "Stopped":
|
||||
frappe.throw(_("{0} {1} is stopped").format(doctype, voucher_no))
|
||||
|
||||
if flt(voucher_properties[4]) < flt(voucher_properties[3]) + flt(sum(payment_list)):
|
||||
frappe.throw(_("Advance paid against {0} {1} cannot be greater \
|
||||
than Grand Total {2}").format(doctype, voucher_no, voucher_properties[3]))
|
||||
|
||||
def set_against_account(self):
|
||||
accounts_debited, accounts_credited = [], []
|
||||
@@ -147,7 +236,13 @@ class JournalVoucher(AccountsController):
|
||||
for d in self.get('entries'):
|
||||
if d.against_invoice and d.credit:
|
||||
currency = frappe.db.get_value("Sales Invoice", d.against_invoice, "currency")
|
||||
r.append(_("{0} {1} against Invoice {2}").format(currency, fmt_money(flt(d.credit)), d.against_invoice))
|
||||
r.append(_("{0} against Sales Invoice {1}").format(fmt_money(flt(d.credit), currency = currency), \
|
||||
d.against_invoice))
|
||||
|
||||
if d.against_sales_order and d.credit:
|
||||
currency = frappe.db.get_value("Sales Order", d.against_sales_order, "currency")
|
||||
r.append(_("{0} against Sales Order {1}").format(fmt_money(flt(d.credit), currency = currency), \
|
||||
d.against_sales_order))
|
||||
|
||||
if d.against_voucher and d.debit:
|
||||
bill_no = frappe.db.sql("""select bill_no, bill_date, currency
|
||||
@@ -158,13 +253,17 @@ class JournalVoucher(AccountsController):
|
||||
fmt_money(flt(d.debit)), bill_no[0][0],
|
||||
bill_no[0][1] and formatdate(bill_no[0][1].strftime('%Y-%m-%d'))))
|
||||
|
||||
if d.against_purchase_order and d.debit:
|
||||
currency = frappe.db.get_value("Purchase Order", d.against_purchase_order, "currency")
|
||||
r.append(_("{0} against Purchase Order {1}").format(fmt_money(flt(d.credit), currency = currency), \
|
||||
d.against_purchase_order))
|
||||
|
||||
if self.user_remark:
|
||||
r.append(_("Note: {0}").format(self.user_remark))
|
||||
|
||||
if r:
|
||||
self.remark = ("\n").join(r)
|
||||
else:
|
||||
frappe.msgprint(_("User Remarks is mandatory"), raise_exception=frappe.MandatoryError)
|
||||
self.remark = ("\n").join(r) #User Remarks is not mandatory
|
||||
|
||||
|
||||
def set_aging_date(self):
|
||||
if self.is_opening != 'Yes':
|
||||
@@ -186,71 +285,38 @@ class JournalVoucher(AccountsController):
|
||||
|
||||
def set_print_format_fields(self):
|
||||
for d in self.get('entries'):
|
||||
result = frappe.db.get_value("Account", d.account,
|
||||
["account_type", "master_type"])
|
||||
acc = frappe.db.get_value("Account", d.account, ["account_type", "master_type"], as_dict=1)
|
||||
|
||||
if not result:
|
||||
continue
|
||||
if not acc: continue
|
||||
|
||||
account_type, master_type = result
|
||||
|
||||
if master_type in ['Supplier', 'Customer']:
|
||||
if acc.master_type in ['Supplier', 'Customer']:
|
||||
if not self.pay_to_recd_from:
|
||||
self.pay_to_recd_from = frappe.db.get_value(master_type,
|
||||
' - '.join(d.account.split(' - ')[:-1]),
|
||||
master_type == 'Customer' and 'customer_name' or 'supplier_name')
|
||||
self.pay_to_recd_from = frappe.db.get_value(acc.master_type, ' - '.join(d.account.split(' - ')[:-1]),
|
||||
acc.master_type == 'Customer' and 'customer_name' or 'supplier_name')
|
||||
if self.voucher_type in ["Credit Note", "Debit Note"]:
|
||||
self.set_total_amount(d.debit or d.credit)
|
||||
|
||||
if account_type in ['Bank', 'Cash']:
|
||||
company_currency = get_company_currency(self.company)
|
||||
amt = flt(d.debit) and d.debit or d.credit
|
||||
self.total_amount = fmt_money(amt, currency=company_currency)
|
||||
from frappe.utils import money_in_words
|
||||
self.total_amount_in_words = money_in_words(amt, company_currency)
|
||||
if acc.account_type in ['Bank', 'Cash']:
|
||||
self.set_total_amount(d.debit or d.credit)
|
||||
|
||||
def check_credit_days(self):
|
||||
date_diff = 0
|
||||
def set_total_amount(self, amt):
|
||||
company_currency = get_company_currency(self.company)
|
||||
self.total_amount = fmt_money(amt, currency=company_currency)
|
||||
from frappe.utils import money_in_words
|
||||
self.total_amount_in_words = money_in_words(amt, company_currency)
|
||||
|
||||
def check_reference_date(self):
|
||||
if self.cheque_date:
|
||||
date_diff = (getdate(self.cheque_date)-getdate(self.posting_date)).days
|
||||
for d in self.get("entries"):
|
||||
due_date = None
|
||||
if d.against_invoice and flt(d.credit) > 0:
|
||||
due_date = frappe.db.get_value("Sales Invoice", d.against_invoice, "due_date")
|
||||
elif d.against_voucher and flt(d.debit) > 0:
|
||||
due_date = frappe.db.get_value("Purchase Invoice", d.against_voucher, "due_date")
|
||||
|
||||
if date_diff <= 0: return
|
||||
|
||||
# Get List of Customer Account
|
||||
acc_list = filter(lambda d: frappe.db.get_value("Account", d.account,
|
||||
"master_type")=='Customer', self.get('entries'))
|
||||
|
||||
for d in acc_list:
|
||||
credit_days = self.get_credit_days_for(d.account)
|
||||
# Check credit days
|
||||
if credit_days > 0 and not self.get_authorized_user() and cint(date_diff) > credit_days:
|
||||
msgprint(_("Maximum allowed credit is {0} days after posting date").format(credit_days),
|
||||
raise_exception=1)
|
||||
|
||||
def get_credit_days_for(self, ac):
|
||||
if not self.credit_days_for.has_key(ac):
|
||||
self.credit_days_for[ac] = cint(frappe.db.get_value("Account", ac, "credit_days"))
|
||||
|
||||
if not self.credit_days_for[ac]:
|
||||
if self.credit_days_global==-1:
|
||||
self.credit_days_global = cint(frappe.db.get_value("Company",
|
||||
self.company, "credit_days"))
|
||||
|
||||
return self.credit_days_global
|
||||
else:
|
||||
return self.credit_days_for[ac]
|
||||
|
||||
def get_authorized_user(self):
|
||||
if self.is_approving_authority==-1:
|
||||
self.is_approving_authority = 0
|
||||
|
||||
# Fetch credit controller role
|
||||
approving_authority = frappe.db.get_value("Accounts Settings", None,
|
||||
"credit_controller")
|
||||
|
||||
# Check logged-in user is authorized
|
||||
if approving_authority in frappe.user.get_roles():
|
||||
self.is_approving_authority = 1
|
||||
|
||||
return self.is_approving_authority
|
||||
if due_date and getdate(self.cheque_date) > getdate(due_date):
|
||||
msgprint(_("Note: Reference Date {0} is after invoice due date {1}")
|
||||
.format(formatdate(self.cheque_date), formatdate(due_date)))
|
||||
|
||||
def make_gl_entries(self, cancel=0, adv_adj=0):
|
||||
from erpnext.accounts.general_ledger import make_gl_entries
|
||||
@@ -264,14 +330,18 @@ class JournalVoucher(AccountsController):
|
||||
"against": d.against_account,
|
||||
"debit": flt(d.debit, self.precision("debit", "entries")),
|
||||
"credit": flt(d.credit, self.precision("credit", "entries")),
|
||||
"against_voucher_type": ((d.against_voucher and "Purchase Invoice")
|
||||
or (d.against_invoice and "Sales Invoice")
|
||||
or (d.against_jv and "Journal Voucher")),
|
||||
"against_voucher": d.against_voucher or d.against_invoice or d.against_jv,
|
||||
"against_voucher_type": (("Purchase Invoice" if d.against_voucher else None)
|
||||
or ("Sales Invoice" if d.against_invoice else None)
|
||||
or ("Journal Voucher" if d.against_jv else None)
|
||||
or ("Sales Order" if d.against_sales_order else None)
|
||||
or ("Purchase Order" if d.against_purchase_order else None)),
|
||||
"against_voucher": d.against_voucher or d.against_invoice or d.against_jv
|
||||
or d.against_sales_order or d.against_purchase_order,
|
||||
"remarks": self.remark,
|
||||
"cost_center": d.cost_center
|
||||
})
|
||||
)
|
||||
|
||||
if gl_map:
|
||||
make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj)
|
||||
|
||||
@@ -279,7 +349,7 @@ class JournalVoucher(AccountsController):
|
||||
for d in self.get("entries"):
|
||||
master_type, master_name = frappe.db.get_value("Account", d.account,
|
||||
["master_type", "master_name"])
|
||||
if master_type == "Customer" and master_name:
|
||||
if master_type == "Customer" and master_name and flt(d.debit) > 0:
|
||||
super(JournalVoucher, self).check_credit_limit(d.account)
|
||||
|
||||
def get_balance(self):
|
||||
@@ -434,9 +504,10 @@ def get_against_sales_invoice(doctype, txt, searchfield, start, page_len, filter
|
||||
(filters["account"], "%%%s%%" % txt, start, page_len))
|
||||
|
||||
def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
|
||||
return frappe.db.sql("""select jv.name, jv.posting_date, jv.user_remark
|
||||
from `tabJournal Voucher` jv, `tabJournal Voucher Detail` jv_detail
|
||||
where jv_detail.parent = jv.name and jv_detail.account = %s and jv.docstatus = 1
|
||||
return frappe.db.sql("""select distinct jv.name, jv.posting_date, jv.user_remark
|
||||
from `tabJournal Voucher` jv, `tabJournal Voucher Detail` jvd
|
||||
where jvd.parent = jv.name and jvd.account = %s and jv.docstatus = 1
|
||||
and (ifnull(jvd.against_invoice, '') = '' and ifnull(jvd.against_voucher, '') = '' and ifnull(jvd.against_jv, '') = '' )
|
||||
and jv.%s like %s order by jv.name desc limit %s, %s""" %
|
||||
("%s", searchfield, "%s", "%s", "%s"),
|
||||
(filters["account"], "%%%s%%" % txt, start, page_len))
|
||||
|
||||
@@ -1,41 +1,88 @@
|
||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import unittest
|
||||
import frappe
|
||||
import unittest, frappe
|
||||
from frappe.utils import flt
|
||||
|
||||
class TestJournalVoucher(unittest.TestCase):
|
||||
def test_journal_voucher_with_against_jv(self):
|
||||
self.clear_account_balance()
|
||||
|
||||
jv_invoice = frappe.copy_doc(test_records[2])
|
||||
jv_invoice.insert()
|
||||
jv_invoice.submit()
|
||||
base_jv = frappe.copy_doc(test_records[0])
|
||||
self.jv_against_voucher_testcase(base_jv, jv_invoice)
|
||||
|
||||
self.assertTrue(frappe.db.sql("""select name from `tabJournal Voucher Detail`
|
||||
where account = %s and docstatus = 1 and parent = %s""",
|
||||
("_Test Customer - _TC", jv_invoice.name)))
|
||||
def test_jv_against_sales_order(self):
|
||||
from erpnext.selling.doctype.sales_order.test_sales_order \
|
||||
import test_records as so_test_records
|
||||
|
||||
sales_order = frappe.copy_doc(so_test_records[0])
|
||||
base_jv = frappe.copy_doc(test_records[0])
|
||||
self.jv_against_voucher_testcase(base_jv, sales_order)
|
||||
|
||||
def test_jv_against_purchase_order(self):
|
||||
from erpnext.buying.doctype.purchase_order.test_purchase_order \
|
||||
import test_records as po_test_records
|
||||
|
||||
purchase_order = frappe.copy_doc(po_test_records[0])
|
||||
base_jv = frappe.copy_doc(test_records[1])
|
||||
self.jv_against_voucher_testcase(base_jv, purchase_order)
|
||||
|
||||
def jv_against_voucher_testcase(self, base_jv, test_voucher):
|
||||
dr_or_cr = "credit" if test_voucher.doctype in ["Sales Order", "Journal Voucher"] else "debit"
|
||||
field_dict = {'Journal Voucher': "against_jv",
|
||||
'Sales Order': "against_sales_order",
|
||||
'Purchase Order': "against_purchase_order"
|
||||
}
|
||||
|
||||
self.clear_account_balance()
|
||||
test_voucher.insert()
|
||||
test_voucher.submit()
|
||||
|
||||
if test_voucher.doctype == "Journal Voucher":
|
||||
self.assertTrue(frappe.db.sql("""select name from `tabJournal Voucher Detail`
|
||||
where account = %s and docstatus = 1 and parent = %s""",
|
||||
("_Test Customer - _TC", test_voucher.name)))
|
||||
|
||||
self.assertTrue(not frappe.db.sql("""select name from `tabJournal Voucher Detail`
|
||||
where against_jv=%s""", jv_invoice.name))
|
||||
where %s=%s""" % (field_dict.get(test_voucher.doctype), '%s'), (test_voucher.name)))
|
||||
|
||||
jv_payment = frappe.copy_doc(test_records[0])
|
||||
jv_payment.get("entries")[0].against_jv = jv_invoice.name
|
||||
jv_payment.insert()
|
||||
jv_payment.submit()
|
||||
base_jv.get("entries")[0].is_advance = "Yes" if (test_voucher.doctype in ["Sales Order", "Purchase Order"]) else "No"
|
||||
base_jv.get("entries")[0].set(field_dict.get(test_voucher.doctype), test_voucher.name)
|
||||
base_jv.insert()
|
||||
base_jv.submit()
|
||||
|
||||
submitted_voucher = frappe.get_doc(test_voucher.doctype, test_voucher.name)
|
||||
|
||||
self.assertTrue(frappe.db.sql("""select name from `tabJournal Voucher Detail`
|
||||
where against_jv=%s""", jv_invoice.name))
|
||||
where %s=%s""" % (field_dict.get(test_voucher.doctype), '%s'), (submitted_voucher.name)))
|
||||
|
||||
self.assertTrue(frappe.db.sql("""select name from `tabJournal Voucher Detail`
|
||||
where against_jv=%s and credit=400""", jv_invoice.name))
|
||||
where %s=%s and %s=400""" % (field_dict.get(submitted_voucher.doctype), '%s', dr_or_cr), (submitted_voucher.name)))
|
||||
|
||||
# cancel jv_invoice
|
||||
jv_invoice.cancel()
|
||||
if base_jv.get("entries")[0].is_advance == "Yes":
|
||||
self.advance_paid_testcase(base_jv, submitted_voucher, dr_or_cr)
|
||||
self.cancel_against_voucher_testcase(submitted_voucher)
|
||||
|
||||
self.assertTrue(not frappe.db.sql("""select name from `tabJournal Voucher Detail`
|
||||
where against_jv=%s""", jv_invoice.name))
|
||||
def advance_paid_testcase(self, base_jv, test_voucher, dr_or_cr):
|
||||
#Test advance paid field
|
||||
advance_paid = frappe.db.sql("""select advance_paid from `tab%s`
|
||||
where name=%s""" % (test_voucher.doctype, '%s'), (test_voucher.name))
|
||||
payment_against_order = base_jv.get("entries")[0].get(dr_or_cr)
|
||||
|
||||
self.assertTrue(flt(advance_paid[0][0]) == flt(payment_against_order))
|
||||
|
||||
def cancel_against_voucher_testcase(self, test_voucher):
|
||||
if test_voucher.doctype == "Journal Voucher":
|
||||
# if test_voucher is a Journal Voucher, test cancellation of test_voucher
|
||||
test_voucher.cancel()
|
||||
self.assertTrue(not frappe.db.sql("""select name from `tabJournal Voucher Detail`
|
||||
where against_jv=%s""", test_voucher.name))
|
||||
|
||||
elif test_voucher.doctype in ["Sales Order", "Purchase Order"]:
|
||||
# if test_voucher is a Sales Order/Purchase Order, test error on cancellation of test_voucher
|
||||
submitted_voucher = frappe.get_doc(test_voucher.doctype, test_voucher.name)
|
||||
self.assertRaises(frappe.LinkExistsError, submitted_voucher.cancel)
|
||||
|
||||
def test_jv_against_stock_account(self):
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
|
||||
|
||||
@@ -117,11 +117,6 @@
|
||||
"print_hide": 0,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "col_break3",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "against_jv",
|
||||
"fieldtype": "Link",
|
||||
@@ -135,6 +130,25 @@
|
||||
"print_hide": 0,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "col_break3",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "against_sales_order",
|
||||
"fieldtype": "Link",
|
||||
"label": "Against Sales Order",
|
||||
"options": "Sales Order",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "against_purchase_order",
|
||||
"fieldtype": "Link",
|
||||
"label": "Against Purchase Order",
|
||||
"options": "Purchase Order",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "is_advance",
|
||||
"fieldtype": "Select",
|
||||
@@ -160,7 +174,7 @@
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"modified": "2014-07-25 03:16:51.149899",
|
||||
"modified": "2014-08-20 12:19:55.049973",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Journal Voucher Detail",
|
||||
|
||||
@@ -19,9 +19,9 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
this.frm.set_query('bank_cash_account', function() {
|
||||
if(!me.frm.doc.company) {
|
||||
msgprint(__("Please select company first"));
|
||||
@@ -35,12 +35,8 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
var help_content = '<i class="icon-hand-right"></i> Note:<br>'+
|
||||
'<ul>If you are unable to match the exact amount, then amend your Journal Voucher and split rows such that payment amount match the invoice amount.</ul>';
|
||||
this.frm.set_value("reconcile_help", help_content);
|
||||
},
|
||||
|
||||
|
||||
get_unreconciled_entries: function() {
|
||||
var me = this;
|
||||
return this.frm.call({
|
||||
@@ -48,12 +44,12 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
||||
method: 'get_unreconciled_entries',
|
||||
callback: function(r, rt) {
|
||||
var invoices = [];
|
||||
|
||||
|
||||
$.each(me.frm.doc.payment_reconciliation_invoices || [], function(i, row) {
|
||||
if (row.invoice_number && !inList(invoices, row.invoice_number))
|
||||
if (row.invoice_number && !inList(invoices, row.invoice_number))
|
||||
invoices.push(row.invoice_number);
|
||||
});
|
||||
|
||||
|
||||
frappe.meta.get_docfield("Payment Reconciliation Payment", "invoice_number",
|
||||
me.frm.doc.name).options = invoices.join("\n");
|
||||
|
||||
@@ -79,4 +75,4 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
||||
|
||||
$.extend(cur_frm.cscript, new erpnext.accounts.PaymentReconciliationController({frm: cur_frm}));
|
||||
|
||||
cur_frm.add_fetch('party_account', 'master_type', 'party_type')
|
||||
cur_frm.add_fetch('party_account', 'master_type', 'party_type')
|
||||
|
||||
@@ -118,19 +118,12 @@
|
||||
"options": "Payment Reconciliation Invoice",
|
||||
"permlevel": 0,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "reconcile_help",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "",
|
||||
"permlevel": 0,
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"hide_toolbar": 1,
|
||||
"icon": "icon-resize-horizontal",
|
||||
"issingle": 1,
|
||||
"modified": "2014-07-31 05:43:03.410832",
|
||||
"modified": "2014-10-16 17:51:44.367107",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Reconciliation",
|
||||
|
||||
@@ -96,13 +96,14 @@ class PaymentReconciliation(Document):
|
||||
|
||||
payment_amount = payment_amount[0][0] if payment_amount else 0
|
||||
|
||||
if d.invoice_amount > payment_amount:
|
||||
if d.invoice_amount - payment_amount > 0.005:
|
||||
non_reconciled_invoices.append({
|
||||
'voucher_no': d.voucher_no,
|
||||
'voucher_type': d.voucher_type,
|
||||
'posting_date': d.posting_date,
|
||||
'invoice_amount': flt(d.invoice_amount),
|
||||
'outstanding_amount': d.invoice_amount - payment_amount})
|
||||
'outstanding_amount': flt(d.invoice_amount - payment_amount, 2)
|
||||
})
|
||||
|
||||
self.add_invoice_entries(non_reconciled_invoices)
|
||||
|
||||
@@ -124,7 +125,7 @@ class PaymentReconciliation(Document):
|
||||
dr_or_cr = "credit" if self.party_type == "Customer" else "debit"
|
||||
lst = []
|
||||
for e in self.get('payment_reconciliation_payments'):
|
||||
if e.invoice_type and e.invoice_number:
|
||||
if e.invoice_type and e.invoice_number and e.allocated_amount:
|
||||
lst.append({
|
||||
'voucher_no' : e.journal_voucher,
|
||||
'voucher_detail_no' : e.voucher_detail_number,
|
||||
@@ -134,7 +135,7 @@ class PaymentReconciliation(Document):
|
||||
'is_advance' : e.is_advance,
|
||||
'dr_or_cr' : dr_or_cr,
|
||||
'unadjusted_amt' : flt(e.amount),
|
||||
'allocated_amt' : flt(e.amount)
|
||||
'allocated_amt' : flt(e.allocated_amount)
|
||||
})
|
||||
|
||||
if lst:
|
||||
@@ -162,18 +163,23 @@ class PaymentReconciliation(Document):
|
||||
|
||||
invoices_to_reconcile = []
|
||||
for p in self.get("payment_reconciliation_payments"):
|
||||
if p.invoice_type and p.invoice_number:
|
||||
if p.invoice_type and p.invoice_number and p.allocated_amount:
|
||||
invoices_to_reconcile.append(p.invoice_number)
|
||||
|
||||
if p.invoice_number not in unreconciled_invoices.get(p.invoice_type, {}):
|
||||
frappe.throw(_("{0}: {1} not found in Invoice Details table")
|
||||
.format(p.invoice_type, p.invoice_number))
|
||||
|
||||
if p.amount > unreconciled_invoices.get(p.invoice_type, {}).get(p.invoice_number):
|
||||
frappe.throw(_("Row {0}: Payment amount must be less than or equals to invoice outstanding amount. Please refer Note below.").format(p.idx))
|
||||
if flt(p.allocated_amount) > flt(p.amount):
|
||||
frappe.throw(_("Row {0}: Allocated amount {1} must be less than or equals to JV amount {2}")
|
||||
.format(p.idx, p.allocated_amount, p.amount))
|
||||
|
||||
if flt(p.allocated_amount) > unreconciled_invoices.get(p.invoice_type, {}).get(p.invoice_number):
|
||||
frappe.throw(_("Row {0}: Allocated amount {1} must be less than or equals to invoice outstanding amount {2}")
|
||||
.format(p.idx, p.allocated_amount, unreconciled_invoices.get(p.invoice_type, {}).get(p.invoice_number)))
|
||||
|
||||
if not invoices_to_reconcile:
|
||||
frappe.throw(_("Please select Invoice Type and Invoice Number in atleast one row"))
|
||||
frappe.throw(_("Please select Allocated Amount, Invoice Type and Invoice Number in atleast one row"))
|
||||
|
||||
def check_condition(self, dr_or_cr):
|
||||
cond = self.from_date and " and posting_date >= '" + self.from_date + "'" or ""
|
||||
|
||||
@@ -53,11 +53,20 @@
|
||||
"label": "Column Break",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "allocated_amount",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Allocated amount",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "Sales Invoice",
|
||||
"fieldname": "invoice_type",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"in_list_view": 0,
|
||||
"label": "Invoice Type",
|
||||
"options": "\nSales Invoice\nPurchase Invoice\nJournal Voucher",
|
||||
"permlevel": 0,
|
||||
@@ -95,7 +104,7 @@
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"modified": "2014-07-21 16:53:56.206169",
|
||||
"modified": "2014-10-16 17:40:54.040194",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Reconciliation Payment",
|
||||
|
||||
214
erpnext/accounts/doctype/payment_tool/payment_tool.js
Normal file
214
erpnext/accounts/doctype/payment_tool/payment_tool.js
Normal file
@@ -0,0 +1,214 @@
|
||||
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.provide("erpnext.payment_tool");
|
||||
|
||||
// Help content
|
||||
frappe.ui.form.on("Payment Tool", "onload", function(frm) {
|
||||
frm.set_value("make_jv_help", '<i class="icon-hand-right"></i> '
|
||||
+ __("Note: If payment is not made against any reference, make Journal Voucher manually."));
|
||||
|
||||
frm.set_query("payment_account", function() {
|
||||
return {
|
||||
filters: [
|
||||
['Account', 'account_type', 'in', 'Bank, Cash'],
|
||||
['Account', 'group_or_ledger', '=', 'Ledger'],
|
||||
['Account', 'company', '=', frm.doc.company]
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
frm.set_query("against_voucher_type", "payment_tool_details", function() {
|
||||
return {
|
||||
filters: {"name": ["in", ["Sales Invoice", "Purchase Invoice", "Journal Voucher", "Sales Order", "Purchase Order"]]}
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Payment Tool", "refresh", function(frm) {
|
||||
frappe.ui.form.trigger("Payment Tool", "party_type");
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Payment Tool", "party_type", function(frm) {
|
||||
frm.toggle_reqd("customer", frm.doc.party_type == "Customer");
|
||||
frm.toggle_reqd("supplier", frm.doc.party_type == "Supplier");
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Payment Tool", "company", function(frm) {
|
||||
erpnext.payment_tool.check_mandatory_to_set_button(frm);
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Payment Tool", "received_or_paid", function(frm) {
|
||||
erpnext.payment_tool.check_mandatory_to_set_button(frm);
|
||||
});
|
||||
|
||||
// Set party account name
|
||||
frappe.ui.form.on("Payment Tool", "customer", function(frm) {
|
||||
erpnext.payment_tool.set_party_account(frm);
|
||||
erpnext.payment_tool.check_mandatory_to_set_button(frm);
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Payment Tool", "supplier", function(frm) {
|
||||
erpnext.payment_tool.set_party_account(frm);
|
||||
erpnext.payment_tool.check_mandatory_to_set_button(frm);
|
||||
});
|
||||
|
||||
erpnext.payment_tool.check_mandatory_to_set_button = function(frm) {
|
||||
if (frm.doc.company && frm.doc.party_type && frm.doc.received_or_paid && (frm.doc.customer || frm.doc.supplier)) {
|
||||
frm.fields_dict.get_outstanding_vouchers.$input.addClass("btn-primary");
|
||||
}
|
||||
}
|
||||
|
||||
//Set Button color
|
||||
erpnext.payment_tool.set_party_account = function(frm) {
|
||||
if(frm.doc.party_type == "Customer") {
|
||||
var party_name = frm.doc.customer;
|
||||
} else {
|
||||
var party_name = frm.doc.supplier;
|
||||
}
|
||||
return frappe.call({
|
||||
method: 'erpnext.accounts.doctype.payment_tool.payment_tool.get_party_account',
|
||||
args: {
|
||||
party_type: frm.doc.party_type,
|
||||
party_name: party_name
|
||||
},
|
||||
callback: function(r, rt) {
|
||||
if(!r.exc) {
|
||||
frm.set_value("party_account", r.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Get outstanding vouchers
|
||||
frappe.ui.form.on("Payment Tool", "get_outstanding_vouchers", function(frm) {
|
||||
erpnext.payment_tool.check_mandatory_to_fetch(frm.doc);
|
||||
|
||||
frm.set_value("payment_tool_details", []);
|
||||
|
||||
return frappe.call({
|
||||
method: 'erpnext.accounts.doctype.payment_tool.payment_tool.get_outstanding_vouchers',
|
||||
args: {
|
||||
args: {
|
||||
"company": frm.doc.company,
|
||||
"party_type": frm.doc.party_type,
|
||||
"received_or_paid": frm.doc.received_or_paid,
|
||||
"party_name": frm.doc.party_type == "Customer" ? frm.doc.customer : frm.doc.supplier,
|
||||
"party_account": frm.doc.party_account
|
||||
}
|
||||
},
|
||||
callback: function(r, rt) {
|
||||
if(r.message) {
|
||||
frm.fields_dict.get_outstanding_vouchers.$input.removeClass("btn-primary");
|
||||
frm.fields_dict.make_journal_voucher.$input.addClass("btn-primary");
|
||||
|
||||
frappe.model.clear_table(frm.doc, "payment_tool_details");
|
||||
$.each(r.message, function(i, d) {
|
||||
var invoice_detail = frappe.model.add_child(frm.doc, "Payment Tool Detail", "payment_tool_details");
|
||||
invoice_detail.against_voucher_type = d.voucher_type;
|
||||
invoice_detail.against_voucher_no = d.voucher_no;
|
||||
invoice_detail.total_amount = d.invoice_amount;
|
||||
invoice_detail.outstanding_amount = d.outstanding_amount;
|
||||
});
|
||||
}
|
||||
refresh_field("payment_tool_details");
|
||||
erpnext.payment_tool.set_total_payment_amount(frm);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// validate against_voucher_type
|
||||
frappe.ui.form.on("Payment Tool Detail", "against_voucher_type", function(frm) {
|
||||
erpnext.payment_tool.validate_against_voucher(frm);
|
||||
});
|
||||
|
||||
erpnext.payment_tool.validate_against_voucher = function(frm) {
|
||||
$.each(frm.doc.payment_tool_details || [], function(i, row) {
|
||||
if(frm.doc.party_type=="Customer"
|
||||
&& !in_list(["Sales Order", "Sales Invoice", "Journal Voucher"], row.against_voucher_type)) {
|
||||
frappe.model.set_value(row.doctype, row.name, "against_voucher_type", "");
|
||||
frappe.throw(__("Against Voucher Type must be one of Sales Order, Sales Invoice or Journal Voucher"))
|
||||
}
|
||||
|
||||
if(frm.doc.party_type=="Supplier"
|
||||
&& !in_list(["Purchase Order", "Purchase Invoice", "Journal Voucher"], row.against_voucher_type)) {
|
||||
frappe.model.set_value(row.doctype, row.name, "against_voucher_type", "");
|
||||
frappe.throw(__("Against Voucher Type must be one of Purchase Order, Purchase Invoice or Journal Voucher"))
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// validate against_voucher_type
|
||||
frappe.ui.form.on("Payment Tool Detail", "against_voucher_no", function(frm, cdt, cdn) {
|
||||
var row = locals[cdt][cdn];
|
||||
frappe.call({
|
||||
method: 'erpnext.accounts.doctype.payment_tool.payment_tool.get_against_voucher_amount',
|
||||
args: {
|
||||
"against_voucher_type": row.against_voucher_type,
|
||||
"against_voucher_no": row.against_voucher_no
|
||||
},
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
$.each(r.message, function(k, v) {
|
||||
frappe.model.set_value(cdt, cdn, k, v);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Set total payment amount
|
||||
frappe.ui.form.on("Payment Tool Detail", "payment_amount", function(frm) {
|
||||
erpnext.payment_tool.set_total_payment_amount(frm);
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Payment Tool Detail", "payment_tool_details_remove", function(frm) {
|
||||
erpnext.payment_tool.set_total_payment_amount(frm);
|
||||
});
|
||||
|
||||
erpnext.payment_tool.set_total_payment_amount = function(frm) {
|
||||
var total_amount = 0.00;
|
||||
$.each(frm.doc.payment_tool_details || [], function(i, row) {
|
||||
if (row.payment_amount && (row.payment_amount <= row.outstanding_amount)) {
|
||||
total_amount = total_amount + row.payment_amount;
|
||||
} else {
|
||||
if(row.payment_amount < 0)
|
||||
msgprint(__("Row {0}: Payment amount can not be negative", [row.idx]));
|
||||
else if(row.payment_amount >= row.outstanding_amount)
|
||||
msgprint(__("Row {0}: Payment Amount cannot be greater than Outstanding Amount", [__(row.idx)]));
|
||||
|
||||
frappe.model.set_value(row.doctype, row.name, "payment_amount", 0.0);
|
||||
}
|
||||
});
|
||||
frm.set_value("total_payment_amount", total_amount);
|
||||
}
|
||||
|
||||
|
||||
// Make Journal voucher
|
||||
frappe.ui.form.on("Payment Tool", "make_journal_voucher", function(frm) {
|
||||
erpnext.payment_tool.check_mandatory_to_fetch(frm.doc);
|
||||
|
||||
return frappe.call({
|
||||
method: 'make_journal_voucher',
|
||||
doc: frm.doc,
|
||||
callback: function(r) {
|
||||
frm.fields_dict.make_journal_voucher.$input.addClass("btn-primary");
|
||||
var doclist = frappe.model.sync(r.message);
|
||||
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
erpnext.payment_tool.check_mandatory_to_fetch = function(doc) {
|
||||
var check_fields = [
|
||||
['Company', doc.company],
|
||||
['Party Type', doc.party_type],
|
||||
['Received Or Paid', doc.received_or_paid],
|
||||
['Customer / Supplier', doc.party_type == "Customer" ? doc.customer : doc.supplier]
|
||||
];
|
||||
|
||||
$.each(check_fields, function(i, v) {
|
||||
if(!v[1]) frappe.throw(__("Please select {0} first", [v[0]]));
|
||||
});
|
||||
}
|
||||
381
erpnext/accounts/doctype/payment_tool/payment_tool.json
Normal file
381
erpnext/accounts/doctype/payment_tool/payment_tool.json
Normal file
@@ -0,0 +1,381 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"creation": "2014-07-23 15:12:27.746665",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "sec_break1",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Party Details",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"label": "Company",
|
||||
"options": "Company",
|
||||
"permlevel": 0,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"default": "Customer",
|
||||
"fieldname": "party_type",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Party Type",
|
||||
"no_copy": 0,
|
||||
"options": "Customer\nSupplier",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"depends_on": "eval:(doc.party_type == 'Customer')",
|
||||
"fieldname": "customer",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Customer",
|
||||
"no_copy": 0,
|
||||
"options": "Customer",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"depends_on": "eval:(doc.party_type == 'Supplier')",
|
||||
"fieldname": "supplier",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Supplier",
|
||||
"no_copy": 0,
|
||||
"options": "Supplier",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "party_account",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
"label": "Party Account",
|
||||
"no_copy": 1,
|
||||
"options": "Account",
|
||||
"permlevel": 0,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "received_or_paid",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Received Or Paid",
|
||||
"no_copy": 0,
|
||||
"options": "Received\nPaid",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "get_outstanding_vouchers",
|
||||
"fieldtype": "Button",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Get Outstanding Vouchers",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "col_break1",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Column Break 1",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "payment_mode",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Payment Mode",
|
||||
"no_copy": 0,
|
||||
"options": "Mode of Payment",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "payment_account",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Payment Account",
|
||||
"no_copy": 0,
|
||||
"options": "Account",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "reference_no",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Reference No",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "reference_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Reference Date",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"depends_on": "eval:(doc.company && doc.party_type && doc.received_or_paid && (doc.customer || doc.supplier))",
|
||||
"fieldname": "sec_break3",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Against Voucher",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "payment_tool_details",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Payment Tool Details",
|
||||
"no_copy": 0,
|
||||
"options": "Payment Tool Detail",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:(doc.company && doc.party_type && doc.received_or_paid && (doc.customer || doc.supplier))",
|
||||
"fieldname": "section_break_19",
|
||||
"fieldtype": "Section Break",
|
||||
"permlevel": 0,
|
||||
"precision": ""
|
||||
},
|
||||
{
|
||||
"fieldname": "total_payment_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Payment Amount",
|
||||
"permlevel": 0,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "make_journal_voucher",
|
||||
"fieldtype": "Button",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Make Journal Voucher",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "data_22",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0,
|
||||
"precision": ""
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:(doc.company && doc.party_type && doc.received_or_paid && (doc.customer || doc.supplier))",
|
||||
"fieldname": "section_break_21",
|
||||
"fieldtype": "Section Break",
|
||||
"permlevel": 0,
|
||||
"precision": ""
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "make_jv_help",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 1,
|
||||
"icon": "icon-magic",
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 1,
|
||||
"istable": 0,
|
||||
"modified": "2014-09-12 04:43:05.963218",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Tool",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 0,
|
||||
"email": 0,
|
||||
"export": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 0,
|
||||
"read": 1,
|
||||
"report": 0,
|
||||
"role": "Accounts Manager",
|
||||
"set_user_permissions": 0,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 0,
|
||||
"email": 0,
|
||||
"export": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 0,
|
||||
"read": 1,
|
||||
"report": 0,
|
||||
"role": "Accounts User",
|
||||
"set_user_permissions": 0,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
||||
119
erpnext/accounts/doctype/payment_tool/payment_tool.py
Normal file
119
erpnext/accounts/doctype/payment_tool/payment_tool.py
Normal file
@@ -0,0 +1,119 @@
|
||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import flt
|
||||
from frappe.model.document import Document
|
||||
import json
|
||||
|
||||
class PaymentTool(Document):
|
||||
def make_journal_voucher(self):
|
||||
from erpnext.accounts.utils import get_balance_on
|
||||
total_payment_amount = 0.00
|
||||
invoice_voucher_type = {
|
||||
'Sales Invoice': 'against_invoice',
|
||||
'Purchase Invoice': 'against_voucher',
|
||||
'Journal Voucher': 'against_jv',
|
||||
'Sales Order': 'against_sales_order',
|
||||
'Purchase Order': 'against_purchase_order',
|
||||
}
|
||||
|
||||
jv = frappe.new_doc('Journal Voucher')
|
||||
jv.voucher_type = 'Journal Entry'
|
||||
jv.company = self.company
|
||||
jv.cheque_no = self.reference_no
|
||||
jv.cheque_date = self.reference_date
|
||||
|
||||
if not self.total_payment_amount:
|
||||
frappe.throw(_("Please enter Payment Amount in atleast one row"))
|
||||
|
||||
for v in self.get("payment_tool_details"):
|
||||
if not frappe.db.get_value(v.against_voucher_type, {"name": v.against_voucher_no}):
|
||||
frappe.throw(_("Row {0}: {1} is not a valid {2}").format(v.idx, v.against_voucher_no,
|
||||
v.against_voucher_type))
|
||||
|
||||
if v.payment_amount:
|
||||
d1 = jv.append("entries")
|
||||
d1.account = self.party_account
|
||||
d1.balance = get_balance_on(self.party_account)
|
||||
d1.set("debit" if self.received_or_paid=="Paid" else "credit", flt(v.payment_amount))
|
||||
d1.set(invoice_voucher_type.get(v.against_voucher_type), v.against_voucher_no)
|
||||
d1.set('is_advance', 'Yes' if v.against_voucher_type in ['Sales Order', 'Purchase Order'] else 'No')
|
||||
total_payment_amount = flt(total_payment_amount) + flt(d1.debit) - flt(d1.credit)
|
||||
|
||||
d2 = jv.append("entries")
|
||||
d2.account = self.payment_account
|
||||
d2.set('debit' if total_payment_amount < 0 else 'credit', abs(total_payment_amount))
|
||||
if self.payment_account:
|
||||
d2.balance = get_balance_on(self.payment_account)
|
||||
|
||||
return jv.as_dict()
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_party_account(party_type, party_name):
|
||||
return frappe.db.get_value("Account", {"master_type": party_type, "master_name": party_name})
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_outstanding_vouchers(args):
|
||||
from erpnext.accounts.utils import get_outstanding_invoices
|
||||
|
||||
if not frappe.has_permission("Payment Tool"):
|
||||
frappe.throw(_("No permission to use Payment Tool"), frappe.PermissionError)
|
||||
|
||||
args = json.loads(args)
|
||||
|
||||
if args.get("party_type") == "Customer" and args.get("received_or_paid") == "Received":
|
||||
amount_query = "ifnull(debit, 0) - ifnull(credit, 0)"
|
||||
elif args.get("party_type") == "Supplier" and args.get("received_or_paid") == "Paid":
|
||||
amount_query = "ifnull(credit, 0) - ifnull(debit, 0)"
|
||||
else:
|
||||
frappe.throw(_("Please enter the Against Vouchers manually"))
|
||||
|
||||
# Get all outstanding sales /purchase invoices
|
||||
outstanding_invoices = get_outstanding_invoices(amount_query, args.get("party_account"))
|
||||
|
||||
# Get all SO / PO which are not fully billed or aginst which full advance not paid
|
||||
orders_to_be_billed = get_orders_to_be_billed(args.get("party_type"), args.get("party_name"))
|
||||
return outstanding_invoices + orders_to_be_billed
|
||||
|
||||
def get_orders_to_be_billed(party_type, party_name):
|
||||
voucher_type = 'Sales Order' if party_type == "Customer" else 'Purchase Order'
|
||||
orders = frappe.db.sql("""
|
||||
select
|
||||
name as voucher_no,
|
||||
ifnull(grand_total, 0) as invoice_amount,
|
||||
(ifnull(grand_total, 0) - ifnull(advance_paid, 0)) as outstanding_amount,
|
||||
transaction_date as posting_date
|
||||
from
|
||||
`tab%s`
|
||||
where
|
||||
%s = %s
|
||||
and docstatus = 1
|
||||
and ifnull(status, "") != "Stopped"
|
||||
and ifnull(grand_total, 0) > ifnull(advance_paid, 0)
|
||||
and abs(100 - ifnull(per_billed, 0)) > 0.01
|
||||
""" % (voucher_type, 'customer' if party_type == "Customer" else 'supplier', '%s'),
|
||||
party_name, as_dict = True)
|
||||
|
||||
order_list = []
|
||||
for d in orders:
|
||||
d["voucher_type"] = voucher_type
|
||||
order_list.append(d)
|
||||
|
||||
return order_list
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_against_voucher_amount(against_voucher_type, against_voucher_no):
|
||||
if against_voucher_type in ["Sales Order", "Purchase Order"]:
|
||||
select_cond = "grand_total as total_amount, ifnull(grand_total, 0) - ifnull(advance_paid, 0) as outstanding_amount"
|
||||
elif against_voucher_type in ["Sales Invoice", "Purchase Invoice"]:
|
||||
select_cond = "grand_total as total_amount, outstanding_amount"
|
||||
elif against_voucher_type == "Journal Voucher":
|
||||
select_cond = "total_debit as total_amount"
|
||||
|
||||
details = frappe.db.sql("""select {0} from `tab{1}` where name = %s"""
|
||||
.format(select_cond, against_voucher_type), against_voucher_no, as_dict=1)
|
||||
|
||||
return details[0] if details else {}
|
||||
195
erpnext/accounts/doctype/payment_tool/test_payment_tool.py
Normal file
195
erpnext/accounts/doctype/payment_tool/test_payment_tool.py
Normal file
@@ -0,0 +1,195 @@
|
||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import unittest, frappe, json
|
||||
from frappe.utils import flt
|
||||
|
||||
test_dependencies = ["Item"]
|
||||
|
||||
class TestPaymentTool(unittest.TestCase):
|
||||
def test_make_journal_voucher(self):
|
||||
from erpnext.accounts.doctype.journal_voucher.test_journal_voucher \
|
||||
import test_records as jv_test_records
|
||||
from erpnext.selling.doctype.sales_order.test_sales_order \
|
||||
import test_records as so_test_records
|
||||
from erpnext.buying.doctype.purchase_order.test_purchase_order \
|
||||
import test_records as po_test_records
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice \
|
||||
import test_records as si_test_records
|
||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice \
|
||||
import test_records as pi_test_records
|
||||
|
||||
self.clear_table_entries()
|
||||
|
||||
base_customer_jv = self.create_against_jv(jv_test_records[2], { "account": "_Test Customer 3 - _TC"})
|
||||
base_supplier_jv = self.create_against_jv(jv_test_records[1], { "account": "_Test Supplier 1 - _TC"})
|
||||
|
||||
|
||||
#Create SO with partial outstanding
|
||||
so1 = self.create_voucher(so_test_records[0], {
|
||||
"customer": "_Test Customer 3"
|
||||
})
|
||||
|
||||
jv_against_so1 = self.create_against_jv(jv_test_records[0], {
|
||||
"account": "_Test Customer 3 - _TC",
|
||||
"against_sales_order": so1.name,
|
||||
"is_advance": "Yes"
|
||||
})
|
||||
|
||||
|
||||
#Create SO with no outstanding
|
||||
so2 = self.create_voucher(so_test_records[0], {
|
||||
"customer": "_Test Customer 3"
|
||||
})
|
||||
|
||||
jv_against_so2 = self.create_against_jv(jv_test_records[0], {
|
||||
"account": "_Test Customer 3 - _TC",
|
||||
"against_sales_order": so2.name,
|
||||
"credit": 1000,
|
||||
"is_advance": "Yes"
|
||||
})
|
||||
po = self.create_voucher(po_test_records[1], {
|
||||
"supplier": "_Test Supplier 1"
|
||||
})
|
||||
|
||||
#Create SI with partial outstanding
|
||||
si1 = self.create_voucher(si_test_records[0], {
|
||||
"customer": "_Test Customer 3",
|
||||
"debit_to": "_Test Customer 3 - _TC"
|
||||
})
|
||||
|
||||
jv_against_si1 = self.create_against_jv(jv_test_records[0], {
|
||||
"account": "_Test Customer 3 - _TC",
|
||||
"against_invoice": si1.name
|
||||
})
|
||||
#Create SI with no outstanding
|
||||
si2 = self.create_voucher(si_test_records[0], {
|
||||
"customer": "_Test Customer 3",
|
||||
"debit_to": "_Test Customer 3 - _TC"
|
||||
})
|
||||
|
||||
jv_against_si2 = self.create_against_jv(jv_test_records[0], {
|
||||
"account": "_Test Customer 3 - _TC",
|
||||
"against_invoice": si2.name,
|
||||
"credit": 561.80
|
||||
})
|
||||
|
||||
pi = self.create_voucher(pi_test_records[0], {
|
||||
"supplier": "_Test Supplier 1",
|
||||
"credit_to": "_Test Supplier 1 - _TC"
|
||||
})
|
||||
|
||||
#Create a dict containing properties and expected values
|
||||
expected_outstanding = {
|
||||
"Journal Voucher" : [base_customer_jv.name, 400.00],
|
||||
"Sales Invoice" : [si1.name, 161.80],
|
||||
"Purchase Invoice" : [pi.name, 1512.30],
|
||||
"Sales Order" : [so1.name, 600.00],
|
||||
"Purchase Order" : [po.name, 5000.00]
|
||||
}
|
||||
|
||||
args = {
|
||||
"company": "_Test Company",
|
||||
"party_type": "Customer",
|
||||
"received_or_paid": "Received",
|
||||
"customer": "_Test Customer",
|
||||
"party_account": "_Test Customer 3 - _TC",
|
||||
"payment_mode": "Cheque",
|
||||
"payment_account": "_Test Account Bank Account - _TC",
|
||||
"reference_no": "123456",
|
||||
"reference_date": "2013-02-14"
|
||||
}
|
||||
|
||||
self.make_voucher_for_party(args, expected_outstanding)
|
||||
|
||||
args.update({
|
||||
"party_type": "Supplier",
|
||||
"received_or_paid": "Paid",
|
||||
"supplier": "_Test Supplier 1",
|
||||
"party_account": "_Test Supplier 1 - _TC"
|
||||
})
|
||||
expected_outstanding["Journal Voucher"] = [base_supplier_jv.name, 400.00]
|
||||
self.make_voucher_for_party(args, expected_outstanding)
|
||||
|
||||
def create_voucher(self, test_record, args):
|
||||
doc = frappe.copy_doc(test_record)
|
||||
doc.update(args)
|
||||
doc.insert()
|
||||
doc.submit()
|
||||
return doc
|
||||
|
||||
def create_against_jv(self, test_record, args):
|
||||
jv = frappe.copy_doc(test_record)
|
||||
jv.get("entries")[0].update(args)
|
||||
if args.get("debit"):
|
||||
jv.get("entries")[1].credit = args["debit"]
|
||||
elif args.get("credit"):
|
||||
jv.get("entries")[1].debit = args["credit"]
|
||||
|
||||
jv.insert()
|
||||
jv.submit()
|
||||
return jv
|
||||
|
||||
def make_voucher_for_party(self, args, expected_outstanding):
|
||||
#Make Journal Voucher for Party
|
||||
payment_tool_doc = frappe.new_doc("Payment Tool")
|
||||
|
||||
for k, v in args.items():
|
||||
payment_tool_doc.set(k, v)
|
||||
|
||||
self.check_outstanding_vouchers(payment_tool_doc, args, expected_outstanding)
|
||||
|
||||
|
||||
def check_outstanding_vouchers(self, doc, args, expected_outstanding):
|
||||
from erpnext.accounts.doctype.payment_tool.payment_tool import get_outstanding_vouchers
|
||||
|
||||
outstanding_entries = get_outstanding_vouchers(json.dumps(args))
|
||||
|
||||
for d in outstanding_entries:
|
||||
self.assertEquals(flt(d.get("outstanding_amount"), 2), expected_outstanding.get(d.get("voucher_type"))[1])
|
||||
|
||||
self.check_jv_entries(doc, outstanding_entries, expected_outstanding)
|
||||
|
||||
def check_jv_entries(self, paytool, outstanding_entries, expected_outstanding):
|
||||
for e in outstanding_entries:
|
||||
d1 = paytool.append("payment_tool_details")
|
||||
d1.against_voucher_type = e.get("voucher_type")
|
||||
d1.against_voucher_no = e.get("voucher_no")
|
||||
d1.total_amount = e.get("invoice_amount")
|
||||
d1.outstanding_amount = e.get("outstanding_amount")
|
||||
d1.payment_amount = 100.00
|
||||
paytool.total_payment_amount = 300
|
||||
|
||||
new_jv = paytool.make_journal_voucher()
|
||||
|
||||
#Create a list of expected values as [party account, payment against, against_jv, against_invoice,
|
||||
#against_voucher, against_sales_order, against_purchase_order]
|
||||
expected_values = [
|
||||
[paytool.party_account, 100.00, expected_outstanding.get("Journal Voucher")[0], None, None, None, None],
|
||||
[paytool.party_account, 100.00, None, expected_outstanding.get("Sales Invoice")[0], None, None, None],
|
||||
[paytool.party_account, 100.00, None, None, expected_outstanding.get("Purchase Invoice")[0], None, None],
|
||||
[paytool.party_account, 100.00, None, None, None, expected_outstanding.get("Sales Order")[0], None],
|
||||
[paytool.party_account, 100.00, None, None, None, None, expected_outstanding.get("Purchase Order")[0]]
|
||||
]
|
||||
|
||||
for jv_entry in new_jv.get("entries"):
|
||||
if paytool.party_account == jv_entry.get("account"):
|
||||
row = [
|
||||
jv_entry.get("account"),
|
||||
jv_entry.get("debit" if paytool.party_type=="Supplier" else "credit"),
|
||||
jv_entry.get("against_jv"),
|
||||
jv_entry.get("against_invoice"),
|
||||
jv_entry.get("against_voucher"),
|
||||
jv_entry.get("against_sales_order"),
|
||||
jv_entry.get("against_purchase_order"),
|
||||
]
|
||||
self.assertTrue(row in expected_values)
|
||||
|
||||
self.assertEquals(new_jv.get("cheque_no"), paytool.reference_no)
|
||||
self.assertEquals(new_jv.get("cheque_date"), paytool.reference_date)
|
||||
|
||||
def clear_table_entries(self):
|
||||
frappe.db.sql("""delete from `tabGL Entry` where (account = "_Test Customer 3 - _TC" or account = "_Test Supplier 1 - _TC")""")
|
||||
frappe.db.sql("""delete from `tabSales Order` where customer_name = "_Test Customer 3" """)
|
||||
frappe.db.sql("""delete from `tabPurchase Order` where supplier_name = "_Test Supplier 1" """)
|
||||
@@ -0,0 +1,130 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"creation": "2014-08-11 14:27:54.463897",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "against_voucher_type",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Against Voucher Type",
|
||||
"no_copy": 0,
|
||||
"options": "DocType",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_width": "",
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"width": ""
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "against_voucher_no",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Against Voucher No",
|
||||
"no_copy": 0,
|
||||
"options": "against_voucher_type",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0,
|
||||
"precision": ""
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "total_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Total Amount",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "outstanding_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Outstanding Amount",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "payment_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Payment Amount",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2014-09-11 08:55:34.384017",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Tool Detail",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
from frappe.model.document import Document
|
||||
|
||||
class ContactControl(Document):
|
||||
pass
|
||||
class PaymentToolDetail(Document):
|
||||
pass
|
||||
@@ -171,6 +171,7 @@
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "letter_head",
|
||||
"fieldtype": "Link",
|
||||
"label": "Letter Head",
|
||||
@@ -192,6 +193,7 @@
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "select_print_heading",
|
||||
"fieldtype": "Link",
|
||||
"in_filter": 0,
|
||||
@@ -205,7 +207,7 @@
|
||||
],
|
||||
"icon": "icon-cog",
|
||||
"idx": 1,
|
||||
"modified": "2014-06-23 16:40:59.510132",
|
||||
"modified": "2014-09-09 05:35:31.969193",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "POS Setting",
|
||||
|
||||
10
erpnext/accounts/doctype/pos_setting/test_pos_setting.py
Normal file
10
erpnext/accounts/doctype/pos_setting/test_pos_setting.py
Normal file
@@ -0,0 +1,10 @@
|
||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors and Contributors
|
||||
# See license.txt
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
test_records = frappe.get_test_records('POS Setting')
|
||||
|
||||
class TestPOSSetting(unittest.TestCase):
|
||||
pass
|
||||
1
erpnext/accounts/doctype/pos_setting/test_records.json
Normal file
1
erpnext/accounts/doctype/pos_setting/test_records.json
Normal file
@@ -0,0 +1 @@
|
||||
[]
|
||||
@@ -232,7 +232,6 @@ cur_frm.fields_dict['entries'].grid.get_field('project_name').get_query = functi
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cur_frm.cscript.select_print_heading = function(doc,cdt,cdn){
|
||||
if(doc.select_print_heading){
|
||||
// print heading
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,11 +4,10 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
from frappe.utils import cint, cstr, flt, formatdate
|
||||
|
||||
from frappe.utils import cint, cstr, formatdate, flt
|
||||
from frappe import msgprint, _, throw
|
||||
from erpnext.setup.utils import get_company_currency
|
||||
|
||||
import frappe.defaults
|
||||
|
||||
from erpnext.controllers.buying_controller import BuyingController
|
||||
@@ -49,6 +48,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.check_conversion_rate()
|
||||
self.validate_credit_acc()
|
||||
self.clear_unallocated_advances("Purchase Invoice Advance", "advance_allocation_details")
|
||||
self.validate_advance_jv("advance_allocation_details", "purchase_order")
|
||||
self.check_for_acc_head_of_supplier()
|
||||
self.check_for_stopped_status()
|
||||
self.validate_with_previous_doc()
|
||||
@@ -80,7 +80,7 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
def get_advances(self):
|
||||
super(PurchaseInvoice, self).get_advances(self.credit_to,
|
||||
"Purchase Invoice Advance", "advance_allocation_details", "debit")
|
||||
"Purchase Invoice Advance", "advance_allocation_details", "debit", "purchase_order")
|
||||
|
||||
def check_active_purchase_items(self):
|
||||
for d in self.get('entries'):
|
||||
@@ -249,6 +249,8 @@ class PurchaseInvoice(BuyingController):
|
||||
reconcile_against_document(lst)
|
||||
|
||||
def on_submit(self):
|
||||
super(PurchaseInvoice, self).on_submit()
|
||||
|
||||
self.check_prev_docstatus()
|
||||
|
||||
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
|
||||
@@ -403,7 +405,7 @@ def get_expense_account(doctype, txt, searchfield, start, page_len, filters):
|
||||
# Hence the first condition is an "OR"
|
||||
return frappe.db.sql("""select tabAccount.name from `tabAccount`
|
||||
where (tabAccount.report_type = "Profit and Loss"
|
||||
or tabAccount.account_type = "Expense Account")
|
||||
or tabAccount.account_type in ("Expense Account", "Fixed Asset"))
|
||||
and tabAccount.group_or_ledger="Ledger"
|
||||
and tabAccount.docstatus!=2
|
||||
and ifnull(tabAccount.master_type, "")=""
|
||||
|
||||
@@ -231,4 +231,8 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
self.assertTrue(not frappe.db.sql("""select name from `tabJournal Voucher Detail`
|
||||
where against_voucher=%s""", pi.name))
|
||||
|
||||
def test_recurring_invoice(self):
|
||||
from erpnext.controllers.tests.test_recurring_document import test_recurring_document
|
||||
test_recurring_document(self, test_records)
|
||||
|
||||
test_records = frappe.get_test_records('Purchase Invoice')
|
||||
|
||||
@@ -399,7 +399,7 @@
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"modified": "2014-09-08 08:06:30.027289",
|
||||
"modified": "2014-09-09 05:35:35.712453",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice Item",
|
||||
|
||||
@@ -114,7 +114,7 @@ erpnext.POS = Class.extend({
|
||||
});
|
||||
|
||||
this.wrapper.find('input.discount-amount').on("change", function() {
|
||||
frappe.model.set_value(me.frm.doctype, me.frm.docname, "discount_amount", this.value);
|
||||
frappe.model.set_value(me.frm.doctype, me.frm.docname, "discount_amount", flt(this.value));
|
||||
});
|
||||
|
||||
this.call_function("remove-items", function() {me.remove_selected_items();});
|
||||
@@ -233,6 +233,11 @@ erpnext.POS = Class.extend({
|
||||
},
|
||||
make_item_list: function() {
|
||||
var me = this;
|
||||
if(!this.price_list) {
|
||||
msgprint(__("Price List not found or disabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
me.item_timeout = null;
|
||||
frappe.call({
|
||||
method: 'erpnext.accounts.doctype.sales_invoice.pos.get_items',
|
||||
@@ -487,7 +492,7 @@ erpnext.POS = Class.extend({
|
||||
});
|
||||
|
||||
me.refresh_delete_btn();
|
||||
if(me.frm.doc[this.party]) {
|
||||
if(me.frm.doc[this.party.toLowerCase()]) {
|
||||
this.barcode.$input.focus();
|
||||
} else {
|
||||
this.party_field.$input.focus();
|
||||
|
||||
@@ -225,8 +225,7 @@ $.extend(cur_frm.cscript, new erpnext.accounts.SalesInvoiceController({frm: cur_
|
||||
// Hide Fields
|
||||
// ------------
|
||||
cur_frm.cscript.hide_fields = function(doc) {
|
||||
par_flds = ['project_name', 'due_date', 'is_opening', 'source', 'total_advance', 'gross_profit',
|
||||
'gross_profit_percent', 'get_advances_received',
|
||||
par_flds = ['project_name', 'due_date', 'is_opening', 'source', 'total_advance', 'get_advances_received',
|
||||
'advance_adjustment_details', 'sales_partner', 'commission_rate',
|
||||
'total_commission', 'advances', 'from_date', 'to_date'];
|
||||
|
||||
@@ -399,37 +398,6 @@ cur_frm.cscript.on_submit = function(doc, cdt, cdn) {
|
||||
})
|
||||
}
|
||||
|
||||
cur_frm.cscript.is_recurring = function(doc, dt, dn) {
|
||||
// set default values for recurring invoices
|
||||
if(doc.is_recurring) {
|
||||
var owner_email = doc.owner=="Administrator"
|
||||
? frappe.user_info("Administrator").email
|
||||
: doc.owner;
|
||||
|
||||
doc.notification_email_address = $.map([cstr(owner_email),
|
||||
cstr(doc.contact_email)], function(v) { return v || null; }).join(", ");
|
||||
doc.repeat_on_day_of_month = frappe.datetime.str_to_obj(doc.posting_date).getDate();
|
||||
}
|
||||
|
||||
refresh_many(["notification_email_address", "repeat_on_day_of_month"]);
|
||||
}
|
||||
|
||||
cur_frm.cscript.from_date = function(doc, dt, dn) {
|
||||
// set to_date
|
||||
if(doc.from_date) {
|
||||
var recurring_type_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6,
|
||||
'Yearly': 12};
|
||||
|
||||
var months = recurring_type_map[doc.recurring_type];
|
||||
if(months) {
|
||||
var to_date = frappe.datetime.add_months(doc.from_date,
|
||||
months);
|
||||
doc.to_date = frappe.datetime.add_days(to_date, -1);
|
||||
refresh_field('to_date');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.cscript.send_sms = function() {
|
||||
frappe.require("assets/erpnext/js/sms_manager.js");
|
||||
var sms_man = new SMSManager(cur_frm.doc);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"allow_attach": 1,
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-05-24 19:29:05",
|
||||
@@ -170,28 +169,25 @@
|
||||
"search_index": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "",
|
||||
"description": "Start date of current invoice's period",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "From",
|
||||
"no_copy": 1,
|
||||
"fieldname": "shipping_address_name",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
"in_filter": 1,
|
||||
"label": "Shipping Address Name",
|
||||
"options": "Address",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0
|
||||
"precision": "",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "",
|
||||
"description": "End date of current invoice's period",
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "To",
|
||||
"no_copy": 1,
|
||||
"fieldname": "shipping_address",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"label": "Shipping Address",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "currency_section",
|
||||
@@ -426,6 +422,15 @@
|
||||
"fieldtype": "Section Break",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "other_charges_total_export",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Taxes and Charges",
|
||||
"options": "currency",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "other_charges_total",
|
||||
"fieldtype": "Currency",
|
||||
@@ -442,23 +447,24 @@
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "other_charges_total_export",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Taxes and Charges",
|
||||
"options": "currency",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "discount_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Discount Amount",
|
||||
"options": "Company:company:default_currency",
|
||||
"options": "currency",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "base_discount_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Discount Amount (Company Currency)",
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "totals",
|
||||
"fieldtype": "Section Break",
|
||||
@@ -572,27 +578,6 @@
|
||||
"print_hide": 0,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "gross_profit",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Gross Profit",
|
||||
"oldfieldname": "gross_profit",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "gross_profit_percent",
|
||||
"fieldtype": "Float",
|
||||
"label": "Gross Profit (%)",
|
||||
"oldfieldname": "gross_profit_percent",
|
||||
"oldfieldtype": "Currency",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "advances",
|
||||
"fieldtype": "Section Break",
|
||||
@@ -1109,6 +1094,30 @@
|
||||
"print_hide": 1,
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "Start date of current invoice's period",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "From Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "End date of current invoice's period",
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "To Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
@@ -1121,17 +1130,6 @@
|
||||
"print_hide": 1,
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The date on which next invoice will be generated. It is generated on submit.\n",
|
||||
"fieldname": "next_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Next Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
@@ -1153,6 +1151,17 @@
|
||||
"read_only": 0,
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The date on which next invoice will be generated. It is generated on submit.\n",
|
||||
"fieldname": "next_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Next Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The unique id for tracking all recurring invoices.\u00a0It is generated on submit.",
|
||||
@@ -1193,7 +1202,7 @@
|
||||
"icon": "icon-file-text",
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"modified": "2014-08-28 11:21:00.726344",
|
||||
"modified": "2015-01-12 17:34:36.353241",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice",
|
||||
|
||||
@@ -4,18 +4,12 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import frappe.defaults
|
||||
|
||||
from frappe.utils import add_days, cint, cstr, date_diff, flt, getdate, nowdate, \
|
||||
get_first_day, get_last_day
|
||||
from frappe.model.naming import make_autoname
|
||||
from frappe.utils import cint, cstr, flt
|
||||
from frappe import _, msgprint, throw
|
||||
|
||||
from erpnext.accounts.party import get_party_account, get_due_date
|
||||
from erpnext.controllers.stock_controller import update_gl_entries_after
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
|
||||
from erpnext.controllers.recurring_document import *
|
||||
|
||||
from erpnext.controllers.selling_controller import SellingController
|
||||
|
||||
form_grid_templates = {
|
||||
@@ -56,6 +50,7 @@ class SalesInvoice(SellingController):
|
||||
self.validate_debit_acc()
|
||||
self.validate_fixed_asset_account()
|
||||
self.clear_unallocated_advances("Sales Invoice Advance", "advance_adjustment_details")
|
||||
self.validate_advance_jv("advance_adjustment_details", "sales_order")
|
||||
self.add_remarks()
|
||||
|
||||
if cint(self.is_pos):
|
||||
@@ -71,15 +66,18 @@ class SalesInvoice(SellingController):
|
||||
self.is_opening = 'No'
|
||||
|
||||
self.set_aging_date()
|
||||
|
||||
frappe.get_doc("Account", self.debit_to).validate_due_date(self.posting_date, self.due_date)
|
||||
|
||||
self.set_against_income_account()
|
||||
self.validate_c_form()
|
||||
self.validate_time_logs_are_submitted()
|
||||
validate_recurring_document(self)
|
||||
self.validate_multiple_billing("Delivery Note", "dn_detail", "amount",
|
||||
"delivery_note_details")
|
||||
|
||||
def on_submit(self):
|
||||
super(SalesInvoice, self).on_submit()
|
||||
|
||||
if cint(self.update_stock) == 1:
|
||||
self.update_stock_ledger()
|
||||
else:
|
||||
@@ -101,9 +99,7 @@ class SalesInvoice(SellingController):
|
||||
if not cint(self.is_pos) == 1:
|
||||
self.update_against_document_in_jv()
|
||||
|
||||
self.update_c_form()
|
||||
self.update_time_log_batch(self.name)
|
||||
convert_to_recurring(self, "RECINV.#####", self.posting_date)
|
||||
|
||||
def before_cancel(self):
|
||||
self.update_time_log_batch(None)
|
||||
@@ -120,6 +116,7 @@ class SalesInvoice(SellingController):
|
||||
self.update_status_updater_args()
|
||||
self.update_prevdoc_status()
|
||||
self.update_billing_status_for_zero_amount_refdoc("Sales Order")
|
||||
self.validate_c_form_on_cancel()
|
||||
|
||||
self.make_gl_entries_on_cancel()
|
||||
|
||||
@@ -143,10 +140,6 @@ class SalesInvoice(SellingController):
|
||||
'overflow_type': 'delivery'
|
||||
})
|
||||
|
||||
def on_update_after_submit(self):
|
||||
validate_recurring_document(self)
|
||||
convert_to_recurring(self, "RECINV.#####", self.posting_date)
|
||||
|
||||
def get_portal_page(self):
|
||||
return "invoice" if self.docstatus==1 else None
|
||||
|
||||
@@ -216,7 +209,7 @@ class SalesInvoice(SellingController):
|
||||
|
||||
def get_advances(self):
|
||||
super(SalesInvoice, self).get_advances(self.debit_to,
|
||||
"Sales Invoice Advance", "advance_adjustment_details", "credit")
|
||||
"Sales Invoice Advance", "advance_adjustment_details", "credit", "sales_order")
|
||||
|
||||
def get_company_abbr(self):
|
||||
return frappe.db.sql("select abbr from tabCompany where name=%s", self.company)[0][0]
|
||||
@@ -376,6 +369,12 @@ class SalesInvoice(SellingController):
|
||||
|
||||
frappe.db.set(self, 'c_form_no', '')
|
||||
|
||||
def validate_c_form_on_cancel(self):
|
||||
""" Display message if C-Form no exists on cancellation of Sales Invoice"""
|
||||
if self.c_form_applicable == 'Yes' and self.c_form_no:
|
||||
msgprint(_("Please remove this Invoice {0} from C-Form {1}")
|
||||
.format(self.name, self.c_form_no), raise_exception = 1)
|
||||
|
||||
def update_current_stock(self):
|
||||
for d in self.get('entries'):
|
||||
if d.item_code and d.warehouse:
|
||||
@@ -474,9 +473,8 @@ class SalesInvoice(SellingController):
|
||||
|
||||
if repost_future_gle and cint(self.update_stock) \
|
||||
and cint(frappe.defaults.get_global_default("auto_accounting_for_stock")):
|
||||
items, warehouse_account = self.get_items_and_warehouse_accounts()
|
||||
update_gl_entries_after(self.posting_date, self.posting_time,
|
||||
warehouse_account, items)
|
||||
items, warehouses = self.get_items_and_warehouses()
|
||||
update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items)
|
||||
|
||||
def get_gl_entries(self, warehouse_account=None):
|
||||
from erpnext.accounts.general_ledger import merge_similar_entries
|
||||
@@ -584,14 +582,6 @@ class SalesInvoice(SellingController):
|
||||
})
|
||||
)
|
||||
|
||||
def update_c_form(self):
|
||||
"""Update amended id in C-form"""
|
||||
if self.c_form_no and self.amended_from:
|
||||
frappe.db.sql("""update `tabC-Form Invoice Detail` set invoice_no = %s,
|
||||
invoice_date = %s, territory = %s, net_total = %s,
|
||||
grand_total = %s where invoice_no = %s and parent = %s""",
|
||||
(self.name, self.amended_from, self.c_form_no))
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_bank_cash_account(mode_of_payment):
|
||||
val = frappe.db.get_value("Mode of Payment", mode_of_payment, "default_account")
|
||||
|
||||
@@ -439,7 +439,7 @@
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"modified": "2014-09-08 08:06:30.382092",
|
||||
"modified": "2014-09-09 05:35:36.019576",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice Item",
|
||||
|
||||
@@ -1,155 +1,156 @@
|
||||
{
|
||||
"autoname": "INVTD.######",
|
||||
"creation": "2013-04-24 11:39:32",
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"autoname": "INVTD.######",
|
||||
"creation": "2013-04-24 11:39:32",
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "charge_type",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"label": "Type",
|
||||
"oldfieldname": "charge_type",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "\nActual\nOn Net Total\nOn Previous Row Amount\nOn Previous Row Total",
|
||||
"permlevel": 0,
|
||||
"fieldname": "charge_type",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"label": "Type",
|
||||
"oldfieldname": "charge_type",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "\nActual\nOn Net Total\nOn Previous Row Amount\nOn Previous Row Total",
|
||||
"permlevel": 0,
|
||||
"reqd": 1
|
||||
},
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:[\"On Previous Row Amount\", \"On Previous Row Total\"].indexOf(doc.charge_type)!==-1",
|
||||
"fieldname": "row_id",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"label": "Reference Row #",
|
||||
"oldfieldname": "row_id",
|
||||
"oldfieldtype": "Data",
|
||||
"depends_on": "eval:[\"On Previous Row Amount\", \"On Previous Row Total\"].indexOf(doc.charge_type)!==-1",
|
||||
"fieldname": "row_id",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"label": "Reference Row #",
|
||||
"oldfieldname": "row_id",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Small Text",
|
||||
"in_list_view": 1,
|
||||
"label": "Description",
|
||||
"oldfieldname": "description",
|
||||
"oldfieldtype": "Small Text",
|
||||
"permlevel": 0,
|
||||
"print_width": "300px",
|
||||
"reqd": 1,
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Small Text",
|
||||
"in_list_view": 1,
|
||||
"label": "Description",
|
||||
"oldfieldname": "description",
|
||||
"oldfieldtype": "Small Text",
|
||||
"permlevel": 0,
|
||||
"print_width": "300px",
|
||||
"reqd": 1,
|
||||
"width": "300px"
|
||||
},
|
||||
},
|
||||
{
|
||||
"fieldname": "col_break_1",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0,
|
||||
"fieldname": "col_break_1",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0,
|
||||
"width": "50%"
|
||||
},
|
||||
},
|
||||
{
|
||||
"fieldname": "account_head",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 0,
|
||||
"label": "Account Head",
|
||||
"oldfieldname": "account_head",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Account",
|
||||
"permlevel": 0,
|
||||
"reqd": 1,
|
||||
"fieldname": "account_head",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 0,
|
||||
"label": "Account Head",
|
||||
"oldfieldname": "account_head",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Account",
|
||||
"permlevel": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
},
|
||||
{
|
||||
"default": ":Company",
|
||||
"fieldname": "cost_center",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 0,
|
||||
"label": "Cost Center",
|
||||
"oldfieldname": "cost_center_other_charges",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Cost Center",
|
||||
"default": ":Company",
|
||||
"fieldname": "cost_center",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 0,
|
||||
"label": "Cost Center",
|
||||
"oldfieldname": "cost_center_other_charges",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Cost Center",
|
||||
"permlevel": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"fieldname": "rate",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Rate",
|
||||
"oldfieldname": "rate",
|
||||
"oldfieldtype": "Currency",
|
||||
"permlevel": 0,
|
||||
"fieldname": "rate",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Rate",
|
||||
"oldfieldname": "rate",
|
||||
"oldfieldtype": "Currency",
|
||||
"permlevel": 0,
|
||||
"reqd": 1
|
||||
},
|
||||
},
|
||||
{
|
||||
"fieldname": "tax_amount",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Amount",
|
||||
"oldfieldname": "tax_amount",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0,
|
||||
"read_only": 1,
|
||||
"fieldname": "tax_amount",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Amount",
|
||||
"oldfieldname": "tax_amount",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0,
|
||||
"read_only": 1,
|
||||
"reqd": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"fieldname": "total",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total",
|
||||
"oldfieldname": "total",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0,
|
||||
"fieldname": "total",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total",
|
||||
"oldfieldname": "total",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0,
|
||||
"read_only": 1
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"description": "If checked, the tax amount will be considered as already included in the Print Rate / Print Amount",
|
||||
"fieldname": "included_in_print_rate",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is this Tax included in Basic Rate?",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_width": "150px",
|
||||
"report_hide": 1,
|
||||
"allow_on_submit": 0,
|
||||
"description": "If checked, the tax amount will be considered as already included in the Print Rate / Print Amount",
|
||||
"fieldname": "included_in_print_rate",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is this Tax included in Basic Rate?",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_width": "150px",
|
||||
"report_hide": 1,
|
||||
"width": "150px"
|
||||
},
|
||||
},
|
||||
{
|
||||
"fieldname": "tax_amount_after_discount_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 1,
|
||||
"label": "Tax Amount After Discount Amount",
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0,
|
||||
"depends_on": "eval:parent.discount_amount",
|
||||
"fieldname": "tax_amount_after_discount_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"label": "Tax Amount After Discount Amount",
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0,
|
||||
"read_only": 1
|
||||
},
|
||||
},
|
||||
{
|
||||
"fieldname": "item_wise_tax_detail",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"label": "Item Wise Tax Detail",
|
||||
"oldfieldname": "item_wise_tax_detail",
|
||||
"oldfieldtype": "Small Text",
|
||||
"permlevel": 0,
|
||||
"fieldname": "item_wise_tax_detail",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"label": "Item Wise Tax Detail",
|
||||
"oldfieldname": "item_wise_tax_detail",
|
||||
"oldfieldtype": "Small Text",
|
||||
"permlevel": 0,
|
||||
"read_only": 1
|
||||
},
|
||||
},
|
||||
{
|
||||
"fieldname": "parenttype",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"in_filter": 1,
|
||||
"label": "Parenttype",
|
||||
"oldfieldname": "parenttype",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"fieldname": "parenttype",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"in_filter": 1,
|
||||
"label": "Parenttype",
|
||||
"oldfieldname": "parenttype",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"search_index": 1
|
||||
}
|
||||
],
|
||||
"hide_heading": 1,
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"modified": "2014-05-30 03:43:39.740638",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Taxes and Charges",
|
||||
"owner": "Administrator",
|
||||
],
|
||||
"hide_heading": 1,
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"modified": "2014-12-10 12:26:41.222471",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Taxes and Charges",
|
||||
"owner": "Administrator",
|
||||
"permissions": []
|
||||
}
|
||||
}
|
||||
@@ -46,17 +46,17 @@ cur_frm.pformat.other_charges= function(doc){
|
||||
var new_val = flt(val)/flt(doc.conversion_rate);
|
||||
return new_val;
|
||||
}
|
||||
|
||||
|
||||
function print_hide(fieldname) {
|
||||
var doc_field = frappe.meta.get_docfield(doc.doctype, fieldname, doc.name);
|
||||
return doc_field.print_hide;
|
||||
}
|
||||
|
||||
|
||||
out ='';
|
||||
if (!doc.print_without_amount) {
|
||||
var cl = doc.other_charges || [];
|
||||
|
||||
// outer table
|
||||
// outer table
|
||||
var out='<div><table class="noborder" style="width:100%"><tr><td style="width: 60%"></td><td>';
|
||||
|
||||
// main table
|
||||
@@ -77,12 +77,12 @@ cur_frm.pformat.other_charges= function(doc){
|
||||
|
||||
// Discount Amount
|
||||
if(!print_hide('discount_amount') && doc.discount_amount)
|
||||
out += make_row('Discount Amount', convert_rate(doc.discount_amount), 0);
|
||||
out += make_row('Discount Amount', doc.discount_amount, 0);
|
||||
|
||||
// grand total
|
||||
if(!print_hide('grand_total_export'))
|
||||
out += make_row('Grand Total', doc.grand_total_export, 1);
|
||||
|
||||
|
||||
if(!print_hide('rounded_total_export'))
|
||||
out += make_row('Rounded Total', doc.rounded_total_export, 1);
|
||||
|
||||
@@ -92,7 +92,7 @@ cur_frm.pformat.other_charges= function(doc){
|
||||
out += '<table><tr><td style="width:25%;"><b>In Words</b></td>';
|
||||
out += '<td style="width:50%;">' + doc.in_words_export + '</td></tr>';
|
||||
}
|
||||
out += '</table></td></tr></table></div>';
|
||||
out += '</table></td></tr></table></div>';
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@@ -139,14 +139,14 @@ cur_frm.fields_dict['other_charges'].grid.get_field("account_head").get_query =
|
||||
"account_type": ["Tax", "Chargeable", "Income Account"],
|
||||
"company": doc.company
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.fields_dict['other_charges'].grid.get_field("cost_center").get_query = function(doc) {
|
||||
return{
|
||||
'company': doc.company,
|
||||
'group_or_ledger': "Ledger"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.cscript.rate = function(doc, cdt, cdn) {
|
||||
|
||||
@@ -97,8 +97,7 @@ def validate_account_for_auto_accounting_for_stock(gl_map):
|
||||
|
||||
for entry in gl_map:
|
||||
if entry.account in aii_accounts:
|
||||
frappe.throw(_("Account: {0} can only be updated via \
|
||||
Stock Transactions").format(entry.account), StockAccountInvalidTransaction)
|
||||
frappe.throw(_("Account: {0} can only be updated via Stock Transactions").format(entry.account), StockAccountInvalidTransaction)
|
||||
|
||||
|
||||
def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
|
||||
|
||||
@@ -45,7 +45,7 @@ pscript['onload_Accounts Browser'] = function(wrapper){
|
||||
'icon-plus');
|
||||
}
|
||||
|
||||
wrapper.appframe.set_title_right('Refresh', function() {
|
||||
wrapper.appframe.set_title_right(__('Refresh'), function() {
|
||||
wrapper.$company_select.change();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
frappe.pages['pos'].onload = function(wrapper) {
|
||||
frappe.ui.make_app_page({
|
||||
parent: wrapper,
|
||||
title: 'Start POS',
|
||||
title: __('Start POS'),
|
||||
single_column: true
|
||||
});
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@ def create_party_account(party, party_type, company):
|
||||
|
||||
company_details = frappe.db.get_value("Company", company,
|
||||
["abbr", "receivables_group", "payables_group"], as_dict=True)
|
||||
if not frappe.db.exists("Account", (party + " - " + company_details.abbr)):
|
||||
if not frappe.db.exists("Account", (party.strip() + " - " + company_details.abbr)):
|
||||
parent_account = company_details.receivables_group \
|
||||
if party_type=="Customer" else company_details.payables_group
|
||||
if not parent_account:
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
"doc_type": "Journal Voucher",
|
||||
"docstatus": 0,
|
||||
"doctype": "Print Format",
|
||||
"html": "<div style=\"position: relative\">\n\n\t{%- from \"templates/print_formats/standard_macros.html\" import add_header -%}\n<div class=\"page-break\">\n {%- if not doc.get(\"print_heading\") and not doc.get(\"select_print_heading\") \n and doc.set(\"select_print_heading\", _(\"Payment Advice\")) -%}{%- endif -%}\n {{ add_header(0, 1, doc, letter_head, no_letterhead) }}\n\n{%- for label, value in (\n (_(\"Voucher Date\"), frappe.utils.formatdate(doc.voucher_date)),\n (_(\"Reference / Cheque No.\"), doc.cheque_no),\n (_(\"Reference / Cheque Date\"), frappe.utils.formatdate(doc.cheque_date))\n ) -%}\n <div class=\"row\">\n <div class=\"col-sm-4\"><label class=\"text-right\">{{ label }}</label></div>\n <div class=\"col-sm-8\">{{ value }}</div>\n </div>\n{%- endfor -%}\n\t<hr>\n\t<p>{{ _(\"This amount is in full / part settlement of the listed bills\") }}:</p>\n{%- for label, value in (\n (_(\"Amount\"), \"<strong>\" + (doc.total_amount or \"\") + \"</strong><br>\" + (doc.total_amount_in_words or \"\") + \"<br>\"),\n (_(\"References\"), doc.remark)\n ) -%}\n <div class=\"row\">\n <div class=\"col-sm-4\"><label class=\"text-right\">{{ label }}</label></div>\n <div class=\"col-sm-8\">{{ value }}</div>\n </div>\n {%- endfor -%}\n <hr>\n\t<div style=\"position: absolute; top: 14cm; left: 0cm;\">\n\t\tPrepared By</div>\n\t<div style=\"position: absolute; top: 14cm; left: 5.5cm;\">\n\t\tAuthorised Signatory</div>\n\t<div style=\"position: absolute; top: 14cm; left: 11cm;\">\n\t\tReceived Payment as Above</div>\n\t<div style=\"position: absolute; top: 16.4cm; left: 5.9cm;\">\n\t\t<strong>_____________</strong></div>\n\t<div style=\"position: absolute; top: 16.7cm; left: 6cm;\">\n\t\t<strong>A/C Payee</strong></div>\n\t<div style=\"position: absolute; top: 16.7cm; left: 5.9cm;\">\n\t\t<strong>_____________</strong></div>\n\t<div style=\"position: absolute; top: 16.9cm; left: 12cm;\">\n\t\t{{ frappe.utils.formatdate(doc.cheque_date) }}</div>\n\t<div style=\"position: absolute; top: 17.9cm; left: 1cm;\">\n\t\t{{ doc.pay_to_recd_from }}</div>\n\t<div style=\"position: absolute; top: 18.6cm; left: 1cm; width: 7cm;\">\n\t\t{{ doc.total_amount_in_words }}</div>\n\t<div style=\"position: absolute; top: 19.7cm; left: 12cm;\">\n\t\t{{ doc.total_amount }}</div>\n</div>",
|
||||
"html": "<div style=\"position: relative\">\n\n\t{%- from \"templates/print_formats/standard_macros.html\" import add_header -%}\n<div class=\"page-break\">\n {%- if not doc.get(\"print_heading\") and not doc.get(\"select_print_heading\") \n and doc.set(\"select_print_heading\", _(\"Payment Advice\")) -%}{%- endif -%}\n {{ add_header(0, 1, doc, letter_head, no_letterhead) }}\n\n{%- for label, value in (\n (_(\"Voucher Date\"), frappe.utils.formatdate(doc.voucher_date)),\n (_(\"Reference / Cheque No.\"), doc.cheque_no),\n (_(\"Reference / Cheque Date\"), frappe.utils.formatdate(doc.cheque_date))\n ) -%}\n <div class=\"row\">\n <div class=\"col-xs-4\"><label class=\"text-right\">{{ label }}</label></div>\n <div class=\"col-xs-8\">{{ value }}</div>\n </div>\n{%- endfor -%}\n\t<hr>\n\t<p>{{ _(\"This amount is in full / part settlement of the listed bills\") }}:</p>\n{%- for label, value in (\n (_(\"Amount\"), \"<strong>\" + (doc.total_amount or \"\") + \"</strong><br>\" + (doc.total_amount_in_words or \"\") + \"<br>\"),\n (_(\"References\"), doc.remark)\n ) -%}\n <div class=\"row\">\n <div class=\"col-xs-4\"><label class=\"text-right\">{{ label }}</label></div>\n <div class=\"col-xs-8\">{{ value }}</div>\n </div>\n {%- endfor -%}\n <hr>\n\t<div style=\"position: absolute; top: 14cm; left: 0cm;\">\n\t\tPrepared By</div>\n\t<div style=\"position: absolute; top: 14cm; left: 5.5cm;\">\n\t\tAuthorised Signatory</div>\n\t<div style=\"position: absolute; top: 14cm; left: 11cm;\">\n\t\tReceived Payment as Above</div>\n\t<div style=\"position: absolute; top: 16.4cm; left: 5.9cm;\">\n\t\t<strong>_____________</strong></div>\n\t<div style=\"position: absolute; top: 16.7cm; left: 6cm;\">\n\t\t<strong>A/C Payee</strong></div>\n\t<div style=\"position: absolute; top: 16.7cm; left: 5.9cm;\">\n\t\t<strong>_____________</strong></div>\n\t<div style=\"position: absolute; top: 16.9cm; left: 12cm;\">\n\t\t{{ frappe.utils.formatdate(doc.cheque_date) }}</div>\n\t<div style=\"position: absolute; top: 17.9cm; left: 1cm;\">\n\t\t{{ doc.pay_to_recd_from }}</div>\n\t<div style=\"position: absolute; top: 18.6cm; left: 1cm; width: 7cm;\">\n\t\t{{ doc.total_amount_in_words }}</div>\n\t<div style=\"position: absolute; top: 19.7cm; left: 12cm;\">\n\t\t{{ doc.total_amount }}</div>\n</div>",
|
||||
"idx": 1,
|
||||
"modified": "2014-09-09 03:27:13.708596",
|
||||
"modified": "2015-01-12 11:03:17.032512",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Cheque Printing Format",
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
"doc_type": "Journal Voucher",
|
||||
"docstatus": 0,
|
||||
"doctype": "Print Format",
|
||||
"html": "{%- from \"templates/print_formats/standard_macros.html\" import add_header -%}\n\n<div class=\"page-break\">\n {%- if not doc.get(\"print_heading\") and not doc.get(\"select_print_heading\") \n and doc.set(\"select_print_heading\", _(\"Credit Note\")) -%}{%- endif -%}\n {{ add_header(0, 1, doc, letter_head, no_letterhead) }}\n\n {%- for label, value in (\n (_(\"Credit To\"), doc.pay_to_recd_from),\n (_(\"Date\"), frappe.utils.formatdate(doc.voucher_date)),\n (_(\"Amount\"), \"<strong>\" + doc.total_amount + \"</strong><br>\" + (doc.total_amount_in_words or \"\") + \"<br>\"),\n (_(\"Remarks\"), doc.remark)\n ) -%}\n\n <div class=\"row\">\n <div class=\"col-sm-3\"><label class=\"text-right\">{{ label }}</label></div>\n <div class=\"col-sm-9\">{{ value }}</div>\n </div>\n\n {%- endfor -%}\n\n <hr>\n <br>\n <p class=\"strong\">\n {{ _(\"For\") }} {{ doc.company }},<br>\n <br>\n <br>\n <br>\n {{ _(\"Authorized Signatory\") }}\n </p>\n</div>\n\n\n",
|
||||
"html": "{%- from \"templates/print_formats/standard_macros.html\" import add_header -%}\n\n<div class=\"page-break\">\n {%- if not doc.get(\"print_heading\") and not doc.get(\"select_print_heading\") \n and doc.set(\"select_print_heading\", _(\"Credit Note\")) -%}{%- endif -%}\n {{ add_header(0, 1, doc, letter_head, no_letterhead) }}\n\n {%- for label, value in (\n (_(\"Credit To\"), doc.pay_to_recd_from),\n (_(\"Date\"), frappe.utils.formatdate(doc.voucher_date)),\n (_(\"Amount\"), \"<strong>\" + frappe.utils.cstr(doc.total_amount) + \"</strong><br>\" + (doc.total_amount_in_words or \"\") + \"<br>\"),\n (_(\"Remarks\"), doc.remark)\n ) -%}\n\n <div class=\"row\">\n <div class=\"col-xs-3\"><label class=\"text-right\">{{ label }}</label></div>\n <div class=\"col-xs-9\">{{ value }}</div>\n </div>\n\n {%- endfor -%}\n\n <hr>\n <br>\n <p class=\"strong\">\n {{ _(\"For\") }} {{ doc.company }},<br>\n <br>\n <br>\n <br>\n {{ _(\"Authorized Signatory\") }}\n </p>\n</div>\n\n\n",
|
||||
"idx": 2,
|
||||
"modified": "2014-08-29 13:20:15.789533",
|
||||
"modified": "2015-01-12 11:02:25.716825",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Credit Note",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"creation": "2012-05-01 12:46:31",
|
||||
"doc_type": "Journal Voucher",
|
||||
"docstatus": 0,
|
||||
"doctype": "Print Format",
|
||||
"html": "{%- from \"templates/print_formats/standard_macros.html\" import add_header -%}\n<div class=\"page-break\">\n {%- if not doc.get(\"print_heading\") and not doc.get(\"select_print_heading\") \n and doc.set(\"select_print_heading\", _(\"Payment Receipt Note\")) -%}{%- endif -%}\n {{ add_header(0, 1, doc, letter_head, no_letterhead) }}\n\n {%- for label, value in (\n (_(\"Received On\"), frappe.utils.formatdate(doc.voucher_date)),\n (_(\"Received From\"), doc.pay_to_recd_from),\n (_(\"Amount\"), \"<strong>\" + doc.total_amount + \"</strong><br>\" + (doc.total_amount_in_words or \"\") + \"<br>\"),\n (_(\"Remarks\"), doc.remark)\n ) -%}\n <div class=\"row\">\n <div class=\"col-sm-3\"><label class=\"text-right\">{{ label }}</label></div>\n <div class=\"col-sm-9\">{{ value }}</div>\n </div>\n\n {%- endfor -%}\n\n <hr>\n <br>\n <p class=\"strong\">\n {{ _(\"For\") }} {{ doc.company }},<br>\n <br>\n <br>\n <br>\n {{ _(\"Authorized Signatory\") }}\n </p>\n</div>\n\n",
|
||||
"idx": 1,
|
||||
"modified": "2014-08-29 15:55:34.248384",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Receipt Voucher",
|
||||
"owner": "Administrator",
|
||||
"print_format_type": "Server",
|
||||
"creation": "2012-05-01 12:46:31",
|
||||
"doc_type": "Journal Voucher",
|
||||
"docstatus": 0,
|
||||
"doctype": "Print Format",
|
||||
"html": "{%- from \"templates/print_formats/standard_macros.html\" import add_header -%}\n<div class=\"page-break\">\n {%- if not doc.get(\"print_heading\") and not doc.get(\"select_print_heading\") \n and doc.set(\"select_print_heading\", _(\"Payment Receipt Note\")) -%}{%- endif -%}\n {{ add_header(0, 1, doc, letter_head, no_letterhead) }}\n\n {%- for label, value in (\n (_(\"Received On\"), frappe.utils.formatdate(doc.voucher_date)),\n (_(\"Received From\"), doc.pay_to_recd_from),\n (_(\"Amount\"), \"<strong>\" + frappe.utils.cstr(doc.total_amount or 0) + \"</strong><br>\" + (doc.total_amount_in_words or \"\") + \"<br>\"),\n (_(\"Remarks\"), doc.remark)\n ) -%}\n <div class=\"row\">\n <div class=\"col-xs-3\"><label class=\"text-right\">{{ label }}</label></div>\n <div class=\"col-xs-9\">{{ value }}</div>\n </div>\n\n {%- endfor -%}\n\n <hr>\n <br>\n <p class=\"strong\">\n {{ _(\"For\") }} {{ doc.company }},<br>\n <br>\n <br>\n <br>\n {{ _(\"Authorized Signatory\") }}\n </p>\n</div>\n\n",
|
||||
"idx": 1,
|
||||
"modified": "2015-01-16 11:03:22.893209",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Receipt Voucher",
|
||||
"owner": "Administrator",
|
||||
"print_format_type": "Server",
|
||||
"standard": "Yes"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
"doc_type": "Sales Invoice",
|
||||
"docstatus": 0,
|
||||
"doctype": "Print Format",
|
||||
"html": "<style>\n\t.print-format table, .print-format tr, \n\t.print-format td, .print-format div, .print-format p {\n\t\tfont-family: Monospace;\n\t\tline-height: 200%;\n\t\tvertical-align: middle;\n\t}\n\t@media screen {\n\t\t.print-format {\n\t\t\twidth: 4in;\n\t\t\tpadding: 0.25in;\n\t\t\tmin-height: 8in;\n\t\t}\n\t}\n</style>\n\n<p class=\"text-center\">\n\t{{ doc.company }}<br>\n\t{{ doc.select_print_heading or _(\"Invoice\") }}<br>\n</p>\n<p>\n\t<b>{{ _(\"Receipt No\") }}:</b> {{ doc.name }}<br>\n\t<b>{{ _(\"Date\") }}:</b> {{ doc.get_formatted(\"posting_date\") }}<br>\n\t<b>{{ _(\"Customer\") }}:</b> {{ doc.customer_name }}\n</p>\n\n<hr>\n<table class=\"table table-condensed cart no-border\">\n\t<thead>\n\t\t<tr>\n\t\t\t<th width=\"60%\">{{ _(\"Item\") }}</b></th>\n\t\t\t<th width=\"10%\" class=\"text-right\">{{ _(\"Qty\") }}</th>\n\t\t\t<th width=\"30%\" class=\"text-right\">{{ _(\"Rate\") }}</th>\n\t\t</tr>\n\t</thead>\n\t<tbody>\n\t\t{%- for item in doc.entries -%}\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t<br>{{ item.item_name }}{%- endif -%}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">{{ item.qty }}</td>\n\t\t\t<td class=\"text-right\">{{ item.amount }}</td>\n\t\t</tr>\n\t\t{%- endfor -%}\n\t</tbody>\n</table>\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ _(\"Net Total\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"net_total_export\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- for row in doc.other_charges -%}\n\t\t{%- if not row.included_in_print_rate -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ row.description }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t{%- endfor -%}\n\t\t{%- if doc.discount_amount -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t<b>{{ _(\"Grand Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"grand_total_export\") }}\n\t\t\t</td>\n\t\t</tr>\n\t</tbody>\n</table>\n{% if doc.get(\"other_charges\", filters={\"included_in_print_rate\": 1}) %}\n<hr>\n<p><b>Taxes Included:</b></p>\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t{%- for row in doc.other_charges -%}\n\t\t{%- if row.included_in_print_rate -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ row.description }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t</td>\n\t\t<tr>\n\t\t{%- endif -%}\n\t\t{%- endfor -%}\n\t</tbody>\n</table>\n{%- endif -%}\n<hr>\n<p>{{ doc.terms or \"\" }}</p>\n<p class=\"text-center\">{{ _(\"Thank you, please visit again.\") }}</p>",
|
||||
"html": "<style>\n\t.print-format table, .print-format tr, \n\t.print-format td, .print-format div, .print-format p {\n\t\tfont-family: Monospace;\n\t\tline-height: 200%;\n\t\tvertical-align: middle;\n\t}\n\t@media screen {\n\t\t.print-format {\n\t\t\twidth: 4in;\n\t\t\tpadding: 0.25in;\n\t\t\tmin-height: 8in;\n\t\t}\n\t}\n</style>\n\n<p class=\"text-center\">\n\t{{ doc.company }}<br>\n\t{{ doc.select_print_heading or _(\"Invoice\") }}<br>\n</p>\n<p>\n\t<b>{{ _(\"Receipt No\") }}:</b> {{ doc.name }}<br>\n\t<b>{{ _(\"Date\") }}:</b> {{ doc.get_formatted(\"posting_date\") }}<br>\n\t<b>{{ _(\"Customer\") }}:</b> {{ doc.customer_name }}\n</p>\n\n<hr>\n<table class=\"table table-condensed cart no-border\">\n\t<thead>\n\t\t<tr>\n\t\t\t<th width=\"60%\">{{ _(\"Item\") }}</b></th>\n\t\t\t<th width=\"10%\" class=\"text-right\">{{ _(\"Qty\") }}</th>\n\t\t\t<th width=\"30%\" class=\"text-right\">{{ _(\"Rate\") }}</th>\n\t\t</tr>\n\t</thead>\n\t<tbody>\n\t\t{%- for item in doc.entries -%}\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t<br>{{ item.item_name }}{%- endif -%}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">{{ item.qty }}</td>\n\t\t\t<td class=\"text-right\">{{ item.amount }}</td>\n\t\t</tr>\n\t\t{%- endfor -%}\n\t</tbody>\n</table>\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ _(\"Net Total\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"net_total_export\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- for row in doc.other_charges -%}\n\t\t{%- if not row.included_in_print_rate -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ row.description }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t{%- endfor -%}\n\t\t{%- if doc.discount_amount -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t<b>{{ _(\"Grand Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"grand_total_export\") }}\n\t\t\t</td>\n\t\t</tr>\n\t</tbody>\n</table>\n{% if doc.get(\"other_charges\", filters={\"included_in_print_rate\": 1}) %}\n<hr>\n<p><b>Taxes Included:</b></p>\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t{%- for row in doc.other_charges -%}\n\t\t{%- if row.included_in_print_rate -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ row.description }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ row.get_formatted(\"tax_amount_after_discount_amount\", doc) }}\n\t\t\t</td>\n\t\t<tr>\n\t\t{%- endif -%}\n\t\t{%- endfor -%}\n\t</tbody>\n</table>\n{%- endif -%}\n<hr>\n<p>{{ doc.terms or \"\" }}</p>\n<p class=\"text-center\">{{ _(\"Thank you, please visit again.\") }}</p>",
|
||||
"idx": 1,
|
||||
"modified": "2014-07-22 02:08:26.603223",
|
||||
"modified": "2014-12-10 12:37:10.854370",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "POS Invoice",
|
||||
|
||||
@@ -30,7 +30,8 @@ def execute(filters=None):
|
||||
data = []
|
||||
for gle in entries:
|
||||
if cstr(gle.against_voucher) == gle.voucher_no or not gle.against_voucher \
|
||||
or [gle.against_voucher_type, gle.against_voucher] in entries_after_report_date:
|
||||
or [gle.against_voucher_type, gle.against_voucher] in entries_after_report_date \
|
||||
or (gle.against_voucher_type == "Purchase Order"):
|
||||
voucher_details = voucher_detail_map.get(gle.voucher_type, {}).get(gle.voucher_no, {})
|
||||
|
||||
invoiced_amount = gle.credit > 0 and gle.credit or 0
|
||||
@@ -67,12 +68,12 @@ def execute(filters=None):
|
||||
|
||||
def get_columns(supplier_naming_by):
|
||||
columns = [
|
||||
"Posting Date:Date:80", "Account:Link/Account:150", "Voucher Type::110",
|
||||
"Voucher No::120", "::30", "Due Date:Date:80", "Bill No::80", "Bill Date:Date:80",
|
||||
"Invoiced Amount:Currency:100", "Paid Amount:Currency:100",
|
||||
"Outstanding Amount:Currency:100", "Age:Int:50", "0-30:Currency:100",
|
||||
"30-60:Currency:100", "60-90:Currency:100", "90-Above:Currency:100",
|
||||
"Supplier:Link/Supplier:150"
|
||||
_("Posting Date") + ":Date:80", _("Account") + ":Link/Account:150", _("Voucher Type") + "::110",
|
||||
_("Voucher No") + "::120", "::30", _("Due Date") + ":Date:80", _("Bill No") + "::80", _("Bill Date") + ":Date:80",
|
||||
_("Invoiced Amount") + ":Currency:100", _("Paid Amount") + ":Currency:100",
|
||||
_("Outstanding Amount") + ":Currency:100", _("Age") + ":Int:50", "0-30:Currency:100",
|
||||
"30-60:Currency:100", "60-90:Currency:100", _("90-Above") + ":Currency:100",
|
||||
_("Supplier") + ":Link/Supplier:150"
|
||||
]
|
||||
|
||||
if supplier_naming_by == "Naming Series":
|
||||
|
||||
@@ -13,20 +13,20 @@ class AccountsReceivableReport(object):
|
||||
self.age_as_on = getdate(nowdate()) \
|
||||
if self.filters.report_date > getdate(nowdate()) \
|
||||
else self.filters.report_date
|
||||
|
||||
|
||||
def run(self):
|
||||
customer_naming_by = frappe.db.get_value("Selling Settings", None, "cust_master_name")
|
||||
return self.get_columns(customer_naming_by), self.get_data(customer_naming_by)
|
||||
|
||||
|
||||
def get_columns(self, customer_naming_by):
|
||||
columns = [
|
||||
"Posting Date:Date:80", "Account:Link/Account:150",
|
||||
"Voucher Type::110", "Voucher No::120", "::30",
|
||||
"Due Date:Date:80",
|
||||
"Invoiced Amount:Currency:100", "Payment Received:Currency:100",
|
||||
"Outstanding Amount:Currency:100", "Age:Int:50", "0-30:Currency:100",
|
||||
"30-60:Currency:100", "60-90:Currency:100", "90-Above:Currency:100",
|
||||
"Customer:Link/Customer:200"
|
||||
_("Posting Date") + ":Date:80", _("Account") + ":Link/Account:150",
|
||||
_("Voucher Type") + "::110", _("Voucher No") + ":Dynamic Link/Voucher Type:120",
|
||||
_("Due Date") + ":Date:80",
|
||||
_("Invoiced Amount") + ":Currency:100", _("Payment Received") + ":Currency:100",
|
||||
_("Outstanding Amount") + ":Currency:100", _("Age") + ":Int:50", "0-30:Currency:100",
|
||||
"30-60:Currency:100", "60-90:Currency:100", _("90-Above") + ":Currency:100",
|
||||
_("Customer") + ":Link/Customer:200"
|
||||
]
|
||||
|
||||
if customer_naming_by == "Naming Series":
|
||||
@@ -63,35 +63,33 @@ class AccountsReceivableReport(object):
|
||||
|
||||
row += [self.get_territory(gle.account), gle.remarks]
|
||||
data.append(row)
|
||||
|
||||
for i in range(0, len(data)):
|
||||
data[i].insert(4, """<a href="%s"><i class="icon icon-share" style="cursor: pointer;"></i></a>""" \
|
||||
% ("/".join(["#Form", data[i][2], data[i][3]]),))
|
||||
|
||||
return data
|
||||
|
||||
def get_entries_after(self, report_date):
|
||||
# returns a distinct list
|
||||
return list(set([(e.voucher_type, e.voucher_no) for e in self.get_gl_entries()
|
||||
if getdate(e.posting_date) > report_date]))
|
||||
|
||||
|
||||
def get_entries_till(self, report_date):
|
||||
# returns a generator
|
||||
return (e for e in self.get_gl_entries()
|
||||
return (e for e in self.get_gl_entries()
|
||||
if getdate(e.posting_date) <= report_date)
|
||||
|
||||
|
||||
def is_receivable(self, gle, future_vouchers):
|
||||
return (
|
||||
# advance
|
||||
(not gle.against_voucher) or
|
||||
|
||||
(not gle.against_voucher) or
|
||||
|
||||
# against sales order
|
||||
(gle.against_voucher_type == "Sales Order") or
|
||||
|
||||
# sales invoice
|
||||
(gle.against_voucher==gle.voucher_no and gle.debit > 0) or
|
||||
|
||||
(gle.against_voucher==gle.voucher_no and gle.debit > 0) or
|
||||
|
||||
# entries adjusted with future vouchers
|
||||
((gle.against_voucher_type, gle.against_voucher) in future_vouchers)
|
||||
)
|
||||
|
||||
|
||||
def get_outstanding_amount(self, gle, report_date):
|
||||
payment_received = 0.0
|
||||
for e in self.get_gl_entries_for(gle.account, gle.voucher_type, gle.voucher_no):
|
||||
@@ -99,7 +97,7 @@ class AccountsReceivableReport(object):
|
||||
payment_received += (flt(e.credit) - flt(e.debit))
|
||||
|
||||
return flt(gle.debit) - flt(gle.credit) - payment_received
|
||||
|
||||
|
||||
def get_customer(self, account):
|
||||
return self.get_account_map().get(account, {}).get("customer") or ""
|
||||
|
||||
@@ -108,25 +106,25 @@ class AccountsReceivableReport(object):
|
||||
|
||||
def get_territory(self, account):
|
||||
return self.get_account_map().get(account, {}).get("territory") or ""
|
||||
|
||||
|
||||
def get_account_map(self):
|
||||
if not hasattr(self, "account_map"):
|
||||
self.account_map = dict(((r.name, r) for r in frappe.db.sql("""select
|
||||
self.account_map = dict(((r.name, r) for r in frappe.db.sql("""select
|
||||
acc.name, cust.name as customer, cust.customer_name, cust.territory
|
||||
from `tabAccount` acc left join `tabCustomer` cust
|
||||
from `tabAccount` acc left join `tabCustomer` cust
|
||||
on cust.name=acc.master_name where acc.master_type="Customer" """, as_dict=True)))
|
||||
|
||||
|
||||
return self.account_map
|
||||
|
||||
|
||||
def get_due_date(self, gle):
|
||||
if not hasattr(self, "invoice_due_date_map"):
|
||||
# TODO can be restricted to posting date
|
||||
self.invoice_due_date_map = dict(frappe.db.sql("""select name, due_date
|
||||
from `tabSales Invoice` where docstatus=1"""))
|
||||
|
||||
|
||||
return gle.voucher_type == "Sales Invoice" \
|
||||
and self.invoice_due_date_map.get(gle.voucher_no) or ""
|
||||
|
||||
|
||||
def get_gl_entries(self):
|
||||
if not hasattr(self, "gl_entries"):
|
||||
conditions, values = self.prepare_conditions()
|
||||
@@ -134,15 +132,15 @@ class AccountsReceivableReport(object):
|
||||
where docstatus < 2 {0} order by posting_date, account""".format(conditions),
|
||||
values, as_dict=True)
|
||||
return self.gl_entries
|
||||
|
||||
|
||||
def prepare_conditions(self):
|
||||
conditions = [""]
|
||||
values = {}
|
||||
|
||||
|
||||
if self.filters.company:
|
||||
conditions.append("company=%(company)s")
|
||||
values["company"] = self.filters.company
|
||||
|
||||
|
||||
if self.filters.account:
|
||||
conditions.append("account=%(account)s")
|
||||
values["account"] = self.filters.account
|
||||
@@ -151,11 +149,11 @@ class AccountsReceivableReport(object):
|
||||
if not account_map:
|
||||
frappe.throw(_("No Customer Accounts found."))
|
||||
else:
|
||||
accounts_list = ['"{0}"'.format(ac) for ac in account_map]
|
||||
accounts_list = ["'{0}'".format(frappe.db.escape(ac)) for ac in account_map]
|
||||
conditions.append("account in ({0})".format(", ".join(accounts_list)))
|
||||
|
||||
|
||||
return " and ".join(conditions), values
|
||||
|
||||
|
||||
def get_gl_entries_for(self, account, against_voucher_type, against_voucher):
|
||||
if not hasattr(self, "gl_entries_map"):
|
||||
self.gl_entries_map = {}
|
||||
@@ -165,7 +163,7 @@ class AccountsReceivableReport(object):
|
||||
.setdefault(gle.against_voucher_type, {})\
|
||||
.setdefault(gle.against_voucher, [])\
|
||||
.append(gle)
|
||||
|
||||
|
||||
return self.gl_entries_map.get(account, {})\
|
||||
.get(against_voucher_type, {})\
|
||||
.get(against_voucher, [])
|
||||
@@ -178,15 +176,15 @@ def get_ageing_data(age_as_on, entry_date, outstanding_amount):
|
||||
outstanding_range = [0.0, 0.0, 0.0, 0.0]
|
||||
if not (age_as_on and entry_date):
|
||||
return [0] + outstanding_range
|
||||
|
||||
|
||||
age = (getdate(age_as_on) - getdate(entry_date)).days or 0
|
||||
index = None
|
||||
for i, days in enumerate([30, 60, 90]):
|
||||
if age <= days:
|
||||
index = i
|
||||
break
|
||||
|
||||
|
||||
if index is None: index = 3
|
||||
outstanding_range[index] = outstanding_amount
|
||||
|
||||
|
||||
return [age] + outstanding_range
|
||||
|
||||
@@ -14,9 +14,9 @@ def execute(filters=None):
|
||||
return columns, data
|
||||
|
||||
def get_columns():
|
||||
return ["Journal Voucher:Link/Journal Voucher:140", "Account:Link/Account:140",
|
||||
"Posting Date:Date:100", "Clearance Date:Date:110", "Against Account:Link/Account:200",
|
||||
"Debit:Currency:120", "Credit:Currency:120"
|
||||
return [_("Journal Voucher") + ":Link/Journal Voucher:140", _("Account") + ":Link/Account:140",
|
||||
_("Posting Date") + ":Date:100", _("Clearance Date") + ":Date:110", _("Against Account") + ":Link/Account:200",
|
||||
_("Debit") + ":Currency:120", _("Credit") + ":Currency:120"
|
||||
]
|
||||
|
||||
def get_conditions(filters):
|
||||
|
||||
@@ -16,31 +16,31 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for(var i=0, l=data.length; i<l; i++) { %}
|
||||
{% if (data[i].posting_date) { %}
|
||||
{% if (data[i][__("Posting Date")]) { %}
|
||||
<tr>
|
||||
<td>{%= dateutil.str_to_user(data[i].posting_date) %}</td>
|
||||
<td>{%= data[i].journal_voucher %}</td>
|
||||
<td>{%= __("Against") %}: {%= data[i].against_account %}
|
||||
{% if (data[i].reference) { %}
|
||||
<br>{%= __("Reference") %}: {%= data[i].reference %}
|
||||
{% if (data[i].ref_date) { %}
|
||||
<br>{%= __("Reference Date") %}: {%= dateutil.str_to_user(data[i].ref_date) %}
|
||||
<td>{%= dateutil.str_to_user(data[i][__("Posting Date")]) %}</td>
|
||||
<td>{%= data[i][__("Journal Voucher")] %}</td>
|
||||
<td>{%= __("Against") %}: {%= data[i][__("Against Account")] %}
|
||||
{% if (data[i][__("Reference")]) { %}
|
||||
<br>{%= __("Reference") %}: {%= data[i][__("Reference")] %}
|
||||
{% if (data[i][__("Ref Date")]) { %}
|
||||
<br>{%= __("Reference Date") %}: {%= dateutil.str_to_user(data[i][__("Ref Date")]) %}
|
||||
{% } %}
|
||||
{% } %}
|
||||
{% if (data[i].clearance_date) { %}
|
||||
<br>{%= __("Clearance Date") %}: {%= dateutil.str_to_user(data[i].clearance_date) %}
|
||||
{% if (data[i][__("Clearance Date")]) { %}
|
||||
<br>{%= __("Clearance Date") %}: {%= dateutil.str_to_user(data[i][__("Clearance Date")]) %}
|
||||
{% } %}
|
||||
</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i].debit) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i].credit) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i][__("Debit")]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i][__("Credit")]) %}</td>
|
||||
</tr>
|
||||
{% } else { %}
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td>{%= data[i].journal_voucher %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i].debit) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i].credit) %}</td>
|
||||
<td>{%= data[i][__("Journal Voucher")] %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i][__("Debit")]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i][__("Credit")]) %}</td>
|
||||
</tr>
|
||||
{% } %}
|
||||
{% } %}
|
||||
|
||||
@@ -26,7 +26,7 @@ def execute(filters=None):
|
||||
amounts_not_reflected_in_system = frappe.db.sql("""select sum(ifnull(jvd.debit, 0) - ifnull(jvd.credit, 0))
|
||||
from `tabJournal Voucher Detail` jvd, `tabJournal Voucher` jv
|
||||
where jvd.parent = jv.name and jv.docstatus=1 and jvd.account=%s
|
||||
and jv.posting_date > %s and jv.clearance_date <= %s
|
||||
and jv.posting_date > %s and jv.clearance_date <= %s and ifnull(jv.is_opening, 'No') = 'No'
|
||||
""", (filters["account"], filters["report_date"], filters["report_date"]))
|
||||
|
||||
amounts_not_reflected_in_system = flt(amounts_not_reflected_in_system[0][0]) \
|
||||
@@ -47,9 +47,9 @@ def execute(filters=None):
|
||||
return columns, data
|
||||
|
||||
def get_columns():
|
||||
return ["Posting Date:Date:100", "Journal Voucher:Link/Journal Voucher:220",
|
||||
"Debit:Currency:120", "Credit:Currency:120",
|
||||
"Against Account:Link/Account:200", "Reference::100", "Ref Date:Date:110", "Clearance Date:Date:110"
|
||||
return [_("Posting Date") + ":Date:100", _("Journal Voucher") + ":Link/Journal Voucher:220",
|
||||
_("Debit") + ":Currency:120", _("Credit") + ":Currency:120",
|
||||
_("Against Account") + ":Link/Account:200", _("Reference") + "::100", _("Ref Date") + ":Date:110", _("Clearance Date") + ":Date:110"
|
||||
]
|
||||
|
||||
def get_entries(filters):
|
||||
@@ -61,6 +61,7 @@ def get_entries(filters):
|
||||
where jvd.parent = jv.name and jv.docstatus=1
|
||||
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'
|
||||
order by jv.name DESC""", filters, as_list=1)
|
||||
|
||||
return entries
|
||||
|
||||
@@ -5,6 +5,7 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _, msgprint
|
||||
from frappe.utils import flt
|
||||
from frappe.utils import formatdate
|
||||
import time
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
from erpnext.controllers.trends import get_period_date_ranges, get_period_month_ranges
|
||||
@@ -44,21 +45,21 @@ def get_columns(filters):
|
||||
msgprint(_("Please specify") + ": " + label,
|
||||
raise_exception=True)
|
||||
|
||||
columns = ["Cost Center:Link/Cost Center:120", "Account:Link/Account:120"]
|
||||
columns = [_("Cost Center") + ":Link/Cost Center:120", _("Account") + ":Link/Account:120"]
|
||||
|
||||
group_months = False if filters["period"] == "Monthly" else True
|
||||
|
||||
for from_date, to_date in get_period_date_ranges(filters["period"], filters["fiscal_year"]):
|
||||
for label in ["Target (%s)", "Actual (%s)", "Variance (%s)"]:
|
||||
for label in [_("Target") + " (%s)", _("Actual") + " (%s)", _("Variance") + " (%s)"]:
|
||||
if group_months:
|
||||
label = label % (from_date.strftime("%b") + " - " + to_date.strftime("%b"))
|
||||
label = label % (formatdate(from_date, format_string="MMM") + " - " + formatdate(from_date, format_string="MMM"))
|
||||
else:
|
||||
label = label % from_date.strftime("%b")
|
||||
label = label % formatdate(from_date, format_string="MMM")
|
||||
|
||||
columns.append(label+":Float:120")
|
||||
|
||||
return columns + ["Total Target:Float:120", "Total Actual:Float:120",
|
||||
"Total Variance:Float:120"]
|
||||
return columns + [_("Total Target") + ":Float:120", _("Total Actual") + ":Float:120",
|
||||
_("Total Variance") + ":Float:120"]
|
||||
|
||||
#Get cost center & target details
|
||||
def get_costcenter_target_details(filters):
|
||||
|
||||
@@ -22,23 +22,23 @@
|
||||
<tbody>
|
||||
{% for(var i=0, l=data.length; i<l; i++) { %}
|
||||
<tr>
|
||||
{% if(data[i].posting_date) { %}
|
||||
<td>{%= dateutil.str_to_user(data[i].posting_date) %}</td>
|
||||
<td>{%= data[i].voucher_type%}
|
||||
<br>{%= data[i].voucher_no %}</td>
|
||||
<td>{%= data[i].account %}
|
||||
<br>{%= __("Against") %}: {%= data[i].against_account %}
|
||||
<br>{%= __("Remarks") %}: {%= data[i].remarks %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i].debit) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i].credit) %}</td>
|
||||
{% if(data[i][__("Posting Date")]) { %}
|
||||
<td>{%= dateutil.str_to_user(data[i][__("Posting Date")]) %}</td>
|
||||
<td>{%= data[i][__("Voucher Type")] %}
|
||||
<br>{%= data[i][__("Voucher No")] %}</td>
|
||||
<td>{%= data[i][__("Account")] %}
|
||||
<br>{%= __("Against") %}: {%= data[i][__("Against Account")] %}
|
||||
<br>{%= __("Remarks") %}: {%= data[i][__("Remarks")] %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i][__("Debit")]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i][__("Credit")]) %}</td>
|
||||
{% } else { %}
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td><b>{%= data[i].account || " " %}</b></td>
|
||||
<td><b>{%= data[i][__("Account")] || " " %}</b></td>
|
||||
<td style="text-align: right">
|
||||
{%= data[i].account && format_currency(data[i].debit) %}</td>
|
||||
{%= data[i][__("Account")] && format_currency(data[i][__("Debit")]) %}</td>
|
||||
<td style="text-align: right">
|
||||
{%= data[i].account && format_currency(data[i].credit) %}</td>
|
||||
{%= data[i][__("Account")] && format_currency(data[i][__("Credit")]) %}</td>
|
||||
{% } %}
|
||||
</tr>
|
||||
{% } %}
|
||||
|
||||
@@ -34,9 +34,9 @@ def validate_filters(filters, account_details):
|
||||
frappe.throw(_("From Date must be before To Date"))
|
||||
|
||||
def get_columns():
|
||||
return ["Posting Date:Date:100", "Account:Link/Account:200", "Debit:Float:100",
|
||||
"Credit:Float:100", "Voucher Type::120", "Voucher No::160", "Link::20",
|
||||
"Against Account::120", "Cost Center:Link/Cost Center:100", "Remarks::400"]
|
||||
return [_("Posting Date") + ":Date:100", _("Account") + ":Link/Account:200", _("Debit") + ":Float:100",
|
||||
_("Credit") + ":Float:100", _("Voucher Type") + "::120", _("Voucher No") + ":Dynamic Link/Voucher Type:160",
|
||||
_("Against Account") + "::120", _("Cost Center") + ":Link/Cost Center:100", _("Remarks") + "::400"]
|
||||
|
||||
def get_result(filters, account_details):
|
||||
gl_entries = get_gl_entries(filters)
|
||||
@@ -162,15 +162,6 @@ def get_result_as_list(data):
|
||||
for d in data:
|
||||
result.append([d.get("posting_date"), d.get("account"), d.get("debit"),
|
||||
d.get("credit"), d.get("voucher_type"), d.get("voucher_no"),
|
||||
get_voucher_link(d.get("voucher_type"), d.get("voucher_no")),
|
||||
d.get("against"), d.get("cost_center"), d.get("remarks")])
|
||||
|
||||
return result
|
||||
|
||||
def get_voucher_link(voucher_type, voucher_no):
|
||||
icon = ""
|
||||
if voucher_type and voucher_no:
|
||||
icon = """<a href="%s"><i class="icon icon-share" style="cursor: pointer;">
|
||||
</i></a>""" % ("/".join(["#Form", voucher_type, voucher_no]))
|
||||
|
||||
return icon
|
||||
|
||||
@@ -3,34 +3,35 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import flt
|
||||
from erpnext.stock.utils import get_buying_amount, get_sales_bom_buying_amount
|
||||
|
||||
def execute(filters=None):
|
||||
if not filters: filters = {}
|
||||
|
||||
|
||||
stock_ledger_entries = get_stock_ledger_entries(filters)
|
||||
source = get_source_data(filters)
|
||||
item_sales_bom = get_item_sales_bom()
|
||||
|
||||
columns = ["Delivery Note/Sales Invoice::120", "Link::30", "Posting Date:Date", "Posting Time",
|
||||
"Item Code:Link/Item", "Item Name", "Description", "Warehouse:Link/Warehouse",
|
||||
"Qty:Float", "Selling Rate:Currency", "Avg. Buying Rate:Currency",
|
||||
"Selling Amount:Currency", "Buying Amount:Currency",
|
||||
"Gross Profit:Currency", "Gross Profit %:Percent", "Project:Link/Project"]
|
||||
|
||||
columns = [_("Delivery Note/Sales Invoice") + "::120", _("Link") + "::30", _("Posting Date") + ":Date", _("Posting Time"),
|
||||
_("Item Code") + ":Link/Item", _("Item Name"), _("Description"), _("Warehouse") + ":Link/Warehouse",
|
||||
_("Qty") + ":Float", _("Selling Rate") + ":Currency", _("Avg. Buying Rate") + ":Currency",
|
||||
_("Selling Amount") + ":Currency", _("Buying Amount") + ":Currency",
|
||||
_("Gross Profit") + ":Currency", _("Gross Profit %") + ":Percent", _("Project") + ":Link/Project"]
|
||||
data = []
|
||||
for row in source:
|
||||
selling_amount = flt(row.base_amount)
|
||||
|
||||
|
||||
item_sales_bom_map = item_sales_bom.get(row.parenttype, {}).get(row.name, frappe._dict())
|
||||
|
||||
|
||||
if item_sales_bom_map.get(row.item_code):
|
||||
buying_amount = get_sales_bom_buying_amount(row.item_code, row.warehouse,
|
||||
buying_amount = get_sales_bom_buying_amount(row.item_code, row.warehouse,
|
||||
row.parenttype, row.name, row.item_row, stock_ledger_entries, item_sales_bom_map)
|
||||
else:
|
||||
buying_amount = get_buying_amount(row.parenttype, row.name, row.item_row,
|
||||
stock_ledger_entries.get((row.item_code, row.warehouse), []))
|
||||
|
||||
|
||||
buying_amount = buying_amount > 0 and buying_amount or 0
|
||||
|
||||
gross_profit = selling_amount - buying_amount
|
||||
@@ -38,41 +39,41 @@ def execute(filters=None):
|
||||
gross_profit_percent = (gross_profit / selling_amount) * 100.0
|
||||
else:
|
||||
gross_profit_percent = 0.0
|
||||
|
||||
|
||||
icon = """<a href="%s"><i class="icon icon-share" style="cursor: pointer;"></i></a>""" \
|
||||
% ("/".join(["#Form", row.parenttype, row.name]),)
|
||||
data.append([row.name, icon, row.posting_date, row.posting_time, row.item_code, row.item_name,
|
||||
row.description, row.warehouse, row.qty, row.base_rate,
|
||||
row.description, row.warehouse, row.qty, row.base_rate,
|
||||
row.qty and (buying_amount / row.qty) or 0, row.base_amount, buying_amount,
|
||||
gross_profit, gross_profit_percent, row.project])
|
||||
|
||||
|
||||
return columns, data
|
||||
|
||||
def get_stock_ledger_entries(filters):
|
||||
|
||||
def get_stock_ledger_entries(filters):
|
||||
query = """select item_code, voucher_type, voucher_no,
|
||||
voucher_detail_no, posting_date, posting_time, stock_value,
|
||||
warehouse, actual_qty as qty
|
||||
from `tabStock Ledger Entry`"""
|
||||
|
||||
|
||||
if filters.get("company"):
|
||||
query += """ where company=%(company)s"""
|
||||
|
||||
|
||||
query += " order by item_code desc, warehouse desc, posting_date desc, posting_time desc, name desc"
|
||||
|
||||
|
||||
res = frappe.db.sql(query, filters, as_dict=True)
|
||||
|
||||
|
||||
out = {}
|
||||
for r in res:
|
||||
if (r.item_code, r.warehouse) not in out:
|
||||
out[(r.item_code, r.warehouse)] = []
|
||||
|
||||
|
||||
out[(r.item_code, r.warehouse)].append(r)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def get_item_sales_bom():
|
||||
item_sales_bom = {}
|
||||
|
||||
|
||||
for d in frappe.db.sql("""select parenttype, parent, parent_item,
|
||||
item_code, warehouse, -1*qty as total_qty, parent_detail_docname
|
||||
from `tabPacked Item` where docstatus=1""", as_dict=True):
|
||||
@@ -80,7 +81,7 @@ def get_item_sales_bom():
|
||||
frappe._dict()).setdefault(d.parent_item, []).append(d)
|
||||
|
||||
return item_sales_bom
|
||||
|
||||
|
||||
def get_source_data(filters):
|
||||
conditions = ""
|
||||
if filters.get("company"):
|
||||
@@ -89,9 +90,9 @@ def get_source_data(filters):
|
||||
conditions += " and posting_date>=%(from_date)s"
|
||||
if filters.get("to_date"):
|
||||
conditions += " and posting_date<=%(to_date)s"
|
||||
|
||||
delivery_note_items = frappe.db.sql("""select item.parenttype, dn.name,
|
||||
dn.posting_date, dn.posting_time, dn.project_name,
|
||||
|
||||
delivery_note_items = frappe.db.sql("""select item.parenttype, dn.name,
|
||||
dn.posting_date, dn.posting_time, dn.project_name,
|
||||
item.item_code, item.item_name, item.description, item.warehouse,
|
||||
item.qty, item.base_rate, item.base_amount, item.name as "item_row",
|
||||
timestamp(dn.posting_date, dn.posting_time) as posting_datetime
|
||||
@@ -99,7 +100,7 @@ def get_source_data(filters):
|
||||
where item.parent = dn.name and dn.docstatus = 1 %s
|
||||
order by dn.posting_date desc, dn.posting_time desc""" % (conditions,), filters, as_dict=1)
|
||||
|
||||
sales_invoice_items = frappe.db.sql("""select item.parenttype, si.name,
|
||||
sales_invoice_items = frappe.db.sql("""select item.parenttype, si.name,
|
||||
si.posting_date, si.posting_time, si.project_name,
|
||||
item.item_code, item.item_name, item.description, item.warehouse,
|
||||
item.qty, item.base_rate, item.base_amount, item.name as "item_row",
|
||||
@@ -108,9 +109,9 @@ def get_source_data(filters):
|
||||
where item.parent = si.name and si.docstatus = 1 %s
|
||||
and si.update_stock = 1
|
||||
order by si.posting_date desc, si.posting_time desc""" % (conditions,), filters, as_dict=1)
|
||||
|
||||
|
||||
source = delivery_note_items + sales_invoice_items
|
||||
if len(source) > len(delivery_note_items):
|
||||
source.sort(key=lambda d: d.posting_datetime, reverse=True)
|
||||
|
||||
return source
|
||||
|
||||
return source
|
||||
|
||||
@@ -3,46 +3,47 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import msgprint, _
|
||||
from frappe.utils import flt
|
||||
|
||||
def execute(filters=None):
|
||||
if not filters: filters = {}
|
||||
columns = get_columns()
|
||||
last_col = len(columns)
|
||||
|
||||
|
||||
item_list = get_items(filters)
|
||||
aii_account_map = get_aii_accounts()
|
||||
if item_list:
|
||||
item_tax, tax_accounts = get_tax_accounts(item_list, columns)
|
||||
|
||||
|
||||
data = []
|
||||
for d in item_list:
|
||||
expense_account = d.expense_account or aii_account_map.get(d.company)
|
||||
row = [d.item_code, d.item_name, d.item_group, d.parent, d.posting_date,
|
||||
d.supplier_name, d.credit_to, d.project_name, d.company, d.purchase_order,
|
||||
row = [d.item_code, d.item_name, d.item_group, d.parent, d.posting_date,
|
||||
d.supplier_name, d.credit_to, d.project_name, d.company, d.purchase_order,
|
||||
d.purchase_receipt, expense_account, d.qty, d.base_rate, d.base_amount]
|
||||
for tax in tax_accounts:
|
||||
row.append(item_tax.get(d.parent, {}).get(d.item_code, {}).get(tax, 0))
|
||||
|
||||
total_tax = sum(row[last_col:])
|
||||
row += [total_tax, d.base_amount + total_tax]
|
||||
|
||||
|
||||
data.append(row)
|
||||
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
|
||||
|
||||
def get_columns():
|
||||
return ["Item Code:Link/Item:120", "Item Name::120", "Item Group:Link/Item Group:100",
|
||||
"Invoice:Link/Purchase Invoice:120", "Posting Date:Date:80", "Supplier:Link/Customer:120",
|
||||
"Supplier Account:Link/Account:120", "Project:Link/Project:80", "Company:Link/Company:100",
|
||||
"Purchase Order:Link/Purchase Order:100", "Purchase Receipt:Link/Purchase Receipt:100",
|
||||
"Expense Account:Link/Account:140", "Qty:Float:120", "Rate:Currency:120",
|
||||
"Amount:Currency:120"]
|
||||
|
||||
return [_("Item Code") + ":Link/Item:120", _("Item Name") + "::120", _("Item Group") + ":Link/Item Group:100",
|
||||
_("Invoice") + ":Link/Purchase Invoice:120", _("Posting Date") + ":Date:80", _("Supplier") + ":Link/Customer:120",
|
||||
_("Supplier Account") + ":Link/Account:120", _("Project") + ":Link/Project:80", _("Company") + ":Link/Company:100",
|
||||
_("Purchase Order") + ":Link/Purchase Order:100", _("Purchase Receipt") + ":Link/Purchase Receipt:100",
|
||||
_("Expense Account") + ":Link/Account:140", _("Qty") + ":Float:120", _("Rate") + ":Currency:120",
|
||||
_("Amount") + ":Currency:120"]
|
||||
|
||||
def get_conditions(filters):
|
||||
conditions = ""
|
||||
|
||||
|
||||
for opts in (("company", " and company=%(company)s"),
|
||||
("account", " and pi.credit_to = %(account)s"),
|
||||
("item_code", " and pi_item.item_code = %(item_code)s"),
|
||||
@@ -52,48 +53,56 @@ def get_conditions(filters):
|
||||
conditions += opts[1]
|
||||
|
||||
return conditions
|
||||
|
||||
|
||||
def get_items(filters):
|
||||
conditions = get_conditions(filters)
|
||||
match_conditions = frappe.build_match_conditions("Purchase Invoice")
|
||||
|
||||
return frappe.db.sql("""select pi_item.parent, pi.posting_date, pi.credit_to, pi.company,
|
||||
pi.supplier, pi.remarks, pi_item.item_code, pi_item.item_name, pi_item.item_group,
|
||||
pi_item.project_name, pi_item.purchase_order, pi_item.purchase_receipt,
|
||||
|
||||
return frappe.db.sql("""select pi_item.parent, pi.posting_date, pi.credit_to, pi.company,
|
||||
pi.supplier, pi.remarks, pi.net_total, pi_item.item_code, pi_item.item_name, pi_item.item_group,
|
||||
pi_item.project_name, pi_item.purchase_order, pi_item.purchase_receipt,
|
||||
pi_item.expense_account, pi_item.qty, pi_item.base_rate, pi_item.base_amount, pi.supplier_name
|
||||
from `tabPurchase Invoice` pi, `tabPurchase Invoice Item` pi_item
|
||||
from `tabPurchase Invoice` pi, `tabPurchase Invoice Item` pi_item
|
||||
where pi.name = pi_item.parent and pi.docstatus = 1 %s %s
|
||||
order by pi.posting_date desc, pi_item.item_code desc""" % (conditions, match_conditions), filters, as_dict=1)
|
||||
|
||||
|
||||
def get_aii_accounts():
|
||||
return dict(frappe.db.sql("select name, stock_received_but_not_billed from tabCompany"))
|
||||
|
||||
|
||||
def get_tax_accounts(item_list, columns):
|
||||
import json
|
||||
item_tax = {}
|
||||
tax_accounts = []
|
||||
|
||||
tax_details = frappe.db.sql("""select parent, account_head, item_wise_tax_detail
|
||||
from `tabPurchase Taxes and Charges` where parenttype = 'Purchase Invoice'
|
||||
and docstatus = 1 and ifnull(account_head, '') != '' and category in ('Total', 'Valuation and Total')
|
||||
and parent in (%s)""" % ', '.join(['%s']*len(item_list)), tuple([item.parent for item in item_list]))
|
||||
|
||||
for parent, account_head, item_wise_tax_detail in tax_details:
|
||||
|
||||
invoice_wise_items = {}
|
||||
for d in item_list:
|
||||
invoice_wise_items.setdefault(d.parent, []).append(d)
|
||||
|
||||
tax_details = frappe.db.sql("""select parent, account_head, item_wise_tax_detail, charge_type, tax_amount
|
||||
from `tabPurchase Taxes and Charges` where parenttype = 'Purchase Invoice'
|
||||
and docstatus = 1 and ifnull(account_head, '') != '' and category in ('Total', 'Valuation and Total')
|
||||
and parent in (%s)""" % ', '.join(['%s']*len(invoice_wise_items)), tuple(invoice_wise_items.keys()))
|
||||
|
||||
for parent, account_head, item_wise_tax_detail, charge_type, tax_amount in tax_details:
|
||||
if account_head not in tax_accounts:
|
||||
tax_accounts.append(account_head)
|
||||
|
||||
|
||||
if item_wise_tax_detail:
|
||||
try:
|
||||
item_wise_tax_detail = json.loads(item_wise_tax_detail)
|
||||
for item, tax_amount in item_wise_tax_detail.items():
|
||||
item_tax.setdefault(parent, {}).setdefault(item, {})[account_head] = \
|
||||
flt(tax_amount[1]) if isinstance(tax_amount, list) else flt(tax_amount)
|
||||
|
||||
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
elif charge_type == "Actual" and tax_amount:
|
||||
for d in invoice_wise_items.get(parent, []):
|
||||
item_tax.setdefault(parent, {}).setdefault(d.item_code, {})[account_head] = \
|
||||
(tax_amount * d.base_amount) / d.net_total
|
||||
|
||||
tax_accounts.sort()
|
||||
columns += [account_head + ":Currency:80" for account_head in tax_accounts]
|
||||
columns += ["Total Tax:Currency:80", "Total:Currency:80"]
|
||||
|
||||
return item_tax, tax_accounts
|
||||
return item_tax, tax_accounts
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import msgprint, _
|
||||
from frappe.utils import flt
|
||||
|
||||
def execute(filters=None):
|
||||
@@ -13,36 +14,36 @@ def execute(filters=None):
|
||||
item_list = get_items(filters)
|
||||
if item_list:
|
||||
item_tax, tax_accounts = get_tax_accounts(item_list, columns)
|
||||
|
||||
|
||||
data = []
|
||||
for d in item_list:
|
||||
row = [d.item_code, d.item_name, d.item_group, d.parent, d.posting_date,
|
||||
d.customer_name, d.debit_to, d.territory, d.project_name, d.company, d.sales_order,
|
||||
row = [d.item_code, d.item_name, d.item_group, d.parent, d.posting_date,
|
||||
d.customer_name, d.debit_to, d.territory, d.project_name, d.company, d.sales_order,
|
||||
d.delivery_note, d.income_account, d.qty, d.base_rate, d.base_amount]
|
||||
|
||||
|
||||
for tax in tax_accounts:
|
||||
row.append(item_tax.get(d.parent, {}).get(d.item_code, {}).get(tax, 0))
|
||||
|
||||
total_tax = sum(row[last_col:])
|
||||
row += [total_tax, d.base_amount + total_tax]
|
||||
|
||||
|
||||
data.append(row)
|
||||
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
def get_columns():
|
||||
return [
|
||||
"Item Code:Link/Item:120", "Item Name::120", "Item Group:Link/Item Group:100",
|
||||
"Invoice:Link/Sales Invoice:120", "Posting Date:Date:80", "Customer:Link/Customer:120",
|
||||
"Customer Account:Link/Account:120", "Territory:Link/Territory:80",
|
||||
"Project:Link/Project:80", "Company:Link/Company:100", "Sales Order:Link/Sales Order:100",
|
||||
"Delivery Note:Link/Delivery Note:100", "Income Account:Link/Account:140",
|
||||
"Qty:Float:120", "Rate:Currency:120", "Amount:Currency:120"
|
||||
_("Item Code") + ":Link/Item:120", _("Item Name") + "::120", _("Item Group") + ":Link/Item Group:100",
|
||||
_("Invoice") + ":Link/Sales Invoice:120", _("Posting Date") + ":Date:80", _("Customer") + ":Link/Customer:120",
|
||||
_("Customer Account") + ":Link/Account:120", _("Territory") + ":Link/Territory:80",
|
||||
_("Project") + ":Link/Project:80", _("Company") + ":Link/Company:100", _("Sales Order") + ":Link/Sales Order:100",
|
||||
_("Delivery Note") + ":Link/Delivery Note:100", _("Income Account") + ":Link/Account:140",
|
||||
_("Qty") + ":Float:120", _("Rate") + ":Currency:120", _("Amount") + ":Currency:120"
|
||||
]
|
||||
|
||||
|
||||
def get_conditions(filters):
|
||||
conditions = ""
|
||||
|
||||
|
||||
for opts in (("company", " and company=%(company)s"),
|
||||
("account", " and si.debit_to = %(account)s"),
|
||||
("item_code", " and si_item.item_code = %(item_code)s"),
|
||||
@@ -52,32 +53,36 @@ def get_conditions(filters):
|
||||
conditions += opts[1]
|
||||
|
||||
return conditions
|
||||
|
||||
|
||||
def get_items(filters):
|
||||
conditions = get_conditions(filters)
|
||||
return frappe.db.sql("""select si_item.parent, si.posting_date, si.debit_to, si.project_name,
|
||||
si.customer, si.remarks, si.territory, si.company, si_item.item_code, si_item.item_name,
|
||||
si_item.item_group, si_item.sales_order, si_item.delivery_note, si_item.income_account,
|
||||
return frappe.db.sql("""select si_item.parent, si.posting_date, si.debit_to, si.project_name,
|
||||
si.customer, si.remarks, si.territory, si.company, si.net_total, si_item.item_code, si_item.item_name,
|
||||
si_item.item_group, si_item.sales_order, si_item.delivery_note, si_item.income_account,
|
||||
si_item.qty, si_item.base_rate, si_item.base_amount, si.customer_name
|
||||
from `tabSales Invoice` si, `tabSales Invoice Item` si_item
|
||||
where si.name = si_item.parent and si.docstatus = 1 %s
|
||||
from `tabSales Invoice` si, `tabSales Invoice Item` si_item
|
||||
where si.name = si_item.parent and si.docstatus = 1 %s
|
||||
order by si.posting_date desc, si_item.item_code desc""" % conditions, filters, as_dict=1)
|
||||
|
||||
|
||||
def get_tax_accounts(item_list, columns):
|
||||
import json
|
||||
item_tax = {}
|
||||
tax_accounts = []
|
||||
|
||||
tax_details = frappe.db.sql("""select parent, account_head, item_wise_tax_detail
|
||||
from `tabSales Taxes and Charges` where parenttype = 'Sales Invoice'
|
||||
|
||||
invoice_wise_items = {}
|
||||
for d in item_list:
|
||||
invoice_wise_items.setdefault(d.parent, []).append(d)
|
||||
|
||||
tax_details = frappe.db.sql("""select parent, account_head, item_wise_tax_detail, charge_type, tax_amount
|
||||
from `tabSales Taxes and Charges` where parenttype = 'Sales Invoice'
|
||||
and docstatus = 1 and ifnull(account_head, '') != ''
|
||||
and parent in (%s)""" % ', '.join(['%s']*len(item_list)),
|
||||
tuple([item.parent for item in item_list]))
|
||||
|
||||
for parent, account_head, item_wise_tax_detail in tax_details:
|
||||
and parent in (%s)""" % ', '.join(['%s']*len(invoice_wise_items)),
|
||||
tuple(invoice_wise_items.keys()))
|
||||
|
||||
for parent, account_head, item_wise_tax_detail, charge_type, tax_amount in tax_details:
|
||||
if account_head not in tax_accounts:
|
||||
tax_accounts.append(account_head)
|
||||
|
||||
|
||||
if item_wise_tax_detail:
|
||||
try:
|
||||
item_wise_tax_detail = json.loads(item_wise_tax_detail)
|
||||
@@ -86,9 +91,13 @@ def get_tax_accounts(item_list, columns):
|
||||
flt(tax_amount[1]) if isinstance(tax_amount, list) else flt(tax_amount)
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
elif charge_type == "Actual" and tax_amount:
|
||||
for d in invoice_wise_items.get(parent, []):
|
||||
item_tax.setdefault(parent, {}).setdefault(d.item_code, {})[account_head] = \
|
||||
flt((tax_amount * d.base_amount) / d.net_total)
|
||||
|
||||
tax_accounts.sort()
|
||||
columns += [account_head + ":Currency:80" for account_head in tax_accounts]
|
||||
columns += ["Total Tax:Currency:80", "Total:Currency:80"]
|
||||
|
||||
return item_tax, tax_accounts
|
||||
return item_tax, tax_accounts
|
||||
|
||||
@@ -37,11 +37,11 @@ def execute(filters=None):
|
||||
return columns, data
|
||||
|
||||
def get_columns():
|
||||
return ["Journal Voucher:Link/Journal Voucher:140", "Account:Link/Account:140",
|
||||
"Posting Date:Date:100", "Against Invoice:Link/Purchase Invoice:130",
|
||||
"Against Invoice Posting Date:Date:130", "Debit:Currency:120", "Credit:Currency:120",
|
||||
"Reference No::100", "Reference Date:Date:100", "Remarks::150", "Age:Int:40",
|
||||
"0-30:Currency:100", "30-60:Currency:100", "60-90:Currency:100", "90-Above:Currency:100"
|
||||
return [_("Journal Voucher") + ":Link/Journal Voucher:140", _("Account") + ":Link/Account:140",
|
||||
_("Posting Date") + ":Date:100", _("Against Invoice") + ":Link/Purchase Invoice:130",
|
||||
_("Against Invoice Posting Date") + ":Date:130", _("Debit") + ":Currency:120", _("Credit") + ":Currency:120",
|
||||
_("Reference No") + "::100", _("Reference Date") + ":Date:100", _("Remarks") + "::150", _("Age") +":Int:40",
|
||||
"0-30:Currency:100", "30-60:Currency:100", "60-90:Currency:100", _("90-Above") + ":Currency:100"
|
||||
]
|
||||
|
||||
def get_conditions(filters):
|
||||
|
||||
@@ -63,11 +63,11 @@ def execute(filters=None):
|
||||
def get_columns(invoice_list):
|
||||
"""return columns based on filters"""
|
||||
columns = [
|
||||
"Invoice:Link/Purchase Invoice:120", "Posting Date:Date:80", "Supplier Id::120",
|
||||
"Supplier Name::120", "Supplier Account:Link/Account:120",
|
||||
"Account Group:LInk/Account:120", "Project:Link/Project:80", "Bill No::120",
|
||||
"Bill Date:Date:80", "Remarks::150",
|
||||
"Purchase Order:Link/Purchase Order:100", "Purchase Receipt:Link/Purchase Receipt:100"
|
||||
_("Invoice") + ":Link/Purchase Invoice:120", _("Posting Date") + ":Date:80", _("Supplier Id") + "::120",
|
||||
_("Supplier Name") + "::120", _("Supplier Account") + ":Link/Account:120",
|
||||
_("Account Group") + ":Link/Account:120", _("Project") + ":Link/Project:80", _("Bill No") + "::120",
|
||||
_("Bill Date") + ":Date:80", _("Remarks") + "::150",
|
||||
_("Purchase Order") + ":Link/Purchase Order:100", _("Purchase Receipt") + ":Link/Purchase Receipt:100"
|
||||
]
|
||||
expense_accounts = tax_accounts = expense_columns = tax_columns = []
|
||||
|
||||
|
||||
@@ -11,4 +11,4 @@ def execute(filters=None):
|
||||
conditions = get_columns(filters, "Sales Invoice")
|
||||
data = get_data(filters, conditions)
|
||||
|
||||
return conditions["columns"], data
|
||||
return conditions["columns"], data
|
||||
|
||||
@@ -63,10 +63,10 @@ def execute(filters=None):
|
||||
def get_columns(invoice_list):
|
||||
"""return columns based on filters"""
|
||||
columns = [
|
||||
"Invoice:Link/Sales Invoice:120", "Posting Date:Date:80", "Customer Id::120",
|
||||
"Customer Name::120", "Customer Account:Link/Account:120", "Account Group:LInk/Account:120",
|
||||
"Territory:Link/Territory:80", "Project:Link/Project:80", "Remarks::150",
|
||||
"Sales Order:Link/Sales Order:100", "Delivery Note:Link/Delivery Note:100"
|
||||
_("Invoice") + ":Link/Sales Invoice:120", _("Posting Date") + ":Date:80", _("Customer Id") + "::120",
|
||||
_("Customer Name") + "::120", _("Customer Account") + ":Link/Account:120", _("Account Group") + ":Link/Account:120",
|
||||
_("Territory") + ":Link/Territory:80", _("Project") + ":Link/Project:80", _("Remarks") + "::150",
|
||||
_("Sales Order") + ":Link/Sales Order:100", _("Delivery Note") + ":Link/Delivery Note:100"
|
||||
]
|
||||
|
||||
income_accounts = tax_accounts = income_columns = tax_columns = []
|
||||
|
||||
@@ -28,7 +28,7 @@ def get_fiscal_years(date=None, fiscal_year=None, label="Date", verbose=1):
|
||||
from `tabFiscal Year` where %s order by year_start_date desc""" % cond)
|
||||
|
||||
if not fy:
|
||||
error_msg = _("""{0} {1} not in any Fiscal Year""").format(label, formatdate(date))
|
||||
error_msg = _("""{0} {1} not in any Fiscal Year. For more details check {2}.""").format(label, formatdate(date), "https://erpnext.com/kb/accounts/fiscal-year-error")
|
||||
if verbose: frappe.msgprint(error_msg)
|
||||
raise FiscalYearError, error_msg
|
||||
|
||||
@@ -391,3 +391,42 @@ def get_stock_rbnb_difference(posting_date, company):
|
||||
|
||||
# Amount should be credited
|
||||
return flt(stock_rbnb) + flt(sys_bal)
|
||||
|
||||
def get_outstanding_invoices(amount_query, account):
|
||||
all_outstanding_vouchers = []
|
||||
outstanding_voucher_list = frappe.db.sql("""
|
||||
select
|
||||
voucher_no, voucher_type, posting_date,
|
||||
ifnull(sum({amount_query}), 0) as invoice_amount
|
||||
from
|
||||
`tabGL Entry`
|
||||
where
|
||||
account = %s and {amount_query} > 0
|
||||
group by voucher_type, voucher_no
|
||||
""".format(amount_query = amount_query), account, as_dict = True)
|
||||
|
||||
for d in outstanding_voucher_list:
|
||||
payment_amount = frappe.db.sql("""
|
||||
select ifnull(sum(ifnull({amount_query}, 0)), 0)
|
||||
from
|
||||
`tabGL Entry`
|
||||
where
|
||||
account = %s and {amount_query} < 0
|
||||
and against_voucher_type = %s and ifnull(against_voucher, '') = %s
|
||||
""".format(**{
|
||||
"amount_query": amount_query
|
||||
}), (account, d.voucher_type, d.voucher_no))
|
||||
|
||||
payment_amount = -1*payment_amount[0][0] if payment_amount else 0
|
||||
|
||||
if d.invoice_amount > payment_amount:
|
||||
|
||||
all_outstanding_vouchers.append({
|
||||
'voucher_no': d.voucher_no,
|
||||
'voucher_type': d.voucher_type,
|
||||
'posting_date': d.posting_date,
|
||||
'invoice_amount': flt(d.invoice_amount),
|
||||
'outstanding_amount': d.invoice_amount - payment_amount
|
||||
})
|
||||
|
||||
return all_outstanding_vouchers
|
||||
|
||||
@@ -71,7 +71,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
|
||||
},
|
||||
|
||||
contact_person: function() {
|
||||
this.supplier_address();
|
||||
erpnext.utils.get_contact_details(this.frm);
|
||||
},
|
||||
|
||||
buying_price_list: function() {
|
||||
@@ -209,24 +209,17 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
|
||||
|
||||
calculate_totals: function() {
|
||||
var tax_count = this.frm.tax_doclist.length;
|
||||
this.frm.doc.grand_total = flt(tax_count ?
|
||||
this.frm.tax_doclist[tax_count - 1].total : this.frm.doc.net_total,
|
||||
precision("grand_total"));
|
||||
this.frm.doc.grand_total_import = flt(this.frm.doc.grand_total /
|
||||
this.frm.doc.conversion_rate, precision("grand_total_import"));
|
||||
this.frm.doc.grand_total = flt(tax_count ? this.frm.tax_doclist[tax_count - 1].total : this.frm.doc.net_total);
|
||||
|
||||
this.frm.doc.total_tax = flt(this.frm.doc.grand_total - this.frm.doc.net_total,
|
||||
precision("total_tax"));
|
||||
this.frm.doc.total_tax = flt(this.frm.doc.grand_total - this.frm.doc.net_total, precision("total_tax"));
|
||||
|
||||
this.frm.doc.grand_total = flt(this.frm.doc.grand_total, precision("grand_total"));
|
||||
|
||||
// rounded totals
|
||||
if(frappe.meta.get_docfield(this.frm.doc.doctype, "rounded_total", this.frm.doc.name)) {
|
||||
this.frm.doc.rounded_total = Math.round(this.frm.doc.grand_total);
|
||||
}
|
||||
|
||||
if(frappe.meta.get_docfield(this.frm.doc.doctype, "rounded_total_import", this.frm.doc.name)) {
|
||||
this.frm.doc.rounded_total_import = Math.round(this.frm.doc.grand_total_import);
|
||||
}
|
||||
|
||||
// other charges added/deducted
|
||||
this.frm.doc.other_charges_added = 0.0
|
||||
this.frm.doc.other_charges_deducted = 0.0
|
||||
@@ -244,6 +237,16 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
|
||||
frappe.model.round_floats_in(this.frm.doc,
|
||||
["other_charges_added", "other_charges_deducted"]);
|
||||
}
|
||||
|
||||
this.frm.doc.grand_total_import = flt((this.frm.doc.other_charges_added || this.frm.doc.other_charges_deducted) ?
|
||||
flt(this.frm.doc.grand_total / this.frm.doc.conversion_rate) : this.frm.doc.net_total_import);
|
||||
|
||||
this.frm.doc.grand_total_import = flt(this.frm.doc.grand_total_import, precision("grand_total_import"));
|
||||
|
||||
if(frappe.meta.get_docfield(this.frm.doc.doctype, "rounded_total_import", this.frm.doc.name)) {
|
||||
this.frm.doc.rounded_total_import = Math.round(this.frm.doc.grand_total_import);
|
||||
}
|
||||
|
||||
this.frm.doc.other_charges_added_import = flt(this.frm.doc.other_charges_added /
|
||||
this.frm.doc.conversion_rate, precision("other_charges_added_import"));
|
||||
this.frm.doc.other_charges_deducted_import = flt(this.frm.doc.other_charges_deducted /
|
||||
|
||||
@@ -3,15 +3,13 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
from frappe.utils import cstr, flt
|
||||
from frappe.utils import flt
|
||||
from frappe import _
|
||||
|
||||
from erpnext.stock.doctype.item.item import get_last_purchase_details
|
||||
from erpnext.controllers.buying_controller import BuyingController
|
||||
|
||||
class PurchaseCommon(BuyingController):
|
||||
|
||||
def update_last_purchase_rate(self, obj, is_submit):
|
||||
"""updates last_purchase_rate in item table for each item"""
|
||||
|
||||
@@ -123,27 +121,6 @@ class PurchaseCommon(BuyingController):
|
||||
else:
|
||||
chk_dupl_itm.append(f)
|
||||
|
||||
def get_qty(self, curr_doctype, ref_tab_fname, ref_tab_dn, ref_doc_tname, transaction, curr_parent_name):
|
||||
# Get total Quantities of current doctype (eg. PR) except for qty of this transaction
|
||||
#------------------------------
|
||||
# please check as UOM changes from Material Request - Purchase Order ,so doing following else uom should be same .
|
||||
# i.e. in PO uom is NOS then in PR uom should be NOS
|
||||
# but if in Material Request uom KG it can change in PO
|
||||
|
||||
get_qty = (transaction == 'Material Request - Purchase Order') and 'qty * conversion_factor' or 'qty'
|
||||
qty = frappe.db.sql("""select sum(%s) from `tab%s` where %s = %s and
|
||||
docstatus = 1 and parent != %s""" % (get_qty, curr_doctype, ref_tab_fname, '%s', '%s'),
|
||||
(ref_tab_dn, curr_parent_name))
|
||||
qty = qty and flt(qty[0][0]) or 0
|
||||
|
||||
# get total qty of ref doctype
|
||||
#--------------------
|
||||
max_qty = frappe.db.sql("""select qty from `tab%s` where name = %s
|
||||
and docstatus = 1""" % (ref_doc_tname, '%s'), ref_tab_dn)
|
||||
max_qty = max_qty and flt(max_qty[0][0]) or 0
|
||||
|
||||
return cstr(qty)+'~~~'+cstr(max_qty)
|
||||
|
||||
def check_for_stopped_status(self, doctype, docname):
|
||||
stopped = frappe.db.sql("""select name from `tab%s` where name = %s and
|
||||
status = 'Stopped'""" % (doctype, '%s'), docname)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"allow_import": 1,
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-05-21 16:16:39",
|
||||
"docstatus": 0,
|
||||
@@ -385,6 +385,14 @@
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "advance_paid",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Advance Paid",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break4",
|
||||
"fieldtype": "Column Break",
|
||||
@@ -644,12 +652,130 @@
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "recurring_order",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Recurring Order",
|
||||
"options": "icon-time",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break",
|
||||
"fieldtype": "Column Break",
|
||||
"label": "Column Break",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.docstatus<2",
|
||||
"description": "Check if recurring order, uncheck to stop recurring or put proper End Date",
|
||||
"fieldname": "is_recurring",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Recurring",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"fieldname": "recurring_type",
|
||||
"fieldtype": "Select",
|
||||
"label": "Recurring Type",
|
||||
"no_copy": 1,
|
||||
"options": "Monthly\nQuarterly\nHalf-yearly\nYearly",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "Start date of current order's period",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "From Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "End date of current order's period",
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "To Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The day of the month on which auto order will be generated e.g. 05, 28 etc",
|
||||
"fieldname": "repeat_on_day_of_month",
|
||||
"fieldtype": "Int",
|
||||
"label": "Repeat on Day of Month",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The date on which recurring order will be stop",
|
||||
"fieldname": "end_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "End Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break83",
|
||||
"fieldtype": "Column Break",
|
||||
"label": "Column Break",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The date on which next invoice will be generated. It is generated on submit.",
|
||||
"fieldname": "next_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Next Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"fieldname": "recurring_id",
|
||||
"fieldtype": "Data",
|
||||
"label": "Recurring Id",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "Enter email id separated by commas, order will be mailed automatically on particular date",
|
||||
"fieldname": "notification_email_address",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Notification Email Address",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1
|
||||
}
|
||||
],
|
||||
"icon": "icon-file-text",
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"modified": "2014-08-12 05:22:53.496614",
|
||||
"modified": "2014-11-27 17:27:38.839440",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Purchase Order",
|
||||
|
||||
@@ -96,50 +96,45 @@ class PurchaseOrder(BuyingController):
|
||||
check_list.append(d.prevdoc_docname)
|
||||
pc_obj.check_for_stopped_status( d.prevdoc_doctype, d.prevdoc_docname)
|
||||
|
||||
def update_requested_qty(self):
|
||||
material_request_map = {}
|
||||
for d in self.get("po_details"):
|
||||
if d.prevdoc_doctype and d.prevdoc_doctype == "Material Request" and d.prevdoc_detail_docname:
|
||||
material_request_map.setdefault(d.prevdoc_docname, []).append(d.prevdoc_detail_docname)
|
||||
|
||||
def update_bin(self, is_submit, is_stopped = 0):
|
||||
from erpnext.stock.utils import update_bin
|
||||
pc_obj = frappe.get_doc('Purchase Common')
|
||||
for d in self.get('po_details'):
|
||||
#1. Check if is_stock_item == 'Yes'
|
||||
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == "Yes":
|
||||
# this happens when item is changed from non-stock to stock item
|
||||
if not d.warehouse:
|
||||
continue
|
||||
for mr, mr_item_rows in material_request_map.items():
|
||||
if mr and mr_item_rows:
|
||||
mr_obj = frappe.get_doc("Material Request", mr)
|
||||
|
||||
ind_qty, po_qty = 0, flt(d.qty) * flt(d.conversion_factor)
|
||||
if is_stopped:
|
||||
po_qty = flt(d.qty) > flt(d.received_qty) and \
|
||||
flt( flt(flt(d.qty) - flt(d.received_qty))*flt(d.conversion_factor)) or 0
|
||||
if mr_obj.status in ["Stopped", "Cancelled"]:
|
||||
frappe.throw(_("Material Request {0} is cancelled or stopped").format(mr), frappe.InvalidStatusError)
|
||||
|
||||
# No updates in Material Request on Stop / Unstop
|
||||
if cstr(d.prevdoc_doctype) == 'Material Request' and not is_stopped:
|
||||
# get qty and pending_qty of prevdoc
|
||||
curr_ref_qty = pc_obj.get_qty(d.doctype, 'prevdoc_detail_docname',
|
||||
d.prevdoc_detail_docname, 'Material Request Item',
|
||||
'Material Request - Purchase Order', self.name)
|
||||
max_qty, qty, curr_qty = flt(curr_ref_qty.split('~~~')[1]), \
|
||||
flt(curr_ref_qty.split('~~~')[0]), 0
|
||||
mr_obj.update_requested_qty(mr_item_rows)
|
||||
|
||||
if flt(qty) + flt(po_qty) > flt(max_qty):
|
||||
curr_qty = flt(max_qty) - flt(qty)
|
||||
# special case as there is no restriction
|
||||
# for Material Request - Purchase Order
|
||||
curr_qty = curr_qty > 0 and curr_qty or 0
|
||||
else:
|
||||
curr_qty = flt(po_qty)
|
||||
def update_ordered_qty(self, po_item_rows=None):
|
||||
"""update requested qty (before ordered_qty is updated)"""
|
||||
from erpnext.stock.utils import get_bin
|
||||
|
||||
ind_qty = -flt(curr_qty)
|
||||
def _update_ordered_qty(item_code, warehouse):
|
||||
ordered_qty = frappe.db.sql("""
|
||||
select sum((po_item.qty - ifnull(po_item.received_qty, 0))*po_item.conversion_factor)
|
||||
from `tabPurchase Order Item` po_item, `tabPurchase Order` po
|
||||
where po_item.item_code=%s and po_item.warehouse=%s
|
||||
and po_item.qty > ifnull(po_item.received_qty, 0) and po_item.parent=po.name
|
||||
and po.status!='Stopped' and po.docstatus=1""", (item_code, warehouse))
|
||||
|
||||
# Update ordered_qty and indented_qty in bin
|
||||
args = {
|
||||
"item_code": d.item_code,
|
||||
"warehouse": d.warehouse,
|
||||
"ordered_qty": (is_submit and 1 or -1) * flt(po_qty),
|
||||
"indented_qty": (is_submit and 1 or -1) * flt(ind_qty),
|
||||
"posting_date": self.transaction_date
|
||||
}
|
||||
update_bin(args)
|
||||
bin_doc = get_bin(item_code, warehouse)
|
||||
bin_doc.ordered_qty = flt(ordered_qty[0][0]) if ordered_qty else 0
|
||||
bin_doc.save()
|
||||
|
||||
item_wh_list = []
|
||||
for d in self.get("po_details"):
|
||||
if (not po_item_rows or d.name in po_item_rows) and [d.item_code, d.warehouse] not in item_wh_list \
|
||||
and frappe.db.get_value("Item", d.item_code, "is_stock_item") == "Yes" and d.warehouse:
|
||||
item_wh_list.append([d.item_code, d.warehouse])
|
||||
|
||||
for item_code, warehouse in item_wh_list:
|
||||
_update_ordered_qty(item_code, warehouse)
|
||||
|
||||
def check_modified_date(self):
|
||||
mod_db = frappe.db.sql("select modified from `tabPurchase Order` where name = %s",
|
||||
@@ -152,20 +147,21 @@ class PurchaseOrder(BuyingController):
|
||||
|
||||
def update_status(self, status):
|
||||
self.check_modified_date()
|
||||
# step 1:=> Set Status
|
||||
frappe.db.set(self,'status',cstr(status))
|
||||
|
||||
# step 2:=> Update Bin
|
||||
self.update_bin(is_submit = (status == 'Submitted') and 1 or 0, is_stopped = 1)
|
||||
self.update_requested_qty()
|
||||
self.update_ordered_qty()
|
||||
|
||||
# step 3:=> Acknowledge user
|
||||
msgprint(_("Status of {0} {1} is now {2}").format(self.doctype, self.name, status))
|
||||
|
||||
def on_submit(self):
|
||||
super(PurchaseOrder, self).on_submit()
|
||||
|
||||
purchase_controller = frappe.get_doc("Purchase Common")
|
||||
|
||||
self.update_prevdoc_status()
|
||||
self.update_bin(is_submit = 1, is_stopped = 0)
|
||||
self.update_requested_qty()
|
||||
self.update_ordered_qty()
|
||||
|
||||
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
|
||||
self.company, self.grand_total)
|
||||
@@ -190,8 +186,13 @@ class PurchaseOrder(BuyingController):
|
||||
throw(_("Purchase Invoice {0} is already submitted").format(", ".join(submitted)))
|
||||
|
||||
frappe.db.set(self,'status','Cancelled')
|
||||
|
||||
self.update_prevdoc_status()
|
||||
self.update_bin( is_submit = 0, is_stopped = 0)
|
||||
|
||||
# Must be called after updating ordered qty in Material Request
|
||||
self.update_requested_qty()
|
||||
self.update_ordered_qty()
|
||||
|
||||
pc_obj.update_last_purchase_rate(self, is_submit = 0)
|
||||
|
||||
def on_update(self):
|
||||
@@ -238,10 +239,15 @@ def make_purchase_receipt(source_name, target_doc=None):
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_purchase_invoice(source_name, target_doc=None):
|
||||
def postprocess(source, target):
|
||||
set_missing_values(source, target)
|
||||
#Get the advance paid Journal Vouchers in Purchase Invoice Advance
|
||||
target.get_advances()
|
||||
|
||||
def update_item(obj, target, source_parent):
|
||||
target.amount = flt(obj.amount) - flt(obj.billed_amt)
|
||||
target.base_amount = target.amount * flt(source_parent.conversion_rate)
|
||||
target.qty = target.amount / flt(obj.rate) if flt(obj.rate) else flt(obj.qty)
|
||||
target.qty = target.amount / flt(obj.rate) if (flt(obj.rate) and flt(obj.billed_amt)) else flt(obj.qty)
|
||||
|
||||
doc = get_mapped_doc("Purchase Order", source_name, {
|
||||
"Purchase Order": {
|
||||
@@ -263,6 +269,6 @@ def make_purchase_invoice(source_name, target_doc=None):
|
||||
"doctype": "Purchase Taxes and Charges",
|
||||
"add_if_empty": True
|
||||
}
|
||||
}, target_doc, set_missing_values)
|
||||
}, target_doc, postprocess)
|
||||
|
||||
return doc
|
||||
|
||||
@@ -29,8 +29,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
frappe.get_doc(pr).insert()
|
||||
|
||||
def test_ordered_qty(self):
|
||||
frappe.db.sql("delete from tabBin")
|
||||
|
||||
existing_ordered_qty = self._get_ordered_qty("_Test Item", "_Test Warehouse - _TC")
|
||||
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
|
||||
|
||||
po = frappe.copy_doc(test_records[0]).insert()
|
||||
@@ -43,8 +42,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
po.get("po_details")[0].item_code = "_Test Item"
|
||||
po.submit()
|
||||
|
||||
self.assertEquals(frappe.db.get_value("Bin", {"item_code": "_Test Item",
|
||||
"warehouse": "_Test Warehouse - _TC"}, "ordered_qty"), 10)
|
||||
self.assertEquals(self._get_ordered_qty("_Test Item", "_Test Warehouse - _TC"), existing_ordered_qty + 10)
|
||||
|
||||
pr = make_purchase_receipt(po.name)
|
||||
|
||||
@@ -56,8 +54,9 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
pr.insert()
|
||||
pr.submit()
|
||||
|
||||
self.assertEquals(flt(frappe.db.get_value("Bin", {"item_code": "_Test Item",
|
||||
"warehouse": "_Test Warehouse - _TC"}, "ordered_qty")), 6.0)
|
||||
po.load_from_db()
|
||||
self.assertEquals(po.get("po_details")[0].received_qty, 4)
|
||||
self.assertEquals(self._get_ordered_qty("_Test Item", "_Test Warehouse - _TC"), existing_ordered_qty + 6)
|
||||
|
||||
frappe.db.set_value('Item', '_Test Item', 'tolerance', 50)
|
||||
|
||||
@@ -68,8 +67,16 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
pr1.insert()
|
||||
pr1.submit()
|
||||
|
||||
self.assertEquals(flt(frappe.db.get_value("Bin", {"item_code": "_Test Item",
|
||||
"warehouse": "_Test Warehouse - _TC"}, "ordered_qty")), 0.0)
|
||||
po.load_from_db()
|
||||
self.assertEquals(po.get("po_details")[0].received_qty, 12)
|
||||
self.assertEquals(self._get_ordered_qty("_Test Item", "_Test Warehouse - _TC"), existing_ordered_qty)
|
||||
|
||||
pr1.load_from_db()
|
||||
pr1.cancel()
|
||||
|
||||
po.load_from_db()
|
||||
self.assertEquals(po.get("po_details")[0].received_qty, 4)
|
||||
self.assertEquals(self._get_ordered_qty("_Test Item", "_Test Warehouse - _TC"), existing_ordered_qty + 6)
|
||||
|
||||
def test_make_purchase_invoice(self):
|
||||
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_invoice
|
||||
@@ -107,7 +114,14 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
po.get("po_details")[0].qty = 3.4
|
||||
self.assertRaises(UOMMustBeIntegerError, po.insert)
|
||||
|
||||
def test_recurring_order(self):
|
||||
from erpnext.controllers.tests.test_recurring_document import test_recurring_document
|
||||
test_recurring_document(self, test_records)
|
||||
|
||||
test_dependencies = ["BOM"]
|
||||
def _get_ordered_qty(self, item_code, warehouse):
|
||||
return flt(frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "ordered_qty"))
|
||||
|
||||
|
||||
test_dependencies = ["BOM", "Item Price"]
|
||||
|
||||
test_records = frappe.get_test_records('Purchase Order')
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
[
|
||||
{
|
||||
"advance_paid": 0.0,
|
||||
"buying_price_list": "_Test Price List",
|
||||
"company": "_Test Company",
|
||||
"conversion_rate": 1.0,
|
||||
@@ -31,5 +32,39 @@
|
||||
"supplier": "_Test Supplier",
|
||||
"supplier_name": "_Test Supplier",
|
||||
"transaction_date": "2013-02-12"
|
||||
},
|
||||
{
|
||||
"advance_paid": 0.0,
|
||||
"buying_price_list": "_Test Price List",
|
||||
"company": "_Test Company",
|
||||
"conversion_rate": 1.0,
|
||||
"currency": "INR",
|
||||
"doctype": "Purchase Order",
|
||||
"fiscal_year": "_Test Fiscal Year 2013",
|
||||
"grand_total": 5000.0,
|
||||
"grand_total_import": 5000.0,
|
||||
"is_subcontracted": "No",
|
||||
"naming_series": "_T-Purchase Order-",
|
||||
"net_total": 5000.0,
|
||||
"po_details": [
|
||||
{
|
||||
"base_amount": 5000.0,
|
||||
"conversion_factor": 1.0,
|
||||
"description": "_Test Item",
|
||||
"doctype": "Purchase Order Item",
|
||||
"item_code": "_Test Item",
|
||||
"item_name": "_Test Item",
|
||||
"parentfield": "po_details",
|
||||
"qty": 10.0,
|
||||
"rate": 500.0,
|
||||
"schedule_date": "2013-03-01",
|
||||
"stock_uom": "_Test UOM",
|
||||
"uom": "_Test UOM",
|
||||
"warehouse": "_Test Warehouse - _TC"
|
||||
}
|
||||
],
|
||||
"supplier": "_Test Supplier",
|
||||
"supplier_name": "_Test Supplier",
|
||||
"transaction_date": "2013-02-12"
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -445,7 +445,7 @@
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"modified": "2014-09-08 08:06:30.684601",
|
||||
"modified": "2014-09-09 05:35:36.346557",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Purchase Order Item",
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
{% include 'setup/doctype/contact_control/contact_control.js' %};
|
||||
|
||||
cur_frm.cscript.refresh = function(doc, dt, dn) {
|
||||
cur_frm.cscript.make_dashboard(doc);
|
||||
|
||||
@@ -17,15 +15,13 @@ cur_frm.cscript.refresh = function(doc, dt, dn) {
|
||||
}
|
||||
else{
|
||||
unhide_field(['address_html','contact_html']);
|
||||
// make lists
|
||||
cur_frm.cscript.make_address(doc,dt,dn);
|
||||
cur_frm.cscript.make_contact(doc,dt,dn);
|
||||
|
||||
erpnext.utils.render_address_and_contact(cur_frm)
|
||||
|
||||
cur_frm.communication_view = new frappe.views.CommunicationList({
|
||||
list: frappe.get_list("Communication", {"supplier": doc.name}),
|
||||
parent: cur_frm.fields_dict.communication_html.wrapper,
|
||||
doc: doc
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,45 +57,6 @@ cur_frm.cscript.make_dashboard = function(doc) {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
cur_frm.cscript.make_address = function() {
|
||||
if(!cur_frm.address_list) {
|
||||
cur_frm.address_list = new frappe.ui.Listing({
|
||||
parent: cur_frm.fields_dict['address_html'].wrapper,
|
||||
page_length: 5,
|
||||
new_doctype: "Address",
|
||||
get_query: function() {
|
||||
return "select name, address_type, address_line1, address_line2, city, state, country, pincode, fax, email_id, phone, is_primary_address, is_shipping_address from tabAddress where supplier='" +
|
||||
cur_frm.doc.name.replace(/'/g, "\\'") + "' and docstatus != 2 order by is_primary_address desc"
|
||||
},
|
||||
as_dict: 1,
|
||||
no_results_message: __('No addresses created'),
|
||||
render_row: cur_frm.cscript.render_address_row,
|
||||
});
|
||||
// note: render_address_row is defined in contact_control.js
|
||||
}
|
||||
cur_frm.address_list.run();
|
||||
}
|
||||
|
||||
cur_frm.cscript.make_contact = function() {
|
||||
if(!cur_frm.contact_list) {
|
||||
cur_frm.contact_list = new frappe.ui.Listing({
|
||||
parent: cur_frm.fields_dict['contact_html'].wrapper,
|
||||
page_length: 5,
|
||||
new_doctype: "Contact",
|
||||
get_query: function() {
|
||||
return "select name, first_name, last_name, email_id, phone, mobile_no, department, designation, is_primary_contact from tabContact where supplier='" +
|
||||
cur_frm.doc.name.replace(/'/g, "\\'") + "' and docstatus != 2 order by is_primary_contact desc"
|
||||
},
|
||||
as_dict: 1,
|
||||
no_results_message: __('No contacts created'),
|
||||
render_row: cur_frm.cscript.render_contact_row,
|
||||
});
|
||||
// note: render_contact_row is defined in contact_control.js
|
||||
}
|
||||
cur_frm.contact_list.run();
|
||||
}
|
||||
|
||||
cur_frm.fields_dict['default_price_list'].get_query = function(doc, cdt, cdn) {
|
||||
return{
|
||||
filters:{'buying': 1}
|
||||
|
||||
@@ -186,12 +186,20 @@
|
||||
],
|
||||
"icon": "icon-user",
|
||||
"idx": 1,
|
||||
"modified": "2014-08-26 04:55:32.004458",
|
||||
"modified": "2014-09-10 17:53:09.286715",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Supplier",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"email": 1,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Purchase User"
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"create": 0,
|
||||
@@ -201,7 +209,7 @@
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Purchase User",
|
||||
"role": "Purchase Manager",
|
||||
"submit": 0,
|
||||
"write": 0
|
||||
},
|
||||
@@ -224,11 +232,27 @@
|
||||
"read": 1,
|
||||
"role": "Material User"
|
||||
},
|
||||
{
|
||||
"email": 1,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Material Manager"
|
||||
},
|
||||
{
|
||||
"apply_user_permissions": 1,
|
||||
"permlevel": 0,
|
||||
"read": 1,
|
||||
"role": "Accounts User"
|
||||
},
|
||||
{
|
||||
"email": 1,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts Manager"
|
||||
}
|
||||
],
|
||||
"search_fields": "supplier_name,supplier_type",
|
||||
|
||||
@@ -9,10 +9,14 @@ from frappe.utils import cint
|
||||
from frappe import msgprint, _
|
||||
from frappe.model.naming import make_autoname
|
||||
from erpnext.accounts.party import create_party_account
|
||||
from erpnext.utilities.address_and_contact import load_address_and_contact
|
||||
|
||||
from erpnext.utilities.transaction_base import TransactionBase
|
||||
|
||||
class Supplier(TransactionBase):
|
||||
def onload(self):
|
||||
"""Load address and contacts in `__onload`"""
|
||||
load_address_and_contact(self, "supplier")
|
||||
|
||||
def autoname(self):
|
||||
supp_master_name = frappe.defaults.get_global_default('supp_master_name')
|
||||
|
||||
@@ -4,5 +4,11 @@
|
||||
"doctype": "Supplier",
|
||||
"supplier_name": "_Test Supplier",
|
||||
"supplier_type": "_Test Supplier Type"
|
||||
},
|
||||
{
|
||||
"company": "_Test Company",
|
||||
"doctype": "Supplier",
|
||||
"supplier_name": "_Test Supplier 1",
|
||||
"supplier_type": "_Test Supplier Type"
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -331,7 +331,7 @@
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"modified": "2014-09-08 08:06:30.976906",
|
||||
"modified": "2014-09-09 05:35:36.623995",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Supplier Quotation Item",
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
"doctype": "Report",
|
||||
"idx": 1,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2014-06-03 07:18:17.358554",
|
||||
"modified_by": "Administrator",
|
||||
"modified": "2014-09-11 08:53:17.358554",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Supplier Addresses and Contacts",
|
||||
"owner": "Administrator",
|
||||
"query": "SELECT\n `tabSupplier`.name as \"Supplier:Link/Supplier:120\",\n\t`tabSupplier`.supplier_name as \"Supplier Name::120\",\n\t`tabSupplier`.supplier_type as \"Supplier Type:Link/Supplier Type:120\",\n\tconcat_ws(', ', \n\t\ttrim(',' from `tabAddress`.address_line1), \n\t\ttrim(',' from tabAddress.address_line2), \n\t\ttabAddress.state, tabAddress.pincode, tabAddress.country\n\t) as 'Address::180',\n concat_ws(', ', `tabContact`.first_name, `tabContact`.last_name) as 'Contact Name::180',\n\t`tabContact`.phone as \"Phone\",\n\t`tabContact`.mobile_no as \"Mobile No\",\n\t`tabContact`.email_id as \"Email Id::120\",\n\t`tabContact`.is_primary_contact as \"Is Primary Contact::120\"\nFROM\n\t`tabSupplier`\n\tleft join `tabAddress` on (\n\t\t`tabAddress`.supplier=`tabSupplier`.name\n\t)\n\tleft join `tabContact` on (\n\t\t`tabContact`.supplier=`tabSupplier`.name\n\t)\nWHERE\n\t`tabSupplier`.docstatus<2\nORDER BY\n\t`tabSupplier`.name asc",
|
||||
"query": "SELECT\n `tabSupplier`.name as \"Supplier:Link/Supplier:120\",\n\t`tabSupplier`.supplier_name as \"Supplier Name::120\",\n\t`tabSupplier`.supplier_type as \"Supplier Type:Link/Supplier Type:120\",\n\tconcat_ws(', ', \n\t\ttrim(',' from `tabAddress`.address_line1), \n\t\ttrim(',' from tabAddress.address_line2), \n\t\ttabAddress.state, tabAddress.pincode, tabAddress.country\n\t) as 'Address::180',\n concat_ws(', ', `tabContact`.first_name, `tabContact`.last_name) as \"Contact Name::180\",\n\t`tabContact`.phone as \"Phone\",\n\t`tabContact`.mobile_no as \"Mobile No\",\n\t`tabContact`.email_id as \"Email Id::120\",\n\t`tabContact`.is_primary_contact as \"Is Primary Contact::120\"\nFROM\n\t`tabSupplier`\n\tleft join `tabAddress` on (\n\t\t`tabAddress`.supplier=`tabSupplier`.name\n\t)\n\tleft join `tabContact` on (\n\t\t`tabContact`.supplier=`tabSupplier`.name\n\t)\nWHERE\n\t`tabSupplier`.docstatus<2\nORDER BY\n\t`tabSupplier`.name asc",
|
||||
"ref_doctype": "Supplier",
|
||||
"report_name": "Supplier Addresses and Contacts",
|
||||
"report_type": "Query Report"
|
||||
|
||||
@@ -61,6 +61,11 @@ def get_data():
|
||||
"name": "Period Closing Voucher",
|
||||
"description": _("Close Balance Sheet and book Profit or Loss.")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Payment Tool",
|
||||
"description": _("Create Payment Entries against Orders or Invoices.")
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -135,12 +135,6 @@ def get_data():
|
||||
"name": "Item-wise Purchase History",
|
||||
"doctype": "Item"
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"is_query_report": True,
|
||||
"name": "Item-wise Last Purchase Rate",
|
||||
"doctype": "Item"
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"is_query_report": True,
|
||||
|
||||
@@ -26,6 +26,11 @@ def get_data():
|
||||
"name": "Purchase Receipt",
|
||||
"description": _("Goods received from Suppliers."),
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Installation Note",
|
||||
"description": _("Installation record for a Serial No.")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Item",
|
||||
@@ -57,11 +62,6 @@ def get_data():
|
||||
"name": "Stock Reconciliation",
|
||||
"description": _("Upload stock balance via csv.")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Installation Note",
|
||||
"description": _("Installation record for a Serial No.")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Packing Slip",
|
||||
@@ -142,10 +142,10 @@ def get_data():
|
||||
"doctype": "Item",
|
||||
},
|
||||
{
|
||||
"type": "page",
|
||||
"name": "stock-balance",
|
||||
"label": _("Stock Balance"),
|
||||
"icon": "icon-table",
|
||||
"type": "report",
|
||||
"is_query_report": True,
|
||||
"name": "Stock Balance",
|
||||
"doctype": "Warehouse"
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
@@ -170,7 +170,7 @@ def get_data():
|
||||
"name": "stock-analytics",
|
||||
"label": _("Stock Analytics"),
|
||||
"icon": "icon-bar-chart"
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -222,12 +222,6 @@ def get_data():
|
||||
"name": "Batch-Wise Balance History",
|
||||
"doctype": "Batch"
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"is_query_report": True,
|
||||
"name": "Warehouse-Wise Stock Balance",
|
||||
"doctype": "Warehouse"
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"is_query_report": True,
|
||||
|
||||
@@ -4,12 +4,11 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _, throw
|
||||
from frappe.utils import add_days, cint, cstr, today, date_diff, flt, getdate, nowdate, \
|
||||
get_first_day, get_last_day
|
||||
from frappe.model.naming import make_autoname
|
||||
from frappe.utils import cint, today, flt
|
||||
from erpnext.setup.utils import get_company_currency, get_exchange_rate
|
||||
from erpnext.accounts.utils import get_fiscal_year, validate_fiscal_year
|
||||
from erpnext.utilities.transaction_base import TransactionBase
|
||||
from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document
|
||||
import json
|
||||
|
||||
class AccountsController(TransactionBase):
|
||||
@@ -24,6 +23,24 @@ class AccountsController(TransactionBase):
|
||||
|
||||
self.validate_for_freezed_account()
|
||||
|
||||
if self.meta.get_field("is_recurring"):
|
||||
validate_recurring_document(self)
|
||||
|
||||
def on_submit(self):
|
||||
if self.meta.get_field("is_recurring"):
|
||||
convert_to_recurring(self, self.get("posting_date") or self.get("transaction_date"))
|
||||
|
||||
def on_update_after_submit(self):
|
||||
if self.meta.get_field("is_recurring"):
|
||||
validate_recurring_document(self)
|
||||
convert_to_recurring(self, self.get("posting_date") or self.get("transaction_date"))
|
||||
|
||||
def before_recurring(self):
|
||||
self.fiscal_year = None
|
||||
for fieldname in ("due_date", "aging_date"):
|
||||
if self.meta.get_field(fieldname):
|
||||
self.set(fieldname, None)
|
||||
|
||||
def set_missing_values(self, for_validate=False):
|
||||
for fieldname in ["posting_date", "transaction_date"]:
|
||||
if not self.get(fieldname) and self.meta.get_field(fieldname):
|
||||
@@ -102,6 +119,10 @@ class AccountsController(TransactionBase):
|
||||
item.get(fieldname) is None and value is not None:
|
||||
item.set(fieldname, value)
|
||||
|
||||
if fieldname == "cost_center" and item.meta.get_field("cost_center") \
|
||||
and not item.get("cost_center") and value is not None:
|
||||
item.set(fieldname, value)
|
||||
|
||||
if ret.get("pricing_rule"):
|
||||
for field in ["base_price_list_rate", "price_list_rate",
|
||||
"discount_percentage", "base_rate", "rate"]:
|
||||
@@ -274,7 +295,7 @@ class AccountsController(TransactionBase):
|
||||
self.precision("tax_amount", tax))
|
||||
|
||||
def adjust_discount_amount_loss(self, tax):
|
||||
discount_amount_loss = self.grand_total - flt(self.discount_amount) - tax.total
|
||||
discount_amount_loss = self.grand_total - flt(self.base_discount_amount) - tax.total
|
||||
tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount +
|
||||
discount_amount_loss, self.precision("tax_amount", tax))
|
||||
tax.total = flt(tax.total + discount_amount_loss, self.precision("total", tax))
|
||||
@@ -362,19 +383,29 @@ class AccountsController(TransactionBase):
|
||||
frappe.db.sql("""delete from `tab%s` where parentfield=%s and parent = %s
|
||||
and ifnull(allocated_amount, 0) = 0""" % (childtype, '%s', '%s'), (parentfield, self.name))
|
||||
|
||||
def get_advances(self, account_head, child_doctype, parentfield, dr_or_cr):
|
||||
def get_advances(self, account_head, child_doctype, parentfield, dr_or_cr, against_order_field):
|
||||
so_list = list(set([d.get(against_order_field) for d in self.get("entries") if d.get(against_order_field)]))
|
||||
cond = ""
|
||||
if so_list:
|
||||
cond = "or (ifnull(t2.%s, '') in (%s))" % ("against_" + against_order_field, ', '.join(['%s']*len(so_list)))
|
||||
|
||||
res = frappe.db.sql("""
|
||||
select
|
||||
t1.name as jv_no, t1.remark, t2.%s as amount, t2.name as jv_detail_no
|
||||
t1.name as jv_no, t1.remark, t2.%s as amount, t2.name as jv_detail_no, `against_%s` as against_order
|
||||
from
|
||||
`tabJournal Voucher` t1, `tabJournal Voucher Detail` t2
|
||||
where
|
||||
t1.name = t2.parent and t2.account = %s and t2.is_advance = 'Yes' and t1.docstatus = 1
|
||||
and ifnull(t2.against_voucher, '') = ''
|
||||
and ifnull(t2.against_invoice, '') = ''
|
||||
and ifnull(t2.against_jv, '') = ''
|
||||
and ((
|
||||
ifnull(t2.against_voucher, '') = ''
|
||||
and ifnull(t2.against_invoice, '') = ''
|
||||
and ifnull(t2.against_jv, '') = ''
|
||||
and ifnull(t2.against_sales_order, '') = ''
|
||||
and ifnull(t2.against_purchase_order, '') = ''
|
||||
) %s)
|
||||
order by t1.posting_date""" %
|
||||
(dr_or_cr, '%s'), account_head, as_dict=1)
|
||||
(dr_or_cr, against_order_field, '%s', cond),
|
||||
tuple([account_head] + so_list), as_dict= True)
|
||||
|
||||
self.set(parentfield, [])
|
||||
for d in res:
|
||||
@@ -384,9 +415,36 @@ class AccountsController(TransactionBase):
|
||||
"jv_detail_no": d.jv_detail_no,
|
||||
"remarks": d.remark,
|
||||
"advance_amount": flt(d.amount),
|
||||
"allocate_amount": 0
|
||||
"allocated_amount": flt(d.amount) if d.against_order else 0
|
||||
})
|
||||
|
||||
def validate_advance_jv(self, advance_table_fieldname, against_order_field):
|
||||
order_list = list(set([d.get(against_order_field) for d in self.get("entries") if d.get(against_order_field)]))
|
||||
if order_list:
|
||||
account = self.get("debit_to" if self.doctype=="Sales Invoice" else "credit_to")
|
||||
|
||||
jv_against_order = frappe.db.sql("""select parent, %s as against_order
|
||||
from `tabJournal Voucher Detail`
|
||||
where docstatus=1 and account=%s and ifnull(is_advance, 'No') = 'Yes'
|
||||
and ifnull(against_sales_order, '') in (%s)
|
||||
group by parent, against_sales_order""" %
|
||||
("against_" + against_order_field, '%s', ', '.join(['%s']*len(order_list))),
|
||||
tuple([account] + order_list), as_dict=1)
|
||||
|
||||
if jv_against_order:
|
||||
order_jv_map = {}
|
||||
for d in jv_against_order:
|
||||
order_jv_map.setdefault(d.against_order, []).append(d.parent)
|
||||
|
||||
advance_jv_against_si = [d.journal_voucher for d in self.get(advance_table_fieldname)]
|
||||
|
||||
for order, jv_list in order_jv_map.items():
|
||||
for jv in jv_list:
|
||||
if not advance_jv_against_si or jv not in advance_jv_against_si:
|
||||
frappe.msgprint(_("Journal Voucher {0} is linked against Order {1}, check if it should be pulled as advance in this invoice.")
|
||||
.format(jv, order))
|
||||
|
||||
|
||||
def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield):
|
||||
from erpnext.controllers.status_updater import get_tolerance_for
|
||||
item_tolerance = {}
|
||||
@@ -413,8 +471,7 @@ class AccountsController(TransactionBase):
|
||||
max_allowed_amt = flt(ref_amt * (100 + tolerance) / 100)
|
||||
|
||||
if total_billed_amt - max_allowed_amt > 0.01:
|
||||
reduce_by = total_billed_amt - max_allowed_amt
|
||||
frappe.throw(_("Cannot overbill for Item {0} in row {0} more than {1}. To allow overbilling, please set in Stock Settings").format(item.item_code, item.idx, max_allowed_amt))
|
||||
frappe.throw(_("Cannot overbill for Item {0} in row {1} more than {2}. To allow overbilling, please set in Stock Settings").format(item.item_code, item.idx, max_allowed_amt))
|
||||
|
||||
def get_company_default(self, fieldname):
|
||||
from erpnext.accounts.utils import get_company_default
|
||||
@@ -430,6 +487,32 @@ class AccountsController(TransactionBase):
|
||||
|
||||
return stock_items
|
||||
|
||||
def set_total_advance_paid(self):
|
||||
if self.doctype == "Sales Order":
|
||||
dr_or_cr = "credit"
|
||||
against_field = "against_sales_order"
|
||||
else:
|
||||
dr_or_cr = "debit"
|
||||
against_field = "against_purchase_order"
|
||||
|
||||
advance_paid = frappe.db.sql("""
|
||||
select
|
||||
sum(ifnull({dr_or_cr}, 0))
|
||||
from
|
||||
`tabJournal Voucher Detail`
|
||||
where
|
||||
{against_field} = %s and docstatus = 1 and is_advance = "Yes" """.format(dr_or_cr=dr_or_cr, \
|
||||
against_field=against_field), self.name)
|
||||
|
||||
if advance_paid:
|
||||
advance_paid = flt(advance_paid[0][0], self.precision("advance_paid"))
|
||||
if flt(self.grand_total) >= advance_paid:
|
||||
frappe.db.set_value(self.doctype, self.name, "advance_paid", advance_paid)
|
||||
else:
|
||||
frappe.throw(_("Total advance ({0}) against Order {1} cannot be greater \
|
||||
than the Grand Total ({2})")
|
||||
.format(advance_paid, self.name, self.grand_total))
|
||||
|
||||
@property
|
||||
def company_abbr(self):
|
||||
if not hasattr(self, "_abbr"):
|
||||
|
||||
@@ -5,6 +5,7 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _, msgprint
|
||||
from frappe.utils import flt, rounded
|
||||
|
||||
from erpnext.setup.utils import get_company_currency
|
||||
from erpnext.accounts.party import get_party_details
|
||||
|
||||
@@ -90,8 +91,7 @@ class BuyingController(StockController):
|
||||
item.rate = flt(item.price_list_rate * (1.0 - (item.discount_percentage / 100.0)),
|
||||
self.precision("rate", item))
|
||||
|
||||
item.amount = flt(item.rate * item.qty,
|
||||
self.precision("amount", item))
|
||||
item.amount = flt(item.rate * item.qty, self.precision("amount", item))
|
||||
item.item_tax_amount = 0.0;
|
||||
|
||||
self._set_in_company_currency(item, "amount", "base_amount")
|
||||
@@ -109,20 +109,15 @@ class BuyingController(StockController):
|
||||
self.round_floats_in(self, ["net_total", "net_total_import"])
|
||||
|
||||
def calculate_totals(self):
|
||||
self.grand_total = flt(self.tax_doclist[-1].total if self.tax_doclist
|
||||
else self.net_total, self.precision("grand_total"))
|
||||
self.grand_total_import = flt(self.grand_total / self.conversion_rate,
|
||||
self.precision("grand_total_import"))
|
||||
self.grand_total = flt(self.tax_doclist[-1].total if self.tax_doclist else self.net_total)
|
||||
|
||||
self.total_tax = flt(self.grand_total - self.net_total,
|
||||
self.precision("total_tax"))
|
||||
self.total_tax = flt(self.grand_total - self.net_total, self.precision("total_tax"))
|
||||
|
||||
self.grand_total = flt(self.grand_total, self.precision("grand_total"))
|
||||
|
||||
if self.meta.get_field("rounded_total"):
|
||||
self.rounded_total = rounded(self.grand_total)
|
||||
|
||||
if self.meta.get_field("rounded_total_import"):
|
||||
self.rounded_total_import = rounded(self.grand_total_import)
|
||||
|
||||
if self.meta.get_field("other_charges_added"):
|
||||
self.other_charges_added = flt(sum([flt(d.tax_amount) for d in self.tax_doclist
|
||||
if d.add_deduct_tax=="Add" and d.category in ["Valuation and Total", "Total"]]),
|
||||
@@ -133,6 +128,14 @@ class BuyingController(StockController):
|
||||
if d.add_deduct_tax=="Deduct" and d.category in ["Valuation and Total", "Total"]]),
|
||||
self.precision("other_charges_deducted"))
|
||||
|
||||
self.grand_total_import = flt(self.grand_total / self.conversion_rate) \
|
||||
if (self.other_charges_added or self.other_charges_deducted) else self.net_total_import
|
||||
|
||||
self.grand_total_import = flt(self.grand_total_import, self.precision("grand_total_import"))
|
||||
|
||||
if self.meta.get_field("rounded_total_import"):
|
||||
self.rounded_total_import = rounded(self.grand_total_import)
|
||||
|
||||
if self.meta.get_field("other_charges_added_import"):
|
||||
self.other_charges_added_import = flt(self.other_charges_added /
|
||||
self.conversion_rate, self.precision("other_charges_added_import"))
|
||||
@@ -255,8 +258,6 @@ class BuyingController(StockController):
|
||||
rm.required_qty = required_qty
|
||||
|
||||
rm.conversion_factor = item.conversion_factor
|
||||
rm.rate = bom_item.rate
|
||||
rm.amount = required_qty * flt(bom_item.rate)
|
||||
rm.idx = rm_supplied_idx
|
||||
|
||||
if self.doctype == "Purchase Receipt":
|
||||
@@ -267,7 +268,25 @@ class BuyingController(StockController):
|
||||
|
||||
rm_supplied_idx += 1
|
||||
|
||||
raw_materials_cost += required_qty * flt(bom_item.rate)
|
||||
# get raw materials rate
|
||||
if self.doctype == "Purchase Receipt":
|
||||
from erpnext.stock.utils import get_incoming_rate
|
||||
rm.rate = get_incoming_rate({
|
||||
"item_code": bom_item.item_code,
|
||||
"warehouse": self.supplier_warehouse,
|
||||
"posting_date": self.posting_date,
|
||||
"posting_time": self.posting_time,
|
||||
"qty": -1 * required_qty,
|
||||
"serial_no": rm.serial_no
|
||||
})
|
||||
if not rm.rate:
|
||||
from erpnext.stock.stock_ledger import get_valuation_rate
|
||||
rm.rate = get_valuation_rate(bom_item.item_code, self.supplier_warehouse)
|
||||
else:
|
||||
rm.rate = bom_item.rate
|
||||
|
||||
rm.amount = required_qty * flt(rm.rate)
|
||||
raw_materials_cost += flt(rm.amount)
|
||||
|
||||
if self.doctype == "Purchase Receipt":
|
||||
item.rm_supp_cost = raw_materials_cost
|
||||
|
||||
@@ -2,15 +2,28 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
import frappe.utils
|
||||
import frappe.defaults
|
||||
from frappe.utils import cint, cstr, getdate, nowdate, get_first_day, get_last_day
|
||||
|
||||
from frappe.utils import add_days, cint, cstr, date_diff, flt, getdate, nowdate, \
|
||||
get_first_day, get_last_day, comma_and
|
||||
from frappe.model.naming import make_autoname
|
||||
|
||||
from frappe import _, msgprint, throw
|
||||
from erpnext.accounts.party import get_party_account, get_due_date, get_party_details
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
|
||||
month_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6, 'Yearly': 12}
|
||||
date_field_map = {
|
||||
"Sales Order": "transaction_date",
|
||||
"Sales Invoice": "posting_date",
|
||||
"Purchase Order": "transaction_date",
|
||||
"Purchase Invoice": "posting_date"
|
||||
}
|
||||
|
||||
def create_recurring_documents():
|
||||
manage_recurring_documents("Sales Order")
|
||||
manage_recurring_documents("Sales Invoice")
|
||||
manage_recurring_documents("Purchase Order")
|
||||
manage_recurring_documents("Purchase Invoice")
|
||||
|
||||
def manage_recurring_documents(doctype, next_date=None, commit=True):
|
||||
"""
|
||||
@@ -19,10 +32,7 @@ def manage_recurring_documents(doctype, next_date=None, commit=True):
|
||||
"""
|
||||
next_date = next_date or nowdate()
|
||||
|
||||
if doctype == "Sales Order":
|
||||
date_field = "transaction_date"
|
||||
elif doctype == "Sales Invoice":
|
||||
date_field = "posting_date"
|
||||
date_field = date_field_map[doctype]
|
||||
|
||||
recurring_documents = frappe.db.sql("""select name, recurring_id
|
||||
from `tab{}` where ifnull(is_recurring, 0)=1
|
||||
@@ -36,6 +46,9 @@ def manage_recurring_documents(doctype, next_date=None, commit=True):
|
||||
% (doctype, date_field, '%s', '%s'), (next_date, recurring_id)):
|
||||
try:
|
||||
ref_wrapper = frappe.get_doc(doctype, ref_document)
|
||||
if hasattr(ref_wrapper, "before_recurring"):
|
||||
ref_wrapper.before_recurring()
|
||||
|
||||
new_document_wrapper = make_new_document(ref_wrapper, date_field, next_date)
|
||||
send_notification(new_document_wrapper)
|
||||
if commit:
|
||||
@@ -48,7 +61,8 @@ def manage_recurring_documents(doctype, next_date=None, commit=True):
|
||||
frappe.db.sql("update `tab%s` \
|
||||
set is_recurring = 0 where name = %s" % (doctype, '%s'),
|
||||
(ref_document))
|
||||
notify_errors(ref_document, doctype, ref_wrapper.customer, ref_wrapper.owner)
|
||||
notify_errors(ref_document, doctype, ref_wrapper.get("customer") or ref_wrapper.get("supplier"),
|
||||
ref_wrapper.owner)
|
||||
frappe.db.commit()
|
||||
|
||||
exception_list.append(frappe.get_traceback())
|
||||
@@ -110,12 +124,9 @@ def send_notification(new_rv):
|
||||
frappe.sendmail(new_rv.notification_email_address,
|
||||
subject= _("New {0}: #{1}").format(new_rv.doctype, new_rv.name),
|
||||
message = _("Please find attached {0} #{1}").format(new_rv.doctype, new_rv.name),
|
||||
attachments = [{
|
||||
"fname": new_rv.name + ".pdf",
|
||||
"fcontent": frappe.get_print_format(new_rv.doctype, new_rv.name, as_pdf=True)
|
||||
}])
|
||||
attachments = [frappe.attach_print(new_rv.doctype, new_rv.name, file_name=new_rv.name)])
|
||||
|
||||
def notify_errors(doc, doctype, customer, owner):
|
||||
def notify_errors(doc, doctype, party, owner):
|
||||
from frappe.utils.user import get_system_managers
|
||||
recipients = get_system_managers(only_name=True)
|
||||
|
||||
@@ -124,7 +135,7 @@ def notify_errors(doc, doctype, customer, owner):
|
||||
message = frappe.get_template("templates/emails/recurring_document_failed.html").render({
|
||||
"type": doctype,
|
||||
"name": doc,
|
||||
"customer": customer
|
||||
"party": party
|
||||
}))
|
||||
|
||||
assign_task_to_owner(doc, doctype, "Recurring Invoice Failed", recipients)
|
||||
@@ -152,18 +163,18 @@ def validate_recurring_document(doc):
|
||||
elif not (doc.from_date and doc.to_date):
|
||||
throw(_("Period From and Period To dates mandatory for recurring %s") % doc.doctype)
|
||||
|
||||
def convert_to_recurring(doc, autoname, posting_date):
|
||||
if doc.is_recurring:
|
||||
if not doc.recurring_id:
|
||||
frappe.db.set(doc, "recurring_id",
|
||||
make_autoname(autoname))
|
||||
#
|
||||
def convert_to_recurring(doc, posting_date):
|
||||
if doc.is_recurring:
|
||||
if not doc.recurring_id:
|
||||
frappe.db.set(doc, "recurring_id", doc.name)
|
||||
|
||||
set_next_date(doc, posting_date)
|
||||
set_next_date(doc, posting_date)
|
||||
|
||||
elif doc.recurring_id:
|
||||
frappe.db.sql("""update `tab%s`
|
||||
set is_recurring = 0
|
||||
where recurring_id = %s""" % (doc.doctype, '%s'), (doc.recurring_id))
|
||||
elif doc.recurring_id:
|
||||
frappe.db.sql("""update `tab%s` set is_recurring = 0
|
||||
where recurring_id = %s""" % (doc.doctype, '%s'), (doc.recurring_id))
|
||||
#
|
||||
|
||||
def validate_notification_email_id(doc):
|
||||
if doc.notification_email_address:
|
||||
|
||||
@@ -216,33 +216,38 @@ class SellingController(StockController):
|
||||
self.round_floats_in(self, ["net_total", "net_total_export"])
|
||||
|
||||
def calculate_totals(self):
|
||||
self.grand_total = flt(self.tax_doclist and \
|
||||
self.tax_doclist[-1].total or self.net_total, self.precision("grand_total"))
|
||||
self.grand_total_export = flt(self.grand_total / self.conversion_rate,
|
||||
self.precision("grand_total_export"))
|
||||
self.grand_total = flt(self.tax_doclist[-1].total if self.tax_doclist else self.net_total)
|
||||
|
||||
self.other_charges_total = flt(self.grand_total - self.net_total,
|
||||
self.precision("other_charges_total"))
|
||||
self.other_charges_total = flt(self.grand_total - self.net_total, self.precision("other_charges_total"))
|
||||
|
||||
self.other_charges_total_export = flt(self.grand_total_export -
|
||||
self.net_total_export + flt(self.discount_amount),
|
||||
self.precision("other_charges_total_export"))
|
||||
self.grand_total_export = flt(self.grand_total / self.conversion_rate) \
|
||||
if (self.other_charges_total or self.discount_amount) else self.net_total_export
|
||||
|
||||
self.other_charges_total_export = flt(self.grand_total_export - self.net_total_export +
|
||||
flt(self.discount_amount), self.precision("other_charges_total_export"))
|
||||
|
||||
self.grand_total = flt(self.grand_total, self.precision("grand_total"))
|
||||
self.grand_total_export = flt(self.grand_total_export, self.precision("grand_total_export"))
|
||||
|
||||
self.rounded_total = rounded(self.grand_total)
|
||||
self.rounded_total_export = rounded(self.grand_total_export)
|
||||
|
||||
def apply_discount_amount(self):
|
||||
if self.discount_amount:
|
||||
self.base_discount_amount = flt(self.discount_amount * self.conversion_rate, self.precision("base_discount_amount"))
|
||||
|
||||
grand_total_for_discount_amount = self.get_grand_total_for_discount_amount()
|
||||
|
||||
if grand_total_for_discount_amount:
|
||||
# calculate item amount after Discount Amount
|
||||
for item in self.item_doclist:
|
||||
distributed_amount = flt(self.discount_amount) * item.base_amount / grand_total_for_discount_amount
|
||||
distributed_amount = flt(self.base_discount_amount) * item.base_amount / grand_total_for_discount_amount
|
||||
item.base_amount = flt(item.base_amount - distributed_amount, self.precision("base_amount", item))
|
||||
|
||||
self.discount_amount_applied = True
|
||||
self._calculate_taxes_and_totals()
|
||||
else:
|
||||
self.base_discount_amount = 0
|
||||
|
||||
def get_grand_total_for_discount_amount(self):
|
||||
actual_taxes_dict = {}
|
||||
|
||||
@@ -8,7 +8,7 @@ from frappe import msgprint, _
|
||||
import frappe.defaults
|
||||
|
||||
from erpnext.controllers.accounts_controller import AccountsController
|
||||
from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries
|
||||
from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries, process_gl_map
|
||||
|
||||
class StockController(AccountsController):
|
||||
def make_gl_entries(self, repost_future_gle=True):
|
||||
@@ -16,20 +16,20 @@ class StockController(AccountsController):
|
||||
delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
|
||||
|
||||
if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")):
|
||||
warehouse_account = self.get_warehouse_account()
|
||||
warehouse_account = get_warehouse_account()
|
||||
|
||||
if self.docstatus==1:
|
||||
gl_entries = self.get_gl_entries(warehouse_account)
|
||||
make_gl_entries(gl_entries)
|
||||
|
||||
if repost_future_gle:
|
||||
items, warehouse_account = self.get_items_and_warehouse_accounts(warehouse_account)
|
||||
update_gl_entries_after(self.posting_date, self.posting_time,
|
||||
warehouse_account, items)
|
||||
items, warehouses = self.get_items_and_warehouses()
|
||||
update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items,
|
||||
warehouse_account)
|
||||
|
||||
def get_gl_entries(self, warehouse_account=None, default_expense_account=None,
|
||||
default_cost_center=None):
|
||||
from erpnext.accounts.general_ledger import process_gl_map
|
||||
|
||||
if not warehouse_account:
|
||||
warehouse_account = get_warehouse_account()
|
||||
|
||||
@@ -88,10 +88,8 @@ class StockController(AccountsController):
|
||||
|
||||
return details
|
||||
|
||||
def get_items_and_warehouse_accounts(self, warehouse_account=None):
|
||||
def get_items_and_warehouses(self):
|
||||
items, warehouses = [], []
|
||||
if not warehouse_account:
|
||||
warehouse_account = get_warehouse_account()
|
||||
|
||||
if hasattr(self, "fname"):
|
||||
item_doclist = self.get(self.fname)
|
||||
@@ -117,86 +115,17 @@ class StockController(AccountsController):
|
||||
if d.get("t_warehouse") and d.t_warehouse not in warehouses:
|
||||
warehouses.append(d.t_warehouse)
|
||||
|
||||
warehouse_account = {wh: warehouse_account[wh] for wh in warehouses
|
||||
if warehouse_account.get(wh)}
|
||||
|
||||
return items, warehouse_account
|
||||
return items, warehouses
|
||||
|
||||
def get_stock_ledger_details(self):
|
||||
stock_ledger = {}
|
||||
for sle in frappe.db.sql("""select warehouse, stock_value_difference, voucher_detail_no
|
||||
for sle in frappe.db.sql("""select warehouse, stock_value_difference,
|
||||
voucher_detail_no, item_code, posting_date, actual_qty
|
||||
from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""",
|
||||
(self.doctype, self.name), as_dict=True):
|
||||
stock_ledger.setdefault(sle.voucher_detail_no, []).append(sle)
|
||||
return stock_ledger
|
||||
|
||||
def get_warehouse_account(self):
|
||||
warehouse_account = dict(frappe.db.sql("""select master_name, name from tabAccount
|
||||
where account_type = 'Warehouse' and ifnull(master_name, '') != ''"""))
|
||||
return warehouse_account
|
||||
|
||||
def update_gl_entries_after(self, warehouse_account=None):
|
||||
future_stock_vouchers = self.get_future_stock_vouchers()
|
||||
gle = self.get_voucherwise_gl_entries(future_stock_vouchers)
|
||||
if not warehouse_account:
|
||||
warehouse_account = self.get_warehouse_account()
|
||||
for voucher_type, voucher_no in future_stock_vouchers:
|
||||
existing_gle = gle.get((voucher_type, voucher_no), [])
|
||||
voucher_obj = frappe.get_doc(voucher_type, voucher_no)
|
||||
expected_gle = voucher_obj.get_gl_entries(warehouse_account)
|
||||
if expected_gle:
|
||||
matched = True
|
||||
if existing_gle:
|
||||
for entry in expected_gle:
|
||||
for e in existing_gle:
|
||||
if entry.account==e.account \
|
||||
and entry.against_account==e.against_account\
|
||||
and entry.cost_center==e.cost_center:
|
||||
if entry.debit != e.debit or entry.credit != e.credit:
|
||||
matched = False
|
||||
break
|
||||
else:
|
||||
matched = False
|
||||
|
||||
if not matched:
|
||||
self.delete_gl_entries(voucher_type, voucher_no)
|
||||
voucher_obj.make_gl_entries(repost_future_gle=False)
|
||||
else:
|
||||
self.delete_gl_entries(voucher_type, voucher_no)
|
||||
|
||||
|
||||
def get_future_stock_vouchers(self):
|
||||
condition = ""
|
||||
item_list = []
|
||||
if getattr(self, "fname", None):
|
||||
item_list = [d.item_code for d in self.get(self.fname)]
|
||||
if item_list:
|
||||
condition = "and item_code in ({})".format(", ".join(["%s"] * len(item_list)))
|
||||
|
||||
future_stock_vouchers = frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
|
||||
from `tabStock Ledger Entry` sle
|
||||
where timestamp(sle.posting_date, sle.posting_time) >= timestamp(%s, %s) {condition}
|
||||
order by timestamp(sle.posting_date, sle.posting_time) asc, name asc""".format(
|
||||
condition=condition), tuple([self.posting_date, self.posting_date] + item_list),
|
||||
as_list=True)
|
||||
|
||||
return future_stock_vouchers
|
||||
|
||||
def get_voucherwise_gl_entries(self, future_stock_vouchers):
|
||||
gl_entries = {}
|
||||
if future_stock_vouchers:
|
||||
for d in frappe.db.sql("""select * from `tabGL Entry`
|
||||
where posting_date >= %s and voucher_no in (%s)""" %
|
||||
('%s', ', '.join(['%s']*len(future_stock_vouchers))),
|
||||
tuple([self.posting_date] + [d[1] for d in future_stock_vouchers]), as_dict=1):
|
||||
gl_entries.setdefault((d.voucher_type, d.voucher_no), []).append(d)
|
||||
|
||||
return gl_entries
|
||||
|
||||
def delete_gl_entries(self, voucher_type, voucher_no):
|
||||
frappe.db.sql("""delete from `tabGL Entry`
|
||||
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
|
||||
|
||||
def make_adjustment_entry(self, expected_gle, voucher_obj):
|
||||
from erpnext.accounts.utils import get_stock_and_account_difference
|
||||
account_list = [d.account for d in expected_gle]
|
||||
@@ -238,7 +167,7 @@ class StockController(AccountsController):
|
||||
else:
|
||||
is_expense_account = frappe.db.get_value("Account",
|
||||
item.get("expense_account"), "report_type")=="Profit and Loss"
|
||||
if self.doctype not in ("Purchase Receipt", "Stock Reconciliation") and not is_expense_account:
|
||||
if self.doctype not in ("Purchase Receipt", "Stock Reconciliation", "Stock Entry") and not is_expense_account:
|
||||
frappe.throw(_("Expense / Difference account ({0}) must be a 'Profit or Loss' account")
|
||||
.format(item.get("expense_account")))
|
||||
if is_expense_account and not item.get("cost_center"):
|
||||
@@ -246,7 +175,7 @@ class StockController(AccountsController):
|
||||
_(self.doctype), self.name, item.get("item_code")))
|
||||
|
||||
def get_sl_entries(self, d, args):
|
||||
sl_dict = {
|
||||
sl_dict = frappe._dict({
|
||||
"item_code": d.get("item_code", None),
|
||||
"warehouse": d.get("warehouse", None),
|
||||
"posting_date": self.posting_date,
|
||||
@@ -263,14 +192,14 @@ class StockController(AccountsController):
|
||||
"serial_no": d.get("serial_no"),
|
||||
"project": d.get("project_name"),
|
||||
"is_cancelled": self.docstatus==2 and "Yes" or "No"
|
||||
}
|
||||
})
|
||||
|
||||
sl_dict.update(args)
|
||||
return sl_dict
|
||||
|
||||
def make_sl_entries(self, sl_entries, is_amended=None):
|
||||
def make_sl_entries(self, sl_entries, is_amended=None, allow_negative_stock=False):
|
||||
from erpnext.stock.stock_ledger import make_sl_entries
|
||||
make_sl_entries(sl_entries, is_amended)
|
||||
make_sl_entries(sl_entries, is_amended, allow_negative_stock)
|
||||
|
||||
def make_gl_entries_on_cancel(self):
|
||||
if frappe.db.sql("""select name from `tabGL Entry` where voucher_type=%s
|
||||
@@ -287,15 +216,16 @@ class StockController(AccountsController):
|
||||
|
||||
return serialized_items
|
||||
|
||||
def update_gl_entries_after(posting_date, posting_time, warehouse_account=None, for_items=None):
|
||||
def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,
|
||||
warehouse_account=None):
|
||||
def _delete_gl_entries(voucher_type, voucher_no):
|
||||
frappe.db.sql("""delete from `tabGL Entry`
|
||||
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
|
||||
|
||||
if not warehouse_account:
|
||||
warehouse_account = get_warehouse_account()
|
||||
future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time,
|
||||
warehouse_account, for_items)
|
||||
|
||||
future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items)
|
||||
gle = get_voucherwise_gl_entries(future_stock_vouchers, posting_date)
|
||||
|
||||
for voucher_type, voucher_no in future_stock_vouchers:
|
||||
@@ -315,13 +245,13 @@ def compare_existing_and_expected_gle(existing_gle, expected_gle):
|
||||
for entry in expected_gle:
|
||||
for e in existing_gle:
|
||||
if entry.account==e.account and entry.against_account==e.against_account \
|
||||
and entry.cost_center==e.cost_center \
|
||||
and (not entry.cost_center or not e.cost_center or entry.cost_center==e.cost_center) \
|
||||
and (entry.debit != e.debit or entry.credit != e.credit):
|
||||
matched = False
|
||||
break
|
||||
return matched
|
||||
|
||||
def get_future_stock_vouchers(posting_date, posting_time, warehouse_account=None, for_items=None):
|
||||
def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, for_items=None):
|
||||
future_stock_vouchers = []
|
||||
|
||||
values = []
|
||||
@@ -330,9 +260,9 @@ def get_future_stock_vouchers(posting_date, posting_time, warehouse_account=None
|
||||
condition += " and item_code in ({})".format(", ".join(["%s"] * len(for_items)))
|
||||
values += for_items
|
||||
|
||||
if warehouse_account:
|
||||
condition += " and warehouse in ({})".format(", ".join(["%s"] * len(warehouse_account.keys())))
|
||||
values += warehouse_account.keys()
|
||||
if for_warehouses:
|
||||
condition += " and warehouse in ({})".format(", ".join(["%s"] * len(for_warehouses)))
|
||||
values += for_warehouses
|
||||
|
||||
for d in frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
|
||||
from `tabStock Ledger Entry` sle
|
||||
|
||||
@@ -2,12 +2,8 @@
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
import frappe
|
||||
import unittest, json, copy
|
||||
from frappe.utils import flt
|
||||
import frappe.permissions
|
||||
from erpnext.accounts.utils import get_stock_and_account_difference
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
|
||||
from erpnext.projects.doctype.time_log_batch.test_time_log_batch import *
|
||||
from erpnext.controllers.recurring_document import date_field_map
|
||||
|
||||
def test_recurring_document(obj, test_records):
|
||||
from frappe.utils import get_first_day, get_last_day, add_to_date, nowdate, getdate, add_days
|
||||
@@ -27,20 +23,11 @@ def test_recurring_document(obj, test_records):
|
||||
"to_date": get_last_day(today)
|
||||
})
|
||||
|
||||
if base_doc.doctype == "Sales Order":
|
||||
base_doc.update({
|
||||
"transaction_date": today,
|
||||
"delivery_date": add_days(today, 15)
|
||||
})
|
||||
elif base_doc.doctype == "Sales Invoice":
|
||||
base_doc.update({
|
||||
"posting_date": today
|
||||
})
|
||||
date_field = date_field_map[base_doc.doctype]
|
||||
base_doc.set(date_field, today)
|
||||
|
||||
if base_doc.doctype == "Sales Order":
|
||||
date_field = "transaction_date"
|
||||
elif base_doc.doctype == "Sales Invoice":
|
||||
date_field = "posting_date"
|
||||
base_doc.set("delivery_date", add_days(today, 15))
|
||||
|
||||
# monthly
|
||||
doc1 = frappe.copy_doc(base_doc)
|
||||
@@ -128,7 +115,7 @@ def _test_recurring_document(obj, base_doc, date_field, first_and_last_day):
|
||||
|
||||
next_date = get_next_date(base_doc.get(date_field), no_of_months,
|
||||
base_doc.repeat_on_day_of_month)
|
||||
|
||||
|
||||
manage_recurring_documents(base_doc.doctype, next_date=next_date, commit=False)
|
||||
|
||||
recurred_documents = frappe.db.sql("""select name from `tab%s`
|
||||
|
||||
@@ -16,10 +16,10 @@ def get_columns(filters, trans):
|
||||
# get conditions for grouping filter cond
|
||||
group_by_cols = group_wise_column(filters.get("group_by"))
|
||||
|
||||
columns = based_on_details["based_on_cols"] + period_cols + ["Total(Qty):Float:120", "Total(Amt):Currency:120"]
|
||||
columns = based_on_details["based_on_cols"] + period_cols + [_("Total(Qty)") + ":Float:120", _("Total(Amt)") + ":Currency:120"]
|
||||
if group_by_cols:
|
||||
columns = based_on_details["based_on_cols"] + group_by_cols + period_cols + \
|
||||
["Total(Qty):Float:120", "Total(Amt):Currency:120"]
|
||||
[_("Total(Qty)") + ":Float:120", _("Total(Amt)") + ":Currency:120"]
|
||||
|
||||
conditions = {"based_on_select": based_on_details["based_on_select"], "period_wise_select": period_select,
|
||||
"columns": columns, "group_by": based_on_details["based_on_group_by"], "grbc": group_by_cols, "trans": trans,
|
||||
@@ -130,8 +130,8 @@ def period_wise_columns_query(filters, trans):
|
||||
get_period_wise_columns(dt, filters.get("period"), pwc)
|
||||
query_details = get_period_wise_query(dt, trans_date, query_details)
|
||||
else:
|
||||
pwc = [filters.get("fiscal_year") + " (Qty):Float:120",
|
||||
filters.get("fiscal_year") + " (Amt):Currency:120"]
|
||||
pwc = [_(filters.get("fiscal_year")) + " ("+_("Qty") + "):Float:120",
|
||||
_(filters.get("fiscal_year")) + " ("+ _("Amt") + "):Currency:120"]
|
||||
query_details = " SUM(t2.qty), SUM(t2.base_amount),"
|
||||
|
||||
query_details += 'SUM(t2.qty), SUM(t2.base_amount)'
|
||||
@@ -139,11 +139,11 @@ def period_wise_columns_query(filters, trans):
|
||||
|
||||
def get_period_wise_columns(bet_dates, period, pwc):
|
||||
if period == 'Monthly':
|
||||
pwc += [get_mon(bet_dates[0]) + " (Qty):Float:120",
|
||||
get_mon(bet_dates[0]) + " (Amt):Currency:120"]
|
||||
pwc += [_(get_mon(bet_dates[0])) + " (" + _("Qty") + "):Float:120",
|
||||
_(get_mon(bet_dates[0])) + " (" + _("Amt") + "):Currency:120"]
|
||||
else:
|
||||
pwc += [get_mon(bet_dates[0]) + "-" + get_mon(bet_dates[1]) + " (Qty):Float:120",
|
||||
get_mon(bet_dates[0]) + "-" + get_mon(bet_dates[1]) + " (Amt):Currency:120"]
|
||||
pwc += [_(get_mon(bet_dates[0])) + "-" + _(get_mon(bet_dates[1])) + " (" + _("Qty") + "):Float:120",
|
||||
_(get_mon(bet_dates[0])) + "-" + _(get_mon(bet_dates[1])) + " (" + _("Amt") + "):Currency:120"]
|
||||
|
||||
def get_period_wise_query(bet_dates, trans_date, query_details):
|
||||
query_details += """SUM(IF(t1.%(trans_date)s BETWEEN '%(sd)s' AND '%(ed)s', t2.qty, NULL)),
|
||||
|
||||
@@ -20,7 +20,7 @@ frappe.pages['activity'].onload = function(wrapper) {
|
||||
});
|
||||
list.run();
|
||||
|
||||
wrapper.appframe.set_title_right("Refresh", function() { list.run(); });
|
||||
wrapper.appframe.set_title_right(__("Refresh"), function() { list.run(); });
|
||||
|
||||
// Build Report Button
|
||||
if(frappe.boot.user.can_get_report.indexOf("Feed")!=-1) {
|
||||
|
||||
@@ -4,7 +4,7 @@ app_publisher = "Web Notes Technologies Pvt. Ltd. and Contributors"
|
||||
app_description = "Open Source Enterprise Resource Planning for Small and Midsized Organizations"
|
||||
app_icon = "icon-th"
|
||||
app_color = "#e74c3c"
|
||||
app_version = "4.3.0"
|
||||
app_version = "4.21.4"
|
||||
|
||||
error_report_email = "support@erpnext.com"
|
||||
|
||||
@@ -47,8 +47,8 @@ doc_events = {
|
||||
"on_update": "erpnext.home.make_comment_feed"
|
||||
},
|
||||
"Stock Entry": {
|
||||
"on_submit": "erpnext.stock.doctype.material_request.material_request.update_completed_qty",
|
||||
"on_cancel": "erpnext.stock.doctype.material_request.material_request.update_completed_qty"
|
||||
"on_submit": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty",
|
||||
"on_cancel": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty"
|
||||
},
|
||||
"User": {
|
||||
"validate": "erpnext.hr.doctype.employee.employee.validate_employee_role",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -200,7 +200,7 @@ def validate_employee_role(doc, method):
|
||||
# called via User hook
|
||||
if "Employee" in [d.role for d in doc.get("user_roles")]:
|
||||
if not frappe.db.get_value("Employee", {"user_id": doc.name}):
|
||||
frappe.msgprint("Please set User ID field in an Employee record to set Employee Role")
|
||||
frappe.msgprint(_("Please set User ID field in an Employee record to set Employee Role"))
|
||||
doc.get("user_roles").remove(doc.get("user_roles", {"role": "Employee"})[0])
|
||||
|
||||
def update_user_permissions(doc, method):
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
{
|
||||
"autoname": "EXP.######",
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-01-10 16:34:14",
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"fields": [
|
||||
{
|
||||
"default": "EXP",
|
||||
"fieldname": "naming_series",
|
||||
"fieldtype": "Select",
|
||||
"label": "Series",
|
||||
"no_copy": 1,
|
||||
"options": "EXP",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "Draft",
|
||||
"depends_on": "eval:!doc.__islocal",
|
||||
@@ -190,7 +202,7 @@
|
||||
"icon": "icon-money",
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"modified": "2014-08-27 07:08:48.454580",
|
||||
"modified": "2014-11-24 18:25:53.038826",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Expense Claim",
|
||||
|
||||
@@ -33,7 +33,7 @@ cur_frm.cscript.refresh = function(doc, dt, dn) {
|
||||
cur_frm.set_intro(__("You are the Leave Approver for this record. Please Update the 'Status' and Save"));
|
||||
cur_frm.toggle_enable("status", true);
|
||||
} else {
|
||||
cur_frm.set_intro(__("This Leave Application is pending approval. Only the Leave Apporver can update status."))
|
||||
cur_frm.set_intro(__("This Leave Application is pending approval. Only the Leave Approver can update status."))
|
||||
cur_frm.toggle_enable("status", false);
|
||||
if(!doc.__islocal) {
|
||||
cur_frm.frm_head.appframe.set_title_right("");
|
||||
@@ -104,9 +104,32 @@ cur_frm.cscript.calculate_total_days = function(doc, dt, dn) {
|
||||
if(cint(doc.half_day) == 1) set_multiple(dt,dn,{total_leave_days:0.5});
|
||||
else{
|
||||
// server call is done to include holidays in leave days calculations
|
||||
return get_server_fields('get_total_leave_days', '', '', doc, dt, dn, 1);
|
||||
return frappe.call({
|
||||
method: 'erpnext.hr.doctype.leave_application.leave_application.get_total_leave_days',
|
||||
args: {leave_app: doc},
|
||||
callback: function(response) {
|
||||
if (response && response.message) {
|
||||
cur_frm.set_value('total_leave_days', response.message.total_leave_days);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.fields_dict.employee.get_query = erpnext.queries.employee;
|
||||
|
||||
frappe.ui.form.on("Leave Application", "leave_approver", function(frm) {
|
||||
frappe.call({
|
||||
"method": "frappe.client.get",
|
||||
args: {
|
||||
doctype: "User",
|
||||
name: frm.doc.leave_approver
|
||||
},
|
||||
callback: function (data) {
|
||||
frappe.model.set_value(frm.doctype, frm.docname, "leave_approver_name",
|
||||
data.message.first_name
|
||||
+ (data.message.last_name ? (" " + data.message.last_name) : ""))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -24,6 +24,13 @@
|
||||
"options": "User",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "leave_approver_name",
|
||||
"fieldtype": "Read Only",
|
||||
"label": "Leave Approver Name",
|
||||
"permlevel": 0,
|
||||
"precision": ""
|
||||
},
|
||||
{
|
||||
"fieldname": "leave_type",
|
||||
"fieldtype": "Link",
|
||||
@@ -158,6 +165,7 @@
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "letter_head",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
@@ -183,7 +191,7 @@
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"max_attachments": 3,
|
||||
"modified": "2014-08-28 03:32:38.865202",
|
||||
"modified": "2014-12-09 16:33:29.626849",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Leave Application",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import frappe, json
|
||||
from frappe import _
|
||||
|
||||
from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, get_url_to_form, \
|
||||
@@ -77,26 +77,10 @@ class LeaveApplication(Document):
|
||||
LeaveDayBlockedError)
|
||||
|
||||
def get_holidays(self):
|
||||
tot_hol = frappe.db.sql("""select count(*) from `tabHoliday` h1, `tabHoliday List` h2, `tabEmployee` e1
|
||||
where e1.name = %s and h1.parent = h2.name and e1.holiday_list = h2.name
|
||||
and h1.holiday_date between %s and %s""", (self.employee, self.from_date, self.to_date))
|
||||
if not tot_hol:
|
||||
tot_hol = frappe.db.sql("""select count(*) from `tabHoliday` h1, `tabHoliday List` h2
|
||||
where h1.parent = h2.name and h1.holiday_date between %s and %s
|
||||
and ifnull(h2.is_default,0) = 1 and h2.fiscal_year = %s""",
|
||||
(self.from_date, self.to_date, self.fiscal_year))
|
||||
return tot_hol and flt(tot_hol[0][0]) or 0
|
||||
return get_holidays(self)
|
||||
|
||||
def get_total_leave_days(self):
|
||||
"""Calculates total leave days based on input and holidays"""
|
||||
ret = {'total_leave_days' : 0.5}
|
||||
if not self.half_day:
|
||||
tot_days = date_diff(self.to_date, self.from_date) + 1
|
||||
holidays = self.get_holidays()
|
||||
ret = {
|
||||
'total_leave_days' : flt(tot_days)-flt(holidays)
|
||||
}
|
||||
return ret
|
||||
return get_total_leave_days(self)
|
||||
|
||||
def validate_to_date(self):
|
||||
if self.from_date and self.to_date and \
|
||||
@@ -216,6 +200,35 @@ class LeaveApplication(Document):
|
||||
post(**{"txt": args.message, "contact": args.message_to, "subject": args.subject,
|
||||
"notify": cint(self.follow_via_email)})
|
||||
|
||||
def get_holidays(leave_app):
|
||||
tot_hol = frappe.db.sql("""select count(*) from `tabHoliday` h1, `tabHoliday List` h2, `tabEmployee` e1
|
||||
where e1.name = %s and h1.parent = h2.name and e1.holiday_list = h2.name
|
||||
and h1.holiday_date between %s and %s""", (leave_app.employee, leave_app.from_date,
|
||||
leave_app.to_date))[0][0]
|
||||
# below line is needed. If an employee hasn't been assigned with any holiday list then above will return 0 rows.
|
||||
if not tot_hol:
|
||||
tot_hol = frappe.db.sql("""select count(*) from `tabHoliday` h1, `tabHoliday List` h2
|
||||
where h1.parent = h2.name and h1.holiday_date between %s and %s
|
||||
and ifnull(h2.is_default,0) = 1 and h2.fiscal_year = %s""",
|
||||
(leave_app.from_date, leave_app.to_date, leave_app.fiscal_year))[0][0]
|
||||
return tot_hol
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_total_leave_days(leave_app):
|
||||
# Parse Leave Application if neccessary
|
||||
if isinstance(leave_app, str) or isinstance(leave_app, unicode):
|
||||
leave_app = frappe.get_doc(json.loads(leave_app))
|
||||
|
||||
"""Calculates total leave days based on input and holidays"""
|
||||
ret = {'total_leave_days' : 0.5}
|
||||
if not leave_app.half_day:
|
||||
tot_days = date_diff(leave_app.to_date, leave_app.from_date) + 1
|
||||
holidays = leave_app.get_holidays()
|
||||
ret = {
|
||||
'total_leave_days' : flt(tot_days)-flt(holidays)
|
||||
}
|
||||
return ret
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_leave_balance(employee, leave_type, fiscal_year):
|
||||
leave_all = frappe.db.sql("""select total_leaves_allocated
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"search_index": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "letter_head",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
@@ -336,7 +337,7 @@
|
||||
"icon": "icon-file-text",
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"modified": "2014-08-27 06:38:10.006224",
|
||||
"modified": "2014-09-09 05:35:33.807228",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Salary Slip",
|
||||
|
||||
@@ -45,7 +45,7 @@ class SalarySlip(TransactionBase):
|
||||
|
||||
def get_leave_details(self, lwp=None):
|
||||
if not self.fiscal_year:
|
||||
self.fiscal_year = frappe.get_default("fiscal_year")
|
||||
self.fiscal_year = frappe.db.get_default("fiscal_year")
|
||||
if not self.month:
|
||||
self.month = "%02d" % getdate(nowdate()).month
|
||||
|
||||
@@ -156,7 +156,7 @@ class SalarySlip(TransactionBase):
|
||||
/ cint(self.total_days_in_month), 2)
|
||||
elif not self.payment_days:
|
||||
d.e_modified_amount = 0
|
||||
else:
|
||||
elif not d.e_modified_amount:
|
||||
d.e_modified_amount = d.e_amount
|
||||
self.gross_pay += flt(d.e_modified_amount)
|
||||
|
||||
@@ -168,7 +168,7 @@ class SalarySlip(TransactionBase):
|
||||
/ cint(self.total_days_in_month), 2)
|
||||
elif not self.payment_days:
|
||||
d.d_modified_amount = 0
|
||||
else:
|
||||
elif not d.d_modified_amount:
|
||||
d.d_modified_amount = d.d_amount
|
||||
|
||||
self.total_deduction += flt(d.d_modified_amount)
|
||||
@@ -191,9 +191,6 @@ class SalarySlip(TransactionBase):
|
||||
if receiver:
|
||||
subj = 'Salary Slip - ' + cstr(self.month) +'/'+cstr(self.fiscal_year)
|
||||
sendmail([receiver], subject=subj, msg = _("Please see attachment"),
|
||||
attachments=[{
|
||||
"fname": self.name + ".pdf",
|
||||
"fcontent": frappe.get_print_format(self.doctype, self.name, as_pdf = True)
|
||||
}])
|
||||
attachments=[frappe.attach_print(self.doctype, self.name, file_name=self.name)])
|
||||
else:
|
||||
msgprint(_("Company Email ID not found, hence mail not sent"))
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import flt
|
||||
|
||||
def execute(filters=None):
|
||||
@@ -15,9 +16,9 @@ def execute(filters=None):
|
||||
|
||||
def get_columns():
|
||||
return [
|
||||
"Employee:Link/Employee:120", "Name:Data:200", "Date of Birth:Date:100",
|
||||
"Branch:Link/Branch:120", "Department:Link/Department:120",
|
||||
"Designation:Link/Designation:120", "Gender::60", "Company:Link/Company:120"
|
||||
_("Employee") + ":Link/Employee:120", _("Name") + ":Data:200", _("Date of Birth")+ ":Date:100",
|
||||
_("Branch") + ":Link/Branch:120", _("Department") + ":Link/Department:120",
|
||||
_("Designation") + ":Link/Designation:120", _("Gender") + "::60", _("Company") + ":Link/Company:120"
|
||||
]
|
||||
|
||||
def get_employees(filters):
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user