From 412b9b7f40ac1b6840a07852aa18e2a3a137cf71 Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Thu, 14 Dec 2023 12:05:06 +0530 Subject: [PATCH] feat: add listview filters using specifications --- .../doctype/specification/specification.py | 9 +- .../inventory_tools/faceted_search.py | 224 +++++++++--------- .../js/faceted_search/FacetedSearch.vue | 115 +++++---- .../FacetedSearchNumericRange.vue | 135 +++++------ 4 files changed, 251 insertions(+), 232 deletions(-) diff --git a/inventory_tools/inventory_tools/doctype/specification/specification.py b/inventory_tools/inventory_tools/doctype/specification/specification.py index 8f05b68..4481a40 100644 --- a/inventory_tools/inventory_tools/doctype/specification/specification.py +++ b/inventory_tools/inventory_tools/doctype/specification/specification.py @@ -17,7 +17,6 @@ def validate(self): def create_linked_values(self, doc, extra_attributes=None): if not extra_attributes: extra_attributes = {} - for at in self.attributes: if at.field: existing_attribute_value = frappe.db.get_value( @@ -37,7 +36,9 @@ def create_linked_values(self, doc, extra_attributes=None): av.attribute = at.attribute_name av.value = doc.get(at.field) av.save() - if extra_attributes and at.attribute_name in extra_attributes: + if not extra_attributes: + continue + if at.attribute_name in extra_attributes: if isinstance(extra_attributes[at.attribute_name], (str, int, float)): existing_attribute_value = frappe.db.get_value( "Specification Value", @@ -57,10 +58,6 @@ def create_linked_values(self, doc, extra_attributes=None): av.value = extra_attributes[at.attribute_name] av.save() continue - - if not extra_attributes: - continue - for value in extra_attributes[at.attribute_name]: # list, tuple or set / not dict existing_attribute_value = frappe.db.get_value( "Specification Value", diff --git a/inventory_tools/inventory_tools/faceted_search.py b/inventory_tools/inventory_tools/faceted_search.py index f427073..5683109 100644 --- a/inventory_tools/inventory_tools/faceted_search.py +++ b/inventory_tools/inventory_tools/faceted_search.py @@ -1,112 +1,112 @@ -import json - -import frappe -from erpnext.e_commerce.api import * -from frappe.utils.data import flt - - -@frappe.whitelist(allow_guest=True) -def show_faceted_search_components(doctype="Item", filters=None): - attributes = frappe.get_all( - "Specification Attribute", - {"applied_on": doctype}, - ["component", "attribute_name", "numeric_values"], - order_by="idx ASC", - ) - - for attribute in attributes: - values = list( - set( - frappe.get_all( - "Specification Value", - {"attribute": attribute.attribute_name, "reference_doctype": doctype}, - pluck="value", - ) - ) - ) - if attribute.numeric_values and values: - _values = [flt(v) for v in values] - _min, _max = min(_values), max(_values) - attribute.values = [_min, _max] - else: - attribute.values = values - - return attributes - - -class FacetedSearchQuery(ProductQuery): - def query_items_with_attributes(self, attributes, start=0): - item_codes = [] - - attributes_in_use = {k: v for (k, v) in attributes.items() if v} - for attribute, values in attributes_in_use.items(): - if not isinstance(values, list): - values = [values] - - item_code_list = frappe.get_all( - "Specification Value", - fields=["reference_name"], - filters=[ - ["attribute", "=", attribute], - ["value", "in", values], - ], - ) - item_codes.append({x.reference_name for x in item_code_list}) - print(attribute, item_codes) - - if item_codes: - item_codes = list(set.intersection(*item_codes)) - self.filters.append(["item_code", "in", item_codes]) - - return self.query_items(start=start) - - -@frappe.whitelist(allow_guest=True) -def get_product_filter_data(query_args=None): - if isinstance(query_args, str): - query_args = json.loads(query_args) - - query_args = frappe._dict(query_args) - if query_args: - search = query_args.get("search") - field_filters = query_args.get("field_filters", {}) - attribute_filters = query_args.get("attributes", {}) - 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 from_filters: - start = 0 - - sub_categories = [] - if item_group: - sub_categories = get_child_groups_for_website(item_group, immediate=True) - - engine = FacetedSearchQuery() - - try: - result = engine.query( - attribute_filters, field_filters, search_term=search, start=start, item_group=item_group - ) - except Exception: - frappe.log_error("Product query with filter failed") - return {"exc": "Something went wrong!"} - - filters = {} - discounts = result["discounts"] - - if discounts: - filter_engine = ProductFiltersBuilder() - filters["discount_filters"] = filter_engine.get_discount_filters(discounts) - - return { - "items": result["items"] or [], - "filters": filters, - "settings": engine.settings, - "sub_categories": sub_categories, - "items_count": result["items_count"], - } +import json + +import frappe +from erpnext.e_commerce.api import * +from frappe.utils.data import flt + + +@frappe.whitelist(allow_guest=True) +def show_faceted_search_components(doctype="Item", filters=None): + attributes = frappe.get_all( + "Specification Attribute", + {"applied_on": doctype}, + ["component", "attribute_name", "numeric_values", "field"], + order_by="idx ASC", + ) + + for attribute in attributes: + values = list( + set( + frappe.get_all( + "Specification Value", + {"attribute": attribute.attribute_name, "reference_doctype": doctype}, + pluck="value", + ) + ) + ) + if attribute.numeric_values and values: + _values = [flt(v) for v in values] + _min, _max = min(_values), max(_values) + attribute.values = [_min, _max] + else: + attribute.values = values + + return attributes + + +class FacetedSearchQuery(ProductQuery): + def query_items_with_attributes(self, attributes, start=0): + item_codes = set() + + attributes_in_use = {k: v for (k, v) in attributes.items() if v} + for attribute, values in attributes_in_use.items(): + if not isinstance(values, list): + values = [values] + + item_code_list = frappe.get_all( + "Specification Value", + fields=["reference_name"], + filters=[ + ["attribute", "=", attribute], + ["value", "in", values], + ], + ) + + for item_code in item_code_list: + item_codes.add(item_code.reference_name) + + if item_codes: + self.filters.append(["item_code", "in", list(item_codes)]) + + return self.query_items(start=start) + + +@frappe.whitelist(allow_guest=True) +def get_product_filter_data(query_args=None): + if isinstance(query_args, str): + query_args = json.loads(query_args) + + query_args = frappe._dict(query_args) + if query_args: + search = query_args.get("search") + field_filters = query_args.get("field_filters", {}) + attribute_filters = query_args.get("attributes", {}) + 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 from_filters: + start = 0 + + sub_categories = [] + if item_group: + sub_categories = get_child_groups_for_website(item_group, immediate=True) + + engine = FacetedSearchQuery() + + try: + result = engine.query( + attribute_filters, field_filters, search_term=search, start=start, item_group=item_group + ) + except Exception: + frappe.log_error("Product query with filter failed") + return {"exc": "Something went wrong!"} + + filters = {} + discounts = result["discounts"] + + if discounts: + filter_engine = ProductFiltersBuilder() + filters["discount_filters"] = filter_engine.get_discount_filters(discounts) + + return { + "items": result["items"] or [], + "filters": filters, + "settings": engine.settings, + "sub_categories": sub_categories, + "items_count": result["items_count"], + } diff --git a/inventory_tools/public/js/faceted_search/FacetedSearch.vue b/inventory_tools/public/js/faceted_search/FacetedSearch.vue index 07cb36c..2259acc 100644 --- a/inventory_tools/public/js/faceted_search/FacetedSearch.vue +++ b/inventory_tools/public/js/faceted_search/FacetedSearch.vue @@ -5,11 +5,11 @@ :is="comp.component" :values="comp.values" :attribute_name="comp.attribute_name" - @update_filters="update_filters($event)" - > - - + @update_filters="update_filters($event)"> + + + + - + + + + +