mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-25 13:03:02 +00:00
fix: prevent duplicate supplier quotations from portal (#58377)
This commit is contained in:
@@ -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):
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user