From 678a1179d4013d612e583c00abe80b06c830872e Mon Sep 17 00:00:00 2001 From: Devin Slauenwhite Date: Thu, 30 Dec 2021 13:06:52 -0500 Subject: [PATCH 1/8] fix: shopping cart quotation without website item --- erpnext/selling/doctype/quotation/quotation.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 61c0b8af4e..92b7fee774 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -27,6 +27,7 @@ class Quotation(SellingController): self.set_status() self.validate_uom_is_integer("stock_uom", "qty") self.validate_valid_till() + self.validate_shopping_cart_items() self.set_customer_name() if self.items: self.with_items = 1 @@ -49,6 +50,13 @@ class Quotation(SellingController): if self.valid_till and getdate(self.valid_till) < getdate(self.transaction_date): frappe.throw(_("Valid till date cannot be before transaction date")) + def validate_shopping_cart_items(self): + if self.order_type != "Shopping Cart": return + + for item in self.items: + if not frappe.db.exists("Website Item", {"item_code": item.item_code}): + frappe.throw(_("Item {0} must be a website item for Shopping Cart quotations".format(item.item_code))) + def has_sales_order(self): return frappe.db.get_value("Sales Order Item", {"prevdoc_docname": self.name, "docstatus": 1}) From 86832fb7fe53e9b48fe25e62c20f5e602215743e Mon Sep 17 00:00:00 2001 From: Devin Slauenwhite Date: Thu, 30 Dec 2021 12:38:18 -0500 Subject: [PATCH 2/8] test: assert error if quotation contains non website item --- erpnext/selling/doctype/quotation/test_quotation.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py index b44fa5e551..54ff4336f1 100644 --- a/erpnext/selling/doctype/quotation/test_quotation.py +++ b/erpnext/selling/doctype/quotation/test_quotation.py @@ -129,7 +129,20 @@ class TestQuotation(FrappeTestCase): quotation.insert() quotation.submit() self.assertRaises(frappe.ValidationError, make_sales_order, quotation.name) + + def test_shopping_cart_without_website_item(self): + from erpnext.selling.doctype.quotation.quotation import make_sales_order + if frappe.db.exists('Website Item', {'item_code': '_Test Item Home Desktop 100'}): + frappe.get_last_doc('Website Item', {'item_code': '_Test Item Home Desktop 100'}).delete() + + quotation = frappe.copy_doc(test_records[0]) + quotation.order_type = 'Shopping Cart' + quotation.valid_till = getdate() + quotation.insert() + quotation.submit() + self.assertRaises(frappe.ValidationError, make_sales_order, quotation.name) + def test_create_quotation_with_margin(self): from erpnext.selling.doctype.quotation.quotation import make_sales_order from erpnext.selling.doctype.sales_order.sales_order import ( From b88d5d135ef2f8882b135551a59214c9199a6e35 Mon Sep 17 00:00:00 2001 From: Devin Slauenwhite Date: Thu, 30 Dec 2021 13:05:54 -0500 Subject: [PATCH 3/8] fix(test): validate exception without website item --- erpnext/selling/doctype/quotation/test_quotation.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py index 54ff4336f1..33d2a81373 100644 --- a/erpnext/selling/doctype/quotation/test_quotation.py +++ b/erpnext/selling/doctype/quotation/test_quotation.py @@ -129,20 +129,18 @@ class TestQuotation(FrappeTestCase): quotation.insert() quotation.submit() self.assertRaises(frappe.ValidationError, make_sales_order, quotation.name) - + def test_shopping_cart_without_website_item(self): from erpnext.selling.doctype.quotation.quotation import make_sales_order if frappe.db.exists('Website Item', {'item_code': '_Test Item Home Desktop 100'}): frappe.get_last_doc('Website Item', {'item_code': '_Test Item Home Desktop 100'}).delete() - + quotation = frappe.copy_doc(test_records[0]) quotation.order_type = 'Shopping Cart' quotation.valid_till = getdate() - quotation.insert() - quotation.submit() - self.assertRaises(frappe.ValidationError, make_sales_order, quotation.name) - + self.assertRaises(frappe.ValidationError, quotation.validate) + def test_create_quotation_with_margin(self): from erpnext.selling.doctype.quotation.quotation import make_sales_order from erpnext.selling.doctype.sales_order.sales_order import ( From 48da3b4482cce05f184190bd98263c906913a9ba Mon Sep 17 00:00:00 2001 From: Devin Slauenwhite Date: Thu, 30 Dec 2021 13:22:25 -0500 Subject: [PATCH 4/8] fix: sider issues --- erpnext/selling/doctype/quotation/quotation.py | 3 ++- erpnext/selling/doctype/quotation/test_quotation.py | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 92b7fee774..a06f0420df 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -51,7 +51,8 @@ class Quotation(SellingController): frappe.throw(_("Valid till date cannot be before transaction date")) def validate_shopping_cart_items(self): - if self.order_type != "Shopping Cart": return + if self.order_type != "Shopping Cart": + return for item in self.items: if not frappe.db.exists("Website Item", {"item_code": item.item_code}): diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py index 33d2a81373..d00ee7ad4b 100644 --- a/erpnext/selling/doctype/quotation/test_quotation.py +++ b/erpnext/selling/doctype/quotation/test_quotation.py @@ -131,8 +131,6 @@ class TestQuotation(FrappeTestCase): self.assertRaises(frappe.ValidationError, make_sales_order, quotation.name) def test_shopping_cart_without_website_item(self): - from erpnext.selling.doctype.quotation.quotation import make_sales_order - if frappe.db.exists('Website Item', {'item_code': '_Test Item Home Desktop 100'}): frappe.get_last_doc('Website Item', {'item_code': '_Test Item Home Desktop 100'}).delete() From ea6d6e31b39be25edf04d5a32eb05c67ae9d9f6c Mon Sep 17 00:00:00 2001 From: Devin Slauenwhite Date: Fri, 31 Dec 2021 10:45:51 -0500 Subject: [PATCH 5/8] fix: linter trilaing whitespace --- erpnext/selling/doctype/quotation/quotation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index a06f0420df..9a5d1b197c 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -27,7 +27,7 @@ class Quotation(SellingController): self.set_status() self.validate_uom_is_integer("stock_uom", "qty") self.validate_valid_till() - self.validate_shopping_cart_items() + self.validate_shopping_cart_items() self.set_customer_name() if self.items: self.with_items = 1 From 53d8168dea8ff02ca4536c7d244184d88a6e8b5a Mon Sep 17 00:00:00 2001 From: Devin Slauenwhite Date: Fri, 31 Dec 2021 11:04:11 -0500 Subject: [PATCH 6/8] fix: linter format string after translation --- erpnext/selling/doctype/quotation/quotation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 9a5d1b197c..de2a9baf5c 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -56,7 +56,7 @@ class Quotation(SellingController): for item in self.items: if not frappe.db.exists("Website Item", {"item_code": item.item_code}): - frappe.throw(_("Item {0} must be a website item for Shopping Cart quotations".format(item.item_code))) + frappe.throw(_("Item {0} must be a website item for Shopping Cart quotations").format(item.item_code)) def has_sales_order(self): return frappe.db.get_value("Sales Order Item", {"prevdoc_docname": self.name, "docstatus": 1}) From b848e619541e288c49222ce47197465cfd0a0d7f Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 19 Apr 2022 13:00:00 +0530 Subject: [PATCH 7/8] fix: Skip unpublished Variants with published templates in shopping cart quote validation --- erpnext/selling/doctype/quotation/quotation.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index de2a9baf5c..22371f763c 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -55,8 +55,20 @@ class Quotation(SellingController): return for item in self.items: - if not frappe.db.exists("Website Item", {"item_code": item.item_code}): - frappe.throw(_("Item {0} must be a website item for Shopping Cart quotations").format(item.item_code)) + has_web_item = frappe.db.exists("Website Item", {"item_code": item.item_code}) + + # If variant is unpublished but template is published: valid + template = frappe.get_cached_value("Item", item.item_code, "variant_of") + if template and not has_web_item: + has_web_item = frappe.db.exists("Website Item", {"item_code": template}) + + if not has_web_item: + frappe.throw( + _( + "Row #{0}: Item {1} must have a Website Item for Shopping Cart Quotations" + ).format(item.idx, frappe.bold(item.item_code)), + title=_("Unpublished Item") + ) def has_sales_order(self): return frappe.db.get_value("Sales Order Item", {"prevdoc_docname": self.name, "docstatus": 1}) From 8d632e9b9c614df624bb42964544a1e14ee4275c Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 22 Apr 2022 16:12:58 +0530 Subject: [PATCH 8/8] style: Re-run pre-commit --- erpnext/selling/doctype/quotation/quotation.py | 8 ++++---- erpnext/selling/doctype/quotation/test_quotation.py | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 22371f763c..548813ddef 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -64,10 +64,10 @@ class Quotation(SellingController): if not has_web_item: frappe.throw( - _( - "Row #{0}: Item {1} must have a Website Item for Shopping Cart Quotations" - ).format(item.idx, frappe.bold(item.item_code)), - title=_("Unpublished Item") + _("Row #{0}: Item {1} must have a Website Item for Shopping Cart Quotations").format( + item.idx, frappe.bold(item.item_code) + ), + title=_("Unpublished Item"), ) def has_sales_order(self): diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py index d00ee7ad4b..6f0b381fc1 100644 --- a/erpnext/selling/doctype/quotation/test_quotation.py +++ b/erpnext/selling/doctype/quotation/test_quotation.py @@ -131,11 +131,11 @@ class TestQuotation(FrappeTestCase): self.assertRaises(frappe.ValidationError, make_sales_order, quotation.name) def test_shopping_cart_without_website_item(self): - if frappe.db.exists('Website Item', {'item_code': '_Test Item Home Desktop 100'}): - frappe.get_last_doc('Website Item', {'item_code': '_Test Item Home Desktop 100'}).delete() + if frappe.db.exists("Website Item", {"item_code": "_Test Item Home Desktop 100"}): + frappe.get_last_doc("Website Item", {"item_code": "_Test Item Home Desktop 100"}).delete() quotation = frappe.copy_doc(test_records[0]) - quotation.order_type = 'Shopping Cart' + quotation.order_type = "Shopping Cart" quotation.valid_till = getdate() self.assertRaises(frappe.ValidationError, quotation.validate)