[cart] add to cart, update cart
diff --git a/public/js/website_utils.js b/public/js/website_utils.js
index 1484f00..83becc6 100644
--- a/public/js/website_utils.js
+++ b/public/js/website_utils.js
@@ -15,7 +15,6 @@
 
 wn.call = function(opts) {
 	if(opts.btn) {
-		var $spinner = $('<img src="lib/images/ui/button-load.gif">').appendTo($(opts.btn).parent())
 		$(opts.btn).attr("disabled", "disabled");
 	}
 	
@@ -23,6 +22,8 @@
 		$(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 @@
 		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 @@
 		$("#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 @@
 		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 @@
 
 // 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 4af35dc..7efceb2 100644
--- a/website/css/website.css
+++ b/website/css/website.css
@@ -6,6 +6,10 @@
 	font-weight: bold;
 }
 
+a {
+	cursor: pointer;
+}
+
 .content {
 	padding-bottom: 30px;
 }
diff --git a/website/helpers/cart.py b/website/helpers/cart.py
index efeb975..b62e0bb 100644
--- a/website/helpers/cart.py
+++ b/website/helpers/cart.py
@@ -7,20 +7,13 @@
 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 @@
 				"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 @@
 		
 		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 @@
 			{"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 @@
 		
 	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 @@
 	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 fb4d4e4..e513c2d 100644
--- a/website/helpers/product.py
+++ b/website/helpers/product.py
@@ -25,18 +25,26 @@
 		(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 e2f4293..566b6b5 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;
-		}
 	</style>
\ No newline at end of file
diff --git a/website/templates/html/product_page.html b/website/templates/html/product_page.html
index ac1af5a..837b105 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]" }}
 				</div>
-				<div class="item-price-info" itemprop="offers" itemscope itemtype="http://schema.org/Offer">
-					<div class="item-price hide" itemprop="price"></div>
-					<div class="item-stock" itemprop="availablity"></div>
-					<button class="btn btn-primary item-add-to-cart hide">Add to Cart</button>
-					<button class="btn btn-default item-remove-from-cart hide">Remove from Cart</button>
+				<div style="min-height: 100px; margin: 10px 0;">
+					<div class="item-price-info" style="display: none;">
+						<h4 class="item-price" itemprop="price"></h4>
+						<div class="item-stock" itemprop="availablity"></div>
+						<div id="item-add-to-cart">
+							<button class="btn btn-primary">Add to Cart</button>
+						</div>
+						<div id="item-update-cart" class="input-group col-lg-6" style="display: none;">
+							<input type="text">
+							<div class="input-group-btn">
+								<button class="btn btn-primary">Update</button>
+							</div>
+						</div>
+					</div>
 				</div>
 			</div>
 		</div>
diff --git a/website/templates/js/cart.js b/website/templates/js/cart.js
index eaa1fab..121ca6a 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('<div class="well"><h4>' + title + '</h4> ' + text + '</div>');
+	},
+	
+	render: function(doclist) {
+		return;
+		var $cart_wrapper = $("#cart-items").empty();
 		if(Object.keys(wn.cart.get_cart()).length) {
 			$('<div class="row">\
 				<div class="col col-lg-9 col-sm-9">\
diff --git a/website/templates/js/product_page.js b/website/templates/js/product_page.js
index e3e4c61..adfeb5e 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 <http://www.gnu.org/licenses/>.
 
 $(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) {
-					$("<h4>")
-						.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("<div class='help'>Not in stock</div>");
 				}
-				else if(data.message.stock==1) {
+				else if(r.message.stock==1) {
 					$(".item-stock").html("<div style='color: green'>\
 						<i class='icon-check'></i> Available (in stock)</div>");
 				}
+				
+				$(".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 31d5084..ada1577 100644
--- a/website/templates/pages/cart.html
+++ b/website/templates/pages/cart.html
@@ -9,9 +9,21 @@
 {% block content %}
 <div class="col col-lg-12">
 	<h2>Shopping Cart</h2>
-	<hr>
-	<div id="cart-added-items">
-		<!-- list of items in the cart will be generated using javascript -->
+	<div class="progress progress-striped active">
+		<div class="progress-bar progress-bar-info" style="width: 100%;"></div>
+	</div>
+	<div id="cart-container" class="hide">
+		<button class="btn btn-success pull-right" type="button">Place Order</button>
+		<div class="clearfix"></div>
+		<hr>
+		<div id="cart-items">
+		</div>
+		<hr>
+		<div id="cart-taxes">
+		</div>
+		<div id="cart-addresses">
+		</div>
+		<button class="btn btn-success pull-right" type="button">Place Order</button>
 	</div>
 </div>
 {% endblock %}
\ No newline at end of file