From 9ab333d10584745e67ff3bcfe3385d890fc1075a Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 25 Jun 2024 12:32:27 +0530 Subject: [PATCH] fix: valuation rate for the legacy batches (#42011) --- erpnext/stock/deprecated_serial_batch.py | 86 ++++++++++++------- .../test_stock_reconciliation.py | 4 +- erpnext/stock/utils.py | 6 +- .../test_subcontracting_receipt.py | 8 +- 4 files changed, 63 insertions(+), 41 deletions(-) diff --git a/erpnext/stock/deprecated_serial_batch.py b/erpnext/stock/deprecated_serial_batch.py index 79ace06d25a..08ac3eea91f 100644 --- a/erpnext/stock/deprecated_serial_batch.py +++ b/erpnext/stock/deprecated_serial_batch.py @@ -1,3 +1,6 @@ +import datetime +from collections import defaultdict + import frappe from frappe.query_builder.functions import CombineDatetime, Sum from frappe.utils import flt @@ -8,12 +11,7 @@ from pypika import Order class DeprecatedSerialNoValuation: @deprecated def calculate_stock_value_from_deprecarated_ledgers(self): - if not frappe.db.get_all( - "Stock Ledger Entry", - fields=["name"], - filters={"serial_no": ("is", "set"), "is_cancelled": 0, "item_code": self.sle.item_code}, - limit=1, - ): + if not has_sle_for_serial_nos(self.sle.item_code): return serial_nos = self.get_filterd_serial_nos() @@ -80,6 +78,20 @@ class DeprecatedSerialNoValuation: return incoming_values +@frappe.request_cache +def has_sle_for_serial_nos(item_code): + serial_nos = frappe.db.get_all( + "Stock Ledger Entry", + fields=["name"], + filters={"serial_no": ("is", "set"), "is_cancelled": 0, "item_code": item_code}, + limit=1, + ) + if serial_nos: + return True + + return False + + class DeprecatedBatchNoValuation: @deprecated def calculate_avg_rate_from_deprecarated_ledgers(self): @@ -90,19 +102,25 @@ class DeprecatedBatchNoValuation: @deprecated def get_sle_for_batches(self): + from erpnext.stock.utils import get_combine_datetime + if not self.batchwise_valuation_batches: return [] sle = frappe.qb.DocType("Stock Ledger Entry") - timestamp_condition = CombineDatetime(sle.posting_date, sle.posting_time) < CombineDatetime( - self.sle.posting_date, self.sle.posting_time - ) - if self.sle.creation: - timestamp_condition |= ( - CombineDatetime(sle.posting_date, sle.posting_time) - == CombineDatetime(self.sle.posting_date, self.sle.posting_time) - ) & (sle.creation < self.sle.creation) + timestamp_condition = None + if self.sle.posting_date and self.sle.posting_time: + posting_datetime = get_combine_datetime(self.sle.posting_date, self.sle.posting_time) + if not self.sle.creation: + posting_datetime = posting_datetime + datetime.timedelta(milliseconds=1) + + timestamp_condition = sle.posting_datetime < posting_datetime + + if self.sle.creation: + timestamp_condition |= (sle.posting_datetime == posting_datetime) & ( + sle.creation < self.sle.creation + ) query = ( frappe.qb.from_(sle) @@ -118,10 +136,12 @@ class DeprecatedBatchNoValuation: & (sle.batch_no.isnotnull()) & (sle.is_cancelled == 0) ) - .where(timestamp_condition) .groupby(sle.batch_no) ) + if timestamp_condition: + query = query.where(timestamp_condition) + if self.sle.name: query = query.where(sle.name != self.sle.name) @@ -132,8 +152,8 @@ class DeprecatedBatchNoValuation: if not self.non_batchwise_valuation_batches: return - self.non_batchwise_balance_value = 0.0 - self.non_batchwise_balance_qty = 0.0 + self.non_batchwise_balance_value = defaultdict(float) + self.non_batchwise_balance_qty = defaultdict(float) self.set_balance_value_for_non_batchwise_valuation_batches() @@ -144,12 +164,12 @@ class DeprecatedBatchNoValuation: if not self.non_batchwise_balance_qty: continue - if self.non_batchwise_balance_value == 0: + if self.non_batchwise_balance_qty.get(batch_no) == 0: self.batch_avg_rate[batch_no] = 0.0 self.stock_value_differece[batch_no] = 0.0 else: self.batch_avg_rate[batch_no] = ( - self.non_batchwise_balance_value / self.non_batchwise_balance_qty + self.non_batchwise_balance_value[batch_no] / self.non_batchwise_balance_qty[batch_no] ) self.stock_value_differece[batch_no] = self.non_batchwise_balance_value @@ -172,17 +192,21 @@ class DeprecatedBatchNoValuation: @deprecated def set_balance_value_from_sl_entries(self) -> None: + from erpnext.stock.utils import get_combine_datetime + sle = frappe.qb.DocType("Stock Ledger Entry") batch = frappe.qb.DocType("Batch") - timestamp_condition = CombineDatetime(sle.posting_date, sle.posting_time) < CombineDatetime( - self.sle.posting_date, self.sle.posting_time - ) + posting_datetime = get_combine_datetime(self.sle.posting_date, self.sle.posting_time) + if not self.sle.creation: + posting_datetime = posting_datetime + datetime.timedelta(milliseconds=1) + + timestamp_condition = sle.posting_datetime < posting_datetime + if self.sle.creation: - timestamp_condition |= ( - CombineDatetime(sle.posting_date, sle.posting_time) - == CombineDatetime(self.sle.posting_date, self.sle.posting_time) - ) & (sle.creation < self.sle.creation) + timestamp_condition |= (sle.posting_datetime == posting_datetime) & ( + sle.creation < self.sle.creation + ) query = ( frappe.qb.from_(sle) @@ -199,6 +223,7 @@ class DeprecatedBatchNoValuation: & (sle.batch_no.isnotnull()) & (batch.use_batchwise_valuation == 0) & (sle.is_cancelled == 0) + & (sle.batch_no.isin(self.non_batchwise_valuation_batches)) ) .where(timestamp_condition) .groupby(sle.batch_no) @@ -208,8 +233,8 @@ class DeprecatedBatchNoValuation: query = query.where(sle.name != self.sle.name) for d in query.run(as_dict=True): - self.non_batchwise_balance_value += flt(d.batch_value) - self.non_batchwise_balance_qty += flt(d.batch_qty) + self.non_batchwise_balance_value[d.batch_no] += flt(d.batch_value) + self.non_batchwise_balance_qty[d.batch_no] += flt(d.batch_qty) self.available_qty[d.batch_no] += flt(d.batch_qty) @deprecated @@ -247,6 +272,7 @@ class DeprecatedBatchNoValuation: & (bundle.is_cancelled == 0) & (bundle.docstatus == 1) & (bundle.type_of_transaction.isin(["Inward", "Outward"])) + & (bundle_child.batch_no.isin(self.non_batchwise_valuation_batches)) ) .where(timestamp_condition) .groupby(bundle_child.batch_no) @@ -258,6 +284,6 @@ class DeprecatedBatchNoValuation: query = query.where(bundle.voucher_type != "Pick List") for d in query.run(as_dict=True): - self.non_batchwise_balance_value += flt(d.batch_value) - self.non_batchwise_balance_qty += flt(d.batch_qty) + self.non_batchwise_balance_value[d.batch_no] += flt(d.batch_value) + self.non_batchwise_balance_qty[d.batch_no] += flt(d.batch_qty) self.available_qty[d.batch_no] += flt(d.batch_qty) diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index a41db6cf611..48d67c2cf46 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -647,7 +647,7 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin): "has_serial_no": 1, "has_batch_no": 1, "serial_no_series": "SRS9.####", - "batch_number_series": "BNS9.####", + "batch_number_series": "BNS90.####", "create_new_batch": 1, }, ) @@ -680,7 +680,7 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin): { "is_stock_item": 1, "has_batch_no": 1, - "batch_number_series": "BNS9.####", + "batch_number_series": "BNS91.####", "create_new_batch": 1, }, ).name diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 64c239adccd..c8941e4bcad 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -276,11 +276,7 @@ def get_incoming_rate(args, raise_error_if_no_rate=True): sn_obj = SerialNoValuation(sle=args, warehouse=args.get("warehouse"), item_code=args.get("item_code")) return sn_obj.get_incoming_rate() - elif ( - args.get("batch_no") - and frappe.db.get_value("Batch", args.get("batch_no"), "use_batchwise_valuation", cache=True) - and not args.get("serial_and_batch_bundle") - ): + elif args.get("batch_no") and not args.get("serial_and_batch_bundle"): args.actual_qty = args.qty args.batch_nos = frappe._dict({args.batch_no: args}) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py index 81662a6257b..0f5fe7ab958 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py @@ -623,8 +623,8 @@ class TestSubcontractingReceipt(FrappeTestCase): "has_batch_no": 1, "has_serial_no": 1, "create_new_batch": 1, - "batch_number_series": "BNGS-.####", - "serial_no_series": "BNSS-.####", + "batch_number_series": "BNGS0-.####", + "serial_no_series": "BNSS90-.####", } ).name @@ -715,8 +715,8 @@ class TestSubcontractingReceipt(FrappeTestCase): "has_batch_no": 1, "has_serial_no": 1, "create_new_batch": 1, - "batch_number_series": "BNGS-.####", - "serial_no_series": "BNSS-.####", + "batch_number_series": "BNGS91-.####", + "serial_no_series": "BNSS91-.####", } ).name