diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index e1ad2ab685a..a57a9f4a80b 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -39,7 +39,6 @@ from erpnext.controllers.selling_controller import SellingController from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timesheet_data from erpnext.setup.doctype.company.company import update_company_current_month_sales from erpnext.stock.doctype.delivery_note.delivery_note import update_billed_amount_based_on_so -from erpnext.stock.doctype.serial_no.serial_no import get_delivery_note_serial_no, get_serial_nos form_grid_templates = {"items": "templates/form_grid/item_grid.html"} @@ -1642,48 +1641,9 @@ class SalesInvoice(SellingController): """ validate serial number agains Delivery Note and Sales Invoice """ - self.set_serial_no_against_delivery_note() - self.validate_serial_against_delivery_note() - - def set_serial_no_against_delivery_note(self): for item in self.items: - if item.serial_no and item.delivery_note and item.qty != len(get_serial_nos(item.serial_no)): - item.serial_no = get_delivery_note_serial_no(item.item_code, item.qty, item.delivery_note) - - def validate_serial_against_delivery_note(self): - """ - validate if the serial numbers in Sales Invoice Items are same as in - Delivery Note Item - """ - - for item in self.items: - if not item.delivery_note or not item.dn_detail: - continue - - serial_nos = frappe.db.get_value("Delivery Note Item", item.dn_detail, "serial_no") or "" - dn_serial_nos = set(get_serial_nos(serial_nos)) - - serial_nos = item.serial_no or "" - si_serial_nos = set(get_serial_nos(serial_nos)) - serial_no_diff = si_serial_nos - dn_serial_nos - - if serial_no_diff: - dn_link = frappe.utils.get_link_to_form("Delivery Note", item.delivery_note) - serial_no_msg = ", ".join(frappe.bold(d) for d in serial_no_diff) - - msg = _("Row #{0}: The following Serial Nos are not present in Delivery Note {1}:").format( - item.idx, dn_link - ) - msg += " " + serial_no_msg - - frappe.throw(msg=msg, title=_("Serial Nos Mismatch")) - - if item.serial_no and cint(item.qty) != len(si_serial_nos): - frappe.throw( - _("Row #{0}: {1} Serial numbers required for Item {2}. You have provided {3}.").format( - item.idx, item.qty, item.item_code, len(si_serial_nos) - ) - ) + item.set_serial_no_against_delivery_note() + item.validate_serial_against_delivery_note() def update_project(self): if self.project: diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py index cf18529a652..cd235bf4aba 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py @@ -5,8 +5,10 @@ import frappe from frappe import _ from frappe.model.document import Document +from frappe.utils.data import cint from erpnext.assets.doctype.asset.depreciation import get_disposal_account_and_cost_center +from erpnext.stock.doctype.serial_no.serial_no import get_delivery_note_serial_no, get_serial_nos class SalesInvoiceItem(Document): @@ -124,3 +126,39 @@ class SalesInvoiceItem(Document): self.income_account = disposal_account if not self.cost_center: self.cost_center = depreciation_cost_center + + def set_serial_no_against_delivery_note(self): + """Set serial no based on delivery note.""" + if self.serial_no and self.delivery_note and self.qty != len(get_serial_nos(self.serial_no)): + self.serial_no = get_delivery_note_serial_no(self.item_code, self.qty, self.delivery_note) + + def validate_serial_against_delivery_note(self): + """Ensure the serial numbers in this Sales Invoice Item are same as in the linked Delivery Note.""" + if not self.delivery_note or not self.dn_detail: + return + + serial_nos = frappe.db.get_value("Delivery Note Item", self.dn_detail, "serial_no") or "" + dn_serial_nos = set(get_serial_nos(serial_nos)) + + serial_nos = self.serial_no or "" + si_serial_nos = set(get_serial_nos(serial_nos)) + serial_no_diff = si_serial_nos - dn_serial_nos + + if serial_no_diff: + dn_link = frappe.utils.get_link_to_form("Delivery Note", self.delivery_note) + msg = ( + _("Row #{0}: The following serial numbers are not present in Delivery Note {1}:").format( + self.idx, dn_link + ) + + " " + + ", ".join(frappe.bold(d) for d in serial_no_diff) + ) + + frappe.throw(msg=msg, title=_("Serial Nos Mismatch")) + + if self.serial_no and cint(self.qty) != len(si_serial_nos): + frappe.throw( + _( + "Row #{0}: {1} serial numbers are required for Item {2}. You have provided {3} serial numbers." + ).format(self.idx, self.qty, self.item_code, len(si_serial_nos)) + )