diff --git a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.py b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.py index cb61de1feef..9d3ca37abbf 100644 --- a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.py +++ b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe -from frappe.utils import cint +from frappe.utils import cint, comma_and from frappe import _, msgprint from frappe.model.document import Document from frappe.utils import get_datetime, get_datetime_str, now_datetime diff --git a/erpnext/e_commerce/product_configurator/test_product_configurator.py b/erpnext/e_commerce/product_configurator/test_product_configurator.py index 0f619b5c67b..87f449fa50a 100644 --- a/erpnext/e_commerce/product_configurator/test_product_configurator.py +++ b/erpnext/e_commerce/product_configurator/test_product_configurator.py @@ -1,7 +1,8 @@ from __future__ import unicode_literals from bs4 import BeautifulSoup -import frappe, unittest +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 diff --git a/erpnext/e_commerce/product_query.py b/erpnext/e_commerce/product_query.py index 9fb3c3a6d70..2c91a72c97c 100644 --- a/erpnext/e_commerce/product_query.py +++ b/erpnext/e_commerce/product_query.py @@ -56,9 +56,13 @@ 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: - item.formatted_price = product_info['price'].get('formatted_price') if product_info['price'] else None - item.price = product_info['price'].get('price_list_rate') if product_info['price'] else None + 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') if self.settings.show_stock_availability: if item.get("website_warehouse"): diff --git a/erpnext/e_commerce/shopping_cart/cart.py b/erpnext/e_commerce/shopping_cart/cart.py index d10920d9364..1a1cbd707d2 100644 --- a/erpnext/e_commerce/shopping_cart/cart.py +++ b/erpnext/e_commerce/shopping_cart/cart.py @@ -10,7 +10,7 @@ from frappe.contacts.doctype.address.address import get_address_display from erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings import get_shopping_cart_settings from frappe.utils.nestedset import get_root_of from erpnext.accounts.utils import get_account_name -from erpnext.utilities.product import get_qty_in_stock +from erpnext.utilities.product import get_web_item_qty_in_stock from frappe.contacts.doctype.contact.contact import get_contact_name @@ -93,7 +93,7 @@ def place_order(): item.item_code, ["website_warehouse", "is_stock_item"]) if is_stock_item: - item_stock = get_qty_in_stock(item.item_code, "website_warehouse") + item_stock = get_web_item_qty_in_stock(item.item_code, "website_warehouse") if not cint(item_stock.in_stock): throw(_("{1} Not in Stock").format(item.item_code)) if item.qty > item_stock.stock_qty[0][0]: diff --git a/erpnext/e_commerce/shopping_cart/product_info.py b/erpnext/e_commerce/shopping_cart/product_info.py index 6f6379b279a..1aeed199e5b 100644 --- a/erpnext/e_commerce/shopping_cart/product_info.py +++ b/erpnext/e_commerce/shopping_cart/product_info.py @@ -7,7 +7,7 @@ import frappe from erpnext.e_commerce.shopping_cart.cart import _get_cart_quotation, _set_price_list from erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings \ import get_shopping_cart_settings, show_quantity_in_website -from erpnext.utilities.product import get_price, get_qty_in_stock, get_non_stock_item_status +from erpnext.utilities.product import get_price, get_web_item_qty_in_stock, get_non_stock_item_status @frappe.whitelist(allow_guest=True) def get_product_info_for_website(item_code, skip_quotation_creation=False): @@ -29,8 +29,7 @@ def get_product_info_for_website(item_code, skip_quotation_creation=False): cart_settings.default_customer_group, cart_settings.company ) - - stock_status = get_qty_in_stock(item_code, "website_warehouse") + stock_status = get_web_item_qty_in_stock(item_code, "website_warehouse") product_info = { "price": price, diff --git a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.py b/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.py index 91c7bf5850a..64bc49a7e9c 100644 --- a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.py +++ b/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.py @@ -8,7 +8,6 @@ from frappe.model.document import Document from frappe.utils import add_years, now, get_datetime, get_datetime_str, cint from frappe import _ from frappe.frappeclient import FrappeClient -from erpnext.utilities.product import get_price, get_qty_in_stock from six import string_types class MarketplaceSettings(Document): diff --git a/erpnext/public/scss/shopping_cart.scss b/erpnext/public/scss/shopping_cart.scss index 1edfb81467e..719eceea264 100644 --- a/erpnext/public/scss/shopping_cart.scss +++ b/erpnext/public/scss/shopping_cart.scss @@ -59,7 +59,7 @@ body.product-page { .item-card-group-section { .card { - height: 360px; + height: 400px; align-items: center; justify-content: center; @@ -134,6 +134,11 @@ body.product-page { } .item-card { + padding: var(--padding-sm); + min-width: 300px; + } + + .wishlist-card { padding: var(--padding-sm); min-width: 260px; } @@ -624,8 +629,7 @@ body.product-page { .btn-explore-variants { box-shadow: none; margin: var(--margin-sm) 0; - margin-left: 18px; - max-height: 30px; // to avoid resizing on window resize + max-height: 50px; // to avoid resizing on window resize flex: none; transition: 0.3s ease; color: var(--orange-500); @@ -641,13 +645,12 @@ body.product-page { .btn-add-to-cart-list{ box-shadow: none; margin: var(--margin-sm) 0; - max-height: 30px; // to avoid resizing on window resize + max-height: 50px; // to avoid resizing on window resize flex: none; transition: 0.3s ease; } .not-added { - margin-left: 18px; color: var(--blue-500); background-color: white; border: 1px solid var(--blue-500); @@ -659,7 +662,6 @@ body.product-page { } .added-to-cart { - margin-left: 18px; background-color: var(--dark-green-400); color: white; border: 2px solid var(--green-300); diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py index 5332ba18847..fc805a36303 100644 --- a/erpnext/setup/doctype/item_group/item_group.py +++ b/erpnext/setup/doctype/item_group/item_group.py @@ -10,7 +10,6 @@ from frappe.utils.nestedset import NestedSet from frappe.website.website_generator import WebsiteGenerator from frappe.website.render import clear_cache from frappe.website.doctype.website_slideshow.website_slideshow import get_slideshow -from erpnext.utilities.product import get_qty_in_stock from six.moves.urllib.parse import quote from erpnext.e_commerce.product_query import ProductQuery from erpnext.e_commerce.filters import ProductFiltersBuilder diff --git a/erpnext/templates/generators/item/item.html b/erpnext/templates/generators/item/item.html index b3e9eded1ac..bfcb4f340eb 100644 --- a/erpnext/templates/generators/item/item.html +++ b/erpnext/templates/generators/item/item.html @@ -39,7 +39,7 @@ {{ doc.website_content or '' }} - {% if shopping_cart.cart_settings.enable_reviews %} + {% if shopping_cart.cart_settings.enable_reviews and not doc.has_variants %} {% include "templates/generators/item/item_reviews.html"%} {% endif %} diff --git a/erpnext/templates/generators/item/item_add_to_cart.html b/erpnext/templates/generators/item/item_add_to_cart.html index 4c426d89301..4fd79ea4fc9 100644 --- a/erpnext/templates/generators/item/item_add_to_cart.html +++ b/erpnext/templates/generators/item/item_add_to_cart.html @@ -7,9 +7,21 @@
{% if cart_settings.show_price and product_info.price %} + {% set price_info = product_info.price %} + + {% if price_info.formatted_mrp %} + + M.R.P.: + {{ price_info.formatted_mrp }} + + + {{ price_info.get("formatted_discount_percent") or price_info.get("formatted_discount_rate")}} OFF + + {% endif %} +
- {{ product_info.price.formatted_price_sales_uom }} - ({{ product_info.price.formatted_price }} / {{ product_info.uom }}) + {{ price_info.formatted_price_sales_uom }} + ({{ price_info.formatted_price }} / {{ product_info.uom }})
{% else %} {{ _("Unit of Measurement") }} : {{ product_info.uom }} diff --git a/erpnext/templates/includes/macros.html b/erpnext/templates/includes/macros.html index 775f68f0105..200b0b2f07d 100644 --- a/erpnext/templates/includes/macros.html +++ b/erpnext/templates/includes/macros.html @@ -138,35 +138,47 @@ {% endif %}
{% if is_featured %} -
{{ item.formatted_price or '' }}
-
{{ description or '' }}
- {% else %} -
{{ item.item_group or '' }}
-
- {% if item.formatted_price %}
{{ item.formatted_price or '' }}
- {% endif %} - {% if item.has_variants %} - -
- {{ _('Explore') }} +
{{ description or '' }}
+ {% else %} +
{{ item.item_group or '' }}
+ + {% if item.formatted_price %} +
+ {{ item.formatted_price or '' }} + + {% if item.get("formatted_mrp") %} + + {{ item.formatted_mrp }} + + + {{ item.discount }} OFF + + {% endif %} +
-
- {% elif settings.enabled %} -
+
+ {{ _('Explore') }} +
+ + {% elif settings.enabled and (settings.allow_items_not_in_stock or item.in_stock != "red")%} +
{{ _('Add to Cart') }}
{% endif %} -
{% endif %}
{%- endmacro -%} {%- macro wishlist_card(item, settings) %} -
-
+
+
{% if item.image %} {%- endmacro -%} diff --git a/erpnext/templates/pages/wishlist.html b/erpnext/templates/pages/wishlist.html index 6e7a65bcc77..4c039e3c1df 100644 --- a/erpnext/templates/pages/wishlist.html +++ b/erpnext/templates/pages/wishlist.html @@ -7,7 +7,7 @@ {% block page_content %} {% if items %}
-
+
{% from "erpnext/templates/includes/macros.html" import wishlist_card %} {% for item in items %} diff --git a/erpnext/templates/pages/wishlist.py b/erpnext/templates/pages/wishlist.py index 96c83da443a..e534a23b9d4 100644 --- a/erpnext/templates/pages/wishlist.py +++ b/erpnext/templates/pages/wishlist.py @@ -5,26 +5,45 @@ from __future__ import unicode_literals no_cache = 1 import frappe +from erpnext.utilities.product import get_price +from erpnext.e_commerce.shopping_cart.cart import _set_price_list def get_context(context): settings = frappe.get_doc("E Commerce Settings") items = get_wishlist_items() + selling_price_list = _set_price_list(settings) - if settings.show_stock_availability: - for item in items: - stock_qty = frappe.utils.flt( - frappe.db.get_value("Bin", - { - "item_code": item.item_code, - "warehouse": item.get("warehouse") - }, - "actual_qty") - ) - item.available = True if stock_qty else False + for item in items: + if settings.show_stock_availability: + item.available = get_stock_availability(item.item_code, item.get("warehouse")) + + price_details = get_price( + item.item_code, + selling_price_list, + settings.default_customer_group, + settings.company + ) + + if price_details: + item.formatted_mrp = price_details.get('formatted_mrp') + if item.formatted_mrp: + item.discount = price_details.get('formatted_discount_percent') or \ + price_details.get('formatted_discount_rate') context.items = items context.settings = settings +def get_stock_availability(item_code, warehouse): + stock_qty = frappe.utils.flt( + frappe.db.get_value("Bin", + { + "item_code": item_code, + "warehouse": warehouse + }, + "actual_qty") + ) + return True if stock_qty else False + def get_wishlist_items(): if frappe.db.exists("Wishlist", frappe.session.user): return frappe.db.sql(""" diff --git a/erpnext/utilities/product.py b/erpnext/utilities/product.py index 66d6cd38886..34e12cfb525 100644 --- a/erpnext/utilities/product.py +++ b/erpnext/utilities/product.py @@ -8,15 +8,15 @@ from frappe.utils import cint, fmt_money, flt, nowdate, getdate from erpnext.accounts.doctype.pricing_rule.pricing_rule import get_pricing_rule_for_item from erpnext.stock.doctype.batch.batch import get_batch_qty -def get_qty_in_stock(item_code, item_warehouse_field, warehouse=None): +def get_web_item_qty_in_stock(item_code, item_warehouse_field, warehouse=None): in_stock, stock_qty = 0, '' template_item_code, is_stock_item = frappe.db.get_value("Item", item_code, ["variant_of", "is_stock_item"]) if not warehouse: - warehouse = frappe.db.get_value("Item", item_code, item_warehouse_field) + warehouse = frappe.db.get_value("Website Item", {"item_code": item_code}, item_warehouse_field) if not warehouse and template_item_code and template_item_code != item_code: - warehouse = frappe.db.get_value("Item", template_item_code, item_warehouse_field) + warehouse = frappe.db.get_value("Website Item", {"item_code": template_item_code }, item_warehouse_field) if warehouse: stock_qty = frappe.db.sql(""" @@ -91,17 +91,26 @@ def get_price(item_code, price_list, customer_group, company, qty=1): "for_shopping_cart": True, "currency": frappe.db.get_value("Price List", price_list, "currency") })) + price_obj = price[0] if pricing_rule: + # price without any rules applied + mrp = price_obj.price_list_rate or 0 + if pricing_rule.pricing_rule_for == "Discount Percentage": - price[0].price_list_rate = flt(price[0].price_list_rate * (1.0 - (flt(pricing_rule.discount_percentage) / 100.0))) + price_obj.formatted_discount_percent = str(flt(pricing_rule.discount_percentage, 0)) + "%" + price_obj.price_list_rate = flt(price_obj.price_list_rate * (1.0 - (flt(pricing_rule.discount_percentage) / 100.0))) if pricing_rule.pricing_rule_for == "Rate": - price[0].price_list_rate = pricing_rule.price_list_rate + rate_discount = flt(mrp) - flt(pricing_rule.price_list_rate) + if rate_discount > 0: + price_obj.formatted_discount_rate = fmt_money(rate_discount, currency=price_obj["currency"]) + price_obj.price_list_rate = pricing_rule.price_list_rate or 0 - price_obj = price[0] if price_obj: price_obj["formatted_price"] = fmt_money(price_obj["price_list_rate"], currency=price_obj["currency"]) + if mrp != price_obj["price_list_rate"]: + price_obj["formatted_mrp"] = fmt_money(mrp, currency=price_obj["currency"]) price_obj["currency_symbol"] = not cint(frappe.db.get_default("hide_currency_symbol")) \ and (frappe.db.get_value("Currency", price_obj.currency, "symbol", cache=True) or price_obj.currency) \ @@ -122,15 +131,15 @@ def get_price(item_code, price_list, customer_group, company, qty=1): price_obj["currency"] = "" if not price_obj["formatted_price"]: - price_obj["formatted_price"] = "" + price_obj["formatted_price"], price_obj["formatted_mrp"] = "", "" return price_obj def get_non_stock_item_status(item_code, item_warehouse_field): -#if item belongs to product bundle, check if bundle items are in stock + # if item is a product bundle, check if its bundle items are in stock if frappe.db.exists("Product Bundle", item_code): items = frappe.get_doc("Product Bundle", item_code).get_all_children() bundle_warehouse = frappe.db.get_value('Item', item_code, item_warehouse_field) - return all([ get_qty_in_stock(d.item_code, item_warehouse_field, bundle_warehouse).in_stock for d in items ]) + return all([ get_web_item_qty_in_stock(d.item_code, item_warehouse_field, bundle_warehouse).in_stock for d in items ]) else: return 1