diff --git a/erpnext/e_commerce/api.py b/erpnext/e_commerce/api.py index 4c9f4a79c6a..728d3362d3f 100644 --- a/erpnext/e_commerce/api.py +++ b/erpnext/e_commerce/api.py @@ -3,6 +3,7 @@ # For license information, please see license.txt import frappe +import json from frappe.utils import cint from erpnext.e_commerce.product_data_engine.query import ProductQuery @@ -10,23 +11,25 @@ from erpnext.e_commerce.product_data_engine.filters import ProductFiltersBuilder from erpnext.setup.doctype.item_group.item_group import get_child_groups @frappe.whitelist(allow_guest=True) -def get_product_filter_data(): - """Get pre-rendered filtered products and discount filters on load.""" - if frappe.form_dict: - search = frappe.form_dict.search - field_filters = frappe.parse_json(frappe.form_dict.field_filters) - attribute_filters = frappe.parse_json(frappe.form_dict.attribute_filters) - start = cint(frappe.parse_json(frappe.form_dict.start)) if frappe.form_dict.start else 0 - item_group = frappe.form_dict.item_group - from_filters = frappe.parse_json(frappe.form_dict.from_filters) +def get_product_filter_data(query_args=None): + """Get filtered products and discount filters.""" + if isinstance(query_args, str): + query_args = json.loads(query_args) + + if query_args: + search = query_args.get("search") + field_filters = query_args.get("field_filters", {}) + attribute_filters = query_args.get("attribute_filters", {}) + start = cint(query_args.start) if query_args.get("start") else 0 + item_group = query_args.get("item_group") + from_filters = query_args.get("from_filters") else: search, attribute_filters, item_group, from_filters = None, None, None, None field_filters = {} start = 0 + # if new filter is checked, reset start to show filtered items from page 1 if from_filters: - # if filter is checked, go to start - # and show filtered items from page 1 start = 0 sub_categories = [] @@ -35,8 +38,18 @@ def get_product_filter_data(): sub_categories = get_child_groups(item_group) engine = ProductQuery() - result = engine.query(attribute_filters, field_filters, search_term=search, - start=start, item_group=item_group) + try: + result = engine.query( + attribute_filters, + field_filters, + search_term=search, + start=start, + item_group=item_group + ) + except Exception as e: + traceback = frappe.get_traceback() + frappe.log_error(traceback, frappe._("Product Engine Error")) + return {"exc": "Something went wrong!"} # discount filter data filters = {} diff --git a/erpnext/e_commerce/product_configurator/test_product_configurator.py b/erpnext/e_commerce/product_configurator/test_product_configurator.py index 48dc90355dd..1817a78e903 100644 --- a/erpnext/e_commerce/product_configurator/test_product_configurator.py +++ b/erpnext/e_commerce/product_configurator/test_product_configurator.py @@ -1,12 +1,9 @@ -from __future__ import unicode_literals - -from bs4 import BeautifulSoup import frappe, unittest -from frappe.utils import get_html_for_route from erpnext.e_commerce.product_data_engine.query import ProductQuery from erpnext.e_commerce.doctype.website_item.website_item import make_website_item test_dependencies = ["Item"] +#TODO: Rename to test item variant configurator class TestProductConfigurator(unittest.TestCase): @classmethod diff --git a/erpnext/e_commerce/product_data_engine/test_product_data_engine.py b/erpnext/e_commerce/product_data_engine/test_product_data_engine.py new file mode 100644 index 00000000000..8bca04634d2 --- /dev/null +++ b/erpnext/e_commerce/product_data_engine/test_product_data_engine.py @@ -0,0 +1,38 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe + +test_dependencies = ["Item"] + +class TestProductDataEngine(unittest.TestCase): + "Test Products Querying for Product Listing." + def test_product_list_ordering(self): + "Check if website items appear by ranking." + pass + + def test_product_list_paging(self): + pass + + def test_product_list_with_field_filter(self): + pass + + def test_product_list_with_attribute_filter(self): + pass + + def test_product_list_with_discount_filter(self): + pass + + def test_product_list_with_mixed_filtes(self): + pass + + def test_product_list_with_mixed_filtes_item_group(self): + pass + + def test_products_in_multiple_item_groups(self): + "Check if product is visible on multiple item group pages barring its own." + pass + + def test_product_list_with_variants(self): + pass + diff --git a/erpnext/e_commerce/product_ui/search.js b/erpnext/e_commerce/product_ui/search.js index b93c9759280..ebe007624a0 100644 --- a/erpnext/e_commerce/product_ui/search.js +++ b/erpnext/e_commerce/product_ui/search.js @@ -49,7 +49,7 @@ erpnext.ProductSearch = class { // Fetch and populate product results frappe.call({ - method: "erpnext.templates.pages.e_commerce.product_search.search", + method: "erpnext.templates.pages.product_search.search", args: { query: query }, @@ -61,7 +61,7 @@ erpnext.ProductSearch = class { // Populate categories if (me.category_container) { frappe.call({ - method: "erpnext.templates.pages.e_commerce.product_search.get_category_suggestions", + method: "erpnext.templates.pages.product_search.get_category_suggestions", args: { query: query }, diff --git a/erpnext/e_commerce/product_ui/views.js b/erpnext/e_commerce/product_ui/views.js index 0a9ae1f6a51..993fd5c9f0f 100644 --- a/erpnext/e_commerce/product_ui/views.js +++ b/erpnext/e_commerce/product_ui/views.js @@ -47,10 +47,14 @@ erpnext.ProductView = class { this.disable_view_toggler(true); frappe.call({ - method: 'erpnext.e_commerce.api.get_product_filter_data', - args: args, + method: "erpnext.e_commerce.api.get_product_filter_data", + args: { + query_args: args + }, callback: function(result) { - if (!result.exc && result && result.message) { + if (!result || result.exc || !result.message || result.message.exc) { + me.render_no_products_section(true); + } else { // Sub Category results are independent of Items if (me.item_group && result.message["sub_categories"].length) { me.render_item_sub_categories(result.message["sub_categories"]); @@ -82,8 +86,6 @@ erpnext.ProductView = class { // Bottom paging me.add_paging_section(result.message["settings"]); - } else { - me.render_no_products_section(); } me.disable_view_toggler(false); @@ -189,7 +191,7 @@ erpnext.ProductView = class { prepare_search() { $(".toolbar").append(` -
+
`); + $(".toolbar").append(`
`); ["btn-list-view", "btn-grid-view"].forEach(view => { let icon = view === "btn-list-view" ? "list" : "image-view"; @@ -473,16 +475,22 @@ erpnext.ProductView = class { } } - render_no_products_section() { - this.products_section.append(` -


-
+ render_no_products_section(error=false) { + let error_section = ` +
+ Something went wrong. Please refresh or contact us. +
+ `; + let no_results_section = ` +
Empty Cart
${ __('No products found') }

- `); + `; + + this.products_section.append(error ? error_section : no_results_section); } render_item_sub_categories(categories) { diff --git a/erpnext/public/scss/shopping_cart.scss b/erpnext/public/scss/shopping_cart.scss index e4988f4351b..6cadfa63033 100644 --- a/erpnext/public/scss/shopping_cart.scss +++ b/erpnext/public/scss/shopping_cart.scss @@ -1306,6 +1306,12 @@ body.product-page { font-size: 14px; } +.alert-error { + color: #e27a84; + background-color: #fff6f7; + border-color: #f5c6cb; +} + .font-md { font-size: 14px !important; }