fix: block invoicing Lost or expired quotations

This commit is contained in:
Shllokkk
2026-10-29 00:12:24 +05:30
parent 85739b57e2
commit d04f1549e8
3 changed files with 37 additions and 12 deletions
@@ -416,7 +416,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
],
get_query_filters: {
docstatus: 1,
status: ["!=", "Lost"],
status: ["not in", ["Lost", "Expired"]],
company: me.frm.doc.company,
},
allow_child_item_selection: true,
+18 -11
View File
@@ -383,19 +383,22 @@ def get_list_context(context=None):
return list_context
def validate_quotation_expiry(source_name):
if frappe.db.get_singles_value("Selling Settings", "allow_sales_order_creation_for_expired_quotation"):
return
quotation = frappe.db.get_value("Quotation", source_name, ["transaction_date", "valid_till"], as_dict=1)
if (
quotation
and quotation.valid_till
and (quotation.valid_till < quotation.transaction_date or quotation.valid_till < getdate(nowdate()))
):
frappe.throw(_("Validity period of this quotation has ended."))
@frappe.whitelist()
def make_sales_order(source_name: str, target_doc=None, args=None):
if not frappe.db.get_singles_value(
"Selling Settings", "allow_sales_order_creation_for_expired_quotation"
):
quotation = frappe.db.get_value(
"Quotation", source_name, ["transaction_date", "valid_till"], as_dict=1
)
if quotation.valid_till and (
quotation.valid_till < quotation.transaction_date or quotation.valid_till < getdate(nowdate())
):
frappe.throw(_("Validity period of this quotation has ended."))
validate_quotation_expiry(source_name)
return _make_sales_order(source_name, target_doc, args=args)
@@ -540,6 +543,10 @@ def set_expired_status():
@frappe.whitelist()
def make_sales_invoice(source_name, target_doc=None, args=None):
if frappe.db.get_value("Quotation", source_name, "status") == "Lost":
frappe.throw(_("Cannot create a Sales Invoice against Lost Quotation {0}.").format(source_name))
validate_quotation_expiry(source_name)
return _make_sales_invoice(source_name, target_doc, args=args)
@@ -1201,6 +1201,24 @@ class TestQuotation(FrappeTestCase):
quotation.reload()
self.assertEqual(quotation.status, "Partially Ordered")
def test_lost_or_expired_quotation_cannot_be_invoiced(self):
from erpnext.selling.doctype.quotation.quotation import make_sales_invoice
if not frappe.db.exists("Quotation Lost Reason", "_Test Quotation Lost Reason"):
frappe.get_doc(
{"doctype": "Quotation Lost Reason", "order_lost_reason": "_Test Quotation Lost Reason"}
).insert()
lost = make_quotation()
lost.declare_enquiry_lost([{"lost_reason": "_Test Quotation Lost Reason"}], [])
self.assertRaises(frappe.ValidationError, make_sales_invoice, lost.name)
expired = make_quotation(do_not_submit=1)
expired.transaction_date = add_days(nowdate(), -10)
expired.valid_till = add_days(nowdate(), -5)
expired.submit()
self.assertRaises(frappe.ValidationError, make_sales_invoice, expired.name)
test_records = frappe.get_test_records("Quotation")