diff --git a/erpnext/shopping_cart/product_query.py b/erpnext/shopping_cart/product_query.py new file mode 100644 index 0000000000..0f3eabb21c --- /dev/null +++ b/erpnext/shopping_cart/product_query.py @@ -0,0 +1,89 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +class ProductQuery: + """Query engine for product listing + + Attributes: + cart_settings (Document): Settings for Cart + fields (list): Fields to fetch in query + filters (list) + or_filters (list) + page_length (Int): Length of page for the query + settings (Document): Products Settings DocType + """ + + def __init__(self): + self.settings = frappe.get_doc("Products Settings") + self.cart_settings = frappe.get_doc("Shopping Cart Settings") + self.page_length = self.settings.products_per_page or 20 + self.fields = ['name', 'item_name', 'website_image', 'variant_of', 'has_variants', 'item_group', 'image', 'web_long_description', 'description', 'route'] + self.filters = [['show_in_website', '=', 1]] + self.or_filters = [] + + def query(self, attributes=None, fields=None, search_term=None, start=0): + if fields: self.build_fields_filters(fields) + if search_term: self.build_search_filters(search_term) + + result = [] + + if attributes: + all_items = [] + for attribute, values in attributes.items(): + if not isinstance(values, list): + values = [values] + + items = frappe.get_all( + "Item", + fields=self.fields, + filters=[ + *self.filters, + ["Item Variant Attribute", "attribute", "=", attribute],` + ["Item Variant Attribute", "attribute_value", "in", values], + ], + or_filters=self.or_filters, + limit=self.page_length + ) + + items_dict = {item.name: item for item in items} + + all_items.append(set(items.keys())) + + result = [items_dict.get(item) for item in list(set.intersection(*all_items))] + else: + result = frappe.get_all("Item", fields=self.fields, filters=self.filters, or_filters=self.or_filters, limit=self.page_length) + + return result + + def build_fields_filters(self, filters): + for field, values in filters.items(): + if not values: + continue + + if isinstance(values, list): + self.filters.append([field, 'IN', values]) + else: + self.filters.append([field, '=', values]) + + def build_search_filters(self, search_term): + # Default fields to search from + default_fields = {'name', 'item_name', 'description', 'item_group'} + + # Get meta search fields + meta = frappe.get_meta("Item") + meta_fields = set(meta.get_search_fields()) + + # Join the meta fields and default fields set + search_fields = default_fields.union(meta_fields) + try: + if frappe.db.count('Item', cache=True) > 50000: + search_fields.remove('description') + except KeyError: + pass + + # Build or filters for query + search = '%{}%'.format(search_term) + self.or_filters += [[field, 'like', search] for field in search_fields] diff --git a/erpnext/www/all-products/index.py b/erpnext/www/all-products/index.py index 0394e4b2cc..6ebc1c305e 100644 --- a/erpnext/www/all-products/index.py +++ b/erpnext/www/all-products/index.py @@ -1,6 +1,7 @@ import frappe from erpnext.portal.product_configurator.utils import (get_products_for_website, get_product_settings, get_field_filter_data, get_attribute_filter_data) +from erpnext.shopping_cart.product_query import ProductQuery sitemap = 1 @@ -12,10 +13,14 @@ def get_context(context): attribute_filters = frappe.parse_json(frappe.form_dict.attribute_filters) else: search = field_filters = attribute_filters = None - - context.items = get_products_for_website(field_filters, attribute_filters, search) + + # items = get_products_for_website(field_filters, attribute_filters, search) + + engine = ProductQuery() + items = engine.query(attribute_filters, field_filters, search) product_settings = get_product_settings() + context.items = items context.field_filters = get_field_filter_data() \ if product_settings.enable_field_filters else [] @@ -23,6 +28,7 @@ def get_context(context): if product_settings.enable_attribute_filters else [] context.product_settings = product_settings + context.body_class = "product-page" context.page_length = product_settings.products_per_page context.no_cache = 1