mirror of
https://github.com/frappe/erpnext.git
synced 2026-09-14 01:20:41 +00:00
feat(stock): show reserved stock in batch balance history (#59008)
This commit is contained in:
@@ -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 = []
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user