Compare commits
197 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
399a3097e8 | ||
|
|
c8f5c3cdbe | ||
|
|
e83d506319 | ||
|
|
690bcd7b66 | ||
|
|
f18d285eab | ||
|
|
7887ccb441 | ||
|
|
d57b57a21d | ||
|
|
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 |
@@ -14,6 +14,8 @@ install:
|
|||||||
- sudo apt-get update
|
- sudo apt-get update
|
||||||
- sudo apt-get purge -y mysql-common
|
- sudo apt-get purge -y mysql-common
|
||||||
- sudo apt-get install mariadb-server mariadb-common libmariadbclient-dev
|
- sudo apt-get install mariadb-server mariadb-common libmariadbclient-dev
|
||||||
|
- ./ci/fix-mariadb.sh
|
||||||
|
|
||||||
- wget http://downloads.sourceforge.net/project/wkhtmltopdf/0.12.1/wkhtmltox-0.12.1_linux-precise-amd64.deb
|
- 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
|
- sudo dpkg -i wkhtmltox-0.12.1_linux-precise-amd64.deb
|
||||||
- CFLAGS=-O0 pip install git+https://github.com/frappe/frappe.git@develop
|
- CFLAGS=-O0 pip install git+https://github.com/frappe/frappe.git@develop
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Includes Accounting, Inventory, CRM, Sales, Purchase, Projects, HRMS. Built on Python / MariaDB.
|
Includes Accounting, Inventory, CRM, Sales, Purchase, Projects, HRMS. Built on Python / MariaDB.
|
||||||
|
|
||||||
ERPNext is built on [frappe](https://github.com/frappe/frappe)
|
ERPNext is built on [frappe](https://github.com/frappe/frappe) Python Framework.
|
||||||
|
|
||||||
- [User Guide](http://erpnext.org/user-guide.html)
|
- [User Guide](http://erpnext.org/user-guide.html)
|
||||||
- [Getting Help](http://erpnext.org/getting-help.html)
|
- [Getting Help](http://erpnext.org/getting-help.html)
|
||||||
@@ -21,7 +21,7 @@ Use the bench, https://github.com/frappe/bench
|
|||||||
|
|
||||||
1. go to "/login"
|
1. go to "/login"
|
||||||
1. Administrator user name: "Administrator"
|
1. Administrator user name: "Administrator"
|
||||||
1. Administrator passowrd "admin"
|
1. Administrator password: "admin"
|
||||||
|
|
||||||
### Download and Install
|
### Download and Install
|
||||||
|
|
||||||
|
|||||||
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.5.1'
|
__version__ = '4.13.0'
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
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 import msgprint, throw, _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
@@ -176,15 +176,7 @@ class Account(Document):
|
|||||||
frappe.throw(_("Due Date cannot be before Posting Date"))
|
frappe.throw(_("Due Date cannot be before Posting Date"))
|
||||||
|
|
||||||
elif credit_days is not None and diff > credit_days:
|
elif credit_days is not None and diff > credit_days:
|
||||||
is_credit_controller = frappe.db.get_value("Accounts Settings", None,
|
msgprint(_("Note: Due Date exceeds the allowed credit days by {0} day(s)").format(diff - credit_days))
|
||||||
"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))
|
|
||||||
|
|
||||||
def validate_trash(self):
|
def validate_trash(self):
|
||||||
"""checks gl entries and if child exists"""
|
"""checks gl entries and if child exists"""
|
||||||
|
|||||||
@@ -124,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
|
from `tabGL Entry` where voucher_type = 'Journal Voucher' and voucher_no = %s
|
||||||
and account = %s and ifnull(against_voucher, '') = ''""",
|
and account = %s and ifnull(against_voucher, '') = ''""",
|
||||||
(against_voucher, account))[0][0])
|
(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
|
bal = against_voucher_amount + bal
|
||||||
if against_voucher_amount < 0:
|
if against_voucher_amount < 0:
|
||||||
bal = -bal
|
bal = -bal
|
||||||
|
|||||||
@@ -213,10 +213,11 @@ cur_frm.cscript.voucher_type = function(doc, cdt, cdn) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var update_jv_details = function(doc, r) {
|
var update_jv_details = function(doc, r) {
|
||||||
$.each(r.message, function(i, d) {
|
var jvdetail = frappe.model.add_child(doc, "Journal Voucher Detail", "entries");
|
||||||
var jvdetail = frappe.model.add_child(doc, "Journal Voucher Detail", "entries");
|
$.each(r, function(i, d) {
|
||||||
jvdetail.account = d.account;
|
var row = frappe.model.add_child(doc, "Journal Voucher Detail", "entries");
|
||||||
jvdetail.balance = d.balance;
|
row.account = d.account;
|
||||||
|
row.balance = d.balance;
|
||||||
});
|
});
|
||||||
refresh_field("entries");
|
refresh_field("entries");
|
||||||
}
|
}
|
||||||
@@ -231,7 +232,7 @@ cur_frm.cscript.voucher_type = function(doc, cdt, cdn) {
|
|||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
if(r.message) {
|
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) {
|
callback: function(r) {
|
||||||
frappe.model.clear_table(doc, "entries");
|
frappe.model.clear_table(doc, "entries");
|
||||||
if(r.message) {
|
if(r.message) {
|
||||||
update_jv_details(doc, r);
|
update_jv_details(doc, r.message);
|
||||||
}
|
}
|
||||||
cur_frm.set_value("is_opening", "Yes")
|
cur_frm.set_value("is_opening", "Yes")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
from frappe.utils import cint, cstr, flt, fmt_money, formatdate, getdate
|
from frappe.utils import cstr, flt, fmt_money, formatdate, getdate
|
||||||
from frappe import msgprint, _, scrub
|
from frappe import msgprint, _, scrub
|
||||||
from erpnext.setup.utils import get_company_currency
|
from erpnext.setup.utils import get_company_currency
|
||||||
|
|
||||||
@@ -13,10 +13,6 @@ from erpnext.controllers.accounts_controller import AccountsController
|
|||||||
class JournalVoucher(AccountsController):
|
class JournalVoucher(AccountsController):
|
||||||
def __init__(self, arg1, arg2=None):
|
def __init__(self, arg1, arg2=None):
|
||||||
super(JournalVoucher, self).__init__(arg1, arg2)
|
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):
|
def validate(self):
|
||||||
if not self.is_opening:
|
if not self.is_opening:
|
||||||
@@ -40,7 +36,7 @@ class JournalVoucher(AccountsController):
|
|||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
if self.voucher_type in ['Bank Voucher', 'Contra Voucher', 'Journal Entry']:
|
if self.voucher_type in ['Bank Voucher', 'Contra Voucher', 'Journal Entry']:
|
||||||
self.check_credit_days()
|
self.check_reference_date()
|
||||||
self.make_gl_entries()
|
self.make_gl_entries()
|
||||||
self.check_credit_limit()
|
self.check_credit_limit()
|
||||||
self.update_advance_paid()
|
self.update_advance_paid()
|
||||||
@@ -76,26 +72,36 @@ class JournalVoucher(AccountsController):
|
|||||||
|
|
||||||
def validate_entries_for_advance(self):
|
def validate_entries_for_advance(self):
|
||||||
for d in self.get('entries'):
|
for d in self.get('entries'):
|
||||||
if not d.is_advance and not d.against_voucher and \
|
if not (d.against_voucher and d.against_invoice and d.against_jv):
|
||||||
not d.against_invoice and not d.against_jv:
|
|
||||||
master_type = frappe.db.get_value("Account", d.account, "master_type")
|
master_type = frappe.db.get_value("Account", d.account, "master_type")
|
||||||
if (master_type == 'Customer' and flt(d.credit) > 0) or \
|
if (master_type == 'Customer' and flt(d.credit) > 0) or \
|
||||||
(master_type == 'Supplier' and flt(d.debit) > 0):
|
(master_type == 'Supplier' and flt(d.debit) > 0):
|
||||||
msgprint(_("Row {0}: Please check 'Is Advance' against Account {1} if this \
|
if not d.is_advance:
|
||||||
is an advance entry.").format(d.idx, d.account))
|
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):
|
def validate_against_jv(self):
|
||||||
for d in self.get('entries'):
|
for d in self.get('entries'):
|
||||||
if d.against_jv:
|
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:
|
if d.against_jv == self.name:
|
||||||
frappe.throw(_("You can not enter current voucher in 'Against Journal Voucher' column"))
|
frappe.throw(_("You can not enter current voucher in 'Against Journal Voucher' column"))
|
||||||
|
|
||||||
against_entries = frappe.db.sql("""select * from `tabJournal Voucher Detail`
|
against_entries = frappe.db.sql("""select * from `tabJournal Voucher Detail`
|
||||||
where account = %s and docstatus = 1 and parent = %s
|
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:
|
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))
|
.format(d.against_jv, d.account))
|
||||||
else:
|
else:
|
||||||
dr_or_cr = "debit" if d.credit > 0 else "credit"
|
dr_or_cr = "debit" if d.credit > 0 else "credit"
|
||||||
@@ -152,7 +158,7 @@ class JournalVoucher(AccountsController):
|
|||||||
and voucher_account != d.account:
|
and voucher_account != d.account:
|
||||||
frappe.throw(_("Row {0}: Account {1} does not match with {2} {3} account") \
|
frappe.throw(_("Row {0}: Account {1} does not match with {2} {3} account") \
|
||||||
.format(d.idx, d.account, doctype, field_dict.get(doctype)))
|
.format(d.idx, d.account, doctype, field_dict.get(doctype)))
|
||||||
|
|
||||||
if against_field in ["against_sales_order", "against_purchase_order"]:
|
if against_field in ["against_sales_order", "against_purchase_order"]:
|
||||||
if voucher_account != account_master_name:
|
if voucher_account != account_master_name:
|
||||||
frappe.throw(_("Row {0}: Account {1} does not match with {2} {3} Name") \
|
frappe.throw(_("Row {0}: Account {1} does not match with {2} {3} Name") \
|
||||||
@@ -164,7 +170,7 @@ class JournalVoucher(AccountsController):
|
|||||||
|
|
||||||
def validate_against_invoice_fields(self, doctype, payment_against_voucher):
|
def validate_against_invoice_fields(self, doctype, payment_against_voucher):
|
||||||
for voucher_no, payment_list in payment_against_voucher.items():
|
for voucher_no, payment_list in payment_against_voucher.items():
|
||||||
voucher_properties = frappe.db.get_value(doctype, voucher_no,
|
voucher_properties = frappe.db.get_value(doctype, voucher_no,
|
||||||
["docstatus", "outstanding_amount"])
|
["docstatus", "outstanding_amount"])
|
||||||
|
|
||||||
if voucher_properties[0] != 1:
|
if voucher_properties[0] != 1:
|
||||||
@@ -176,8 +182,8 @@ class JournalVoucher(AccountsController):
|
|||||||
|
|
||||||
def validate_against_order_fields(self, doctype, payment_against_voucher):
|
def validate_against_order_fields(self, doctype, payment_against_voucher):
|
||||||
for voucher_no, payment_list in payment_against_voucher.items():
|
for voucher_no, payment_list in payment_against_voucher.items():
|
||||||
voucher_properties = frappe.db.get_value(doctype, voucher_no,
|
voucher_properties = frappe.db.get_value(doctype, voucher_no,
|
||||||
["docstatus", "per_billed", "advance_paid", "grand_total"])
|
["docstatus", "per_billed", "status", "advance_paid", "grand_total"])
|
||||||
|
|
||||||
if voucher_properties[0] != 1:
|
if voucher_properties[0] != 1:
|
||||||
frappe.throw(_("{0} {1} is not submitted").format(doctype, voucher_no))
|
frappe.throw(_("{0} {1} is not submitted").format(doctype, voucher_no))
|
||||||
@@ -185,7 +191,10 @@ class JournalVoucher(AccountsController):
|
|||||||
if flt(voucher_properties[1]) >= 100:
|
if flt(voucher_properties[1]) >= 100:
|
||||||
frappe.throw(_("{0} {1} is fully billed").format(doctype, voucher_no))
|
frappe.throw(_("{0} {1} is fully billed").format(doctype, voucher_no))
|
||||||
|
|
||||||
if flt(voucher_properties[3]) < flt(voucher_properties[2]) + flt(sum(payment_list)):
|
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 \
|
frappe.throw(_("Advance paid against {0} {1} cannot be greater \
|
||||||
than Grand Total {2}").format(doctype, voucher_no, voucher_properties[3]))
|
than Grand Total {2}").format(doctype, voucher_no, voucher_properties[3]))
|
||||||
|
|
||||||
@@ -276,71 +285,38 @@ class JournalVoucher(AccountsController):
|
|||||||
|
|
||||||
def set_print_format_fields(self):
|
def set_print_format_fields(self):
|
||||||
for d in self.get('entries'):
|
for d in self.get('entries'):
|
||||||
result = frappe.db.get_value("Account", d.account,
|
acc = frappe.db.get_value("Account", d.account, ["account_type", "master_type"], as_dict=1)
|
||||||
["account_type", "master_type"])
|
|
||||||
|
|
||||||
if not result:
|
if not acc: continue
|
||||||
continue
|
|
||||||
|
|
||||||
account_type, master_type = result
|
if acc.master_type in ['Supplier', 'Customer']:
|
||||||
|
|
||||||
if master_type in ['Supplier', 'Customer']:
|
|
||||||
if not self.pay_to_recd_from:
|
if not self.pay_to_recd_from:
|
||||||
self.pay_to_recd_from = frappe.db.get_value(master_type,
|
self.pay_to_recd_from = frappe.db.get_value(acc.master_type, ' - '.join(d.account.split(' - ')[:-1]),
|
||||||
' - '.join(d.account.split(' - ')[:-1]),
|
acc.master_type == 'Customer' and 'customer_name' or 'supplier_name')
|
||||||
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']:
|
if acc.account_type in ['Bank', 'Cash']:
|
||||||
company_currency = get_company_currency(self.company)
|
self.set_total_amount(d.debit or d.credit)
|
||||||
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)
|
|
||||||
|
|
||||||
def check_credit_days(self):
|
def set_total_amount(self, amt):
|
||||||
date_diff = 0
|
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:
|
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
|
if due_date and getdate(self.cheque_date) > getdate(due_date):
|
||||||
|
msgprint(_("Note: Reference Date {0} is after invoice due date {1}")
|
||||||
# Get List of Customer Account
|
.format(formatdate(self.cheque_date), formatdate(due_date)))
|
||||||
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
|
|
||||||
|
|
||||||
def make_gl_entries(self, cancel=0, adv_adj=0):
|
def make_gl_entries(self, cancel=0, adv_adj=0):
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries
|
from erpnext.accounts.general_ledger import make_gl_entries
|
||||||
@@ -373,7 +349,7 @@ class JournalVoucher(AccountsController):
|
|||||||
for d in self.get("entries"):
|
for d in self.get("entries"):
|
||||||
master_type, master_name = frappe.db.get_value("Account", d.account,
|
master_type, master_name = frappe.db.get_value("Account", d.account,
|
||||||
["master_type", "master_name"])
|
["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)
|
super(JournalVoucher, self).check_credit_limit(d.account)
|
||||||
|
|
||||||
def get_balance(self):
|
def get_balance(self):
|
||||||
@@ -528,9 +504,10 @@ def get_against_sales_invoice(doctype, txt, searchfield, start, page_len, filter
|
|||||||
(filters["account"], "%%%s%%" % txt, start, page_len))
|
(filters["account"], "%%%s%%" % txt, start, page_len))
|
||||||
|
|
||||||
def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
|
def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
|
||||||
return frappe.db.sql("""select jv.name, jv.posting_date, jv.user_remark
|
return frappe.db.sql("""select distinct jv.name, jv.posting_date, jv.user_remark
|
||||||
from `tabJournal Voucher` jv, `tabJournal Voucher Detail` jv_detail
|
from `tabJournal Voucher` jv, `tabJournal Voucher Detail` jvd
|
||||||
where jv_detail.parent = jv.name and jv_detail.account = %s and jv.docstatus = 1
|
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""" %
|
and jv.%s like %s order by jv.name desc limit %s, %s""" %
|
||||||
("%s", searchfield, "%s", "%s", "%s"),
|
("%s", searchfield, "%s", "%s", "%s"),
|
||||||
(filters["account"], "%%%s%%" % txt, start, page_len))
|
(filters["account"], "%%%s%%" % txt, start, page_len))
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.frm.set_query('bank_cash_account', function() {
|
this.frm.set_query('bank_cash_account', function() {
|
||||||
if(!me.frm.doc.company) {
|
if(!me.frm.doc.company) {
|
||||||
msgprint(__("Please select company first"));
|
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() {
|
get_unreconciled_entries: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
return this.frm.call({
|
return this.frm.call({
|
||||||
@@ -48,12 +44,12 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
|||||||
method: 'get_unreconciled_entries',
|
method: 'get_unreconciled_entries',
|
||||||
callback: function(r, rt) {
|
callback: function(r, rt) {
|
||||||
var invoices = [];
|
var invoices = [];
|
||||||
|
|
||||||
$.each(me.frm.doc.payment_reconciliation_invoices || [], function(i, row) {
|
$.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);
|
invoices.push(row.invoice_number);
|
||||||
});
|
});
|
||||||
|
|
||||||
frappe.meta.get_docfield("Payment Reconciliation Payment", "invoice_number",
|
frappe.meta.get_docfield("Payment Reconciliation Payment", "invoice_number",
|
||||||
me.frm.doc.name).options = invoices.join("\n");
|
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}));
|
$.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",
|
"options": "Payment Reconciliation Invoice",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "reconcile_help",
|
|
||||||
"fieldtype": "Small Text",
|
|
||||||
"label": "",
|
|
||||||
"permlevel": 0,
|
|
||||||
"read_only": 1
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hide_toolbar": 1,
|
"hide_toolbar": 1,
|
||||||
"icon": "icon-resize-horizontal",
|
"icon": "icon-resize-horizontal",
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"modified": "2014-07-31 05:43:03.410832",
|
"modified": "2014-10-16 17:51:44.367107",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Reconciliation",
|
"name": "Payment Reconciliation",
|
||||||
|
|||||||
@@ -96,13 +96,14 @@ class PaymentReconciliation(Document):
|
|||||||
|
|
||||||
payment_amount = payment_amount[0][0] if payment_amount else 0
|
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({
|
non_reconciled_invoices.append({
|
||||||
'voucher_no': d.voucher_no,
|
'voucher_no': d.voucher_no,
|
||||||
'voucher_type': d.voucher_type,
|
'voucher_type': d.voucher_type,
|
||||||
'posting_date': d.posting_date,
|
'posting_date': d.posting_date,
|
||||||
'invoice_amount': flt(d.invoice_amount),
|
'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)
|
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"
|
dr_or_cr = "credit" if self.party_type == "Customer" else "debit"
|
||||||
lst = []
|
lst = []
|
||||||
for e in self.get('payment_reconciliation_payments'):
|
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({
|
lst.append({
|
||||||
'voucher_no' : e.journal_voucher,
|
'voucher_no' : e.journal_voucher,
|
||||||
'voucher_detail_no' : e.voucher_detail_number,
|
'voucher_detail_no' : e.voucher_detail_number,
|
||||||
@@ -134,7 +135,7 @@ class PaymentReconciliation(Document):
|
|||||||
'is_advance' : e.is_advance,
|
'is_advance' : e.is_advance,
|
||||||
'dr_or_cr' : dr_or_cr,
|
'dr_or_cr' : dr_or_cr,
|
||||||
'unadjusted_amt' : flt(e.amount),
|
'unadjusted_amt' : flt(e.amount),
|
||||||
'allocated_amt' : flt(e.amount)
|
'allocated_amt' : flt(e.allocated_amount)
|
||||||
})
|
})
|
||||||
|
|
||||||
if lst:
|
if lst:
|
||||||
@@ -162,18 +163,23 @@ class PaymentReconciliation(Document):
|
|||||||
|
|
||||||
invoices_to_reconcile = []
|
invoices_to_reconcile = []
|
||||||
for p in self.get("payment_reconciliation_payments"):
|
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)
|
invoices_to_reconcile.append(p.invoice_number)
|
||||||
|
|
||||||
if p.invoice_number not in unreconciled_invoices.get(p.invoice_type, {}):
|
if p.invoice_number not in unreconciled_invoices.get(p.invoice_type, {}):
|
||||||
frappe.throw(_("{0}: {1} not found in Invoice Details table")
|
frappe.throw(_("{0}: {1} not found in Invoice Details table")
|
||||||
.format(p.invoice_type, p.invoice_number))
|
.format(p.invoice_type, p.invoice_number))
|
||||||
|
|
||||||
if p.amount > unreconciled_invoices.get(p.invoice_type, {}).get(p.invoice_number):
|
if flt(p.allocated_amount) > flt(p.amount):
|
||||||
frappe.throw(_("Row {0}: Payment amount must be less than or equals to invoice outstanding amount. Please refer Note below.").format(p.idx))
|
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:
|
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):
|
def check_condition(self, dr_or_cr):
|
||||||
cond = self.from_date and " and posting_date >= '" + self.from_date + "'" or ""
|
cond = self.from_date and " and posting_date >= '" + self.from_date + "'" or ""
|
||||||
|
|||||||
@@ -53,11 +53,20 @@
|
|||||||
"label": "Column Break",
|
"label": "Column Break",
|
||||||
"permlevel": 0
|
"permlevel": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "allocated_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Allocated amount",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"default": "Sales Invoice",
|
"default": "Sales Invoice",
|
||||||
"fieldname": "invoice_type",
|
"fieldname": "invoice_type",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"in_list_view": 1,
|
"in_list_view": 0,
|
||||||
"label": "Invoice Type",
|
"label": "Invoice Type",
|
||||||
"options": "\nSales Invoice\nPurchase Invoice\nJournal Voucher",
|
"options": "\nSales Invoice\nPurchase Invoice\nJournal Voucher",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
@@ -95,7 +104,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2014-07-21 16:53:56.206169",
|
"modified": "2014-10-16 17:40:54.040194",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Reconciliation Payment",
|
"name": "Payment Reconciliation Payment",
|
||||||
|
|||||||
@@ -42,9 +42,6 @@ frappe.ui.form.on("Payment Tool", "received_or_paid", function(frm) {
|
|||||||
erpnext.payment_tool.check_mandatory_to_set_button(frm);
|
erpnext.payment_tool.check_mandatory_to_set_button(frm);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fetch bank/cash account based on payment mode
|
|
||||||
cur_frm.add_fetch("payment_mode", "default_account", "payment_account");
|
|
||||||
|
|
||||||
// Set party account name
|
// Set party account name
|
||||||
frappe.ui.form.on("Payment Tool", "customer", function(frm) {
|
frappe.ui.form.on("Payment Tool", "customer", function(frm) {
|
||||||
erpnext.payment_tool.set_party_account(frm);
|
erpnext.payment_tool.set_party_account(frm);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _, scrub
|
from frappe import _
|
||||||
from frappe.utils import flt
|
from frappe.utils import flt
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
import json
|
import json
|
||||||
@@ -91,8 +91,9 @@ def get_orders_to_be_billed(party_type, party_name):
|
|||||||
where
|
where
|
||||||
%s = %s
|
%s = %s
|
||||||
and docstatus = 1
|
and docstatus = 1
|
||||||
|
and ifnull(status, "") != "Stopped"
|
||||||
and ifnull(grand_total, 0) > ifnull(advance_paid, 0)
|
and ifnull(grand_total, 0) > ifnull(advance_paid, 0)
|
||||||
and ifnull(per_billed, 0) < 100.0
|
and abs(100 - ifnull(per_billed, 0)) > 0.01
|
||||||
""" % (voucher_type, 'customer' if party_type == "Customer" else 'supplier', '%s'),
|
""" % (voucher_type, 'customer' if party_type == "Customer" else 'supplier', '%s'),
|
||||||
party_name, as_dict = True)
|
party_name, as_dict = True)
|
||||||
|
|
||||||
|
|||||||
@@ -31,9 +31,10 @@ class TestPaymentTool(unittest.TestCase):
|
|||||||
"customer": "_Test Customer 3"
|
"customer": "_Test Customer 3"
|
||||||
})
|
})
|
||||||
|
|
||||||
jv_against_so1 = self.create_against_jv(jv_test_records[0], {
|
jv_against_so1 = self.create_against_jv(jv_test_records[0], {
|
||||||
"account": "_Test Customer 3 - _TC",
|
"account": "_Test Customer 3 - _TC",
|
||||||
"against_sales_order": so1.name
|
"against_sales_order": so1.name,
|
||||||
|
"is_advance": "Yes"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@@ -42,10 +43,11 @@ class TestPaymentTool(unittest.TestCase):
|
|||||||
"customer": "_Test Customer 3"
|
"customer": "_Test Customer 3"
|
||||||
})
|
})
|
||||||
|
|
||||||
jv_against_so2 = self.create_against_jv(jv_test_records[0], {
|
jv_against_so2 = self.create_against_jv(jv_test_records[0], {
|
||||||
"account": "_Test Customer 3 - _TC",
|
"account": "_Test Customer 3 - _TC",
|
||||||
"against_sales_order": so2.name,
|
"against_sales_order": so2.name,
|
||||||
"credit": 1000
|
"credit": 1000,
|
||||||
|
"is_advance": "Yes"
|
||||||
})
|
})
|
||||||
po = self.create_voucher(po_test_records[1], {
|
po = self.create_voucher(po_test_records[1], {
|
||||||
"supplier": "_Test Supplier 1"
|
"supplier": "_Test Supplier 1"
|
||||||
@@ -54,20 +56,20 @@ class TestPaymentTool(unittest.TestCase):
|
|||||||
#Create SI with partial outstanding
|
#Create SI with partial outstanding
|
||||||
si1 = self.create_voucher(si_test_records[0], {
|
si1 = self.create_voucher(si_test_records[0], {
|
||||||
"customer": "_Test Customer 3",
|
"customer": "_Test Customer 3",
|
||||||
"debit_to": "_Test Customer 3 - _TC"
|
"debit_to": "_Test Customer 3 - _TC"
|
||||||
})
|
})
|
||||||
|
|
||||||
jv_against_si1 = self.create_against_jv(jv_test_records[0], {
|
jv_against_si1 = self.create_against_jv(jv_test_records[0], {
|
||||||
"account": "_Test Customer 3 - _TC",
|
"account": "_Test Customer 3 - _TC",
|
||||||
"against_invoice": si1.name
|
"against_invoice": si1.name
|
||||||
})
|
})
|
||||||
#Create SI with no outstanding
|
#Create SI with no outstanding
|
||||||
si2 = self.create_voucher(si_test_records[0], {
|
si2 = self.create_voucher(si_test_records[0], {
|
||||||
"customer": "_Test Customer 3",
|
"customer": "_Test Customer 3",
|
||||||
"debit_to": "_Test Customer 3 - _TC"
|
"debit_to": "_Test Customer 3 - _TC"
|
||||||
})
|
})
|
||||||
|
|
||||||
jv_against_si2 = self.create_against_jv(jv_test_records[0], {
|
jv_against_si2 = self.create_against_jv(jv_test_records[0], {
|
||||||
"account": "_Test Customer 3 - _TC",
|
"account": "_Test Customer 3 - _TC",
|
||||||
"against_invoice": si2.name,
|
"against_invoice": si2.name,
|
||||||
"credit": 561.80
|
"credit": 561.80
|
||||||
@@ -75,7 +77,7 @@ class TestPaymentTool(unittest.TestCase):
|
|||||||
|
|
||||||
pi = self.create_voucher(pi_test_records[0], {
|
pi = self.create_voucher(pi_test_records[0], {
|
||||||
"supplier": "_Test Supplier 1",
|
"supplier": "_Test Supplier 1",
|
||||||
"credit_to": "_Test Supplier 1 - _TC"
|
"credit_to": "_Test Supplier 1 - _TC"
|
||||||
})
|
})
|
||||||
|
|
||||||
#Create a dict containing properties and expected values
|
#Create a dict containing properties and expected values
|
||||||
@@ -137,7 +139,7 @@ class TestPaymentTool(unittest.TestCase):
|
|||||||
payment_tool_doc.set(k, v)
|
payment_tool_doc.set(k, v)
|
||||||
|
|
||||||
self.check_outstanding_vouchers(payment_tool_doc, args, expected_outstanding)
|
self.check_outstanding_vouchers(payment_tool_doc, args, expected_outstanding)
|
||||||
|
|
||||||
|
|
||||||
def check_outstanding_vouchers(self, 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
|
from erpnext.accounts.doctype.payment_tool.payment_tool import get_outstanding_vouchers
|
||||||
@@ -161,7 +163,7 @@ class TestPaymentTool(unittest.TestCase):
|
|||||||
|
|
||||||
new_jv = paytool.make_journal_voucher()
|
new_jv = paytool.make_journal_voucher()
|
||||||
|
|
||||||
#Create a list of expected values as [party account, payment against, against_jv, against_invoice,
|
#Create a list of expected values as [party account, payment against, against_jv, against_invoice,
|
||||||
#against_voucher, against_sales_order, against_purchase_order]
|
#against_voucher, against_sales_order, against_purchase_order]
|
||||||
expected_values = [
|
expected_values = [
|
||||||
[paytool.party_account, 100.00, expected_outstanding.get("Journal Voucher")[0], None, None, None, None],
|
[paytool.party_account, 100.00, expected_outstanding.get("Journal Voucher")[0], None, None, None, None],
|
||||||
@@ -171,7 +173,7 @@ class TestPaymentTool(unittest.TestCase):
|
|||||||
[paytool.party_account, 100.00, None, None, None, None, expected_outstanding.get("Purchase Order")[0]]
|
[paytool.party_account, 100.00, None, None, None, None, expected_outstanding.get("Purchase Order")[0]]
|
||||||
]
|
]
|
||||||
|
|
||||||
for jv_entry in new_jv.get("entries"):
|
for jv_entry in new_jv.get("entries"):
|
||||||
if paytool.party_account == jv_entry.get("account"):
|
if paytool.party_account == jv_entry.get("account"):
|
||||||
row = [
|
row = [
|
||||||
jv_entry.get("account"),
|
jv_entry.get("account"),
|
||||||
@@ -183,11 +185,11 @@ class TestPaymentTool(unittest.TestCase):
|
|||||||
jv_entry.get("against_purchase_order"),
|
jv_entry.get("against_purchase_order"),
|
||||||
]
|
]
|
||||||
self.assertTrue(row in expected_values)
|
self.assertTrue(row in expected_values)
|
||||||
|
|
||||||
self.assertEquals(new_jv.get("cheque_no"), paytool.reference_no)
|
self.assertEquals(new_jv.get("cheque_no"), paytool.reference_no)
|
||||||
self.assertEquals(new_jv.get("cheque_date"), paytool.reference_date)
|
self.assertEquals(new_jv.get("cheque_date"), paytool.reference_date)
|
||||||
|
|
||||||
def clear_table_entries(self):
|
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 `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 `tabSales Order` where customer_name = "_Test Customer 3" """)
|
||||||
frappe.db.sql("""delete from `tabPurchase Order` where supplier_name = "_Test Supplier 1" """)
|
frappe.db.sql("""delete from `tabPurchase Order` where supplier_name = "_Test Supplier 1" """)
|
||||||
|
|||||||
@@ -142,24 +142,6 @@
|
|||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 1
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"allow_on_submit": 1,
|
|
||||||
"description": "Start date of current invoice's period",
|
|
||||||
"fieldname": "from_date",
|
|
||||||
"fieldtype": "Date",
|
|
||||||
"label": "From Date",
|
|
||||||
"no_copy": 1,
|
|
||||||
"permlevel": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_on_submit": 1,
|
|
||||||
"description": "End date of current invoice's period",
|
|
||||||
"fieldname": "to_date",
|
|
||||||
"fieldtype": "Date",
|
|
||||||
"label": "To Date",
|
|
||||||
"no_copy": 1,
|
|
||||||
"permlevel": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "amended_from",
|
"fieldname": "amended_from",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
@@ -810,6 +792,28 @@
|
|||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1
|
"print_hide": 1
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"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": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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": 1
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
"depends_on": "eval:doc.is_recurring==1",
|
"depends_on": "eval:doc.is_recurring==1",
|
||||||
@@ -821,17 +825,6 @@
|
|||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1
|
"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
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
"depends_on": "eval:doc.is_recurring==1",
|
"depends_on": "eval:doc.is_recurring==1",
|
||||||
@@ -850,6 +843,17 @@
|
|||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"width": "50%"
|
"width": "50%"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"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",
|
"depends_on": "eval:doc.is_recurring==1",
|
||||||
"description": "The unique id for tracking all recurring invoices. It is generated on submit.",
|
"description": "The unique id for tracking all recurring invoices. It is generated on submit.",
|
||||||
@@ -876,7 +880,7 @@
|
|||||||
"icon": "icon-file-text",
|
"icon": "icon-file-text",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2014-09-18 03:12:51.994059",
|
"modified": "2014-11-27 17:28:20.133701",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Invoice",
|
"name": "Purchase Invoice",
|
||||||
|
|||||||
@@ -233,6 +233,11 @@ erpnext.POS = Class.extend({
|
|||||||
},
|
},
|
||||||
make_item_list: function() {
|
make_item_list: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
|
if(!this.price_list) {
|
||||||
|
msgprint(__("Price List not found or disabled"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
me.item_timeout = null;
|
me.item_timeout = null;
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: 'erpnext.accounts.doctype.sales_invoice.pos.get_items',
|
method: 'erpnext.accounts.doctype.sales_invoice.pos.get_items',
|
||||||
|
|||||||
@@ -169,28 +169,25 @@
|
|||||||
"search_index": 0
|
"search_index": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 1,
|
"fieldname": "shipping_address_name",
|
||||||
"depends_on": "",
|
"fieldtype": "Link",
|
||||||
"description": "Start date of current invoice's period",
|
"hidden": 1,
|
||||||
"fieldname": "from_date",
|
"in_filter": 1,
|
||||||
"fieldtype": "Date",
|
"label": "Shipping Address Name",
|
||||||
"label": "From Date",
|
"options": "Address",
|
||||||
"no_copy": 1,
|
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"precision": "",
|
||||||
"read_only": 0
|
"print_hide": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 1,
|
"fieldname": "shipping_address",
|
||||||
"depends_on": "",
|
"fieldtype": "Small Text",
|
||||||
"description": "End date of current invoice's period",
|
"hidden": 1,
|
||||||
"fieldname": "to_date",
|
"label": "Shipping Address",
|
||||||
"fieldtype": "Date",
|
|
||||||
"label": "To Date",
|
|
||||||
"no_copy": 1,
|
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"precision": "",
|
||||||
"read_only": 0
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "currency_section",
|
"fieldname": "currency_section",
|
||||||
@@ -1108,6 +1105,30 @@
|
|||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 0
|
"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,
|
"allow_on_submit": 1,
|
||||||
"depends_on": "eval:doc.is_recurring==1",
|
"depends_on": "eval:doc.is_recurring==1",
|
||||||
@@ -1120,17 +1141,6 @@
|
|||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 0
|
"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,
|
"allow_on_submit": 1,
|
||||||
"depends_on": "eval:doc.is_recurring==1",
|
"depends_on": "eval:doc.is_recurring==1",
|
||||||
@@ -1152,6 +1162,17 @@
|
|||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"width": "50%"
|
"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",
|
"depends_on": "eval:doc.is_recurring==1",
|
||||||
"description": "The unique id for tracking all recurring invoices.\u00a0It is generated on submit.",
|
"description": "The unique id for tracking all recurring invoices.\u00a0It is generated on submit.",
|
||||||
@@ -1192,7 +1213,7 @@
|
|||||||
"icon": "icon-file-text",
|
"icon": "icon-file-text",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2014-09-18 03:17:54.976732",
|
"modified": "2014-10-10 16:54:22.284284",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice",
|
"name": "Sales Invoice",
|
||||||
|
|||||||
@@ -97,8 +97,7 @@ def validate_account_for_auto_accounting_for_stock(gl_map):
|
|||||||
|
|
||||||
for entry in gl_map:
|
for entry in gl_map:
|
||||||
if entry.account in aii_accounts:
|
if entry.account in aii_accounts:
|
||||||
frappe.throw(_("Account: {0} can only be updated via \
|
frappe.throw(_("Account: {0} can only be updated via Stock Transactions").format(entry.account), StockAccountInvalidTransaction)
|
||||||
Stock Transactions").format(entry.account), StockAccountInvalidTransaction)
|
|
||||||
|
|
||||||
|
|
||||||
def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
|
def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
"doc_type": "Journal Voucher",
|
"doc_type": "Journal Voucher",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Print Format",
|
"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-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",
|
||||||
"idx": 2,
|
"idx": 2,
|
||||||
"modified": "2014-08-29 13:20:15.789533",
|
"modified": "2014-10-17 17:20:02.740340",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Credit Note",
|
"name": "Credit Note",
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
"doc_type": "Journal Voucher",
|
"doc_type": "Journal Voucher",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Print Format",
|
"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",
|
"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 or 0 + \"</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,
|
"idx": 1,
|
||||||
"modified": "2014-08-29 15:55:34.248384",
|
"modified": "2014-11-04 11:25:57.560873",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Receipt Voucher",
|
"name": "Payment Receipt Voucher",
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ def execute(filters=None):
|
|||||||
data = []
|
data = []
|
||||||
for gle in entries:
|
for gle in entries:
|
||||||
if cstr(gle.against_voucher) == gle.voucher_no or not gle.against_voucher \
|
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, {})
|
voucher_details = voucher_detail_map.get(gle.voucher_type, {}).get(gle.voucher_no, {})
|
||||||
|
|
||||||
invoiced_amount = gle.credit > 0 and gle.credit or 0
|
invoiced_amount = gle.credit > 0 and gle.credit or 0
|
||||||
|
|||||||
@@ -13,17 +13,17 @@ class AccountsReceivableReport(object):
|
|||||||
self.age_as_on = getdate(nowdate()) \
|
self.age_as_on = getdate(nowdate()) \
|
||||||
if self.filters.report_date > getdate(nowdate()) \
|
if self.filters.report_date > getdate(nowdate()) \
|
||||||
else self.filters.report_date
|
else self.filters.report_date
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
customer_naming_by = frappe.db.get_value("Selling Settings", None, "cust_master_name")
|
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)
|
return self.get_columns(customer_naming_by), self.get_data(customer_naming_by)
|
||||||
|
|
||||||
def get_columns(self, customer_naming_by):
|
def get_columns(self, customer_naming_by):
|
||||||
columns = [
|
columns = [
|
||||||
_("Posting Date") + ":Date:80", _("Account") + ":Link/Account:150",
|
_("Posting Date") + ":Date:80", _("Account") + ":Link/Account:150",
|
||||||
_("Voucher Type") + "::110", _("Voucher No") + "::120", "::30",
|
_("Voucher Type") + "::110", _("Voucher No") + ":Dynamic Link/Voucher Type:120",
|
||||||
_("Due Date") + ":Date:80",
|
_("Due Date") + ":Date:80",
|
||||||
_("Invoiced Amount") + ":Currency:100", _("Payment Received") + ":Currency:100",
|
_("Invoiced Amount") + ":Currency:100", _("Payment Received") + ":Currency:100",
|
||||||
_("Outstanding Amount") + ":Currency:100", _("Age") + ":Int:50", "0-30: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",
|
"30-60:Currency:100", "60-90:Currency:100", _("90-Above") + ":Currency:100",
|
||||||
_("Customer") + ":Link/Customer:200"
|
_("Customer") + ":Link/Customer:200"
|
||||||
@@ -63,35 +63,33 @@ class AccountsReceivableReport(object):
|
|||||||
|
|
||||||
row += [self.get_territory(gle.account), gle.remarks]
|
row += [self.get_territory(gle.account), gle.remarks]
|
||||||
data.append(row)
|
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
|
return data
|
||||||
|
|
||||||
def get_entries_after(self, report_date):
|
def get_entries_after(self, report_date):
|
||||||
# returns a distinct list
|
# returns a distinct list
|
||||||
return list(set([(e.voucher_type, e.voucher_no) for e in self.get_gl_entries()
|
return list(set([(e.voucher_type, e.voucher_no) for e in self.get_gl_entries()
|
||||||
if getdate(e.posting_date) > report_date]))
|
if getdate(e.posting_date) > report_date]))
|
||||||
|
|
||||||
def get_entries_till(self, report_date):
|
def get_entries_till(self, report_date):
|
||||||
# returns a generator
|
# 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)
|
if getdate(e.posting_date) <= report_date)
|
||||||
|
|
||||||
def is_receivable(self, gle, future_vouchers):
|
def is_receivable(self, gle, future_vouchers):
|
||||||
return (
|
return (
|
||||||
# advance
|
# advance
|
||||||
(not gle.against_voucher) or
|
(not gle.against_voucher) or
|
||||||
|
|
||||||
|
# against sales order
|
||||||
|
(gle.against_voucher_type == "Sales Order") or
|
||||||
|
|
||||||
# sales invoice
|
# 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
|
# entries adjusted with future vouchers
|
||||||
((gle.against_voucher_type, gle.against_voucher) in future_vouchers)
|
((gle.against_voucher_type, gle.against_voucher) in future_vouchers)
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_outstanding_amount(self, gle, report_date):
|
def get_outstanding_amount(self, gle, report_date):
|
||||||
payment_received = 0.0
|
payment_received = 0.0
|
||||||
for e in self.get_gl_entries_for(gle.account, gle.voucher_type, gle.voucher_no):
|
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))
|
payment_received += (flt(e.credit) - flt(e.debit))
|
||||||
|
|
||||||
return flt(gle.debit) - flt(gle.credit) - payment_received
|
return flt(gle.debit) - flt(gle.credit) - payment_received
|
||||||
|
|
||||||
def get_customer(self, account):
|
def get_customer(self, account):
|
||||||
return self.get_account_map().get(account, {}).get("customer") or ""
|
return self.get_account_map().get(account, {}).get("customer") or ""
|
||||||
|
|
||||||
@@ -108,25 +106,25 @@ class AccountsReceivableReport(object):
|
|||||||
|
|
||||||
def get_territory(self, account):
|
def get_territory(self, account):
|
||||||
return self.get_account_map().get(account, {}).get("territory") or ""
|
return self.get_account_map().get(account, {}).get("territory") or ""
|
||||||
|
|
||||||
def get_account_map(self):
|
def get_account_map(self):
|
||||||
if not hasattr(self, "account_map"):
|
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
|
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)))
|
on cust.name=acc.master_name where acc.master_type="Customer" """, as_dict=True)))
|
||||||
|
|
||||||
return self.account_map
|
return self.account_map
|
||||||
|
|
||||||
def get_due_date(self, gle):
|
def get_due_date(self, gle):
|
||||||
if not hasattr(self, "invoice_due_date_map"):
|
if not hasattr(self, "invoice_due_date_map"):
|
||||||
# TODO can be restricted to posting date
|
# TODO can be restricted to posting date
|
||||||
self.invoice_due_date_map = dict(frappe.db.sql("""select name, due_date
|
self.invoice_due_date_map = dict(frappe.db.sql("""select name, due_date
|
||||||
from `tabSales Invoice` where docstatus=1"""))
|
from `tabSales Invoice` where docstatus=1"""))
|
||||||
|
|
||||||
return gle.voucher_type == "Sales Invoice" \
|
return gle.voucher_type == "Sales Invoice" \
|
||||||
and self.invoice_due_date_map.get(gle.voucher_no) or ""
|
and self.invoice_due_date_map.get(gle.voucher_no) or ""
|
||||||
|
|
||||||
def get_gl_entries(self):
|
def get_gl_entries(self):
|
||||||
if not hasattr(self, "gl_entries"):
|
if not hasattr(self, "gl_entries"):
|
||||||
conditions, values = self.prepare_conditions()
|
conditions, values = self.prepare_conditions()
|
||||||
@@ -134,15 +132,15 @@ class AccountsReceivableReport(object):
|
|||||||
where docstatus < 2 {0} order by posting_date, account""".format(conditions),
|
where docstatus < 2 {0} order by posting_date, account""".format(conditions),
|
||||||
values, as_dict=True)
|
values, as_dict=True)
|
||||||
return self.gl_entries
|
return self.gl_entries
|
||||||
|
|
||||||
def prepare_conditions(self):
|
def prepare_conditions(self):
|
||||||
conditions = [""]
|
conditions = [""]
|
||||||
values = {}
|
values = {}
|
||||||
|
|
||||||
if self.filters.company:
|
if self.filters.company:
|
||||||
conditions.append("company=%(company)s")
|
conditions.append("company=%(company)s")
|
||||||
values["company"] = self.filters.company
|
values["company"] = self.filters.company
|
||||||
|
|
||||||
if self.filters.account:
|
if self.filters.account:
|
||||||
conditions.append("account=%(account)s")
|
conditions.append("account=%(account)s")
|
||||||
values["account"] = self.filters.account
|
values["account"] = self.filters.account
|
||||||
@@ -151,11 +149,11 @@ class AccountsReceivableReport(object):
|
|||||||
if not account_map:
|
if not account_map:
|
||||||
frappe.throw(_("No Customer Accounts found."))
|
frappe.throw(_("No Customer Accounts found."))
|
||||||
else:
|
else:
|
||||||
accounts_list = ['"{0}"'.format(ac) for ac in account_map]
|
accounts_list = ['"{0}"'.format(ac.replace('"', '\"')) for ac in account_map]
|
||||||
conditions.append("account in ({0})".format(", ".join(accounts_list)))
|
conditions.append("account in ({0})".format(", ".join(accounts_list)))
|
||||||
|
|
||||||
return " and ".join(conditions), values
|
return " and ".join(conditions), values
|
||||||
|
|
||||||
def get_gl_entries_for(self, account, against_voucher_type, against_voucher):
|
def get_gl_entries_for(self, account, against_voucher_type, against_voucher):
|
||||||
if not hasattr(self, "gl_entries_map"):
|
if not hasattr(self, "gl_entries_map"):
|
||||||
self.gl_entries_map = {}
|
self.gl_entries_map = {}
|
||||||
@@ -165,7 +163,7 @@ class AccountsReceivableReport(object):
|
|||||||
.setdefault(gle.against_voucher_type, {})\
|
.setdefault(gle.against_voucher_type, {})\
|
||||||
.setdefault(gle.against_voucher, [])\
|
.setdefault(gle.against_voucher, [])\
|
||||||
.append(gle)
|
.append(gle)
|
||||||
|
|
||||||
return self.gl_entries_map.get(account, {})\
|
return self.gl_entries_map.get(account, {})\
|
||||||
.get(against_voucher_type, {})\
|
.get(against_voucher_type, {})\
|
||||||
.get(against_voucher, [])
|
.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]
|
outstanding_range = [0.0, 0.0, 0.0, 0.0]
|
||||||
if not (age_as_on and entry_date):
|
if not (age_as_on and entry_date):
|
||||||
return [0] + outstanding_range
|
return [0] + outstanding_range
|
||||||
|
|
||||||
age = (getdate(age_as_on) - getdate(entry_date)).days or 0
|
age = (getdate(age_as_on) - getdate(entry_date)).days or 0
|
||||||
index = None
|
index = None
|
||||||
for i, days in enumerate([30, 60, 90]):
|
for i, days in enumerate([30, 60, 90]):
|
||||||
if age <= days:
|
if age <= days:
|
||||||
index = i
|
index = i
|
||||||
break
|
break
|
||||||
|
|
||||||
if index is None: index = 3
|
if index is None: index = 3
|
||||||
outstanding_range[index] = outstanding_amount
|
outstanding_range[index] = outstanding_amount
|
||||||
|
|
||||||
return [age] + outstanding_range
|
return [age] + outstanding_range
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ def validate_filters(filters, account_details):
|
|||||||
|
|
||||||
def get_columns():
|
def get_columns():
|
||||||
return [_("Posting Date") + ":Date:100", _("Account") + ":Link/Account:200", _("Debit") + ":Float:100",
|
return [_("Posting Date") + ":Date:100", _("Account") + ":Link/Account:200", _("Debit") + ":Float:100",
|
||||||
_("Credit") + ":Float:100", _("Voucher Type") + "::120", _("Voucher No") + "::160", _("Link") + "::20",
|
_("Credit") + ":Float:100", _("Voucher Type") + "::120", _("Voucher No") + ":Dynamic Link/Voucher Type:160",
|
||||||
_("Against Account") + "::120", _("Cost Center") + ":Link/Cost Center:100", _("Remarks") + "::400"]
|
_("Against Account") + "::120", _("Cost Center") + ":Link/Cost Center:100", _("Remarks") + "::400"]
|
||||||
|
|
||||||
def get_result(filters, account_details):
|
def get_result(filters, account_details):
|
||||||
@@ -162,15 +162,6 @@ def get_result_as_list(data):
|
|||||||
for d in data:
|
for d in data:
|
||||||
result.append([d.get("posting_date"), d.get("account"), d.get("debit"),
|
result.append([d.get("posting_date"), d.get("account"), d.get("debit"),
|
||||||
d.get("credit"), d.get("voucher_type"), d.get("voucher_no"),
|
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")])
|
d.get("against"), d.get("cost_center"), d.get("remarks")])
|
||||||
|
|
||||||
return result
|
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
|
|
||||||
|
|||||||
@@ -210,14 +210,15 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
|
|||||||
calculate_totals: function() {
|
calculate_totals: function() {
|
||||||
var tax_count = this.frm.tax_doclist.length;
|
var tax_count = this.frm.tax_doclist.length;
|
||||||
this.frm.doc.grand_total = flt(tax_count ?
|
this.frm.doc.grand_total = flt(tax_count ?
|
||||||
this.frm.tax_doclist[tax_count - 1].total : this.frm.doc.net_total,
|
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);
|
||||||
this.frm.doc.grand_total_import = flt(this.frm.doc.grand_total /
|
|
||||||
this.frm.doc.conversion_rate, precision("grand_total_import"));
|
|
||||||
|
|
||||||
this.frm.doc.total_tax = flt(this.frm.doc.grand_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"));
|
precision("total_tax"));
|
||||||
|
|
||||||
|
this.frm.doc.grand_total = flt(this.frm.doc.grand_total, precision("grand_total"));
|
||||||
|
this.frm.doc.grand_total_import = flt(this.frm.doc.grand_total_import, precision("grand_total_import"));
|
||||||
|
|
||||||
// rounded totals
|
// rounded totals
|
||||||
if(frappe.meta.get_docfield(this.frm.doc.doctype, "rounded_total", this.frm.doc.name)) {
|
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);
|
this.frm.doc.rounded_total = Math.round(this.frm.doc.grand_total);
|
||||||
|
|||||||
@@ -3,15 +3,13 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
from frappe.utils import flt
|
||||||
from frappe.utils import cstr, flt
|
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
|
||||||
from erpnext.stock.doctype.item.item import get_last_purchase_details
|
from erpnext.stock.doctype.item.item import get_last_purchase_details
|
||||||
from erpnext.controllers.buying_controller import BuyingController
|
from erpnext.controllers.buying_controller import BuyingController
|
||||||
|
|
||||||
class PurchaseCommon(BuyingController):
|
class PurchaseCommon(BuyingController):
|
||||||
|
|
||||||
def update_last_purchase_rate(self, obj, is_submit):
|
def update_last_purchase_rate(self, obj, is_submit):
|
||||||
"""updates last_purchase_rate in item table for each item"""
|
"""updates last_purchase_rate in item table for each item"""
|
||||||
|
|
||||||
@@ -123,27 +121,6 @@ class PurchaseCommon(BuyingController):
|
|||||||
else:
|
else:
|
||||||
chk_dupl_itm.append(f)
|
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):
|
def check_for_stopped_status(self, doctype, docname):
|
||||||
stopped = frappe.db.sql("""select name from `tab%s` where name = %s and
|
stopped = frappe.db.sql("""select name from `tab%s` where name = %s and
|
||||||
status = 'Stopped'""" % (doctype, '%s'), docname)
|
status = 'Stopped'""" % (doctype, '%s'), docname)
|
||||||
|
|||||||
@@ -101,24 +101,6 @@
|
|||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 1
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"allow_on_submit": 1,
|
|
||||||
"description": "Start date of current order's period",
|
|
||||||
"fieldname": "from_date",
|
|
||||||
"fieldtype": "Date",
|
|
||||||
"label": "From Date",
|
|
||||||
"no_copy": 1,
|
|
||||||
"permlevel": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_on_submit": 1,
|
|
||||||
"description": "End date of current order's period",
|
|
||||||
"fieldname": "to_date",
|
|
||||||
"fieldtype": "Date",
|
|
||||||
"label": "To Date",
|
|
||||||
"no_copy": 1,
|
|
||||||
"permlevel": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "amended_from",
|
"fieldname": "amended_from",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
@@ -703,7 +685,30 @@
|
|||||||
"label": "Recurring Type",
|
"label": "Recurring Type",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Monthly\nQuarterly\nHalf-yearly\nYearly",
|
"options": "Monthly\nQuarterly\nHalf-yearly\nYearly",
|
||||||
"permlevel": 0
|
"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,
|
"allow_on_submit": 1,
|
||||||
@@ -716,17 +721,6 @@
|
|||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1
|
"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
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
"depends_on": "eval:doc.is_recurring==1",
|
"depends_on": "eval:doc.is_recurring==1",
|
||||||
@@ -745,6 +739,17 @@
|
|||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1
|
"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",
|
"depends_on": "eval:doc.is_recurring==1",
|
||||||
"fieldname": "recurring_id",
|
"fieldname": "recurring_id",
|
||||||
@@ -770,7 +775,7 @@
|
|||||||
"icon": "icon-file-text",
|
"icon": "icon-file-text",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2014-09-18 03:16:06.299317",
|
"modified": "2014-11-27 17:27:38.839440",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Purchase Order",
|
"name": "Purchase Order",
|
||||||
|
|||||||
@@ -96,50 +96,45 @@ class PurchaseOrder(BuyingController):
|
|||||||
check_list.append(d.prevdoc_docname)
|
check_list.append(d.prevdoc_docname)
|
||||||
pc_obj.check_for_stopped_status( d.prevdoc_doctype, 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):
|
for mr, mr_item_rows in material_request_map.items():
|
||||||
from erpnext.stock.utils import update_bin
|
if mr and mr_item_rows:
|
||||||
pc_obj = frappe.get_doc('Purchase Common')
|
mr_obj = frappe.get_doc("Material Request", mr)
|
||||||
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
|
|
||||||
|
|
||||||
ind_qty, po_qty = 0, flt(d.qty) * flt(d.conversion_factor)
|
if mr_obj.status in ["Stopped", "Cancelled"]:
|
||||||
if is_stopped:
|
frappe.throw(_("Material Request {0} is cancelled or stopped").format(mr), frappe.InvalidStatusError)
|
||||||
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
|
|
||||||
|
|
||||||
# No updates in Material Request on Stop / Unstop
|
mr_obj.update_requested_qty(mr_item_rows)
|
||||||
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
|
|
||||||
|
|
||||||
if flt(qty) + flt(po_qty) > flt(max_qty):
|
def update_ordered_qty(self, po_item_rows=None):
|
||||||
curr_qty = flt(max_qty) - flt(qty)
|
"""update requested qty (before ordered_qty is updated)"""
|
||||||
# special case as there is no restriction
|
from erpnext.stock.utils import get_bin
|
||||||
# for Material Request - Purchase Order
|
|
||||||
curr_qty = curr_qty > 0 and curr_qty or 0
|
|
||||||
else:
|
|
||||||
curr_qty = flt(po_qty)
|
|
||||||
|
|
||||||
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
|
bin_doc = get_bin(item_code, warehouse)
|
||||||
args = {
|
bin_doc.ordered_qty = flt(ordered_qty[0][0]) if ordered_qty else 0
|
||||||
"item_code": d.item_code,
|
bin_doc.save()
|
||||||
"warehouse": d.warehouse,
|
|
||||||
"ordered_qty": (is_submit and 1 or -1) * flt(po_qty),
|
item_wh_list = []
|
||||||
"indented_qty": (is_submit and 1 or -1) * flt(ind_qty),
|
for d in self.get("po_details"):
|
||||||
"posting_date": self.transaction_date
|
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:
|
||||||
update_bin(args)
|
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):
|
def check_modified_date(self):
|
||||||
mod_db = frappe.db.sql("select modified from `tabPurchase Order` where name = %s",
|
mod_db = frappe.db.sql("select modified from `tabPurchase Order` where name = %s",
|
||||||
@@ -152,13 +147,11 @@ class PurchaseOrder(BuyingController):
|
|||||||
|
|
||||||
def update_status(self, status):
|
def update_status(self, status):
|
||||||
self.check_modified_date()
|
self.check_modified_date()
|
||||||
# step 1:=> Set Status
|
|
||||||
frappe.db.set(self,'status',cstr(status))
|
frappe.db.set(self,'status',cstr(status))
|
||||||
|
|
||||||
# step 2:=> Update Bin
|
self.update_requested_qty()
|
||||||
self.update_bin(is_submit = (status == 'Submitted') and 1 or 0, is_stopped = 1)
|
self.update_ordered_qty()
|
||||||
|
|
||||||
# step 3:=> Acknowledge user
|
|
||||||
msgprint(_("Status of {0} {1} is now {2}").format(self.doctype, self.name, status))
|
msgprint(_("Status of {0} {1} is now {2}").format(self.doctype, self.name, status))
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
@@ -167,7 +160,8 @@ class PurchaseOrder(BuyingController):
|
|||||||
purchase_controller = frappe.get_doc("Purchase Common")
|
purchase_controller = frappe.get_doc("Purchase Common")
|
||||||
|
|
||||||
self.update_prevdoc_status()
|
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,
|
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
|
||||||
self.company, self.grand_total)
|
self.company, self.grand_total)
|
||||||
@@ -192,8 +186,13 @@ class PurchaseOrder(BuyingController):
|
|||||||
throw(_("Purchase Invoice {0} is already submitted").format(", ".join(submitted)))
|
throw(_("Purchase Invoice {0} is already submitted").format(", ".join(submitted)))
|
||||||
|
|
||||||
frappe.db.set(self,'status','Cancelled')
|
frappe.db.set(self,'status','Cancelled')
|
||||||
|
|
||||||
self.update_prevdoc_status()
|
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)
|
pc_obj.update_last_purchase_rate(self, is_submit = 0)
|
||||||
|
|
||||||
def on_update(self):
|
def on_update(self):
|
||||||
@@ -248,7 +247,7 @@ def make_purchase_invoice(source_name, target_doc=None):
|
|||||||
def update_item(obj, target, source_parent):
|
def update_item(obj, target, source_parent):
|
||||||
target.amount = flt(obj.amount) - flt(obj.billed_amt)
|
target.amount = flt(obj.amount) - flt(obj.billed_amt)
|
||||||
target.base_amount = target.amount * flt(source_parent.conversion_rate)
|
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, {
|
doc = get_mapped_doc("Purchase Order", source_name, {
|
||||||
"Purchase Order": {
|
"Purchase Order": {
|
||||||
|
|||||||
@@ -29,8 +29,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
frappe.get_doc(pr).insert()
|
frappe.get_doc(pr).insert()
|
||||||
|
|
||||||
def test_ordered_qty(self):
|
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
|
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
|
||||||
|
|
||||||
po = frappe.copy_doc(test_records[0]).insert()
|
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.get("po_details")[0].item_code = "_Test Item"
|
||||||
po.submit()
|
po.submit()
|
||||||
|
|
||||||
self.assertEquals(frappe.db.get_value("Bin", {"item_code": "_Test Item",
|
self.assertEquals(self._get_ordered_qty("_Test Item", "_Test Warehouse - _TC"), existing_ordered_qty + 10)
|
||||||
"warehouse": "_Test Warehouse - _TC"}, "ordered_qty"), 10)
|
|
||||||
|
|
||||||
pr = make_purchase_receipt(po.name)
|
pr = make_purchase_receipt(po.name)
|
||||||
|
|
||||||
@@ -56,8 +54,9 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
pr.insert()
|
pr.insert()
|
||||||
pr.submit()
|
pr.submit()
|
||||||
|
|
||||||
self.assertEquals(flt(frappe.db.get_value("Bin", {"item_code": "_Test Item",
|
po.load_from_db()
|
||||||
"warehouse": "_Test Warehouse - _TC"}, "ordered_qty")), 6.0)
|
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)
|
frappe.db.set_value('Item', '_Test Item', 'tolerance', 50)
|
||||||
|
|
||||||
@@ -68,8 +67,16 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
pr1.insert()
|
pr1.insert()
|
||||||
pr1.submit()
|
pr1.submit()
|
||||||
|
|
||||||
self.assertEquals(flt(frappe.db.get_value("Bin", {"item_code": "_Test Item",
|
po.load_from_db()
|
||||||
"warehouse": "_Test Warehouse - _TC"}, "ordered_qty")), 0.0)
|
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):
|
def test_make_purchase_invoice(self):
|
||||||
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_invoice
|
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_invoice
|
||||||
@@ -111,7 +118,10 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
from erpnext.controllers.tests.test_recurring_document import test_recurring_document
|
from erpnext.controllers.tests.test_recurring_document import test_recurring_document
|
||||||
test_recurring_document(self, test_records)
|
test_recurring_document(self, test_records)
|
||||||
|
|
||||||
|
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"]
|
|
||||||
|
test_dependencies = ["BOM", "Item Price"]
|
||||||
|
|
||||||
test_records = frappe.get_test_records('Purchase Order')
|
test_records = frappe.get_test_records('Purchase Order')
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||||
// License: GNU General Public License v3. See license.txt
|
// 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.refresh = function(doc, dt, dn) {
|
||||||
cur_frm.cscript.make_dashboard(doc);
|
cur_frm.cscript.make_dashboard(doc);
|
||||||
|
|
||||||
@@ -17,15 +15,13 @@ cur_frm.cscript.refresh = function(doc, dt, dn) {
|
|||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
unhide_field(['address_html','contact_html']);
|
unhide_field(['address_html','contact_html']);
|
||||||
// make lists
|
|
||||||
cur_frm.cscript.make_address(doc,dt,dn);
|
erpnext.utils.render_address_and_contact(cur_frm)
|
||||||
cur_frm.cscript.make_contact(doc,dt,dn);
|
|
||||||
|
|
||||||
cur_frm.communication_view = new frappe.views.CommunicationList({
|
cur_frm.communication_view = new frappe.views.CommunicationList({
|
||||||
list: frappe.get_list("Communication", {"supplier": doc.name}),
|
|
||||||
parent: cur_frm.fields_dict.communication_html.wrapper,
|
parent: cur_frm.fields_dict.communication_html.wrapper,
|
||||||
doc: doc
|
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) {
|
cur_frm.fields_dict['default_price_list'].get_query = function(doc, cdt, cdn) {
|
||||||
return{
|
return{
|
||||||
filters:{'buying': 1}
|
filters:{'buying': 1}
|
||||||
|
|||||||
@@ -9,10 +9,14 @@ from frappe.utils import cint
|
|||||||
from frappe import msgprint, _
|
from frappe import msgprint, _
|
||||||
from frappe.model.naming import make_autoname
|
from frappe.model.naming import make_autoname
|
||||||
from erpnext.accounts.party import create_party_account
|
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
|
from erpnext.utilities.transaction_base import TransactionBase
|
||||||
|
|
||||||
class Supplier(TransactionBase):
|
class Supplier(TransactionBase):
|
||||||
|
def onload(self):
|
||||||
|
"""Load address and contacts in `__onload`"""
|
||||||
|
load_address_and_contact(self, "supplier")
|
||||||
|
|
||||||
def autoname(self):
|
def autoname(self):
|
||||||
supp_master_name = frappe.defaults.get_global_default('supp_master_name')
|
supp_master_name = frappe.defaults.get_global_default('supp_master_name')
|
||||||
|
|||||||
@@ -142,10 +142,10 @@ def get_data():
|
|||||||
"doctype": "Item",
|
"doctype": "Item",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "page",
|
"type": "report",
|
||||||
"name": "stock-balance",
|
"is_query_report": True,
|
||||||
"label": _("Stock Balance"),
|
"name": "Stock Balance",
|
||||||
"icon": "icon-table",
|
"doctype": "Warehouse"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "report",
|
"type": "report",
|
||||||
@@ -170,13 +170,7 @@ def get_data():
|
|||||||
"name": "stock-analytics",
|
"name": "stock-analytics",
|
||||||
"label": _("Stock Analytics"),
|
"label": _("Stock Analytics"),
|
||||||
"icon": "icon-bar-chart"
|
"icon": "icon-bar-chart"
|
||||||
},
|
}
|
||||||
{
|
|
||||||
"type": "report",
|
|
||||||
"is_query_report": True,
|
|
||||||
"name": "Warehouse-Wise Stock Balance",
|
|
||||||
"doctype": "Warehouse"
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -387,7 +387,7 @@ class AccountsController(TransactionBase):
|
|||||||
|
|
||||||
res = frappe.db.sql("""
|
res = frappe.db.sql("""
|
||||||
select
|
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
|
from
|
||||||
`tabJournal Voucher` t1, `tabJournal Voucher Detail` t2
|
`tabJournal Voucher` t1, `tabJournal Voucher Detail` t2
|
||||||
where
|
where
|
||||||
@@ -400,7 +400,7 @@ class AccountsController(TransactionBase):
|
|||||||
and ifnull(t2.against_purchase_order, '') = ''
|
and ifnull(t2.against_purchase_order, '') = ''
|
||||||
) %s)
|
) %s)
|
||||||
order by t1.posting_date""" %
|
order by t1.posting_date""" %
|
||||||
(dr_or_cr, '%s', cond),
|
(dr_or_cr, against_order_field, '%s', cond),
|
||||||
tuple([account_head] + so_list), as_dict= True)
|
tuple([account_head] + so_list), as_dict= True)
|
||||||
|
|
||||||
self.set(parentfield, [])
|
self.set(parentfield, [])
|
||||||
@@ -411,7 +411,7 @@ class AccountsController(TransactionBase):
|
|||||||
"jv_detail_no": d.jv_detail_no,
|
"jv_detail_no": d.jv_detail_no,
|
||||||
"remarks": d.remark,
|
"remarks": d.remark,
|
||||||
"advance_amount": flt(d.amount),
|
"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):
|
def validate_advance_jv(self, advance_table_fieldname, against_order_field):
|
||||||
@@ -437,7 +437,7 @@ class AccountsController(TransactionBase):
|
|||||||
for order, jv_list in order_jv_map.items():
|
for order, jv_list in order_jv_map.items():
|
||||||
for jv in jv_list:
|
for jv in jv_list:
|
||||||
if not advance_jv_against_si or jv not in advance_jv_against_si:
|
if not advance_jv_against_si or jv not in advance_jv_against_si:
|
||||||
frappe.throw(_("Journal Voucher {0} is linked against Order {1}, hence it must be fetched as advance in Invoice as well.")
|
frappe.msgprint(_("Journal Voucher {0} is linked against Order {1}, check if it should be pulled as advance in this invoice.")
|
||||||
.format(jv, order))
|
.format(jv, order))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -110,13 +110,13 @@ class BuyingController(StockController):
|
|||||||
self.round_floats_in(self, ["net_total", "net_total_import"])
|
self.round_floats_in(self, ["net_total", "net_total_import"])
|
||||||
|
|
||||||
def calculate_totals(self):
|
def calculate_totals(self):
|
||||||
self.grand_total = flt(self.tax_doclist[-1].total if self.tax_doclist
|
self.grand_total = flt(self.tax_doclist[-1].total if self.tax_doclist else self.net_total)
|
||||||
else self.net_total, self.precision("grand_total"))
|
self.grand_total_import = flt(self.grand_total / self.conversion_rate)
|
||||||
self.grand_total_import = flt(self.grand_total / self.conversion_rate,
|
|
||||||
self.precision("grand_total_import"))
|
|
||||||
|
|
||||||
self.total_tax = flt(self.grand_total - self.net_total,
|
self.total_tax = flt(self.grand_total - self.net_total, self.precision("total_tax"))
|
||||||
self.precision("total_tax"))
|
|
||||||
|
self.grand_total = flt(self.grand_total, self.precision("grand_total"))
|
||||||
|
self.grand_total_import = flt(self.grand_total_import, self.precision("grand_total_import"))
|
||||||
|
|
||||||
if self.meta.get_field("rounded_total"):
|
if self.meta.get_field("rounded_total"):
|
||||||
self.rounded_total = rounded(self.grand_total)
|
self.rounded_total = rounded(self.grand_total)
|
||||||
@@ -256,8 +256,6 @@ class BuyingController(StockController):
|
|||||||
rm.required_qty = required_qty
|
rm.required_qty = required_qty
|
||||||
|
|
||||||
rm.conversion_factor = item.conversion_factor
|
rm.conversion_factor = item.conversion_factor
|
||||||
rm.rate = bom_item.rate
|
|
||||||
rm.amount = required_qty * flt(bom_item.rate)
|
|
||||||
rm.idx = rm_supplied_idx
|
rm.idx = rm_supplied_idx
|
||||||
|
|
||||||
if self.doctype == "Purchase Receipt":
|
if self.doctype == "Purchase Receipt":
|
||||||
@@ -268,7 +266,25 @@ class BuyingController(StockController):
|
|||||||
|
|
||||||
rm_supplied_idx += 1
|
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":
|
if self.doctype == "Purchase Receipt":
|
||||||
item.rm_supp_cost = raw_materials_cost
|
item.rm_supp_cost = raw_materials_cost
|
||||||
|
|||||||
@@ -216,17 +216,17 @@ class SellingController(StockController):
|
|||||||
self.round_floats_in(self, ["net_total", "net_total_export"])
|
self.round_floats_in(self, ["net_total", "net_total_export"])
|
||||||
|
|
||||||
def calculate_totals(self):
|
def calculate_totals(self):
|
||||||
self.grand_total = flt(self.tax_doclist and \
|
self.grand_total = flt(self.tax_doclist[-1].total if self.tax_doclist else self.net_total)
|
||||||
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.other_charges_total = flt(self.grand_total - self.net_total,
|
self.grand_total_export = flt(self.grand_total / self.conversion_rate)
|
||||||
self.precision("other_charges_total"))
|
|
||||||
|
|
||||||
self.other_charges_total_export = flt(self.grand_total_export -
|
self.other_charges_total = flt(self.grand_total - self.net_total, self.precision("other_charges_total"))
|
||||||
self.net_total_export + flt(self.discount_amount),
|
|
||||||
self.precision("other_charges_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 = rounded(self.grand_total)
|
||||||
self.rounded_total_export = rounded(self.grand_total_export)
|
self.rounded_total_export = rounded(self.grand_total_export)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from frappe import msgprint, _
|
|||||||
import frappe.defaults
|
import frappe.defaults
|
||||||
|
|
||||||
from erpnext.controllers.accounts_controller import AccountsController
|
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):
|
class StockController(AccountsController):
|
||||||
def make_gl_entries(self, repost_future_gle=True):
|
def make_gl_entries(self, repost_future_gle=True):
|
||||||
@@ -24,11 +24,12 @@ class StockController(AccountsController):
|
|||||||
|
|
||||||
if repost_future_gle:
|
if repost_future_gle:
|
||||||
items, warehouses = self.get_items_and_warehouses()
|
items, warehouses = self.get_items_and_warehouses()
|
||||||
update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items, warehouse_account)
|
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,
|
def get_gl_entries(self, warehouse_account=None, default_expense_account=None,
|
||||||
default_cost_center=None):
|
default_cost_center=None):
|
||||||
from erpnext.accounts.general_ledger import process_gl_map
|
|
||||||
if not warehouse_account:
|
if not warehouse_account:
|
||||||
warehouse_account = get_warehouse_account()
|
warehouse_account = get_warehouse_account()
|
||||||
|
|
||||||
@@ -118,7 +119,8 @@ class StockController(AccountsController):
|
|||||||
|
|
||||||
def get_stock_ledger_details(self):
|
def get_stock_ledger_details(self):
|
||||||
stock_ledger = {}
|
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""",
|
from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""",
|
||||||
(self.doctype, self.name), as_dict=True):
|
(self.doctype, self.name), as_dict=True):
|
||||||
stock_ledger.setdefault(sle.voucher_detail_no, []).append(sle)
|
stock_ledger.setdefault(sle.voucher_detail_no, []).append(sle)
|
||||||
@@ -173,7 +175,7 @@ class StockController(AccountsController):
|
|||||||
_(self.doctype), self.name, item.get("item_code")))
|
_(self.doctype), self.name, item.get("item_code")))
|
||||||
|
|
||||||
def get_sl_entries(self, d, args):
|
def get_sl_entries(self, d, args):
|
||||||
sl_dict = {
|
sl_dict = frappe._dict({
|
||||||
"item_code": d.get("item_code", None),
|
"item_code": d.get("item_code", None),
|
||||||
"warehouse": d.get("warehouse", None),
|
"warehouse": d.get("warehouse", None),
|
||||||
"posting_date": self.posting_date,
|
"posting_date": self.posting_date,
|
||||||
@@ -190,7 +192,7 @@ class StockController(AccountsController):
|
|||||||
"serial_no": d.get("serial_no"),
|
"serial_no": d.get("serial_no"),
|
||||||
"project": d.get("project_name"),
|
"project": d.get("project_name"),
|
||||||
"is_cancelled": self.docstatus==2 and "Yes" or "No"
|
"is_cancelled": self.docstatus==2 and "Yes" or "No"
|
||||||
}
|
})
|
||||||
|
|
||||||
sl_dict.update(args)
|
sl_dict.update(args)
|
||||||
return sl_dict
|
return sl_dict
|
||||||
@@ -214,7 +216,8 @@ class StockController(AccountsController):
|
|||||||
|
|
||||||
return serialized_items
|
return serialized_items
|
||||||
|
|
||||||
def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None, warehouse_account=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):
|
def _delete_gl_entries(voucher_type, voucher_no):
|
||||||
frappe.db.sql("""delete from `tabGL Entry`
|
frappe.db.sql("""delete from `tabGL Entry`
|
||||||
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
|
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
|
||||||
|
|||||||
@@ -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_description = "Open Source Enterprise Resource Planning for Small and Midsized Organizations"
|
||||||
app_icon = "icon-th"
|
app_icon = "icon-th"
|
||||||
app_color = "#e74c3c"
|
app_color = "#e74c3c"
|
||||||
app_version = "4.5.1"
|
app_version = "4.13.0"
|
||||||
|
|
||||||
error_report_email = "support@erpnext.com"
|
error_report_email = "support@erpnext.com"
|
||||||
|
|
||||||
@@ -47,8 +47,8 @@ doc_events = {
|
|||||||
"on_update": "erpnext.home.make_comment_feed"
|
"on_update": "erpnext.home.make_comment_feed"
|
||||||
},
|
},
|
||||||
"Stock Entry": {
|
"Stock Entry": {
|
||||||
"on_submit": "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_qty"
|
"on_cancel": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty"
|
||||||
},
|
},
|
||||||
"User": {
|
"User": {
|
||||||
"validate": "erpnext.hr.doctype.employee.employee.validate_employee_role",
|
"validate": "erpnext.hr.doctype.employee.employee.validate_employee_role",
|
||||||
|
|||||||
@@ -1,9 +1,21 @@
|
|||||||
{
|
{
|
||||||
"autoname": "EXP.######",
|
"autoname": "naming_series:",
|
||||||
"creation": "2013-01-10 16:34:14",
|
"creation": "2013-01-10 16:34:14",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"fields": [
|
"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",
|
"default": "Draft",
|
||||||
"depends_on": "eval:!doc.__islocal",
|
"depends_on": "eval:!doc.__islocal",
|
||||||
@@ -190,7 +202,7 @@
|
|||||||
"icon": "icon-money",
|
"icon": "icon-money",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2014-08-27 07:08:48.454580",
|
"modified": "2014-11-24 18:25:53.038826",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Expense Claim",
|
"name": "Expense Claim",
|
||||||
|
|||||||
@@ -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});
|
if(cint(doc.half_day) == 1) set_multiple(dt,dn,{total_leave_days:0.5});
|
||||||
else{
|
else{
|
||||||
// server call is done to include holidays in leave days calculations
|
// 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;
|
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",
|
"options": "User",
|
||||||
"permlevel": 0
|
"permlevel": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "leave_approver_name",
|
||||||
|
"fieldtype": "Read Only",
|
||||||
|
"label": "Leave Approver Name",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "leave_type",
|
"fieldname": "leave_type",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
@@ -184,7 +191,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"max_attachments": 3,
|
"max_attachments": 3,
|
||||||
"modified": "2014-09-09 05:35:31.531651",
|
"modified": "2014-12-09 16:33:29.626849",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Leave Application",
|
"name": "Leave Application",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe, json
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
|
||||||
from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, get_url_to_form, \
|
from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, get_url_to_form, \
|
||||||
@@ -77,26 +77,10 @@ class LeaveApplication(Document):
|
|||||||
LeaveDayBlockedError)
|
LeaveDayBlockedError)
|
||||||
|
|
||||||
def get_holidays(self):
|
def get_holidays(self):
|
||||||
tot_hol = frappe.db.sql("""select count(*) from `tabHoliday` h1, `tabHoliday List` h2, `tabEmployee` e1
|
return get_holidays(self)
|
||||||
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
|
|
||||||
|
|
||||||
def get_total_leave_days(self):
|
def get_total_leave_days(self):
|
||||||
"""Calculates total leave days based on input and holidays"""
|
return get_total_leave_days(self)
|
||||||
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
|
|
||||||
|
|
||||||
def validate_to_date(self):
|
def validate_to_date(self):
|
||||||
if self.from_date and self.to_date and \
|
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,
|
post(**{"txt": args.message, "contact": args.message_to, "subject": args.subject,
|
||||||
"notify": cint(self.follow_via_email)})
|
"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))
|
||||||
|
# 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))
|
||||||
|
return tot_hol and tot_hol[0][0] or 0
|
||||||
|
|
||||||
|
@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()
|
@frappe.whitelist()
|
||||||
def get_leave_balance(employee, leave_type, fiscal_year):
|
def get_leave_balance(employee, leave_type, fiscal_year):
|
||||||
leave_all = frappe.db.sql("""select total_leaves_allocated
|
leave_all = frappe.db.sql("""select total_leaves_allocated
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ class SalarySlip(TransactionBase):
|
|||||||
/ cint(self.total_days_in_month), 2)
|
/ cint(self.total_days_in_month), 2)
|
||||||
elif not self.payment_days:
|
elif not self.payment_days:
|
||||||
d.e_modified_amount = 0
|
d.e_modified_amount = 0
|
||||||
else:
|
elif not d.e_modified_amount:
|
||||||
d.e_modified_amount = d.e_amount
|
d.e_modified_amount = d.e_amount
|
||||||
self.gross_pay += flt(d.e_modified_amount)
|
self.gross_pay += flt(d.e_modified_amount)
|
||||||
|
|
||||||
@@ -168,7 +168,7 @@ class SalarySlip(TransactionBase):
|
|||||||
/ cint(self.total_days_in_month), 2)
|
/ cint(self.total_days_in_month), 2)
|
||||||
elif not self.payment_days:
|
elif not self.payment_days:
|
||||||
d.d_modified_amount = 0
|
d.d_modified_amount = 0
|
||||||
else:
|
elif not d.d_modified_amount:
|
||||||
d.d_modified_amount = d.d_amount
|
d.d_modified_amount = d.d_amount
|
||||||
|
|
||||||
self.total_deduction += flt(d.d_modified_amount)
|
self.total_deduction += flt(d.d_modified_amount)
|
||||||
|
|||||||
@@ -115,6 +115,9 @@ class BOM(Document):
|
|||||||
return rate
|
return rate
|
||||||
|
|
||||||
def update_cost(self):
|
def update_cost(self):
|
||||||
|
if self.docstatus == 2:
|
||||||
|
return
|
||||||
|
|
||||||
for d in self.get("bom_materials"):
|
for d in self.get("bom_materials"):
|
||||||
d.rate = self.get_bom_material_detail({
|
d.rate = self.get_bom_material_detail({
|
||||||
'item_code': d.item_code,
|
'item_code': d.item_code,
|
||||||
@@ -122,9 +125,10 @@ class BOM(Document):
|
|||||||
'qty': d.qty
|
'qty': d.qty
|
||||||
})["rate"]
|
})["rate"]
|
||||||
|
|
||||||
if self.docstatus in (0, 1):
|
if self.docstatus == 1:
|
||||||
self.ignore_validate_update_after_submit = True
|
self.ignore_validate_update_after_submit = True
|
||||||
self.save()
|
self.calculate_cost()
|
||||||
|
self.save()
|
||||||
|
|
||||||
def get_bom_unitcost(self, bom_no):
|
def get_bom_unitcost(self, bom_no):
|
||||||
bom = frappe.db.sql("""select name, total_variable_cost/quantity as unit_cost from `tabBOM`
|
bom = frappe.db.sql("""select name, total_variable_cost/quantity as unit_cost from `tabBOM`
|
||||||
@@ -269,29 +273,27 @@ class BOM(Document):
|
|||||||
"""Calculate bom totals"""
|
"""Calculate bom totals"""
|
||||||
self.calculate_op_cost()
|
self.calculate_op_cost()
|
||||||
self.calculate_rm_cost()
|
self.calculate_rm_cost()
|
||||||
self.calculate_fixed_cost()
|
|
||||||
self.total_variable_cost = self.raw_material_cost + self.operating_cost
|
self.total_variable_cost = self.raw_material_cost + self.operating_cost
|
||||||
|
self.total_cost = self.total_variable_cost + self.total_fixed_cost
|
||||||
|
|
||||||
def calculate_op_cost(self):
|
def calculate_op_cost(self):
|
||||||
"""Update workstation rate and calculates totals"""
|
"""Update workstation rate and calculates totals"""
|
||||||
total_op_cost = 0
|
total_op_cost, fixed_cost = 0, 0
|
||||||
for d in self.get('bom_operations'):
|
for d in self.get('bom_operations'):
|
||||||
if d.workstation and not d.hour_rate:
|
if d.workstation:
|
||||||
d.hour_rate = frappe.db.get_value("Workstation", d.workstation, "hour_rate")
|
w = frappe.db.get_value("Workstation", d.workstation, ["hour_rate", "fixed_cycle_cost"])
|
||||||
|
if not d.hour_rate:
|
||||||
|
d.hour_rate = flt(w[0])
|
||||||
|
|
||||||
|
fixed_cost += flt(w[1])
|
||||||
|
|
||||||
if d.hour_rate and d.time_in_mins:
|
if d.hour_rate and d.time_in_mins:
|
||||||
d.operating_cost = flt(d.hour_rate) * flt(d.time_in_mins) / 60.0
|
d.operating_cost = flt(d.hour_rate) * flt(d.time_in_mins) / 60.0
|
||||||
total_op_cost += flt(d.operating_cost)
|
total_op_cost += flt(d.operating_cost)
|
||||||
|
|
||||||
self.operating_cost = total_op_cost
|
self.operating_cost = total_op_cost
|
||||||
|
|
||||||
def calculate_fixed_cost(self):
|
|
||||||
"""Update workstation rate and calculates totals"""
|
|
||||||
fixed_cost = 0
|
|
||||||
for d in self.get('bom_operations'):
|
|
||||||
if d.workstation:
|
|
||||||
fixed_cost += flt(frappe.db.get_value("Workstation", d.workstation, "fixed_cycle_cost"))
|
|
||||||
self.total_fixed_cost = fixed_cost
|
self.total_fixed_cost = fixed_cost
|
||||||
|
|
||||||
|
|
||||||
def calculate_rm_cost(self):
|
def calculate_rm_cost(self):
|
||||||
"""Fetch RM rate as per today's valuation rate and calculate totals"""
|
"""Fetch RM rate as per today's valuation rate and calculate totals"""
|
||||||
total_rm_cost = 0
|
total_rm_cost = 0
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ cur_frm.cscript['Transfer Raw Materials'] = function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cur_frm.cscript['Update Finished Goods'] = function() {
|
cur_frm.cscript['Update Finished Goods'] = function() {
|
||||||
cur_frm.cscript.make_se('Manufacture/Repack');
|
cur_frm.cscript.make_se('Manufacture');
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_frm.fields_dict['production_item'].get_query = function(doc) {
|
cur_frm.fields_dict['production_item'].get_query = function(doc) {
|
||||||
|
|||||||
@@ -109,15 +109,15 @@
|
|||||||
"permlevel": 0
|
"permlevel": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:doc.docstatus==1",
|
"depends_on": "eval:doc.docstatus==1",
|
||||||
"description": "Automatically updated via Stock Entry of type Manufacture/Repack",
|
"description": "Automatically updated via Stock Entry of type Manufacture or Repack",
|
||||||
"fieldname": "produced_qty",
|
"fieldname": "produced_qty",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "Manufactured Qty",
|
"label": "Manufactured Qty",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "produced_qty",
|
"oldfieldname": "produced_qty",
|
||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ class ProductionOrder(Document):
|
|||||||
status = "Submitted"
|
status = "Submitted"
|
||||||
if stock_entries:
|
if stock_entries:
|
||||||
status = "In Process"
|
status = "In Process"
|
||||||
produced_qty = stock_entries.get("Manufacture/Repack")
|
produced_qty = stock_entries.get("Manufacture")
|
||||||
if flt(produced_qty) == flt(self.qty):
|
if flt(produced_qty) == flt(self.qty):
|
||||||
status = "Completed"
|
status = "Completed"
|
||||||
|
|
||||||
@@ -113,7 +113,7 @@ class ProductionOrder(Document):
|
|||||||
def update_produced_qty(self):
|
def update_produced_qty(self):
|
||||||
produced_qty = frappe.db.sql("""select sum(fg_completed_qty)
|
produced_qty = frappe.db.sql("""select sum(fg_completed_qty)
|
||||||
from `tabStock Entry` where production_order=%s and docstatus=1
|
from `tabStock Entry` where production_order=%s and docstatus=1
|
||||||
and purpose='Manufacture/Repack'""", self.name)
|
and purpose='Manufacture'""", self.name)
|
||||||
produced_qty = flt(produced_qty[0][0]) if produced_qty else 0
|
produced_qty = flt(produced_qty[0][0]) if produced_qty else 0
|
||||||
|
|
||||||
if produced_qty > self.qty:
|
if produced_qty > self.qty:
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class TestProductionOrder(unittest.TestCase):
|
|||||||
s.submit()
|
s.submit()
|
||||||
|
|
||||||
# from wip to fg
|
# from wip to fg
|
||||||
s = frappe.get_doc(make_stock_entry(pro_doc.name, "Manufacture/Repack", 4))
|
s = frappe.get_doc(make_stock_entry(pro_doc.name, "Manufacture", 4))
|
||||||
s.insert()
|
s.insert()
|
||||||
s.submit()
|
s.submit()
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ class TestProductionOrder(unittest.TestCase):
|
|||||||
test_stock_entry.make_stock_entry("_Test Item", None, "_Test Warehouse - _TC", 100, 100)
|
test_stock_entry.make_stock_entry("_Test Item", None, "_Test Warehouse - _TC", 100, 100)
|
||||||
test_stock_entry.make_stock_entry("_Test Item Home Desktop 100", None, "_Test Warehouse - _TC", 100, 100)
|
test_stock_entry.make_stock_entry("_Test Item Home Desktop 100", None, "_Test Warehouse - _TC", 100, 100)
|
||||||
|
|
||||||
s = frappe.get_doc(make_stock_entry(pro_doc.name, "Manufacture/Repack", 7))
|
s = frappe.get_doc(make_stock_entry(pro_doc.name, "Manufacture", 7))
|
||||||
s.insert()
|
s.insert()
|
||||||
|
|
||||||
self.assertRaises(StockOverProductionError, s.submit)
|
self.assertRaises(StockOverProductionError, s.submit)
|
||||||
|
|||||||
@@ -6,12 +6,12 @@
|
|||||||
"doctype": "Report",
|
"doctype": "Report",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_standard": "Yes",
|
"is_standard": "Yes",
|
||||||
"modified": "2014-06-03 07:18:17.082436",
|
"modified": "2014-09-17 12:41:55.740299",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Issued Items Against Production Order",
|
"name": "Issued Items Against Production Order",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"query": "select\n ste.production_order as \"Production Order:Link/Production Order:120\",\n ste.posting_date as \"Issue Date:Date:140\",\n ste_item.item_code as \"Item Code:Link/Item:120\",\n\tste_item.description as \"Description::150\",\n\tste_item.transfer_qty as \"Qty:Float:100\",\n\tste_item.stock_uom as \"UOM:Link/UOM:80\",\n\tste_item.amount as \"Amount:Currency:120\",\n\tste_item.serial_no as \"Serial No:Link/Serial No:80\",\n\tste_item.s_warehouse as \"Source Warehouse:Link/Warehouse:120\",\n\tste_item.t_warehouse as \"Target Warehouse:Link/Warehouse:120\",\n\tpro.production_item as \"Finished Goods:Link/Item:120\", \n\tste.name as \"Stock Entry:Link/Stock Entry:120\"\nfrom\n\t`tabStock Entry` ste, `tabStock Entry Detail` ste_item, `tabProduction Order` pro\nwhere\n\tifnull(ste.production_order, '') != '' and ste.name = ste_item.parent \n\tand ste.production_order = pro.name and ste.docstatus = 1 \n\tand ste.purpose = 'Manufacture/Repack'\norder by ste.posting_date, ste.production_order, ste_item.item_code",
|
"query": "select\n ste.production_order as \"Production Order:Link/Production Order:120\",\n ste.posting_date as \"Issue Date:Date:140\",\n ste_item.item_code as \"Item Code:Link/Item:120\",\n\tste_item.description as \"Description::150\",\n\tste_item.transfer_qty as \"Qty:Float:100\",\n\tste_item.stock_uom as \"UOM:Link/UOM:80\",\n\tste_item.amount as \"Amount:Currency:120\",\n\tste_item.serial_no as \"Serial No:Link/Serial No:80\",\n\tste_item.s_warehouse as \"Source Warehouse:Link/Warehouse:120\",\n\tste_item.t_warehouse as \"Target Warehouse:Link/Warehouse:120\",\n\tpro.production_item as \"Finished Goods:Link/Item:120\", \n\tste.name as \"Stock Entry:Link/Stock Entry:120\"\nfrom\n\t`tabStock Entry` ste, `tabStock Entry Detail` ste_item, `tabProduction Order` pro\nwhere\n\tifnull(ste.production_order, '') != '' and ste.name = ste_item.parent \n\tand ste.production_order = pro.name and ste.docstatus = 1 \n\tand ste.purpose = 'Manufacture' or 'Repack'\norder by ste.posting_date, ste.production_order, ste_item.item_code",
|
||||||
"ref_doctype": "Production Order",
|
"ref_doctype": "Production Order",
|
||||||
"report_name": "Issued Items Against Production Order",
|
"report_name": "Issued Items Against Production Order",
|
||||||
"report_type": "Query Report"
|
"report_type": "Query Report"
|
||||||
|
|||||||
@@ -80,4 +80,12 @@ execute:frappe.delete_doc("DocType", "Landed Cost Wizard")
|
|||||||
erpnext.patches.v4_2.default_website_style
|
erpnext.patches.v4_2.default_website_style
|
||||||
erpnext.patches.v4_2.set_company_country
|
erpnext.patches.v4_2.set_company_country
|
||||||
erpnext.patches.v4_2.update_sales_order_invoice_field_name
|
erpnext.patches.v4_2.update_sales_order_invoice_field_name
|
||||||
erpnext.patches.v4_2.cost_of_production_cycle
|
erpnext.patches.v4_2.cost_of_production_cycle
|
||||||
|
erpnext.patches.v4_2.seprate_manufacture_and_repack
|
||||||
|
execute:frappe.delete_doc("Report", "Warehouse-Wise Stock Balance")
|
||||||
|
execute:frappe.delete_doc("DocType", "Purchase Request")
|
||||||
|
execute:frappe.delete_doc("DocType", "Purchase Request Item")
|
||||||
|
erpnext.patches.v4_2.recalculate_bom_cost
|
||||||
|
erpnext.patches.v4_2.fix_gl_entries_for_stock_transactions
|
||||||
|
erpnext.patches.v4_2.update_requested_and_ordered_qty
|
||||||
|
execute:frappe.delete_doc("DocType", "Contact Control")
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import frappe
|
|||||||
from frappe.templates.pages.style_settings import default_properties
|
from frappe.templates.pages.style_settings import default_properties
|
||||||
|
|
||||||
def execute():
|
def execute():
|
||||||
|
frappe.reload_doc('website', 'doctype', 'style_settings')
|
||||||
style_settings = frappe.get_doc("Style Settings", "Style Settings")
|
style_settings = frappe.get_doc("Style Settings", "Style Settings")
|
||||||
if not style_settings.apply_style:
|
if not style_settings.apply_style:
|
||||||
style_settings.update(default_properties)
|
style_settings.update(default_properties)
|
||||||
|
|||||||
@@ -3,24 +3,50 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
from frappe.utils import flt
|
||||||
|
|
||||||
def execute():
|
def execute():
|
||||||
warehouses_with_account = frappe.db.sql_list("""select master_name from tabAccount
|
from erpnext.utilities.repost_stock import repost
|
||||||
|
repost(allow_zero_rate=True, only_actual=True)
|
||||||
|
|
||||||
|
warehouse_account = frappe.db.sql("""select name, master_name from tabAccount
|
||||||
where ifnull(account_type, '') = 'Warehouse'""")
|
where ifnull(account_type, '') = 'Warehouse'""")
|
||||||
|
if warehouse_account:
|
||||||
|
warehouses = [d[1] for d in warehouse_account]
|
||||||
|
accounts = [d[0] for d in warehouse_account]
|
||||||
|
|
||||||
stock_vouchers_without_gle = frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
|
stock_vouchers = frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
|
||||||
from `tabStock Ledger Entry` sle
|
from `tabStock Ledger Entry` sle
|
||||||
where sle.warehouse in (%s)
|
where sle.warehouse in (%s)
|
||||||
and not exists(select name from `tabGL Entry`
|
order by sle.posting_date""" %
|
||||||
where voucher_type=sle.voucher_type and voucher_no=sle.voucher_no)
|
', '.join(['%s']*len(warehouses)), tuple(warehouses))
|
||||||
order by sle.posting_date""" %
|
|
||||||
', '.join(['%s']*len(warehouses_with_account)), tuple(warehouses_with_account))
|
|
||||||
|
|
||||||
for voucher_type, voucher_no in stock_vouchers_without_gle:
|
rejected = []
|
||||||
print voucher_type, voucher_no
|
for voucher_type, voucher_no in stock_vouchers:
|
||||||
frappe.db.sql("""delete from `tabGL Entry`
|
stock_bal = frappe.db.sql("""select sum(stock_value_difference) from `tabStock Ledger Entry`
|
||||||
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
|
where voucher_type=%s and voucher_no =%s and warehouse in (%s)""" %
|
||||||
|
('%s', '%s', ', '.join(['%s']*len(warehouses))), tuple([voucher_type, voucher_no] + warehouses))
|
||||||
|
|
||||||
voucher = frappe.get_doc(voucher_type, voucher_no)
|
account_bal = frappe.db.sql("""select ifnull(sum(ifnull(debit, 0) - ifnull(credit, 0)), 0)
|
||||||
voucher.make_gl_entries()
|
from `tabGL Entry`
|
||||||
frappe.db.commit()
|
where voucher_type=%s and voucher_no =%s and account in (%s)
|
||||||
|
group by voucher_type, voucher_no""" %
|
||||||
|
('%s', '%s', ', '.join(['%s']*len(accounts))), tuple([voucher_type, voucher_no] + accounts))
|
||||||
|
|
||||||
|
if stock_bal and account_bal and abs(flt(stock_bal[0][0]) - flt(account_bal[0][0])) > 0.1:
|
||||||
|
try:
|
||||||
|
print voucher_type, voucher_no, stock_bal[0][0], account_bal[0][0]
|
||||||
|
|
||||||
|
frappe.db.sql("""delete from `tabGL Entry`
|
||||||
|
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
|
||||||
|
|
||||||
|
voucher = frappe.get_doc(voucher_type, voucher_no)
|
||||||
|
voucher.make_gl_entries(repost_future_gle=False)
|
||||||
|
frappe.db.commit()
|
||||||
|
except Exception, e:
|
||||||
|
print frappe.get_traceback()
|
||||||
|
rejected.append([voucher_type, voucher_no])
|
||||||
|
frappe.db.rollback()
|
||||||
|
|
||||||
|
print "Failed to repost: "
|
||||||
|
print rejected
|
||||||
|
|||||||
16
erpnext/patches/v4_2/recalculate_bom_cost.py
Normal file
16
erpnext/patches/v4_2/recalculate_bom_cost.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# 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 frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
for d in frappe.db.sql("select name from `tabBOM` where docstatus < 2"):
|
||||||
|
try:
|
||||||
|
document = frappe.get_doc('BOM', d[0])
|
||||||
|
if document.docstatus == 1:
|
||||||
|
document.ignore_validate_update_after_submit = True
|
||||||
|
document.calculate_cost()
|
||||||
|
document.save()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
9
erpnext/patches/v4_2/seprate_manufacture_and_repack.py
Normal file
9
erpnext/patches/v4_2/seprate_manufacture_and_repack.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# 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 frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
frappe.db.sql("""update `tabStock Entry` set purpose='Manufacture' where purpose='Manufacture/Repack' and ifnull(production_order,"")!="" """)
|
||||||
|
frappe.db.sql("""update `tabStock Entry` set purpose='Repack' where purpose='Manufacture/Repack' and ifnull(production_order,"")="" """)
|
||||||
24
erpnext/patches/v4_2/update_requested_and_ordered_qty.py
Normal file
24
erpnext/patches/v4_2/update_requested_and_ordered_qty.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# 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 frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
from erpnext.utilities.repost_stock import update_bin_qty, get_indented_qty, get_ordered_qty
|
||||||
|
|
||||||
|
count=0
|
||||||
|
for item_code, warehouse in frappe.db.sql("""select distinct item_code, warehouse from
|
||||||
|
(select item_code, warehouse from tabBin
|
||||||
|
union
|
||||||
|
select item_code, warehouse from `tabStock Ledger Entry`) a"""):
|
||||||
|
try:
|
||||||
|
count += 1
|
||||||
|
update_bin_qty(item_code, warehouse, {
|
||||||
|
"indented_qty": get_indented_qty(item_code, warehouse),
|
||||||
|
"ordered_qty": get_ordered_qty(item_code, warehouse)
|
||||||
|
})
|
||||||
|
if count % 200 == 0:
|
||||||
|
frappe.db.commit()
|
||||||
|
except:
|
||||||
|
frappe.db.rollback()
|
||||||
@@ -16,6 +16,15 @@ class TestTimeLog(unittest.TestCase):
|
|||||||
self.assertRaises(OverlapError, ts.insert)
|
self.assertRaises(OverlapError, ts.insert)
|
||||||
|
|
||||||
frappe.db.sql("delete from `tabTime Log`")
|
frappe.db.sql("delete from `tabTime Log`")
|
||||||
|
|
||||||
|
def test_negative_hours(self):
|
||||||
|
frappe.db.sql("delete from `tabTime Log`")
|
||||||
|
test_time_log = frappe.new_doc("Time Log")
|
||||||
|
test_time_log.activity_type = "Communication"
|
||||||
|
test_time_log.from_time = "2013-01-01 11:00:00.000000"
|
||||||
|
test_time_log.to_time = "2013-01-01 10:00:00.000000"
|
||||||
|
self.assertRaises(frappe.ValidationError, test_time_log.save)
|
||||||
|
frappe.db.sql("delete from `tabTime Log`")
|
||||||
|
|
||||||
test_records = frappe.get_test_records('Time Log')
|
test_records = frappe.get_test_records('Time Log')
|
||||||
test_ignore = ["Time Log Batch", "Sales Invoice"]
|
test_ignore = ["Time Log Batch", "Sales Invoice"]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"autoname": "naming_series:",
|
"autoname": "naming_series:",
|
||||||
"creation": "2013-04-03 16:38:41",
|
"creation": "2013-04-03 16:38:41",
|
||||||
"description": "Log of Activities performed by users against Tasks that can be used for tracking time, billing.",
|
"description": "Log of Activities performed by users against Tasks that can be used for tracking time, billing.",
|
||||||
@@ -25,14 +25,6 @@
|
|||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "hours",
|
|
||||||
"fieldtype": "Float",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Hours",
|
|
||||||
"permlevel": 0,
|
|
||||||
"read_only": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "to_time",
|
"fieldname": "to_time",
|
||||||
"fieldtype": "Datetime",
|
"fieldtype": "Datetime",
|
||||||
@@ -42,6 +34,14 @@
|
|||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "hours",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Hours",
|
||||||
|
"permlevel": 0,
|
||||||
|
"read_only": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_3",
|
"fieldname": "column_break_3",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
@@ -151,7 +151,7 @@
|
|||||||
"icon": "icon-time",
|
"icon": "icon-time",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2014-08-04 05:23:15.740050",
|
"modified": "2014-10-22 16:53:26.993828",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Projects",
|
"module": "Projects",
|
||||||
"name": "Time Log",
|
"name": "Time Log",
|
||||||
|
|||||||
@@ -19,10 +19,12 @@ class TimeLog(Document):
|
|||||||
self.set_status()
|
self.set_status()
|
||||||
self.validate_overlap()
|
self.validate_overlap()
|
||||||
self.calculate_total_hours()
|
self.calculate_total_hours()
|
||||||
|
|
||||||
def calculate_total_hours(self):
|
def calculate_total_hours(self):
|
||||||
from frappe.utils import time_diff_in_hours
|
from frappe.utils import time_diff_in_hours
|
||||||
self.hours = time_diff_in_hours(self.to_time, self.from_time)
|
self.hours = time_diff_in_hours(self.to_time, self.from_time)
|
||||||
|
if self.hours < 0:
|
||||||
|
frappe.throw(_("'From Time' cannot be later than 'To Time'"))
|
||||||
|
|
||||||
def set_status(self):
|
def set_status(self):
|
||||||
self.status = {
|
self.status = {
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
"public/js/feature_setup.js",
|
"public/js/feature_setup.js",
|
||||||
"public/js/utils.js",
|
"public/js/utils.js",
|
||||||
"public/js/queries.js",
|
"public/js/queries.js",
|
||||||
"public/js/utils/party.js"
|
"public/js/utils/party.js",
|
||||||
|
"public/js/templates/address_list.html",
|
||||||
|
"public/js/templates/contact_list.html"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ erpnext.StockAnalytics = erpnext.StockGridReport.extend({
|
|||||||
|
|
||||||
if(me.is_default("warehouse") ? true : me.warehouse == sl.warehouse) {
|
if(me.is_default("warehouse") ? true : me.warehouse == sl.warehouse) {
|
||||||
var item = me.item_by_name[sl.item_code];
|
var item = me.item_by_name[sl.item_code];
|
||||||
|
if(item.closing_qty_value==undefined) item.closing_qty_value = 0;
|
||||||
|
|
||||||
if(me.value_or_qty!="Quantity") {
|
if(me.value_or_qty!="Quantity") {
|
||||||
var wh = me.get_item_warehouse(sl.warehouse, sl.item_code);
|
var wh = me.get_item_warehouse(sl.warehouse, sl.item_code);
|
||||||
@@ -138,9 +139,20 @@ erpnext.StockAnalytics = erpnext.StockGridReport.extend({
|
|||||||
item.valuation_method : sys_defaults.valuation_method;
|
item.valuation_method : sys_defaults.valuation_method;
|
||||||
var is_fifo = valuation_method == "FIFO";
|
var is_fifo = valuation_method == "FIFO";
|
||||||
|
|
||||||
var diff = me.get_value_diff(wh, sl, is_fifo);
|
if(sl.voucher_type=="Stock Reconciliation") {
|
||||||
|
var diff = (sl.qty_after_transaction * sl.valuation_rate) - item.closing_qty_value;
|
||||||
|
wh.fifo_stack.push([sl.qty_after_transaction, sl.valuation_rate, sl.posting_date]);
|
||||||
|
wh.balance_qty = sl.qty_after_transaction;
|
||||||
|
wh.balance_value = sl.valuation_rate * sl.qty_after_transaction;
|
||||||
|
} else {
|
||||||
|
var diff = me.get_value_diff(wh, sl, is_fifo);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
var diff = sl.qty;
|
if(sl.voucher_type=="Stock Reconciliation") {
|
||||||
|
var diff = sl.qty_after_transaction - item.closing_qty_value;
|
||||||
|
} else {
|
||||||
|
var diff = sl.qty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(posting_datetime < from_date) {
|
if(posting_datetime < from_date) {
|
||||||
@@ -150,6 +162,8 @@ erpnext.StockAnalytics = erpnext.StockGridReport.extend({
|
|||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
item.closing_qty_value += diff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ erpnext.StockGridReport = frappe.views.TreeGridReport.extend({
|
|||||||
};
|
};
|
||||||
return this.item_warehouse[item][warehouse];
|
return this.item_warehouse[item][warehouse];
|
||||||
},
|
},
|
||||||
|
|
||||||
get_value_diff: function(wh, sl, is_fifo) {
|
get_value_diff: function(wh, sl, is_fifo) {
|
||||||
// value
|
// value
|
||||||
if(sl.qty > 0) {
|
if(sl.qty > 0) {
|
||||||
// incoming - rate is given
|
// incoming - rate is given
|
||||||
@@ -30,9 +30,9 @@ erpnext.StockGridReport = frappe.views.TreeGridReport.extend({
|
|||||||
} else {
|
} else {
|
||||||
var value_diff = (rate * add_qty);
|
var value_diff = (rate * add_qty);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(add_qty)
|
if(add_qty)
|
||||||
wh.fifo_stack.push([add_qty, sl.incoming_rate, sl.posting_date]);
|
wh.fifo_stack.push([add_qty, sl.incoming_rate, sl.posting_date]);
|
||||||
} else {
|
} else {
|
||||||
// called everytime for maintaining fifo stack
|
// called everytime for maintaining fifo stack
|
||||||
var fifo_value_diff = this.get_fifo_value_diff(wh, sl);
|
var fifo_value_diff = this.get_fifo_value_diff(wh, sl);
|
||||||
@@ -44,13 +44,13 @@ erpnext.StockGridReport = frappe.views.TreeGridReport.extend({
|
|||||||
var value_diff = fifo_value_diff;
|
var value_diff = fifo_value_diff;
|
||||||
} else {
|
} else {
|
||||||
// average rate for weighted average
|
// average rate for weighted average
|
||||||
var rate = (wh.balance_qty.toFixed(2) == 0.00 ? 0 :
|
var rate = (wh.balance_qty.toFixed(2) == 0.00 ? 0 :
|
||||||
flt(wh.balance_value) / flt(wh.balance_qty));
|
flt(wh.balance_value) / flt(wh.balance_qty));
|
||||||
|
|
||||||
// no change in value if negative qty
|
// no change in value if negative qty
|
||||||
if((wh.balance_qty + sl.qty).toFixed(2) >= 0.00)
|
if((wh.balance_qty + sl.qty).toFixed(2) >= 0.00)
|
||||||
var value_diff = (rate * sl.qty);
|
var value_diff = (rate * sl.qty);
|
||||||
else
|
else
|
||||||
var value_diff = -wh.balance_value;
|
var value_diff = -wh.balance_value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,7 +58,6 @@ erpnext.StockGridReport = frappe.views.TreeGridReport.extend({
|
|||||||
// update balance (only needed in case of valuation)
|
// update balance (only needed in case of valuation)
|
||||||
wh.balance_qty += sl.qty;
|
wh.balance_qty += sl.qty;
|
||||||
wh.balance_value += value_diff;
|
wh.balance_value += value_diff;
|
||||||
|
|
||||||
return value_diff;
|
return value_diff;
|
||||||
},
|
},
|
||||||
get_fifo_value_diff: function(wh, sl) {
|
get_fifo_value_diff: function(wh, sl) {
|
||||||
@@ -66,19 +65,19 @@ erpnext.StockGridReport = frappe.views.TreeGridReport.extend({
|
|||||||
var fifo_stack = (wh.fifo_stack || []).reverse();
|
var fifo_stack = (wh.fifo_stack || []).reverse();
|
||||||
var fifo_value_diff = 0.0;
|
var fifo_value_diff = 0.0;
|
||||||
var qty = -sl.qty;
|
var qty = -sl.qty;
|
||||||
|
|
||||||
for(var i=0, j=fifo_stack.length; i<j; i++) {
|
for(var i=0, j=fifo_stack.length; i<j; i++) {
|
||||||
var batch = fifo_stack.pop();
|
var batch = fifo_stack.pop();
|
||||||
if(batch[0] >= qty) {
|
if(batch[0] >= qty) {
|
||||||
batch[0] = batch[0] - qty;
|
batch[0] = batch[0] - qty;
|
||||||
fifo_value_diff += (qty * batch[1]);
|
fifo_value_diff += (qty * batch[1]);
|
||||||
|
|
||||||
qty = 0.0;
|
qty = 0.0;
|
||||||
if(batch[0]) {
|
if(batch[0]) {
|
||||||
// batch still has qty put it back
|
// batch still has qty put it back
|
||||||
fifo_stack.push(batch);
|
fifo_stack.push(batch);
|
||||||
}
|
}
|
||||||
|
|
||||||
// all qty found
|
// all qty found
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
@@ -87,35 +86,34 @@ erpnext.StockGridReport = frappe.views.TreeGridReport.extend({
|
|||||||
qty = qty - batch[0];
|
qty = qty - batch[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset the updated stack
|
// reset the updated stack
|
||||||
wh.fifo_stack = fifo_stack.reverse();
|
wh.fifo_stack = fifo_stack.reverse();
|
||||||
return -fifo_value_diff;
|
return -fifo_value_diff;
|
||||||
},
|
},
|
||||||
|
|
||||||
get_serialized_value_diff: function(sl) {
|
get_serialized_value_diff: function(sl) {
|
||||||
var me = this;
|
var me = this;
|
||||||
|
|
||||||
var value_diff = 0.0;
|
var value_diff = 0.0;
|
||||||
|
|
||||||
$.each(sl.serial_no.trim().split("\n"), function(i, sr) {
|
$.each(sl.serial_no.trim().split("\n"), function(i, sr) {
|
||||||
if(sr) {
|
if(sr) {
|
||||||
value_diff += flt(me.serialized_buying_rates[sr.trim().toLowerCase()]);
|
value_diff += flt(me.serialized_buying_rates[sr.trim().toLowerCase()]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return value_diff;
|
return value_diff;
|
||||||
},
|
},
|
||||||
|
|
||||||
get_serialized_buying_rates: function() {
|
get_serialized_buying_rates: function() {
|
||||||
var serialized_buying_rates = {};
|
var serialized_buying_rates = {};
|
||||||
|
|
||||||
if (frappe.report_dump.data["Serial No"]) {
|
if (frappe.report_dump.data["Serial No"]) {
|
||||||
$.each(frappe.report_dump.data["Serial No"], function(i, sn) {
|
$.each(frappe.report_dump.data["Serial No"], function(i, sn) {
|
||||||
serialized_buying_rates[sn.name.toLowerCase()] = flt(sn.incoming_rate);
|
serialized_buying_rates[sn.name.toLowerCase()] = flt(sn.incoming_rate);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return serialized_buying_rates;
|
return serialized_buying_rates;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
20
erpnext/public/js/templates/address_list.html
Normal file
20
erpnext/public/js/templates/address_list.html
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<p><button class="btn btn-sm btn-default btn-address">
|
||||||
|
<i class="icon-plus"></i> New Address</button></p>
|
||||||
|
{% for(var i=0, l=addr_list.length; i<l; i++) { %}
|
||||||
|
<hr>
|
||||||
|
<a href="#Form/Address/{%= addr_list[i].name %}" class="btn btn-sm btn-default pull-right">
|
||||||
|
{%= __("Edit") %}</a>
|
||||||
|
<p><b>{%= i+1 %}. {%= addr_list[i].address_type %}</b></p>
|
||||||
|
<div style="padding-left: 15px;">
|
||||||
|
<div>
|
||||||
|
{% if(addr_list[i].is_primary_address) { %}<span class="label label-info">
|
||||||
|
{%= __("Primary") %}</span>{% } %}
|
||||||
|
{% if(addr_list[i].is_shipping_address) { %}<span class="label label-default">
|
||||||
|
{%= __("Shipping") %}</span>{% } %}
|
||||||
|
</div>
|
||||||
|
<p style="margin-top: 5px;">{%= addr_list[i].display %}</p>
|
||||||
|
</div>
|
||||||
|
{% } %}
|
||||||
|
{% if(!addr_list.length) { %}
|
||||||
|
<p class="text-muted">{%= __("No address added yet.") %}</p>
|
||||||
|
{% } %}
|
||||||
28
erpnext/public/js/templates/contact_list.html
Normal file
28
erpnext/public/js/templates/contact_list.html
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<p><button class="btn btn-sm btn-default btn-contact">
|
||||||
|
<i class="icon-plus"></i> New Contact</button></p>
|
||||||
|
{% for(var i=0, l=contact_list.length; i<l; i++) { %}
|
||||||
|
<hr>
|
||||||
|
<a href="#Form/Contact/{%= contact_list[i].name %}" class="btn btn-sm btn-default pull-right">
|
||||||
|
{%= __("Edit") %}</a>
|
||||||
|
<p><b>{%= i+1 %}. {%= contact_list[i].first_name %} {%= contact_list[i].last_name %}</b></p>
|
||||||
|
<div style="padding-left: 15px;">
|
||||||
|
<div>
|
||||||
|
{% if(contact_list[i].is_primary_contact) { %}<span class="label label-info">
|
||||||
|
{%= __("Primary") %}</span>{% } %}
|
||||||
|
</div>
|
||||||
|
<p style="padding-top: 5px;">
|
||||||
|
{% if(contact_list[i].phone) { %}
|
||||||
|
{%= __("Phone") %}: {%= contact_list[i].phone %}<br>
|
||||||
|
{% } %}
|
||||||
|
{% if(contact_list[i].mobile_no) { %}
|
||||||
|
{%= __("Mobile No.") %}: {%= contact_list[i].mobile_no %}<br>
|
||||||
|
{% } %}
|
||||||
|
{% if(contact_list[i].email_id) { %}
|
||||||
|
{%= __("Email ID") %}: {%= contact_list[i].email_id %}
|
||||||
|
{% } %}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{% } %}
|
||||||
|
{% if(!contact_list.length) { %}
|
||||||
|
<p class="text-muted">{%= __("No contacts added yet.") %}</p>
|
||||||
|
{% } %}
|
||||||
@@ -81,5 +81,28 @@ $.extend(erpnext, {
|
|||||||
|
|
||||||
d.show();
|
d.show();
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
erpnext.utils = {
|
||||||
|
render_address_and_contact: function(frm) {
|
||||||
|
// render address
|
||||||
|
$(frm.fields_dict['address_html'].wrapper)
|
||||||
|
.html(frappe.render(frappe.templates.address_list,
|
||||||
|
cur_frm.doc.__onload))
|
||||||
|
.find(".btn-address").on("click", function() {
|
||||||
|
new_doc("Address");
|
||||||
|
});
|
||||||
|
|
||||||
|
// render contact
|
||||||
|
if(frm.fields_dict['contact_html']) {
|
||||||
|
$(frm.fields_dict['contact_html'].wrapper)
|
||||||
|
.html(frappe.render(frappe.templates.contact_list,
|
||||||
|
cur_frm.doc.__onload))
|
||||||
|
.find(".btn-contact").on("click", function() {
|
||||||
|
new_doc("Contact");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||||
// License: GNU General Public License v3. See license.txt
|
// License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
{% include 'setup/doctype/contact_control/contact_control.js' %};
|
|
||||||
|
|
||||||
cur_frm.cscript.onload = function(doc, dt, dn) {
|
cur_frm.cscript.onload = function(doc, dt, dn) {
|
||||||
cur_frm.cscript.load_defaults(doc, dt, dn);
|
cur_frm.cscript.load_defaults(doc, dt, dn);
|
||||||
}
|
}
|
||||||
@@ -32,8 +30,8 @@ cur_frm.cscript.refresh = function(doc, dt, dn) {
|
|||||||
}else{
|
}else{
|
||||||
unhide_field(['address_html','contact_html']);
|
unhide_field(['address_html','contact_html']);
|
||||||
// make lists
|
// 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({
|
cur_frm.communication_view = new frappe.views.CommunicationList({
|
||||||
parent: cur_frm.fields_dict.communication_html.wrapper,
|
parent: cur_frm.fields_dict.communication_html.wrapper,
|
||||||
@@ -79,44 +77,6 @@ cur_frm.cscript.setup_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 customer='" +
|
|
||||||
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 customer='" +
|
|
||||||
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['customer_group'].get_query = function(doc, dt, dn) {
|
cur_frm.fields_dict['customer_group'].get_query = function(doc, dt, dn) {
|
||||||
return{
|
return{
|
||||||
filters:{'is_group': 'No'}
|
filters:{'is_group': 'No'}
|
||||||
|
|||||||
@@ -7,11 +7,14 @@ from frappe.model.naming import make_autoname
|
|||||||
from frappe import msgprint, _
|
from frappe import msgprint, _
|
||||||
import frappe.defaults
|
import frappe.defaults
|
||||||
|
|
||||||
|
|
||||||
from erpnext.utilities.transaction_base import TransactionBase
|
from erpnext.utilities.transaction_base import TransactionBase
|
||||||
|
from erpnext.utilities.address_and_contact import load_address_and_contact
|
||||||
from erpnext.accounts.party import create_party_account
|
from erpnext.accounts.party import create_party_account
|
||||||
|
|
||||||
class Customer(TransactionBase):
|
class Customer(TransactionBase):
|
||||||
|
def onload(self):
|
||||||
|
"""Load address and contacts in `__onload`"""
|
||||||
|
load_address_and_contact(self, "customer")
|
||||||
|
|
||||||
def autoname(self):
|
def autoname(self):
|
||||||
cust_master_name = frappe.defaults.get_global_default('cust_master_name')
|
cust_master_name = frappe.defaults.get_global_default('cust_master_name')
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||||
// License: GNU General Public License v3. See license.txt
|
// License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
{% include 'setup/doctype/contact_control/contact_control.js' %};
|
|
||||||
|
|
||||||
frappe.provide("erpnext");
|
frappe.provide("erpnext");
|
||||||
erpnext.LeadController = frappe.ui.form.Controller.extend({
|
erpnext.LeadController = frappe.ui.form.Controller.extend({
|
||||||
setup: function() {
|
setup: function() {
|
||||||
@@ -42,33 +40,10 @@ erpnext.LeadController = frappe.ui.form.Controller.extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if(!this.frm.doc.__islocal) {
|
if(!this.frm.doc.__islocal) {
|
||||||
this.make_address_list();
|
erpnext.utils.render_address_and_contact(cur_frm);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
make_address_list: function() {
|
|
||||||
var me = this;
|
|
||||||
if(!this.frm.address_list) {
|
|
||||||
this.frm.address_list = new frappe.ui.Listing({
|
|
||||||
parent: this.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 lead="'+me.frm.doc.name+'" and docstatus != 2 \
|
|
||||||
order by is_primary_address, is_shipping_address desc'
|
|
||||||
},
|
|
||||||
as_dict: 1,
|
|
||||||
no_results_message: __('No addresses created'),
|
|
||||||
render_row: this.render_address_row,
|
|
||||||
});
|
|
||||||
// note: render_address_row is defined in contact_control.js
|
|
||||||
}
|
|
||||||
this.frm.address_list.run();
|
|
||||||
},
|
|
||||||
|
|
||||||
create_customer: function() {
|
create_customer: function() {
|
||||||
frappe.model.open_mapped_doc({
|
frappe.model.open_mapped_doc({
|
||||||
method: "erpnext.selling.doctype.lead.lead.make_customer",
|
method: "erpnext.selling.doctype.lead.lead.make_customer",
|
||||||
|
|||||||
@@ -160,7 +160,7 @@
|
|||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"description": "Add to calendar on this date",
|
"description": "Add to calendar on this date",
|
||||||
"fieldname": "contact_date",
|
"fieldname": "contact_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Datetime",
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
"label": "Next Contact Date",
|
"label": "Next Contact Date",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
@@ -368,7 +368,7 @@
|
|||||||
],
|
],
|
||||||
"icon": "icon-user",
|
"icon": "icon-user",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"modified": "2014-08-12 05:22:18.801092",
|
"modified": "2014-12-01 08:22:23.286314",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Selling",
|
"module": "Selling",
|
||||||
"name": "Lead",
|
"name": "Lead",
|
||||||
|
|||||||
@@ -9,11 +9,13 @@ from frappe import session
|
|||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
|
|
||||||
from erpnext.controllers.selling_controller import SellingController
|
from erpnext.controllers.selling_controller import SellingController
|
||||||
|
from erpnext.utilities.address_and_contact import load_address_and_contact
|
||||||
|
|
||||||
class Lead(SellingController):
|
class Lead(SellingController):
|
||||||
def onload(self):
|
def onload(self):
|
||||||
customer = frappe.db.get_value("Customer", {"lead_name": self.name})
|
customer = frappe.db.get_value("Customer", {"lead_name": self.name})
|
||||||
self.get("__onload").is_customer = customer
|
self.get("__onload").is_customer = customer
|
||||||
|
load_address_and_contact(self, "lead")
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self._prev = frappe._dict({
|
self._prev = frappe._dict({
|
||||||
@@ -43,6 +45,7 @@ class Lead(SellingController):
|
|||||||
def add_calendar_event(self, opts=None, force=False):
|
def add_calendar_event(self, opts=None, force=False):
|
||||||
super(Lead, self).add_calendar_event({
|
super(Lead, self).add_calendar_event({
|
||||||
"owner": self.lead_owner,
|
"owner": self.lead_owner,
|
||||||
|
"starts_on": self.contact_date,
|
||||||
"subject": ('Contact ' + cstr(self.lead_name)),
|
"subject": ('Contact ' + cstr(self.lead_name)),
|
||||||
"description": ('Contact ' + cstr(self.lead_name)) + \
|
"description": ('Contact ' + cstr(self.lead_name)) + \
|
||||||
(self.contact_by and ('. By : ' + cstr(self.contact_by)) or '')
|
(self.contact_by and ('. By : ' + cstr(self.contact_by)) or '')
|
||||||
|
|||||||
@@ -372,7 +372,7 @@
|
|||||||
{
|
{
|
||||||
"description": "Your sales person will get a reminder on this date to contact the customer",
|
"description": "Your sales person will get a reminder on this date to contact the customer",
|
||||||
"fieldname": "contact_date",
|
"fieldname": "contact_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Datetime",
|
||||||
"label": "Next Contact Date",
|
"label": "Next Contact Date",
|
||||||
"oldfieldname": "contact_date",
|
"oldfieldname": "contact_date",
|
||||||
"oldfieldtype": "Date",
|
"oldfieldtype": "Date",
|
||||||
@@ -416,7 +416,7 @@
|
|||||||
"icon": "icon-info-sign",
|
"icon": "icon-info-sign",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2014-08-12 05:21:51.282397",
|
"modified": "2014-12-01 08:46:35.331148",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Selling",
|
"module": "Selling",
|
||||||
"name": "Opportunity",
|
"name": "Opportunity",
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ class Opportunity(TransactionBase):
|
|||||||
opts = frappe._dict()
|
opts = frappe._dict()
|
||||||
|
|
||||||
opts.description = ""
|
opts.description = ""
|
||||||
|
opts.contact_date = self.contact_date
|
||||||
|
|
||||||
if self.customer:
|
if self.customer:
|
||||||
if self.contact_person:
|
if self.contact_person:
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class Quotation(SellingController):
|
|||||||
self.validate_order_type()
|
self.validate_order_type()
|
||||||
self.validate_for_items()
|
self.validate_for_items()
|
||||||
self.validate_uom_is_integer("stock_uom", "qty")
|
self.validate_uom_is_integer("stock_uom", "qty")
|
||||||
self.quotation_to = "Customer" if self.customer else "Lead"
|
self.validate_quotation_to()
|
||||||
|
|
||||||
def has_sales_order(self):
|
def has_sales_order(self):
|
||||||
return frappe.db.get_value("Sales Order Item", {"prevdoc_docname": self.name, "docstatus": 1})
|
return frappe.db.get_value("Sales Order Item", {"prevdoc_docname": self.name, "docstatus": 1})
|
||||||
@@ -54,6 +54,13 @@ class Quotation(SellingController):
|
|||||||
if is_sales_item == 'No':
|
if is_sales_item == 'No':
|
||||||
frappe.throw(_("Item {0} must be Sales Item").format(d.item_code))
|
frappe.throw(_("Item {0} must be Sales Item").format(d.item_code))
|
||||||
|
|
||||||
|
def validate_quotation_to(self):
|
||||||
|
if self.customer:
|
||||||
|
self.quotation_to = "Customer"
|
||||||
|
self.lead = None
|
||||||
|
elif self.lead:
|
||||||
|
self.quotation_to = "Lead"
|
||||||
|
|
||||||
def update_opportunity(self):
|
def update_opportunity(self):
|
||||||
for opportunity in list(set([d.prevdoc_docname for d in self.get("quotation_details")])):
|
for opportunity in list(set([d.prevdoc_docname for d in self.get("quotation_details")])):
|
||||||
if opportunity:
|
if opportunity:
|
||||||
@@ -139,8 +146,8 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False):
|
|||||||
return doclist
|
return doclist
|
||||||
|
|
||||||
def _make_customer(source_name, ignore_permissions=False):
|
def _make_customer(source_name, ignore_permissions=False):
|
||||||
quotation = frappe.db.get_value("Quotation", source_name, ["lead", "order_type"])
|
quotation = frappe.db.get_value("Quotation", source_name, ["lead", "order_type", "customer"])
|
||||||
if quotation and quotation[0]:
|
if quotation and quotation[0] and not quotation[2]:
|
||||||
lead_name = quotation[0]
|
lead_name = quotation[0]
|
||||||
customer_name = frappe.db.get_value("Customer", {"lead_name": lead_name},
|
customer_name = frappe.db.get_value("Customer", {"lead_name": lead_name},
|
||||||
["name", "customer_name"], as_dict=True)
|
["name", "customer_name"], as_dict=True)
|
||||||
|
|||||||
@@ -169,24 +169,6 @@
|
|||||||
"search_index": 1,
|
"search_index": 1,
|
||||||
"width": "160px"
|
"width": "160px"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"allow_on_submit": 1,
|
|
||||||
"description": "Start date of current order's period",
|
|
||||||
"fieldname": "from_date",
|
|
||||||
"fieldtype": "Date",
|
|
||||||
"label": "From Date",
|
|
||||||
"no_copy": 1,
|
|
||||||
"permlevel": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_on_submit": 1,
|
|
||||||
"description": "End date of current order's period",
|
|
||||||
"fieldname": "to_date",
|
|
||||||
"fieldtype": "Date",
|
|
||||||
"label": "To Date",
|
|
||||||
"no_copy": 1,
|
|
||||||
"permlevel": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"description": "Customer's Purchase Order Number",
|
"description": "Customer's Purchase Order Number",
|
||||||
"fieldname": "po_no",
|
"fieldname": "po_no",
|
||||||
@@ -951,6 +933,26 @@
|
|||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1
|
"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
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
"depends_on": "eval:doc.is_recurring==1",
|
"depends_on": "eval:doc.is_recurring==1",
|
||||||
@@ -962,17 +964,6 @@
|
|||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1
|
"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
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
"depends_on": "eval:doc.is_recurring==1",
|
"depends_on": "eval:doc.is_recurring==1",
|
||||||
@@ -991,6 +982,17 @@
|
|||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1
|
"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",
|
"depends_on": "eval:doc.is_recurring==1",
|
||||||
"fieldname": "recurring_id",
|
"fieldname": "recurring_id",
|
||||||
@@ -1018,7 +1020,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"modified": "2014-09-18 03:17:33.241162",
|
"modified": "2014-10-08 14:22:44.717108",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Selling",
|
"module": "Selling",
|
||||||
"name": "Sales Order",
|
"name": "Sales Order",
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ class SalesOrder(SellingController):
|
|||||||
|
|
||||||
self.check_credit(self.grand_total)
|
self.check_credit(self.grand_total)
|
||||||
|
|
||||||
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype, self.grand_total, self)
|
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype, self.company, self.grand_total, self)
|
||||||
|
|
||||||
self.update_prevdoc_status('submit')
|
self.update_prevdoc_status('submit')
|
||||||
frappe.db.set(self, 'status', 'Submitted')
|
frappe.db.set(self, 'status', 'Submitted')
|
||||||
@@ -329,7 +329,7 @@ def make_sales_invoice(source_name, target_doc=None):
|
|||||||
def update_item(source, target, source_parent):
|
def update_item(source, target, source_parent):
|
||||||
target.amount = flt(source.amount) - flt(source.billed_amt)
|
target.amount = flt(source.amount) - flt(source.billed_amt)
|
||||||
target.base_amount = target.amount * flt(source_parent.conversion_rate)
|
target.base_amount = target.amount * flt(source_parent.conversion_rate)
|
||||||
target.qty = source.rate and target.amount / flt(source.rate) or source.qty
|
target.qty = target.amount / flt(source.rate) if (source.rate and source.billed_amt) else source.qty
|
||||||
|
|
||||||
doclist = get_mapped_doc("Sales Order", source_name, {
|
doclist = get_mapped_doc("Sales Order", source_name, {
|
||||||
"Sales Order": {
|
"Sales Order": {
|
||||||
@@ -357,17 +357,6 @@ def make_sales_invoice(source_name, target_doc=None):
|
|||||||
}
|
}
|
||||||
}, target_doc, postprocess)
|
}, target_doc, postprocess)
|
||||||
|
|
||||||
def set_advance_vouchers(source, target):
|
|
||||||
advance_voucher_list = []
|
|
||||||
|
|
||||||
advance_voucher = frappe.db.sql("""
|
|
||||||
select
|
|
||||||
t1.name as voucher_no, t1.posting_date, t1.remark, t2.account,
|
|
||||||
t2.name as voucher_detail_no, {amount_query} as payment_amount, t2.is_advance
|
|
||||||
from
|
|
||||||
`tabJournal Voucher` t1, `tabJournal Voucher Detail` t2
|
|
||||||
""")
|
|
||||||
|
|
||||||
return doclist
|
return doclist
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
|
|||||||
@@ -341,11 +341,8 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
|
|||||||
var me = this;
|
var me = this;
|
||||||
var tax_count = this.frm.tax_doclist.length;
|
var tax_count = this.frm.tax_doclist.length;
|
||||||
|
|
||||||
this.frm.doc.grand_total = flt(
|
this.frm.doc.grand_total = flt(tax_count ? this.frm.tax_doclist[tax_count - 1].total : this.frm.doc.net_total);
|
||||||
tax_count ? this.frm.tax_doclist[tax_count - 1].total : this.frm.doc.net_total,
|
this.frm.doc.grand_total_export = flt(this.frm.doc.grand_total / this.frm.doc.conversion_rate);
|
||||||
precision("grand_total"));
|
|
||||||
this.frm.doc.grand_total_export = flt(this.frm.doc.grand_total / this.frm.doc.conversion_rate,
|
|
||||||
precision("grand_total_export"));
|
|
||||||
|
|
||||||
this.frm.doc.other_charges_total = flt(this.frm.doc.grand_total - this.frm.doc.net_total,
|
this.frm.doc.other_charges_total = flt(this.frm.doc.grand_total - this.frm.doc.net_total,
|
||||||
precision("other_charges_total"));
|
precision("other_charges_total"));
|
||||||
@@ -353,6 +350,9 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
|
|||||||
this.frm.doc.net_total_export + flt(this.frm.doc.discount_amount),
|
this.frm.doc.net_total_export + flt(this.frm.doc.discount_amount),
|
||||||
precision("other_charges_total_export"));
|
precision("other_charges_total_export"));
|
||||||
|
|
||||||
|
this.frm.doc.grand_total = flt(this.frm.doc.grand_total, precision("grand_total"));
|
||||||
|
this.frm.doc.grand_total_export = flt(this.frm.doc.grand_total_export, precision("grand_total_export"));
|
||||||
|
|
||||||
this.frm.doc.rounded_total = Math.round(this.frm.doc.grand_total);
|
this.frm.doc.rounded_total = Math.round(this.frm.doc.grand_total);
|
||||||
this.frm.doc.rounded_total_export = Math.round(this.frm.doc.grand_total_export);
|
this.frm.doc.rounded_total_export = Math.round(this.frm.doc.grand_total_export);
|
||||||
},
|
},
|
||||||
@@ -587,16 +587,18 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
frappe.ui.form.on(cur_frm.doctype,"project_name", function(frm) {
|
frappe.ui.form.on(cur_frm.doctype,"project_name", function(frm) {
|
||||||
frappe.call({
|
if(in_list(["Delivery Note", "Sales Invoice"], frm.doc.doctype)) {
|
||||||
method:'erpnext.projects.doctype.project.project.get_cost_center_name' ,
|
frappe.call({
|
||||||
args: { project_name: frm.doc.project_name },
|
method:'erpnext.projects.doctype.project.project.get_cost_center_name' ,
|
||||||
callback: function(r, rt) {
|
args: { project_name: frm.doc.project_name },
|
||||||
if(!r.exc) {
|
callback: function(r, rt) {
|
||||||
$.each(frm.doc[cur_frm.cscript.fname] || [], function(i, row) {
|
if(!r.exc) {
|
||||||
frappe.model.set_value(row.doctype, row.name, "cost_center", r.message);
|
$.each(frm.doc[cur_frm.cscript.fname] || [], function(i, row) {
|
||||||
msgprint(__("Cost Center For Item with Item Code '"+row.item_name+"' has been Changed to "+ r.message));
|
frappe.model.set_value(row.doctype, row.name, "cost_center", r.message);
|
||||||
})
|
msgprint(__("Cost Center For Item with Item Code '"+row.item_name+"' has been Changed to "+ r.message));
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
})
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -98,9 +98,12 @@ class AuthorizationControl(TransactionBase):
|
|||||||
if doc_obj:
|
if doc_obj:
|
||||||
price_list_rate, base_rate = 0, 0
|
price_list_rate, base_rate = 0, 0
|
||||||
for d in doc_obj.get(doc_obj.fname):
|
for d in doc_obj.get(doc_obj.fname):
|
||||||
if d.base_price_list_rate and d.base_rate:
|
if d.base_rate:
|
||||||
price_list_rate += flt(d.base_price_list_rate)
|
price_list_rate += flt(d.base_price_list_rate) or flt(d.base_rate)
|
||||||
base_rate += flt(d.base_rate)
|
base_rate += flt(d.base_rate)
|
||||||
|
if doc_obj.get("discount_amount"):
|
||||||
|
base_rate -= flt(doc_obj.discount_amount)
|
||||||
|
|
||||||
if price_list_rate: av_dis = 100 - flt(base_rate * 100 / price_list_rate)
|
if price_list_rate: av_dis = 100 - flt(base_rate * 100 / price_list_rate)
|
||||||
|
|
||||||
final_based_on = ['Grand Total','Average Discount','Customerwise Discount','Itemwise Discount']
|
final_based_on = ['Grand Total','Average Discount','Customerwise Discount','Itemwise Discount']
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
[To deprecate] Common scripts for Contacts.
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
|
||||||
// License: GNU General Public License v3. See license.txt
|
|
||||||
|
|
||||||
if(cur_frm.fields_dict['territory']) {
|
|
||||||
cur_frm.fields_dict['territory'].get_query = function(doc, dt, dn) {
|
|
||||||
return {
|
|
||||||
filters: {
|
|
||||||
'is_group': "No"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_frm.cscript.render_contact_row = function(wrapper, data) {
|
|
||||||
// prepare data
|
|
||||||
data.fullname = (data.first_name || '')
|
|
||||||
+ (data.last_name ? ' ' + data.last_name : '');
|
|
||||||
data.primary = data.is_primary_contact ? ' [Primary]' : '';
|
|
||||||
|
|
||||||
// prepare description
|
|
||||||
var description = [];
|
|
||||||
$.each([
|
|
||||||
['phone', 'Tel'],
|
|
||||||
['mobile_no', 'Mobile'],
|
|
||||||
['email_id', 'Email'],
|
|
||||||
['department', 'Department'],
|
|
||||||
['designation', 'Designation']],
|
|
||||||
function(i, v) {
|
|
||||||
if(v[0] && data[v[0]]) {
|
|
||||||
description.push(repl('<h6>%(label)s:</h6> %(value)s', {
|
|
||||||
label: v[1],
|
|
||||||
value: data[v[0]],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
data.description = description.join('<br />');
|
|
||||||
|
|
||||||
cur_frm.cscript.render_row_in_wrapper(wrapper, data, 'Contact');
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_frm.cscript.render_address_row = function(wrapper, data) {
|
|
||||||
// prepare data
|
|
||||||
data.fullname = data.address_type;
|
|
||||||
data.primary = '';
|
|
||||||
if (data.is_primary_address) data.primary += ' [Preferred for Billing]';
|
|
||||||
if (data.is_shipping_address) data.primary += ' [Preferred for Shipping]';
|
|
||||||
|
|
||||||
// prepare address
|
|
||||||
var address = [];
|
|
||||||
$.each(['address_line1', 'address_line2', 'city', 'state', 'country', 'pincode'],
|
|
||||||
function(i, v) {
|
|
||||||
if(data[v]) address.push(data[v]);
|
|
||||||
});
|
|
||||||
|
|
||||||
data.address = address.join('<br />');
|
|
||||||
data.address = "<p class='address-list'>" + data.address + "</p>";
|
|
||||||
|
|
||||||
// prepare description
|
|
||||||
var description = [];
|
|
||||||
$.each([
|
|
||||||
['address', 'Address'],
|
|
||||||
['phone', 'Tel'],
|
|
||||||
['fax', 'Fax'],
|
|
||||||
['email_id', 'Email']],
|
|
||||||
function(i, v) {
|
|
||||||
if(data[v[0]]) {
|
|
||||||
description.push(repl('<h6>%(label)s:</h6> %(value)s', {
|
|
||||||
label: v[1],
|
|
||||||
value: data[v[0]],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
data.description = description.join('<br />');
|
|
||||||
|
|
||||||
cur_frm.cscript.render_row_in_wrapper(wrapper, data, 'Address');
|
|
||||||
|
|
||||||
$(wrapper).find('p.address-list').css({
|
|
||||||
'padding-left': '10px',
|
|
||||||
'margin-bottom': '-10px'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_frm.cscript.render_row_in_wrapper = function(wrapper, data, doctype) {
|
|
||||||
// render
|
|
||||||
var $wrapper = $(wrapper);
|
|
||||||
|
|
||||||
data.doctype = doctype.toLowerCase();
|
|
||||||
|
|
||||||
$wrapper.append(repl("\
|
|
||||||
<h4><a class='link_type'>%(fullname)s</a>%(primary)s</h4>\
|
|
||||||
<div class='description'>\
|
|
||||||
<p>%(description)s</p>\
|
|
||||||
<p><a class='delete link_type'>delete this %(doctype)s</a></p>\
|
|
||||||
</div>", data));
|
|
||||||
|
|
||||||
// make link
|
|
||||||
$wrapper.find('h4 a.link_type').click(function() {
|
|
||||||
loaddoc(doctype, data.name);
|
|
||||||
});
|
|
||||||
|
|
||||||
// css
|
|
||||||
$wrapper.css({ 'margin': '0px' });
|
|
||||||
$wrapper.find('div.description').css({
|
|
||||||
'padding': '5px 2px',
|
|
||||||
'line-height': '150%',
|
|
||||||
});
|
|
||||||
$wrapper.find('h6').css({ 'display': 'inline-block' });
|
|
||||||
|
|
||||||
// show delete
|
|
||||||
var $delete_doc = $wrapper.find('a.delete');
|
|
||||||
if (frappe.model.can_delete(doctype))
|
|
||||||
$delete_doc.toggle(true);
|
|
||||||
else
|
|
||||||
$delete_doc.toggle(false);
|
|
||||||
|
|
||||||
$delete_doc.css({ 'padding-left': '0px' });
|
|
||||||
|
|
||||||
$delete_doc.click(function() {
|
|
||||||
cur_frm.cscript.delete_doc(doctype, data.name);
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_frm.cscript.delete_doc = function(doctype, name) {
|
|
||||||
// confirm deletion
|
|
||||||
var go_ahead = confirm(__("Delete {0} {1}?", [doctype, name]));
|
|
||||||
if (!go_ahead) return;
|
|
||||||
|
|
||||||
frappe.model.delete_doc(doctype, name, function(r) {
|
|
||||||
if (!r.exc) {
|
|
||||||
var list_name = doctype.toLowerCase() + '_list';
|
|
||||||
cur_frm[list_name].run();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render List
|
|
||||||
cur_frm.cscript.render_list = function(doc, doctype, wrapper, ListView, make_new_doc) {
|
|
||||||
frappe.model.with_doctype(doctype, function(r) {
|
|
||||||
if((r && r['403']) || frappe.boot.user.all_read.indexOf(doctype)===-1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var RecordListView = frappe.views.RecordListView.extend({
|
|
||||||
default_docstatus: ['0', '1', '2'],
|
|
||||||
default_filters: [
|
|
||||||
[doctype, doc.doctype.toLowerCase().replace(" ", "_"), '=', doc.name],
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
if (make_new_doc) {
|
|
||||||
RecordListView = RecordListView.extend({
|
|
||||||
make_new_doc: make_new_doc,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var record_list_view = new RecordListView(doctype, wrapper, ListView);
|
|
||||||
if (!cur_frm[doctype.toLowerCase().replace(" ", "_") + "_list"]) {
|
|
||||||
cur_frm[doctype.toLowerCase().replace(" ", "_") + "_list"] = record_list_view;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
{
|
|
||||||
"creation": "2012-03-27 14:36:19.000000",
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"fieldname": "header",
|
|
||||||
"fieldtype": "Text",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Header",
|
|
||||||
"permlevel": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "customer_intro",
|
|
||||||
"fieldtype": "Text",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Customer Intro",
|
|
||||||
"permlevel": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "supplier_intro",
|
|
||||||
"fieldtype": "Text",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Supplier Intro",
|
|
||||||
"permlevel": 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"idx": 1,
|
|
||||||
"in_create": 1,
|
|
||||||
"issingle": 1,
|
|
||||||
"modified": "2013-12-20 19:23:02.000000",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "Setup",
|
|
||||||
"name": "Contact Control",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [
|
|
||||||
{
|
|
||||||
"create": 0,
|
|
||||||
"email": 1,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"role": "System Manager",
|
|
||||||
"write": 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"read_only": 1
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# 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 frappe
|
|
||||||
|
|
||||||
from frappe.model.document import Document
|
|
||||||
|
|
||||||
class ContactControl(Document):
|
|
||||||
pass
|
|
||||||
@@ -1,12 +1,6 @@
|
|||||||
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||||
// License: GNU General Public License v3. See license.txt
|
// License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
{% include 'setup/doctype/contact_control/contact_control.js' %};
|
|
||||||
|
|
||||||
cur_frm.cscript.onload = function(doc,dt,dn){
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_frm.cscript.refresh = function(doc,dt,dn){
|
cur_frm.cscript.refresh = function(doc,dt,dn){
|
||||||
|
|
||||||
if(doc.__islocal){
|
if(doc.__islocal){
|
||||||
@@ -15,8 +9,8 @@ cur_frm.cscript.refresh = function(doc,dt,dn){
|
|||||||
else{
|
else{
|
||||||
unhide_field(['address_html', 'contact_html']);
|
unhide_field(['address_html', 'contact_html']);
|
||||||
// make lists
|
// 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)
|
||||||
|
|
||||||
if (doc.show_in_website) {
|
if (doc.show_in_website) {
|
||||||
cur_frm.set_intro(__("Published on website at: {0}",
|
cur_frm.set_intro(__("Published on website at: {0}",
|
||||||
@@ -25,57 +19,6 @@ cur_frm.cscript.refresh = function(doc,dt,dn){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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: 2,
|
|
||||||
new_doctype: "Address",
|
|
||||||
custom_new_doc: function(doctype) {
|
|
||||||
var address = frappe.model.make_new_doc_and_get_name('Address');
|
|
||||||
address = locals['Address'][address];
|
|
||||||
address.sales_partner = cur_frm.doc.name;
|
|
||||||
address.address_title = cur_frm.doc.name;
|
|
||||||
address.address_type = "Office";
|
|
||||||
frappe.set_route("Form", "Address", address.name);
|
|
||||||
},
|
|
||||||
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 sales_partner='" +
|
|
||||||
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,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
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: 2,
|
|
||||||
new_doctype: "Contact",
|
|
||||||
custom_new_doc: function(doctype) {
|
|
||||||
var contact = frappe.model.make_new_doc_and_get_name('Contact');
|
|
||||||
contact = locals['Contact'][contact];
|
|
||||||
contact.sales_partner = cur_frm.doc.name;
|
|
||||||
frappe.set_route("Form", "Contact", contact.name);
|
|
||||||
},
|
|
||||||
get_query: function() {
|
|
||||||
return "select name, first_name, last_name, email_id, phone, mobile_no, department, designation, is_primary_contact from tabContact where sales_partner='" +
|
|
||||||
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,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
cur_frm.contact_list.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_frm.fields_dict['partner_target_details'].grid.get_field("item_group").get_query = function(doc, dt, dn) {
|
cur_frm.fields_dict['partner_target_details'].grid.get_field("item_group").get_query = function(doc, dt, dn) {
|
||||||
return{
|
return{
|
||||||
filters:{ 'is_group': "No" }
|
filters:{ 'is_group': "No" }
|
||||||
|
|||||||
@@ -5,11 +5,16 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import cstr, filter_strip_join
|
from frappe.utils import cstr, filter_strip_join
|
||||||
from frappe.website.website_generator import WebsiteGenerator
|
from frappe.website.website_generator import WebsiteGenerator
|
||||||
|
from erpnext.utilities.address_and_contact import load_address_and_contact
|
||||||
|
|
||||||
class SalesPartner(WebsiteGenerator):
|
class SalesPartner(WebsiteGenerator):
|
||||||
page_title_field = "partner_name"
|
page_title_field = "partner_name"
|
||||||
condition_field = "show_in_website"
|
condition_field = "show_in_website"
|
||||||
template = "templates/generators/sales_partner.html"
|
template = "templates/generators/sales_partner.html"
|
||||||
|
def onload(self):
|
||||||
|
"""Load address and contacts in `__onload`"""
|
||||||
|
load_address_and_contact(self, "sales_partner")
|
||||||
|
|
||||||
def autoname(self):
|
def autoname(self):
|
||||||
self.name = self.partner_name
|
self.name = self.partner_name
|
||||||
|
|
||||||
|
|||||||
@@ -59,15 +59,18 @@ frappe.pages['setup-wizard'].onload = function(wrapper) {
|
|||||||
title: __("Select Your Language"),
|
title: __("Select Your Language"),
|
||||||
icon: "icon-globe",
|
icon: "icon-globe",
|
||||||
fields: [
|
fields: [
|
||||||
{"fieldname": "language", "label": __("Language"), "fieldtype": "Select",
|
{
|
||||||
|
"fieldname": "language", "label": __("Language"), "fieldtype": "Select",
|
||||||
options: ["english", "العربية", "deutsch", "ελληνικά", "español", "français", "हिंदी", "hrvatski",
|
options: ["english", "العربية", "deutsch", "ελληνικά", "español", "français", "हिंदी", "hrvatski",
|
||||||
"italiano", "nederlands", "polski", "português brasileiro", "português", "српски", "தமிழ்",
|
"italiano", "nederlands", "polski", "português brasileiro", "português", "српски", "தமிழ்",
|
||||||
"ไทย", "中国(简体)", "中國(繁體)"], reqd:1},
|
"ไทย", "中国(简体)", "中國(繁體)"],
|
||||||
|
reqd:1, "default": "english"
|
||||||
|
},
|
||||||
],
|
],
|
||||||
help: __("Welcome to ERPNext. Please select your language to begin the Setup Wizard."),
|
help: __("Welcome to ERPNext. Please select your language to begin the Setup Wizard."),
|
||||||
onload: function(slide) {
|
onload: function(slide) {
|
||||||
slide.get_input("language").on("change", function() {
|
slide.get_input("language").on("change", function() {
|
||||||
var lang = $(this).val();
|
var lang = $(this).val() || "english";
|
||||||
frappe._messages = {};
|
frappe._messages = {};
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.setup.page.setup_wizard.setup_wizard.load_messages",
|
method: "erpnext.setup.page.setup_wizard.setup_wizard.load_messages",
|
||||||
|
|||||||
@@ -71,16 +71,17 @@ def setup_account(args=None):
|
|||||||
|
|
||||||
frappe.db.set_default('desktop:home_page', 'desktop')
|
frappe.db.set_default('desktop:home_page', 'desktop')
|
||||||
|
|
||||||
website_maker(args.company_name, args.company_tagline, args.name)
|
website_maker(args.company_name.strip(), args.company_tagline, args.name)
|
||||||
create_logo(args)
|
create_logo(args)
|
||||||
|
|
||||||
frappe.clear_cache()
|
frappe.clear_cache()
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
|
|
||||||
except:
|
except:
|
||||||
traceback = frappe.get_traceback()
|
if args:
|
||||||
for hook in frappe.get_hooks("setup_wizard_exception"):
|
traceback = frappe.get_traceback()
|
||||||
frappe.get_attr(hook)(traceback, args)
|
for hook in frappe.get_hooks("setup_wizard_exception"):
|
||||||
|
frappe.get_attr(hook)(traceback, args)
|
||||||
|
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@@ -134,7 +135,7 @@ def create_fiscal_year_and_company(args):
|
|||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
"doctype":"Company",
|
"doctype":"Company",
|
||||||
'domain': args.get("industry"),
|
'domain': args.get("industry"),
|
||||||
'company_name':args.get('company_name'),
|
'company_name':args.get('company_name').strip(),
|
||||||
'abbr':args.get('company_abbr'),
|
'abbr':args.get('company_abbr'),
|
||||||
'default_currency':args.get('currency'),
|
'default_currency':args.get('currency'),
|
||||||
'country': args.get('country'),
|
'country': args.get('country'),
|
||||||
@@ -165,7 +166,7 @@ def set_defaults(args):
|
|||||||
global_defaults.update({
|
global_defaults.update({
|
||||||
'current_fiscal_year': args.curr_fiscal_year,
|
'current_fiscal_year': args.curr_fiscal_year,
|
||||||
'default_currency': args.get('currency'),
|
'default_currency': args.get('currency'),
|
||||||
'default_company':args.get('company_name'),
|
'default_company':args.get('company_name').strip(),
|
||||||
"country": args.get("country"),
|
"country": args.get("country"),
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -284,7 +285,7 @@ def create_taxes(args):
|
|||||||
try:
|
try:
|
||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
"doctype":"Account",
|
"doctype":"Account",
|
||||||
"company": args.get("company_name"),
|
"company": args.get("company_name").strip(),
|
||||||
"parent_account": _("Duties and Taxes") + " - " + args.get("company_abbr"),
|
"parent_account": _("Duties and Taxes") + " - " + args.get("company_abbr"),
|
||||||
"account_name": args.get("tax_" + str(i)),
|
"account_name": args.get("tax_" + str(i)),
|
||||||
"group_or_ledger": "Ledger",
|
"group_or_ledger": "Ledger",
|
||||||
@@ -344,7 +345,7 @@ def create_customers(args):
|
|||||||
"customer_type": "Company",
|
"customer_type": "Company",
|
||||||
"customer_group": _("Commercial"),
|
"customer_group": _("Commercial"),
|
||||||
"territory": args.get("country"),
|
"territory": args.get("country"),
|
||||||
"company": args.get("company_name")
|
"company": args.get("company_name").strip()
|
||||||
}).insert()
|
}).insert()
|
||||||
|
|
||||||
if args.get("customer_contact_" + str(i)):
|
if args.get("customer_contact_" + str(i)):
|
||||||
@@ -364,7 +365,7 @@ def create_suppliers(args):
|
|||||||
"doctype":"Supplier",
|
"doctype":"Supplier",
|
||||||
"supplier_name": supplier,
|
"supplier_name": supplier,
|
||||||
"supplier_type": _("Local"),
|
"supplier_type": _("Local"),
|
||||||
"company": args.get("company_name")
|
"company": args.get("company_name").strip()
|
||||||
}).insert()
|
}).insert()
|
||||||
|
|
||||||
if args.get("supplier_contact_" + str(i)):
|
if args.get("supplier_contact_" + str(i)):
|
||||||
|
|||||||
@@ -78,7 +78,8 @@ data_map = {
|
|||||||
"Stock Ledger Entry": {
|
"Stock Ledger Entry": {
|
||||||
"columns": ["name", "posting_date", "posting_time", "item_code", "warehouse",
|
"columns": ["name", "posting_date", "posting_time", "item_code", "warehouse",
|
||||||
"actual_qty as qty", "voucher_type", "voucher_no", "project",
|
"actual_qty as qty", "voucher_type", "voucher_no", "project",
|
||||||
"ifnull(incoming_rate,0) as incoming_rate", "stock_uom", "serial_no"],
|
"ifnull(incoming_rate,0) as incoming_rate", "stock_uom", "serial_no",
|
||||||
|
"qty_after_transaction", "valuation_rate"],
|
||||||
"order_by": "posting_date, posting_time, name",
|
"order_by": "posting_date, posting_time, name",
|
||||||
"links": {
|
"links": {
|
||||||
"item_code": ["Item", "name"],
|
"item_code": ["Item", "name"],
|
||||||
|
|||||||
@@ -3,8 +3,15 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class Batch(Document):
|
class Batch(Document):
|
||||||
pass
|
|
||||||
|
def validate(self):
|
||||||
|
self.item_has_batch_enabled()
|
||||||
|
|
||||||
|
def item_has_batch_enabled(self):
|
||||||
|
has_batch_no = frappe.db.get_value("Item",self.item,"has_batch_no")
|
||||||
|
if has_batch_no =='No':
|
||||||
|
frappe.throw(_("The selected item cannot have Batch"))
|
||||||
14
erpnext/stock/doctype/batch/test_batch.py
Normal file
14
erpnext/stock/doctype/batch/test_batch.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||||
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe.exceptions import ValidationError
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
class TestBatch(unittest.TestCase):
|
||||||
|
def test_item_has_batch_enabled(self):
|
||||||
|
self.assertRaises(ValidationError, frappe.get_doc({
|
||||||
|
"doctype": "Batch",
|
||||||
|
"name": "_test Batch",
|
||||||
|
"item": "_Test Item"
|
||||||
|
}).save)
|
||||||
@@ -11,27 +11,27 @@ class Bin(Document):
|
|||||||
def validate(self):
|
def validate(self):
|
||||||
if self.get("__islocal") or not self.stock_uom:
|
if self.get("__islocal") or not self.stock_uom:
|
||||||
self.stock_uom = frappe.db.get_value('Item', self.item_code, 'stock_uom')
|
self.stock_uom = frappe.db.get_value('Item', self.item_code, 'stock_uom')
|
||||||
|
|
||||||
self.validate_mandatory()
|
self.validate_mandatory()
|
||||||
|
|
||||||
self.projected_qty = flt(self.actual_qty) + flt(self.ordered_qty) + \
|
self.projected_qty = flt(self.actual_qty) + flt(self.ordered_qty) + \
|
||||||
flt(self.indented_qty) + flt(self.planned_qty) - flt(self.reserved_qty)
|
flt(self.indented_qty) + flt(self.planned_qty) - flt(self.reserved_qty)
|
||||||
|
|
||||||
def validate_mandatory(self):
|
def validate_mandatory(self):
|
||||||
qf = ['actual_qty', 'reserved_qty', 'ordered_qty', 'indented_qty']
|
qf = ['actual_qty', 'reserved_qty', 'ordered_qty', 'indented_qty']
|
||||||
for f in qf:
|
for f in qf:
|
||||||
if (not getattr(self, f, None)) or (not self.get(f)):
|
if (not getattr(self, f, None)) or (not self.get(f)):
|
||||||
self.set(f, 0.0)
|
self.set(f, 0.0)
|
||||||
|
|
||||||
def update_stock(self, args):
|
def update_stock(self, args):
|
||||||
self.update_qty(args)
|
self.update_qty(args)
|
||||||
|
|
||||||
if args.get("actual_qty"):
|
if args.get("actual_qty") or args.get("voucher_type") == "Stock Reconciliation":
|
||||||
from erpnext.stock.stock_ledger import update_entries_after
|
from erpnext.stock.stock_ledger import update_entries_after
|
||||||
|
|
||||||
if not args.get("posting_date"):
|
if not args.get("posting_date"):
|
||||||
args["posting_date"] = nowdate()
|
args["posting_date"] = nowdate()
|
||||||
|
|
||||||
# update valuation and qty after transaction for post dated entry
|
# update valuation and qty after transaction for post dated entry
|
||||||
update_entries_after({
|
update_entries_after({
|
||||||
"item_code": self.item_code,
|
"item_code": self.item_code,
|
||||||
@@ -39,21 +39,34 @@ class Bin(Document):
|
|||||||
"posting_date": args.get("posting_date"),
|
"posting_date": args.get("posting_date"),
|
||||||
"posting_time": args.get("posting_time")
|
"posting_time": args.get("posting_time")
|
||||||
})
|
})
|
||||||
|
|
||||||
def update_qty(self, args):
|
def update_qty(self, args):
|
||||||
# update the stock values (for current quantities)
|
# update the stock values (for current quantities)
|
||||||
|
if args.get("voucher_type")=="Stock Reconciliation":
|
||||||
self.actual_qty = flt(self.actual_qty) + flt(args.get("actual_qty"))
|
if args.get('is_cancelled') == 'No':
|
||||||
|
self.actual_qty = args.get("qty_after_transaction")
|
||||||
|
else:
|
||||||
|
qty_after_transaction = frappe.db.get_value("""select qty_after_transaction
|
||||||
|
from `tabStock Ledger Entry`
|
||||||
|
where item_code=%s and warehouse=%s
|
||||||
|
and not (voucher_type='Stock Reconciliation' and voucher_no=%s)
|
||||||
|
order by posting_date desc limit 1""",
|
||||||
|
(self.item_code, self.warehouse, args.get('voucher_no')))
|
||||||
|
|
||||||
|
self.actual_qty = flt(qty_after_transaction[0][0]) if qty_after_transaction else 0.0
|
||||||
|
else:
|
||||||
|
self.actual_qty = flt(self.actual_qty) + flt(args.get("actual_qty"))
|
||||||
|
|
||||||
self.ordered_qty = flt(self.ordered_qty) + flt(args.get("ordered_qty"))
|
self.ordered_qty = flt(self.ordered_qty) + flt(args.get("ordered_qty"))
|
||||||
self.reserved_qty = flt(self.reserved_qty) + flt(args.get("reserved_qty"))
|
self.reserved_qty = flt(self.reserved_qty) + flt(args.get("reserved_qty"))
|
||||||
self.indented_qty = flt(self.indented_qty) + flt(args.get("indented_qty"))
|
self.indented_qty = flt(self.indented_qty) + flt(args.get("indented_qty"))
|
||||||
self.planned_qty = flt(self.planned_qty) + flt(args.get("planned_qty"))
|
self.planned_qty = flt(self.planned_qty) + flt(args.get("planned_qty"))
|
||||||
|
|
||||||
self.projected_qty = flt(self.actual_qty) + flt(self.ordered_qty) + \
|
self.projected_qty = flt(self.actual_qty) + flt(self.ordered_qty) + \
|
||||||
flt(self.indented_qty) + flt(self.planned_qty) - flt(self.reserved_qty)
|
flt(self.indented_qty) + flt(self.planned_qty) - flt(self.reserved_qty)
|
||||||
|
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
def get_first_sle(self):
|
def get_first_sle(self):
|
||||||
sle = frappe.db.sql("""
|
sle = frappe.db.sql("""
|
||||||
select * from `tabStock Ledger Entry`
|
select * from `tabStock Ledger Entry`
|
||||||
@@ -62,4 +75,4 @@ class Bin(Document):
|
|||||||
order by timestamp(posting_date, posting_time) asc, name asc
|
order by timestamp(posting_date, posting_time) asc, name asc
|
||||||
limit 1
|
limit 1
|
||||||
""", (self.item_code, self.warehouse), as_dict=1)
|
""", (self.item_code, self.warehouse), as_dict=1)
|
||||||
return sle and sle[0] or None
|
return sle and sle[0] or None
|
||||||
|
|||||||
@@ -52,11 +52,15 @@ class DeliveryNote(SellingController):
|
|||||||
else:
|
else:
|
||||||
df.delete_key("__print_hide")
|
df.delete_key("__print_hide")
|
||||||
|
|
||||||
toggle_print_hide(self.meta, "currency")
|
|
||||||
|
|
||||||
item_meta = frappe.get_meta("Delivery Note Item")
|
item_meta = frappe.get_meta("Delivery Note Item")
|
||||||
for fieldname in ("rate", "amount", "price_list_rate", "discount_percentage"):
|
print_hide_fields = {
|
||||||
toggle_print_hide(item_meta, fieldname)
|
"parent": ["grand_total_export", "rounded_total_export", "in_words_export", "currency", "net_total_export"],
|
||||||
|
"items": ["rate", "amount", "price_list_rate", "discount_percentage"]
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, fieldname in print_hide_fields.items():
|
||||||
|
for f in fieldname:
|
||||||
|
toggle_print_hide(self.meta if key == "parent" else item_meta, f)
|
||||||
|
|
||||||
def get_portal_page(self):
|
def get_portal_page(self):
|
||||||
return "shipment" if self.docstatus==1 else None
|
return "shipment" if self.docstatus==1 else None
|
||||||
@@ -245,7 +249,7 @@ class DeliveryNote(SellingController):
|
|||||||
sl_entries = []
|
sl_entries = []
|
||||||
for d in self.get_item_list():
|
for d in self.get_item_list():
|
||||||
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == "Yes" \
|
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == "Yes" \
|
||||||
and d.warehouse:
|
and d.warehouse and flt(d['qty']):
|
||||||
self.update_reserved_qty(d)
|
self.update_reserved_qty(d)
|
||||||
|
|
||||||
sl_entries.append(self.get_sl_entries(d, {
|
sl_entries.append(self.get_sl_entries(d, {
|
||||||
@@ -377,7 +381,8 @@ def make_packing_slip(source_name, target_doc=None):
|
|||||||
"Delivery Note": {
|
"Delivery Note": {
|
||||||
"doctype": "Packing Slip",
|
"doctype": "Packing Slip",
|
||||||
"field_map": {
|
"field_map": {
|
||||||
"name": "delivery_note"
|
"name": "delivery_note",
|
||||||
|
"letter_head": "letter_head"
|
||||||
},
|
},
|
||||||
"validation": {
|
"validation": {
|
||||||
"docstatus": ["=", 0]
|
"docstatus": ["=", 0]
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ cur_frm.cscript.refresh = function(doc) {
|
|||||||
cur_frm.cscript.edit_prices_button();
|
cur_frm.cscript.edit_prices_button();
|
||||||
|
|
||||||
if (!doc.__islocal && doc.is_stock_item == 'Yes') {
|
if (!doc.__islocal && doc.is_stock_item == 'Yes') {
|
||||||
cur_frm.toggle_enable(['has_serial_no', 'is_stock_item', 'valuation_method'],
|
cur_frm.toggle_enable(['has_serial_no', 'is_stock_item', 'valuation_method', 'has_batch_no'],
|
||||||
(doc.__onload && doc.__onload.sle_exists=="exists") ? false : true);
|
(doc.__onload && doc.__onload.sle_exists=="exists") ? false : true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,4 +185,4 @@ cur_frm.cscript.image = function() {
|
|||||||
else {
|
else {
|
||||||
msgprint(__("You may need to update: {0}", [frappe.meta.get_docfield(cur_frm.doc.doctype, "description_html").label]));
|
msgprint(__("You may need to update: {0}", [frappe.meta.get_docfield(cur_frm.doc.doctype, "description_html").label]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,13 +187,14 @@ class Item(WebsiteGenerator):
|
|||||||
def cant_change(self):
|
def cant_change(self):
|
||||||
if not self.get("__islocal"):
|
if not self.get("__islocal"):
|
||||||
vals = frappe.db.get_value("Item", self.name,
|
vals = frappe.db.get_value("Item", self.name,
|
||||||
["has_serial_no", "is_stock_item", "valuation_method"], as_dict=True)
|
["has_serial_no", "is_stock_item", "valuation_method", "has_batch_no"], as_dict=True)
|
||||||
|
|
||||||
if vals and ((self.is_stock_item == "No" and vals.is_stock_item == "Yes") or
|
if vals and ((self.is_stock_item == "No" and vals.is_stock_item == "Yes") or
|
||||||
vals.has_serial_no != self.has_serial_no or
|
vals.has_serial_no != self.has_serial_no or
|
||||||
|
vals.has_batch_no != self.has_batch_no or
|
||||||
cstr(vals.valuation_method) != cstr(self.valuation_method)):
|
cstr(vals.valuation_method) != cstr(self.valuation_method)):
|
||||||
if self.check_if_sle_exists() == "exists":
|
if self.check_if_sle_exists() == "exists":
|
||||||
frappe.throw(_("As there are existing stock transactions for this item, you can not change the values of 'Has Serial No', 'Is Stock Item' and 'Valuation Method'"))
|
frappe.throw(_("As there are existing stock transactions for this item, you can not change the values of 'Has Serial No', 'Has Batch No', 'Is Stock Item' and 'Valuation Method'"))
|
||||||
|
|
||||||
def validate_item_type_for_reorder(self):
|
def validate_item_type_for_reorder(self):
|
||||||
if self.re_order_level or len(self.get("item_reorder", {"material_request_type": "Purchase"})):
|
if self.re_order_level or len(self.get("item_reorder", {"material_request_type": "Purchase"})):
|
||||||
|
|||||||
@@ -9,6 +9,6 @@ class TestItem(unittest.TestCase):
|
|||||||
def test_duplicate_item(self):
|
def test_duplicate_item(self):
|
||||||
from erpnext.stock.doctype.item_price.item_price import ItemPriceDuplicateItem
|
from erpnext.stock.doctype.item_price.item_price import ItemPriceDuplicateItem
|
||||||
doc = frappe.copy_doc(test_records[0])
|
doc = frappe.copy_doc(test_records[0])
|
||||||
self.assertRaises(ItemPriceDuplicateItem, doc.insert)
|
self.assertRaises(ItemPriceDuplicateItem, doc.save)
|
||||||
|
|
||||||
test_records = frappe.get_test_records('Item Price')
|
test_records = frappe.get_test_records('Item Price')
|
||||||
@@ -16,5 +16,11 @@
|
|||||||
"item_code": "_Test Item 2",
|
"item_code": "_Test Item 2",
|
||||||
"price_list": "_Test Price List Rest of the World",
|
"price_list": "_Test Price List Rest of the World",
|
||||||
"price_list_rate": 20
|
"price_list_rate": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"doctype": "Item Price",
|
||||||
|
"item_code": "_Test Item Home Desktop 100",
|
||||||
|
"price_list": "_Test Price List",
|
||||||
|
"price_list_rate": 1000
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -97,10 +97,10 @@ class LandedCostVoucher(Document):
|
|||||||
|
|
||||||
# update stock & gl entries for cancelled state of PR
|
# update stock & gl entries for cancelled state of PR
|
||||||
pr.docstatus = 2
|
pr.docstatus = 2
|
||||||
pr.update_stock()
|
pr.update_stock_ledger()
|
||||||
pr.make_gl_entries_on_cancel()
|
pr.make_gl_entries_on_cancel()
|
||||||
|
|
||||||
# update stock & gl entries for submit state of PR
|
# update stock & gl entries for submit state of PR
|
||||||
pr.docstatus = 1
|
pr.docstatus = 1
|
||||||
pr.update_stock()
|
pr.update_stock_ledger()
|
||||||
pr.make_gl_entries()
|
pr.make_gl_entries()
|
||||||
|
|||||||
@@ -79,30 +79,9 @@ class MaterialRequest(BuyingController):
|
|||||||
# NOTE: Since Item BOM and FG quantities are combined, using current data, it cannot be validated
|
# NOTE: Since Item BOM and FG quantities are combined, using current data, it cannot be validated
|
||||||
# Though the creation of Material Request from a Production Plan can be rethought to fix this
|
# Though the creation of Material Request from a Production Plan can be rethought to fix this
|
||||||
|
|
||||||
def update_bin(self, is_submit, is_stopped):
|
|
||||||
""" Update Quantity Requested for Purchase in Bin for Material Request of type 'Purchase'"""
|
|
||||||
|
|
||||||
from erpnext.stock.utils import update_bin
|
|
||||||
for d in self.get('indent_details'):
|
|
||||||
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == "Yes":
|
|
||||||
if not d.warehouse:
|
|
||||||
frappe.throw(_("Warehouse required for stock Item {0}").format(d.item_code))
|
|
||||||
|
|
||||||
qty =flt(d.qty)
|
|
||||||
if is_stopped:
|
|
||||||
qty = (d.qty > d.ordered_qty) and flt(flt(d.qty) - flt(d.ordered_qty)) or 0
|
|
||||||
|
|
||||||
args = {
|
|
||||||
"item_code": d.item_code,
|
|
||||||
"warehouse": d.warehouse,
|
|
||||||
"indented_qty": (is_submit and 1 or -1) * flt(qty),
|
|
||||||
"posting_date": self.transaction_date
|
|
||||||
}
|
|
||||||
update_bin(args)
|
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
frappe.db.set(self, 'status', 'Submitted')
|
frappe.db.set(self, 'status', 'Submitted')
|
||||||
self.update_bin(is_submit = 1, is_stopped = 0)
|
self.update_requested_qty()
|
||||||
|
|
||||||
def check_modified_date(self):
|
def check_modified_date(self):
|
||||||
mod_db = frappe.db.sql("""select modified from `tabMaterial Request` where name = %s""",
|
mod_db = frappe.db.sql("""select modified from `tabMaterial Request` where name = %s""",
|
||||||
@@ -115,23 +94,18 @@ class MaterialRequest(BuyingController):
|
|||||||
|
|
||||||
def update_status(self, status):
|
def update_status(self, status):
|
||||||
self.check_modified_date()
|
self.check_modified_date()
|
||||||
self.update_bin(is_submit = (status == 'Submitted') and 1 or 0, is_stopped = 1)
|
self.update_requested_qty()
|
||||||
frappe.db.set(self, 'status', cstr(status))
|
frappe.db.set(self, 'status', cstr(status))
|
||||||
frappe.msgprint(_("Status updated to {0}").format(_(status)))
|
frappe.msgprint(_("Status updated to {0}").format(_(status)))
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
# Step 1:=> Get Purchase Common Obj
|
|
||||||
pc_obj = frappe.get_doc('Purchase Common')
|
pc_obj = frappe.get_doc('Purchase Common')
|
||||||
|
|
||||||
# Step 2:=> Check for stopped status
|
|
||||||
pc_obj.check_for_stopped_status(self.doctype, self.name)
|
pc_obj.check_for_stopped_status(self.doctype, self.name)
|
||||||
|
|
||||||
# Step 3:=> Check if Purchase Order has been submitted against current Material Request
|
|
||||||
pc_obj.check_docstatus(check = 'Next', doctype = 'Purchase Order', docname = self.name, detail_doctype = 'Purchase Order Item')
|
pc_obj.check_docstatus(check = 'Next', doctype = 'Purchase Order', docname = self.name, detail_doctype = 'Purchase Order Item')
|
||||||
# Step 4:=> Update Bin
|
|
||||||
self.update_bin(is_submit = 0, is_stopped = (cstr(self.status) == 'Stopped') and 1 or 0)
|
|
||||||
|
|
||||||
# Step 5:=> Set Status
|
self.update_requested_qty()
|
||||||
|
|
||||||
frappe.db.set(self,'status','Cancelled')
|
frappe.db.set(self,'status','Cancelled')
|
||||||
|
|
||||||
def update_completed_qty(self, mr_items=None):
|
def update_completed_qty(self, mr_items=None):
|
||||||
@@ -162,56 +136,47 @@ class MaterialRequest(BuyingController):
|
|||||||
self.per_ordered = flt((per_ordered / flt(len(item_doclist))) * 100.0, 2)
|
self.per_ordered = flt((per_ordered / flt(len(item_doclist))) * 100.0, 2)
|
||||||
frappe.db.set_value(self.doctype, self.name, "per_ordered", self.per_ordered)
|
frappe.db.set_value(self.doctype, self.name, "per_ordered", self.per_ordered)
|
||||||
|
|
||||||
def update_completed_qty(doc, method):
|
def update_requested_qty(self, mr_item_rows=None):
|
||||||
if doc.doctype == "Stock Entry":
|
"""update requested qty (before ordered_qty is updated)"""
|
||||||
|
from erpnext.stock.utils import get_bin
|
||||||
|
|
||||||
|
def _update_requested_qty(item_code, warehouse):
|
||||||
|
requested_qty = frappe.db.sql("""select sum(mr_item.qty - ifnull(mr_item.ordered_qty, 0))
|
||||||
|
from `tabMaterial Request Item` mr_item, `tabMaterial Request` mr
|
||||||
|
where mr_item.item_code=%s and mr_item.warehouse=%s
|
||||||
|
and mr_item.qty > ifnull(mr_item.ordered_qty, 0) and mr_item.parent=mr.name
|
||||||
|
and mr.status!='Stopped' and mr.docstatus=1""", (item_code, warehouse))
|
||||||
|
|
||||||
|
bin_doc = get_bin(item_code, warehouse)
|
||||||
|
bin_doc.indented_qty = flt(requested_qty[0][0]) if requested_qty else 0
|
||||||
|
bin_doc.save()
|
||||||
|
|
||||||
|
item_wh_list = []
|
||||||
|
for d in self.get("indent_details"):
|
||||||
|
if (not mr_item_rows or d.name in mr_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_requested_qty(item_code, warehouse)
|
||||||
|
|
||||||
|
def update_completed_and_requested_qty(stock_entry, method):
|
||||||
|
if stock_entry.doctype == "Stock Entry":
|
||||||
material_request_map = {}
|
material_request_map = {}
|
||||||
|
|
||||||
for d in doc.get("mtn_details"):
|
for d in stock_entry.get("mtn_details"):
|
||||||
if d.material_request:
|
if d.material_request:
|
||||||
material_request_map.setdefault(d.material_request, []).append(d.material_request_item)
|
material_request_map.setdefault(d.material_request, []).append(d.material_request_item)
|
||||||
|
|
||||||
for mr_name, mr_items in material_request_map.items():
|
for mr, mr_item_rows in material_request_map.items():
|
||||||
mr_obj = frappe.get_doc("Material Request", mr_name)
|
if mr and mr_item_rows:
|
||||||
|
mr_obj = frappe.get_doc("Material Request", mr)
|
||||||
|
|
||||||
if mr_obj.status in ["Stopped", "Cancelled"]:
|
if mr_obj.status in ["Stopped", "Cancelled"]:
|
||||||
frappe.throw(_("Material Request {0} is cancelled or stopped").format(mr_obj.name),
|
frappe.throw(_("Material Request {0} is cancelled or stopped").format(mr), frappe.InvalidStatusError)
|
||||||
frappe.InvalidStatusError)
|
|
||||||
|
|
||||||
_update_requested_qty(doc, mr_obj, mr_items)
|
mr_obj.update_completed_qty(mr_item_rows)
|
||||||
|
mr_obj.update_requested_qty(mr_item_rows)
|
||||||
# update ordered percentage and qty
|
|
||||||
mr_obj.update_completed_qty(mr_items)
|
|
||||||
|
|
||||||
def _update_requested_qty(doc, mr_obj, mr_items):
|
|
||||||
"""update requested qty (before ordered_qty is updated)"""
|
|
||||||
from erpnext.stock.utils import update_bin
|
|
||||||
for mr_item_name in mr_items:
|
|
||||||
mr_item = mr_obj.get("indent_details", {"name": mr_item_name})
|
|
||||||
se_detail = doc.get("mtn_details", {"material_request": mr_obj.name,
|
|
||||||
"material_request_item": mr_item_name})
|
|
||||||
|
|
||||||
if mr_item and se_detail:
|
|
||||||
mr_item = mr_item[0]
|
|
||||||
se_detail = se_detail[0]
|
|
||||||
mr_item.ordered_qty = flt(mr_item.ordered_qty)
|
|
||||||
mr_item.qty = flt(mr_item.qty)
|
|
||||||
se_detail.transfer_qty = flt(se_detail.transfer_qty)
|
|
||||||
|
|
||||||
if se_detail.docstatus == 2 and mr_item.ordered_qty > mr_item.qty \
|
|
||||||
and se_detail.transfer_qty == mr_item.ordered_qty:
|
|
||||||
add_indented_qty = mr_item.qty
|
|
||||||
elif se_detail.docstatus == 1 and \
|
|
||||||
mr_item.ordered_qty + se_detail.transfer_qty > mr_item.qty:
|
|
||||||
add_indented_qty = mr_item.qty - mr_item.ordered_qty
|
|
||||||
else:
|
|
||||||
add_indented_qty = se_detail.transfer_qty
|
|
||||||
|
|
||||||
update_bin({
|
|
||||||
"item_code": se_detail.item_code,
|
|
||||||
"warehouse": se_detail.t_warehouse,
|
|
||||||
"indented_qty": (se_detail.docstatus==2 and 1 or -1) * add_indented_qty,
|
|
||||||
"posting_date": doc.posting_date,
|
|
||||||
})
|
|
||||||
|
|
||||||
def set_missing_values(source, target_doc):
|
def set_missing_values(source, target_doc):
|
||||||
target_doc.run_method("set_missing_values")
|
target_doc.run_method("set_missing_values")
|
||||||
@@ -240,7 +205,8 @@ def make_purchase_order(source_name, target_doc=None):
|
|||||||
["uom", "stock_uom"],
|
["uom", "stock_uom"],
|
||||||
["uom", "uom"]
|
["uom", "uom"]
|
||||||
],
|
],
|
||||||
"postprocess": update_item
|
"postprocess": update_item,
|
||||||
|
"condition": lambda doc: doc.ordered_qty < doc.qty
|
||||||
}
|
}
|
||||||
}, target_doc, set_missing_values)
|
}, target_doc, set_missing_values)
|
||||||
|
|
||||||
@@ -278,7 +244,8 @@ def make_purchase_order_based_on_supplier(source_name, target_doc=None):
|
|||||||
["uom", "stock_uom"],
|
["uom", "stock_uom"],
|
||||||
["uom", "uom"]
|
["uom", "uom"]
|
||||||
],
|
],
|
||||||
"postprocess": update_item
|
"postprocess": update_item,
|
||||||
|
"condition": lambda doc: doc.ordered_qty < doc.qty
|
||||||
}
|
}
|
||||||
}, target_doc, postprocess)
|
}, target_doc, postprocess)
|
||||||
|
|
||||||
@@ -350,7 +317,8 @@ def make_stock_entry(source_name, target_doc=None):
|
|||||||
"uom": "stock_uom",
|
"uom": "stock_uom",
|
||||||
"warehouse": "t_warehouse"
|
"warehouse": "t_warehouse"
|
||||||
},
|
},
|
||||||
"postprocess": update_item
|
"postprocess": update_item,
|
||||||
|
"condition": lambda doc: doc.ordered_qty < doc.qty
|
||||||
}
|
}
|
||||||
}, target_doc, set_missing_values)
|
}, target_doc, set_missing_values)
|
||||||
|
|
||||||
|
|||||||
@@ -58,12 +58,6 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
self.assertEquals(se.doctype, "Stock Entry")
|
self.assertEquals(se.doctype, "Stock Entry")
|
||||||
self.assertEquals(len(se.get("mtn_details")), len(mr.get("indent_details")))
|
self.assertEquals(len(se.get("mtn_details")), len(mr.get("indent_details")))
|
||||||
|
|
||||||
def _test_requested_qty(self, qty1, qty2):
|
|
||||||
self.assertEqual(flt(frappe.db.get_value("Bin", {"item_code": "_Test Item Home Desktop 100",
|
|
||||||
"warehouse": "_Test Warehouse - _TC"}, "indented_qty")), qty1)
|
|
||||||
self.assertEqual(flt(frappe.db.get_value("Bin", {"item_code": "_Test Item Home Desktop 200",
|
|
||||||
"warehouse": "_Test Warehouse - _TC"}, "indented_qty")), qty2)
|
|
||||||
|
|
||||||
def _insert_stock_entry(self, qty1, qty2):
|
def _insert_stock_entry(self, qty1, qty2):
|
||||||
se = frappe.get_doc({
|
se = frappe.get_doc({
|
||||||
"company": "_Test Company",
|
"company": "_Test Company",
|
||||||
@@ -103,7 +97,8 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
se.submit()
|
se.submit()
|
||||||
|
|
||||||
def test_completed_qty_for_purchase(self):
|
def test_completed_qty_for_purchase(self):
|
||||||
frappe.db.sql("""delete from `tabBin`""")
|
existing_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
|
||||||
|
existing_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
|
||||||
|
|
||||||
# submit material request of type Purchase
|
# submit material request of type Purchase
|
||||||
mr = frappe.copy_doc(test_records[0])
|
mr = frappe.copy_doc(test_records[0])
|
||||||
@@ -115,8 +110,6 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 0)
|
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 0)
|
||||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 0)
|
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 0)
|
||||||
|
|
||||||
self._test_requested_qty(54.0, 3.0)
|
|
||||||
|
|
||||||
# map a purchase order
|
# map a purchase order
|
||||||
from erpnext.stock.doctype.material_request.material_request import make_purchase_order
|
from erpnext.stock.doctype.material_request.material_request import make_purchase_order
|
||||||
po_doc = make_purchase_order(mr.name)
|
po_doc = make_purchase_order(mr.name)
|
||||||
@@ -149,7 +142,12 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
self.assertEquals(mr.per_ordered, 50)
|
self.assertEquals(mr.per_ordered, 50)
|
||||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 27.0)
|
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 27.0)
|
||||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 1.5)
|
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 1.5)
|
||||||
self._test_requested_qty(27.0, 1.5)
|
|
||||||
|
current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
|
||||||
|
current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
|
||||||
|
|
||||||
|
self.assertEquals(current_requested_qty_item1, existing_requested_qty_item1 + 27.0)
|
||||||
|
self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2 + 1.5)
|
||||||
|
|
||||||
po.cancel()
|
po.cancel()
|
||||||
# check if per complete is as expected
|
# check if per complete is as expected
|
||||||
@@ -158,11 +156,15 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, None)
|
self.assertEquals(mr.get("indent_details")[0].ordered_qty, None)
|
||||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, None)
|
self.assertEquals(mr.get("indent_details")[1].ordered_qty, None)
|
||||||
|
|
||||||
self._test_requested_qty(54.0, 3.0)
|
current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
|
||||||
|
current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
|
||||||
|
|
||||||
|
self.assertEquals(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
|
||||||
|
self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)
|
||||||
|
|
||||||
def test_completed_qty_for_transfer(self):
|
def test_completed_qty_for_transfer(self):
|
||||||
frappe.db.sql("""delete from `tabBin`""")
|
existing_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
|
||||||
frappe.db.sql("""delete from `tabStock Ledger Entry`""")
|
existing_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
|
||||||
|
|
||||||
# submit material request of type Purchase
|
# submit material request of type Purchase
|
||||||
mr = frappe.copy_doc(test_records[0])
|
mr = frappe.copy_doc(test_records[0])
|
||||||
@@ -175,7 +177,11 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 0)
|
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 0)
|
||||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 0)
|
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 0)
|
||||||
|
|
||||||
self._test_requested_qty(54.0, 3.0)
|
current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
|
||||||
|
current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
|
||||||
|
|
||||||
|
self.assertEquals(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
|
||||||
|
self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)
|
||||||
|
|
||||||
from erpnext.stock.doctype.material_request.material_request import make_stock_entry
|
from erpnext.stock.doctype.material_request.material_request import make_stock_entry
|
||||||
|
|
||||||
@@ -226,7 +232,11 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 27.0)
|
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 27.0)
|
||||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 1.5)
|
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 1.5)
|
||||||
|
|
||||||
self._test_requested_qty(27.0, 1.5)
|
current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
|
||||||
|
current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
|
||||||
|
|
||||||
|
self.assertEquals(current_requested_qty_item1, existing_requested_qty_item1 + 27.0)
|
||||||
|
self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2 + 1.5)
|
||||||
|
|
||||||
# check if per complete is as expected for Stock Entry cancelled
|
# check if per complete is as expected for Stock Entry cancelled
|
||||||
se.cancel()
|
se.cancel()
|
||||||
@@ -235,11 +245,15 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 0)
|
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 0)
|
||||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 0)
|
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 0)
|
||||||
|
|
||||||
self._test_requested_qty(54.0, 3.0)
|
current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
|
||||||
|
current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
|
||||||
|
|
||||||
|
self.assertEquals(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
|
||||||
|
self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)
|
||||||
|
|
||||||
def test_completed_qty_for_over_transfer(self):
|
def test_completed_qty_for_over_transfer(self):
|
||||||
frappe.db.sql("""delete from `tabBin`""")
|
existing_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
|
||||||
frappe.db.sql("""delete from `tabStock Ledger Entry`""")
|
existing_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
|
||||||
|
|
||||||
# submit material request of type Purchase
|
# submit material request of type Purchase
|
||||||
mr = frappe.copy_doc(test_records[0])
|
mr = frappe.copy_doc(test_records[0])
|
||||||
@@ -252,8 +266,6 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 0)
|
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 0)
|
||||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 0)
|
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 0)
|
||||||
|
|
||||||
self._test_requested_qty(54.0, 3.0)
|
|
||||||
|
|
||||||
# map a stock entry
|
# map a stock entry
|
||||||
from erpnext.stock.doctype.material_request.material_request import make_stock_entry
|
from erpnext.stock.doctype.material_request.material_request import make_stock_entry
|
||||||
|
|
||||||
@@ -297,7 +309,12 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
self.assertEquals(mr.per_ordered, 100)
|
self.assertEquals(mr.per_ordered, 100)
|
||||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 60.0)
|
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 60.0)
|
||||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 3.0)
|
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 3.0)
|
||||||
self._test_requested_qty(0.0, 0.0)
|
|
||||||
|
current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
|
||||||
|
current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
|
||||||
|
|
||||||
|
self.assertEquals(current_requested_qty_item1, existing_requested_qty_item1)
|
||||||
|
self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2)
|
||||||
|
|
||||||
# check if per complete is as expected for Stock Entry cancelled
|
# check if per complete is as expected for Stock Entry cancelled
|
||||||
se.cancel()
|
se.cancel()
|
||||||
@@ -306,7 +323,11 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 0)
|
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 0)
|
||||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 0)
|
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 0)
|
||||||
|
|
||||||
self._test_requested_qty(54.0, 3.0)
|
current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
|
||||||
|
current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
|
||||||
|
|
||||||
|
self.assertEquals(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
|
||||||
|
self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)
|
||||||
|
|
||||||
def test_incorrect_mapping_of_stock_entry(self):
|
def test_incorrect_mapping_of_stock_entry(self):
|
||||||
# submit material request of type Purchase
|
# submit material request of type Purchase
|
||||||
@@ -348,5 +369,9 @@ class TestMaterialRequest(unittest.TestCase):
|
|||||||
mr.company = "_Test Company 1"
|
mr.company = "_Test Company 1"
|
||||||
self.assertRaises(InvalidWarehouseCompany, mr.insert)
|
self.assertRaises(InvalidWarehouseCompany, mr.insert)
|
||||||
|
|
||||||
|
def _get_requested_qty(self, item_code, warehouse):
|
||||||
|
return flt(frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "indented_qty"))
|
||||||
|
|
||||||
|
|
||||||
test_dependencies = ["Currency Exchange"]
|
test_dependencies = ["Currency Exchange"]
|
||||||
test_records = frappe.get_test_records('Material Request')
|
test_records = frappe.get_test_records('Material Request')
|
||||||
|
|||||||
@@ -1,264 +1,281 @@
|
|||||||
{
|
{
|
||||||
"autoname": "PS.#######",
|
"autoname": "PS.#######",
|
||||||
"creation": "2013-04-11 15:32:24",
|
"creation": "2013-04-11 15:32:24",
|
||||||
"description": "Generate packing slips for packages to be delivered. Used to notify package number, package contents and its weight.",
|
"description": "Generate packing slips for packages to be delivered. Used to notify package number, package contents and its weight.",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "Transaction",
|
"document_type": "Transaction",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"fieldname": "packing_slip_details",
|
"fieldname": "packing_slip_details",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Packing Slip Items",
|
"label": "Packing Slip Items",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 0
|
"read_only": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break0",
|
"fieldname": "column_break0",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 0
|
"read_only": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Indicates that the package is a part of this delivery (Only Draft)",
|
"description": "Indicates that the package is a part of this delivery (Only Draft)",
|
||||||
"fieldname": "delivery_note",
|
"fieldname": "delivery_note",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Delivery Note",
|
"label": "Delivery Note",
|
||||||
"options": "Delivery Note",
|
"options": "Delivery Note",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break1",
|
"fieldname": "column_break1",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 0
|
"read_only": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "naming_series",
|
"fieldname": "naming_series",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Series",
|
"label": "Series",
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"options": "PS-",
|
"options": "PS-",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "section_break0",
|
"fieldname": "section_break0",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 0
|
"read_only": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break2",
|
"fieldname": "column_break2",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 0
|
"read_only": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Identification of the package for the delivery (for print)",
|
"description": "Identification of the package for the delivery (for print)",
|
||||||
"fieldname": "from_case_no",
|
"fieldname": "from_case_no",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "From Package No.",
|
"label": "From Package No.",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"width": "50px"
|
"width": "50px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break3",
|
"fieldname": "column_break3",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 0
|
"read_only": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "If more than one package of the same type (for print)",
|
"description": "If more than one package of the same type (for print)",
|
||||||
"fieldname": "to_case_no",
|
"fieldname": "to_case_no",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "To Package No.",
|
"label": "To Package No.",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"width": "50px"
|
"width": "50px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "package_item_details",
|
"fieldname": "package_item_details",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Package Item Details",
|
"label": "Package Item Details",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 0
|
"read_only": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "get_items",
|
"fieldname": "get_items",
|
||||||
"fieldtype": "Button",
|
"fieldtype": "Button",
|
||||||
"label": "Get Items",
|
"label": "Get Items",
|
||||||
"permlevel": 0
|
"permlevel": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "item_details",
|
"fieldname": "item_details",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"label": "Items",
|
"label": "Items",
|
||||||
"options": "Packing Slip Item",
|
"options": "Packing Slip Item",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 0
|
"read_only": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "package_weight_details",
|
"fieldname": "package_weight_details",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Package Weight Details",
|
"label": "Package Weight Details",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 0
|
"read_only": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "The net weight of this package. (calculated automatically as sum of net weight of items)",
|
"description": "The net weight of this package. (calculated automatically as sum of net weight of items)",
|
||||||
"fieldname": "net_weight_pkg",
|
"fieldname": "net_weight_pkg",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "Net Weight",
|
"label": "Net Weight",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "net_weight_uom",
|
"fieldname": "net_weight_uom",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Net Weight UOM",
|
"label": "Net Weight UOM",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "UOM",
|
"options": "UOM",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break4",
|
"fieldname": "column_break4",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 0
|
"read_only": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "The gross weight of the package. Usually net weight + packaging material weight. (for print)",
|
"description": "The gross weight of the package. Usually net weight + packaging material weight. (for print)",
|
||||||
"fieldname": "gross_weight_pkg",
|
"fieldname": "gross_weight_pkg",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "Gross Weight",
|
"label": "Gross Weight",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 0
|
"read_only": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "gross_weight_uom",
|
"fieldname": "gross_weight_uom",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Gross Weight UOM",
|
"label": "Gross Weight UOM",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "UOM",
|
"options": "UOM",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 0
|
"read_only": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "misc_details",
|
"fieldname": "letter_head_details",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Misc Details",
|
"label": "Letter Head",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"precision": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 1,
|
||||||
|
"fieldname": "letter_head",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Letter Head",
|
||||||
|
"options": "Letter Head",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "misc_details",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Misc Details",
|
||||||
|
"permlevel": 0,
|
||||||
"read_only": 0
|
"read_only": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "amended_from",
|
"fieldname": "amended_from",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"ignore_user_permissions": 1,
|
"ignore_user_permissions": 1,
|
||||||
"label": "Amended From",
|
"label": "Amended From",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Packing Slip",
|
"options": "Packing Slip",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "icon-suitcase",
|
"icon": "icon-suitcase",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2014-05-27 03:49:14.251039",
|
"modified": "2014-11-13 16:50:50.423299",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Packing Slip",
|
"name": "Packing Slip",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 1,
|
"amend": 1,
|
||||||
"apply_user_permissions": 1,
|
"apply_user_permissions": 1,
|
||||||
"cancel": 1,
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Material User",
|
"role": "Material User",
|
||||||
"submit": 1,
|
"submit": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 1,
|
"amend": 1,
|
||||||
"apply_user_permissions": 1,
|
"apply_user_permissions": 1,
|
||||||
"cancel": 1,
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Sales User",
|
"role": "Sales User",
|
||||||
"submit": 1,
|
"submit": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 1,
|
"amend": 1,
|
||||||
"cancel": 1,
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Material Master Manager",
|
"role": "Material Master Manager",
|
||||||
"submit": 1,
|
"submit": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 1,
|
"amend": 1,
|
||||||
"cancel": 1,
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Material Manager",
|
"role": "Material Manager",
|
||||||
"submit": 1,
|
"submit": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 1,
|
"amend": 1,
|
||||||
"cancel": 1,
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Sales Manager",
|
"role": "Sales Manager",
|
||||||
"submit": 1,
|
"submit": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"read_only_onload": 1,
|
"read_only_onload": 1,
|
||||||
"search_fields": "delivery_note"
|
"search_fields": "delivery_note"
|
||||||
}
|
}
|
||||||
@@ -162,8 +162,7 @@ def item_details(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
from erpnext.controllers.queries import get_match_cond
|
from erpnext.controllers.queries import get_match_cond
|
||||||
return frappe.db.sql("""select name, item_name, description from `tabItem`
|
return frappe.db.sql("""select name, item_name, description from `tabItem`
|
||||||
where name in ( select item_code FROM `tabDelivery Note Item`
|
where name in ( select item_code FROM `tabDelivery Note Item`
|
||||||
where parent= %s
|
where parent= %s)
|
||||||
and ifnull(qty, 0) > ifnull(packed_qty, 0))
|
|
||||||
and %s like "%s" %s
|
and %s like "%s" %s
|
||||||
limit %s, %s """ % ("%s", searchfield, "%s",
|
limit %s, %s """ % ("%s", searchfield, "%s",
|
||||||
get_match_cond(doctype), "%s", "%s"),
|
get_match_cond(doctype), "%s", "%s"),
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ from frappe.utils import cstr, flt, cint
|
|||||||
|
|
||||||
from frappe import _
|
from frappe import _
|
||||||
import frappe.defaults
|
import frappe.defaults
|
||||||
from erpnext.stock.utils import update_bin
|
|
||||||
|
|
||||||
from erpnext.controllers.buying_controller import BuyingController
|
from erpnext.controllers.buying_controller import BuyingController
|
||||||
|
|
||||||
@@ -130,7 +129,7 @@ class PurchaseReceipt(BuyingController):
|
|||||||
if not d.prevdoc_docname:
|
if not d.prevdoc_docname:
|
||||||
frappe.throw(_("Purchase Order number required for Item {0}").format(d.item_code))
|
frappe.throw(_("Purchase Order number required for Item {0}").format(d.item_code))
|
||||||
|
|
||||||
def update_stock(self):
|
def update_stock_ledger(self):
|
||||||
sl_entries = []
|
sl_entries = []
|
||||||
stock_items = self.get_stock_items()
|
stock_items = self.get_stock_items()
|
||||||
|
|
||||||
@@ -157,29 +156,19 @@ class PurchaseReceipt(BuyingController):
|
|||||||
self.make_sl_entries(sl_entries)
|
self.make_sl_entries(sl_entries)
|
||||||
|
|
||||||
def update_ordered_qty(self):
|
def update_ordered_qty(self):
|
||||||
stock_items = self.get_stock_items()
|
po_map = {}
|
||||||
for d in self.get("purchase_receipt_details"):
|
for d in self.get("purchase_receipt_details"):
|
||||||
if d.item_code in stock_items and d.warehouse \
|
if d.prevdoc_doctype and d.prevdoc_doctype == "Purchase Order" and d.prevdoc_detail_docname:
|
||||||
and cstr(d.prevdoc_doctype) == 'Purchase Order':
|
po_map.setdefault(d.prevdoc_docname, []).append(d.prevdoc_detail_docname)
|
||||||
|
|
||||||
already_received_qty = self.get_already_received_qty(d.prevdoc_docname,
|
for po, po_item_rows in po_map.items():
|
||||||
d.prevdoc_detail_docname)
|
if po and po_item_rows:
|
||||||
po_qty, ordered_warehouse = self.get_po_qty_and_warehouse(d.prevdoc_detail_docname)
|
po_obj = frappe.get_doc("Purchase Order", po)
|
||||||
|
|
||||||
if not ordered_warehouse:
|
if po_obj.status in ["Stopped", "Cancelled"]:
|
||||||
frappe.throw(_("Warehouse is missing in Purchase Order"))
|
frappe.throw(_("Material Request {0} is cancelled or stopped").format(po), frappe.InvalidStatusError)
|
||||||
|
|
||||||
if already_received_qty + d.qty > po_qty:
|
po_obj.update_ordered_qty(po_item_rows)
|
||||||
ordered_qty = - (po_qty - already_received_qty) * flt(d.conversion_factor)
|
|
||||||
else:
|
|
||||||
ordered_qty = - flt(d.qty) * flt(d.conversion_factor)
|
|
||||||
|
|
||||||
update_bin({
|
|
||||||
"item_code": d.item_code,
|
|
||||||
"warehouse": ordered_warehouse,
|
|
||||||
"posting_date": self.posting_date,
|
|
||||||
"ordered_qty": flt(ordered_qty) if self.docstatus==1 else -flt(ordered_qty)
|
|
||||||
})
|
|
||||||
|
|
||||||
def get_already_received_qty(self, po, po_detail):
|
def get_already_received_qty(self, po, po_detail):
|
||||||
qty = frappe.db.sql("""select sum(qty) from `tabPurchase Receipt Item`
|
qty = frappe.db.sql("""select sum(qty) from `tabPurchase Receipt Item`
|
||||||
@@ -234,7 +223,7 @@ class PurchaseReceipt(BuyingController):
|
|||||||
|
|
||||||
self.update_ordered_qty()
|
self.update_ordered_qty()
|
||||||
|
|
||||||
self.update_stock()
|
self.update_stock_ledger()
|
||||||
|
|
||||||
from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
|
from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
|
||||||
update_serial_nos_after_submit(self, "purchase_receipt_details")
|
update_serial_nos_after_submit(self, "purchase_receipt_details")
|
||||||
@@ -265,11 +254,13 @@ class PurchaseReceipt(BuyingController):
|
|||||||
|
|
||||||
frappe.db.set(self,'status','Cancelled')
|
frappe.db.set(self,'status','Cancelled')
|
||||||
|
|
||||||
self.update_ordered_qty()
|
self.update_stock_ledger()
|
||||||
|
|
||||||
self.update_stock()
|
|
||||||
|
|
||||||
self.update_prevdoc_status()
|
self.update_prevdoc_status()
|
||||||
|
|
||||||
|
# Must be called after updating received qty in PO
|
||||||
|
self.update_ordered_qty()
|
||||||
|
|
||||||
pc_obj.update_last_purchase_rate(self, 0)
|
pc_obj.update_last_purchase_rate(self, 0)
|
||||||
|
|
||||||
self.make_gl_entries_on_cancel()
|
self.make_gl_entries_on_cancel()
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user