fix: batch qty calculation performance issue

(cherry picked from commit 1a262483a4)
This commit is contained in:
Rohit Waghchaure
2025-09-05 18:18:55 +05:30
committed by Mergify
parent 08cfd00373
commit 921f317423
3 changed files with 37 additions and 24 deletions

View File

@@ -957,7 +957,9 @@ class StockController(AccountsController):
from erpnext.stock.stock_ledger import make_sl_entries from erpnext.stock.stock_ledger import make_sl_entries
make_sl_entries(sl_entries, allow_negative_stock, via_landed_cost_voucher) make_sl_entries(sl_entries, allow_negative_stock, via_landed_cost_voucher)
update_batch_qty(self.doctype, self.name, via_landed_cost_voucher=via_landed_cost_voucher) update_batch_qty(
self.doctype, self.name, self.docstatus, via_landed_cost_voucher=via_landed_cost_voucher
)
def make_gl_entries_on_cancel(self, from_repost=False): def make_gl_entries_on_cancel(self, from_repost=False):
if not from_repost: if not from_repost:

View File

@@ -222,7 +222,12 @@ class TestSerialandBatchBundle(FrappeTestCase):
).insert(ignore_permissions=True) ).insert(ignore_permissions=True)
self.assertTrue(batch_doc.use_batchwise_valuation) self.assertTrue(batch_doc.use_batchwise_valuation)
batch_doc.db_set("use_batchwise_valuation", 0) batch_doc.db_set(
{
"use_batchwise_valuation": 0,
"batch_qty": 30,
}
)
stock_queue = [] stock_queue = []
qty_after_transaction = 0 qty_after_transaction = 0

View File

@@ -1326,40 +1326,40 @@ def get_serial_nos_batch(serial_nos):
) )
def update_batch_qty(voucher_type, voucher_no, via_landed_cost_voucher=False): def update_batch_qty(voucher_type, voucher_no, docstatus, via_landed_cost_voucher=False):
from erpnext.stock.doctype.batch.batch import get_available_batches batches = get_batchwise_qty(voucher_type, voucher_no)
batches = get_distinct_batches(voucher_type, voucher_no)
if not batches: if not batches:
return return
precision = frappe.get_precision("Batch", "batch_qty") precision = frappe.get_precision("Batch", "batch_qty")
batch_data = get_available_batches( for batch, qty in batches.items():
frappe._dict({"batch_no": batches, "consider_negative_batches": 1, "based_on_warehouse": True}) current_qty = get_batch_current_qty(batch)
) current_qty += flt(qty, precision) * (-1 if docstatus == 2 else 1)
batchwise_qty = defaultdict(float)
for (batch_no, warehouse), qty in batch_data.items(): if not via_landed_cost_voucher and current_qty < 0:
if not via_landed_cost_voucher and flt(qty, precision) < 0: throw_negative_batch_validation(batch, current_qty)
throw_negative_batch_validation(batch_no, warehouse, qty)
batchwise_qty[batch_no] += qty frappe.db.set_value("Batch", batch, "batch_qty", current_qty)
for batch_no in batches:
qty = flt(batchwise_qty.get(batch_no, 0), precision)
frappe.db.set_value("Batch", batch_no, "batch_qty", qty)
def throw_negative_batch_validation(batch_no, warehouse, qty): def get_batch_current_qty(batch):
doctype = frappe.qb.DocType("Batch")
query = frappe.qb.from_(doctype).select(doctype.batch_qty).where(doctype.name == batch).for_update()
batch_qty = query.run()
return flt(batch_qty[0][0]) if batch_qty else 0.0
def throw_negative_batch_validation(batch_no, qty):
frappe.throw( frappe.throw(
_("The Batch {0} has negative quantity {1} in warehouse {2}. Please correct the quantity.").format( _("The Batch {0} has negative quantity {1}. Please correct the quantity.").format(
bold(batch_no), bold(qty), bold(warehouse) bold(batch_no), bold(qty)
), ),
title=_("Negative Batch Quantity"), title=_("Negative Batch Quantity"),
) )
def get_distinct_batches(voucher_type, voucher_no): def get_batchwise_qty(voucher_type, voucher_no):
bundles = frappe.get_all( bundles = frappe.get_all(
"Serial and Batch Bundle", "Serial and Batch Bundle",
filters={"voucher_no": voucher_no, "voucher_type": voucher_type}, filters={"voucher_no": voucher_no, "voucher_type": voucher_type},
@@ -1368,9 +1368,15 @@ def get_distinct_batches(voucher_type, voucher_no):
if not bundles: if not bundles:
return return
return frappe.get_all( batches = frappe.get_all(
"Serial and Batch Entry", "Serial and Batch Entry",
filters={"parent": ("in", bundles), "batch_no": ("is", "set")}, filters={"parent": ("in", bundles), "batch_no": ("is", "set")},
fields=["batch_no", "SUM(qty) as qty"],
group_by="batch_no", group_by="batch_no",
pluck="batch_no", as_list=1,
) )
if not batches:
return frappe._dict({})
return frappe._dict(batches)