diff --git a/erpnext/buying/doctype/request_for_quotation/mapper.py b/erpnext/buying/doctype/request_for_quotation/mapper.py index 643f4824fe7..e3bb05bc946 100644 --- a/erpnext/buying/doctype/request_for_quotation/mapper.py +++ b/erpnext/buying/doctype/request_for_quotation/mapper.py @@ -58,25 +58,26 @@ def make_supplier_quotation_from_rfq( @frappe.whitelist(methods=["POST"]) def create_supplier_quotation(doc: str | Document | dict): doc = frappe.parse_json(doc) + supplier = doc.get("supplier") - if frappe.session.user not in frappe.get_all( - "Portal User", {"parent": doc.get("supplier")}, pluck="user" - ): + if frappe.session.user not in frappe.get_all("Portal User", {"parent": supplier}, pluck="user"): frappe.throw(_("Not Permitted"), frappe.PermissionError) + validate_existing_supplier_quotation(supplier, doc.get("items")) + sq_doc = frappe.get_doc( { "doctype": "Supplier Quotation", - "supplier": doc.get("supplier"), + "supplier": supplier, "terms": doc.get("terms"), "company": doc.get("company"), "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") 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.run_method("set_missing_values") sq_doc.save() @@ -84,6 +85,45 @@ def create_supplier_quotation(doc: str | Document | dict): 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): for data in items: if isinstance(data, dict): diff --git a/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py index ee4cae18782..14ecd284f96 100644 --- a/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py @@ -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].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): item_code = "_Test Multi UOM RFQ Item" if not frappe.db.exists("Item", item_code): diff --git a/erpnext/templates/pages/rfq.html b/erpnext/templates/pages/rfq.html index 8d55d6b47e1..d2a9382dc6b 100644 --- a/erpnext/templates/pages/rfq.html +++ b/erpnext/templates/pages/rfq.html @@ -13,7 +13,7 @@ {% endblock %} {% block header_actions %} -{% if doc.items %} +{% if doc.items and not doc.rfq_links %}