diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 2b4780437f4..ed29316030f 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -191,7 +191,7 @@ class StockReconciliation(StockController): sl_entries = [] - serialized_items = False + serialized_items = [] for row in self.items: item = frappe.get_cached_doc("Item", row.item_code) if not (item.has_serial_no): @@ -229,27 +229,29 @@ class StockReconciliation(StockController): sl_entries.append(sle_data) else: - serialized_items = True + serialized_items.append(row.item_code) if serialized_items: - self.get_sle_for_serialized_items(sl_entries) + self.get_sle_for_serialized_items(sl_entries, serialized_items) if sl_entries: allow_negative_stock = frappe.get_cached_value("Stock Settings", None, "allow_negative_stock") self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock) - def get_sle_for_serialized_items(self, sl_entries): - self.issue_existing_serial_and_batch(sl_entries) - self.add_new_serial_and_batch(sl_entries) - self.update_valuation_rate_for_serial_no() + def get_sle_for_serialized_items(self, sl_entries, serialized_items=[]): + self.issue_existing_serial_and_batch(sl_entries, serialized_items) + self.add_new_serial_and_batch(sl_entries, serialized_items) + self.update_valuation_rate_for_serial_no(serialized_items) if sl_entries: sl_entries = self.merge_similar_item_serial_nos(sl_entries) - def issue_existing_serial_and_batch(self, sl_entries): + def issue_existing_serial_and_batch(self, sl_entries, serialized_items=[]): from erpnext.stock.stock_ledger import get_stock_ledger_entries for row in self.items: + if row.item_code not in serialized_items: continue + serial_nos = get_serial_nos(row.serial_no) or [] # To issue existing serial nos @@ -303,8 +305,10 @@ class StockReconciliation(StockController): sl_entries.append(new_args) - def add_new_serial_and_batch(self, sl_entries): + def add_new_serial_and_batch(self, sl_entries, serialized_items=[]): for row in self.items: + if row.item_code not in serialized_items: continue + if row.qty: args = self.get_sle_for_items(row) @@ -316,9 +320,9 @@ class StockReconciliation(StockController): sl_entries.append(args) - def update_valuation_rate_for_serial_no(self): + def update_valuation_rate_for_serial_no(self, serialized_items=[]): for d in self.items: - if not d.serial_no: continue + if d.item_code not in serialized_items: continue serial_nos = get_serial_nos(d.serial_no) self.update_valuation_rate_for_serial_nos(d, serial_nos) @@ -372,7 +376,16 @@ class StockReconciliation(StockController): where voucher_type=%s and voucher_no=%s""", (self.doctype, self.name)) sl_entries = [] - self.get_sle_for_serialized_items(sl_entries) + + serialized_items = [] + + for row in self.items: + has_serial_no = frappe.get_cached_value("Item", row.item_code, "has_serial_no") + if has_serial_no: + serialized_items.append(row.item_code) + + if serialized_items: + self.get_sle_for_serialized_items(sl_entries, serialized_items) if sl_entries: sl_entries.reverse() diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index 27908016407..7c55fd6da1c 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -207,9 +207,9 @@ class TestStockReconciliation(unittest.TestCase): def test_stock_reco_for_serial_and_batch_item(self): set_perpetual_inventory() - item = frappe.db.exists("Item", {'item_name': 'Batched and Serialised Item'}) + item = frappe.db.exists("Item", {'item_name': 'Batched and Serialised Item 1'}) if not item: - item = create_item("Batched and Serialised Item") + item = create_item("Batched and Serialised Item 1") item.has_batch_no = 1 item.create_new_batch = 1 item.has_serial_no = 1 @@ -217,7 +217,7 @@ class TestStockReconciliation(unittest.TestCase): item.serial_no_series = "S-.####" item.save() else: - item = frappe.get_doc("Item", {'item_name': 'Batched and Serialised Item'}) + item = frappe.get_doc("Item", {'item_name': 'Batched and Serialised Item 1'}) warehouse = "_Test Warehouse for Stock Reco2 - _TC" @@ -236,7 +236,7 @@ class TestStockReconciliation(unittest.TestCase): self.assertEqual(frappe.db.exists("Batch", batch_no), None) if frappe.db.exists("Serial No", serial_nos[0]): - frappe.delete_doc("Serial No", serial_nos[0]) + frappe.delete_doc("Serial No", serial_nos[0]) def test_stock_reco_for_serial_and_batch_item_with_future_dependent_entry(self): """ @@ -255,9 +255,9 @@ class TestStockReconciliation(unittest.TestCase): set_perpetual_inventory() - item = frappe.db.exists("Item", {'item_name': 'Batched and Serialised Item'}) + item = frappe.db.exists("Item", {'item_name': 'Batched and Serialised Item 1'}) if not item: - item = create_item("Batched and Serialised Item") + item = create_item("Batched and Serialised Item 1") item.has_batch_no = 1 item.create_new_batch = 1 item.has_serial_no = 1 @@ -265,7 +265,7 @@ class TestStockReconciliation(unittest.TestCase): item.serial_no_series = "S-.####" item.save() else: - item = frappe.get_doc("Item", {'item_name': 'Batched and Serialised Item'}) + item = frappe.get_doc("Item", {'item_name': 'Batched and Serialised Item 1'}) warehouse = "_Test Warehouse for Stock Reco2 - _TC" @@ -392,6 +392,35 @@ class TestStockReconciliation(unittest.TestCase): doc.cancel() frappe.delete_doc(doc.doctype, doc.name) + def test_stock_reco_with_serial_and_batch(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + + warehouse = "_Test Warehouse for Stock Reco1 - _TC" + ste1=make_stock_entry(item_code="Stock-Reco-Serial-Item-1", + target=warehouse, qty=2, basic_rate=100) + + ste2=make_stock_entry(item_code="Stock-Reco-batch-Item-1", + target=warehouse, qty=2, basic_rate=100) + + sr = create_stock_reconciliation(item_code="Stock-Reco-Serial-Item-1", + warehouse = warehouse, rate=200, do_not_submit=True) + + sr.append("items", { + "item_code": "Stock-Reco-batch-Item-1", + "warehouse": warehouse, + "batch_no": ste2.items[0].batch_no, + "valuation_rate": 200 + }) + + sr.submit() + sle = frappe.get_all("Stock Ledger Entry", filters={"item_code": "Stock-Reco-batch-Item-1", + "warehouse": warehouse, "voucher_no": sr.name, "voucher_type": sr.doctype}) + + self.assertEquals(len(sle), 1) + + for doc in [sr, ste2, ste1]: + doc.cancel() + def insert_existing_sle(warehouse): from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry