diff --git a/erpnext/e_commerce/product_list.js b/erpnext/e_commerce/product_list.js index fa35c7e66cf..3aa6e8c4336 100644 --- a/erpnext/e_commerce/product_list.js +++ b/erpnext/e_commerce/product_list.js @@ -39,7 +39,7 @@ erpnext.ProductList = class { if (image) { return ` -
+
${ title } diff --git a/erpnext/e_commerce/product_view.js b/erpnext/e_commerce/product_view.js index a64d55fa0e0..4e1c23c516c 100644 --- a/erpnext/e_commerce/product_view.js +++ b/erpnext/e_commerce/product_view.js @@ -79,7 +79,7 @@ erpnext.ProductView = class { let me = this; this.prepare_product_area_wrapper("grid"); - frappe.require('assets/js/e-commerce.min.js', function() { + frappe.require('/assets/js/e-commerce.min.js', function() { new erpnext.ProductGrid({ items: items, products_section: $("#products-grid-area"), @@ -93,7 +93,7 @@ erpnext.ProductView = class { let me = this; this.prepare_product_area_wrapper("list"); - frappe.require('assets/js/e-commerce.min.js', function() { + frappe.require('/assets/js/e-commerce.min.js', function() { new erpnext.ProductList({ items: items, products_section: $("#products-list-area"), @@ -304,10 +304,10 @@ erpnext.ProductView = class { } let route_params = frappe.utils.get_query_params(); - const query_string = get_query_string({ - start: if_key_exists(route_params.start) || 0, - field_filters: JSON.stringify(if_key_exists(this.field_filters)), - attribute_filters: JSON.stringify(if_key_exists(this.attribute_filters)), + const query_string = me.get_query_string({ + start: me.if_key_exists(route_params.start) || 0, + field_filters: JSON.stringify(me.if_key_exists(this.field_filters)), + attribute_filters: JSON.stringify(me.if_key_exists(this.attribute_filters)), }); window.history.pushState('filters', '', `${location.pathname}?` + query_string); @@ -380,26 +380,26 @@ erpnext.ProductView = class { $("#product-listing").prepend(sub_group_html); } } -}; -function get_query_string(object) { - const url = new URLSearchParams(); - for (let key in object) { - const value = object[key]; - if (value) { - url.append(key, value); + get_query_string(object) { + const url = new URLSearchParams(); + for (let key in object) { + const value = object[key]; + if (value) { + url.append(key, value); + } } + return url.toString(); } - return url.toString(); -} -function if_key_exists(obj) { - let exists = false; - for (let key in obj) { - if (obj.hasOwnProperty(key) && obj[key]) { - exists = true; - break; + if_key_exists(obj) { + let exists = false; + for (let key in obj) { + if (obj.hasOwnProperty(key) && obj[key]) { + exists = true; + break; + } } + return exists ? obj : undefined; } - return exists ? obj : undefined; } \ No newline at end of file diff --git a/erpnext/e_commerce/shopping_cart/cart.py b/erpnext/e_commerce/shopping_cart/cart.py index e81c1d31db4..6bdb02a12e2 100644 --- a/erpnext/e_commerce/shopping_cart/cart.py +++ b/erpnext/e_commerce/shopping_cart/cart.py @@ -89,8 +89,14 @@ def place_order(): if not cint(cart_settings.allow_items_not_in_stock): for item in sales_order.get("items"): - item.reserved_warehouse, is_stock_item = frappe.db.get_value("Item", - item.item_code, ["website_warehouse", "is_stock_item"]) + item.warehouse = frappe.db.get_value( + "Website Item", + { + "item_code": item.item_code + }, + "website_warehouse" + ) + is_stock_item = frappe.db.get_value("Item", item.item_code, "is_stock_item") if is_stock_item: item_stock = get_web_item_qty_in_stock(item.item_code, "website_warehouse") diff --git a/erpnext/public/js/shopping_cart.js b/erpnext/public/js/shopping_cart.js index 80f731e51c5..6b42987c718 100644 --- a/erpnext/public/js/shopping_cart.js +++ b/erpnext/public/js/shopping_cart.js @@ -80,6 +80,7 @@ $.extend(shopping_cart, { } window.location.href = "/login"; } else { + shopping_cart.freeze(); return frappe.call({ type: "POST", method: "erpnext.e_commerce.shopping_cart.cart.update_cart", @@ -91,6 +92,7 @@ $.extend(shopping_cart, { }, btn: opts.btn, callback: function(r) { + shopping_cart.unfreeze(); shopping_cart.set_cart_count(); if(opts.callback) opts.callback(r); @@ -135,7 +137,6 @@ $.extend(shopping_cart, { }, shopping_cart_update: function({item_code, qty, cart_dropdown, additional_notes}) { - frappe.freeze(); shopping_cart.update_cart({ item_code, qty, @@ -143,7 +144,6 @@ $.extend(shopping_cart, { with_items: 1, btn: this, callback: function(r) { - frappe.unfreeze(); if(!r.exc) { $(".cart-items").html(r.message.items); $(".cart-tax-items").html(r.message.taxes); @@ -196,6 +196,29 @@ $.extend(shopping_cart, { }); }); - } + }, + freeze() { + if (window.location.pathname !== "/cart") return + + if (!$('#freeze').length) { + let freeze = $('') + .appendTo("body"); + + setTimeout(function() { + freeze.addClass("show"); + }, 1); + } else { + $("#freeze").addClass("show"); + } + }, + + unfreeze() { + if ($('#freeze').length) { + let freeze = $('#freeze').removeClass("show"); + setTimeout(function() { + freeze.remove(); + }, 1); + } + } }); diff --git a/erpnext/public/scss/shopping_cart.scss b/erpnext/public/scss/shopping_cart.scss index 98ae103bf56..599073cedc7 100644 --- a/erpnext/public/scss/shopping_cart.scss +++ b/erpnext/public/scss/shopping_cart.scss @@ -184,6 +184,12 @@ body.product-page { } } +.list-image { + overflow: hidden; + max-height: 200px; + background-color: white; +} + .product-container { @include card($padding: var(--padding-md)); min-height: 70vh; @@ -415,7 +421,10 @@ body.product-page { } } - +.total-discount { + font-size: var(--text-base); + color: var(--primary-color); +} #page-cart { .shopping-cart-header { @@ -436,6 +445,10 @@ body.product-page { } .cart-table { + tr { + margin-bottom: 1rem; + } + th, tr, td { border-color: var(--border-color); border-width: 1px; @@ -470,6 +483,16 @@ body.product-page { font-weight: 500; } + .sm-item-subtotal { + font-size: var(--text-base); + font-weight: 500; + display: none; + + @include media-breakpoint-between(xs, md) { + display: unset !important; + } + } + .item-rate { font-size: var(--text-md); color: var(--text-muted); @@ -483,10 +506,44 @@ body.product-page { .cart-tax-items { .item-grand-total { font-size: 16px; - font-weight: 600; + font-weight: 700; color: var(--text-color); } } + + .column-sm-view { + @include media-breakpoint-between(xs, md) { + display: none !important; + } + } + + .item-column { + width: 50%; + @include media-breakpoint-between(xs, md) { + width: 70%; + } + } + + .remove-cart-item { + border-radius: 50%; + border: 1px solid var(--gray-100); + width: 22px; + height: 22px; + background-color: var(--gray-200); + float: right; + } + + .remove-cart-item-logo { + margin-bottom: 6px; + margin-left: 1px; + } + + .totals { + padding-right: 4rem; + @include media-breakpoint-between(xs, md) { + padding-right: 1rem; + } + } } .cart-addresses { @@ -497,12 +554,16 @@ body.product-page { .number-spinner { width: 75%; + min-width: 105px; .cart-btn { border: none; - background: var(--gray-100); + background: var(--primary-color); + color: white; box-shadow: none; + width: 24px; height: 28px; align-items: center; + justify-content: center; display: flex; } @@ -514,7 +575,7 @@ body.product-page { .place-order-container { .btn-place-order { - width: 62%; + float: right; } } } @@ -650,12 +711,12 @@ body.product-page { } .not-added { - color: var(--blue-500); - background-color: white; + color: var(--primary-color); + background-color: transparent; border: 1px solid var(--blue-500); &:hover { - background-color: var(--blue-500); + background-color: var(--primary-color); color: white; } } @@ -792,4 +853,13 @@ body.product-page { } .placeholder { font-size: 72px; +} + +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + left: 0; + background-color: var(--gray-100); + height: 100%; } \ No newline at end of file diff --git a/erpnext/templates/includes/cart.js b/erpnext/templates/includes/cart.js index f0781ab68f5..28fe882dca1 100644 --- a/erpnext/templates/includes/cart.js +++ b/erpnext/templates/includes/cart.js @@ -18,6 +18,7 @@ $.extend(shopping_cart, { shopping_cart.bind_place_order(); shopping_cart.bind_request_quotation(); shopping_cart.bind_change_qty(); + shopping_cart.bind_remove_cart_item(); shopping_cart.bind_change_notes(); shopping_cart.bind_coupon_code(); }, @@ -153,6 +154,18 @@ $.extend(shopping_cart, { }); }, + bind_remove_cart_item: function() { + $(".cart-items").on("click", ".remove-cart-item", (e) => { + const $remove_cart_item_btn = $(e.currentTarget); + var item_code = $remove_cart_item_btn.data("item-code"); + + shopping_cart.shopping_cart_update({ + item_code: item_code, + qty: 0 + }); + }) + }, + render_tax_row: function($cart_taxes, doc, shipping_rules) { var shipping_selector; if(shipping_rules) { diff --git a/erpnext/templates/includes/cart/cart_items.html b/erpnext/templates/includes/cart/cart_items.html index d534b8fd7cf..226c600e58d 100644 --- a/erpnext/templates/includes/cart/cart_items.html +++ b/erpnext/templates/includes/cart/cart_items.html @@ -1,42 +1,86 @@ -{% for d in doc.items %} - - -
- {{ d.item_name }} -
-
- {{ d.item_code }} -
- {%- set variant_of = frappe.db.get_value('Item', d.item_code, 'variant_of') %} - {% if variant_of %} - - {{ _('Variant of') }}
{{ variant_of }} - - {% endif %} -
- -
- - -
- - - - - - +{% macro item_subtotal(item) %} +
+ {{ item.get_formatted('amount') }} +
+ + {% if item.is_free_item %} +
+ + {{ _('FREE') }}
- - {% if cart_settings.enable_checkout %} - -
- {{ d.get_formatted('amount') }} -
+ {% else %} - {{ _('Rate:') }} {{ d.get_formatted('rate') }} + {{ _('Rate:') }} {{ item.get_formatted('rate') }} - {% endif %} - +{% endmacro %} + +{% for d in doc.items %} + + +
+ {{ d.item_name }} +
+
+ {{ d.item_code }} +
+ {%- set variant_of = frappe.db.get_value('Item', d.item_code, 'variant_of') %} + {% if variant_of %} + + {{ _('Variant of') }} {{ variant_of }} + + {% endif %} +
+ +
+ + + + +
+ + + + + + + +
+ + + {% if cart_settings.enable_checkout %} +
+ {{ item_subtotal(d) }} +
+ {% endif %} + + + + {% if cart_settings.enable_checkout %} + + {{ item_subtotal(d) }} + + {% endif %} + + + + {% if not d.is_free_item %} +
+ + + +
+ {% endif %} + + {% endfor %} diff --git a/erpnext/templates/includes/order/order_taxes.html b/erpnext/templates/includes/order/order_taxes.html index d2c458e0a49..b821e6253d0 100644 --- a/erpnext/templates/includes/order/order_taxes.html +++ b/erpnext/templates/includes/order/order_taxes.html @@ -1,9 +1,9 @@ {% if doc.taxes %} - + {{ _("Net Total") }} - + {{ doc.get_formatted("net_total") }} @@ -12,10 +12,10 @@ {% for d in doc.taxes %} {% if d.base_tax_amount %} - + {{ d.description }} - + {{ d.get_formatted("base_tax_amount") }} @@ -23,76 +23,62 @@ {% endfor %} {% if doc.doctype == 'Quotation' %} -{% if doc.coupon_code %} - - - {{ _("Discount") }} - - - {% set tot_quotation_discount = [] %} - {%- for item in doc.items -%} - {% if tot_quotation_discount.append((((item.price_list_rate * item.qty) - * item.discount_percentage) / 100)) %}{% endif %} - {% endfor %} - {{ frappe.utils.fmt_money((tot_quotation_discount | sum),currency=doc.currency) }} - - -{% endif %} + {% if doc.coupon_code %} + + + {{ _("Savings") }} + + + {% set tot_quotation_discount = [] %} + {%- for item in doc.items -%} + {% if tot_quotation_discount.append((((item.price_list_rate * item.qty) + * item.discount_percentage) / 100)) %} + {% endif %} + {% endfor %} + {{ frappe.utils.fmt_money((tot_quotation_discount | sum),currency=doc.currency) }} + + + {% endif %} {% endif %} {% if doc.doctype == 'Sales Order' %} -{% if doc.coupon_code %} - - - {{ _("Total Amount") }} - - - - {% set total_amount = [] %} - {%- for item in doc.items -%} - {% if total_amount.append((item.price_list_rate * item.qty)) %}{% endif %} - {% endfor %} - {{ frappe.utils.fmt_money((total_amount | sum),currency=doc.currency) }} - - - - - - {{ _("Applied Coupon Code") }} - - - - {%- for row in frappe.get_all(doctype="Coupon Code", - fields=["coupon_code"], filters={ "name":doc.coupon_code}) -%} - {{ row.coupon_code }} - {% endfor %} - - - - - - {{ _("Discount") }} - - - - {% set tot_SO_discount = [] %} - {%- for item in doc.items -%} - {% if tot_SO_discount.append((((item.price_list_rate * item.qty) - * item.discount_percentage) / 100)) %}{% endif %} - {% endfor %} - {{ frappe.utils.fmt_money((tot_SO_discount | sum),currency=doc.currency) }} - - - -{% endif %} + {% if doc.coupon_code %} + + + {{ _("Applied Coupon Code") }} + + + + {%- for row in frappe.get_all(doctype="Coupon Code", + fields=["coupon_code"], filters={ "name":doc.coupon_code}) -%} + {{ row.coupon_code }} + {% endfor %} + + + + + + {{ _("Savings") }} + + + + {% set tot_SO_discount = [] %} + {%- for item in doc.items -%} + {% if tot_SO_discount.append((((item.price_list_rate * item.qty) + * item.discount_percentage) / 100)) %}{% endif %} + {% endfor %} + {{ frappe.utils.fmt_money((tot_SO_discount | sum),currency=doc.currency) }} + + + + {% endif %} {% endif %} - - + {{ _("Grand Total") }} - + {{ doc.get_formatted("grand_total") }} diff --git a/erpnext/templates/pages/cart.html b/erpnext/templates/pages/cart.html index 87d2d4034a1..13845095c08 100644 --- a/erpnext/templates/pages/cart.html +++ b/erpnext/templates/pages/cart.html @@ -21,7 +21,7 @@ {% if doc.items %}
-
+
@@ -30,11 +30,12 @@ - + {% if cart_settings.enable_checkout %} - + {% endif %} + @@ -47,31 +48,32 @@ {% endif %}
{{ _('Item') }}{{ _('Item') }} {{ _('Quantity') }}{{ _('Subtotal') }}{{ _('Subtotal') }}
-
-
+ +
+ -
+
{% if doc.items %}
- {{ _("Continue Shopping") }} + {{ _('Back to Shop') }} {% if cart_settings.enable_checkout %} {% else %} {% endif %}
diff --git a/erpnext/www/all-products/index.js b/erpnext/www/all-products/index.js index 336aa9a1d4b..ef8c2105bea 100644 --- a/erpnext/www/all-products/index.js +++ b/erpnext/www/all-products/index.js @@ -8,7 +8,7 @@ $(() => { let view_type = "List View"; // Render Product Views and setup Filters - frappe.require('assets/js/e-commerce.min.js', function() { + frappe.require('/assets/js/e-commerce.min.js', function() { new erpnext.ProductView({ view_type: view_type, products_section: $('#product-listing'), @@ -23,20 +23,6 @@ $(() => { e_commerce.shopping_cart.bind_add_to_cart_action(); e_commerce.wishlist.bind_wishlist_action(); } - - // bind_search() { - // $('input[type=search]').on('keydown', (e) => { - // if (e.keyCode === 13) { - // // Enter - // const value = e.target.value; - // if (value) { - // window.location.search = 'search=' + e.target.value; - // } else { - // window.location.search = ''; - // } - // } - // }); - // } } new ProductListing();