From b06eca8dcb3d553fd1e04020dbaf72b0f4ce0969 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 23 Jun 2025 14:28:28 +0530 Subject: [PATCH] fix: incoming rate for the stand-alone credit note --- .../sales_invoice/test_sales_invoice.py | 89 +++++++++++++++++++ .../controllers/sales_and_purchase_return.py | 8 ++ erpnext/controllers/selling_controller.py | 9 ++ 3 files changed, 106 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 8124f1b95e5..38f769d9941 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -4442,6 +4442,94 @@ class TestSalesInvoice(ERPNextTestSuite): pos.append("payments", {"mode_of_payment": "Cash", "amount": 1000}) self.assertRaises(frappe.ValidationError, pos.insert) + def test_stand_alone_credit_note_valuation(self): + from erpnext.stock.doctype.item.test_item import make_item + + item_code = "_Test Item for Credit Note Valuation" + make_item_for_si( + item_code, + { + "is_stock_item": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "BATCH-TCNV.####", + }, + ) + + si = create_sales_invoice( + item=item_code, + qty=-2, + rate=1200, + is_return=1, + update_stock=1, + ) + + stock_ledger_entry = frappe.db.get_value( + "Stock Ledger Entry", + { + "voucher_type": "Sales Invoice", + "voucher_no": si.name, + "item_code": item_code, + "warehouse": "_Test Warehouse - _TC", + }, + ["incoming_rate", "valuation_rate", "actual_qty as qty", "stock_value_difference"], + as_dict=True, + ) + + self.assertEqual(stock_ledger_entry.incoming_rate, 1200.0) + self.assertEqual(stock_ledger_entry.valuation_rate, 1200.0) + self.assertEqual(stock_ledger_entry.qty, 2.0) + self.assertEqual(stock_ledger_entry.stock_value_difference, 2400.0) + + def test_stand_alone_credit_note_zero_valuation(self): + from erpnext.stock.doctype.item.test_item import make_item + + item_code = "_Test Item for Credit Note Zero Valuation" + make_item_for_si( + item_code, + { + "is_stock_item": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "BATCH-TCNZV.####", + }, + ) + + si = create_sales_invoice( + item=item_code, + qty=-2, + rate=1200, + is_return=1, + update_stock=1, + allow_zero_valuation_rate=1, + ) + + stock_ledger_entry = frappe.db.get_value( + "Stock Ledger Entry", + { + "voucher_type": "Sales Invoice", + "voucher_no": si.name, + "item_code": item_code, + "warehouse": "_Test Warehouse - _TC", + }, + ["incoming_rate", "valuation_rate", "actual_qty as qty", "stock_value_difference"], + as_dict=True, + ) + + self.assertEqual(stock_ledger_entry.incoming_rate, 0.0) + self.assertEqual(stock_ledger_entry.valuation_rate, 0.0) + self.assertEqual(stock_ledger_entry.qty, 2.0) + self.assertEqual(stock_ledger_entry.stock_value_difference, 0.0) + + +def make_item_for_si(item_code, properties=None): + from erpnext.stock.doctype.item.test_item import make_item + + item = make_item(item_code, properties=properties) + item.is_stock_item = 1 + item.save() + return item + def set_advance_flag(company, flag, default_account): frappe.db.set_value( @@ -4553,6 +4641,7 @@ def create_sales_invoice(**args): "conversion_factor": args.get("conversion_factor", 1), "incoming_rate": args.incoming_rate or 0, "serial_and_batch_bundle": bundle_id, + "allow_zero_valuation_rate": args.allow_zero_valuation_rate or 0, }, ) diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 0a221d9d2c6..0715020c2bd 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -685,6 +685,14 @@ def get_rate_for_return( raise_error_if_no_rate=False, ) + if not rate and voucher_type in ["Sales Invoice", "Delivery Note"]: + details = frappe.db.get_value( + voucher_type + " Item", voucher_detail_no, ["rate", "allow_zero_valuation_rate"], as_dict=1 + ) + + if details and not details.allow_zero_valuation_rate: + rate = flt(details.rate) + return rate diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index bca73736922..34492de794f 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -527,6 +527,15 @@ class SellingController(StockController): self.doctype, self.name, d.item_code, self.return_against, item_row=d ) + if ( + self.get("is_return") + and not d.incoming_rate + and not self.get("return_against") + and not self.is_internal_transfer() + and not d.get("allow_zero_valuation_rate") + ): + d.incoming_rate = d.rate + # For internal transfers use incoming rate as the valuation rate if self.is_internal_transfer(): if self.doctype == "Delivery Note" or self.get("update_stock"):