From d378e514926fe6b292cf6e92c0824211435cbb0f Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 21 Jul 2025 17:35:56 +0530 Subject: [PATCH] fix: valuation for rejected materials (cherry picked from commit b7039cc506b61249e4667b8f38ec80dd2928fb6c) # Conflicts: # erpnext/controllers/buying_controller.py --- erpnext/controllers/buying_controller.py | 42 +++++++++++++++++++ .../purchase_receipt/purchase_receipt.py | 18 +++++--- .../purchase_receipt/test_purchase_receipt.py | 41 ++++++++++++++++++ 3 files changed, 96 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 73d8a42c505..18c8e0243e7 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -324,11 +324,41 @@ class BuyingController(SubcontractingController): valuation_amount_adjustment = total_valuation_amount for i, item in enumerate(self.get("items")): +<<<<<<< HEAD if item.item_code and item.qty and item.item_code in stock_and_asset_items: item_proportion = ( flt(item.base_net_amount) / stock_and_asset_items_amount if stock_and_asset_items_amount else flt(item.qty) / stock_and_asset_items_qty +======= + if item.item_code and (item.qty or item.get("rejected_qty")): + item_tax_amount, actual_tax_amount = 0.0, 0.0 + if i == (last_item_idx - 1): + item_tax_amount = total_valuation_amount + actual_tax_amount = total_actual_tax_amount + else: + # calculate item tax amount + item_tax_amount = self.get_item_tax_amount(item, tax_accounts) + total_valuation_amount -= item_tax_amount + + if total_actual_tax_amount: + actual_tax_amount = self.get_item_actual_tax_amount( + item, + total_actual_tax_amount, + stock_and_asset_items_amount, + stock_and_asset_items_qty, + ) + total_actual_tax_amount -= actual_tax_amount + + # This code is required here to calculate the correct valuation for stock items + if item.item_code not in stock_and_asset_items: + item.valuation_rate = 0.0 + continue + + # Item tax amount is the total tax amount applied on that item and actual tax type amount + item.item_tax_amount = flt( + item_tax_amount + actual_tax_amount, self.precision("item_tax_amount", item) +>>>>>>> b7039cc506 (fix: valuation for rejected materials) ) if i == (last_item_idx - 1): @@ -351,7 +381,19 @@ class BuyingController(SubcontractingController): if item.sales_incoming_rate: # for internal transfer net_rate = item.qty * item.sales_incoming_rate + if ( + not net_rate + and item.get("rejected_qty") + and frappe.get_single_value( + "Buying Settings", "set_valuation_rate_for_rejected_materials" + ) + ): + net_rate = item.rejected_qty * item.net_rate + qty_in_stock_uom = flt(item.qty * item.conversion_factor) + if not qty_in_stock_uom and item.get("rejected_qty"): + qty_in_stock_uom = flt(item.rejected_qty * item.conversion_factor) + if self.get("is_old_subcontracting_flow"): item.rm_supp_cost = self.get_supplied_items_cost(item.name, reset_outgoing_rate) item.valuation_rate = ( diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index f38375f943f..d9597fe413a 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -668,6 +668,10 @@ class PurchaseReceipt(BuyingController): warehouse_with_no_account = [] for d in self.get("items"): + remarks = self.get("remarks") or _("Accounting Entry for {0}").format( + "Asset" if d.is_fixed_asset else "Stock" + ) + if ( provisional_accounting_for_non_stock_items and d.item_code not in stock_items @@ -679,10 +683,6 @@ class PurchaseReceipt(BuyingController): d, gl_entries, self.posting_date, d.get("provisional_expense_account") ) elif flt(d.qty) and (flt(d.valuation_rate) or self.is_return): - remarks = self.get("remarks") or _("Accounting Entry for {0}").format( - "Asset" if d.is_fixed_asset else "Stock" - ) - if not ( (erpnext.is_perpetual_inventory_enabled(self.company) and d.item_code in stock_items) or (d.is_fixed_asset and not d.purchase_invoice) @@ -737,7 +737,7 @@ class PurchaseReceipt(BuyingController): make_amount_difference_entry(d) make_sub_contracting_gl_entries(d) make_divisional_loss_gl_entry(d, outgoing_amount) - elif (d.warehouse and d.warehouse not in warehouse_with_no_account) or ( + elif (d.warehouse and d.qty and d.warehouse not in warehouse_with_no_account) or ( not frappe.db.get_single_value("Buying Settings", "set_valuation_rate_for_rejected_materials") and d.rejected_warehouse and d.rejected_warehouse not in warehouse_with_no_account @@ -750,10 +750,18 @@ class PurchaseReceipt(BuyingController): if d.rejected_qty and frappe.db.get_single_value( "Buying Settings", "set_valuation_rate_for_rejected_materials" ): + stock_asset_rbnb = ( + self.get_company_default("asset_received_but_not_billed") + if d.is_fixed_asset + else self.get_company_default("stock_received_but_not_billed") + ) + stock_value_diff = get_stock_value_difference(self.name, d.name, d.rejected_warehouse) stock_asset_account_name = warehouse_account[d.rejected_warehouse]["account"] make_item_asset_inward_gl_entry(d, stock_value_diff, stock_asset_account_name) + if not d.qty: + make_stock_received_but_not_billed_entry(d) if warehouse_with_no_account: frappe.msgprint( diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index ae762093182..00fab425397 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -4261,6 +4261,47 @@ class TestPurchaseReceipt(FrappeTestCase): frappe.db.set_single_value("Buying Settings", "set_valuation_rate_for_rejected_materials", 0) + def test_valuation_rate_for_rejected_materials_withoout_accepted_materials(self): + item = make_item("Test Item with Rej Material Valuation WO Accepted", {"is_stock_item": 1}) + company = "_Test Company with perpetual inventory" + + warehouse = create_warehouse( + "_Test In-ward Warehouse", + company="_Test Company with perpetual inventory", + ) + + rej_warehouse = create_warehouse( + "_Test Warehouse - Rejected Material", + company="_Test Company with perpetual inventory", + ) + + frappe.db.set_single_value("Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice", 1) + + frappe.db.set_single_value("Buying Settings", "set_valuation_rate_for_rejected_materials", 1) + + pr = make_purchase_receipt( + item_code=item.name, + qty=0, + rate=100, + company=company, + warehouse=warehouse, + rejected_qty=5, + rejected_warehouse=rej_warehouse, + ) + + gl_entry = frappe.get_all( + "GL Entry", filters={"debit": (">", 0), "voucher_no": pr.name}, pluck="name" + ) + + stock_value_diff = frappe.db.get_value( + "Stock Ledger Entry", + {"warehouse": rej_warehouse, "voucher_no": pr.name}, + "stock_value_difference", + ) + + self.assertTrue(gl_entry) + self.assertEqual(stock_value_diff, 500.00) + def test_no_valuation_rate_for_rejected_materials(self): item = make_item("Test Item with Rej Material No Valuation", {"is_stock_item": 1}) company = "_Test Company with perpetual inventory"