fix: Error state and passing args for product listing

- Show error state in case of unexpected errors in query engine
- Pass args appropriately from `view.js`
- Use args correctly in `api.py`
- Error handling in `api.py` if query engine raises error
- Instantiated product data engine tests
- Fix dotted path for search api call in `search.js`
This commit is contained in:
marination 2021-08-13 14:23:52 +05:30
parent 335a237383
commit 7d1df9d4c3
6 changed files with 93 additions and 32 deletions

View File

@ -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 = {}

View File

@ -1,13 +1,9 @@
import unittest
import frappe
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):
def setUp(self):

View File

@ -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

View File

@ -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
},

View File

@ -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(`
<div class="input-group col-6">
<div class="input-group col-6 p-0">
<div class="dropdown w-100" id="dropdownMenuSearch">
<input type="search" name="query" id="search-box" class="form-control font-md"
placeholder="Search for Products"
@ -211,7 +213,7 @@ erpnext.ProductView = class {
}
render_view_toggler() {
$(".toolbar").append(`<div class="toggle-container col-6"></div>`);
$(".toolbar").append(`<div class="toggle-container col-6 p-0"></div>`);
["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(`
<br><br><br>
<div class="cart-empty frappe-card">
render_no_products_section(error=false) {
let error_section = `
<div class="mt-4 w-100 alert alert-error font-md">
Something went wrong. Please refresh or contact us.
</div>
`;
let no_results_section = `
<div class="cart-empty frappe-card mt-4">
<div class="cart-empty-state">
<img src="/assets/erpnext/images/ui-states/cart-empty-state.png" alt="Empty Cart">
</div>
<div class="cart-empty-message mt-4">${ __('No products found') }</p>
</div>
`);
`;
this.products_section.append(error ? error_section : no_results_section);
}
render_item_sub_categories(categories) {

View File

@ -1316,6 +1316,12 @@ body.product-page {
font-size: 14px;
}
.alert-error {
color: #e27a84;
background-color: #fff6f7;
border-color: #f5c6cb;
}
.font-md {
font-size: 14px !important;
}