feat(stock): show reserved stock in batch balance history (#59008)

This commit is contained in:
Pandiyan P
2026-09-11 18:21:16 +05:30
committed by GitHub
parent 825d24f406
commit 000dcfc23d
2 changed files with 77 additions and 1 deletions

View File

@@ -38,6 +38,7 @@ def execute(filters=None):
columns = get_columns(filters)
item_map = get_item_details(filters)
iwb_map = get_item_warehouse_batch_map(filters, float_precision)
reserved_stock = get_reserved_stock(filters, iwb_map)
data = []
for item in sorted(iwb_map):
@@ -63,6 +64,7 @@ def execute(filters=None):
),
flt(qty_dict.bal_value, float_precision),
item_map[item]["stock_uom"],
flt(reserved_stock.get((item, wh, batch), 0), float_precision),
]
)
@@ -85,11 +87,48 @@ def get_columns(filters):
_("Valuation Rate") + ":Float:120",
_("Balance Value") + ":Currency:120",
_("UOM") + "::90",
_("Reserved Stock (Current)") + ":Float:175",
]
return columns
def get_reserved_stock(filters, iwb_map):
if not iwb_map:
return {}
sre = frappe.qb.DocType("Stock Reservation Entry")
sb_entry = frappe.qb.DocType("Serial and Batch Entry")
warehouses = {warehouse for item in iwb_map.values() for warehouse in item}
query = (
frappe.qb.from_(sre)
.inner_join(sb_entry)
.on(sre.name == sb_entry.parent)
.select(
sre.item_code,
sre.warehouse,
sb_entry.batch_no,
fn.Sum(sb_entry.qty - sb_entry.delivered_qty).as_("reserved_qty"),
)
.where(
(sre.docstatus == 1)
& (sre.reservation_based_on == "Serial and Batch")
& (sre.status.notin(["Closed", "Delivered"]))
& (sre.item_code.isin(list(iwb_map)))
& (sre.warehouse.isin(warehouses))
& (sb_entry.batch_no.isnotnull())
& (sb_entry.qty > sb_entry.delivered_qty)
)
.groupby(sre.item_code, sre.warehouse, sb_entry.batch_no)
)
if filters.get("company"):
query = query.where(sre.company == filters.company)
if filters.get("batch_no"):
query = query.where(sb_entry.batch_no == filters.batch_no)
return {(row.item_code, row.warehouse, row.batch_no): row.reserved_qty for row in query.run(as_dict=True)}
def get_stock_ledger_entries(filters):
entries = []

View File

@@ -2,14 +2,18 @@
# See license.txt
import frappe
from frappe.utils import today
from erpnext.selling.doctype.sales_order.mapper import make_delivery_note
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from erpnext.stock.report.batch_wise_balance_history.batch_wise_balance_history import execute
from erpnext.tests.utils import ERPNextTestSuite
WH = "Stores - _TC"
# row indexes: 0 item, 1 name, 2 desc, 3 wh, 4 batch, 5 opening, 6 in, 7 out, 8 bal, 9 rate, 10 value, 11 uom
# row indexes: 0 item, 1 name, 2 desc, 3 wh, 4 batch, 5 opening, 6 in, 7 out, 8 bal,
# 9 rate, 10 value, 11 uom, 12 reserved stock
class TestBatchWiseBalanceHistory(ERPNextTestSuite):
@@ -50,3 +54,36 @@ class TestBatchWiseBalanceHistory(ERPNextTestSuite):
self.assertEqual(row[5], 8) # opening carried from 2025
self.assertEqual(row[6], 0)
self.assertEqual(row[8], 8) # balance
@ERPNextTestSuite.change_settings(
"Stock Settings",
{
"enable_stock_reservation": 1,
"auto_reserve_stock": 0,
"auto_reserve_serial_and_batch": 1,
"use_serial_batch_fields": 1,
},
)
def test_reserved_stock_after_delivery(self):
posting_date = today()
report_dates = {"from_date": posting_date, "to_date": posting_date}
item = self.make_batch_item()
make_stock_entry(item_code=item, to_warehouse=WH, qty=10, rate=100, posting_date=posting_date)
(row,) = self.run_report(item, **report_dates)
batch = row[4]
self.assertEqual(row[12], 0)
order = make_sales_order(item_code=item, warehouse=WH, qty=6, transaction_date=posting_date)
order.create_stock_reservation_entries()
self.assertEqual(self.run_report(item, **report_dates)[0][12], 6)
for delivered_qty, expected_reserved_qty in [(2, 4), (4, 0)]:
delivery = make_delivery_note(order.name)
delivery.set_posting_time = 1
delivery.posting_date = posting_date
delivery.items[0].qty = delivered_qty
delivery.items[0].use_serial_batch_fields = 1
delivery.items[0].batch_no = batch
delivery.items[0].serial_and_batch_bundle = None
delivery.insert().submit()
self.assertEqual(self.run_report(item, **report_dates)[0][12], expected_reserved_qty)