mirror of
https://github.com/frappe/erpnext.git
synced 2026-03-29 09:01:14 +02:00
fix: pick serial nos from selected batch only (#37988)
* fix: pick current serial nos from selected batch only * test: add test case for current qty and current serial nos
This commit is contained in:
@@ -890,6 +890,7 @@ def get_stock_balance_for(
|
|||||||
with_valuation_rate=with_valuation_rate,
|
with_valuation_rate=with_valuation_rate,
|
||||||
with_serial_no=has_serial_no,
|
with_serial_no=has_serial_no,
|
||||||
inventory_dimensions_dict=inventory_dimensions_dict,
|
inventory_dimensions_dict=inventory_dimensions_dict,
|
||||||
|
batch_no=batch_no,
|
||||||
)
|
)
|
||||||
|
|
||||||
if has_serial_no:
|
if has_serial_no:
|
||||||
|
|||||||
@@ -1009,6 +1009,52 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin):
|
|||||||
|
|
||||||
self.assertEqual(sr2.docstatus, 1)
|
self.assertEqual(sr2.docstatus, 1)
|
||||||
|
|
||||||
|
def test_current_qty_and_current_serial_no_count(self):
|
||||||
|
# Step - 1: Create a Serial Batch Item
|
||||||
|
item = self.make_item(
|
||||||
|
properties={
|
||||||
|
"is_stock_item": 1,
|
||||||
|
"has_serial_no": 1,
|
||||||
|
"serial_no_series": "TEST-SERIAL-.###",
|
||||||
|
"has_batch_no": 1,
|
||||||
|
"create_new_batch": 1,
|
||||||
|
"batch_number_series": "TEST-BATCH-.###",
|
||||||
|
}
|
||||||
|
).name
|
||||||
|
|
||||||
|
# Step - 2: Inward stock in multiple Batches
|
||||||
|
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
||||||
|
|
||||||
|
se1 = make_stock_entry(
|
||||||
|
item_code=item,
|
||||||
|
target="_Test Warehouse - _TC",
|
||||||
|
qty=10,
|
||||||
|
)
|
||||||
|
se2 = make_stock_entry(
|
||||||
|
item_code=item,
|
||||||
|
target="_Test Warehouse - _TC",
|
||||||
|
qty=5,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Step - 3: Create Stock Reconciliation
|
||||||
|
sr = create_stock_reconciliation(
|
||||||
|
item_code=item,
|
||||||
|
warehouse="_Test Warehouse - _TC",
|
||||||
|
qty=0,
|
||||||
|
batch_no=se1.items[0].batch_no,
|
||||||
|
do_not_submit=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Test - 1: Current Serial No Count should be equal to Current Qty
|
||||||
|
self.assertEqual(sr.items[0].current_qty, se1.items[0].qty)
|
||||||
|
self.assertEqual(len(sr.items[0].current_serial_no.split("\n")), sr.items[0].current_qty)
|
||||||
|
|
||||||
|
sr.items[0].batch_no = se2.items[0].batch_no
|
||||||
|
sr.save()
|
||||||
|
|
||||||
|
self.assertEqual(sr.items[0].current_qty, se2.items[0].qty)
|
||||||
|
self.assertEqual(len(sr.items[0].current_serial_no.split("\n")), sr.items[0].current_qty)
|
||||||
|
|
||||||
|
|
||||||
def create_batch_item_with_batch(item_name, batch_id):
|
def create_batch_item_with_batch(item_name, batch_id):
|
||||||
batch_item_doc = create_item(item_name, is_stock_item=1)
|
batch_item_doc = create_item(item_name, is_stock_item=1)
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ def get_stock_balance(
|
|||||||
with_valuation_rate=False,
|
with_valuation_rate=False,
|
||||||
with_serial_no=False,
|
with_serial_no=False,
|
||||||
inventory_dimensions_dict=None,
|
inventory_dimensions_dict=None,
|
||||||
|
batch_no=None,
|
||||||
):
|
):
|
||||||
"""Returns stock balance quantity at given warehouse on given posting date or current date.
|
"""Returns stock balance quantity at given warehouse on given posting date or current date.
|
||||||
|
|
||||||
@@ -124,6 +125,9 @@ def get_stock_balance(
|
|||||||
|
|
||||||
if with_valuation_rate:
|
if with_valuation_rate:
|
||||||
if with_serial_no:
|
if with_serial_no:
|
||||||
|
if batch_no:
|
||||||
|
args["batch_no"] = batch_no
|
||||||
|
|
||||||
serial_nos = get_serial_nos_data_after_transactions(args)
|
serial_nos = get_serial_nos_data_after_transactions(args)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -140,27 +144,30 @@ def get_stock_balance(
|
|||||||
|
|
||||||
|
|
||||||
def get_serial_nos_data_after_transactions(args):
|
def get_serial_nos_data_after_transactions(args):
|
||||||
|
|
||||||
serial_nos = set()
|
serial_nos = set()
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
sle = frappe.qb.DocType("Stock Ledger Entry")
|
|
||||||
|
|
||||||
stock_ledger_entries = (
|
sle = frappe.qb.DocType("Stock Ledger Entry")
|
||||||
|
query = (
|
||||||
frappe.qb.from_(sle)
|
frappe.qb.from_(sle)
|
||||||
.select("serial_no", "actual_qty")
|
.select(sle.serial_no, sle.actual_qty)
|
||||||
.where(
|
.where(
|
||||||
(sle.item_code == args.item_code)
|
(sle.is_cancelled == 0)
|
||||||
|
& (sle.item_code == args.item_code)
|
||||||
& (sle.warehouse == args.warehouse)
|
& (sle.warehouse == args.warehouse)
|
||||||
& (
|
& (
|
||||||
CombineDatetime(sle.posting_date, sle.posting_time)
|
CombineDatetime(sle.posting_date, sle.posting_time)
|
||||||
< CombineDatetime(args.posting_date, args.posting_time)
|
< CombineDatetime(args.posting_date, args.posting_time)
|
||||||
)
|
)
|
||||||
& (sle.is_cancelled == 0)
|
|
||||||
)
|
)
|
||||||
.orderby(sle.posting_date, sle.posting_time, sle.creation)
|
.orderby(sle.posting_date, sle.posting_time, sle.creation)
|
||||||
.run(as_dict=1)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if args.batch_no:
|
||||||
|
query = query.where(sle.batch_no == args.batch_no)
|
||||||
|
|
||||||
|
stock_ledger_entries = query.run(as_dict=True)
|
||||||
|
|
||||||
for stock_ledger_entry in stock_ledger_entries:
|
for stock_ledger_entry in stock_ledger_entries:
|
||||||
changed_serial_no = get_serial_nos_data(stock_ledger_entry.serial_no)
|
changed_serial_no = get_serial_nos_data(stock_ledger_entry.serial_no)
|
||||||
if stock_ledger_entry.actual_qty > 0:
|
if stock_ledger_entry.actual_qty > 0:
|
||||||
|
|||||||
Reference in New Issue
Block a user