diff --git a/erpnext/e_commerce/doctype/item_review/item_review.py b/erpnext/e_commerce/doctype/item_review/item_review.py
index 637194e6b85..f19dcf3ee44 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 f22e7ff5bf0..a629dcffa8e 100644
--- a/erpnext/e_commerce/filters.py
+++ b/erpnext/e_commerce/filters.py
@@ -4,6 +4,7 @@
from __future__ import unicode_literals
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:
)
return valid_attributes
+
+ 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
\ No newline at end of file
diff --git a/erpnext/e_commerce/product_query.py b/erpnext/e_commerce/product_query.py
index 2c91a72c97c..86afe67bba2 100644
--- a/erpnext/e_commerce/product_query.py
+++ b/erpnext/e_commerce/product_query.py
@@ -3,6 +3,7 @@
import frappe
from erpnext.e_commerce.shopping_cart.product_info import get_product_info_for_website
+from frappe.utils import flt
class ProductQuery:
"""Query engine for product listing
@@ -37,16 +38,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 = []
-
if attributes:
result = self.query_items_with_attributes(attributes, start)
else:
@@ -56,33 +56,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."""
@@ -139,7 +156,7 @@ class ProductQuery:
filters (dict): Filters
"""
for field, values in filters.items():
- if not values:
+ if not values or field == "discount":
continue
if isinstance(values, list):
diff --git a/erpnext/public/js/wishlist.js b/erpnext/public/js/wishlist.js
index b2c840a4d58..6bcb6b14e2c 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 fc805a36303..63438222e01 100644
--- a/erpnext/setup/doctype/item_group/item_group.py
+++ b/erpnext/setup/doctype/item_group/item_group.py
@@ -90,12 +90,14 @@ class ItemGroup(NestedSet, WebsiteGenerator):
field_filters['item_group'] = self.name
engine = ProductQuery()
- context.items = engine.query(attribute_filters, field_filters, search, start)
+ 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/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 1dd1bfa6cc2..040b95df91f 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -399,7 +399,7 @@ class Item(Document):
if not records: return
document = _("Stock Reconciliation") if len(records) == 1 else _("Stock Reconciliations")
- msg = _("The items {0} and {1} are present in the following {2} : ").format(
+ msg = _("The items {0} and {1} are present in the following {2} :").format(
frappe.bold(old_name), frappe.bold(new_name), document)
msg += '
'
diff --git a/erpnext/templates/generators/item_group.html b/erpnext/templates/generators/item_group.html
index 6823467a3bf..250b3bc59e0 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 @@