mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-15 19:19:17 +00:00
fix: stock ledger entries for stock reco (#23955)
This commit is contained in:
@@ -191,7 +191,7 @@ class StockReconciliation(StockController):
|
|||||||
|
|
||||||
sl_entries = []
|
sl_entries = []
|
||||||
|
|
||||||
serialized_items = False
|
serialized_items = []
|
||||||
for row in self.items:
|
for row in self.items:
|
||||||
item = frappe.get_cached_doc("Item", row.item_code)
|
item = frappe.get_cached_doc("Item", row.item_code)
|
||||||
if not (item.has_serial_no):
|
if not (item.has_serial_no):
|
||||||
@@ -229,27 +229,29 @@ class StockReconciliation(StockController):
|
|||||||
sl_entries.append(sle_data)
|
sl_entries.append(sle_data)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
serialized_items = True
|
serialized_items.append(row.item_code)
|
||||||
|
|
||||||
if serialized_items:
|
if serialized_items:
|
||||||
self.get_sle_for_serialized_items(sl_entries)
|
self.get_sle_for_serialized_items(sl_entries, serialized_items)
|
||||||
|
|
||||||
if sl_entries:
|
if sl_entries:
|
||||||
allow_negative_stock = frappe.get_cached_value("Stock Settings", None, "allow_negative_stock")
|
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)
|
self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock)
|
||||||
|
|
||||||
def get_sle_for_serialized_items(self, sl_entries):
|
def get_sle_for_serialized_items(self, sl_entries, serialized_items=[]):
|
||||||
self.issue_existing_serial_and_batch(sl_entries)
|
self.issue_existing_serial_and_batch(sl_entries, serialized_items)
|
||||||
self.add_new_serial_and_batch(sl_entries)
|
self.add_new_serial_and_batch(sl_entries, serialized_items)
|
||||||
self.update_valuation_rate_for_serial_no()
|
self.update_valuation_rate_for_serial_no(serialized_items)
|
||||||
|
|
||||||
if sl_entries:
|
if sl_entries:
|
||||||
sl_entries = self.merge_similar_item_serial_nos(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
|
from erpnext.stock.stock_ledger import get_stock_ledger_entries
|
||||||
|
|
||||||
for row in self.items:
|
for row in self.items:
|
||||||
|
if row.item_code not in serialized_items: continue
|
||||||
|
|
||||||
serial_nos = get_serial_nos(row.serial_no) or []
|
serial_nos = get_serial_nos(row.serial_no) or []
|
||||||
|
|
||||||
# To issue existing serial nos
|
# To issue existing serial nos
|
||||||
@@ -303,8 +305,10 @@ class StockReconciliation(StockController):
|
|||||||
|
|
||||||
sl_entries.append(new_args)
|
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:
|
for row in self.items:
|
||||||
|
if row.item_code not in serialized_items: continue
|
||||||
|
|
||||||
if row.qty:
|
if row.qty:
|
||||||
args = self.get_sle_for_items(row)
|
args = self.get_sle_for_items(row)
|
||||||
|
|
||||||
@@ -316,9 +320,9 @@ class StockReconciliation(StockController):
|
|||||||
|
|
||||||
sl_entries.append(args)
|
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:
|
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)
|
serial_nos = get_serial_nos(d.serial_no)
|
||||||
self.update_valuation_rate_for_serial_nos(d, serial_nos)
|
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))
|
where voucher_type=%s and voucher_no=%s""", (self.doctype, self.name))
|
||||||
|
|
||||||
sl_entries = []
|
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:
|
if sl_entries:
|
||||||
sl_entries.reverse()
|
sl_entries.reverse()
|
||||||
|
|||||||
@@ -207,9 +207,9 @@ class TestStockReconciliation(unittest.TestCase):
|
|||||||
def test_stock_reco_for_serial_and_batch_item(self):
|
def test_stock_reco_for_serial_and_batch_item(self):
|
||||||
set_perpetual_inventory()
|
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:
|
if not item:
|
||||||
item = create_item("Batched and Serialised Item")
|
item = create_item("Batched and Serialised Item 1")
|
||||||
item.has_batch_no = 1
|
item.has_batch_no = 1
|
||||||
item.create_new_batch = 1
|
item.create_new_batch = 1
|
||||||
item.has_serial_no = 1
|
item.has_serial_no = 1
|
||||||
@@ -217,7 +217,7 @@ class TestStockReconciliation(unittest.TestCase):
|
|||||||
item.serial_no_series = "S-.####"
|
item.serial_no_series = "S-.####"
|
||||||
item.save()
|
item.save()
|
||||||
else:
|
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"
|
warehouse = "_Test Warehouse for Stock Reco2 - _TC"
|
||||||
|
|
||||||
@@ -236,7 +236,7 @@ class TestStockReconciliation(unittest.TestCase):
|
|||||||
self.assertEqual(frappe.db.exists("Batch", batch_no), None)
|
self.assertEqual(frappe.db.exists("Batch", batch_no), None)
|
||||||
|
|
||||||
if frappe.db.exists("Serial No", serial_nos[0]):
|
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):
|
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()
|
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:
|
if not item:
|
||||||
item = create_item("Batched and Serialised Item")
|
item = create_item("Batched and Serialised Item 1")
|
||||||
item.has_batch_no = 1
|
item.has_batch_no = 1
|
||||||
item.create_new_batch = 1
|
item.create_new_batch = 1
|
||||||
item.has_serial_no = 1
|
item.has_serial_no = 1
|
||||||
@@ -265,7 +265,7 @@ class TestStockReconciliation(unittest.TestCase):
|
|||||||
item.serial_no_series = "S-.####"
|
item.serial_no_series = "S-.####"
|
||||||
item.save()
|
item.save()
|
||||||
else:
|
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"
|
warehouse = "_Test Warehouse for Stock Reco2 - _TC"
|
||||||
|
|
||||||
@@ -392,6 +392,35 @@ class TestStockReconciliation(unittest.TestCase):
|
|||||||
doc.cancel()
|
doc.cancel()
|
||||||
frappe.delete_doc(doc.doctype, doc.name)
|
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):
|
def insert_existing_sle(warehouse):
|
||||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user