From 06e6a82d56faf45212fc293334360a1390f326d3 Mon Sep 17 00:00:00 2001 From: ashish Date: Wed, 25 Oct 2017 15:56:39 +0530 Subject: [PATCH 01/17] customer.json -- a checkbox field is added which gives user ability to bypass credit limit check at sales order level for a customer. test_customer.js : auto generated file as customer.json is modified --- .../selling/doctype/customer/customer.json | 32 ++++++++++++++++++- .../selling/doctype/customer/test_customer.js | 23 +++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 erpnext/selling/doctype/customer/test_customer.js diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index 52c6b6db86..f231b85c65 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -907,6 +907,36 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "bypass_credit_limit_check_at_sales_order", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Bypass credit limit check at Sales Order", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -1202,7 +1232,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-07-24 00:55:07.445783", + "modified": "2017-10-24 14:44:48.508334", "modified_by": "Administrator", "module": "Selling", "name": "Customer", diff --git a/erpnext/selling/doctype/customer/test_customer.js b/erpnext/selling/doctype/customer/test_customer.js new file mode 100644 index 0000000000..65b81af32c --- /dev/null +++ b/erpnext/selling/doctype/customer/test_customer.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Customer", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Customer + () => frappe.tests.make('Customer', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); From 8ad4efee25a3060b33f05fc82dc8ce1146602f75 Mon Sep 17 00:00:00 2001 From: ashish Date: Wed, 25 Oct 2017 16:07:21 +0530 Subject: [PATCH 02/17] sales_order.py -- Three changes are done (a) if bypass credit limit check is enabled we should not check customer credit on submit of sales order (b.1/b.2) There is provision to make delivery note and sales invoice from sales order. Since credit limit is bypassed at sales order level we need to check credit of customer on make of (b.1)delivery note or (b.2)sales invoice --- .../doctype/sales_order/sales_order.py | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index fa21a3d7ee..d68f09535f 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -196,7 +196,11 @@ class SalesOrder(SellingController): def check_credit_limit(self): from erpnext.selling.doctype.customer.customer import check_credit_limit - check_credit_limit(self.customer, self.company) + #PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com + # bypass credit limit check is set to true (1) at sales order level, then we need not to check credit limit and vise versa + bypass_credit_limit_check_at_sales_order = frappe.db.get_value("Customer", self.customer, "bypass_credit_limit_check_at_sales_order") + if bypass_credit_limit_check_at_sales_order == 0: + check_credit_limit(self.customer, self.company) def check_nextdoc_docstatus(self): # Checks Delivery Note @@ -347,15 +351,15 @@ class SalesOrder(SellingController): return items def on_recurring(self, reference_doc, subscription_doc): - self.set("delivery_date", get_next_schedule_date(reference_doc.delivery_date, - subscription_doc.frequency, cint(subscription_doc.repeat_on_day))) + self.set("delivery_date", get_next_schedule_date(reference_doc.delivery_date, subscription_doc.frequency, + cint(subscription_doc.repeat_on_day))) for d in self.get("items"): reference_delivery_date = frappe.db.get_value("Sales Order Item", {"parent": reference_doc.name, "item_code": d.item_code, "idx": d.idx}, "delivery_date") - d.set("delivery_date", get_next_schedule_date(reference_delivery_date, - subscription_doc.frequency, cint(subscription_doc.repeat_on_day))) + d.set("delivery_date", + get_next_schedule_date(reference_delivery_date, subscription_doc.frequency, cint(subscription_doc.repeat_on_day))) def get_list_context(context=None): from erpnext.controllers.website_list_for_contact import get_list_context @@ -461,6 +465,14 @@ def make_delivery_note(source_name, target_doc=None): target.po_no = ", ".join(list(set(target_po_no))) if len(target_po_no) > 1 else target_po_no[0] else: target.po_no = source.po_no + + #PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com + # Since the credit limit check is bypassed at sales order level, we need to check it at delivery note + bypass_credit_limit_check_at_sales_order = frappe.db.get_value("Customer", source.customer, "bypass_credit_limit_check_at_sales_order") + if bypass_credit_limit_check_at_sales_order == 1: + from erpnext.selling.doctype.customer.customer import check_credit_limit + check_credit_limit(source.customer, source.company) + target.ignore_pricing_rule = 1 target.run_method("set_missing_values") @@ -524,6 +536,13 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): target.run_method("set_missing_values") target.run_method("calculate_taxes_and_totals") + #PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com + # Since the credit limit check is bypassed at sales order level, we need to check it at sales invoice + bypass_credit_limit_check_at_sales_order = frappe.db.get_value("Customer", source.customer, "bypass_credit_limit_check_at_sales_order") + if bypass_credit_limit_check_at_sales_order == 1: + from erpnext.selling.doctype.customer.customer import check_credit_limit + check_credit_limit(source.customer, source.company) + # set company address target.update(get_company_address(target.company)) if target.company_address: From ff20182d03c8ee691107f453e46ba0d40aed57d1 Mon Sep 17 00:00:00 2001 From: ashish Date: Wed, 25 Oct 2017 16:11:58 +0530 Subject: [PATCH 03/17] sales_invoice.py -- Check credit limit when make delivery is done from sales invoice.We need to check the credit limit as there could be multiple partially paid sales order(note we are bypassing credit check at sales order) so we need to recheck credit balance of customer to avoid any delivery crossing credit limit from sales invoice --- .../doctype/sales_invoice/sales_invoice.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 1c4fe3d084..30fb71561a 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -70,7 +70,6 @@ class SalesInvoice(SellingController): self.clear_unallocated_advances("Sales Invoice Advance", "advances") self.add_remarks() self.validate_write_off_account() - self.validate_duplicate_offline_pos_entry() self.validate_account_for_change_amount() self.validate_fixed_asset() self.set_income_account_for_fixed_assets() @@ -463,12 +462,6 @@ class SalesInvoice(SellingController): if flt(self.write_off_amount) and not self.write_off_account: msgprint(_("Please enter Write Off Account"), raise_exception=1) - def validate_duplicate_offline_pos_entry(self): - if self.is_pos and self.offline_pos_name \ - and frappe.db.get_value('Sales Invoice', - {'offline_pos_name': self.offline_pos_name, 'docstatus': 1}): - frappe.throw(_("Duplicate offline pos sales invoice {0}").format(self.offline_pos_name)) - def validate_account_for_change_amount(self): if flt(self.change_amount) and not self.account_for_change_amount: msgprint(_("Please enter Account for Change Amount"), raise_exception=1) @@ -924,6 +917,13 @@ def make_delivery_note(source_name, target_doc=None): target.run_method("set_missing_values") target.run_method("calculate_taxes_and_totals") + #PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com + # Since the credit limit check is bypassed at sales order level, we need to check it at delivery note + bypass_credit_limit_check_at_sales_order = frappe.db.get_value("Customer", source.customer, "bypass_credit_limit_check_at_sales_order") + if bypass_credit_limit_check_at_sales_order == 1: + from erpnext.selling.doctype.customer.customer import check_credit_limit + check_credit_limit(source.customer, source.company) + def update_item(source_doc, target_doc, source_parent): target_doc.qty = flt(source_doc.qty) - flt(source_doc.delivered_qty) target_doc.stock_qty = target_doc.qty * flt(source_doc.conversion_factor) From fb364df091d643c268ca6ca2dd13d9402d2c4cf1 Mon Sep 17 00:00:00 2001 From: ashish Date: Wed, 25 Oct 2017 16:17:55 +0530 Subject: [PATCH 04/17] customer_credit_balance.py -- Credit limit should now reflect outstanding amount for each customer based on value of flage bypass credit limit check at sales order level. A new column is added to show the value of this flag. customer.py -- get_credit_limit function which gets called by the above customer_credit_balance report is updated to deduct the amount of sales order for which credit limit bypass flag is set --- erpnext/selling/doctype/customer/customer.py | 22 ++++++++++++++++--- .../customer_credit_balance.py | 18 +++++++++------ 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 64cd190d21..648e129b0d 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -170,7 +170,9 @@ def check_credit_limit(customer, company): throw(_("Please contact to the user who have Sales Master Manager {0} role") .format(" / " + credit_controller if credit_controller else "")) -def get_customer_outstanding(customer, company): +#PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com +# get_customer_outstanding is a very generic function which is invoked from many places. A third non mandatory argument is added to change its behaviour based on caller . +def get_customer_outstanding(customer, company,caller=None): # Outstanding based on GL Entries outstanding_based_on_gle = frappe.db.sql("""select sum(debit) - sum(credit) from `tabGL Entry` where party_type = 'Customer' and party = %s and company=%s""", (customer, company)) @@ -186,6 +188,20 @@ def get_customer_outstanding(customer, company): outstanding_based_on_so = flt(outstanding_based_on_so[0][0]) if outstanding_based_on_so else 0.0 + #PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com + # Since the credit limit check is bypassed at sales order level, when customer credit balance report is run we need to treat sales order with status 'To Deliver and Bill' as not outstanding + outstanding_based_on_bypassed_so = 0.0 + bypass_credit_limit_check_at_sales_order = frappe.db.get_value("Customer", customer, "bypass_credit_limit_check_at_sales_order") + if bypass_credit_limit_check_at_sales_order == 1 and caller=='customer_credit_balance_report': + outstanding_based_on_bypassed_so = frappe.db.sql(""" + select (sum(base_grand_total)) + from `tabSales Order` + where customer=%s and docstatus = 1 and company=%s + and advance_paid = 0 + and per_billed < 100 and status ='To Deliver and Bill'""", (customer, company)) + + outstanding_based_on_bypassed_so = flt(outstanding_based_on_bypassed_so[0][0]) if outstanding_based_on_bypassed_so else 0.0 + # Outstanding based on Delivery Note unmarked_delivery_note_items = frappe.db.sql("""select dn_item.name, dn_item.amount, dn.base_net_total, dn.base_grand_total @@ -207,8 +223,8 @@ def get_customer_outstanding(customer, company): if flt(dn_item.amount) > flt(si_amount) and dn_item.base_net_total: outstanding_based_on_dn += ((flt(dn_item.amount) - flt(si_amount)) \ / dn_item.base_net_total) * dn_item.base_grand_total - - return outstanding_based_on_gle + outstanding_based_on_so + outstanding_based_on_dn + #PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com. In return substract the bypassed SO values + return outstanding_based_on_gle + outstanding_based_on_so + outstanding_based_on_dn - outstanding_based_on_bypassed_so def get_credit_limit(customer, company): diff --git a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py index 9075c3fa9f..87f1ee738a 100644 --- a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py +++ b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py @@ -19,14 +19,15 @@ def execute(filters=None): for d in customer_list: row = [] - outstanding_amt = get_customer_outstanding(d.name, filters.get("company")) + #PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com. 3rd arg is added + outstanding_amt = get_customer_outstanding(d.name, filters.get("company"),'customer_credit_balance_report') credit_limit = get_credit_limit(d.name, filters.get("company")) bal = flt(credit_limit) - flt(outstanding_amt) - + #PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com. Value of new col is passed if customer_naming_type == "Naming Series": - row = [d.name, d.customer_name, credit_limit, outstanding_amt, bal] + row = [d.name, d.customer_name, d.bypass_credit_limit_check_at_sales_order, credit_limit, outstanding_amt, bal] else: - row = [d.name, credit_limit, outstanding_amt, bal] + row = [d.name, d.bypass_credit_limit_check_at_sales_order, credit_limit, outstanding_amt, bal] if credit_limit: data.append(row) @@ -35,7 +36,9 @@ def execute(filters=None): def get_columns(customer_naming_type): columns = [ - _("Customer") + ":Link/Customer:120", _("Credit Limit") + ":Currency:120", + #PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com + # New column is added to reflect status of bypass credit limit check flag of sales order + _("Customer") + ":Link/Customer:120",_("Bypass credit check at Sales Order ") + ":Check:30", _("Credit Limit") + ":Currency:120", _("Outstanding Amt") + ":Currency:100", _("Credit Balance") + ":Currency:120" ] @@ -49,6 +52,7 @@ def get_details(filters): if filters.get("customer"): conditions += " where name = %(customer)s" - - return frappe.db.sql("""select name, customer_name from `tabCustomer` %s""" + #PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com + # Return column bypass credit limit check at sales order level + return frappe.db.sql("""select name, customer_name,bypass_credit_limit_check_at_sales_order from `tabCustomer` %s""" % conditions, filters, as_dict=1) From 8fbf10f5dbaa36b3c3eb95115eb58083e33cfa33 Mon Sep 17 00:00:00 2001 From: ashish Date: Wed, 25 Oct 2017 16:20:50 +0530 Subject: [PATCH 05/17] New test case files added.(a)test_sales_order_with_bypass_credit_limit_check.js -- New feature: i.e. bypass credit limit check at sales order.(b)test_sales_order_without_bypass_credit_limit_check.js --Regression test case : the existing default flow is checked i.e. credit limit check happens at sales order level --- ...es_order_with_bypass_credit_limit_check.js | 54 +++++++++++++++++ ...order_without_bypass_credit_limit_check.js | 59 +++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 erpnext/selling/doctype/sales_order/tests/test_sales_order_with_bypass_credit_limit_check.js create mode 100644 erpnext/selling/doctype/sales_order/tests/test_sales_order_without_bypass_credit_limit_check.js diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_bypass_credit_limit_check.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_bypass_credit_limit_check.js new file mode 100644 index 0000000000..3ffb825717 --- /dev/null +++ b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_bypass_credit_limit_check.js @@ -0,0 +1,54 @@ +QUnit.module('Sales Order'); + +QUnit.test("test_sales_order_with_bypass_credit_limit_check", function(assert) { +//#PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com + assert.expect(2); + let done = assert.async(); + frappe.run_serially([ + () => frappe.new_doc('Customer'), + () => frappe.timeout(1), + () => frappe.click_link('Edit in full page'), + () => cur_frm.set_value("customer_name", "Test Customer 10"), + () => cur_frm.set_value("credit_limit", 100.00), + () => cur_frm.set_value("bypass_credit_limit_check_at_sales_order", 1), + // save form + () => cur_frm.save(), + () => frappe.timeout(1), + + () => frappe.new_doc('Item'), + () => frappe.timeout(1), + () => frappe.click_link('Edit in full page'), + () => cur_frm.set_value("item_code", "Test Product 10"), + () => cur_frm.set_value("item_group", "Products"), + () => cur_frm.set_value("standard_rate", 100), + // save form + () => cur_frm.save(), + () => frappe.timeout(1), + + () => { + return frappe.tests.make('Sales Order', [ + {customer: 'Test Customer 5'}, + {items: [ + [ + {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, + {'qty': 5}, + {'item_code': 'Test Product 10'}, + ] + ]} + + ]); + }, + () => cur_frm.save(), + () => frappe.tests.click_button('Submit'), + () => assert.equal("Confirm", cur_dialog.title,'confirmation for submit'), + () => frappe.tests.click_button('Yes'), + () => frappe.timeout(3), + () => { + + assert.ok(cur_frm.doc.status=="To Deliver and Bill", "It is submited. Credit limit is NOT checked for sales order"); + + + }, + () => done() + ]); +}); diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_without_bypass_credit_limit_check.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_without_bypass_credit_limit_check.js new file mode 100644 index 0000000000..ea15edc0e1 --- /dev/null +++ b/erpnext/selling/doctype/sales_order/tests/test_sales_order_without_bypass_credit_limit_check.js @@ -0,0 +1,59 @@ +QUnit.module('Sales Order'); + +QUnit.test("test_sales_order_without_bypass_credit_limit_check", function(assert) { +//#PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com + assert.expect(2); + let done = assert.async(); + frappe.run_serially([ + () => frappe.new_doc('Customer'), + () => frappe.timeout(1), + () => frappe.click_link('Edit in full page'), + () => cur_frm.set_value("customer_name", "Test Customer 11"), + () => cur_frm.set_value("credit_limit", 100.00), + () => cur_frm.set_value("bypass_credit_limit_check_at_sales_order", 0), + // save form + () => cur_frm.save(), + () => frappe.timeout(1), + + () => frappe.new_doc('Item'), + () => frappe.timeout(1), + () => frappe.click_link('Edit in full page'), + () => cur_frm.set_value("item_code", "Test Product 11"), + () => cur_frm.set_value("item_group", "Products"), + () => cur_frm.set_value("standard_rate", 100), + // save form + () => cur_frm.save(), + () => frappe.timeout(1), + + () => { + return frappe.tests.make('Sales Order', [ + {customer: 'Test Customer 11'}, + {items: [ + [ + {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, + {'qty': 5}, + {'item_code': 'Test Product 11'}, + ] + ]} + + ]); + }, + () => cur_frm.save(), + () => frappe.tests.click_button('Submit'), + () => assert.equal("Confirm", cur_dialog.title,'confirmation for submit'), + () => frappe.tests.click_button('Yes'), + () => frappe.timeout(3), + () => { + + if (cur_dialog.body.innerText.match(/^Credit limit has been crossed for customer.*$/)) + { + /*Match found */ + assert.ok(true, "Credit Limit crossed message received"); + } + + + }, + () => cur_dialog.cancel(), + () => done() + ]); +}); From 9dfc0f32ab2e3db4f44741f120d0ecde390b9d9b Mon Sep 17 00:00:00 2001 From: ashish Date: Thu, 26 Oct 2017 20:07:32 +0530 Subject: [PATCH 06/17] customer.json -- a checkbox field is added which gives user ability to bypass credit limit check at sales order level for a customer. It is default to 0. Also patch is added to update value of new field to 0 after migrate. test_customer.js -- It is auto generated file as customer.json is modified. And it is removed. --- erpnext/patches.txt | 1 + ...reditlimitcheckatsalesorder_in_customer.py | 10 ++++++++ .../selling/doctype/customer/customer.json | 3 ++- .../selling/doctype/customer/test_customer.js | 23 ------------------- 4 files changed, 13 insertions(+), 24 deletions(-) create mode 100644 erpnext/patches/v9_0/set_bypasscreditlimitcheckatsalesorder_in_customer.py delete mode 100644 erpnext/selling/doctype/customer/test_customer.js diff --git a/erpnext/patches.txt b/erpnext/patches.txt index fd7a1b4da6..41999a99b7 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -455,3 +455,4 @@ erpnext.patches.v9_0.add_healthcare_domain erpnext.patches.v9_0.set_variant_item_description erpnext.patches.v9_0.set_uoms_in_variant_field erpnext.patches.v9_0.copy_old_fees_field_data +erpnext.patches.v9_0.set_bypasscreditlimitcheckatsalesorder_in_customer diff --git a/erpnext/patches/v9_0/set_bypasscreditlimitcheckatsalesorder_in_customer.py b/erpnext/patches/v9_0/set_bypasscreditlimitcheckatsalesorder_in_customer.py new file mode 100644 index 0000000000..e94a5081f0 --- /dev/null +++ b/erpnext/patches/v9_0/set_bypasscreditlimitcheckatsalesorder_in_customer.py @@ -0,0 +1,10 @@ +import frappe + +def execute(): + frappe.reload_doctype("Customer") + + if "bypass_credit_limit_check_at_sales_order" in frappe.db.get_table_columns("Customer"): + frappe.db.sql(""" + update `tabCustomer` + set bypass_credit_limit_check_at_sales_order = 0 + where (bypass_credit_limit_check_at_sales_order is NULL)""") \ No newline at end of file diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index f231b85c65..24b1968d5f 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -913,6 +913,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "default": "0", "fieldname": "bypass_credit_limit_check_at_sales_order", "fieldtype": "Check", "hidden": 0, @@ -1232,7 +1233,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-10-24 14:44:48.508334", + "modified": "2017-10-26 16:21:18.028471", "modified_by": "Administrator", "module": "Selling", "name": "Customer", diff --git a/erpnext/selling/doctype/customer/test_customer.js b/erpnext/selling/doctype/customer/test_customer.js deleted file mode 100644 index 65b81af32c..0000000000 --- a/erpnext/selling/doctype/customer/test_customer.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Customer", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Customer - () => frappe.tests.make('Customer', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); From 751b05f1e878d516957b53a2a3307893f78a6d95 Mon Sep 17 00:00:00 2001 From: ashish Date: Thu, 26 Oct 2017 20:15:24 +0530 Subject: [PATCH 07/17] sales_order.py -- Three changes are done (a) if bypass credit limit check is enabled we should not check customer credit on submit of sales order (b.1/b.2) There is provision to make delivery note and sales invoice from sales order. Since credit limit is bypassed at sales order level we need to check credit of customer on make of (b.1)delivery note or (b.2)sales invoice. cint function is added. --- .../doctype/sales_order/sales_order.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index d68f09535f..69e91e13c2 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -198,7 +198,7 @@ class SalesOrder(SellingController): from erpnext.selling.doctype.customer.customer import check_credit_limit #PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com # bypass credit limit check is set to true (1) at sales order level, then we need not to check credit limit and vise versa - bypass_credit_limit_check_at_sales_order = frappe.db.get_value("Customer", self.customer, "bypass_credit_limit_check_at_sales_order") + bypass_credit_limit_check_at_sales_order = cint(frappe.db.get_value("Customer", self.customer, "bypass_credit_limit_check_at_sales_order")) if bypass_credit_limit_check_at_sales_order == 0: check_credit_limit(self.customer, self.company) @@ -351,15 +351,15 @@ class SalesOrder(SellingController): return items def on_recurring(self, reference_doc, subscription_doc): - self.set("delivery_date", get_next_schedule_date(reference_doc.delivery_date, subscription_doc.frequency, - cint(subscription_doc.repeat_on_day))) + self.set("delivery_date", get_next_schedule_date(reference_doc.delivery_date, + subscription_doc.frequency, cint(subscription_doc.repeat_on_day))) for d in self.get("items"): reference_delivery_date = frappe.db.get_value("Sales Order Item", {"parent": reference_doc.name, "item_code": d.item_code, "idx": d.idx}, "delivery_date") - d.set("delivery_date", - get_next_schedule_date(reference_delivery_date, subscription_doc.frequency, cint(subscription_doc.repeat_on_day))) + d.set("delivery_date", get_next_schedule_date(reference_delivery_date, + subscription_doc.frequency, cint(subscription_doc.repeat_on_day))) def get_list_context(context=None): from erpnext.controllers.website_list_for_contact import get_list_context @@ -465,15 +465,14 @@ def make_delivery_note(source_name, target_doc=None): target.po_no = ", ".join(list(set(target_po_no))) if len(target_po_no) > 1 else target_po_no[0] else: target.po_no = source.po_no - + #PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com # Since the credit limit check is bypassed at sales order level, we need to check it at delivery note - bypass_credit_limit_check_at_sales_order = frappe.db.get_value("Customer", source.customer, "bypass_credit_limit_check_at_sales_order") + bypass_credit_limit_check_at_sales_order = cint(frappe.db.get_value("Customer", source.customer, "bypass_credit_limit_check_at_sales_order")) if bypass_credit_limit_check_at_sales_order == 1: from erpnext.selling.doctype.customer.customer import check_credit_limit check_credit_limit(source.customer, source.company) - target.ignore_pricing_rule = 1 target.run_method("set_missing_values") target.run_method("calculate_taxes_and_totals") @@ -538,7 +537,7 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): #PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com # Since the credit limit check is bypassed at sales order level, we need to check it at sales invoice - bypass_credit_limit_check_at_sales_order = frappe.db.get_value("Customer", source.customer, "bypass_credit_limit_check_at_sales_order") + bypass_credit_limit_check_at_sales_order = cint(frappe.db.get_value("Customer", source.customer, "bypass_credit_limit_check_at_sales_order")) if bypass_credit_limit_check_at_sales_order == 1: from erpnext.selling.doctype.customer.customer import check_credit_limit check_credit_limit(source.customer, source.company) @@ -805,4 +804,4 @@ def get_default_bom_item(item_code): order_by='is_default desc') bom = bom[0].name if bom else None - return bom + return bom \ No newline at end of file From ca2250c4401f75f01eaef4e70009a42ca8029c9e Mon Sep 17 00:00:00 2001 From: ashish Date: Thu, 26 Oct 2017 20:22:34 +0530 Subject: [PATCH 08/17] sales_invoice.py -- Check credit limit when make delivery is done from sales invoice.We need to check the credit limit as there could be multiple partially paid sales order(note we are bypassing credit check at sales order) so we need to recheck credit balance of customer to avoid any delivery crossing credit limit from sales invoice.cint function is added. --- .../accounts/doctype/sales_invoice/sales_invoice.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 30fb71561a..e21b95af13 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -70,6 +70,7 @@ class SalesInvoice(SellingController): self.clear_unallocated_advances("Sales Invoice Advance", "advances") self.add_remarks() self.validate_write_off_account() + self.validate_duplicate_offline_pos_entry() self.validate_account_for_change_amount() self.validate_fixed_asset() self.set_income_account_for_fixed_assets() @@ -462,6 +463,12 @@ class SalesInvoice(SellingController): if flt(self.write_off_amount) and not self.write_off_account: msgprint(_("Please enter Write Off Account"), raise_exception=1) + def validate_duplicate_offline_pos_entry(self): + if self.is_pos and self.offline_pos_name \ + and frappe.db.get_value('Sales Invoice', + {'offline_pos_name': self.offline_pos_name, 'docstatus': 1}): + frappe.throw(_("Duplicate offline pos sales invoice {0}").format(self.offline_pos_name)) + def validate_account_for_change_amount(self): if flt(self.change_amount) and not self.account_for_change_amount: msgprint(_("Please enter Account for Change Amount"), raise_exception=1) @@ -919,7 +926,7 @@ def make_delivery_note(source_name, target_doc=None): #PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com # Since the credit limit check is bypassed at sales order level, we need to check it at delivery note - bypass_credit_limit_check_at_sales_order = frappe.db.get_value("Customer", source.customer, "bypass_credit_limit_check_at_sales_order") + bypass_credit_limit_check_at_sales_order = cint(frappe.db.get_value("Customer", source.customer, "bypass_credit_limit_check_at_sales_order")) if bypass_credit_limit_check_at_sales_order == 1: from erpnext.selling.doctype.customer.customer import check_credit_limit check_credit_limit(source.customer, source.company) @@ -974,4 +981,4 @@ def make_sales_return(source_name, target_doc=None): def set_account_for_mode_of_payment(self): for data in self.payments: if not data.account: - data.account = get_bank_cash_account(data.mode_of_payment, self.company).get("account") + data.account = get_bank_cash_account(data.mode_of_payment, self.company).get("account") \ No newline at end of file From b883e4ee0d5a1fdbb8c25fe95f7af6b76d07b099 Mon Sep 17 00:00:00 2001 From: ashish Date: Thu, 26 Oct 2017 20:32:08 +0530 Subject: [PATCH 09/17] customer.py -- get_credit_limit function which gets called by the above customer_credit_balance report is updated to deduct the amount of sales order for which credit limit bypass flag is set. cint function is added. --- erpnext/selling/doctype/customer/customer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 648e129b0d..5a0ddccf0e 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -191,7 +191,7 @@ def get_customer_outstanding(customer, company,caller=None): #PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com # Since the credit limit check is bypassed at sales order level, when customer credit balance report is run we need to treat sales order with status 'To Deliver and Bill' as not outstanding outstanding_based_on_bypassed_so = 0.0 - bypass_credit_limit_check_at_sales_order = frappe.db.get_value("Customer", customer, "bypass_credit_limit_check_at_sales_order") + bypass_credit_limit_check_at_sales_order = cint(frappe.db.get_value("Customer", customer, "bypass_credit_limit_check_at_sales_order")) if bypass_credit_limit_check_at_sales_order == 1 and caller=='customer_credit_balance_report': outstanding_based_on_bypassed_so = frappe.db.sql(""" select (sum(base_grand_total)) From bccdf74344fd29dd3f686ac237e1dbcb8ee1fb2f Mon Sep 17 00:00:00 2001 From: ashish Date: Thu, 16 Nov 2017 10:53:22 +0530 Subject: [PATCH 10/17] set default value of bypass_credit_limit flag removed as it is not required --- erpnext/patches.txt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index ac446fd427..9d055b373a 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -456,13 +456,9 @@ erpnext.patches.v9_0.add_healthcare_domain erpnext.patches.v9_0.set_variant_item_description erpnext.patches.v9_0.set_uoms_in_variant_field erpnext.patches.v9_0.copy_old_fees_field_data -<<<<<<< HEAD execute:frappe.delete_doc_if_exists("DocType", "Program Fee") erpnext.patches.v9_0.set_pos_profile_name erpnext.patches.v9_0.remove_non_existing_warehouse_from_stock_settings execute:frappe.delete_doc_if_exists("DocType", "Program Fee") erpnext.patches.v9_0.update_employee_loan_details -erpnext.patches.v9_2.delete_healthcare_domain_default_items -======= -erpnext.patches.v9_0.set_bypasscreditlimitcheckatsalesorder_in_customer ->>>>>>> b883e4ee0d5a1fdbb8c25fe95f7af6b76d07b099 +erpnext.patches.v9_2.delete_healthcare_domain_default_items \ No newline at end of file From 8146c7a3df5d5fa787f5ab0878b3e541a76db050 Mon Sep 17 00:00:00 2001 From: ashish Date: Thu, 16 Nov 2017 10:54:22 +0530 Subject: [PATCH 11/17] not required as default is set by default --- ...t_bypasscreditlimitcheckatsalesorder_in_customer.py | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 erpnext/patches/v9_0/set_bypasscreditlimitcheckatsalesorder_in_customer.py diff --git a/erpnext/patches/v9_0/set_bypasscreditlimitcheckatsalesorder_in_customer.py b/erpnext/patches/v9_0/set_bypasscreditlimitcheckatsalesorder_in_customer.py deleted file mode 100644 index e94a5081f0..0000000000 --- a/erpnext/patches/v9_0/set_bypasscreditlimitcheckatsalesorder_in_customer.py +++ /dev/null @@ -1,10 +0,0 @@ -import frappe - -def execute(): - frappe.reload_doctype("Customer") - - if "bypass_credit_limit_check_at_sales_order" in frappe.db.get_table_columns("Customer"): - frappe.db.sql(""" - update `tabCustomer` - set bypass_credit_limit_check_at_sales_order = 0 - where (bypass_credit_limit_check_at_sales_order is NULL)""") \ No newline at end of file From f67372cfb339abb49bc3421d49d71ad1873ecd20 Mon Sep 17 00:00:00 2001 From: ashish Date: Thu, 16 Nov 2017 10:58:25 +0530 Subject: [PATCH 12/17] removed logic from delivery note for checking bypass credit limit flag --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index b85c9a8b04..85d102209e 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -918,13 +918,6 @@ def make_delivery_note(source_name, target_doc=None): target.run_method("set_missing_values") target.run_method("calculate_taxes_and_totals") - #PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com - # Since the credit limit check is bypassed at sales order level, we need to check it at delivery note - bypass_credit_limit_check_at_sales_order = cint(frappe.db.get_value("Customer", source.customer, "bypass_credit_limit_check_at_sales_order")) - if bypass_credit_limit_check_at_sales_order == 1: - from erpnext.selling.doctype.customer.customer import check_credit_limit - check_credit_limit(source.customer, source.company) - def update_item(source_doc, target_doc, source_parent): target_doc.qty = flt(source_doc.qty) - flt(source_doc.delivered_qty) target_doc.stock_qty = target_doc.qty * flt(source_doc.conversion_factor) From a1feb32891262cfe43c1315b624e08b34bd6619f Mon Sep 17 00:00:00 2001 From: ashish Date: Thu, 16 Nov 2017 11:01:43 +0530 Subject: [PATCH 13/17] added json for bypass credit limit checkbox --- erpnext/selling/doctype/customer/customer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index 24b1968d5f..965c1b7071 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -1233,7 +1233,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-10-26 16:21:18.028471", + "modified": "2017-11-15 18:59:26.897370", "modified_by": "Administrator", "module": "Selling", "name": "Customer", From a20c8dbd5c93071c0316c46b0c79fa6d3cd50bb2 Mon Sep 17 00:00:00 2001 From: ashish Date: Thu, 16 Nov 2017 11:03:39 +0530 Subject: [PATCH 14/17] instead of caller as 3rd arg added a flag --- erpnext/selling/doctype/customer/customer.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 5a0ddccf0e..d53ea95d3d 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -170,9 +170,8 @@ def check_credit_limit(customer, company): throw(_("Please contact to the user who have Sales Master Manager {0} role") .format(" / " + credit_controller if credit_controller else "")) -#PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com # get_customer_outstanding is a very generic function which is invoked from many places. A third non mandatory argument is added to change its behaviour based on caller . -def get_customer_outstanding(customer, company,caller=None): +def get_customer_outstanding(customer, company, ignore_bypass_credit_limit_check_at_sales_order=None): # Outstanding based on GL Entries outstanding_based_on_gle = frappe.db.sql("""select sum(debit) - sum(credit) from `tabGL Entry` where party_type = 'Customer' and party = %s and company=%s""", (customer, company)) @@ -188,11 +187,10 @@ def get_customer_outstanding(customer, company,caller=None): outstanding_based_on_so = flt(outstanding_based_on_so[0][0]) if outstanding_based_on_so else 0.0 - #PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com # Since the credit limit check is bypassed at sales order level, when customer credit balance report is run we need to treat sales order with status 'To Deliver and Bill' as not outstanding outstanding_based_on_bypassed_so = 0.0 - bypass_credit_limit_check_at_sales_order = cint(frappe.db.get_value("Customer", customer, "bypass_credit_limit_check_at_sales_order")) - if bypass_credit_limit_check_at_sales_order == 1 and caller=='customer_credit_balance_report': + bypass_credit_limit_check_at_sales_order =cint(frappe.db.get_value("Customer", customer, "bypass_credit_limit_check_at_sales_order")) + if bypass_credit_limit_check_at_sales_order == 1 and ignore_bypass_credit_limit_check_at_sales_order==False: outstanding_based_on_bypassed_so = frappe.db.sql(""" select (sum(base_grand_total)) from `tabSales Order` @@ -223,7 +221,7 @@ def get_customer_outstanding(customer, company,caller=None): if flt(dn_item.amount) > flt(si_amount) and dn_item.base_net_total: outstanding_based_on_dn += ((flt(dn_item.amount) - flt(si_amount)) \ / dn_item.base_net_total) * dn_item.base_grand_total - #PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com. In return substract the bypassed SO values +#In return substract the bypassed SO values return outstanding_based_on_gle + outstanding_based_on_so + outstanding_based_on_dn - outstanding_based_on_bypassed_so @@ -239,4 +237,4 @@ def get_credit_limit(customer, company): if not credit_limit: credit_limit = frappe.db.get_value("Company", company, "credit_limit") - return flt(credit_limit) + return flt(credit_limit) \ No newline at end of file From 604eb9d689b9884735f1c5250adfa0c02271a797 Mon Sep 17 00:00:00 2001 From: ashish Date: Thu, 16 Nov 2017 11:05:59 +0530 Subject: [PATCH 15/17] added flag to function get_customer_outstanding and removed names in comments --- .../customer_credit_balance.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py index 87f1ee738a..194d12cb77 100644 --- a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py +++ b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py @@ -19,11 +19,11 @@ def execute(filters=None): for d in customer_list: row = [] - #PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com. 3rd arg is added - outstanding_amt = get_customer_outstanding(d.name, filters.get("company"),'customer_credit_balance_report') - credit_limit = get_credit_limit(d.name, filters.get("company")) + #3rd argument has flag + outstanding_amt = get_customer_outstanding(d.name, filters.get("company"),False) + credit_limit = get_credit_limit(d.name, filters.get("company")) bal = flt(credit_limit) - flt(outstanding_amt) - #PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com. Value of new col is passed + #Value of new col is passed if customer_naming_type == "Naming Series": row = [d.name, d.customer_name, d.bypass_credit_limit_check_at_sales_order, credit_limit, outstanding_amt, bal] else: @@ -36,9 +36,8 @@ def execute(filters=None): def get_columns(customer_naming_type): columns = [ - #PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com # New column is added to reflect status of bypass credit limit check flag of sales order - _("Customer") + ":Link/Customer:120",_("Bypass credit check at Sales Order ") + ":Check:30", _("Credit Limit") + ":Currency:120", + _("Customer") + ":Link/Customer:120", _("Bypass credit check at Sales Order ") + ":Check:30", _("Credit Limit") + ":Currency:120", _("Outstanding Amt") + ":Currency:100", _("Credit Balance") + ":Currency:120" ] @@ -52,7 +51,7 @@ def get_details(filters): if filters.get("customer"): conditions += " where name = %(customer)s" - #PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com + # Return column bypass credit limit check at sales order level - return frappe.db.sql("""select name, customer_name,bypass_credit_limit_check_at_sales_order from `tabCustomer` %s""" - % conditions, filters, as_dict=1) + return frappe.db.sql("""select name, customer_name, bypass_credit_limit_check_at_sales_order from `tabCustomer` %s""" + % conditions, filters, as_dict=1) \ No newline at end of file From f17ca7866a2bd7fd60dddf1be8dcc0f54b7b89cf Mon Sep 17 00:00:00 2001 From: ashish Date: Thu, 16 Nov 2017 11:08:14 +0530 Subject: [PATCH 16/17] removed names from comments --- erpnext/selling/doctype/sales_order/sales_order.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index cd6e8144eb..a87ccf6b0d 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -196,11 +196,10 @@ class SalesOrder(SellingController): def check_credit_limit(self): from erpnext.selling.doctype.customer.customer import check_credit_limit - #PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com # bypass credit limit check is set to true (1) at sales order level, then we need not to check credit limit and vise versa bypass_credit_limit_check_at_sales_order = cint(frappe.db.get_value("Customer", self.customer, "bypass_credit_limit_check_at_sales_order")) if bypass_credit_limit_check_at_sales_order == 0: - check_credit_limit(self.customer, self.company) + check_credit_limit(self.customer, self.company) def check_nextdoc_docstatus(self): # Checks Delivery Note @@ -467,9 +466,7 @@ def make_delivery_note(source_name, target_doc=None): target.po_no = ", ".join(list(set(target_po_no))) if len(target_po_no) > 1 else target_po_no[0] else: target.po_no = source.po_no - - #PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com - # Since the credit limit check is bypassed at sales order level, we need to check it at delivery note +# Since the credit limit check is bypassed at sales order level, we need to check it at delivery note bypass_credit_limit_check_at_sales_order = cint(frappe.db.get_value("Customer", source.customer, "bypass_credit_limit_check_at_sales_order")) if bypass_credit_limit_check_at_sales_order == 1: from erpnext.selling.doctype.customer.customer import check_credit_limit @@ -538,9 +535,7 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): target.flags.ignore_permissions = True target.run_method("set_missing_values") target.run_method("calculate_taxes_and_totals") - - #PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com - # Since the credit limit check is bypassed at sales order level, we need to check it at sales invoice + # Since the credit limit check is bypassed at sales order level, we need to check it at sales invoice bypass_credit_limit_check_at_sales_order = cint(frappe.db.get_value("Customer", source.customer, "bypass_credit_limit_check_at_sales_order")) if bypass_credit_limit_check_at_sales_order == 1: from erpnext.selling.doctype.customer.customer import check_credit_limit From 68768252ce6d5e52f022d29804855b189213bbcb Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 23 Nov 2017 17:48:59 +0530 Subject: [PATCH 17/17] Cleanup and fixes on bypass credit limit check on sales order --- erpnext/domains/retail.py | 3 +- .../selling/doctype/customer/customer.json | 6 +- erpnext/selling/doctype/customer/customer.py | 42 +++++------ .../doctype/sales_order/sales_order.py | 72 +++++++++++-------- .../customer_credit_balance.py | 28 +++++--- 5 files changed, 83 insertions(+), 68 deletions(-) diff --git a/erpnext/domains/retail.py b/erpnext/domains/retail.py index 1bfd65faf8..897c4d9e40 100644 --- a/erpnext/domains/retail.py +++ b/erpnext/domains/retail.py @@ -10,8 +10,7 @@ data = { 'ToDo' ], 'properties': [ - {'doctype': 'Item', 'fieldname': 'manufacturing', 'property': 'hidden', 'value': 1}, - {'doctype': 'Customer', 'fieldname': 'credit_limit_section', 'property': 'hidden', 'value': 1}, + {'doctype': 'Item', 'fieldname': 'manufacturing', 'property': 'hidden', 'value': 1} ], 'set_value': [ ['Stock Settings', None, 'show_barcode_field', 1] diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index f97cf6fe75..fd9d0a173f 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -797,7 +797,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Credit Limit", + "label": "Credit Limit and Payment Terms", "length": 0, "no_copy": 0, "permlevel": 0, @@ -810,7 +810,7 @@ "search_index": 0, "set_only_once": 0, "unique": 0, - "width": "50%" + "width": "" }, { "allow_bulk_edit": 0, @@ -1202,7 +1202,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-11-23 18:59:26.897370", + "modified": "2017-11-23 17:41:23.243421", "modified_by": "Administrator", "module": "Selling", "name": "Customer", diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index d53ea95d3d..a24f4a368e 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -170,37 +170,30 @@ def check_credit_limit(customer, company): throw(_("Please contact to the user who have Sales Master Manager {0} role") .format(" / " + credit_controller if credit_controller else "")) -# get_customer_outstanding is a very generic function which is invoked from many places. A third non mandatory argument is added to change its behaviour based on caller . -def get_customer_outstanding(customer, company, ignore_bypass_credit_limit_check_at_sales_order=None): +def get_customer_outstanding(customer, company, ignore_outstanding_sales_order=False): # Outstanding based on GL Entries - outstanding_based_on_gle = frappe.db.sql("""select sum(debit) - sum(credit) - from `tabGL Entry` where party_type = 'Customer' and party = %s and company=%s""", (customer, company)) + outstanding_based_on_gle = frappe.db.sql(""" + select sum(debit) - sum(credit) + from `tabGL Entry` + where party_type = 'Customer' and party = %s and company=%s""", (customer, company)) outstanding_based_on_gle = flt(outstanding_based_on_gle[0][0]) if outstanding_based_on_gle else 0 # Outstanding based on Sales Order - outstanding_based_on_so = frappe.db.sql(""" - select sum(base_grand_total*(100 - per_billed)/100) - from `tabSales Order` - where customer=%s and docstatus = 1 and company=%s - and per_billed < 100 and status != 'Closed'""", (customer, company)) + outstanding_based_on_so = 0.0 - outstanding_based_on_so = flt(outstanding_based_on_so[0][0]) if outstanding_based_on_so else 0.0 - - # Since the credit limit check is bypassed at sales order level, when customer credit balance report is run we need to treat sales order with status 'To Deliver and Bill' as not outstanding - outstanding_based_on_bypassed_so = 0.0 - bypass_credit_limit_check_at_sales_order =cint(frappe.db.get_value("Customer", customer, "bypass_credit_limit_check_at_sales_order")) - if bypass_credit_limit_check_at_sales_order == 1 and ignore_bypass_credit_limit_check_at_sales_order==False: - outstanding_based_on_bypassed_so = frappe.db.sql(""" - select (sum(base_grand_total)) + # if credit limit check is bypassed at sales order level, + # we should not consider outstanding Sales Orders, when customer credit balance report is run + if not ignore_outstanding_sales_order: + outstanding_based_on_so = frappe.db.sql(""" + select sum(base_grand_total*(100 - per_billed)/100) from `tabSales Order` where customer=%s and docstatus = 1 and company=%s - and advance_paid = 0 - and per_billed < 100 and status ='To Deliver and Bill'""", (customer, company)) + and per_billed < 100 and status != 'Closed'""", (customer, company)) - outstanding_based_on_bypassed_so = flt(outstanding_based_on_bypassed_so[0][0]) if outstanding_based_on_bypassed_so else 0.0 + outstanding_based_on_so = flt(outstanding_based_on_so[0][0]) if outstanding_based_on_so else 0.0 - # Outstanding based on Delivery Note + # Outstanding based on Delivery Note, which are not created against Sales Order unmarked_delivery_note_items = frappe.db.sql("""select dn_item.name, dn_item.amount, dn.base_net_total, dn.base_grand_total from `tabDelivery Note` dn, `tabDelivery Note Item` dn_item @@ -221,15 +214,16 @@ def get_customer_outstanding(customer, company, ignore_bypass_credit_limit_check if flt(dn_item.amount) > flt(si_amount) and dn_item.base_net_total: outstanding_based_on_dn += ((flt(dn_item.amount) - flt(si_amount)) \ / dn_item.base_net_total) * dn_item.base_grand_total -#In return substract the bypassed SO values - return outstanding_based_on_gle + outstanding_based_on_so + outstanding_based_on_dn - outstanding_based_on_bypassed_so + + return outstanding_based_on_gle + outstanding_based_on_so + outstanding_based_on_dn def get_credit_limit(customer, company): credit_limit = None if customer: - credit_limit, customer_group = frappe.db.get_value("Customer", customer, ["credit_limit", "customer_group"]) + credit_limit, customer_group = frappe.db.get_value("Customer", + customer, ["credit_limit", "customer_group"]) if not credit_limit: credit_limit = frappe.db.get_value("Customer Group", customer_group, "credit_limit") diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 7425cbdc8f..c9e77336cd 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -14,6 +14,7 @@ from frappe.desk.notifications import clear_doctype_notifications from frappe.contacts.doctype.address.address import get_company_address from erpnext.controllers.selling_controller import SellingController from erpnext.accounts.doctype.subscription.subscription import get_next_schedule_date +from erpnext.selling.doctype.customer.customer import check_credit_limit form_grid_templates = { "items": "templates/form_grid/item_grid.html" @@ -188,52 +189,68 @@ class SalesOrder(SellingController): def update_project(self): project_list = [] if self.project: - project = frappe.get_doc("Project", self.project) - project.flags.dont_sync_tasks = True - project.update_sales_costing() - project.save() - project_list.append(self.project) + project = frappe.get_doc("Project", self.project) + project.flags.dont_sync_tasks = True + project.update_sales_costing() + project.save() + project_list.append(self.project) def check_credit_limit(self): - from erpnext.selling.doctype.customer.customer import check_credit_limit - # bypass credit limit check is set to true (1) at sales order level, then we need not to check credit limit and vise versa - bypass_credit_limit_check_at_sales_order = cint(frappe.db.get_value("Customer", self.customer, "bypass_credit_limit_check_at_sales_order")) - if bypass_credit_limit_check_at_sales_order == 0: - check_credit_limit(self.customer, self.company) + # if bypass credit limit check is set to true (1) at sales order level, + # then we need not to check credit limit and vise versa + if not cint(frappe.db.get_value("Customer", self.customer, "bypass_credit_limit_check_at_sales_order")): + check_credit_limit(self.customer, self.company) def check_nextdoc_docstatus(self): # Checks Delivery Note - submit_dn = frappe.db.sql_list("""select t1.name from `tabDelivery Note` t1,`tabDelivery Note Item` t2 + submit_dn = frappe.db.sql_list(""" + select t1.name + from `tabDelivery Note` t1,`tabDelivery Note Item` t2 where t1.name = t2.parent and t2.against_sales_order = %s and t1.docstatus = 1""", self.name) + if submit_dn: - frappe.throw(_("Delivery Notes {0} must be cancelled before cancelling this Sales Order").format(comma_and(submit_dn))) + frappe.throw(_("Delivery Notes {0} must be cancelled before cancelling this Sales Order") + .format(comma_and(submit_dn))) # Checks Sales Invoice submit_rv = frappe.db.sql_list("""select t1.name from `tabSales Invoice` t1,`tabSales Invoice Item` t2 where t1.name = t2.parent and t2.sales_order = %s and t1.docstatus = 1""", self.name) + if submit_rv: - frappe.throw(_("Sales Invoice {0} must be cancelled before cancelling this Sales Order").format(comma_and(submit_rv))) + frappe.throw(_("Sales Invoice {0} must be cancelled before cancelling this Sales Order") + .format(comma_and(submit_rv))) #check maintenance schedule - submit_ms = frappe.db.sql_list("""select t1.name from `tabMaintenance Schedule` t1, - `tabMaintenance Schedule Item` t2 + submit_ms = frappe.db.sql_list(""" + select t1.name + from `tabMaintenance Schedule` t1, `tabMaintenance Schedule Item` t2 where t2.parent=t1.name and t2.sales_order = %s and t1.docstatus = 1""", self.name) + if submit_ms: - frappe.throw(_("Maintenance Schedule {0} must be cancelled before cancelling this Sales Order").format(comma_and(submit_ms))) + frappe.throw(_("Maintenance Schedule {0} must be cancelled before cancelling this Sales Order") + .format(comma_and(submit_ms))) # check maintenance visit - submit_mv = frappe.db.sql_list("""select t1.name from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2 + submit_mv = frappe.db.sql_list(""" + select t1.name + from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2 where t2.parent=t1.name and t2.prevdoc_docname = %s and t1.docstatus = 1""",self.name) + if submit_mv: - frappe.throw(_("Maintenance Visit {0} must be cancelled before cancelling this Sales Order").format(comma_and(submit_mv))) + frappe.throw(_("Maintenance Visit {0} must be cancelled before cancelling this Sales Order") + .format(comma_and(submit_mv))) # check production order - pro_order = frappe.db.sql_list("""select name from `tabProduction Order` + pro_order = frappe.db.sql_list(""" + select name + from `tabProduction Order` where sales_order = %s and docstatus = 1""", self.name) + if pro_order: - frappe.throw(_("Production Order {0} must be cancelled before cancelling this Sales Order").format(comma_and(pro_order))) + frappe.throw(_("Production Order {0} must be cancelled before cancelling this Sales Order") + .format(comma_and(pro_order))) def check_modified_date(self): mod_db = frappe.db.get_value("Sales Order", self.name, "modified") @@ -466,10 +483,10 @@ def make_delivery_note(source_name, target_doc=None): target.po_no = ", ".join(list(set(target_po_no))) if len(target_po_no) > 1 else target_po_no[0] else: target.po_no = source.po_no -# Since the credit limit check is bypassed at sales order level, we need to check it at delivery note - bypass_credit_limit_check_at_sales_order = cint(frappe.db.get_value("Customer", source.customer, "bypass_credit_limit_check_at_sales_order")) - if bypass_credit_limit_check_at_sales_order == 1: - from erpnext.selling.doctype.customer.customer import check_credit_limit + + # Since the credit limit check is bypassed at sales order level, + # we need to check it at delivery note + if cint(frappe.db.get_value("Customer", source.customer, "bypass_credit_limit_check_at_sales_order")): check_credit_limit(source.customer, source.company) target.ignore_pricing_rule = 1 @@ -535,10 +552,9 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): target.flags.ignore_permissions = True target.run_method("set_missing_values") target.run_method("calculate_taxes_and_totals") - # Since the credit limit check is bypassed at sales order level, we need to check it at sales invoice - bypass_credit_limit_check_at_sales_order = cint(frappe.db.get_value("Customer", source.customer, "bypass_credit_limit_check_at_sales_order")) - if bypass_credit_limit_check_at_sales_order == 1: - from erpnext.selling.doctype.customer.customer import check_credit_limit + + # Since the credit limit check is bypassed at sales order level, we need to check it at sales invoice + if cint(frappe.db.get_value("Customer", source.customer, "bypass_credit_limit_check_at_sales_order")): check_credit_limit(source.customer, source.company) # set company address diff --git a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py index 194d12cb77..ffa418017c 100644 --- a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py +++ b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py @@ -19,15 +19,19 @@ def execute(filters=None): for d in customer_list: row = [] - #3rd argument has flag - outstanding_amt = get_customer_outstanding(d.name, filters.get("company"),False) + + outstanding_amt = get_customer_outstanding(d.name, filters.get("company"), + ignore_outstanding_sales_order=d.bypass_credit_limit_check_at_sales_order) + credit_limit = get_credit_limit(d.name, filters.get("company")) + bal = flt(credit_limit) - flt(outstanding_amt) - #Value of new col is passed + if customer_naming_type == "Naming Series": - row = [d.name, d.customer_name, d.bypass_credit_limit_check_at_sales_order, credit_limit, outstanding_amt, bal] + row = [d.name, d.customer_name, credit_limit, outstanding_amt, bal, + d.bypass_credit_limit_check_at_sales_order] else: - row = [d.name, d.bypass_credit_limit_check_at_sales_order, credit_limit, outstanding_amt, bal] + row = [d.name, credit_limit, outstanding_amt, bal, d.bypass_credit_limit_check_at_sales_order] if credit_limit: data.append(row) @@ -36,9 +40,11 @@ def execute(filters=None): def get_columns(customer_naming_type): columns = [ - # New column is added to reflect status of bypass credit limit check flag of sales order - _("Customer") + ":Link/Customer:120", _("Bypass credit check at Sales Order ") + ":Check:30", _("Credit Limit") + ":Currency:120", - _("Outstanding Amt") + ":Currency:100", _("Credit Balance") + ":Currency:120" + _("Customer") + ":Link/Customer:120", + _("Credit Limit") + ":Currency:120", + _("Outstanding Amt") + ":Currency:100", + _("Credit Balance") + ":Currency:120", + _("Bypass credit check at Sales Order ") + ":Check:240" ] if customer_naming_type == "Naming Series": @@ -52,6 +58,6 @@ def get_details(filters): if filters.get("customer"): conditions += " where name = %(customer)s" - # Return column bypass credit limit check at sales order level - return frappe.db.sql("""select name, customer_name, bypass_credit_limit_check_at_sales_order from `tabCustomer` %s""" - % conditions, filters, as_dict=1) \ No newline at end of file + return frappe.db.sql("""select name, customer_name, + bypass_credit_limit_check_at_sales_order from `tabCustomer` %s + """ % conditions, filters, as_dict=1) \ No newline at end of file