diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index dce625518c9446bab038570ac84296125aa006de..3648d6edd7d87cc5e27b380ff69dd2973f0d2fbd 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -518,6 +518,10 @@ class SalesOrder(SellingController): def update_status(self, status): self.check_modified_date() self.set_status(update=True, status=status) + # Upon Sales Order Re-open, check for credit limit. + # Limit should be checked after the 'Hold/Closed' status is reset. + if status == "Draft" and self.docstatus == 1: + self.check_credit_limit() self.update_reserved_qty() self.notify_update() clear_doctype_notifications(self) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 16e4a15a021a5523416d4bf256cea18303571a96..28afa8cb713dc3b0664cc465a7e503e64324284f 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -9,7 +9,8 @@ from frappe.core.doctype.user_permission.test_user_permission import create_user from frappe.tests.utils import FrappeTestCase, change_settings from frappe.utils import add_days, flt, getdate, nowdate, today -from erpnext.controllers.accounts_controller import update_child_qty_rate +from erpnext.accounts.test.accounts_mixin import AccountsTestMixin +from erpnext.controllers.accounts_controller import InvalidQtyError, update_child_qty_rate from erpnext.maintenance.doctype.maintenance_schedule.test_maintenance_schedule import ( make_maintenance_schedule, ) @@ -31,7 +32,7 @@ from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry -class TestSalesOrder(FrappeTestCase): +class TestSalesOrder(AccountsTestMixin, FrappeTestCase): @classmethod def setUpClass(cls): super().setUpClass() @@ -52,6 +53,9 @@ class TestSalesOrder(FrappeTestCase): super().tearDownClass() + def setUp(self): + self.create_customer("_Test Customer Credit") + def tearDown(self): frappe.set_user("Administrator") @@ -2124,6 +2128,28 @@ class TestSalesOrder(FrappeTestCase): frappe.db.set_single_value("Stock Settings", "update_existing_price_list_rate", 0) frappe.db.set_single_value("Stock Settings", "auto_insert_price_list_rate_if_missing", 0) + def test_credit_limit_on_so_reopning(self): + # set credit limit + company = "_Test Company" + customer = frappe.get_doc("Customer", self.customer) + customer.credit_limits = [] + customer.append( + "credit_limits", {"company": company, "credit_limit": 1000, "bypass_credit_limit_check": False} + ) + customer.save() + + so1 = make_sales_order(qty=9, rate=100, do_not_submit=True) + so1.customer = self.customer + so1.save().submit() + + so1.update_status("Closed") + + so2 = make_sales_order(qty=9, rate=100, do_not_submit=True) + so2.customer = self.customer + so2.save().submit() + + self.assertRaises(frappe.ValidationError, so1.update_status, "Draft") + def automatically_fetch_payment_terms(enable=1): accounts_settings = frappe.get_doc("Accounts Settings")