feat: allow to set valuation rate for Rejected Materials (backport #47582) (#47869)

feat: allow to set valuation rate for Rejected Materials (#47582)

(cherry picked from commit ca0e53dd78)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
This commit is contained in:
mergify[bot]
2025-06-03 16:25:53 +05:30
committed by GitHub
parent 2815a0d827
commit 3582b32f03
5 changed files with 190 additions and 12 deletions

View File

@@ -12,21 +12,25 @@
"column_break_4",
"maintain_same_rate_action",
"role_to_override_stop_action",
"transaction_settings_section",
"section_break_xmlt",
"po_required",
"pr_required",
"blanket_order_allowance",
"column_break_sbwq",
"pr_required",
"project_update_frequency",
"column_break_12",
"maintain_same_rate",
"transaction_settings_section",
"column_break_fcyl",
"set_landed_cost_based_on_purchase_invoice_rate",
"allow_multiple_items",
"bill_for_rejected_quantity_in_purchase_invoice",
"disable_last_purchase_rate",
"show_pay_button",
"allow_zero_qty_in_supplier_quotation",
"use_transaction_date_exchange_rate",
"allow_zero_qty_in_request_for_quotation",
"allow_zero_qty_in_supplier_quotation",
"column_break_12",
"maintain_same_rate",
"allow_multiple_items",
"bill_for_rejected_quantity_in_purchase_invoice",
"set_valuation_rate_for_rejected_materials",
"disable_last_purchase_rate",
"show_pay_button",
"allow_zero_qty_in_purchase_order",
"subcontract",
"backflush_raw_materials_of_subcontract_based_on",
@@ -231,6 +235,26 @@
"fieldname": "allow_zero_qty_in_supplier_quotation",
"fieldtype": "Check",
"label": "Allow Supplier Quotation with Zero Quantity"
},
{
"fieldname": "section_break_xmlt",
"fieldtype": "Section Break"
},
{
"fieldname": "column_break_sbwq",
"fieldtype": "Column Break"
},
{
"fieldname": "column_break_fcyl",
"fieldtype": "Column Break"
},
{
"default": "0",
"depends_on": "bill_for_rejected_quantity_in_purchase_invoice",
"description": "If enabled, the system will generate an accounting entry for materials rejected in the Purchase Receipt.",
"fieldname": "set_valuation_rate_for_rejected_materials",
"fieldtype": "Check",
"label": "Set Valuation Rate for Rejected Materials"
}
],
"grid_page_length": 50,
@@ -239,7 +263,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2025-05-06 15:21:49.639642",
"modified": "2025-05-16 15:56:38.321369",
"modified_by": "Administrator",
"module": "Buying",
"name": "Buying Settings",

View File

@@ -38,6 +38,7 @@ class BuyingSettings(Document):
project_update_frequency: DF.Literal["Each Transaction", "Manual"]
role_to_override_stop_action: DF.Link | None
set_landed_cost_based_on_purchase_invoice_rate: DF.Check
set_valuation_rate_for_rejected_materials: DF.Check
show_pay_button: DF.Check
supp_master_name: DF.Literal["Supplier Name", "Naming Series", "Auto Name"]
supplier_group: DF.Link | None
@@ -57,6 +58,9 @@ class BuyingSettings(Document):
hide_name_field=False,
)
if not self.bill_for_rejected_quantity_in_purchase_invoice:
self.set_valuation_rate_for_rejected_materials = 0
def before_save(self):
self.check_maintain_same_rate()

View File

@@ -658,6 +658,10 @@ class BuyingController(SubcontractingController):
sl_entries.append(from_warehouse_sle)
if flt(d.rejected_qty) != 0:
valuation_rate_for_rejected_item = 0.0
if frappe.db.get_single_value("Buying Settings", "set_valuation_rate_for_rejected_materials"):
valuation_rate_for_rejected_item = d.valuation_rate
sl_entries.append(
self.get_sl_entries(
d,
@@ -666,7 +670,7 @@ class BuyingController(SubcontractingController):
"actual_qty": flt(
flt(d.rejected_qty) * flt(d.conversion_factor), d.precision("stock_qty")
),
"incoming_rate": 0.0,
"incoming_rate": valuation_rate_for_rejected_item,
"serial_and_batch_bundle": d.rejected_serial_and_batch_bundle,
},
)

