diff --git a/erpnext/e_commerce/shopping_cart/cart.py b/erpnext/e_commerce/shopping_cart/cart.py index 03aa36fa883..7a509cab6b0 100644 --- a/erpnext/e_commerce/shopping_cart/cart.py +++ b/erpnext/e_commerce/shopping_cart/cart.py @@ -276,10 +276,29 @@ def guess_territory(): def decorate_quotation_doc(doc): for d in doc.get("items", []): + item_code = d.item_code + fields = ["web_item_name", "thumbnail", "website_image", "description", "route"] + + # Variant Item + if not frappe.db.exists("Website Item", {"item_code": item_code}): + variant_data = frappe.db.get_values( + "Item", + filters={"item_code": item_code}, + fieldname=["variant_of", "item_name", "image"], + as_dict=True + )[0] + item_code = variant_data.variant_of + fields = fields[1:] + d.website_item_name = variant_data.item_name + + if variant_data.image: # get image from variant or template web item + d.thumbnail = variant_data.image + fields = fields[2:] + d.update(frappe.db.get_value( "Website Item", - {"item_code": d.item_code}, - ["web_item_name", "thumbnail", "website_image", "description", "route"], + {"item_code": item_code}, + fields, as_dict=True) ) diff --git a/erpnext/e_commerce/shopping_cart/test_shopping_cart.py b/erpnext/e_commerce/shopping_cart/test_shopping_cart.py index 784f869e579..4e14068c647 100644 --- a/erpnext/e_commerce/shopping_cart/test_shopping_cart.py +++ b/erpnext/e_commerce/shopping_cart/test_shopping_cart.py @@ -9,8 +9,13 @@ from frappe.utils import add_months, nowdate from erpnext.accounts.doctype.tax_rule.tax_rule import ConflictingTaxRule from erpnext.e_commerce.doctype.website_item.website_item import make_website_item -from erpnext.e_commerce.shopping_cart.cart import _get_cart_quotation, get_party, update_cart -from erpnext.tests.utils import create_test_contact_and_address +from erpnext.e_commerce.shopping_cart.cart import ( + _get_cart_quotation, + get_cart_quotation, + get_party, + update_cart, +) +from erpnext.tests.utils import change_settings, create_test_contact_and_address class TestShoppingCart(unittest.TestCase): @@ -34,6 +39,7 @@ class TestShoppingCart(unittest.TestCase): make_website_item(frappe.get_cached_doc("Item", "_Test Item 2")) def tearDown(self): + frappe.db.rollback() frappe.set_user("Administrator") self.disable_shopping_cart() @@ -128,6 +134,40 @@ class TestShoppingCart(unittest.TestCase): self.remove_test_quotation(quotation) + @change_settings("E Commerce Settings",{ + "company": "_Test Company", + "enabled": 1, + "default_customer_group": "_Test Customer Group", + "price_list": "_Test Price List India", + "show_price": 1 + }) + def test_add_item_variant_without_web_item_to_cart(self): + "Test adding Variants having no Website Items in cart via Template Web Item." + from erpnext.controllers.item_variant import create_variant + from erpnext.e_commerce.doctype.website_item.website_item import make_website_item + from erpnext.stock.doctype.item.test_item import make_item + + template_item = make_item("Test-Tshirt-Temp", { + "has_variant": 1, + "variant_based_on": "Item Attribute", + "attributes": [ + {"attribute": "Test Size"}, + {"attribute": "Test Colour"} + ] + }) + variant = create_variant("Test-Tshirt-Temp", { + "Test Size": "Small", "Test Colour": "Red" + }) + variant.save() + make_website_item(template_item) # publish template not variant + + update_cart("Test-Tshirt-Temp-S-R", 1) + + cart = get_cart_quotation() # test if cart page gets data without errors + doc = cart.get("doc") + + self.assertEqual(doc.get("items")[0].item_name, "Test-Tshirt-Temp-S-R") + def create_tax_rule(self): tax_rule = frappe.get_test_records("Tax Rule")[0] try: diff --git a/erpnext/e_commerce/variant_selector/test_variant_selector.py b/erpnext/e_commerce/variant_selector/test_variant_selector.py index 31c86f1fd53..83f08733612 100644 --- a/erpnext/e_commerce/variant_selector/test_variant_selector.py +++ b/erpnext/e_commerce/variant_selector/test_variant_selector.py @@ -4,23 +4,22 @@ import frappe from erpnext.controllers.item_variant import create_variant from erpnext.e_commerce.doctype.website_item.website_item import make_website_item +from erpnext.e_commerce.variant_selector.utils import get_next_attribute_and_values from erpnext.stock.doctype.item.test_item import make_item +from erpnext.tests.utils import ERPNextTestCase, change_settings test_dependencies = ["Item"] -class TestVariantSelector(unittest.TestCase): +class TestVariantSelector(ERPNextTestCase): - def setUp(self) -> None: - self.template_item = make_item("Test-Tshirt-Temp", { + @classmethod + def setUpClass(cls): + template_item = make_item("Test-Tshirt-Temp", { "has_variant": 1, "variant_based_on": "Item Attribute", "attributes": [ - { - "attribute": "Test Size" - }, - { - "attribute": "Test Colour" - } + {"attribute": "Test Size"}, + {"attribute": "Test Colour"} ] }) @@ -28,19 +27,16 @@ class TestVariantSelector(unittest.TestCase): for size in ("Large", "Medium",): for colour in ("Red", "Green",): variant = create_variant("Test-Tshirt-Temp", { - "Test Size": size, - "Test Colour": colour + "Test Size": size, "Test Colour": colour }) variant.save() variant = create_variant("Test-Tshirt-Temp", { - "Test Size": "Small", - "Test Colour": "Red" + "Test Size": "Small", "Test Colour": "Red" }) variant.save() - def tearDown(self): - frappe.db.rollback() + make_website_item(template_item) # publish template not variants def test_item_attributes(self): """ @@ -51,8 +47,6 @@ class TestVariantSelector(unittest.TestCase): """ from erpnext.e_commerce.variant_selector.utils import get_attributes_and_values - make_website_item(self.template_item) # publish template not variants - attr_data = get_attributes_and_values("Test-Tshirt-Temp") self.assertEqual(attr_data[0]["attribute"], "Test Size") @@ -72,7 +66,7 @@ class TestVariantSelector(unittest.TestCase): self.assertEqual(len(attr_data[0]["values"]), 2) # ['Medium', 'Large'] # teardown - small_variant.disabled = 1 + small_variant.disabled = 0 small_variant.save() def test_next_item_variant_values(self): @@ -84,8 +78,6 @@ class TestVariantSelector(unittest.TestCase): There is a ** Small-Red ** Tshirt. No other colour in this size. On selecting ** Small **, only ** Red ** should be selectable next. """ - from erpnext.e_commerce.variant_selector.utils import get_next_attribute_and_values - next_values = get_next_attribute_and_values("Test-Tshirt-Temp", selected_attributes={"Test Size": "Small"}) next_colours = next_values["valid_options_for_attributes"]["Test Colour"] filtered_items = next_values["filtered_items"] @@ -94,3 +86,29 @@ class TestVariantSelector(unittest.TestCase): self.assertEqual(next_colours.pop(), "Red") self.assertEqual(len(filtered_items), 1) self.assertEqual(filtered_items.pop(), "Test-Tshirt-Temp-S-R") + + @change_settings("E Commerce Settings",{ + "company": "_Test Company", + "enabled": 1, + "default_customer_group": "_Test Customer Group", + "price_list": "_Test Price List India", + "show_price": 1 + }) + def test_exact_match_with_price(self): + """ + Test price fetching and matching of variant without Website Item + """ + from erpnext.e_commerce.doctype.website_item.test_website_item import make_web_item_price + + frappe.set_user("Administrator") + make_web_item_price(item_code="Test-Tshirt-Temp-S-R", price_list_rate=100) + next_values = get_next_attribute_and_values( + "Test-Tshirt-Temp", + selected_attributes={"Test Size": "Small", "Test Colour": "Red"} + ) + price_info = next_values["product_info"]["price"] + + self.assertEqual(next_values["exact_match"][0],"Test-Tshirt-Temp-S-R") + self.assertEqual(next_values["exact_match"][0],"Test-Tshirt-Temp-S-R") + self.assertEqual(price_info["price_list_rate"], 100.0) + self.assertEqual(price_info["formatted_price_sales_uom"], "₹ 100.00") \ No newline at end of file diff --git a/erpnext/e_commerce/variant_selector/utils.py b/erpnext/e_commerce/variant_selector/utils.py index 61df3adca58..5caa4d0819f 100644 --- a/erpnext/e_commerce/variant_selector/utils.py +++ b/erpnext/e_commerce/variant_selector/utils.py @@ -1,7 +1,12 @@ import frappe from frappe.utils import cint +from erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings import ( + get_shopping_cart_settings, +) +from erpnext.e_commerce.shopping_cart.cart import _set_price_list from erpnext.e_commerce.variant_selector.item_variants_cache import ItemVariantsCacheManager +from erpnext.utilities.product import get_price def get_item_codes_by_attributes(attribute_filters, template_item_code=None): @@ -143,14 +148,13 @@ def get_next_attribute_and_values(item_code, selected_attributes): filtered_items_count = len(filtered_items) # get product info if exact match - from erpnext.e_commerce.shopping_cart.product_info import get_product_info_for_website + # from erpnext.e_commerce.shopping_cart.product_info import get_product_info_for_website if exact_match: - data = get_product_info_for_website(exact_match[0]) - product_info = data.product_info + cart_settings = get_shopping_cart_settings() + product_info = get_item_variant_price_dict(exact_match[0], cart_settings) + if product_info: - product_info["allow_items_not_in_stock"] = cint(data.cart_settings.allow_items_not_in_stock) - if not data.cart_settings.show_price: - product_info = None + product_info["allow_items_not_in_stock"] = cint(cart_settings.allow_items_not_in_stock) else: product_info = None @@ -195,3 +199,20 @@ def get_item_attributes(item_code): return attributes +def get_item_variant_price_dict(item_code, cart_settings): + if cart_settings.enabled and cart_settings.show_price: + is_guest = frappe.session.user == "Guest" + # Show Price if logged in. + # If not logged in, check if price is hidden for guest. + if not is_guest or not cart_settings.hide_price_for_guest: + price_list = _set_price_list(cart_settings, None) + price = get_price( + item_code, + price_list, + cart_settings.default_customer_group, + cart_settings.company + ) + return {"price": price} + + return None + diff --git a/erpnext/templates/generators/item/item_configure.js b/erpnext/templates/generators/item/item_configure.js index b5f92989ef4..231ae0587ed 100644 --- a/erpnext/templates/generators/item/item_configure.js +++ b/erpnext/templates/generators/item/item_configure.js @@ -214,7 +214,7 @@ class ItemConfigure { ? `