From 26ad6885845962130cfb178fcbdbe2c4e75ce194 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 11 Oct 2023 18:44:32 +0530 Subject: [PATCH] fix: negative valuation rate in PR return (#37424) * fix: negative valuation rate in PR return * test: add test case for PR return --- erpnext/controllers/buying_controller.py | 20 +++++-- .../purchase_receipt/test_purchase_receipt.py | 54 +++++++++++++++++++ erpnext/stock/stock_ledger.py | 18 ++++--- 3 files changed, 80 insertions(+), 12 deletions(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 01990a3a268d..a38905c7e2bd 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -14,7 +14,8 @@ from erpnext.controllers.sales_and_purchase_return import get_rate_for_return from erpnext.controllers.subcontracting_controller import SubcontractingController from erpnext.stock.get_item_details import get_conversion_factor -from erpnext.stock.utils import get_incoming_rate +from erpnext.stock.stock_ledger import get_previous_sle +from erpnext.stock.utils import get_incoming_rate, get_valuation_method class QtyMismatchError(ValidationError): @@ -514,9 +515,20 @@ def update_stock_ledger(self, allow_negative_stock=False, via_landed_cost_vouche ) if self.is_return: - outgoing_rate = get_rate_for_return( - self.doctype, self.name, d.item_code, self.return_against, item_row=d - ) + if get_valuation_method(d.item_code) == "Moving Average": + previous_sle = get_previous_sle( + { + "item_code": d.item_code, + "warehouse": d.warehouse, + "posting_date": self.posting_date, + "posting_time": self.posting_time, + } + ) + outgoing_rate = flt(previous_sle.get("valuation_rate")) + else: + outgoing_rate = get_rate_for_return( + self.doctype, self.name, d.item_code, self.return_against, item_row=d + ) sle.update({"outgoing_rate": outgoing_rate, "recalculate_rate": 1}) if d.from_warehouse: diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 82694a0b1929..463353e25495 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -2093,6 +2093,60 @@ def test_purchase_return_status_with_debit_note(self): return_pr.reload() self.assertEqual(return_pr.status, "Completed") + def test_valuation_rate_in_return_purchase_receipt_for_moving_average(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + from erpnext.stock.stock_ledger import get_previous_sle + + # Step - 1: Create an Item (Valuation Method = Moving Average) + item_code = make_item(properties={"is_stock_item": 1, "valuation_method": "Moving Average"}).name + + # Step - 2: Create a Purchase Receipt (Qty = 10, Rate = 100) + pr = make_purchase_receipt(qty=10, rate=100, item_code=item_code) + + # Step - 3: Create a Material Receipt Stock Entry (Qty = 100, Basic Rate = 10) + warehouse = "_Test Warehouse - _TC" + make_stock_entry( + purpose="Material Receipt", + item_code=item_code, + to_warehouse=warehouse, + qty=100, + rate=10, + ) + + # Step - 4: Create a Material Issue Stock Entry (Qty = 100, Basic Rate = 18.18 [Auto Fetched]) + make_stock_entry( + purpose="Material Issue", item_code=item_code, from_warehouse=warehouse, qty=100 + ) + + # Step - 5: Create a Return Purchase Return (Qty = -8, Rate = 100 [Auto fetched]) + return_pr = make_purchase_receipt( + is_return=1, + return_against=pr.name, + item_code=item_code, + qty=-8, + ) + + sle = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_no": return_pr.name, "voucher_detail_no": return_pr.items[0].name}, + ["posting_date", "posting_time", "outgoing_rate", "valuation_rate"], + as_dict=1, + ) + previous_sle_valuation_rate = get_previous_sle( + { + "item_code": item_code, + "warehouse": warehouse, + "posting_date": sle.posting_date, + "posting_time": sle.posting_time, + } + ).get("valuation_rate") + + # Test - 1: Valuation Rate should be equal to Outgoing Rate + self.assertEqual(flt(sle.outgoing_rate, 2), flt(sle.valuation_rate, 2)) + + # Test - 2: Valuation Rate should be equal to Previous SLE Valuation Rate + self.assertEqual(flt(sle.valuation_rate, 2), flt(previous_sle_valuation_rate, 2)) + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 108d36a490d9..13bbe1f5c08c 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -698,14 +698,16 @@ def get_incoming_outgoing_rate_from_transaction(self, sle): get_rate_for_return, # don't move this import to top ) - rate = get_rate_for_return( - sle.voucher_type, - sle.voucher_no, - sle.item_code, - voucher_detail_no=sle.voucher_detail_no, - sle=sle, - ) - + if self.valuation_method == "Moving Average": + rate = self.data[self.args.warehouse].previous_sle.valuation_rate + else: + rate = get_rate_for_return( + sle.voucher_type, + sle.voucher_no, + sle.item_code, + voucher_detail_no=sle.voucher_detail_no, + sle=sle, + ) elif ( sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"] and sle.voucher_detail_no