Merge pull request #49810 from frappe/mergify/bp/version-15-hotfix/pr-49803

fix: valuation rate for old batch (backport #49803)
This commit is contained in:
rohitwaghchaure
2025-09-30 17:50:13 +05:30
committed by GitHub
3 changed files with 81 additions and 11 deletions

View File

@@ -311,7 +311,7 @@ class SerialandBatchBundle(Document):
def throw_error_message(self, message, exception=frappe.ValidationError):
frappe.throw(_(message), exception, title=_("Error"))
def set_incoming_rate(self, parent=None, row=None, save=False, allow_negative_stock=False):
def set_incoming_rate(self, parent=None, row=None, save=False, allow_negative_stock=False, prev_sle=None):
if self.type_of_transaction not in ["Inward", "Outward"] or self.voucher_type in [
"Installation Note",
"Job Card",
@@ -321,15 +321,15 @@ class SerialandBatchBundle(Document):
return
if return_against := self.get_return_against(parent=parent):
self.set_valuation_rate_for_return_entry(return_against, row, save)
self.set_valuation_rate_for_return_entry(return_against, row, save, prev_sle=prev_sle)
elif self.type_of_transaction == "Outward":
self.set_incoming_rate_for_outward_transaction(
row, save, allow_negative_stock=allow_negative_stock
)
else:
self.set_incoming_rate_for_inward_transaction(row, save)
self.set_incoming_rate_for_inward_transaction(row, save, prev_sle=prev_sle)
def set_valuation_rate_for_return_entry(self, return_against, row, save=False):
def set_valuation_rate_for_return_entry(self, return_against, row, save=False, prev_sle=None):
if valuation_details := self.get_valuation_rate_for_return_entry(return_against):
for row in self.entries:
if valuation_details:
@@ -361,7 +361,7 @@ class SerialandBatchBundle(Document):
)
elif self.type_of_transaction == "Inward":
self.set_incoming_rate_for_inward_transaction(row, save)
self.set_incoming_rate_for_inward_transaction(row, save, prev_sle=prev_sle)
def validate_returned_serial_batch_no(self, return_against, row, original_inv_details):
if frappe.flags.through_repost_item_valuation:
@@ -529,7 +529,11 @@ class SerialandBatchBundle(Document):
if save:
d.db_set(
{"incoming_rate": d.incoming_rate, "stock_value_difference": d.stock_value_difference}
{
"incoming_rate": d.incoming_rate,
"stock_value_difference": d.stock_value_difference,
"stock_queue": d.get("stock_queue"),
}
)
def validate_negative_batch(self, batch_no, available_qty):
@@ -606,7 +610,11 @@ class SerialandBatchBundle(Document):
return return_against
def set_incoming_rate_for_inward_transaction(self, row=None, save=False):
def set_incoming_rate_for_inward_transaction(self, row=None, save=False, prev_sle=None):
from erpnext.stock.utils import get_valuation_method
valuation_method = get_valuation_method(self.item_code)
valuation_field = "valuation_rate"
if self.voucher_type in ["Sales Invoice", "Delivery Note", "Quotation"]:
valuation_field = "incoming_rate"
@@ -630,19 +638,42 @@ class SerialandBatchBundle(Document):
if not rate and self.voucher_detail_no and self.voucher_no:
rate = frappe.db.get_value(child_table, self.voucher_detail_no, valuation_field)
stock_queue = []
batches = []
if prev_sle and prev_sle.stock_queue:
batches = frappe.get_all(
"Batch",
filters={
"name": ("in", [d.batch_no for d in self.entries if d.batch_no]),
"use_batchwise_valuation": 0,
},
pluck="name",
)
if batches and valuation_method == "FIFO":
stock_queue = parse_json(prev_sle.stock_queue)
for d in self.entries:
if self.is_rejected:
rate = 0.0
elif (d.incoming_rate == rate) and d.qty and d.stock_value_difference:
elif (d.incoming_rate == rate) and not stock_queue and d.qty and d.stock_value_difference:
continue
d.incoming_rate = flt(rate)
if d.qty:
d.stock_value_difference = flt(d.qty) * d.incoming_rate
if stock_queue and valuation_method == "FIFO" and d.batch_no in batches:
stock_queue.append([d.qty, d.incoming_rate])
d.stock_queue = json.dumps(stock_queue)
if save:
d.db_set(
{"incoming_rate": d.incoming_rate, "stock_value_difference": d.stock_value_difference}
{
"incoming_rate": d.incoming_rate,
"stock_value_difference": d.stock_value_difference,
"stock_queue": d.get("stock_queue"),
}
)
def set_serial_and_batch_values(self, parent, row, qty_field=None):

View File

@@ -285,6 +285,25 @@ class TestSerialandBatchBundle(FrappeTestCase):
self.assertEqual(flt(sle.stock_value_difference), 1000.00 * -1)
self.assertEqual(json.loads(sle.stock_queue), [[20, 200]])
se = make_stock_entry(
item_code=batch_item_code,
target="_Test Warehouse - _TC",
qty=10,
rate=100,
batch_no=batch_id,
use_serial_batch_fields=True,
)
sle = frappe.db.get_value(
"Serial and Batch Entry",
{"parent": se.items[0].serial_and_batch_bundle, "docstatus": 1},
["stock_value_difference", "stock_queue"],
as_dict=True,
)
self.assertEqual(flt(sle.stock_value_difference), 1000.00)
self.assertEqual(json.loads(sle.stock_queue), [[20, 200], [10, 100]])
se = make_stock_entry(
item_code=batch_item_code,
target="_Test Warehouse - _TC",
@@ -301,7 +320,7 @@ class TestSerialandBatchBundle(FrappeTestCase):
)
self.assertEqual(flt(sle.stock_value_difference), 1000.00)
self.assertEqual(json.loads(sle.stock_queue), [[20, 200]])
self.assertEqual(json.loads(sle.stock_queue), [[20, 200], [10, 100]])
se = make_stock_entry(
item_code=batch_item_code,
@@ -319,6 +338,24 @@ class TestSerialandBatchBundle(FrappeTestCase):
self.assertEqual(flt(sle.stock_value_difference), 5000.00 * -1)
self.assertFalse(json.loads(sle.stock_queue or "[]"))
self.assertEqual(flt(sle.stock_value), 1000.0)
se = make_stock_entry(
item_code=batch_item_code,
source="_Test Warehouse - _TC",
qty=10,
use_serial_batch_fields=False,
)
sle = frappe.db.get_value(
"Stock Ledger Entry",
{"item_code": batch_item_code, "is_cancelled": 0, "voucher_no": se.name},
["stock_value_difference", "stock_queue", "stock_value"],
as_dict=True,
)
self.assertEqual(flt(sle.stock_value_difference), 1000.00 * -1)
self.assertFalse(json.loads(sle.stock_queue or "[]"))
self.assertEqual(flt(sle.stock_value), 0.0)
def test_old_serial_no_valuation(self):

View File

@@ -1019,7 +1019,9 @@ class update_entries_after:
)
else:
doc = frappe.get_doc("Serial and Batch Bundle", sle.serial_and_batch_bundle)
doc.set_incoming_rate(save=True, allow_negative_stock=self.allow_negative_stock)
doc.set_incoming_rate(
save=True, allow_negative_stock=self.allow_negative_stock, prev_sle=self.wh_data
)
doc.calculate_qty_and_amount(save=True)
if stock_queue := frappe.get_all(