Merge pull request #56335 from aerele/fix/skip-over-allowance-for-non-stock-items

fix: skip over-allowance qty validation for non-stock items
This commit is contained in:
Mihir Kandoi
2026-06-23 19:14:42 +05:30
committed by GitHub
3 changed files with 78 additions and 4 deletions

View File

@@ -3046,6 +3046,24 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
# Test 4 - Since this PI is overbilled by 130% and only 120% is allowed, it will fail
self.assertRaises(frappe.ValidationError, pi.submit)
@ERPNextTestSuite.change_settings("Accounts Settings", {"over_billing_allowance": 0})
def test_non_stock_item_over_billing_against_po_is_blocked(self):
service_item = create_item(
"_Test Service Item Non Stock PI",
is_stock_item=0,
is_purchase_item=1,
).name
po = create_purchase_order(item_code=service_item, qty=5, rate=100, do_not_save=False)
po.submit()
pi = make_pi_from_po(po.name)
pi.items[0].qty = 10 # overbill by 100 %
pi.save()
with self.assertRaises(frappe.ValidationError):
pi.submit()
def test_discount_percentage_not_set_when_amount_is_manually_set(self):
pi = make_purchase_invoice(do_not_save=True)
discount_amount = 7

View File

@@ -4126,6 +4126,51 @@ class TestSalesInvoice(ERPNextTestSuite):
self.assertIn("cannot overbill", str(err.exception).lower())
dn.cancel()
@ERPNextTestSuite.change_settings("Accounts Settings", {"over_billing_allowance": 0})
def test_non_stock_item_over_billing_against_so_is_blocked(self):
from erpnext.selling.doctype.sales_order.mapper import make_sales_invoice as make_si_from_so
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
service_item = create_item(
"_Test Service Item Non Stock SI",
is_stock_item=0,
).name
so = make_sales_order(item_code=service_item, qty=5, rate=100)
so.submit()
si = make_si_from_so(so.name)
si.items[0].qty = 10 # overbill by 100 %
si.save()
with self.assertRaises(frappe.ValidationError):
si.submit()
@ERPNextTestSuite.change_settings("Accounts Settings", {"over_billing_allowance": 0})
def test_non_stock_item_over_billing_against_so_from_quotation_is_blocked(self):
from erpnext.selling.doctype.quotation.mapper import make_sales_order as make_so_from_quotation
from erpnext.selling.doctype.quotation.test_quotation import make_quotation
from erpnext.selling.doctype.sales_order.mapper import make_sales_invoice as make_si_from_so
service_item = create_item(
"_Test Service Item Non Stock SI Quot",
is_stock_item=0,
).name
quotation = make_quotation(item_code=service_item, qty=5, rate=100)
so = make_so_from_quotation(quotation.name)
so.delivery_date = frappe.utils.add_days(frappe.utils.today(), 7)
so.insert()
so.submit()
si = make_si_from_so(so.name)
si.items[0].qty = 10 # overbill by 100 %
si.save()
with self.assertRaises(frappe.ValidationError):
si.submit()
@ERPNextTestSuite.change_settings(
"Accounts Settings",
{

View File

@@ -384,15 +384,17 @@ class StatusUpdater(Document):
def fetch_items_with_pending_qty(self, args, item_field, items):
doctype = frappe.qb.DocType(args["target_dt"])
item_field = doctype[item_field]
item_field_col = doctype[item_field]
target_ref_field = doctype[args["target_ref_field"]]
target_field = doctype[args["target_field"]]
return (
is_qty_check = "qty" in args["target_ref_field"]
query = (
frappe.qb.from_(doctype)
.select(
doctype.name,
item_field.as_("item_code"),
item_field_col.as_("item_code"),
target_ref_field,
target_field,
doctype.parenttype,
@@ -401,9 +403,18 @@ class StatusUpdater(Document):
.where(target_ref_field < target_field)
.where(doctype.name.isin(items))
.where(doctype.docstatus == 1)
.run(as_dict=True)
)
if is_qty_check:
item_table = frappe.qb.DocType("Item")
query = (
query.join(item_table)
.on(item_table.name == item_field_col)
.where(item_table.is_stock_item == 1)
)
return query.run(as_dict=True)
def check_overflow_with_allowance(self, item, args):
"""
Checks if there is overflow considering a relaxation allowance.