mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-31 07:28:04 +00:00
Merge branch 'version-13-hotfix' of https://github.com/frappe/erpnext into e-commerce-refactor
This commit is contained in:
@@ -26,12 +26,15 @@ class ProductFiltersBuilder:
|
||||
|
||||
filter_data = []
|
||||
for df in fields:
|
||||
filters = {}
|
||||
filters, or_filters = {}, []
|
||||
if df.fieldtype == "Link":
|
||||
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:
|
||||
doctype = df.get_link_doctype()
|
||||
|
||||
@@ -70,14 +73,18 @@ class ProductFiltersBuilder:
|
||||
for attr_doc in attribute_docs:
|
||||
selected_attributes = []
|
||||
for attr in attr_doc.item_attribute_values:
|
||||
or_filters = []
|
||||
filters= [
|
||||
["Item Variant Attribute", "attribute", "=", attr.parent],
|
||||
["Item Variant Attribute", "attribute_value", "=", attr.attribute_value]
|
||||
]
|
||||
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)
|
||||
|
||||
if selected_attributes:
|
||||
|
||||
@@ -1,69 +1,30 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
import frappe
|
||||
import unittest
|
||||
from frappe.utils import get_html_for_route
|
||||
from erpnext.e_commerce.product_query import ProductQuery
|
||||
from erpnext.e_commerce.doctype.website_item.website_item import make_website_item
|
||||
import frappe, unittest
|
||||
from frappe.utils import set_request, get_html_for_route
|
||||
from frappe.website.render import render
|
||||
from erpnext.portal.product_configurator.utils import get_products_for_website
|
||||
from erpnext.stock.doctype.item.test_item import make_item_variant
|
||||
|
||||
test_dependencies = ["Item"]
|
||||
|
||||
class TestProductConfigurator(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.create_variant_item()
|
||||
self.publish_items_on_website()
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.create_variant_item()
|
||||
|
||||
def test_product_list(self):
|
||||
usual_items = frappe.get_all('Website Item', {'published': 1, 'has_variants': 0, 'variant_of': ['is', 'not set']})
|
||||
template_items = frappe.get_all('Website Item', {'published': 1, 'has_variants': 1})
|
||||
variant_items = frappe.get_all('Website Item', {'published': 1, 'variant_of': ['is', 'set']})
|
||||
|
||||
e_commerce_settings = frappe.get_doc('E Commerce Settings')
|
||||
e_commerce_settings.enable_field_filters = 1
|
||||
e_commerce_settings.append('filter_fields', {'fieldname': 'item_group'})
|
||||
e_commerce_settings.append('filter_fields', {'fieldname': 'stock_uom'})
|
||||
e_commerce_settings.save()
|
||||
|
||||
html = get_html_for_route('all-products')
|
||||
|
||||
soup = BeautifulSoup(html, 'html.parser')
|
||||
products_list = soup.find(class_='products-list')
|
||||
items = products_list.find_all(class_='card')
|
||||
|
||||
self.assertEqual(len(items), len(template_items + variant_items + usual_items))
|
||||
|
||||
items_with_item_group = frappe.get_all('Website Item', {'item_group': '_Test Item Group Desktops', 'published': 1})
|
||||
|
||||
# mock query params
|
||||
frappe.form_dict = frappe._dict({
|
||||
'field_filters': '{"item_group":["_Test Item Group Desktops"]}'
|
||||
})
|
||||
html = get_html_for_route('all-products')
|
||||
soup = BeautifulSoup(html, 'html.parser')
|
||||
products_list = soup.find(class_='products-list')
|
||||
items = products_list.find_all(class_='card')
|
||||
self.assertEqual(len(items), len(items_with_item_group))
|
||||
|
||||
|
||||
def test_get_products_for_website(self):
|
||||
engine = ProductQuery()
|
||||
items = engine.query(attributes={
|
||||
'Test Size': ['Medium']
|
||||
})
|
||||
self.assertEqual(len(items), 1)
|
||||
|
||||
|
||||
def create_variant_item(self):
|
||||
if not frappe.db.exists('Item', '_Test Variant Item 1'):
|
||||
@classmethod
|
||||
def create_variant_item(cls):
|
||||
if not frappe.db.exists('Item', '_Test Variant Item - 2XL'):
|
||||
frappe.get_doc({
|
||||
"description": "_Test Variant Item 12",
|
||||
"description": "_Test Variant Item - 2XL",
|
||||
"item_code": "_Test Variant Item - 2XL",
|
||||
"item_name": "_Test Variant Item - 2XL",
|
||||
"doctype": "Item",
|
||||
"is_stock_item": 1,
|
||||
"variant_of": "_Test Variant Item",
|
||||
"item_code": "_Test Variant Item 1",
|
||||
"item_group": "_Test Item Group",
|
||||
"item_name": "_Test Variant Item 1",
|
||||
"stock_uom": "_Test UOM",
|
||||
"item_defaults": [{
|
||||
"company": "_Test Company",
|
||||
@@ -76,19 +37,108 @@ class TestProductConfigurator(unittest.TestCase):
|
||||
"attributes": [
|
||||
{
|
||||
"attribute": "Test Size",
|
||||
"attribute_value": "Medium"
|
||||
"attribute_value": "2XL"
|
||||
}
|
||||
]
|
||||
],
|
||||
"show_variant_in_website": 1
|
||||
}).insert()
|
||||
|
||||
def publish_items_on_website(self):
|
||||
if frappe.db.exists("Item", "_Test Item") and not frappe.db.exists("Website Item", {"item_code": "_Test Item"}):
|
||||
make_website_item(frappe.get_cached_doc("Item", "_Test Item"))
|
||||
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
|
||||
|
||||
if frappe.db.exists("Item", "_Test Variant Item") and not frappe.db.exists("Website Item", {"item_code": "_Test Variant Item"}):
|
||||
make_website_item(frappe.get_cached_doc("Item", "_Test Variant Item"))
|
||||
def test_product_list(self):
|
||||
template_items = frappe.get_all('Item', {'show_in_website': 1})
|
||||
variant_items = frappe.get_all('Item', {'show_variant_in_website': 1})
|
||||
|
||||
make_website_item(frappe.get_cached_doc("Item", "_Test Variant Item 1"))
|
||||
products_settings = frappe.get_doc('Products Settings')
|
||||
products_settings.enable_field_filters = 1
|
||||
products_settings.append('filter_fields', {'fieldname': 'item_group'})
|
||||
products_settings.append('filter_fields', {'fieldname': 'stock_uom'})
|
||||
products_settings.save()
|
||||
|
||||
def tearDown(self):
|
||||
frappe.db.rollback()
|
||||
html = get_html_for_route('all-products')
|
||||
|
||||
soup = BeautifulSoup(html, 'html.parser')
|
||||
products_list = soup.find(class_='products-list')
|
||||
items = products_list.find_all(class_='card')
|
||||
self.assertEqual(len(items), len(template_items + variant_items))
|
||||
|
||||
items_with_item_group = frappe.get_all('Item', {'item_group': '_Test Item Group Desktops', 'show_in_website': 1})
|
||||
variants_with_item_group = frappe.get_all('Item', {'item_group': '_Test Item Group Desktops', 'show_variant_in_website': 1})
|
||||
|
||||
# mock query params
|
||||
frappe.form_dict = frappe._dict({
|
||||
'field_filters': '{"item_group":["_Test Item Group Desktops"]}'
|
||||
})
|
||||
html = get_html_for_route('all-products')
|
||||
soup = BeautifulSoup(html, 'html.parser')
|
||||
products_list = soup.find(class_='products-list')
|
||||
items = products_list.find_all(class_='card')
|
||||
self.assertEqual(len(items), len(items_with_item_group + variants_with_item_group))
|
||||
|
||||
|
||||
def test_get_products_for_website(self):
|
||||
items = get_products_for_website(attribute_filters={
|
||||
'Test Size': ['2XL']
|
||||
})
|
||||
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()
|
||||
@@ -21,12 +21,12 @@ class ProductQuery:
|
||||
self.page_length = self.settings.products_per_page or 20
|
||||
self.fields = ['wi.web_item_name', 'wi.name', 'wi.item_name', 'wi.item_code', 'wi.website_image', 'wi.variant_of',
|
||||
'wi.has_variants', 'wi.item_group', 'wi.image', 'wi.web_long_description', 'wi.description',
|
||||
'wi.route', 'wi.website_warehouse']
|
||||
'wi.route', 'wi.website_warehouse', 'wi.ranking']
|
||||
self.conditions = ""
|
||||
self.or_conditions = ""
|
||||
self.substitutions = []
|
||||
|
||||
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
|
||||
|
||||
Args:
|
||||
@@ -39,13 +39,22 @@ class ProductQuery:
|
||||
list: List of results with set fields
|
||||
"""
|
||||
result, discount_list = [], []
|
||||
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 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"
|
||||
self.conditions += " and wi.variant_of IS NULL"
|
||||
|
||||
if attributes:
|
||||
result = self.query_items_with_attributes(attributes, start)
|
||||
@@ -53,6 +62,15 @@ class ProductQuery:
|
||||
result = self.query_items(self.conditions, self.or_conditions,
|
||||
self.substitutions, start=start)
|
||||
|
||||
# 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("ranking"), reverse=True)
|
||||
|
||||
# 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')
|
||||
@@ -140,7 +158,6 @@ class ProductQuery:
|
||||
start=start, with_attributes=True)
|
||||
|
||||
items_dict = {item.name: item for item in items}
|
||||
# TODO: Replace Variants by their parent templates
|
||||
|
||||
all_items.append(set(items_dict.keys()))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user