Merge pull request #26170 from marination/web-item-group
fix: Website item group logic for product listing in Item Group pages
This commit is contained in:
commit
ce99701150
@ -43,6 +43,30 @@ class TestProductConfigurator(unittest.TestCase):
|
|||||||
"show_variant_in_website": 1
|
"show_variant_in_website": 1
|
||||||
}).insert()
|
}).insert()
|
||||||
|
|
||||||
|
def create_regular_web_item(self, name, item_group=None):
|
||||||
|
if not frappe.db.exists('Item', name):
|
||||||
|
doc = frappe.get_doc({
|
||||||
|
"description": name,
|
||||||
|
"item_code": name,
|
||||||
|
"item_name": name,
|
||||||
|
"doctype": "Item",
|
||||||
|
"is_stock_item": 1,
|
||||||
|
"item_group": item_group or "_Test Item Group",
|
||||||
|
"stock_uom": "_Test UOM",
|
||||||
|
"item_defaults": [{
|
||||||
|
"company": "_Test Company",
|
||||||
|
"default_warehouse": "_Test Warehouse - _TC",
|
||||||
|
"expense_account": "_Test Account Cost for Goods Sold - _TC",
|
||||||
|
"buying_cost_center": "_Test Cost Center - _TC",
|
||||||
|
"selling_cost_center": "_Test Cost Center - _TC",
|
||||||
|
"income_account": "Sales - _TC"
|
||||||
|
}],
|
||||||
|
"show_in_website": 1
|
||||||
|
}).insert()
|
||||||
|
else:
|
||||||
|
doc = frappe.get_doc("Item", name)
|
||||||
|
return doc
|
||||||
|
|
||||||
def test_product_list(self):
|
def test_product_list(self):
|
||||||
template_items = frappe.get_all('Item', {'show_in_website': 1})
|
template_items = frappe.get_all('Item', {'show_in_website': 1})
|
||||||
variant_items = frappe.get_all('Item', {'show_variant_in_website': 1})
|
variant_items = frappe.get_all('Item', {'show_variant_in_website': 1})
|
||||||
@ -79,3 +103,42 @@ class TestProductConfigurator(unittest.TestCase):
|
|||||||
'Test Size': ['2XL']
|
'Test Size': ['2XL']
|
||||||
})
|
})
|
||||||
self.assertEqual(len(items), 1)
|
self.assertEqual(len(items), 1)
|
||||||
|
|
||||||
|
def test_products_in_multiple_item_groups(self):
|
||||||
|
"""Check if product is visible on multiple item group pages barring its own."""
|
||||||
|
from erpnext.shopping_cart.product_query import ProductQuery
|
||||||
|
|
||||||
|
if not frappe.db.exists("Item Group", {"name": "Tech Items"}):
|
||||||
|
item_group_doc = frappe.get_doc({
|
||||||
|
"doctype": "Item Group",
|
||||||
|
"item_group_name": "Tech Items",
|
||||||
|
"parent_item_group": "All Item Groups",
|
||||||
|
"show_in_website": 1
|
||||||
|
}).insert()
|
||||||
|
else:
|
||||||
|
item_group_doc = frappe.get_doc("Item Group", "Tech Items")
|
||||||
|
|
||||||
|
doc = self.create_regular_web_item("Portal Item", item_group="Tech Items")
|
||||||
|
if not frappe.db.exists("Website Item Group", {"parent": "Portal Item"}):
|
||||||
|
doc.append("website_item_groups", {
|
||||||
|
"item_group": "_Test Item Group Desktops"
|
||||||
|
})
|
||||||
|
doc.save()
|
||||||
|
|
||||||
|
# check if item is visible in its own Item Group's page
|
||||||
|
engine = ProductQuery()
|
||||||
|
items = engine.query({}, {"item_group": "Tech Items"}, None, start=0, item_group="Tech Items")
|
||||||
|
self.assertEqual(len(items), 1)
|
||||||
|
self.assertEqual(items[0].item_code, "Portal Item")
|
||||||
|
|
||||||
|
# check if item is visible in configured foreign Item Group's page
|
||||||
|
engine = ProductQuery()
|
||||||
|
items = engine.query({}, {"item_group": "_Test Item Group Desktops"}, None, start=0, item_group="_Test Item Group Desktops")
|
||||||
|
item_codes = [row.item_code for row in items]
|
||||||
|
|
||||||
|
self.assertIn(len(items), [2, 3])
|
||||||
|
self.assertIn("Portal Item", item_codes)
|
||||||
|
|
||||||
|
# teardown
|
||||||
|
doc.delete()
|
||||||
|
item_group_doc.delete()
|
@ -91,7 +91,7 @@ class ItemGroup(NestedSet, WebsiteGenerator):
|
|||||||
field_filters['item_group'] = self.name
|
field_filters['item_group'] = self.name
|
||||||
|
|
||||||
engine = ProductQuery()
|
engine = ProductQuery()
|
||||||
context.items = engine.query(attribute_filters, field_filters, search, start)
|
context.items = engine.query(attribute_filters, field_filters, search, start, item_group=self.name)
|
||||||
|
|
||||||
filter_engine = ProductFiltersBuilder(self.name)
|
filter_engine = ProductFiltersBuilder(self.name)
|
||||||
|
|
||||||
|
@ -22,12 +22,15 @@ class ProductFiltersBuilder:
|
|||||||
|
|
||||||
filter_data = []
|
filter_data = []
|
||||||
for df in fields:
|
for df in fields:
|
||||||
filters = {}
|
filters, or_filters = {}, []
|
||||||
if df.fieldtype == "Link":
|
if df.fieldtype == "Link":
|
||||||
if self.item_group:
|
if self.item_group:
|
||||||
filters['item_group'] = self.item_group
|
or_filters.extend([
|
||||||
|
["item_group", "=", self.item_group],
|
||||||
|
["Website Item Group", "item_group", "=", self.item_group]
|
||||||
|
])
|
||||||
|
|
||||||
values = frappe.get_all("Item", fields=[df.fieldname], filters=filters, distinct="True", pluck=df.fieldname)
|
values = frappe.get_all("Item", fields=[df.fieldname], filters=filters, or_filters=or_filters, distinct="True", pluck=df.fieldname)
|
||||||
else:
|
else:
|
||||||
doctype = df.get_link_doctype()
|
doctype = df.get_link_doctype()
|
||||||
|
|
||||||
@ -44,7 +47,9 @@ class ProductFiltersBuilder:
|
|||||||
values = [d.name for d in frappe.get_all(doctype, filters)]
|
values = [d.name for d in frappe.get_all(doctype, filters)]
|
||||||
|
|
||||||
# Remove None
|
# Remove None
|
||||||
values = values.remove(None) if None in values else values
|
if None in values:
|
||||||
|
values.remove(None)
|
||||||
|
|
||||||
if values:
|
if values:
|
||||||
filter_data.append([df, values])
|
filter_data.append([df, values])
|
||||||
|
|
||||||
@ -61,14 +66,18 @@ class ProductFiltersBuilder:
|
|||||||
for attr_doc in attribute_docs:
|
for attr_doc in attribute_docs:
|
||||||
selected_attributes = []
|
selected_attributes = []
|
||||||
for attr in attr_doc.item_attribute_values:
|
for attr in attr_doc.item_attribute_values:
|
||||||
|
or_filters = []
|
||||||
filters= [
|
filters= [
|
||||||
["Item Variant Attribute", "attribute", "=", attr.parent],
|
["Item Variant Attribute", "attribute", "=", attr.parent],
|
||||||
["Item Variant Attribute", "attribute_value", "=", attr.attribute_value]
|
["Item Variant Attribute", "attribute_value", "=", attr.attribute_value]
|
||||||
]
|
]
|
||||||
if self.item_group:
|
if self.item_group:
|
||||||
filters.append(["item_group", "=", self.item_group])
|
or_filters.extend([
|
||||||
|
["item_group", "=", self.item_group],
|
||||||
|
["Website Item Group", "item_group", "=", self.item_group]
|
||||||
|
])
|
||||||
|
|
||||||
if frappe.db.get_all("Item", filters, limit=1):
|
if frappe.db.get_all("Item", filters, or_filters=or_filters, limit=1):
|
||||||
selected_attributes.append(attr)
|
selected_attributes.append(attr)
|
||||||
|
|
||||||
if selected_attributes:
|
if selected_attributes:
|
||||||
|
@ -22,13 +22,14 @@ class ProductQuery:
|
|||||||
self.settings = frappe.get_doc("Products Settings")
|
self.settings = frappe.get_doc("Products Settings")
|
||||||
self.cart_settings = frappe.get_doc("Shopping Cart Settings")
|
self.cart_settings = frappe.get_doc("Shopping Cart Settings")
|
||||||
self.page_length = self.settings.products_per_page or 20
|
self.page_length = self.settings.products_per_page or 20
|
||||||
self.fields = ['name', 'item_name', 'item_code', 'website_image', 'variant_of', 'has_variants', 'item_group', 'image', 'web_long_description', 'description', 'route']
|
self.fields = ['name', 'item_name', 'item_code', 'website_image', 'variant_of', 'has_variants',
|
||||||
|
'item_group', 'image', 'web_long_description', 'description', 'route', 'weightage']
|
||||||
self.filters = []
|
self.filters = []
|
||||||
self.or_filters = [['show_in_website', '=', 1]]
|
self.or_filters = [['show_in_website', '=', 1]]
|
||||||
if not self.settings.get('hide_variants'):
|
if not self.settings.get('hide_variants'):
|
||||||
self.or_filters.append(['show_variant_in_website', '=', 1])
|
self.or_filters.append(['show_variant_in_website', '=', 1])
|
||||||
|
|
||||||
def query(self, attributes=None, fields=None, search_term=None, start=0):
|
def query(self, attributes=None, fields=None, search_term=None, start=0, item_group=None):
|
||||||
"""Summary
|
"""Summary
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -44,6 +45,15 @@ class ProductQuery:
|
|||||||
if search_term: self.build_search_filters(search_term)
|
if search_term: self.build_search_filters(search_term)
|
||||||
|
|
||||||
result = []
|
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:
|
if attributes:
|
||||||
all_items = []
|
all_items = []
|
||||||
@ -61,12 +71,10 @@ class ProductQuery:
|
|||||||
],
|
],
|
||||||
or_filters=self.or_filters,
|
or_filters=self.or_filters,
|
||||||
start=start,
|
start=start,
|
||||||
limit=self.page_length,
|
limit=self.page_length
|
||||||
order_by="weightage desc"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
items_dict = {item.name: item for item in items}
|
items_dict = {item.name: item for item in items}
|
||||||
# TODO: Replace Variants by their parent templates
|
|
||||||
|
|
||||||
all_items.append(set(items_dict.keys()))
|
all_items.append(set(items_dict.keys()))
|
||||||
|
|
||||||
@ -78,14 +86,22 @@ class ProductQuery:
|
|||||||
filters=self.filters,
|
filters=self.filters,
|
||||||
or_filters=self.or_filters,
|
or_filters=self.or_filters,
|
||||||
start=start,
|
start=start,
|
||||||
limit=self.page_length,
|
limit=self.page_length
|
||||||
order_by="weightage desc"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Combine results having context of website item groups into item results
|
||||||
|
if item_group and website_item_groups:
|
||||||
|
items_list = {row.name for row in result}
|
||||||
|
for row in website_item_groups:
|
||||||
|
if row.wig_parent not in items_list:
|
||||||
|
result.append(row)
|
||||||
|
|
||||||
|
result = sorted(result, key=lambda x: x.get("weightage"), reverse=True)
|
||||||
|
|
||||||
for item in result:
|
for item in result:
|
||||||
product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True).get('product_info')
|
product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True).get('product_info')
|
||||||
if product_info:
|
if product_info:
|
||||||
item.formatted_price = product_info['price'].get('formatted_price') if product_info['price'] else None
|
item.formatted_price = (product_info.get('price') or {}).get('formatted_price')
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@ -99,7 +115,16 @@ class ProductQuery:
|
|||||||
if not values:
|
if not values:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if isinstance(values, list):
|
# handle multiselect fields in filter addition
|
||||||
|
meta = frappe.get_meta('Item', cached=True)
|
||||||
|
df = meta.get_field(field)
|
||||||
|
if df.fieldtype == 'Table MultiSelect':
|
||||||
|
child_doctype = df.options
|
||||||
|
child_meta = frappe.get_meta(child_doctype, cached=True)
|
||||||
|
fields = child_meta.get("fields")
|
||||||
|
if fields:
|
||||||
|
self.filters.append([child_doctype, fields[0].fieldname, 'IN', values])
|
||||||
|
elif isinstance(values, list):
|
||||||
# If value is a list use `IN` query
|
# If value is a list use `IN` query
|
||||||
self.filters.append([field, 'IN', values])
|
self.filters.append([field, 'IN', values])
|
||||||
else:
|
else:
|
||||||
|
Loading…
Reference in New Issue
Block a user