mirror of
https://github.com/frappe/erpnext.git
synced 2026-04-14 20:35:09 +00:00
fix: prevent reuse of serial no in manufacture and repack entry
(cherry picked from commit 48b537dc8c)
This commit is contained in:
committed by
Mergify
parent
f263a7f65c
commit
24f6f1e434
@@ -312,6 +312,30 @@ class SerialandBatchBundle(Document):
|
|||||||
SerialNoDuplicateError,
|
SerialNoDuplicateError,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
self.voucher_type == "Stock Entry"
|
||||||
|
and self.type_of_transaction == "Inward"
|
||||||
|
and frappe.get_cached_value("Stock Entry", self.voucher_no, "purpose")
|
||||||
|
in ["Manufacture", "Repack"]
|
||||||
|
):
|
||||||
|
serial_nos = frappe.get_all(
|
||||||
|
"Serial No", filters={"name": ("in", serial_nos), "status": "Delivered"}, pluck="name"
|
||||||
|
)
|
||||||
|
|
||||||
|
if serial_nos:
|
||||||
|
if len(serial_nos) == 1:
|
||||||
|
frappe.throw(
|
||||||
|
_(
|
||||||
|
"Serial No {0} is already Delivered. You cannot use them again in Manufacture / Repack entry."
|
||||||
|
).format(bold(serial_nos[0]))
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
frappe.throw(
|
||||||
|
_(
|
||||||
|
"Serial Nos {0} are already Delivered. You cannot use them again in Manufacture / Repack entry."
|
||||||
|
).format(bold(", ".join(serial_nos)))
|
||||||
|
)
|
||||||
|
|
||||||
def throw_error_message(self, message, exception=frappe.ValidationError):
|
def throw_error_message(self, message, exception=frappe.ValidationError):
|
||||||
frappe.throw(_(message), exception, title=_("Error"))
|
frappe.throw(_(message), exception, title=_("Error"))
|
||||||
|
|
||||||
|
|||||||
@@ -2088,6 +2088,45 @@ class TestStockEntry(FrappeTestCase):
|
|||||||
|
|
||||||
self.assertEqual(incoming_rate, 125.0)
|
self.assertEqual(incoming_rate, 125.0)
|
||||||
|
|
||||||
|
def test_prevent_reuse_delivered_serial_no_in_repack(self):
|
||||||
|
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
||||||
|
|
||||||
|
item = "Test Prevent Reuse Delivered Serial No"
|
||||||
|
warehouse = "_Test Warehouse - _TC"
|
||||||
|
|
||||||
|
item_doc = make_item(item, {"is_stock_item": 1, "has_serial_no": 1, "serial_no_series": "SHGJ.####"})
|
||||||
|
|
||||||
|
make_stock_entry(item_code="_Test Item", target=warehouse, qty=2, rate=100)
|
||||||
|
make_stock_entry(item_code=item, target=warehouse, qty=2, rate=100)
|
||||||
|
|
||||||
|
dn = create_delivery_note(item_code=item, qty=2)
|
||||||
|
delivered_serial_no = get_serial_nos_from_bundle(dn.get("items")[0].serial_and_batch_bundle)[0]
|
||||||
|
|
||||||
|
se = make_stock_entry(
|
||||||
|
item_code="_Test Item", source=warehouse, qty=1, purpose="Repack", do_not_save=True
|
||||||
|
)
|
||||||
|
se.append(
|
||||||
|
"items",
|
||||||
|
{
|
||||||
|
"item_code": item_doc.name,
|
||||||
|
"item_name": item_doc.item_name,
|
||||||
|
"s_warehouse": None,
|
||||||
|
"t_warehouse": warehouse,
|
||||||
|
"description": item_doc.description,
|
||||||
|
"uom": item_doc.stock_uom,
|
||||||
|
"qty": 1,
|
||||||
|
"use_serial_batch_fields": 1,
|
||||||
|
"serial_no": delivered_serial_no,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
se.save()
|
||||||
|
status = frappe.db.get_value("Serial No", delivered_serial_no, "status")
|
||||||
|
|
||||||
|
self.assertEqual(status, "Delivered")
|
||||||
|
self.assertEqual(se.purpose, "Repack")
|
||||||
|
self.assertRaises(frappe.ValidationError, se.submit)
|
||||||
|
|
||||||
|
|
||||||
def make_serialized_item(**args):
|
def make_serialized_item(**args):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
|||||||
@@ -406,12 +406,7 @@ class SerialBatchBundle:
|
|||||||
|
|
||||||
self.update_serial_no_status_warehouse(self.sle, serial_nos)
|
self.update_serial_no_status_warehouse(self.sle, serial_nos)
|
||||||
|
|
||||||
def update_serial_no_status_warehouse(self, sle, serial_nos):
|
def get_status_for_serial_nos(self, sle):
|
||||||
warehouse = sle.warehouse if sle.actual_qty > 0 else None
|
|
||||||
|
|
||||||
if isinstance(serial_nos, str):
|
|
||||||
serial_nos = [serial_nos]
|
|
||||||
|
|
||||||
status = "Inactive"
|
status = "Inactive"
|
||||||
if sle.actual_qty < 0:
|
if sle.actual_qty < 0:
|
||||||
status = "Delivered"
|
status = "Delivered"
|
||||||
@@ -425,6 +420,23 @@ class SerialBatchBundle:
|
|||||||
]:
|
]:
|
||||||
status = "Consumed"
|
status = "Consumed"
|
||||||
|
|
||||||
|
if sle.is_cancelled == 1 and (
|
||||||
|
sle.voucher_type in ["Purchase Invoice", "Purchase Receipt"] or status == "Consumed"
|
||||||
|
):
|
||||||
|
status = "Inactive"
|
||||||
|
|
||||||
|
return status
|
||||||
|
|
||||||
|
def update_serial_no_status_warehouse(self, sle, serial_nos):
|
||||||
|
warehouse = sle.warehouse if sle.actual_qty > 0 else None
|
||||||
|
|
||||||
|
if isinstance(serial_nos, str):
|
||||||
|
serial_nos = [serial_nos]
|
||||||
|
|
||||||
|
status = "Active"
|
||||||
|
if not warehouse:
|
||||||
|
status = self.get_status_for_serial_nos(sle)
|
||||||
|
|
||||||
customer = None
|
customer = None
|
||||||
if sle.voucher_type in ["Sales Invoice", "Delivery Note"] and sle.actual_qty < 0:
|
if sle.voucher_type in ["Sales Invoice", "Delivery Note"] and sle.actual_qty < 0:
|
||||||
customer = frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "customer")
|
customer = frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "customer")
|
||||||
|
|||||||
Reference in New Issue
Block a user