Merge branch 'wip_payrec' of github.com:ankitjavalkarwork/erpnext into ankitjavalkarwork-wip_payrec
diff --git a/erpnext/accounts/doctype/c_form/c_form.py b/erpnext/accounts/doctype/c_form/c_form.py
index e0f008f..88ced9a 100644
--- a/erpnext/accounts/doctype/c_form/c_form.py
+++ b/erpnext/accounts/doctype/c_form/c_form.py
@@ -55,12 +55,12 @@
def get_invoice_details(self, invoice_no):
""" Pull details from invoices for referrence """
-
- inv = frappe.db.get_value("Sales Invoice", invoice_no,
- ["posting_date", "territory", "net_total", "grand_total"], as_dict=True)
- return {
- 'invoice_date' : inv.posting_date,
- 'territory' : inv.territory,
- 'net_total' : inv.net_total,
- 'grand_total' : inv.grand_total
- }
+ if invoice_no:
+ inv = frappe.db.get_value("Sales Invoice", invoice_no,
+ ["posting_date", "territory", "net_total", "grand_total"], as_dict=True)
+ return {
+ 'invoice_date' : inv.posting_date,
+ 'territory' : inv.territory,
+ 'net_total' : inv.net_total,
+ 'grand_total' : inv.grand_total
+ }
diff --git a/erpnext/accounts/doctype/payment_reconciliation/__init__.py b/erpnext/accounts/doctype/payment_reconciliation/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_reconciliation/__init__.py
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
new file mode 100644
index 0000000..14520c2
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
@@ -0,0 +1,64 @@
+// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+// For license information, please see license.txt
+
+frappe.provide("erpnext.accounts");
+
+erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.extend({
+
+ onload: function() {
+ var me = this
+ this.frm.set_query ('party_account', function() {
+ return{
+ filters:[
+ ['Account', 'company', '=', me.frm.doc.company],
+ ['Account', 'group_or_ledger', '=', 'Ledger'],
+ ['Account', 'master_type', 'in', ['Customer', 'Supplier']]
+ ]
+ };
+ });
+
+ var help_content = ['<i class="icon-hand-right"></i> Note:',
+ '<ul>If you are unable to match the exact amount, then amend your Journal Voucher and split rows such that your amounts match the invoice you are trying to reconcile. </ul>'].join("\n");
+ this.frm.set_value("reconcile_help", help_content);
+ },
+
+
+ get_unreconciled_entries: function() {
+ var me = this;
+ return this.frm.call({
+ doc: me.frm.doc,
+ method: 'get_unreconciled_entries',
+ callback: function(r, rt) {
+ var invoices = [];
+
+ $.each(me.frm.doc.payment_reconciliation_invoices || [], function(i, row) {
+ if (row.invoice_number && !inList(invoices, row.invoice_number))
+ invoices.push(row.invoice_number);
+ });
+
+ frappe.meta.get_docfield("Payment Reconciliation Payment", "invoice_number",
+ me.frm.doc.name).options = invoices.join("\n");
+
+ $.each(me.frm.doc.payment_reconciliation_payments || [], function(i, p) {
+ if(!inList(invoices, cstr(p.invoice_number))) p.invoice_number = null;
+ });
+
+ refresh_field("payment_reconciliation_payments");
+ }
+ });
+
+ },
+
+ reconcile: function() {
+ var me = this;
+ return this.frm.call({
+ doc: me.frm.doc,
+ method: 'reconcile'
+ });
+ }
+
+});
+
+$.extend(cur_frm.cscript, new erpnext.accounts.PaymentReconciliationController({frm: cur_frm}));
+
+cur_frm.add_fetch('party_account', 'master_type', 'party_type')
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json
new file mode 100644
index 0000000..40b5706
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json
@@ -0,0 +1,162 @@
+{
+ "allow_copy": 1,
+ "creation": "2014-07-09 12:04:51.681583",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "fields": [
+ {
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "label": "Company",
+ "options": "Company",
+ "permlevel": 0,
+ "reqd": 1
+ },
+ {
+ "depends_on": "",
+ "fieldname": "party_account",
+ "fieldtype": "Link",
+ "in_list_view": 0,
+ "label": "Party Account",
+ "options": "Account",
+ "permlevel": 0,
+ "reqd": 1,
+ "search_index": 0
+ },
+ {
+ "fieldname": "party_type",
+ "fieldtype": "Select",
+ "hidden": 1,
+ "in_list_view": 1,
+ "label": "Party Type",
+ "options": "Customer\nSupplier",
+ "permlevel": 0,
+ "read_only": 1,
+ "reqd": 0
+ },
+ {
+ "fieldname": "bank_cash_account",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Bank / Cash Account",
+ "options": "Account",
+ "permlevel": 0,
+ "reqd": 0,
+ "search_index": 0
+ },
+ {
+ "fieldname": "col_break1",
+ "fieldtype": "Column Break",
+ "label": "Column Break",
+ "permlevel": 0
+ },
+ {
+ "fieldname": "from_date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "From Date",
+ "permlevel": 0,
+ "search_index": 1
+ },
+ {
+ "fieldname": "to_date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "To Date",
+ "permlevel": 0,
+ "search_index": 1
+ },
+ {
+ "fieldname": "minimum_amount",
+ "fieldtype": "Currency",
+ "label": "Minimum Amount",
+ "permlevel": 0
+ },
+ {
+ "fieldname": "maximum_amount",
+ "fieldtype": "Currency",
+ "label": "Maximum Amount",
+ "permlevel": 0
+ },
+ {
+ "fieldname": "get_unreconciled_entries",
+ "fieldtype": "Button",
+ "label": "Get Unreconciled Entries",
+ "permlevel": 0
+ },
+ {
+ "fieldname": "sec_break1",
+ "fieldtype": "Section Break",
+ "label": "Unreconciled Payment Details",
+ "permlevel": 0
+ },
+ {
+ "fieldname": "payment_reconciliation_payments",
+ "fieldtype": "Table",
+ "label": "Payment Reconciliation Payments",
+ "options": "Payment Reconciliation Payment",
+ "permlevel": 0
+ },
+ {
+ "fieldname": "reconcile",
+ "fieldtype": "Button",
+ "label": "Reconcile",
+ "permlevel": 0
+ },
+ {
+ "fieldname": "sec_break2",
+ "fieldtype": "Section Break",
+ "label": "Invoice/Journal Voucher Details",
+ "permlevel": 0
+ },
+ {
+ "fieldname": "payment_reconciliation_invoices",
+ "fieldtype": "Table",
+ "label": "Payment Reconciliation Invoices",
+ "options": "Payment Reconciliation Invoice",
+ "permlevel": 0,
+ "read_only": 1
+ },
+ {
+ "fieldname": "reconcile_help",
+ "fieldtype": "Small Text",
+ "label": "",
+ "permlevel": 0,
+ "read_only": 1
+ }
+ ],
+ "hide_toolbar": 1,
+ "issingle": 1,
+ "modified": "2014-07-18 15:53:20.638456",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Payment Reconciliation",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "permlevel": 0,
+ "read": 1,
+ "role": "Accounts Manager",
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "permlevel": 0,
+ "read": 1,
+ "role": "Accounts User",
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
new file mode 100644
index 0000000..be53aca
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -0,0 +1,178 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+from frappe.utils import flt
+
+from frappe import msgprint, _
+
+from frappe.model.document import Document
+
+class PaymentReconciliation(Document):
+ def get_unreconciled_entries(self):
+ self.get_jv_entries()
+ self.get_invoice_entries()
+
+ def get_jv_entries(self):
+ self.check_mandatory_to_fetch()
+ dr_or_cr = "credit" if self.party_type == "Customer" else "debit"
+ cond = self.check_condition(dr_or_cr)
+
+ bank_account_condition = "t2.against_account like %(bank_cash_account)s" \
+ if self.bank_cash_account else "1=1"
+
+ jv_entries = frappe.db.sql("""
+ select
+ t1.name as voucher_no, t1.posting_date, t1.remark, t2.account,
+ t2.name as voucher_detail_no, t2.{dr_or_cr}, t2.is_advance
+ from
+ `tabJournal Voucher` t1, `tabJournal Voucher Detail` t2
+ where
+ t1.name = t2.parent and t1.docstatus = 1 and t2.account = %(party_account)s
+ and t2.{dr_or_cr} > 0 and ifnull(t2.against_voucher, '')='' and ifnull(t2.against_invoice, '')=''
+ and ifnull(t2.against_jv, '')='' {cond}
+ and (CASE
+ WHEN t1.voucher_type in ('Debit Note', 'Credit Note')
+ THEN 1=1
+ ELSE {bank_account_condition}
+ END)
+ group by t1.name, t2.name """.format(**{
+ "dr_or_cr": dr_or_cr,
+ "cond": cond,
+ "bank_account_condition": bank_account_condition
+ }), {
+ "party_account": self.party_account,
+ "bank_cash_account": "%%%s%%" % self.bank_cash_account
+ }, as_dict=1)
+
+ self.add_payment_entries(jv_entries)
+
+ def add_payment_entries(self, jv_entries):
+ self.set('payment_reconciliation_payments', [])
+ for e in jv_entries:
+ ent = self.append('payment_reconciliation_payments', {})
+ ent.journal_voucher = e.get('voucher_no')
+ ent.posting_date = e.get('posting_date')
+ ent.amount = flt(e.get('credit')) or flt(e.get('debit'))
+ ent.remark = e.get('remark')
+ ent.voucher_detail_number = e.get('voucher_detail_no')
+ ent.is_advance = e.get('is_advance')
+
+ def get_invoice_entries(self):
+ #Fetch JVs, Sales and Purchase Invoices for 'payment_reconciliation_invoices' to reconcile against
+ non_reconciled_invoices = []
+ dr_or_cr = "debit" if self.party_type == "Customer" else "credit"
+ cond = self.check_condition(dr_or_cr)
+
+ invoice_list = frappe.db.sql("""
+ select
+ voucher_no, voucher_type, posting_date, ifnull(sum(ifnull(%s, 0)), 0) as amount
+ from
+ `tabGL Entry`
+ where
+ account = %s and ifnull(%s, 0) > 0 %s
+ group by voucher_no, voucher_type""" % (dr_or_cr, '%s',
+ dr_or_cr, cond), (self.party_account), as_dict=True)
+
+ for d in invoice_list:
+ payment_amount = frappe.db.sql("""
+ select
+ ifnull(sum(ifnull(%s, 0)), 0)
+ from
+ `tabGL Entry`
+ where
+ account = %s and against_voucher_type = %s and ifnull(against_voucher, '') = %s""" %
+ (("credit" if self.party_type == "Customer" else "debit"), '%s', '%s', '%s'),
+ (self.party_account, d.voucher_type, d.voucher_no))
+
+ payment_amount = payment_amount[0][0] if payment_amount else 0
+
+ if d.amount > payment_amount:
+ non_reconciled_invoices.append({
+ 'voucher_no': d.voucher_no,
+ 'voucher_type': d.voucher_type,
+ 'posting_date': d.posting_date,
+ 'amount': flt(d.amount),
+ 'outstanding_amount': d.amount - payment_amount})
+
+ self.add_invoice_entries(non_reconciled_invoices)
+
+
+ def add_invoice_entries(self, non_reconciled_invoices):
+ #Populate 'payment_reconciliation_invoices' with JVs and Invoices to reconcile against
+ self.set('payment_reconciliation_invoices', [])
+ if not non_reconciled_invoices:
+ frappe.throw(_("No invoices found to be reconciled"))
+
+
+ for e in non_reconciled_invoices:
+ ent = self.append('payment_reconciliation_invoices', {})
+ ent.invoice_type = e.get('voucher_type')
+ ent.invoice_number = e.get('voucher_no')
+ ent.invoice_date = e.get('posting_date')
+ ent.amount = flt(e.get('amount'))
+ ent.outstanding_amount = e.get('outstanding_amount')
+
+ def reconcile(self, args):
+ self.get_invoice_entries()
+ self.validate_invoice()
+ dr_or_cr = "credit" if self.party_type == "Customer" else "debit"
+ lst = []
+ for e in self.get('payment_reconciliation_payments'):
+ lst.append({
+ 'voucher_no' : e.journal_voucher,
+ 'voucher_detail_no' : e.voucher_detail_number,
+ 'against_voucher_type' : e.invoice_type,
+ 'against_voucher' : e.invoice_number,
+ 'account' : self.party_account,
+ 'is_advance' : e.is_advance,
+ 'dr_or_cr' : dr_or_cr,
+ 'unadjusted_amt' : flt(e.amount),
+ 'allocated_amt' : flt(e.amount)
+ })
+
+ if lst:
+ from erpnext.accounts.utils import reconcile_against_document
+ reconcile_against_document(lst)
+ self.get_unreconciled_entries()
+ msgprint(_("Successfully Reconciled"))
+
+
+ def check_mandatory_to_fetch(self):
+ for fieldname in ["company", "party_account"]:
+ if not self.get(fieldname):
+ frappe.throw(_("Please select {0} first").format(self.meta.get_label(fieldname)))
+
+
+ def validate_invoice(self):
+ unreconciled_invoices = frappe._dict()
+ for d in self.get("payment_reconciliation_invoices"):
+ unreconciled_invoices.setdefault(d.invoice_type, {}).setdefault(d.invoice_number, d.outstanding_amount)
+
+ invoices_to_reconcile = []
+ for p in self.get("payment_reconciliation_payments"):
+ if p.invoice_type and p.invoice_number:
+ invoices_to_reconcile.append(p.invoice_number)
+
+ if p.invoice_number not in unreconciled_invoices.get(p.invoice_type):
+ frappe.throw(_("{0}: {1} not found in Invoice Details table")
+ .format(p.invoice_type, p.invoice_number))
+
+ if p.amount > unreconciled_invoices.get(p.invoice_type).get(p.invoice_number):
+ frappe.throw(_("Row {0}: Payment amount must be less than or equals to invoice outstanding amount").format(p.idx))
+
+ if not invoices_to_reconcile:
+ frappe.throw(_("Please select Invoice Type and Invoice Number in atleast one row"))
+
+ def check_condition(self, dr_or_cr):
+ cond = self.from_date and " and posting_date >= '" + self.from_date + "'" or ""
+ cond += self.to_date and " and posting_date <= '" + self.to_date + "'" or ""
+
+ if self.minimum_amount:
+ cond += " and ifnull(%s, 0) >= %s" % (dr_or_cr, self.minimum_amount)
+ if self.maximum_amount:
+ cond += " and ifnull(%s, 0) <= %s" % (dr_or_cr, self.maximum_amount)
+
+ return cond
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_reconciliation_invoice/__init__.py b/erpnext/accounts/doctype/payment_reconciliation_invoice/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_reconciliation_invoice/__init__.py
diff --git a/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json
new file mode 100644
index 0000000..4e4ee1a
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json
@@ -0,0 +1,66 @@
+{
+ "creation": "2014-07-09 16:14:23.672922",
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "fields": [
+ {
+ "fieldname": "invoice_type",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Invoice Type",
+ "options": "Sales Invoice\nPurchase Invoice\nJournal Voucher",
+ "permlevel": 0,
+ "read_only": 1
+ },
+ {
+ "fieldname": "invoice_number",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Invoice Number",
+ "options": "",
+ "permlevel": 0,
+ "read_only": 1
+ },
+ {
+ "fieldname": "invoice_date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "Invoice Date",
+ "permlevel": 0,
+ "read_only": 1
+ },
+ {
+ "fieldname": "col_break1",
+ "fieldtype": "Column Break",
+ "label": "Column Break",
+ "permlevel": 0
+ },
+ {
+ "fieldname": "amount",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Amount",
+ "permlevel": 0,
+ "read_only": 1
+ },
+ {
+ "fieldname": "outstanding_amount",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Outstanding Amount",
+ "permlevel": 0,
+ "read_only": 1
+ }
+ ],
+ "istable": 1,
+ "modified": "2014-07-18 12:20:51.269974",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Payment Reconciliation Invoice",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.py b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.py
new file mode 100644
index 0000000..3094a17
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class PaymentReconciliationInvoice(Document):
+ pass
diff --git a/erpnext/accounts/doctype/payment_reconciliation_payment/__init__.py b/erpnext/accounts/doctype/payment_reconciliation_payment/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_reconciliation_payment/__init__.py
diff --git a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json
new file mode 100644
index 0000000..3dd36fc
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json
@@ -0,0 +1,107 @@
+{
+ "creation": "2014-07-09 16:13:35.452759",
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "fields": [
+ {
+ "fieldname": "journal_voucher",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Journal Voucher",
+ "options": "Journal Voucher",
+ "permlevel": 0,
+ "read_only": 1,
+ "reqd": 0
+ },
+ {
+ "fieldname": "posting_date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "Posting Date",
+ "permlevel": 0,
+ "read_only": 1
+ },
+ {
+ "fieldname": "amount",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Amount",
+ "permlevel": 0,
+ "read_only": 1
+ },
+ {
+ "fieldname": "is_advance",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "label": "Is Advance",
+ "permlevel": 0,
+ "read_only": 1
+ },
+ {
+ "fieldname": "voucher_detail_number",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "in_list_view": 0,
+ "label": "Voucher Detail Number",
+ "permlevel": 0,
+ "read_only": 1
+ },
+ {
+ "fieldname": "col_break1",
+ "fieldtype": "Column Break",
+ "label": "Column Break",
+ "permlevel": 0
+ },
+ {
+ "default": "Sales Invoice",
+ "fieldname": "invoice_type",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Invoice Type",
+ "options": "Sales Invoice\nPurchase Invoice\nJournal Voucher",
+ "permlevel": 0,
+ "read_only": 0,
+ "reqd": 1
+ },
+ {
+ "fieldname": "invoice_number",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Invoice Number",
+ "options": "",
+ "permlevel": 0,
+ "reqd": 1
+ },
+ {
+ "fieldname": "sec_break1",
+ "fieldtype": "Section Break",
+ "label": "",
+ "permlevel": 0
+ },
+ {
+ "fieldname": "remark",
+ "fieldtype": "Small Text",
+ "in_list_view": 1,
+ "label": "Remark",
+ "permlevel": 0,
+ "read_only": 1
+ },
+ {
+ "fieldname": "col_break2",
+ "fieldtype": "Column Break",
+ "label": "Column Break",
+ "permlevel": 0
+ }
+ ],
+ "istable": 1,
+ "modified": "2014-07-18 15:53:15.589501",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Payment Reconciliation Payment",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.py b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.py
new file mode 100644
index 0000000..21e19bd
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class PaymentReconciliationPayment(Document):
+ pass
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
index 9141697..9a3b312 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
@@ -183,7 +183,7 @@
group_condition = _get_tree_conditions(parenttype)
if group_condition:
conditions += " and " + group_condition
-
+ if not args.price_list: args.price_list = None
conditions += " and ifnull(for_price_list, '') in (%(price_list)s, '')"
if args.get("transaction_date"):
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 80aa73a..74a9628 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -30,6 +30,7 @@
'target_ref_field': 'amount',
'source_field': 'amount',
'percent_join_field': 'purchase_order',
+ 'overflow_type': 'billing'
}]
def validate(self):
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 834865b..0f6737c 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -36,7 +36,8 @@
'join_field': 'so_detail',
'percent_join_field': 'sales_order',
'status_field': 'billing_status',
- 'keyword': 'Billed'
+ 'keyword': 'Billed',
+ 'overflow_type': 'billing'
}]
def validate(self):
@@ -134,7 +135,8 @@
'keyword':'Delivered',
'second_source_dt': 'Delivery Note Item',
'second_source_field': 'qty',
- 'second_join_field': 'prevdoc_detail_docname'
+ 'second_join_field': 'prevdoc_detail_docname',
+ 'overflow_type': 'delivery'
})
def on_update_after_submit(self):
@@ -339,8 +341,8 @@
def validate_pos(self):
if not self.cash_bank_account and flt(self.paid_amount):
- msgprint(_("Cash or Bank Account is mandatory for making payment entry"))
- raise Exception
+ frappe.throw(_("Cash or Bank Account is mandatory for making payment entry"))
+
if flt(self.paid_amount) + flt(self.write_off_amount) \
- flt(self.grand_total) > 1/(10**(self.precision("grand_total") + 1)):
frappe.throw(_("""Paid amount + Write Off Amount can not be greater than Grand Total"""))
@@ -431,9 +433,8 @@
submitted = frappe.db.sql("""select name from `tabSales Order`
where docstatus = 1 and name = %s""", d.sales_order)
if not submitted:
- msgprint(_("Sales Order {0} is not submitted").format(d.sales_order))
- raise Exception
-
+ frappe.throw(_("Sales Order {0} is not submitted").format(d.sales_order))
+
if d.delivery_note:
submitted = frappe.db.sql("""select name from `tabDelivery Note`
where docstatus = 1 and name = %s""", d.delivery_note)
@@ -682,7 +683,7 @@
if exception_list:
exception_message = "\n\n".join([cstr(d) for d in exception_list])
- raise Exception, exception_message
+ frappe.throw(exception_message)
def make_new_invoice(ref_wrapper, posting_date):
from erpnext.accounts.utils import get_fiscal_year
diff --git a/erpnext/accounts/doctype/sales_invoice/test_records.json b/erpnext/accounts/doctype/sales_invoice/test_records.json
index b0828f5..eb86672 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_records.json
+++ b/erpnext/accounts/doctype/sales_invoice/test_records.json
@@ -1,385 +1,385 @@
[
{
- "company": "_Test Company",
- "conversion_rate": 1.0,
- "currency": "INR",
- "customer": "_Test Customer",
- "customer_name": "_Test Customer",
- "debit_to": "_Test Customer - _TC",
- "doctype": "Sales Invoice",
- "due_date": "2013-01-23",
+ "company": "_Test Company",
+ "conversion_rate": 1.0,
+ "currency": "INR",
+ "customer": "_Test Customer",
+ "customer_name": "_Test Customer",
+ "debit_to": "_Test Customer - _TC",
+ "doctype": "Sales Invoice",
+ "due_date": "2013-01-23",
"entries": [
{
- "amount": 500.0,
- "base_amount": 500.0,
- "base_rate": 500.0,
- "cost_center": "_Test Cost Center - _TC",
- "description": "138-CMS Shoe",
- "doctype": "Sales Invoice Item",
- "income_account": "Sales - _TC",
- "item_name": "138-CMS Shoe",
- "parentfield": "entries",
- "qty": 1.0,
+ "amount": 500.0,
+ "base_amount": 500.0,
+ "base_rate": 500.0,
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "138-CMS Shoe",
+ "doctype": "Sales Invoice Item",
+ "income_account": "Sales - _TC",
+ "item_name": "138-CMS Shoe",
+ "parentfield": "entries",
+ "qty": 1.0,
"rate": 500.0
}
- ],
- "fiscal_year": "_Test Fiscal Year 2013",
- "grand_total": 561.8,
- "grand_total_export": 561.8,
- "is_pos": 0,
- "naming_series": "_T-Sales Invoice-",
- "net_total": 500.0,
+ ],
+ "fiscal_year": "_Test Fiscal Year 2013",
+ "grand_total": 561.8,
+ "grand_total_export": 561.8,
+ "is_pos": 0,
+ "naming_series": "_T-Sales Invoice-",
+ "net_total": 500.0,
"other_charges": [
{
- "account_head": "_Test Account VAT - _TC",
- "charge_type": "On Net Total",
- "description": "VAT",
- "doctype": "Sales Taxes and Charges",
- "parentfield": "other_charges",
+ "account_head": "_Test Account VAT - _TC",
+ "charge_type": "On Net Total",
+ "description": "VAT",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
"rate": 6
- },
+ },
{
- "account_head": "_Test Account Service Tax - _TC",
- "charge_type": "On Net Total",
- "description": "Service Tax",
- "doctype": "Sales Taxes and Charges",
- "parentfield": "other_charges",
+ "account_head": "_Test Account Service Tax - _TC",
+ "charge_type": "On Net Total",
+ "description": "Service Tax",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
"rate": 6.36
}
- ],
- "plc_conversion_rate": 1.0,
- "posting_date": "2013-01-23",
- "price_list_currency": "INR",
+ ],
+ "plc_conversion_rate": 1.0,
+ "posting_date": "2013-01-23",
+ "price_list_currency": "INR",
"sales_team": [
{
- "allocated_percentage": 65.5,
- "doctype": "Sales Team",
- "parentfield": "sales_team",
+ "allocated_percentage": 65.5,
+ "doctype": "Sales Team",
+ "parentfield": "sales_team",
"sales_person": "_Test Sales Person 1"
- },
+ },
{
- "allocated_percentage": 34.5,
- "doctype": "Sales Team",
- "parentfield": "sales_team",
+ "allocated_percentage": 34.5,
+ "doctype": "Sales Team",
+ "parentfield": "sales_team",
"sales_person": "_Test Sales Person 2"
}
- ],
- "selling_price_list": "_Test Price List",
+ ],
+ "selling_price_list": "_Test Price List",
"territory": "_Test Territory"
- },
+ },
{
- "company": "_Test Company",
- "conversion_rate": 1.0,
- "currency": "INR",
- "customer": "_Test Customer",
- "customer_name": "_Test Customer",
- "debit_to": "_Test Customer - _TC",
- "doctype": "Sales Invoice",
- "due_date": "2013-01-23",
+ "company": "_Test Company",
+ "conversion_rate": 1.0,
+ "currency": "INR",
+ "customer": "_Test Customer",
+ "customer_name": "_Test Customer",
+ "debit_to": "_Test Customer - _TC",
+ "doctype": "Sales Invoice",
+ "due_date": "2013-03-07",
"entries": [
{
- "amount": 500.0,
- "base_amount": 500.0,
- "base_rate": 500.0,
- "cost_center": "_Test Cost Center - _TC",
- "description": "_Test Item",
- "doctype": "Sales Invoice Item",
- "expense_account": "_Test Account Cost for Goods Sold - _TC",
- "income_account": "Sales - _TC",
- "item_code": "_Test Item",
- "item_name": "_Test Item",
- "parentfield": "entries",
- "price_list_rate": 500.0,
+ "amount": 500.0,
+ "base_amount": 500.0,
+ "base_rate": 500.0,
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "_Test Item",
+ "doctype": "Sales Invoice Item",
+ "expense_account": "_Test Account Cost for Goods Sold - _TC",
+ "income_account": "Sales - _TC",
+ "item_code": "_Test Item",
+ "item_name": "_Test Item",
+ "parentfield": "entries",
+ "price_list_rate": 500.0,
"qty": 1.0
}
- ],
- "fiscal_year": "_Test Fiscal Year 2013",
- "grand_total": 630.0,
- "grand_total_export": 630.0,
- "is_pos": 0,
- "naming_series": "_T-Sales Invoice-",
- "net_total": 500.0,
+ ],
+ "fiscal_year": "_Test Fiscal Year 2013",
+ "grand_total": 630.0,
+ "grand_total_export": 630.0,
+ "is_pos": 0,
+ "naming_series": "_T-Sales Invoice-",
+ "net_total": 500.0,
"other_charges": [
{
- "account_head": "_Test Account VAT - _TC",
- "charge_type": "On Net Total",
- "description": "VAT",
- "doctype": "Sales Taxes and Charges",
- "parentfield": "other_charges",
+ "account_head": "_Test Account VAT - _TC",
+ "charge_type": "On Net Total",
+ "description": "VAT",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
"rate": 16
- },
+ },
{
- "account_head": "_Test Account Service Tax - _TC",
- "charge_type": "On Net Total",
- "description": "Service Tax",
- "doctype": "Sales Taxes and Charges",
- "parentfield": "other_charges",
+ "account_head": "_Test Account Service Tax - _TC",
+ "charge_type": "On Net Total",
+ "description": "Service Tax",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
"rate": 10
}
- ],
- "plc_conversion_rate": 1.0,
- "posting_date": "2013-03-07",
- "price_list_currency": "INR",
- "selling_price_list": "_Test Price List",
+ ],
+ "plc_conversion_rate": 1.0,
+ "posting_date": "2013-03-07",
+ "price_list_currency": "INR",
+ "selling_price_list": "_Test Price List",
"territory": "_Test Territory"
- },
+ },
{
- "company": "_Test Company",
- "conversion_rate": 1.0,
- "currency": "INR",
- "customer": "_Test Customer",
- "customer_name": "_Test Customer",
- "debit_to": "_Test Customer - _TC",
- "doctype": "Sales Invoice",
- "due_date": "2013-01-23",
+ "company": "_Test Company",
+ "conversion_rate": 1.0,
+ "currency": "INR",
+ "customer": "_Test Customer",
+ "customer_name": "_Test Customer",
+ "debit_to": "_Test Customer - _TC",
+ "doctype": "Sales Invoice",
+ "due_date": "2013-01-23",
"entries": [
{
- "cost_center": "_Test Cost Center - _TC",
- "doctype": "Sales Invoice Item",
- "income_account": "Sales - _TC",
- "item_code": "_Test Item Home Desktop 100",
- "item_name": "_Test Item Home Desktop 100",
- "item_tax_rate": "{\"_Test Account Excise Duty - _TC\": 10}",
- "parentfield": "entries",
- "price_list_rate": 50,
- "qty": 10,
- "rate": 50,
+ "cost_center": "_Test Cost Center - _TC",
+ "doctype": "Sales Invoice Item",
+ "income_account": "Sales - _TC",
+ "item_code": "_Test Item Home Desktop 100",
+ "item_name": "_Test Item Home Desktop 100",
+ "item_tax_rate": "{\"_Test Account Excise Duty - _TC\": 10}",
+ "parentfield": "entries",
+ "price_list_rate": 50,
+ "qty": 10,
+ "rate": 50,
"stock_uom": "_Test UOM"
- },
+ },
{
- "cost_center": "_Test Cost Center - _TC",
- "doctype": "Sales Invoice Item",
- "income_account": "Sales - _TC",
- "item_code": "_Test Item Home Desktop 200",
- "item_name": "_Test Item Home Desktop 200",
- "parentfield": "entries",
- "price_list_rate": 150,
- "qty": 5,
- "rate": 150,
+ "cost_center": "_Test Cost Center - _TC",
+ "doctype": "Sales Invoice Item",
+ "income_account": "Sales - _TC",
+ "item_code": "_Test Item Home Desktop 200",
+ "item_name": "_Test Item Home Desktop 200",
+ "parentfield": "entries",
+ "price_list_rate": 150,
+ "qty": 5,
+ "rate": 150,
"stock_uom": "_Test UOM"
}
- ],
- "fiscal_year": "_Test Fiscal Year 2013",
- "grand_total_export": 0,
- "is_pos": 0,
- "naming_series": "_T-Sales Invoice-",
+ ],
+ "fiscal_year": "_Test Fiscal Year 2013",
+ "grand_total_export": 0,
+ "is_pos": 0,
+ "naming_series": "_T-Sales Invoice-",
"other_charges": [
{
- "account_head": "_Test Account Shipping Charges - _TC",
- "charge_type": "Actual",
- "cost_center": "_Test Cost Center - _TC",
- "description": "Shipping Charges",
- "doctype": "Sales Taxes and Charges",
- "parentfield": "other_charges",
+ "account_head": "_Test Account Shipping Charges - _TC",
+ "charge_type": "Actual",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Shipping Charges",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
"rate": 100
- },
+ },
{
- "account_head": "_Test Account Customs Duty - _TC",
- "charge_type": "On Net Total",
- "cost_center": "_Test Cost Center - _TC",
- "description": "Customs Duty",
- "doctype": "Sales Taxes and Charges",
- "parentfield": "other_charges",
+ "account_head": "_Test Account Customs Duty - _TC",
+ "charge_type": "On Net Total",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Customs Duty",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
"rate": 10
- },
+ },
{
- "account_head": "_Test Account Excise Duty - _TC",
- "charge_type": "On Net Total",
- "cost_center": "_Test Cost Center - _TC",
- "description": "Excise Duty",
- "doctype": "Sales Taxes and Charges",
- "parentfield": "other_charges",
+ "account_head": "_Test Account Excise Duty - _TC",
+ "charge_type": "On Net Total",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Excise Duty",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
"rate": 12
- },
+ },
{
- "account_head": "_Test Account Education Cess - _TC",
- "charge_type": "On Previous Row Amount",
- "cost_center": "_Test Cost Center - _TC",
- "description": "Education Cess",
- "doctype": "Sales Taxes and Charges",
- "parentfield": "other_charges",
- "rate": 2,
+ "account_head": "_Test Account Education Cess - _TC",
+ "charge_type": "On Previous Row Amount",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Education Cess",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "rate": 2,
"row_id": 3
- },
+ },
{
- "account_head": "_Test Account S&H Education Cess - _TC",
- "charge_type": "On Previous Row Amount",
- "cost_center": "_Test Cost Center - _TC",
- "description": "S&H Education Cess",
- "doctype": "Sales Taxes and Charges",
- "parentfield": "other_charges",
- "rate": 1,
+ "account_head": "_Test Account S&H Education Cess - _TC",
+ "charge_type": "On Previous Row Amount",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "S&H Education Cess",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "rate": 1,
"row_id": 3
- },
+ },
{
- "account_head": "_Test Account CST - _TC",
- "charge_type": "On Previous Row Total",
- "cost_center": "_Test Cost Center - _TC",
- "description": "CST",
- "doctype": "Sales Taxes and Charges",
- "parentfield": "other_charges",
- "rate": 2,
+ "account_head": "_Test Account CST - _TC",
+ "charge_type": "On Previous Row Total",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "CST",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "rate": 2,
"row_id": 5
- },
+ },
{
- "account_head": "_Test Account VAT - _TC",
- "charge_type": "On Net Total",
- "cost_center": "_Test Cost Center - _TC",
- "description": "VAT",
- "doctype": "Sales Taxes and Charges",
- "parentfield": "other_charges",
+ "account_head": "_Test Account VAT - _TC",
+ "charge_type": "On Net Total",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "VAT",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
"rate": 12.5
- },
+ },
{
- "account_head": "_Test Account Discount - _TC",
- "charge_type": "On Previous Row Total",
- "cost_center": "_Test Cost Center - _TC",
- "description": "Discount",
- "doctype": "Sales Taxes and Charges",
- "parentfield": "other_charges",
- "rate": -10,
+ "account_head": "_Test Account Discount - _TC",
+ "charge_type": "On Previous Row Total",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Discount",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "rate": -10,
"row_id": 7
}
- ],
- "plc_conversion_rate": 1.0,
- "posting_date": "2013-01-23",
- "price_list_currency": "INR",
- "selling_price_list": "_Test Price List",
+ ],
+ "plc_conversion_rate": 1.0,
+ "posting_date": "2013-01-23",
+ "price_list_currency": "INR",
+ "selling_price_list": "_Test Price List",
"territory": "_Test Territory"
- },
+ },
{
- "company": "_Test Company",
- "conversion_rate": 1.0,
- "currency": "INR",
- "customer": "_Test Customer",
- "customer_name": "_Test Customer",
- "debit_to": "_Test Customer - _TC",
- "doctype": "Sales Invoice",
- "due_date": "2013-01-23",
+ "company": "_Test Company",
+ "conversion_rate": 1.0,
+ "currency": "INR",
+ "customer": "_Test Customer",
+ "customer_name": "_Test Customer",
+ "debit_to": "_Test Customer - _TC",
+ "doctype": "Sales Invoice",
+ "due_date": "2013-01-23",
"entries": [
{
- "cost_center": "_Test Cost Center - _TC",
- "doctype": "Sales Invoice Item",
- "income_account": "Sales - _TC",
- "item_code": "_Test Item Home Desktop 100",
- "item_name": "_Test Item Home Desktop 100",
- "item_tax_rate": "{\"_Test Account Excise Duty - _TC\": 10}",
- "parentfield": "entries",
- "price_list_rate": 62.5,
- "qty": 10,
+ "cost_center": "_Test Cost Center - _TC",
+ "doctype": "Sales Invoice Item",
+ "income_account": "Sales - _TC",
+ "item_code": "_Test Item Home Desktop 100",
+ "item_name": "_Test Item Home Desktop 100",
+ "item_tax_rate": "{\"_Test Account Excise Duty - _TC\": 10}",
+ "parentfield": "entries",
+ "price_list_rate": 62.5,
+ "qty": 10,
"stock_uom": "_Test UOM"
- },
+ },
{
- "cost_center": "_Test Cost Center - _TC",
- "doctype": "Sales Invoice Item",
- "income_account": "Sales - _TC",
- "item_code": "_Test Item Home Desktop 200",
- "item_name": "_Test Item Home Desktop 200",
- "parentfield": "entries",
- "price_list_rate": 190.66,
- "qty": 5,
+ "cost_center": "_Test Cost Center - _TC",
+ "doctype": "Sales Invoice Item",
+ "income_account": "Sales - _TC",
+ "item_code": "_Test Item Home Desktop 200",
+ "item_name": "_Test Item Home Desktop 200",
+ "parentfield": "entries",
+ "price_list_rate": 190.66,
+ "qty": 5,
"stock_uom": "_Test UOM"
}
- ],
- "fiscal_year": "_Test Fiscal Year 2013",
- "grand_total_export": 0,
- "is_pos": 0,
- "naming_series": "_T-Sales Invoice-",
+ ],
+ "fiscal_year": "_Test Fiscal Year 2013",
+ "grand_total_export": 0,
+ "is_pos": 0,
+ "naming_series": "_T-Sales Invoice-",
"other_charges": [
{
- "account_head": "_Test Account Excise Duty - _TC",
- "charge_type": "On Net Total",
- "cost_center": "_Test Cost Center - _TC",
- "description": "Excise Duty",
- "doctype": "Sales Taxes and Charges",
- "idx": 1,
- "included_in_print_rate": 1,
- "parentfield": "other_charges",
+ "account_head": "_Test Account Excise Duty - _TC",
+ "charge_type": "On Net Total",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Excise Duty",
+ "doctype": "Sales Taxes and Charges",
+ "idx": 1,
+ "included_in_print_rate": 1,
+ "parentfield": "other_charges",
"rate": 12
- },
+ },
{
- "account_head": "_Test Account Education Cess - _TC",
- "charge_type": "On Previous Row Amount",
- "cost_center": "_Test Cost Center - _TC",
- "description": "Education Cess",
- "doctype": "Sales Taxes and Charges",
- "idx": 2,
- "included_in_print_rate": 1,
- "parentfield": "other_charges",
- "rate": 2,
+ "account_head": "_Test Account Education Cess - _TC",
+ "charge_type": "On Previous Row Amount",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Education Cess",
+ "doctype": "Sales Taxes and Charges",
+ "idx": 2,
+ "included_in_print_rate": 1,
+ "parentfield": "other_charges",
+ "rate": 2,
"row_id": 1
- },
+ },
{
- "account_head": "_Test Account S&H Education Cess - _TC",
- "charge_type": "On Previous Row Amount",
- "cost_center": "_Test Cost Center - _TC",
- "description": "S&H Education Cess",
- "doctype": "Sales Taxes and Charges",
- "idx": 3,
- "included_in_print_rate": 1,
- "parentfield": "other_charges",
- "rate": 1,
+ "account_head": "_Test Account S&H Education Cess - _TC",
+ "charge_type": "On Previous Row Amount",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "S&H Education Cess",
+ "doctype": "Sales Taxes and Charges",
+ "idx": 3,
+ "included_in_print_rate": 1,
+ "parentfield": "other_charges",
+ "rate": 1,
"row_id": 1
- },
+ },
{
- "account_head": "_Test Account CST - _TC",
- "charge_type": "On Previous Row Total",
- "cost_center": "_Test Cost Center - _TC",
- "description": "CST",
- "doctype": "Sales Taxes and Charges",
- "idx": 4,
- "included_in_print_rate": 1,
- "parentfield": "other_charges",
- "rate": 2,
+ "account_head": "_Test Account CST - _TC",
+ "charge_type": "On Previous Row Total",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "CST",
+ "doctype": "Sales Taxes and Charges",
+ "idx": 4,
+ "included_in_print_rate": 1,
+ "parentfield": "other_charges",
+ "rate": 2,
"row_id": 3
- },
+ },
{
- "account_head": "_Test Account VAT - _TC",
- "charge_type": "On Net Total",
- "cost_center": "_Test Cost Center - _TC",
- "description": "VAT",
- "doctype": "Sales Taxes and Charges",
- "idx": 5,
- "included_in_print_rate": 1,
- "parentfield": "other_charges",
+ "account_head": "_Test Account VAT - _TC",
+ "charge_type": "On Net Total",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "VAT",
+ "doctype": "Sales Taxes and Charges",
+ "idx": 5,
+ "included_in_print_rate": 1,
+ "parentfield": "other_charges",
"rate": 12.5
- },
+ },
{
- "account_head": "_Test Account Customs Duty - _TC",
- "charge_type": "On Net Total",
- "cost_center": "_Test Cost Center - _TC",
- "description": "Customs Duty",
- "doctype": "Sales Taxes and Charges",
- "idx": 6,
- "parentfield": "other_charges",
+ "account_head": "_Test Account Customs Duty - _TC",
+ "charge_type": "On Net Total",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Customs Duty",
+ "doctype": "Sales Taxes and Charges",
+ "idx": 6,
+ "parentfield": "other_charges",
"rate": 10
- },
+ },
{
- "account_head": "_Test Account Shipping Charges - _TC",
- "charge_type": "Actual",
- "cost_center": "_Test Cost Center - _TC",
- "description": "Shipping Charges",
- "doctype": "Sales Taxes and Charges",
- "idx": 7,
- "parentfield": "other_charges",
+ "account_head": "_Test Account Shipping Charges - _TC",
+ "charge_type": "Actual",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Shipping Charges",
+ "doctype": "Sales Taxes and Charges",
+ "idx": 7,
+ "parentfield": "other_charges",
"rate": 100
- },
+ },
{
- "account_head": "_Test Account Discount - _TC",
- "charge_type": "On Previous Row Total",
- "cost_center": "_Test Cost Center - _TC",
- "description": "Discount",
- "doctype": "Sales Taxes and Charges",
- "idx": 8,
- "parentfield": "other_charges",
- "rate": -10,
+ "account_head": "_Test Account Discount - _TC",
+ "charge_type": "On Previous Row Total",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Discount",
+ "doctype": "Sales Taxes and Charges",
+ "idx": 8,
+ "parentfield": "other_charges",
+ "rate": -10,
"row_id": 7
}
- ],
- "plc_conversion_rate": 1.0,
- "posting_date": "2013-01-23",
- "price_list_currency": "INR",
- "selling_price_list": "_Test Price List",
+ ],
+ "plc_conversion_rate": 1.0,
+ "posting_date": "2013-01-23",
+ "price_list_currency": "INR",
+ "selling_price_list": "_Test Price List",
"territory": "_Test Territory"
}
-]
\ No newline at end of file
+]
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 85e5782..1d22e09 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -674,6 +674,7 @@
"notification_email_address": "test@example.com, test1@example.com, test2@example.com",
"repeat_on_day_of_month": getdate(today).day,
"posting_date": today,
+ "due_date": None,
"fiscal_year": get_fiscal_year(today)[0],
"invoice_period_from_date": get_first_day(today),
"invoice_period_to_date": get_last_day(today)
diff --git a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.html b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.html
new file mode 100644
index 0000000..1a401f5
--- /dev/null
+++ b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.html
@@ -0,0 +1,49 @@
+<div style="margin-bottom: 7px;" class="text-center">
+ {%= frappe.boot.letter_heads[frappe.defaults.get_default("letter_head")] %}
+</div>
+<h2 class="text-center">{%= __("Bank Reconciliation Statement") %}</h2>
+<h4 class="text-center">{%= filters.account %}</h3>
+<hr>
+<table class="table table-bordered">
+ <thead>
+ <tr>
+ <th style="width: 15%">{%= __("Posting Date") %}</th>
+ <th style="width: 15%">{%= __("Journal Voucher") %}</th>
+ <th style="width: 40%">{%= __("Reference") %}</th>
+ <th style="width: 15%; text-align: right;">{%= __("Debit") %}</th>
+ <th style="width: 15%; text-align: right;">{%= __("Credit") %}</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for(var i=0, l=data.length; i<l; i++) { %}
+ {% if (data[i].posting_date) { %}
+ <tr>
+ <td>{%= dateutil.str_to_user(data[i].posting_date) %}</td>
+ <td>{%= data[i].journal_voucher %}</td>
+ <td>{%= __("Against") %}: {%= data[i].against_account %}
+ {% if (data[i].reference) { %}
+ <br>{%= __("Reference") %}: {%= data[i].reference %}
+ {% if (data[i].ref_date) { %}
+ <br>{%= __("Reference Date") %}: {%= dateutil.str_to_user(data[i].ref_date) %}
+ {% } %}
+ {% } %}
+ {% if (data[i].clearance_date) { %}
+ <br>{%= __("Clearance Date") %}: {%= dateutil.str_to_user(data[i].clearance_date) %}
+ {% } %}
+ </td>
+ <td style="text-align: right">{%= format_currency(data[i].debit) %}</td>
+ <td style="text-align: right">{%= format_currency(data[i].credit) %}</td>
+ </tr>
+ {% } else { %}
+ <tr>
+ <td></td>
+ <td></td>
+ <td>{%= data[i].journal_voucher %}</td>
+ <td style="text-align: right">{%= format_currency(data[i].debit) %}</td>
+ <td style="text-align: right">{%= format_currency(data[i].credit) %}</td>
+ </tr>
+ {% } %}
+ {% } %}
+ </tbody>
+</table>
+<p class="text-right text-muted">Printed On {%= dateutil.str_to_user(dateutil.get_datetime_as_string()) %}</p>
diff --git a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
index e87fbd3..119de09 100644
--- a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
+++ b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
@@ -19,28 +19,29 @@
total_debit, total_credit = 0,0
for d in data:
- total_debit += flt(d[4])
- total_credit += flt(d[5])
+ total_debit += flt(d[2])
+ total_credit += flt(d[3])
bank_bal = flt(balance_as_per_company) - flt(total_debit) + flt(total_credit)
data += [
get_balance_row("Balance as per company books", balance_as_per_company),
- ["", "", "", "Amounts not reflected in bank", total_debit, total_credit],
+ ["", "Amounts not reflected in bank", total_debit, total_credit, "", "", "", ""],
get_balance_row("Balance as per bank", bank_bal)
]
return columns, data
def get_columns():
- return ["Journal Voucher:Link/Journal Voucher:140", "Posting Date:Date:100",
- "Clearance Date:Date:110", "Against Account:Link/Account:200",
- "Debit:Currency:120", "Credit:Currency:120"
+ return ["Posting Date:Date:100", "Journal Voucher:Link/Journal Voucher:200",
+ "Debit:Currency:120", "Credit:Currency:120",
+ "Against Account:Link/Account:200", "Reference::100", "Ref Date:Date:110", "Clearance Date:Date:110"
]
def get_entries(filters):
entries = frappe.db.sql("""select
- jv.name, jv.posting_date, jv.clearance_date, jvd.against_account, jvd.debit, jvd.credit
+ jv.posting_date, jv.name, jvd.debit, jvd.credit,
+ jvd.against_account, jv.cheque_no, jv.cheque_date, jv.clearance_date
from
`tabJournal Voucher Detail` jvd, `tabJournal Voucher` jv
where jvd.parent = jv.name and jv.docstatus=1
@@ -52,6 +53,6 @@
def get_balance_row(label, amount):
if amount > 0:
- return ["", "", "", label, amount, 0]
+ return ["", label, amount, 0, "", "", "", ""]
else:
- return ["", "", "", label, 0, amount]
+ return ["", label, 0, amount, "", "", "", ""]
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.html b/erpnext/accounts/report/general_ledger/general_ledger.html
index 63cd1a1..190d455 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.html
+++ b/erpnext/accounts/report/general_ledger/general_ledger.html
@@ -1,5 +1,13 @@
+<div style="margin-bottom: 7px;" class="text-center">
+ {%= frappe.boot.letter_heads[frappe.defaults.get_default("letter_head")] %}
+</div>
<h2 class="text-center">{%= __("Statement of Account") %}</h2>
-<h4 class="text-center">{%= filters.account || "General Ledger" %}</h3>
+<h4 class="text-center">{%= filters.account || "General Ledger" %}</h4>
+<h5 class="text-center">
+ {%= dateutil.str_to_user(filters.from_date) %}
+ {%= __("to") %}
+ {%= dateutil.str_to_user(filters.to_date) %}
+</h5>
<hr>
<table class="table table-bordered">
<thead>
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index 2109d72..f9f5103 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -24,6 +24,7 @@
'target_ref_field': 'qty',
'source_field': 'qty',
'percent_join_field': 'prevdoc_docname',
+ 'overflow_type': 'order'
}]
def validate(self):
diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js
index 99d0e4e..c2b29a4 100644
--- a/erpnext/buying/doctype/supplier/supplier.js
+++ b/erpnext/buying/doctype/supplier/supplier.js
@@ -69,7 +69,8 @@
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.docname+"' and docstatus != 2 order by is_primary_address desc"
+ 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("'", "\\'") + "' and docstatus != 2 order by is_primary_address desc"
},
as_dict: 1,
no_results_message: __('No addresses created'),
@@ -87,7 +88,8 @@
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.docname+"' and docstatus != 2 order by is_primary_contact desc"
+ 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("'", "\\'") + "' and docstatus != 2 order by is_primary_contact desc"
},
as_dict: 1,
no_results_message: __('No contacts created'),
diff --git a/erpnext/config/buying.py b/erpnext/config/buying.py
index bc62519..1b9e5a2 100644
--- a/erpnext/config/buying.py
+++ b/erpnext/config/buying.py
@@ -150,7 +150,7 @@
{
"type": "report",
"is_query_report": True,
- "name": "Supplier Addresses And Contacts",
+ "name": "Supplier Addresses and Contacts",
"doctype": "Supplier"
},
{
diff --git a/erpnext/config/selling.py b/erpnext/config/selling.py
index 200ab6d..c95f15a 100644
--- a/erpnext/config/selling.py
+++ b/erpnext/config/selling.py
@@ -206,7 +206,7 @@
{
"type": "report",
"is_query_report": True,
- "name": "Customer Addresses And Contacts",
+ "name": "Customer Addresses and Contacts",
"doctype": "Contact"
},
{
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index afccdfa..acb0024 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -17,6 +17,7 @@
self.supplier_name = frappe.db.get_value("Supplier",
self.supplier, "supplier_name")
self.is_item_table_empty()
+ self.set_qty_as_per_stock_uom()
self.validate_stock_or_nonstock_items()
self.validate_warehouse()
@@ -317,3 +318,10 @@
def is_item_table_empty(self):
if not len(self.get(self.fname)):
frappe.throw(_("Item table can not be blank"))
+
+ def set_qty_as_per_stock_uom(self):
+ for d in self.get(self.fname):
+ if d.meta.get_field("stock_qty") and not d.stock_qty:
+ if not d.conversion_factor:
+ frappe.throw(_("Row {0}: Conversion Factor is mandatory"))
+ d.stock_qty = flt(d.qty) * flt(d.conversion_factor)
\ No newline at end of file
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index 16d27d4..2650c66 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -32,34 +32,46 @@
return frappe.db.sql("""select name, employee_name from `tabEmployee`
where status = 'Active'
and docstatus < 2
- and (%(key)s like "%(txt)s"
- or employee_name like "%(txt)s")
- %(mcond)s
+ and ({key} like %(txt)s
+ or employee_name like %(txt)s)
+ {mcond}
order by
- if(locate("%(_txt)s", name), locate("%(_txt)s", name), 99999),
- if(locate("%(_txt)s", employee_name), locate("%(_txt)s", employee_name), 99999),
+ if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
+ if(locate(%(_txt)s, employee_name), locate(%(_txt)s, employee_name), 99999),
name, employee_name
- limit %(start)s, %(page_len)s""" % {'key': searchfield, 'txt': "%%%s%%" % txt,
- '_txt': txt.replace("%", ""),
- 'mcond':get_match_cond(doctype), 'start': start, 'page_len': page_len})
+ limit %(start)s, %(page_len)s""".format(**{
+ 'key': searchfield,
+ 'mcond': get_match_cond(doctype)
+ }), {
+ 'txt': "%%%s%%" % txt,
+ '_txt': txt.replace("%", ""),
+ 'start': start,
+ 'page_len': page_len
+ })
# searches for leads which are not converted
def lead_query(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql("""select name, lead_name, company_name from `tabLead`
where docstatus < 2
and ifnull(status, '') != 'Converted'
- and (%(key)s like "%(txt)s"
- or lead_name like "%(txt)s"
- or company_name like "%(txt)s")
- %(mcond)s
+ and ({key} like %(txt)s
+ or lead_name like %(txt)s
+ or company_name like %(txt)s)
+ {mcond}
order by
- if(locate("%(_txt)s", name), locate("%(_txt)s", name), 99999),
- if(locate("%(_txt)s", lead_name), locate("%(_txt)s", lead_name), 99999),
- if(locate("%(_txt)s", company_name), locate("%(_txt)s", company_name), 99999),
+ if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
+ if(locate(%(_txt)s, lead_name), locate(%(_txt)s, lead_name), 99999),
+ if(locate(%(_txt)s, company_name), locate(%(_txt)s, company_name), 99999),
name, lead_name
- limit %(start)s, %(page_len)s""" % {'key': searchfield, 'txt': "%%%s%%" % txt,
- '_txt': txt.replace("%", ""),
- 'mcond':get_match_cond(doctype), 'start': start, 'page_len': page_len})
+ limit %(start)s, %(page_len)s""".format(**{
+ 'key': searchfield,
+ 'mcond':get_match_cond(doctype)
+ }), {
+ 'txt': "%%%s%%" % txt,
+ '_txt': txt.replace("%", ""),
+ 'start': start,
+ 'page_len': page_len
+ })
# searches for customer
def customer_query(doctype, txt, searchfield, start, page_len, filters):
@@ -72,19 +84,25 @@
fields = ", ".join(fields)
- return frappe.db.sql("""select %(field)s from `tabCustomer`
+ return frappe.db.sql("""select {fields} from `tabCustomer`
where docstatus < 2
- and (%(key)s like "%(txt)s"
- or customer_name like "%(txt)s")
- %(mcond)s
+ and ({key} like %(txt)s
+ or customer_name like %(txt)s)
+ {mcond}
order by
- if(locate("%(_txt)s", name), locate("%(_txt)s", name), 99999),
- if(locate("%(_txt)s", customer_name), locate("%(_txt)s", customer_name), 99999),
+ if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
+ if(locate(%(_txt)s, customer_name), locate(%(_txt)s, customer_name), 99999),
name, customer_name
- limit %(start)s, %(page_len)s""" % {'field': fields,'key': searchfield,
- 'txt': "%%%s%%" % txt, '_txt': txt.replace("%", ""),
- 'mcond':get_match_cond(doctype),
- 'start': start, 'page_len': page_len})
+ limit %(start)s, %(page_len)s""".format(**{
+ "fields": fields,
+ "key": searchfield,
+ "mcond": get_match_cond(doctype)
+ }), {
+ 'txt': "%%%s%%" % txt,
+ '_txt': txt.replace("%", ""),
+ 'start': start,
+ 'page_len': page_len
+ })
# searches for supplier
def supplier_query(doctype, txt, searchfield, start, page_len, filters):
@@ -95,19 +113,25 @@
fields = ["name", "supplier_name", "supplier_type"]
fields = ", ".join(fields)
- return frappe.db.sql("""select %(field)s from `tabSupplier`
+ return frappe.db.sql("""select {field} from `tabSupplier`
where docstatus < 2
- and (%(key)s like "%(txt)s"
- or supplier_name like "%(txt)s")
- %(mcond)s
+ and ({key} like %(txt)s
+ or supplier_name like %(txt)s)
+ {mcond}
order by
- if(locate("%(_txt)s", name), locate("%(_txt)s", name), 99999),
- if(locate("%(_txt)s", supplier_name), locate("%(_txt)s", supplier_name), 99999),
+ if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
+ if(locate(%(_txt)s, supplier_name), locate(%(_txt)s, supplier_name), 99999),
name, supplier_name
- limit %(start)s, %(page_len)s """ % {'field': fields,'key': searchfield,
- 'txt': "%%%s%%" % txt, '_txt': txt.replace("%", ""),
- 'mcond':get_match_cond(doctype), 'start': start,
- 'page_len': page_len})
+ limit %(start)s, %(page_len)s """.format(**{
+ 'field': fields,
+ 'key': searchfield,
+ 'mcond':get_match_cond(doctype)
+ }), {
+ 'txt': "%%%s%%" % txt,
+ '_txt': txt.replace("%", ""),
+ 'start': start,
+ 'page_len': page_len
+ })
def tax_account_query(doctype, txt, searchfield, start, page_len, filters):
tax_accounts = frappe.db.sql("""select name, parent_account from tabAccount
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index 90eacd9..6a2dce0 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -132,11 +132,12 @@
if not item[args['target_ref_field']]:
msgprint(_("Note: System will not check over-delivery and over-booking for Item {0} as quantity or amount is 0").format(item.item_code))
elif args.get('no_tolerance'):
- item['reduce_by'] = item[args['target_field']] - \
- item[args['target_ref_field']]
+ item['reduce_by'] = item[args['target_field']] - item[args['target_ref_field']]
if item['reduce_by'] > .01:
- msgprint(_("Allowance for over-delivery / over-billing crossed for Item {0}").format(item.item_code))
- throw(_("{0} must be less than or equal to {1}").format(_(item.target_ref_field), item[args["target_ref_field"]]))
+ msgprint(_("Allowance for over-{0} crossed for Item {1}")
+ .format(args["overflow_type"], item.item_code))
+ throw(_("{0} must be reduced by {1} or you should increase overflow tolerance")
+ .format(_(item.target_ref_field.title()), item["reduce_by"]))
else:
self.check_overflow_with_tolerance(item, args)
@@ -156,8 +157,10 @@
item['max_allowed'] = flt(item[args['target_ref_field']] * (100+tolerance)/100)
item['reduce_by'] = item[args['target_field']] - item['max_allowed']
- msgprint(_("Allowance for over-delivery / over-billing crossed for Item {0}.").format(item["item_code"]))
- throw(_("{0} must be less than or equal to {1}").format(item["target_ref_field"].title(), item["max_allowed"]))
+ msgprint(_("Allowance for over-{0} crossed for Item {1}.")
+ .format(args["overflow_type"], item["item_code"]))
+ throw(_("{0} must be reduced by {1} or you should increase overflow tolerance")
+ .format(_(item["target_ref_field"].title()), item["reduce_by"]))
def update_qty(self, change_modified=True):
"""
@@ -263,8 +266,7 @@
if not tolerance:
if global_tolerance == None:
- global_tolerance = flt(frappe.db.get_value('Global Defaults', None,
- 'tolerance'))
+ global_tolerance = flt(frappe.db.get_value('Stock Settings', None, 'tolerance'))
tolerance = global_tolerance
item_tolerance[item_code] = tolerance
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index bb3ab69..27437a3 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -240,7 +240,7 @@
else:
is_expense_account = frappe.db.get_value("Account",
item.get("expense_account"), "report_type")=="Profit and Loss"
- if self.doctype != "Purchase Receipt" and not is_expense_account:
+ if self.doctype not in ("Purchase Receipt", "Stock Reconciliation") and not is_expense_account:
frappe.throw(_("Expense / Difference account ({0}) must be a 'Profit or Loss' account")
.format(item.get("expense_account")))
if is_expense_account and not item.get("cost_center"):
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 1dde638..df15916 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -7,6 +7,8 @@
app_color = "#e74c3c"
app_version = __version__
+error_report_email = "support@erpnext.com"
+
app_include_js = "assets/js/erpnext.min.js"
app_include_css = "assets/css/erpnext.css"
web_include_js = "assets/js/erpnext-web.min.js"
diff --git a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.js b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.js
index 0415c91..d57f639 100644
--- a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.js
+++ b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.js
@@ -37,8 +37,8 @@
});
}
-cur_frm.fields_dict['pp_details'].grid.get_field('bom_no').get_query = function(doc) {
- var d = locals[this.doctype][this.docname];
+cur_frm.fields_dict['pp_details'].grid.get_field('bom_no').get_query = function(doc, cdt, cdn) {
+ var d = locals[cdt][cdn];
if (d.item_code) {
return {
query: "erpnext.controllers.queries.bom",
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 1c2112d..1a903de 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -67,4 +67,5 @@
erpnext.patches.v4_1.set_outgoing_email_footer
erpnext.patches.v4_1.fix_jv_remarks
erpnext.patches.v4_1.fix_sales_order_delivered_status
-erpnext.patches.v4_1.fix_delivery_and_billing_status_for_draft_so
\ No newline at end of file
+erpnext.patches.v4_1.fix_delivery_and_billing_status
+execute:frappe.db.sql("update `tabAccount` set root_type='Liability' where root_type='Income' and report_type='Balance Sheet'")
\ No newline at end of file
diff --git a/erpnext/patches/v4_1/fix_delivery_and_billing_status_for_draft_so.py b/erpnext/patches/v4_1/fix_delivery_and_billing_status.py
similarity index 79%
rename from erpnext/patches/v4_1/fix_delivery_and_billing_status_for_draft_so.py
rename to erpnext/patches/v4_1/fix_delivery_and_billing_status.py
index 8d38338..2dbc825 100644
--- a/erpnext/patches/v4_1/fix_delivery_and_billing_status_for_draft_so.py
+++ b/erpnext/patches/v4_1/fix_delivery_and_billing_status.py
@@ -5,8 +5,8 @@
import frappe
def execute():
- frappe.db.sql("""update `tabSales Order` set delivery_status = 'Not Delivered'
- where delivery_status = 'Delivered' and ifnull(per_delivered, 0) = 0 and docstatus = 0""")
+ frappe.db.sql("""update `tabSales Order` set delivery_status = 'Not Delivered'
+ where delivery_status = 'Delivered' and ifnull(per_delivered, 0) = 0 and ifnull(docstatus, 0) in (0, 1)""")
- frappe.db.sql("""update `tabSales Order` set billing_status = 'Not Billed'
- where billing_status = 'Billed' and ifnull(per_billed, 0) = 0 and docstatus = 0""")
\ No newline at end of file
+ frappe.db.sql("""update `tabSales Order` set billing_status = 'Not Billed'
+ where billing_status = 'Billed' and ifnull(per_billed, 0) = 0 and ifnull(docstatus, 0) in (0, 1)""")
diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py
index bce9c52..4a14c55 100644
--- a/erpnext/projects/doctype/project/project.py
+++ b/erpnext/projects/doctype/project/project.py
@@ -55,7 +55,7 @@
"event_type": "Private",
"ref_type": self.doctype,
"ref_name": self.name
- }).insert()
+ }).insert(ignore_permissions=True)
def on_trash(self):
delete_events(self.doctype, self.name)
diff --git a/erpnext/public/js/queries.js b/erpnext/public/js/queries.js
index b57b765..4bb3302 100644
--- a/erpnext/public/js/queries.js
+++ b/erpnext/public/js/queries.js
@@ -71,7 +71,7 @@
warehouse: function(doc) {
return {
- filters: [["Warehouse", "company", "in", ["", doc.company]]]
+ filters: [["Warehouse", "company", "in", ["", cstr(doc.company)]]]
}
}
});
diff --git a/erpnext/public/js/transaction.js b/erpnext/public/js/transaction.js
index 1790a47..ae5864d 100644
--- a/erpnext/public/js/transaction.js
+++ b/erpnext/public/js/transaction.js
@@ -20,16 +20,13 @@
currency: currency,
price_list_currency: currency,
status: "Draft",
- fiscal_year: frappe.defaults.get_user_default("fiscal_year"),
is_subcontracted: "No",
}, function(fieldname, value) {
if(me.frm.fields_dict[fieldname] && !me.frm.doc[fieldname])
me.frm.set_value(fieldname, value);
});
- if(!this.frm.doc.company) {
- this.frm.set_value("company", frappe.defaults.get_user_default("company"));
- } else {
+ if(this.frm.doc.company) {
cur_frm.script_manager.trigger("company");
}
}
@@ -332,7 +329,7 @@
method: "erpnext.accounts.doctype.pricing_rule.pricing_rule.apply_pricing_rule",
args: { args: this._get_args(item) },
callback: function(r) {
- if (!r.exc) {
+ if (!r.exc && r.message) {
me._set_values_for_item_list(r.message);
if(calculate_taxes_and_totals) me.calculate_taxes_and_totals();
}
diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js
index e840758..9a4a356 100644
--- a/erpnext/selling/doctype/customer/customer.js
+++ b/erpnext/selling/doctype/customer/customer.js
@@ -86,7 +86,8 @@
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.docname+"' and docstatus != 2 order by is_primary_address desc"
+ 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("'", "\\'") + "' and docstatus != 2 order by is_primary_address desc"
},
as_dict: 1,
no_results_message: __('No addresses created'),
@@ -104,7 +105,8 @@
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.docname+"' and docstatus != 2 order by is_primary_contact desc"
+ 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("'", "\\'") + "' and docstatus != 2 order by is_primary_contact desc"
},
as_dict: 1,
no_results_message: __('No contacts created'),
diff --git a/erpnext/selling/doctype/installation_note/installation_note.py b/erpnext/selling/doctype/installation_note/installation_note.py
index 7d2d3d0..0477abc 100644
--- a/erpnext/selling/doctype/installation_note/installation_note.py
+++ b/erpnext/selling/doctype/installation_note/installation_note.py
@@ -28,7 +28,8 @@
'source_field': 'qty',
'percent_join_field': 'prevdoc_docname',
'status_field': 'installation_status',
- 'keyword': 'Installed'
+ 'keyword': 'Installed',
+ 'overflow_type': 'installation'
}]
def validate(self):
diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json
index ebe1d84..bb52113 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.json
+++ b/erpnext/selling/doctype/sales_order/sales_order.json
@@ -420,7 +420,7 @@
"label": "Taxes and Charges Calculation",
"oldfieldtype": "HTML",
"permlevel": 0,
- "print_hide": 0
+ "print_hide": 1
},
{
"fieldname": "section_break_43",
@@ -883,7 +883,7 @@
"idx": 1,
"is_submittable": 1,
"issingle": 0,
- "modified": "2014-07-07 17:47:40.089520",
+ "modified": "2014-07-10 02:43:45.504009",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order",
diff --git a/erpnext/selling/report/customer_addresses_and_contacts/customer_addresses_and_contacts.json b/erpnext/selling/report/customer_addresses_and_contacts/customer_addresses_and_contacts.json
index 9bde272..deb90b7 100644
--- a/erpnext/selling/report/customer_addresses_and_contacts/customer_addresses_and_contacts.json
+++ b/erpnext/selling/report/customer_addresses_and_contacts/customer_addresses_and_contacts.json
@@ -1,17 +1,17 @@
{
- "apply_user_permissions": 1,
- "creation": "2012-10-04 18:45:27",
- "docstatus": 0,
- "doctype": "Report",
- "idx": 1,
- "is_standard": "Yes",
- "modified": "2014-06-03 07:18:17.006732",
- "modified_by": "Administrator",
- "module": "Selling",
- "name": "Customer Addresses And Contacts",
- "owner": "Administrator",
- "query": "SELECT\n\t`tabCustomer`.name as customer_id,\n\t`tabCustomer`.customer_name,\n\t`tabCustomer`.customer_group,\n\t`tabAddress`.address_line1,\n\t`tabAddress`.address_line2,\n\t`tabAddress`.city,\n\t`tabAddress`.state,\n\t`tabAddress`.pincode,\n\t`tabAddress`.country,\n\t`tabAddress`.is_primary_address, \n\t`tabContact`.first_name,\n\t`tabContact`.last_name,\n\t`tabContact`.phone,\n\t`tabContact`.mobile_no,\n\t`tabContact`.email_id,\n\t`tabContact`.is_primary_contact\nFROM\n\t`tabCustomer`\n\tleft join `tabAddress` on (\n\t\t`tabAddress`.customer=`tabCustomer`.name\n\t)\n\tleft join `tabContact` on (\n\t\t`tabContact`.customer=`tabCustomer`.name\n\t)\nWHERE\n\t`tabCustomer`.docstatus<2\nORDER BY\n\t`tabCustomer`.name asc",
- "ref_doctype": "Customer",
- "report_name": "Customer Addresses And Contacts",
+ "apply_user_permissions": 1,
+ "creation": "2012-10-04 18:45:27",
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 1,
+ "is_standard": "Yes",
+ "modified": "2014-07-14 07:18:17.006732",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Customer Addresses and Contacts",
+ "owner": "Administrator",
+ "query": "SELECT\n\t`tabCustomer`.name as customer_id,\n\t`tabCustomer`.customer_name,\n\t`tabCustomer`.customer_group,\n\t`tabAddress`.address_line1,\n\t`tabAddress`.address_line2,\n\t`tabAddress`.city,\n\t`tabAddress`.state,\n\t`tabAddress`.pincode,\n\t`tabAddress`.country,\n\t`tabAddress`.is_primary_address, \n\t`tabContact`.first_name,\n\t`tabContact`.last_name,\n\t`tabContact`.phone,\n\t`tabContact`.mobile_no,\n\t`tabContact`.email_id,\n\t`tabContact`.is_primary_contact\nFROM\n\t`tabCustomer`\n\tleft join `tabAddress` on (\n\t\t`tabAddress`.customer=`tabCustomer`.name\n\t)\n\tleft join `tabContact` on (\n\t\t`tabContact`.customer=`tabCustomer`.name\n\t)\nWHERE\n\t`tabCustomer`.docstatus<2\nORDER BY\n\t`tabCustomer`.name asc",
+ "ref_doctype": "Customer",
+ "report_name": "Customer Addresses And Contacts",
"report_type": "Query Report"
-}
\ No newline at end of file
+}
diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json
index 51a1ac5..4209e7d 100644
--- a/erpnext/setup/doctype/company/company.json
+++ b/erpnext/setup/doctype/company/company.json
@@ -54,6 +54,15 @@
"reqd": 0
},
{
+ "fieldname": "country",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Country",
+ "options": "Country",
+ "permlevel": 0,
+ "reqd": 1
+ },
+ {
"fieldname": "charts_section",
"fieldtype": "Section Break",
"hidden": 1,
@@ -61,17 +70,9 @@
"permlevel": 0
},
{
- "fieldname": "country",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Country",
- "options": "Country",
- "permlevel": 0,
- "reqd": 0
- },
- {
"fieldname": "chart_of_accounts",
"fieldtype": "Link",
+ "hidden": 0,
"ignore_user_permissions": 1,
"label": "Chart of Accounts",
"options": "Chart of Accounts",
@@ -348,7 +349,7 @@
],
"icon": "icon-building",
"idx": 1,
- "modified": "2014-05-27 03:49:08.597191",
+ "modified": "2014-07-17 19:30:24.487672",
"modified_by": "Administrator",
"module": "Setup",
"name": "Company",
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index d24c7e7..f8e043a 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -241,7 +241,7 @@
[_('Sales'),_('Direct Income'),'Ledger','Income Account','Profit and Loss', None, 'Income'],
[_('Service'),_('Direct Income'),'Ledger','Income Account','Profit and Loss', None, 'Income'],
[_('Indirect Income'),_('Income'),'Group','Income Account','Profit and Loss', None, 'Income'],
- [_('Source of Funds (Liabilities)'), None,'Group', None,'Balance Sheet', None, 'Income'],
+ [_('Source of Funds (Liabilities)'), None,'Group', None,'Balance Sheet', None, 'Liability'],
[_('Capital Account'),_('Source of Funds (Liabilities)'),'Group', None,'Balance Sheet', None, 'Liability'],
[_('Reserves and Surplus'),_('Capital Account'),'Ledger', None,'Balance Sheet', None, 'Liability'],
[_('Shareholders Funds'),_('Capital Account'),'Ledger', None,'Balance Sheet', None, 'Liability'],
diff --git a/erpnext/setup/doctype/company/fixtures/india/__init__.py b/erpnext/setup/doctype/company/fixtures/india/__init__.py
index fa45ab0..d52b304 100644
--- a/erpnext/setup/doctype/company/fixtures/india/__init__.py
+++ b/erpnext/setup/doctype/company/fixtures/india/__init__.py
@@ -4,6 +4,7 @@
from __future__ import unicode_literals
import frappe
+from frappe import _
def install(company):
docs = [
@@ -27,44 +28,45 @@
'group_or_ledger': 2,
'account_type': 3,
'report_type': 4,
- 'tax_rate': 5
+ 'tax_rate': 5,
+ 'root_type': 6
}
acc_list_india = [
- ['CENVAT Capital Goods','Tax Assets','Ledger','Chargeable','Balance Sheet', None],
- ['CENVAT','Tax Assets','Ledger','Chargeable','Balance Sheet', None],
- ['CENVAT Service Tax','Tax Assets','Ledger','Chargeable','Balance Sheet', None],
- ['CENVAT Service Tax Cess 1','Tax Assets','Ledger','Chargeable','Balance Sheet', None],
- ['CENVAT Service Tax Cess 2','Tax Assets','Ledger','Chargeable','Balance Sheet', None],
- ['CENVAT Edu Cess','Tax Assets','Ledger','Chargeable','Balance Sheet', None],
- ['CENVAT SHE Cess','Tax Assets','Ledger','Chargeable','Balance Sheet', None],
- ['Excise Duty 4','Tax Assets','Ledger','Tax','Balance Sheet','4.00'],
- ['Excise Duty 8','Tax Assets','Ledger','Tax','Balance Sheet','8.00'],
- ['Excise Duty 10','Tax Assets','Ledger','Tax','Balance Sheet','10.00'],
- ['Excise Duty 14','Tax Assets','Ledger','Tax','Balance Sheet','14.00'],
- ['Excise Duty Edu Cess 2','Tax Assets','Ledger','Tax','Balance Sheet','2.00'],
- ['Excise Duty SHE Cess 1','Tax Assets','Ledger','Tax','Balance Sheet','1.00'],
- ['P L A','Tax Assets','Ledger','Chargeable','Balance Sheet', None],
- ['P L A - Cess Portion','Tax Assets','Ledger','Chargeable','Balance Sheet', None],
- ['Edu. Cess on Excise','Duties and Taxes','Ledger','Tax','Balance Sheet','2.00'],
- ['Edu. Cess on Service Tax','Duties and Taxes','Ledger','Tax','Balance Sheet','2.00'],
- ['Edu. Cess on TDS','Duties and Taxes','Ledger','Tax','Balance Sheet','2.00'],
- ['Excise Duty @ 4','Duties and Taxes','Ledger','Tax','Balance Sheet','4.00'],
- ['Excise Duty @ 8','Duties and Taxes','Ledger','Tax','Balance Sheet','8.00'],
- ['Excise Duty @ 10','Duties and Taxes','Ledger','Tax','Balance Sheet','10.00'],
- ['Excise Duty @ 14','Duties and Taxes','Ledger','Tax','Balance Sheet','14.00'],
- ['Service Tax','Duties and Taxes','Ledger','Tax','Balance Sheet','10.3'],
- ['SHE Cess on Excise','Duties and Taxes','Ledger','Tax','Balance Sheet','1.00'],
- ['SHE Cess on Service Tax','Duties and Taxes','Ledger','Tax','Balance Sheet','1.00'],
- ['SHE Cess on TDS','Duties and Taxes','Ledger','Tax','Balance Sheet','1.00'],
- ['Professional Tax','Duties and Taxes','Ledger','Chargeable','Balance Sheet', None],
- ['VAT','Duties and Taxes','Ledger','Chargeable','Balance Sheet', None],
- ['TDS (Advertisement)','Duties and Taxes','Ledger','Chargeable','Balance Sheet', None],
- ['TDS (Commission)','Duties and Taxes','Ledger','Chargeable','Balance Sheet', None],
- ['TDS (Contractor)','Duties and Taxes','Ledger','Chargeable','Balance Sheet', None],
- ['TDS (Interest)','Duties and Taxes','Ledger','Chargeable','Balance Sheet', None],
- ['TDS (Rent)','Duties and Taxes','Ledger','Chargeable','Balance Sheet', None],
- ['TDS (Salary)','Duties and Taxes','Ledger','Chargeable','Balance Sheet', None]
+ [_('CENVAT Capital Goods'),_(_('Tax Assets')),'Ledger','Chargeable','Balance Sheet', None, 'Asset'],
+ [_('CENVAT'),_('Tax Assets'),'Ledger','Chargeable','Balance Sheet', None, 'Asset'],
+ [_('CENVAT Service Tax'),_('Tax Assets'),'Ledger','Chargeable','Balance Sheet', None, 'Asset'],
+ [_('CENVAT Service Tax Cess 1'),_('Tax Assets'),'Ledger','Chargeable','Balance Sheet', None, 'Asset'],
+ [_('CENVAT Service Tax Cess 2'),_('Tax Assets'),'Ledger','Chargeable','Balance Sheet', None, 'Asset'],
+ [_('CENVAT Edu Cess'),_('Tax Assets'),'Ledger','Chargeable','Balance Sheet', None, 'Asset'],
+ [_('CENVAT SHE Cess'),_('Tax Assets'),'Ledger','Chargeable','Balance Sheet', None, 'Asset'],
+ [_('Excise Duty 4'),_('Tax Assets'),'Ledger','Tax','Balance Sheet','4.00', 'Asset'],
+ [_('Excise Duty 8'),_('Tax Assets'),'Ledger','Tax','Balance Sheet','8.00', 'Asset'],
+ [_('Excise Duty 10'),_('Tax Assets'),'Ledger','Tax','Balance Sheet','10.00', 'Asset'],
+ [_('Excise Duty 14'),_('Tax Assets'),'Ledger','Tax','Balance Sheet','14.00', 'Asset'],
+ [_('Excise Duty Edu Cess 2'),_('Tax Assets'),'Ledger','Tax','Balance Sheet','2.00', 'Asset'],
+ [_('Excise Duty SHE Cess 1'),_('Tax Assets'),'Ledger','Tax','Balance Sheet','1.00', 'Asset'],
+ [_('P L A'),_('Tax Assets'),'Ledger','Chargeable','Balance Sheet', None, 'Asset'],
+ [_('P L A - Cess Portion'),_('Tax Assets'),'Ledger','Chargeable','Balance Sheet', None, 'Asset'],
+ [_('Edu. Cess on Excise'),_('Duties and Taxes'),'Ledger','Tax','Balance Sheet','2.00', 'Liability'],
+ [_('Edu. Cess on Service Tax'),_('Duties and Taxes'),'Ledger','Tax','Balance Sheet','2.00', 'Liability'],
+ [_('Edu. Cess on TDS'),_('Duties and Taxes'),'Ledger','Tax','Balance Sheet','2.00', 'Liability'],
+ [_('Excise Duty @ 4'),_('Duties and Taxes'),'Ledger','Tax','Balance Sheet','4.00', 'Liability'],
+ [_('Excise Duty @ 8'),_('Duties and Taxes'),'Ledger','Tax','Balance Sheet','8.00', 'Liability'],
+ [_('Excise Duty @ 10'),_('Duties and Taxes'),'Ledger','Tax','Balance Sheet','10.00', 'Liability'],
+ [_('Excise Duty @ 14'),_('Duties and Taxes'),'Ledger','Tax','Balance Sheet','14.00', 'Liability'],
+ [_('Service Tax'),_('Duties and Taxes'),'Ledger','Tax','Balance Sheet','10.3', 'Liability'],
+ [_('SHE Cess on Excise'),_('Duties and Taxes'),'Ledger','Tax','Balance Sheet','1.00', 'Liability'],
+ [_('SHE Cess on Service Tax'),_('Duties and Taxes'),'Ledger','Tax','Balance Sheet','1.00', 'Liability'],
+ [_('SHE Cess on TDS'),_('Duties and Taxes'),'Ledger','Tax','Balance Sheet','1.00', 'Liability'],
+ [_('Professional Tax'),_('Duties and Taxes'),'Ledger','Chargeable','Balance Sheet', None, 'Liability'],
+ [_('VAT'),_('Duties and Taxes'),'Ledger','Chargeable','Balance Sheet', None, 'Liability'],
+ [_('TDS (Advertisement)'),_('Duties and Taxes'),'Ledger','Chargeable','Balance Sheet', None, 'Liability'],
+ [_('TDS (Commission)'),_('Duties and Taxes'),'Ledger','Chargeable','Balance Sheet', None, 'Liability'],
+ [_('TDS (Contractor)'),_('Duties and Taxes'),'Ledger','Chargeable','Balance Sheet', None, 'Liability'],
+ [_('TDS (Interest)'),_('Duties and Taxes'),'Ledger','Chargeable','Balance Sheet', None, 'Liability'],
+ [_('TDS (Rent)'),_('Duties and Taxes'),'Ledger','Chargeable','Balance Sheet', None, 'Liability'],
+ [_('TDS (Salary)'),_('Duties and Taxes'),'Ledger','Chargeable','Balance Sheet', None, 'Liability']
]
for lst in acc_list_india:
diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py
index 745345e..c6f49a1 100644
--- a/erpnext/setup/doctype/item_group/item_group.py
+++ b/erpnext/setup/doctype/item_group/item_group.py
@@ -82,7 +82,7 @@
def get_item_for_list_in_html(context):
# add missing absolute link in files
# user may forget it during upload
- if context.get("website_image", "").startswith("files/"):
+ if (context.get("website_image") or "").startswith("files/"):
context["website_image"] = "/" + context["website_image"]
return frappe.get_template("templates/includes/product_in_grid.html").render(context)
diff --git a/erpnext/setup/doctype/sales_partner/sales_partner.js b/erpnext/setup/doctype/sales_partner/sales_partner.js
index 02af9f7..64c4e21 100644
--- a/erpnext/setup/doctype/sales_partner/sales_partner.js
+++ b/erpnext/setup/doctype/sales_partner/sales_partner.js
@@ -41,7 +41,8 @@
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.docname+"' and docstatus != 2 order by is_primary_address desc"
+ 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("'", "\\'") + "' and docstatus != 2 order by is_primary_address desc"
},
as_dict: 1,
no_results_message: __('No addresses created'),
@@ -64,7 +65,8 @@
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.docname+"' and docstatus != 2 order by is_primary_contact desc"
+ 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("'", "\\'") + "' and docstatus != 2 order by is_primary_contact desc"
},
as_dict: 1,
no_results_message: __('No contacts created'),
diff --git a/erpnext/setup/page/setup_wizard/setup_wizard.py b/erpnext/setup/page/setup_wizard/setup_wizard.py
index ca52efb..343cba5 100644
--- a/erpnext/setup/page/setup_wizard/setup_wizard.py
+++ b/erpnext/setup/page/setup_wizard/setup_wizard.py
@@ -113,9 +113,11 @@
last_name=%(last_name)s WHERE name=%(name)s""", args)
if args.get("attach_user"):
- filename, filetype, content = args.get("attach_user").split(",")
- fileurl = save_file(filename, content, "User", args.get("name"), decode=True).file_url
- frappe.db.set_value("User", args.get("name"), "user_image", fileurl)
+ attach_user = args.get("attach_user").split(",")
+ if len(attach_user)==3:
+ filename, filetype, content = attach_user
+ fileurl = save_file(filename, content, "User", args.get("name"), decode=True).file_url
+ frappe.db.set_value("User", args.get("name"), "user_image", fileurl)
add_all_roles_to(args.get("name"))
@@ -319,9 +321,11 @@
}).insert()
if args.get("item_img_" + str(i)):
- filename, filetype, content = args.get("item_img_" + str(i)).split(",")
- fileurl = save_file(filename, content, "Item", item, decode=True).file_url
- frappe.db.set_value("Item", item, "image", fileurl)
+ item_image = args.get("item_img_" + str(i)).split(",")
+ if len(item_image)==3:
+ filename, filetype, content = item_image
+ fileurl = save_file(filename, content, "Item", item, decode=True).file_url
+ frappe.db.set_value("Item", item, "image", fileurl)
def create_customers(args):
for i in xrange(1,6):
@@ -374,17 +378,21 @@
"is_default": 1
}).insert()
- filename, filetype, content = args.get("attach_letterhead").split(",")
- fileurl = save_file(filename, content, "Letter Head", _("Standard"), decode=True).file_url
- frappe.db.set_value("Letter Head", _("Standard"), "content", "<img src='%s' style='max-width: 100%%;'>" % fileurl)
+ attach_letterhead = args.get("attach_letterhead").split(",")
+ if len(attach_letterhead)==3:
+ filename, filetype, content = attach_letterhead
+ fileurl = save_file(filename, content, "Letter Head", _("Standard"), decode=True).file_url
+ frappe.db.set_value("Letter Head", _("Standard"), "content", "<img src='%s' style='max-width: 100%%;'>" % fileurl)
def create_logo(args):
if args.get("attach_logo"):
- filename, filetype, content = args.get("attach_logo").split(",")
- fileurl = save_file(filename, content, "Website Settings", "Website Settings",
- decode=True).file_url
- frappe.db.set_value("Website Settings", "Website Settings", "banner_html",
- "<img src='%s' style='max-width: 100%%;'>" % fileurl)
+ attach_logo = args.get("attach_logo").split(",")
+ if len(attach_logo)==3:
+ filename, filetype, content = attach_logo
+ fileurl = save_file(filename, content, "Website Settings", "Website Settings",
+ decode=True).file_url
+ frappe.db.set_value("Website Settings", "Website Settings", "banner_html",
+ "<img src='%s' style='max-width: 100%%;'>" % fileurl)
def add_all_roles_to(name):
user = frappe.get_doc("User", name)
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json
index f1dc5a3..9b43a71 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.json
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.json
@@ -427,6 +427,7 @@
"label": "Taxes and Charges Calculation",
"oldfieldtype": "HTML",
"permlevel": 0,
+ "print_hide": 1,
"read_only": 0
},
{
@@ -1008,7 +1009,7 @@
"idx": 1,
"in_create": 0,
"is_submittable": 1,
- "modified": "2014-06-23 07:55:47.859869",
+ "modified": "2014-07-10 02:45:47.673011",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Note",
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index c1ddf63..13da907 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -29,7 +29,8 @@
'source_field': 'qty',
'percent_join_field': 'against_sales_order',
'status_field': 'delivery_status',
- 'keyword': 'Delivered'
+ 'keyword': 'Delivered',
+ 'overflow_type': 'delivery'
}]
def onload(self):
diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js
index e53a92a..7a47765 100644
--- a/erpnext/stock/doctype/material_request/material_request.js
+++ b/erpnext/stock/doctype/material_request/material_request.js
@@ -11,8 +11,7 @@
this._super();
this.frm.set_query("item_code", this.frm.cscript.fname, function() {
return {
- query: "erpnext.controllers.queries.item_query",
- filters: {'is_stock_item': 'Yes'}
+ query: "erpnext.controllers.queries.item_query"
}
});
},
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 71c07eb..74f1198 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -27,6 +27,7 @@
'target_ref_field': 'qty',
'source_field': 'qty',
'percent_join_field': 'prevdoc_docname',
+ 'overflow_type': 'receipt'
}]
def onload(self):
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 1024198..7274ece 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -92,7 +92,7 @@
set_default_account: function() {
var me = this;
- if(cint(frappe.defaults.get_default("auto_accounting_for_stock"))) {
+ if(cint(frappe.defaults.get_default("auto_accounting_for_stock")) && this.frm.doc.company) {
var account_for = "stock_adjustment_account";
if (this.frm.doc.purpose == "Purchase Return")
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 586179d..7629c3c 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -204,7 +204,7 @@
if not self.posting_date or not self.posting_time:
frappe.throw(_("Posting date and posting time is mandatory"))
- allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock")
+ allow_negative_stock = cint(frappe.db.get_default("allow_negative_stock"))
for d in self.get('mtn_details'):
args = frappe._dict({
@@ -219,7 +219,8 @@
# get actual stock at source warehouse
d.actual_qty = get_previous_sle(args).get("qty_after_transaction") or 0
- if d.s_warehouse and not allow_negative_stock and d.actual_qty <= d.transfer_qty:
+ # validate qty during submit
+ if d.docstatus==1 and d.s_warehouse and not allow_negative_stock and d.actual_qty < d.transfer_qty:
frappe.throw(_("""Row {0}: Qty not avalable in warehouse {1} on {2} {3}.
Available Qty: {4}, Transfer Qty: {5}""").format(d.idx, d.s_warehouse,
self.posting_date, self.posting_time, d.actual_qty, d.transfer_qty))
@@ -675,9 +676,9 @@
batch_nos = None
args = {
- 'item_code': filters['item_code'],
- 's_warehouse': filters['s_warehouse'],
- 'posting_date': filters['posting_date'],
+ 'item_code': filters.get("item_code"),
+ 's_warehouse': filters.get('s_warehouse'),
+ 'posting_date': filters.get('posting_date'),
'txt': "%%%s%%" % txt,
'mcond':get_match_cond(doctype),
"start": start,
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index 260223d..0f6a33f 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -21,7 +21,7 @@
frappe.db.sql("""delete from `tabMaterial Request`""")
self._clear_stock_account_balance()
- frappe.db.set_value("Stock Settings", None, "auto_indent", True)
+ frappe.db.set_value("Stock Settings", None, "auto_indent", 1)
st1 = frappe.copy_doc(test_records[0])
st1.insert()
@@ -664,15 +664,17 @@
def test_serial_no_not_exists(self):
self._clear_stock_account_balance()
frappe.db.sql("delete from `tabSerial No` where name in ('ABCD', 'EFGH')")
+ make_serialized_item(target_warehouse="_Test Warehouse 1 - _TC")
se = frappe.copy_doc(test_records[0])
se.purpose = "Material Issue"
- se.get("mtn_details")[0].item_code = "_Test Serialized Item"
+ se.get("mtn_details")[0].item_code = "_Test Serialized Item With Series"
se.get("mtn_details")[0].qty = 2
se.get("mtn_details")[0].s_warehouse = "_Test Warehouse 1 - _TC"
se.get("mtn_details")[0].t_warehouse = None
se.get("mtn_details")[0].serial_no = "ABCD\nEFGH"
se.get("mtn_details")[0].transfer_qty = 2
se.insert()
+
self.assertRaises(SerialNoNotExistsError, se.submit)
def test_serial_duplicate(self):
@@ -699,8 +701,8 @@
return se, serial_nos
def test_serial_item_error(self):
- self._clear_stock_account_balance()
se, serial_nos = self.test_serial_by_series()
+ make_serialized_item("_Test Serialized Item", "ABCD\nEFGH")
se = frappe.copy_doc(test_records[0])
se.purpose = "Material Transfer"
@@ -735,6 +737,8 @@
def test_serial_warehouse_error(self):
self._clear_stock_account_balance()
+ make_serialized_item(target_warehouse="_Test Warehouse 1 - _TC")
+
t = make_serialized_item()
serial_nos = get_serial_nos(t.get("mtn_details")[0].serial_no)
@@ -818,11 +822,16 @@
self.assertRaises (StockFreezeError, se.submit)
frappe.db.set_value("Stock Settings", None, "stock_frozen_upto_days", 0)
-def make_serialized_item():
+def make_serialized_item(item_code=None, serial_no=None, target_warehouse=None):
se = frappe.copy_doc(test_records[0])
- se.get("mtn_details")[0].item_code = "_Test Serialized Item With Series"
+ se.get("mtn_details")[0].item_code = item_code or "_Test Serialized Item With Series"
+ se.get("mtn_details")[0].serial_no = serial_no
se.get("mtn_details")[0].qty = 2
se.get("mtn_details")[0].transfer_qty = 2
+
+ if target_warehouse:
+ se.get("mtn_details")[0].t_warehouse = target_warehouse
+
se.insert()
se.submit()
return se
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index f9a1d9f..82b396f 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -152,6 +152,7 @@
"min_order_qty": flt(item.min_order_qty) if args.parenttype == "Material Request" else "",
"conversion_factor": 1.0,
"qty": 1.0,
+ "stock_qty": 1.0,
"price_list_rate": 0.0,
"base_price_list_rate": 0.0,
"rate": 0.0,
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index 340e551..100d338 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -176,7 +176,6 @@
def reorder_item():
""" Reorder item if stock reaches reorder level"""
-
# if initial setup not completed, return
if not frappe.db.sql("select name from `tabFiscal Year` limit 1"):
return
@@ -185,46 +184,69 @@
frappe.local.auto_indent = cint(frappe.db.get_value('Stock Settings', None, 'auto_indent'))
if frappe.local.auto_indent:
- material_requests = {}
- bin_list = frappe.db.sql("""select item_code, warehouse, projected_qty
- from tabBin where ifnull(item_code, '') != '' and ifnull(warehouse, '') != ''
- and exists (select name from `tabItem`
- where `tabItem`.name = `tabBin`.item_code and
- is_stock_item='Yes' and (is_purchase_item='Yes' or is_sub_contracted_item='Yes') and
- (ifnull(end_of_life, '0000-00-00')='0000-00-00' or end_of_life > curdate()))""",
- as_dict=True)
+ _reorder_item()
- for bin in bin_list:
- #check if re-order is required
- item_reorder = frappe.db.get("Item Reorder",
- {"parent": bin.item_code, "warehouse": bin.warehouse})
- if item_reorder:
- reorder_level = item_reorder.warehouse_reorder_level
- reorder_qty = item_reorder.warehouse_reorder_qty
- material_request_type = item_reorder.material_request_type or "Purchase"
- else:
- reorder_level, reorder_qty = frappe.db.get_value("Item", bin.item_code,
- ["re_order_level", "re_order_qty"])
- material_request_type = "Purchase"
+def _reorder_item():
+ # {"Purchase": {"Company": [{"item_code": "", "warehouse": "", "reorder_qty": 0.0}]}, "Transfer": {...}}
+ material_requests = {"Purchase": {}, "Transfer": {}}
- if flt(reorder_level) and flt(bin.projected_qty) < flt(reorder_level):
- if flt(reorder_level) - flt(bin.projected_qty) > flt(reorder_qty):
- reorder_qty = flt(reorder_level) - flt(bin.projected_qty)
+ item_warehouse_projected_qty = get_item_warehouse_projected_qty()
+ warehouse_company = frappe._dict(frappe.db.sql("""select name, company from `tabWarehouse`"""))
+ default_company = (frappe.defaults.get_defaults().get("company") or
+ frappe.db.sql("""select name from tabCompany limit 1""")[0][0])
- company = frappe.db.get_value("Warehouse", bin.warehouse, "company") or \
- frappe.defaults.get_defaults()["company"] or \
- frappe.db.sql("""select name from tabCompany limit 1""")[0][0]
+ def add_to_material_request(item_code, warehouse, reorder_level, reorder_qty, material_request_type):
+ if warehouse not in item_warehouse_projected_qty[item_code]:
+ # likely a disabled warehouse or a warehouse where BIN does not exist
+ return
- material_requests.setdefault(material_request_type, frappe._dict()).setdefault(
- company, []).append(frappe._dict({
- "item_code": bin.item_code,
- "warehouse": bin.warehouse,
- "reorder_qty": reorder_qty
- })
- )
+ reorder_level = flt(reorder_level)
+ reorder_qty = flt(reorder_qty)
+ projected_qty = item_warehouse_projected_qty[item_code][warehouse]
- if material_requests:
- create_material_request(material_requests)
+ if reorder_level and projected_qty < reorder_level:
+ deficiency = reorder_level - projected_qty
+ if deficiency > reorder_qty:
+ reorder_qty = deficiency
+
+ company = warehouse_company.get(warehouse) or default_company
+
+ material_requests[material_request_type].setdefault(company, []).append({
+ "item_code": item_code,
+ "warehouse": warehouse,
+ "reorder_qty": reorder_qty
+ })
+
+ for item_code in item_warehouse_projected_qty:
+ item = frappe.get_doc("Item", item_code)
+ if item.get("item_reorder"):
+ for d in item.get("item_reorder"):
+ add_to_material_request(item_code, d.warehouse, d.warehouse_reorder_level,
+ d.warehouse_reorder_qty, d.material_request_type)
+
+ else:
+ # raise for default warehouse
+ add_to_material_request(item_code, item.default_warehouse, item.re_order_level, item.re_order_qty, "Purchase")
+
+ if material_requests:
+ create_material_request(material_requests)
+
+def get_item_warehouse_projected_qty():
+ item_warehouse_projected_qty = {}
+
+ for item_code, warehouse, projected_qty in frappe.db.sql("""select item_code, warehouse, projected_qty
+ from tabBin where ifnull(item_code, '') != '' and ifnull(warehouse, '') != ''
+ and exists (select name from `tabItem`
+ where `tabItem`.name = `tabBin`.item_code and
+ is_stock_item='Yes' and (is_purchase_item='Yes' or is_sub_contracted_item='Yes') and
+ (ifnull(end_of_life, '0000-00-00')='0000-00-00' or end_of_life > %s))
+ and exists (select name from `tabWarehouse`
+ where `tabWarehouse`.name = `tabBin`.warehouse
+ and ifnull(disabled, 0)=0)""", nowdate()):
+
+ item_warehouse_projected_qty.setdefault(item_code, {})[warehouse] = flt(projected_qty)
+
+ return item_warehouse_projected_qty
def create_material_request(material_requests):
""" Create indent on reaching reorder level """
@@ -263,6 +285,7 @@
})
for d in items:
+ d = frappe._dict(d)
item = frappe.get_doc("Item", d.item_code)
mr.append("indent_details", {
"doctype": "Material Request Item",
diff --git a/erpnext/support/doctype/support_ticket/support_ticket.py b/erpnext/support/doctype/support_ticket/support_ticket.py
index 9517ea4..4cdc20a 100644
--- a/erpnext/support/doctype/support_ticket/support_ticket.py
+++ b/erpnext/support/doctype/support_ticket/support_ticket.py
@@ -8,31 +8,31 @@
from frappe.utils import now, extract_email_id
class SupportTicket(TransactionBase):
-
+
def get_sender(self, comm):
return frappe.db.get_value('Support Email Settings',None,'support_email')
def get_subject(self, comm):
return '[' + self.name + '] ' + (comm.subject or 'No Subject Specified')
-
+
def get_content(self, comm):
signature = frappe.db.get_value('Support Email Settings',None,'support_signature')
content = comm.content
if signature:
content += '<p>' + signature + '</p>'
return content
-
+
def get_portal_page(self):
return "ticket"
-
+
def validate(self):
self.update_status()
self.set_lead_contact(self.raised_by)
-
+
if self.status == "Closed":
from frappe.widgets.form.assign_to import clear
clear(self.doctype, self.name)
-
+
def set_lead_contact(self, email_id):
import email.utils
email_id = email.utils.parseaddr(email_id)
@@ -41,8 +41,8 @@
self.lead = frappe.db.get_value("Lead", {"email_id": email_id})
if not self.contact:
self.contact = frappe.db.get_value("Contact", {"email_id": email_id})
-
- if not self.company:
+
+ if not self.company:
self.company = frappe.db.get_value("Lead", self.lead, "company") or \
frappe.db.get_default("company")
@@ -53,15 +53,16 @@
if self.status=="Closed" and status !="Closed":
self.resolution_date = now()
if self.status=="Open" and status !="Open":
- self.resolution_date = ""
+ # if no date, it should be set as None and not a blank string "", as per mysql strict config
+ self.resolution_date = None
@frappe.whitelist()
def set_status(name, status):
st = frappe.get_doc("Support Ticket", name)
st.status = status
st.save()
-
+
def auto_close_tickets():
- frappe.db.sql("""update `tabSupport Ticket` set status = 'Closed'
- where status = 'Replied'
+ frappe.db.sql("""update `tabSupport Ticket` set status = 'Closed'
+ where status = 'Replied'
and date_sub(curdate(),interval 15 Day) > modified""")
diff --git a/erpnext/utilities/doctype/note/note.json b/erpnext/utilities/doctype/note/note.json
index 2ee6d9a..6cf756c 100644
--- a/erpnext/utilities/doctype/note/note.json
+++ b/erpnext/utilities/doctype/note/note.json
@@ -12,7 +12,8 @@
"in_list_view": 1,
"label": "Title",
"permlevel": 0,
- "print_hide": 1
+ "print_hide": 1,
+ "reqd": 1
},
{
"description": "Help: To link to another record in the system, use \"#Form/Note/[Note Name]\" as the Link URL. (don't use \"http://\")",
@@ -48,7 +49,7 @@
],
"icon": "icon-file-text",
"idx": 1,
- "modified": "2014-05-27 03:49:13.934698",
+ "modified": "2014-07-09 12:54:11.897597",
"modified_by": "Administrator",
"module": "Utilities",
"name": "Note",
diff --git a/erpnext/utilities/repost_stock.py b/erpnext/utilities/repost_stock.py
index e4028b6..159825a 100644
--- a/erpnext/utilities/repost_stock.py
+++ b/erpnext/utilities/repost_stock.py
@@ -190,3 +190,22 @@
"posting_date": posting_date,
"posting_time": posting_time
})
+
+def reset_serial_no_status_and_warehouse(serial_nos=None):
+ if not serial_nos:
+ serial_nos = frappe.db.sql_list("""select name from `tabSerial No` where status != 'Not in Use'
+ and docstatus = 0""")
+ for serial_no in serial_nos:
+ try:
+ sr = frappe.get_doc("Serial No", serial_no)
+ last_sle = sr.get_last_sle()
+ if flt(last_sle.actual_qty) > 0:
+ sr.warehouse = last_sle.warehouse
+
+ sr.via_stock_ledger = True
+ sr.save()
+ except:
+ pass
+
+ frappe.db.sql("""update `tabSerial No` set warehouse='' where status in ('Delivered', 'Purchase Returned')""")
+
\ No newline at end of file