From 28d3525b42761f2f0d529d80dbc37f27a13586c1 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 15 Mar 2024 13:05:07 +0530 Subject: [PATCH 1/2] refactor: validate SO and SI references (cherry picked from commit 4d090bd3b83c25e18b3e44b1f7dd75dd38faba7f) --- .../doctype/delivery_note/delivery_note.py | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index a7fcd04c634b..4c2c023b6ae3 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -251,6 +251,7 @@ def so_required(self): def validate(self): self.validate_posting_time() super(DeliveryNote, self).validate() + self.validate_references() self.set_status() self.so_required() self.validate_proj_cust() @@ -341,6 +342,58 @@ def set_serial_and_batch_bundle_from_pick_list(self): item.serial_and_batch_bundle = cls_obj.serial_and_batch_bundle + def validate_references(self): + self.validate_sales_order_references() + self.validate_sales_invoice_references() + + def validate_sales_order_references(self): + err_msg = "" + for item in self.items: + if (item.against_sales_order and not item.so_detail) or ( + not item.against_sales_order and item.so_detail + ): + if not item.against_sales_order: + err_msg += ( + _("'Sales Order' reference ({1}) is missing in row {0}").format( + frappe.bold(item.idx), frappe.bold("against_sales_order") + ) + + "
" + ) + else: + err_msg += ( + _("'Sales Order Item' reference ({1}) is missing in row {0}").format( + frappe.bold(item.idx), frappe.bold("so_detail") + ) + + "
" + ) + + if err_msg: + frappe.throw(err_msg, title=_("References to Sales Orders are Incomplete")) + + def validate_sales_invoice_references(self): + err_msg = "" + for item in self.items: + if (item.against_sales_invoice and not item.si_detail) or ( + not item.against_sales_invoice and item.si_detail + ): + if not item.against_sales_invoice: + err_msg += ( + _("'Sales Invoice' reference ({1}) is missing in row {0}").format( + frappe.bold(item.idx), frappe.bold("against_sales_invoice") + ) + + "
" + ) + else: + err_msg += ( + _("'Sales Invoice Item' reference ({1}) is missing in row {0}").format( + frappe.bold(item.idx), frappe.bold("si_detail") + ) + + "
" + ) + + if err_msg: + frappe.throw(err_msg, title=_("References to Sales Invoices are Incomplete")) + def validate_proj_cust(self): """check for does customer belong to same project as entered..""" if self.project and self.customer: From 409a65ec64b86d1932c728194e1fb760f55593a2 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 16 Mar 2024 14:39:57 +0530 Subject: [PATCH 2/2] test: SO reference validation (cherry picked from commit 4f396d304979a23c3265c467112aa1dbc99932bd) --- erpnext/selling/doctype/sales_order/test_sales_order.py | 5 +++-- .../stock/doctype/delivery_note/test_delivery_note.py | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 261566ec001e..375accd65b68 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -2156,13 +2156,14 @@ def make_sales_order(**args): return so -def create_dn_against_so(so, delivered_qty=0): +def create_dn_against_so(so, delivered_qty=0, do_not_submit=False): frappe.db.set_single_value("Stock Settings", "allow_negative_stock", 1) dn = make_delivery_note(so) dn.get("items")[0].qty = delivered_qty or 5 dn.insert() - dn.submit() + if not do_not_submit: + dn.submit() return dn diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 808862949584..f9341393ad01 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -813,6 +813,15 @@ def test_closed_delivery_note(self): dn.cancel() self.assertEqual(dn.status, "Cancelled") + def test_sales_order_reference_validation(self): + so = make_sales_order(po_no="12345") + dn = create_dn_against_so(so.name, delivered_qty=2, do_not_submit=True) + dn.items[0].against_sales_order = None + self.assertRaises(frappe.ValidationError, dn.save) + dn.reload() + dn.items[0].so_detail = None + self.assertRaises(frappe.ValidationError, dn.save) + def test_dn_billing_status_case1(self): # SO -> DN -> SI so = make_sales_order(po_no="12345")