From 1d949141024e372477de837c5b8cb4a301c1bc98 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 20 Apr 2021 21:54:52 +0530 Subject: [PATCH] feat: Discount Filters - Discount filters in filters section - Code cleanup --- .../doctype/item_review/item_review.py | 2 +- erpnext/e_commerce/filters.py | 19 +++- erpnext/e_commerce/product_query.py | 73 ++++++++------- erpnext/public/js/wishlist.js | 4 +- .../setup/doctype/item_group/item_group.py | 4 +- erpnext/templates/generators/item_group.html | 69 ++------------- erpnext/templates/includes/macros.html | 88 +++++++++++++++++++ erpnext/utilities/product.py | 5 +- erpnext/www/all-products/index.html | 70 +++------------ erpnext/www/all-products/index.js | 6 +- erpnext/www/all-products/index.py | 9 +- 11 files changed, 186 insertions(+), 163 deletions(-) diff --git a/erpnext/e_commerce/doctype/item_review/item_review.py b/erpnext/e_commerce/doctype/item_review/item_review.py index 637194e6b8..f19dcf3ee4 100644 --- a/erpnext/e_commerce/doctype/item_review/item_review.py +++ b/erpnext/e_commerce/doctype/item_review/item_review.py @@ -75,4 +75,4 @@ def get_customer(): if customer: return frappe.db.get_value("Customer", customer) else: - frappe.throw("You are not verified to write a review yet. Please contact us for verification.") \ No newline at end of file + frappe.throw(_("You are not verified to write a review yet. Please contact us for verification.")) \ No newline at end of file diff --git a/erpnext/e_commerce/filters.py b/erpnext/e_commerce/filters.py index 3ca3d26878..9ad817c27a 100644 --- a/erpnext/e_commerce/filters.py +++ b/erpnext/e_commerce/filters.py @@ -2,7 +2,8 @@ # License: GNU General Public License v3. See license.txt import frappe - +from frappe import _dict +from frappe.utils import floor, ceil, flt class ProductFiltersBuilder: def __init__(self, item_group=None): @@ -88,3 +89,19 @@ class ProductFiltersBuilder: for name, values in attribute_value_map.items(): out.append(frappe._dict(name=name, item_attribute_values=values)) return out + + def get_discount_filters(self, discounts): + discount_filters = [] + + # [25.89, 60.5] + min_discount, max_discount = discounts[0], discounts[1] + # [25, 60] + min_range_absolute, max_range_absolute = floor(min_discount), floor(max_discount) + min_range = int(min_discount - (min_range_absolute%10)) # 20 + max_range = int(max_discount - (max_range_absolute%10)) # 60 + + for discount in range(min_range, (max_range + 1), 10): + label = f"{discount}% and above" + discount_filters.append([discount, label]) + + return discount_filters diff --git a/erpnext/e_commerce/product_query.py b/erpnext/e_commerce/product_query.py index 2dbed0af17..c186a05282 100644 --- a/erpnext/e_commerce/product_query.py +++ b/erpnext/e_commerce/product_query.py @@ -3,6 +3,8 @@ import frappe +from frappe.utils import flt + from erpnext.e_commerce.shopping_cart.product_info import get_product_info_for_website @@ -39,25 +41,15 @@ class ProductQuery: Returns: list: List of results with set fields """ + result, discount_list = [], [] + if fields: self.build_fields_filters(fields) if search_term: self.build_search_filters(search_term) - if self.settings.hide_variants: self.conditions += " and wi.variant_of is null" - result = [] - website_item_groups = [] - - # if from item group page consider website item group table - if item_group: - website_item_groups = frappe.db.get_all( - "Item", - fields=self.fields + ["`tabWebsite Item Group`.parent as wig_parent"], - filters=[["Website Item Group", "item_group", "=", item_group]] - ) - if attributes: result = self.query_items_with_attributes(attributes, start) else: @@ -67,33 +59,50 @@ class ProductQuery: # add price and availability info in results for item in result: product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True).get('product_info') + if product_info and product_info['price']: - item.formatted_mrp = product_info['price'].get('formatted_mrp') - item.formatted_price = product_info['price'].get('formatted_price') - if item.formatted_mrp: - item.discount = product_info['price'].get('formatted_discount_percent') or \ - product_info['price'].get('formatted_discount_rate') - item.price = product_info['price'].get('price_list_rate') + self.get_price_discount_info(item, product_info['price'], discount_list) if self.settings.show_stock_availability: - if item.get("website_warehouse"): - stock_qty = frappe.utils.flt( - frappe.db.get_value("Bin", - { - "item_code": item.item_code, - "warehouse": item.get("website_warehouse") - }, - "actual_qty") - ) - item.in_stock = "green" if stock_qty else "red" - elif not frappe.db.get_value("Item", item.item_code, "is_stock_item"): - item.in_stock = "green" # non-stock item will always be available + self.get_stock_availability(item) item.wished = False if frappe.db.exists("Wishlist Items", {"item_code": item.item_code, "parent": frappe.session.user}): item.wished = True - return result + discounts = [] + if discount_list: + discounts = [min(discount_list), max(discount_list)] + + if fields and "discount" in fields: + discount_percent = frappe.utils.flt(fields["discount"][0]) + result = [row for row in result if row.get("discount_percent") and row.discount_percent >= discount_percent] + + return result, discounts + + def get_price_discount_info(self, item, price_object, discount_list): + """Modify item object and add price details.""" + item.formatted_mrp = price_object.get('formatted_mrp') + item.formatted_price = price_object.get('formatted_price') + + if price_object.get('discount_percent'): + item.discount_percent = flt(price_object.discount_percent) + discount_list.append(price_object.discount_percent) + + if item.formatted_mrp: + item.discount = price_object.get('formatted_discount_percent') or \ + price_object.get('formatted_discount_rate') + item.price = price_object.get('price_list_rate') + + def get_stock_availability(self, item): + """Modify item object and add stock details.""" + if item.get("website_warehouse"): + stock_qty = frappe.utils.flt( + frappe.db.get_value("Bin", {"item_code": item.item_code, "warehouse": item.get("website_warehouse")}, + "actual_qty")) + item.in_stock = "green" if stock_qty else "red" + elif not frappe.db.get_value("Item", item.item_code, "is_stock_item"): + item.in_stock = "green" # non-stock item will always be available def query_items(self, conditions, or_conditions, substitutions, start=0): """Build a query to fetch Website Items based on field filters.""" @@ -150,7 +159,7 @@ class ProductQuery: filters (dict): Filters """ for field, values in filters.items(): - if not values: + if not values or field == "discount": continue # handle multiselect fields in filter addition diff --git a/erpnext/public/js/wishlist.js b/erpnext/public/js/wishlist.js index b2c840a4d5..6bcb6b14e2 100644 --- a/erpnext/public/js/wishlist.js +++ b/erpnext/public/js/wishlist.js @@ -48,7 +48,7 @@ $.extend(wishlist, { }); let success_action = function() { - const $card_wrapper = $move_to_cart_btn.closest(".item-card"); + const $card_wrapper = $move_to_cart_btn.closest(".wishlist-card"); $card_wrapper.addClass("wish-removed"); }; let args = { item_code: item_code }; @@ -63,7 +63,7 @@ $.extend(wishlist, { let item_code = $remove_wish_btn.data("item-code"); let success_action = function() { - const $card_wrapper = $remove_wish_btn.closest(".item-card"); + const $card_wrapper = $remove_wish_btn.closest(".wishlist-card"); $card_wrapper.addClass("wish-removed"); }; let args = { item_code: item_code }; diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py index cdff775f74..9ff9260326 100644 --- a/erpnext/setup/doctype/item_group/item_group.py +++ b/erpnext/setup/doctype/item_group/item_group.py @@ -93,12 +93,14 @@ class ItemGroup(NestedSet, WebsiteGenerator): field_filters['item_group'] = self.name engine = ProductQuery() - context.items = engine.query(attribute_filters, field_filters, search, start, item_group=self.name) + context.items, discounts = engine.query(attribute_filters, field_filters, search, start) filter_engine = ProductFiltersBuilder(self.name) context.field_filters = filter_engine.get_field_filters() context.attribute_filters = filter_engine.get_attribute_filters() + if discounts: + context.discount_filters = filter_engine.get_discount_filters(discounts) context.update({ "parents": get_parent_item_groups(self.parent_item_group), diff --git a/erpnext/templates/generators/item_group.html b/erpnext/templates/generators/item_group.html index 233b16974b..a27b566cf3 100644 --- a/erpnext/templates/generators/item_group.html +++ b/erpnext/templates/generators/item_group.html @@ -1,3 +1,4 @@ +{% from "erpnext/templates/includes/macros.html" import field_filter_section, attribute_filter_section, discount_range_filters %} {% extends "templates/web.html" %} {% block header %} @@ -63,68 +64,16 @@
{{ _('Filters') }}
{{ _('Clear All') }} - {% for field_filter in field_filters %} - {%- set item_field = field_filter[0] %} - {%- set values = field_filter[1] %} -
-
{{ item_field.label }}
+ + {{ field_filter_section(field_filters) }} - {% if values | len > 20 %} - - - {% endif %} + + {{ attribute_filter_section(attribute_filters) }} - {% if values %} -
- {% for value in values %} -
- -
- {% endfor %} -
- {% else %} - {{ _('No values') }} - {% endif %} -
- {% endfor %} - - {% for attribute in attribute_filters %} -
-
{{ attribute.name}}
- {% if values | len > 20 %} - - - {% endif %} - - {% if attribute.item_attribute_values %} -
- {% for attr_value in attribute.item_attribute_values %} -
- -
- {% endfor %} -
- {% else %} - {{ _('No values') }} - {% endif %} -
- {% endfor %} + + {% if discount_filters %} + {{ discount_range_filters(discount_filters) }} + {% endif %}