Merge branch 'version-13-hotfix' of https://github.com/frappe/erpnext into e-commerce-refactor

This commit is contained in:
marination
2021-06-28 16:50:08 +05:30
701 changed files with 9390 additions and 13054 deletions

View File

@@ -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:

View File

@@ -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()

View File

@@ -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()))