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
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):
if not from_repost:

View File

@@ -222,7 +222,12 @@ class TestSerialandBatchBundle(FrappeTestCase):
).insert(ignore_permissions=True)
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 = []
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):
from erpnext.stock.doctype.batch.batch import get_available_batches
batches = get_distinct_batches(voucher_type, voucher_no)
def update_batch_qty(voucher_type, voucher_no, docstatus, via_landed_cost_voucher=False):
batches = get_batchwise_qty(voucher_type, voucher_no)
if not batches:
return
precision = frappe.get_precision("Batch", "batch_qty")
batch_data = get_available_batches(
frappe._dict({"batch_no": batches, "consider_negative_batches": 1, "based_on_warehouse": True})
)
batchwise_qty = defaultdict(float)
for batch, qty in batches.items():
current_qty = get_batch_current_qty(batch)
current_qty += flt(qty, precision) * (-1 if docstatus == 2 else 1)
for (batch_no, warehouse), qty in batch_data.items():
if not via_landed_cost_voucher and flt(qty, precision) < 0:
throw_negative_batch_validation(batch_no, warehouse, qty)
if not via_landed_cost_voucher and current_qty < 0:
throw_negative_batch_validation(batch, current_qty)
batchwise_qty[batch_no] += 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)
frappe.db.set_value("Batch", batch, "batch_qty", current_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(
_("The Batch {0} has negative quantity {1} in warehouse {2}. Please correct the quantity.").format(
bold(batch_no), bold(qty), bold(warehouse)
_("The Batch {0} has negative quantity {1}. Please correct the quantity.").format(
bold(batch_no), bold(qty)
),
title=_("Negative Batch Quantity"),
)
def get_distinct_batches(voucher_type, voucher_no):
def get_batchwise_qty(voucher_type, voucher_no):
bundles = frappe.get_all(
"Serial and Batch Bundle",
filters={"voucher_no": voucher_no, "voucher_type": voucher_type},
@@ -1368,9 +1368,15 @@ def get_distinct_batches(voucher_type, voucher_no):
if not bundles:
return
return frappe.get_all(
batches = frappe.get_all(
"Serial and Batch Entry",
filters={"parent": ("in", bundles), "batch_no": ("is", "set")},
fields=["batch_no", "SUM(qty) as qty"],
group_by="batch_no",
pluck="batch_no",
as_list=1,
)
if not batches:
return frappe._dict({})
return frappe._dict(batches)