From 3dceb8438efe0cab105e72eef70f0a482d08cc89 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 19 Jun 2013 14:57:14 +0530 Subject: [PATCH] [cart] add to cart, update cart --- public/js/website_utils.js | 87 ++++++++++----------- website/css/website.css | 4 + website/helpers/cart.py | 51 ++++++------ website/helpers/product.py | 12 ++- website/templates/css/product_page.css | 3 - website/templates/html/product_page.html | 19 +++-- website/templates/js/cart.js | 38 ++++++++- website/templates/js/product_page.js | 98 +++++++++++++++--------- website/templates/pages/cart.html | 18 ++++- 9 files changed, 203 insertions(+), 127 deletions(-) diff --git a/public/js/website_utils.js b/public/js/website_utils.js index 1484f00946..83becc6b0e 100644 --- a/public/js/website_utils.js +++ b/public/js/website_utils.js @@ -15,7 +15,6 @@ erpnext.send_message = function(opts) { wn.call = function(opts) { if(opts.btn) { - var $spinner = $('').appendTo($(opts.btn).parent()) $(opts.btn).attr("disabled", "disabled"); } @@ -23,6 +22,8 @@ wn.call = function(opts) { $(opts.msg).toggle(false); } + if(!opts.args) opts.args = {}; + // get or post? if(!opts.args._type) { opts.args._type = opts.type || "GET"; @@ -48,10 +49,17 @@ wn.call = function(opts) { success: function(data) { if(opts.btn) { $(opts.btn).attr("disabled", false); - $spinner.remove(); } if(data.exc) { - console.log(data.exc); + if(opts.btn) { + $(opts.btn).addClass("btn-danger"); + setTimeout(function() { $(opts.btn).removeClass("btn-danger"); }, 1000); + } + } else{ + if(opts.btn) { + $(opts.btn).addClass("btn-success"); + setTimeout(function() { $(opts.btn).removeClass("btn-success"); }, 1000); + } } if(opts.msg && data.message) { $(opts.msg).html(data.message).toggle(true); @@ -75,11 +83,8 @@ $(document).ready(function() { $("#user-full-name").text(full_name); } - wn.cart.update_display(); $("#user-tools a").tooltip({"placement":"bottom"}); $("#user-tools-post-login a").tooltip({"placement":"bottom"}); - - $(window).on("storage", function() { wn.cart.update_display(); }); }); // Utility functions @@ -101,6 +106,12 @@ function get_url_arg(name) { return decodeURIComponent(results[1]); } +function make_query_string(obj) { + var query_params = []; + $.each(obj, function(k, v) { query_params.push(encodeURIComponent(k) + "=" + encodeURIComponent(v)); }); + return "?" + query_params.join("&"); +} + function repl(s, dict) { if(s==null)return ''; for(key in dict) { @@ -166,48 +177,30 @@ if (typeof Array.prototype.map !== "function") { // shopping cart if(!wn.cart) wn.cart = {}; +var full_name = getCookie("full_name"); + $.extend(wn.cart, { - get_count: function() { - return Object.keys(this.get_cart()).length; - }, - - add_to_cart: function(itemprop) { - var cart = this.get_cart(); - cart[itemprop.item_code] = $.extend(itemprop, {qty: 1}); - this.set_cart(cart); - console.log(this.get_cart()); - }, - - remove_from_cart: function(item_code) { - var cart = this.get_cart(); - delete cart[item_code]; - this.set_cart(cart); - console.log(this.get_cart()); - }, - - get_cart: function() { - if( !("localStorage" in window) ) { - alert("Your browser seems to be ancient. Please use a modern browser."); - throw "ancient browser error"; + update_cart: function(opts) { + if(!full_name) { + if(localStorage) { + localStorage.setItem("last_visited", window.location.pathname.slice(1)); + localStorage.setItem("pending_add_to_cart", opts.item_code); + } + window.location.href = "login"; + } else { + wn.call({ + type: "POST", + method: "website.helpers.cart.update_cart", + args: { + item_code: opts.item_code, + qty: opts.qty + }, + btn: opts.btn, + callback: function(r) { + if(opts.callback) + opts.callback(r); + } + }); } - - return JSON.parse(localStorage.getItem("cart")) || {}; }, - - set_cart: function(cart) { - localStorage.setItem("cart", JSON.stringify(cart)); - wn.cart.update_display(); - }, - - update_display: function() { - $(".cart-count").text("( " + wn.cart.get_count() + " )"); - }, - - set_value_in_cart: function(item_code, fieldname, value) { - var cart = this.get_cart(); - if(cart[item_code]) { - cart[item_code][fieldname] = value; - this.set_cart(cart); - } - } }); \ No newline at end of file diff --git a/website/css/website.css b/website/css/website.css index 4af35dcc72..7efceb2e63 100644 --- a/website/css/website.css +++ b/website/css/website.css @@ -6,6 +6,10 @@ h1, h2, h3, h4, h5 { font-weight: bold; } +a { + cursor: pointer; +} + .content { padding-bottom: 30px; } diff --git a/website/helpers/cart.py b/website/helpers/cart.py index efeb9759a2..b62e0bb7cf 100644 --- a/website/helpers/cart.py +++ b/website/helpers/cart.py @@ -7,20 +7,13 @@ from webnotes import _, msgprint import webnotes.defaults from webnotes.utils import today, get_fullname -@webnotes.whitelist() -def add_to_cart(item_code): - update_qty(item_code, 1) - -@webnotes.whitelist() -def remove_from_cart(item_code): - update_qty(item_code, 0) +class WebsitePriceListMissingError(webnotes.ValidationError): pass @webnotes.whitelist() -def update_qty(item_code, qty_to_set): - party = get_lead_or_customer() - quotation = get_shopping_cart_quotation(party) +def update_cart(item_code, qty): + quotation = _get_cart_quotation() - if qty_to_set == 0: + if qty == 0: quotation.set_doclist(quotation.doclist.get({"item_code": ["!=", item_code]})) else: quotation_items = quotation.doclist.get({"item_code": item_code}) @@ -29,10 +22,10 @@ def update_qty(item_code, qty_to_set): "doctype": "Quotation Item", "parentfield": "quotation_details", "item_code": item_code, - "qty": qty_to_set + "qty": qty }) else: - quotation_items[0].qty = qty_to_set + quotation_items[0].qty = qty quotation.ignore_permissions = True quotation.save() @@ -59,7 +52,16 @@ def get_lead_or_customer(): return lead_bean.doc -def get_shopping_cart_quotation(party): + +@webnotes.whitelist() +def get_cart_quotation(): + return [d.fields for d in _get_cart_quotation(get_lead_or_customer()).doclist] + + +def _get_cart_quotation(party=None): + if not party: + party = get_lead_or_customer() + quotation = webnotes.conn.get_value("Quotation", {party.doctype.lower(): party.name, "order_type": "Shopping Cart", "docstatus": 0}) @@ -104,15 +106,14 @@ def get_price_list_using_geoip(): {"use_for_website": 1, "valid_for_all_countries": 1}) if not price_list_name: - raise Exception, "No website Price List specified" + raise WebsitePriceListMissingError, "No website Price List specified" return price_list_name @webnotes.whitelist() def checkout(): - party = get_lead_or_customer() - quotation = get_shopping_cart_quotation(party) + quotation = _get_cart_quotation() quotation.ignore_permissions = True quotation.submit() @@ -143,21 +144,21 @@ class TestCart(unittest.TestCase): def test_add_to_cart(self): webnotes.session.user = "test@example.com" - add_to_cart("_Test Item") + update_cart("_Test Item", 1) - quotation = get_shopping_cart_quotation(get_lead_or_customer()) + quotation = _get_cart_quotation() quotation_items = quotation.doclist.get({"parentfield": "quotation_details", "item_code": "_Test Item"}) self.assertTrue(quotation_items) self.assertEquals(quotation_items[0].qty, 1) return quotation - def test_update_qty(self): + def test_update_cart(self): self.test_add_to_cart() - update_qty("_Test Item", 5) + update_cart("_Test Item", 5) - quotation = get_shopping_cart_quotation(get_lead_or_customer()) + quotation = _get_cart_quotation() quotation_items = quotation.doclist.get({"parentfield": "quotation_details", "item_code": "_Test Item"}) self.assertTrue(quotation_items) self.assertEquals(quotation_items[0].qty, 5) @@ -167,16 +168,16 @@ class TestCart(unittest.TestCase): def test_remove_from_cart(self): quotation0 = self.test_add_to_cart() - remove_from_cart("_Test Item") + update_cart("_Test Item", 0) - quotation = get_shopping_cart_quotation(get_lead_or_customer()) + quotation = _get_cart_quotation() self.assertEquals(quotation0.doc.name, quotation.doc.name) quotation_items = quotation.doclist.get({"parentfield": "quotation_details", "item_code": "_Test Item"}) self.assertEquals(quotation_items, []) def test_checkout(self): - quotation = self.test_update_qty() + quotation = self.test_update_cart() sales_order = checkout() self.assertEquals(sales_order.doclist.getone({"item_code": "_Test Item"}).prevdoc_docname, quotation.doc.name) \ No newline at end of file diff --git a/website/helpers/product.py b/website/helpers/product.py index fb4d4e4aed..e513c2d1d0 100644 --- a/website/helpers/product.py +++ b/website/helpers/product.py @@ -25,18 +25,26 @@ def get_product_info(item_code): (item_code, price_list), as_dict=1) or [] price = price and price[0] or None - + qty = 0 + if price: price["formatted_price"] = fmt_money(price["ref_rate"], currency=price["ref_currency"]) price["ref_currency"] = not cint(webnotes.conn.get_default("hide_currency_symbol")) \ and (webnotes.conn.get_value("Currency", price.ref_currency, "symbol") or price.ref_currency) \ or "" + + if webnotes.session.user != "Guest": + from website.helpers.cart import _get_cart_quotation + item = _get_cart_quotation().doclist.get({"item_code": item_code}) + if item: + qty = item[0].qty return { "price": price, "stock": in_stock, - "uom": webnotes.conn.get_value("Item", item_code, "stock_uom") + "uom": webnotes.conn.get_value("Item", item_code, "stock_uom"), + "qty": qty } @webnotes.whitelist(allow_guest=True) diff --git a/website/templates/css/product_page.css b/website/templates/css/product_page.css index e2f42937ec..566b6b57aa 100644 --- a/website/templates/css/product_page.css +++ b/website/templates/css/product_page.css @@ -7,7 +7,4 @@ font-size: 18px; line-height: 200%; } - .item-price-info { - margin-top: 20px; - } \ No newline at end of file diff --git a/website/templates/html/product_page.html b/website/templates/html/product_page.html index ac1af5ab6b..837b105702 100644 --- a/website/templates/html/product_page.html +++ b/website/templates/html/product_page.html @@ -35,11 +35,20 @@ {{ web_long_description or web_short_description or "[No description given]" }} -
-
-
- - +
+
diff --git a/website/templates/js/cart.js b/website/templates/js/cart.js index eaa1fab553..121ca6ab81 100644 --- a/website/templates/js/cart.js +++ b/website/templates/js/cart.js @@ -18,15 +18,45 @@ $(document).ready(function() { // make list of items in the cart - wn.cart.render(); - wn.cart.bind_events(); + // wn.cart.render(); + // wn.cart.bind_events(); + wn.call({ + method: "website.helpers.cart.get_cart_quotation", + args: { + _type: "POST" + }, + callback: function(r) { + console.log(r); + $("#cart-container").removeClass("hide"); + $(".progress").remove(); + if(r.exc) { + if(r.exc.indexOf("WebsitePriceListMissingError")!==-1) { + wn.cart.show_error("Oops!", "Price List not configured."); + } else { + wn.cart.show_error("Oops!", "Something went wrong."); + } + } else { + if(r.message[0].__islocal) { + wn.cart.show_error("Empty :-(", "Go ahead and add something to your cart."); + } else { + wn.cart.render(r.message); + } + } + + } + }); }); // shopping cart if(!wn.cart) wn.cart = {}; $.extend(wn.cart, { - render: function() { - var $cart_wrapper = $("#cart-added-items").empty(); + show_error: function(title, text) { + $("#cart-container").html('

' + title + '

' + text + '
'); + }, + + render: function(doclist) { + return; + var $cart_wrapper = $("#cart-items").empty(); if(Object.keys(wn.cart.get_cart()).length) { $('
\
\ diff --git a/website/templates/js/product_page.js b/website/templates/js/product_page.js index e3e4c618ab..adfeb5e4ad 100644 --- a/website/templates/js/product_page.js +++ b/website/templates/js/product_page.js @@ -15,55 +15,77 @@ // along with this program. If not, see . $(document).ready(function() { - $.ajax({ - method: "GET", - url:"server.py", - dataType: "json", - data: { - cmd: "website.helpers.product.get_product_info", + var item_code = $('[itemscope] [itemprop="name"]').text().trim(); + var qty = 0; + + wn.call({ + type: "POST", + method: "website.helpers.product.get_product_info", + args: { item_code: "{{ name }}" }, - success: function(data) { - if(data.message) { - if(data.message.price) { - $("

") - .html(data.message.price.formatted_price + " per " + data.message.uom) - .appendTo(".item-price"); - $(".item-price").removeClass("hide"); - } - if(data.message.stock==0) { + callback: function(r) { + if(r.message && r.message.price) { + $(".item-price") + .html(r.message.price.formatted_price + " per " + r.message.uom); + + if(r.message.stock==0) { $(".item-stock").html("
Not in stock
"); } - else if(data.message.stock==1) { + else if(r.message.stock==1) { $(".item-stock").html("
\ Available (in stock)
"); } + + $(".item-price-info").toggle(true); + + if(r.message.qty) { + qty = r.message.qty; + toggle_update_cart(qty); + $("#item-update-cart input").val(qty); + } } } - }); + }) - if(wn.cart.get_cart()[$('[itemscope] [itemprop="name"]').text().trim()]) { - $(".item-remove-from-cart").removeClass("hide"); - } else { - $(".item-add-to-cart").removeClass("hide"); - } - - $("button.item-add-to-cart").on("click", function() { - wn.cart.add_to_cart({ - url: window.location.href, - image: $('[itemscope] [itemprop="image"]').attr("src"), - item_code: $('[itemscope] [itemprop="name"]').text().trim(), - item_name: $('[itemscope] [itemprop="productID"]').text().trim(), - description: $('[itemscope] [itemprop="description"]').html().trim(), - price: $('[itemscope] [itemprop="price"]').text().trim() + $("#item-add-to-cart button").on("click", function() { + wn.cart.update_cart({ + item_code: item_code, + qty: 1, + callback: function(r) { + if(!r.exc) { + toggle_update_cart(1); + qty = 1; + } + }, + btn: this, }); - $(".item-add-to-cart").addClass("hide"); - $(".item-remove-from-cart").removeClass("hide"); }); - $("button.item-remove-from-cart").on("click", function() { - wn.cart.remove_from_cart($('[itemscope] [itemprop="name"]').text().trim()); - $(".item-add-to-cart").removeClass("hide"); - $(".item-remove-from-cart").addClass("hide"); + $("#item-update-cart button").on("click", function() { + wn.cart.update_cart({ + item_code: item_code, + qty: $("#item-update-cart input").val(), + btn: this, + callback: function(r) { + if(r.exc) { + $("#item-update-cart input").val(qty); + } else { + qty = $("#item-update-cart input").val(); + } + }, + }); }); -}) \ No newline at end of file + + if(localStorage && localStorage.getItem("pending_add_to_cart") && full_name) { + localStorage.removeItem("pending_add_to_cart"); + $("#item-add-to-cart button").trigger("click"); + } +}); + +var toggle_update_cart = function(qty) { + $("#item-add-to-cart").toggle(qty ? false : true); + $("#item-update-cart") + .toggle(qty ? true : false) + .find("input").val(qty); +} \ No newline at end of file diff --git a/website/templates/pages/cart.html b/website/templates/pages/cart.html index 31d50848fd..ada15770f8 100644 --- a/website/templates/pages/cart.html +++ b/website/templates/pages/cart.html @@ -9,9 +9,21 @@ {% block content %}

Shopping Cart

-
-
- +
+
+
+
+ +
+
+
+
+
+
+
+
+
+
{% endblock %} \ No newline at end of file