From 5d4da9c68f8c3ebd528af6099127c2c78397523b Mon Sep 17 00:00:00 2001 From: Kerolles Fathy Date: Mon, 3 Nov 2025 11:46:50 +0200 Subject: [PATCH] fix: Respect allowed zero qty in SO and PO based on Buying/Selling settings when update items (#49673) * fix: respect allowed zero qty in SO and PO based on buying/selling settings * refactor: remove parent_doctype param due to it's already avaliable * refactor: rename is_allowed_zero_qty_for to is_allowed_zero_qty * fix: prevent rate change for unit price items * fix: unboundlocal variable rate_unchanged * refactor: only throw on 'zero' qty items --------- Co-authored-by: ruthra kumar (cherry picked from commit 44363c069e8e05650f259bd3295336977b3668dd) --- erpnext/controllers/accounts_controller.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 101b070a192..6410c28ed19 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -3661,8 +3661,15 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil child_doctype = "Sales Order Item" if parent_doctype == "Sales Order" else "Purchase Order Item" return set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child_docname, item_row) + def is_allowed_zero_qty(): + if parent_doctype == "Sales Order": + return frappe.db.get_single_value("Selling Settings", "allow_zero_qty_in_sales_order") or False + elif parent_doctype == "Purchase Order": + return frappe.db.get_single_value("Buying Settings", "allow_zero_qty_in_purchase_order") or False + return False + def validate_quantity(child_item, new_data): - if not flt(new_data.get("qty")): + if not flt(new_data.get("qty")) and not is_allowed_zero_qty(): frappe.throw( _("Row #{0}: Quantity for Item {1} cannot be zero.").format( new_data.get("idx"), frappe.bold(new_data.get("item_code")) @@ -3798,6 +3805,11 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil conv_fac_precision = child_item.precision("conversion_factor") or 2 qty_precision = child_item.precision("qty") or 2 + prev_rate, new_rate = flt(child_item.get("rate")), flt(d.get("rate")) + rate_unchanged = prev_rate == new_rate + if not rate_unchanged and not child_item.get("qty") and is_allowed_zero_qty(): + frappe.throw(_("Rate of '{}' items cannot be changed").format(frappe.bold(_("Unit Price")))) + # Amount cannot be lesser than billed amount, except for negative amounts row_rate = flt(d.get("rate"), rate_precision) amount_below_billed_amt = flt(child_item.billed_amt, rate_precision) > flt(