From 3b25ee4e6a1fdb8cd24c9b86be8f268c3df330e1 Mon Sep 17 00:00:00 2001 From: RitvikSardana Date: Sun, 3 Sep 2023 13:35:35 +0530 Subject: [PATCH] fix: added validation for duplicate serial nos in pos --- .../doctype/pos_invoice/pos_invoice.py | 24 +++++++++++++- .../doctype/pos_invoice/test_pos_invoice.py | 32 +++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 0ff230bb18be..3815172f5e42 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -6,7 +6,7 @@ from frappe import _ from frappe.query_builder.functions import IfNull, Sum from frappe.utils import cint, flt, get_link_to_form, getdate, nowdate - +import collections from erpnext.accounts.doctype.loyalty_program.loyalty_program import validate_loyalty_points from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request from erpnext.accounts.doctype.sales_invoice.sales_invoice import ( @@ -55,6 +55,7 @@ def validate(self): self.validate_payment_amount() self.validate_loyalty_transaction() self.validate_company_with_pos_company() + self.validate_duplicate_serial_and_batch_no() if self.coupon_code: from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code @@ -155,6 +156,27 @@ def validate_pos_reserved_serial_nos(self, item): title=_("Item Unavailable"), ) + def validate_duplicate_serial_and_batch_no(self): + serial_nos = [] + batch_nos = [] + + for row in self.get("items"): + if row.serial_no: + serial_nos = row.serial_no.split("\n") + + if row.batch_no and not row.serial_no: + batch_nos.append(row.batch_no) + + if serial_nos: + for key, value in collections.Counter(serial_nos).items(): + if value > 1: + frappe.throw(_("Duplicate Serial No {0} found").format("key")) + + if batch_nos: + for key, value in collections.Counter(batch_nos).items(): + if value > 1: + frappe.throw(_("Duplicate Batch No {0} found").format("key")) + def validate_pos_reserved_batch_qty(self, item): filters = {"item_code": item.item_code, "warehouse": item.warehouse, "batch_no": item.batch_no} diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py index 3132fdd259a0..75d43088b52d 100644 --- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py @@ -464,6 +464,38 @@ def test_delivered_serialized_item_transaction(self): pos2.insert() self.assertRaises(frappe.ValidationError, pos2.submit) + def test_pos_invoice_with_duplicate_serial_no(self): + from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item + + se = make_serialized_item( + company="_Test Company", + target_warehouse="Stores - _TC", + cost_center="Main - _TC", + expense_account="Cost of Goods Sold - _TC", + ) + + serial_nos = get_serial_nos(se.get("items")[0].serial_no) + + pos = create_pos_invoice( + company="_Test Company", + debit_to="Debtors - _TC", + account_for_change_amount="Cash - _TC", + warehouse="Stores - _TC", + income_account="Sales - _TC", + expense_account="Cost of Goods Sold - _TC", + cost_center="Main - _TC", + item=se.get("items")[0].item_code, + rate=1000, + qty=2, + do_not_save=1, + ) + + pos.get("items")[0].has_serial_no = 1 + pos.get("items")[0].serial_no = serial_nos[0] + "\n" + serial_nos[0] + self.assertRaises(frappe.ValidationError, pos.submit) + + def test_invalid_serial_no_validation(self): from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item