diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index af0afb4dd020..734365082828 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -166,7 +166,7 @@ def clean_serial_nos(self): # remove extra whitespace and store one serial no on each line row.serial_no = clean_serial_no_string(row.serial_no) - def make_bundle_using_old_serial_batch_fields(self, table_name=None): + def make_bundle_using_old_serial_batch_fields(self, table_name=None, via_landed_cost_voucher=False): if self.get("_action") == "update_after_submit": return @@ -205,7 +205,7 @@ def make_bundle_using_old_serial_batch_fields(self, table_name=None): "company": self.company, "is_rejected": 1 if row.get("rejected_warehouse") else 0, "use_serial_batch_fields": row.use_serial_batch_fields, - "do_not_submit": True, + "do_not_submit": True if not via_landed_cost_voucher else False, } if row.get("qty") or row.get("consumed_qty"): diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py index ff6d31a777f8..601cde6c905b 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -250,6 +250,7 @@ def update_landed_cost(self): # update stock & gl entries for submit state of PR doc.docstatus = 1 + doc.make_bundle_using_old_serial_batch_fields(via_landed_cost_voucher=True) doc.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True) doc.make_gl_entries() doc.repost_future_sle_and_gle() diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py index 9ec2d695707a..32b384def285 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py @@ -596,6 +596,155 @@ def test_asset_lcv(self): lcv.cancel() pr.cancel() + def test_landed_cost_voucher_with_serial_batch_for_legacy_pr(self): + from erpnext.stock.doctype.item.test_item import make_item + + frappe.flags.ignore_serial_batch_bundle_validation = True + frappe.flags.use_serial_and_batch_fields = True + sn_item = "Test Landed Cost Voucher Serial NO for Legacy PR" + batch_item = "Test Landed Cost Voucher Batch NO for Legacy PR" + sn_item_doc = make_item( + sn_item, + { + "has_serial_no": 1, + "serial_no_series": "SN-TLCVSNO-.####", + "is_stock_item": 1, + }, + ) + + batch_item_doc = make_item( + batch_item, + { + "has_batch_no": 1, + "batch_number_series": "BATCH-TLCVSNO-.####", + "create_new_batch": 1, + "is_stock_item": 1, + }, + ) + + serial_nos = [ + "SN-TLCVSNO-0001", + "SN-TLCVSNO-0002", + "SN-TLCVSNO-0003", + "SN-TLCVSNO-0004", + "SN-TLCVSNO-0005", + ] + + for sn in serial_nos: + if not frappe.db.exists("Serial No", sn): + sn_doc = frappe.get_doc( + { + "doctype": "Serial No", + "item_code": sn_item, + "serial_no": sn, + } + ) + sn_doc.insert() + + if not frappe.db.exists("Batch", "BATCH-TLCVSNO-0001"): + batch_doc = frappe.get_doc( + { + "doctype": "Batch", + "item": batch_item, + "batch_id": "BATCH-TLCVSNO-0001", + } + ) + batch_doc.insert() + + warehouse = "_Test Warehouse - _TC" + company = frappe.db.get_value("Warehouse", warehouse, "company") + + pr = make_purchase_receipt( + company=company, + warehouse=warehouse, + item_code=sn_item, + qty=5, + rate=100, + uom=sn_item_doc.stock_uom, + stock_uom=sn_item_doc.stock_uom, + do_not_submit=True, + ) + + pr.append( + "items", + { + "item_code": batch_item, + "item_name": batch_item, + "description": "Test Batch Item", + "uom": batch_item_doc.stock_uom, + "stock_uom": batch_item_doc.stock_uom, + "qty": 5, + "rate": 100, + "warehouse": warehouse, + }, + ) + + pr.submit() + pr.reload() + + for row in pr.items: + self.assertEqual(row.valuation_rate, 100) + self.assertFalse(row.serial_no) + self.assertFalse(row.batch_no) + self.assertFalse(row.serial_and_batch_bundle) + + if row.item_code == sn_item: + row.db_set("serial_no", ", ".join(serial_nos)) + else: + row.db_set("batch_no", "BATCH-TLCVSNO-0001") + + for sn in serial_nos: + sn_doc = frappe.get_doc("Serial No", sn) + sn_doc.db_set( + { + "warehouse": warehouse, + "status": "Active", + } + ) + + batch_doc.db_set( + { + "batch_qty": 5, + } + ) + + frappe.flags.ignore_serial_batch_bundle_validation = False + frappe.flags.use_serial_and_batch_fields = False + + lcv = make_landed_cost_voucher( + company=pr.company, + receipt_document_type="Purchase Receipt", + receipt_document=pr.name, + charges=20, + distribute_charges_based_on="Qty", + do_not_save=True, + ) + + lcv.get_items_from_purchase_receipts() + lcv.save() + lcv.submit() + + pr.reload() + + for row in pr.items: + self.assertEqual(row.valuation_rate, 102) + self.assertTrue(row.serial_and_batch_bundle) + self.assertEqual( + row.valuation_rate, + frappe.db.get_value("Serial and Batch Bundle", row.serial_and_batch_bundle, "avg_rate"), + ) + + lcv.cancel() + pr.reload() + + for row in pr.items: + self.assertEqual(row.valuation_rate, 100) + self.assertTrue(row.serial_and_batch_bundle) + self.assertEqual( + row.valuation_rate, + frappe.db.get_value("Serial and Batch Bundle", row.serial_and_batch_bundle, "avg_rate"), + ) + def make_landed_cost_voucher(**args): args = frappe._dict(args)