View File

@@ -496,6 +496,14 @@ class PurchaseReceipt(BuyingController):
outgoing_amount = abs(get_stock_value_difference(self.name, item.name, item.from_warehouse))
credit_amount = outgoing_amount
if item.get("rejected_qty") and frappe.db.get_single_value(
"Buying Settings", "set_valuation_rate_for_rejected_materials"
):
outgoing_amount += abs(
get_stock_value_difference(self.name, item.name, item.rejected_warehouse)
)
credit_amount = outgoing_amount
if credit_amount:
if not account:
validate_account("Stock or Asset Received But Not Billed")
@@ -629,6 +637,14 @@ class PurchaseReceipt(BuyingController):
valuation_amount_as_per_doc - flt(stock_value_diff), item.precision("base_net_amount")
)
if item.get("rejected_qty") and frappe.db.get_single_value(
"Buying Settings", "set_valuation_rate_for_rejected_materials"
):
rejected_item_cost = abs(
get_stock_value_difference(self.name, item.name, item.rejected_warehouse)
)
divisional_loss -= rejected_item_cost
if divisional_loss:
loss_account = (
self.get_company_default("default_expense_account", ignore_validation=True)
@@ -726,13 +742,23 @@ class PurchaseReceipt(BuyingController):
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 (
d.rejected_warehouse and d.rejected_warehouse not in warehouse_with_no_account
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
):
warehouse_with_no_account.append(d.warehouse or d.rejected_warehouse)
if d.is_fixed_asset and d.landed_cost_voucher_amount:
self.update_assets(d, d.valuation_rate)
if d.rejected_qty and frappe.db.get_single_value(
"Buying Settings", "set_valuation_rate_for_rejected_materials"
):
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 warehouse_with_no_account:
frappe.msgprint(
_("No accounting entries for the following warehouses")

View File

@@ -4199,6 +4199,126 @@ class TestPurchaseReceipt(FrappeTestCase):
# Test 3 - OverAllowanceError should be thrown as qty is greater than qty in DN
self.assertRaises(erpnext.controllers.status_updater.OverAllowanceError, pr.submit)
def test_valuation_rate_for_rejected_materials(self):
item = make_item("Test Item with Rej Material Valuation", {"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=10,
rate=100,
company=company,
warehouse=warehouse,
rejected_qty=5,
rejected_warehouse=rej_warehouse,
)
stock_received_but_not_billed_account = frappe.get_value(
"Company",
company,
"stock_received_but_not_billed",
)
rejected_item_cost = frappe.db.get_value(
"Stock Ledger Entry",
{
"voucher_type": "Purchase Receipt",
"voucher_no": pr.name,
"warehouse": rej_warehouse,
},
"stock_value_difference",
)
self.assertEqual(rejected_item_cost, 500)
srbnb_cost = frappe.db.get_value(
"GL Entry",
{
"voucher_type": "Purchase Receipt",
"voucher_no": pr.name,
"account": stock_received_but_not_billed_account,
},
"credit",
)
self.assertEqual(srbnb_cost, 1500)
frappe.db.set_single_value("Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice", 0)
frappe.db.set_single_value("Buying Settings", "set_valuation_rate_for_rejected_materials", 0)
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"
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", 0)
frappe.db.set_single_value("Buying Settings", "set_valuation_rate_for_rejected_materials", 0)
pr = make_purchase_receipt(
item_code=item.name,
qty=10,
rate=100,
company=company,
warehouse=warehouse,
rejected_qty=5,
rejected_warehouse=rej_warehouse,
)
stock_received_but_not_billed_account = frappe.get_value(
"Company",
company,
"stock_received_but_not_billed",
)
rejected_item_cost = frappe.db.get_value(
"Stock Ledger Entry",
{
"voucher_type": "Purchase Receipt",
"voucher_no": pr.name,
"warehouse": rej_warehouse,
},
"stock_value_difference",
)
self.assertEqual(rejected_item_cost, 0.0)
srbnb_cost = frappe.db.get_value(
"GL Entry",
{
"voucher_type": "Purchase Receipt",
"voucher_no": pr.name,
"account": stock_received_but_not_billed_account,
},
"credit",
)
self.assertEqual(srbnb_cost, 1000)
def prepare_data_for_internal_transfer():
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier