fix: prevent duplicate supplier quotations from portal (#58377)

This commit is contained in:
Pandiyan P
2026-08-24 18:32:12 +05:30
committed by GitHub
parent 7ec34bab8d
commit ac2bacd3d0
3 changed files with 59 additions and 7 deletions

View File

@@ -58,25 +58,26 @@ def make_supplier_quotation_from_rfq(
@frappe.whitelist(methods=["POST"]) @frappe.whitelist(methods=["POST"])
def create_supplier_quotation(doc: str | Document | dict): def create_supplier_quotation(doc: str | Document | dict):
doc = frappe.parse_json(doc) doc = frappe.parse_json(doc)
supplier = doc.get("supplier")
if frappe.session.user not in frappe.get_all( if frappe.session.user not in frappe.get_all("Portal User", {"parent": supplier}, pluck="user"):
"Portal User", {"parent": doc.get("supplier")}, pluck="user"
):
frappe.throw(_("Not Permitted"), frappe.PermissionError) frappe.throw(_("Not Permitted"), frappe.PermissionError)
validate_existing_supplier_quotation(supplier, doc.get("items"))
sq_doc = frappe.get_doc( sq_doc = frappe.get_doc(
{ {
"doctype": "Supplier Quotation", "doctype": "Supplier Quotation",
"supplier": doc.get("supplier"), "supplier": supplier,
"terms": doc.get("terms"), "terms": doc.get("terms"),
"company": doc.get("company"), "company": doc.get("company"),
"currency": doc.get("currency") "currency": doc.get("currency")
or get_party_account_currency("Supplier", doc.get("supplier"), doc.get("company")), or get_party_account_currency("Supplier", supplier, doc.get("company")),
"buying_price_list": doc.get("buying_price_list") "buying_price_list": doc.get("buying_price_list")
or frappe.db.get_single_value("Buying Settings", "buying_price_list"), or frappe.db.get_single_value("Buying Settings", "buying_price_list"),
} }
) )
add_items(sq_doc, doc.get("supplier"), doc.get("items")) add_items(sq_doc, supplier, doc.get("items"))
sq_doc.flags.ignore_permissions = True sq_doc.flags.ignore_permissions = True
sq_doc.run_method("set_missing_values") sq_doc.run_method("set_missing_values")
sq_doc.save() sq_doc.save()
@@ -84,6 +85,45 @@ def create_supplier_quotation(doc: str | Document | dict):
return sq_doc.name return sq_doc.name
def validate_existing_supplier_quotation(supplier, items):
request_for_quotations = {item.get("parent") for item in items if item.get("parent")}
if not request_for_quotations:
return
rfq = frappe.qb.DocType("Request for Quotation")
(
frappe.qb.from_(rfq)
.select(rfq.name)
.where(rfq.name.isin(request_for_quotations))
.orderby(rfq.name)
.for_update()
).run()
sq = frappe.qb.DocType("Supplier Quotation")
sqi = frappe.qb.DocType("Supplier Quotation Item")
existing_quotation = (
frappe.qb.from_(sq)
.inner_join(sqi)
.on(sq.name == sqi.parent)
.select(sq.name, sqi.request_for_quotation)
.where(
(sq.docstatus < 2)
& (sq.supplier == supplier)
& (sqi.request_for_quotation.isin(request_for_quotations))
)
.limit(1)
).run(as_dict=True)
if existing_quotation:
existing_quotation = existing_quotation[0]
frappe.throw(
_("Supplier Quotation {0} already exists against Request for Quotation {1}").format(
frappe.bold(existing_quotation.name),
frappe.bold(existing_quotation.request_for_quotation),
)
)
def add_items(sq_doc, supplier, items): def add_items(sq_doc, supplier, items):
for data in items: for data in items:
if isinstance(data, dict): if isinstance(data, dict):

View File

@@ -200,6 +200,18 @@ class TestRequestforQuotation(ERPNextTestSuite):
self.assertEqual(supplier_quotation_doc.get("items")[0].qty, 5) self.assertEqual(supplier_quotation_doc.get("items")[0].qty, 5)
self.assertEqual(supplier_quotation_doc.get("items")[0].amount, 500) self.assertEqual(supplier_quotation_doc.get("items")[0].amount, 500)
def test_make_duplicate_supplier_quotation_from_portal(self):
rfq = make_request_for_quotation()
rfq.supplier = rfq.suppliers[0].supplier
supplier_quotation = frappe.get_doc("Supplier Quotation", create_supplier_quotation(rfq))
supplier_quotation.submit()
with self.assertRaisesRegex(frappe.ValidationError, "already exists"):
create_supplier_quotation(rfq)
supplier_quotation.cancel()
self.assertTrue(create_supplier_quotation(rfq))
def test_make_multi_uom_supplier_quotation(self): def test_make_multi_uom_supplier_quotation(self):
item_code = "_Test Multi UOM RFQ Item" item_code = "_Test Multi UOM RFQ Item"
if not frappe.db.exists("Item", item_code): if not frappe.db.exists("Item", item_code):

View File

@@ -13,7 +13,7 @@
{% endblock %} {% endblock %}
{% block header_actions %} {% block header_actions %}
{% if doc.items %} {% if doc.items and not doc.rfq_links %}
<button class="btn btn-primary btn-sm" <button class="btn btn-primary btn-sm"
type="button"> type="button">
{{ _("Make Quotation") }}</button> {{ _("Make Quotation") }}</button>