[webshop] shopping cart with shipping rule selector, totals
diff --git a/accounts/doctype/shipping_rule/shipping_rule.py b/accounts/doctype/shipping_rule/shipping_rule.py
index ade76e5..a363b18 100644
--- a/accounts/doctype/shipping_rule/shipping_rule.py
+++ b/accounts/doctype/shipping_rule/shipping_rule.py
@@ -58,7 +58,7 @@
 				then condition y can only be like 50 to 99 or 301 to 400
 				hence, non-overlapping condition = (x1 <= x2 < y1 <= y2) or (y1 <= y2 < x1 <= x2)
 			"""
-			separate = (x1 <= x2 < y1 <= y2) or (y1 <= y2 < x1 <= x2)
+			separate = (x1 <= x2 <= y1 <= y2) or (y1 <= y2 <= x1 <= x2)
 			return (not separate)
 		
 		overlaps = []
diff --git a/controllers/selling_controller.py b/controllers/selling_controller.py
index d9f19ac..2f77295 100644
--- a/controllers/selling_controller.py
+++ b/controllers/selling_controller.py
@@ -73,17 +73,16 @@
 				if not condition.to_value or (flt(condition.from_value) <= value <= flt(condition.to_value)):
 					shipping_amount = condition.shipping_amount
 					break
-					
-			if shipping_amount:
-				self.doclist.append({
-					"doctype": "Sales Taxes and Charges",
-					"parentfield": "other_charges",
-					"charge_type": "Actual",
-					"account_head": shipping_rule.doc.account,
-					"cost_center": shipping_rule.doc.cost_center,
-					"description": shipping_rule.doc.label,
-					"rate": shipping_amount
-				})
+			
+			self.doclist.append({
+				"doctype": "Sales Taxes and Charges",
+				"parentfield": "other_charges",
+				"charge_type": "Actual",
+				"account_head": shipping_rule.doc.account,
+				"cost_center": shipping_rule.doc.cost_center,
+				"description": shipping_rule.doc.label,
+				"rate": shipping_amount
+			})
 		
 	def set_total_in_words(self):
 		from webnotes.utils import money_in_words
diff --git a/website/doctype/shopping_cart_settings/shopping_cart_settings.py b/website/doctype/shopping_cart_settings/shopping_cart_settings.py
index 1424823..121422f 100644
--- a/website/doctype/shopping_cart_settings/shopping_cart_settings.py
+++ b/website/doctype/shopping_cart_settings/shopping_cart_settings.py
@@ -63,12 +63,15 @@
 			# if list against each territory has more than one element, raise exception
 			territory_name = webnotes.conn.sql("""select `territory`, `parent` 
 				from `tabFor Territory`
-				where `parenttype`=%s and `parent` in (%s) """ %
+				where `parenttype`=%s and `parent` in (%s)""" %
 				("%s", ", ".join(["%s"]*len(names))), tuple([parenttype] + names))
 		
 			for territory, name in territory_name:
 				territory_name_map.setdefault(territory, []).append(name)
-			
+				
+				if len(territory_name_map[territory]) > 1:
+					territory_name_map[territory].sort(key=lambda val: names.index(val))
+		
 		return territory_name_map
 					
 	def validate_exchange_rates_exist(self):
@@ -101,24 +104,26 @@
 		territory_name_map = self.get_territory_name_map(parentfield, fieldname)
 		
 		if territory_name_map.get(territory):
-			name = territory_name_map.get(territory)[0]
+			name = territory_name_map.get(territory)
 		else:
 			territory_ancestry = self.get_territory_ancestry(territory)
 			for ancestor in territory_ancestry:
 				if territory_name_map.get(ancestor):
-					name = territory_name_map.get(ancestor)[0]
+					name = territory_name_map.get(ancestor)
 					break
 		
 		return name
 				
 	def get_price_list(self, billing_territory):
-		return self.get_name_from_territory(billing_territory, "price_lists", "price_list")
+		price_list = self.get_name_from_territory(billing_territory, "price_lists", "price_list")
+		return price_list and price_list[0] or None
 		
 	def get_tax_master(self, billing_territory):
-		return self.get_name_from_territory(billing_territory, "sales_taxes_and_charges_masters", 
+		tax_master = self.get_name_from_territory(billing_territory, "sales_taxes_and_charges_masters", 
 			"sales_taxes_and_charges_master")
+		return tax_master and tax_master[0] or None
 		
-	def get_shipping_rule(self, shipping_territory):
+	def get_shipping_rules(self, shipping_territory):
 		return self.get_name_from_territory(shipping_territory, "shipping_rules", "shipping_rule")
 		
 	def get_territory_ancestry(self, territory):
diff --git a/website/doctype/shopping_cart_settings/shopping_cart_settings.txt b/website/doctype/shopping_cart_settings/shopping_cart_settings.txt
index fa13f18..1d472b7 100644
--- a/website/doctype/shopping_cart_settings/shopping_cart_settings.txt
+++ b/website/doctype/shopping_cart_settings/shopping_cart_settings.txt
@@ -2,7 +2,7 @@
  {
   "creation": "2013-06-19 15:57:32", 
   "docstatus": 0, 
-  "modified": "2013-07-03 21:00:00", 
+  "modified": "2013-07-03 21:01:00", 
   "modified_by": "Administrator", 
   "owner": "Administrator"
  }, 
@@ -69,7 +69,7 @@
  }, 
  {
   "doctype": "DocField", 
-  "fieldname": "shopping_cart_shipping_rules", 
+  "fieldname": "shipping_rules", 
   "fieldtype": "Table", 
   "label": "Shopping Cart Shipping Rules", 
   "options": "Shopping Cart Shipping Rule", 
diff --git a/website/helpers/cart.py b/website/helpers/cart.py
index 0338709..98edc07 100644
--- a/website/helpers/cart.py
+++ b/website/helpers/cart.py
@@ -4,7 +4,7 @@
 from __future__ import unicode_literals
 import webnotes
 import webnotes.defaults
-from webnotes.utils import cint, get_fullname, fmt_money
+from webnotes.utils import flt, get_fullname, fmt_money
 
 class WebsitePriceListMissingError(webnotes.ValidationError): pass
 
@@ -18,14 +18,15 @@
 	return {
 		"doclist": decorate_quotation_doclist(doclist),
 		"addresses": [{"name": address.name, "display": address.display} 
-			for address in get_address_docs(party)]
+			for address in get_address_docs(party)],
+		"shipping_rules": get_applicable_shipping_rules(party)
 	}
 
 @webnotes.whitelist()
 def update_cart(item_code, qty, with_doclist=0):
 	quotation = _get_cart_quotation()
 	
-	qty = cint(qty)
+	qty = flt(qty)
 	if qty == 0:
 		quotation.set_doclist(quotation.doclist.get({"item_code": ["!=", item_code]}))
 	else:
@@ -40,8 +41,7 @@
 		else:
 			quotation_items[0].qty = qty
 	
-	quotation.ignore_permissions = True
-	quotation.save()
+	apply_cart_settings(quotation=quotation)
 	
 	if with_doclist:
 		return get_cart_quotation(quotation.doclist)
@@ -158,7 +158,13 @@
 				["website_image", "web_short_description", "page_name"], as_dict=True))
 			d.formatted_rate = fmt_money(d.export_rate, currency=doclist[0].currency)
 			d.formatted_amount = fmt_money(d.export_amount, currency=doclist[0].currency)
+		elif d.charge_type:
+			d.formatted_tax_amount = fmt_money(d.tax_amount / doclist[0].conversion_rate,
+				currency=doclist[0].currency)
 
+	doclist[0].formatted_grand_total_export = fmt_money(doclist[0].grand_total_export,
+		currency=doclist[0].currency)
+	
 	return [d.fields for d in doclist]
 
 def _get_cart_quotation(party=None):
@@ -199,16 +205,13 @@
 	
 	set_price_list_and_rate(quotation, cart_settings, billing_territory)
 	
-	set_taxes(quotation, cart_settings, billing_territory)
-	
-	# set shipping rule based on shipping territory	
-	shipping_territory = get_address_territory(quotation.doc.shipping_address_name) or \
-		party.territory
-	
-	apply_shipping_rule(quotation, cart_settings, shipping_territory)
-	
 	quotation.run_method("calculate_taxes_and_totals")
 	
+	set_taxes(quotation, cart_settings, billing_territory)
+	
+	_apply_shipping_rule(party, quotation, cart_settings)
+	
+	quotation.ignore_permissions = True
 	quotation.save()
 	
 def set_price_list_and_rate(quotation, cart_settings, billing_territory):
@@ -235,10 +238,54 @@
 	controller = quotation.make_controller()
 	controller.append_taxes_from_master("other_charges", "charge")
 	quotation.set_doclist(controller.doclist)
+
+@webnotes.whitelist()
+def apply_shipping_rule(shipping_rule):
+	quotation = _get_cart_quotation()
 	
-def apply_shipping_rule(quotation, cart_settings, shipping_territory):
-	quotation.doc.shipping_rule = cart_settings.get_shipping_rule(shipping_territory)
+	quotation.doc.shipping_rule = shipping_rule
+	
+	apply_cart_settings(quotation=quotation)
+	
+	quotation.save()
+	
+	return get_cart_quotation(quotation.doclist)
+	
+def _apply_shipping_rule(party=None, quotation=None, cart_settings=None):
+	shipping_rules = get_shipping_rules(party, quotation, cart_settings)
+	
+	if not shipping_rules:
+		return
+		
+	elif quotation.doc.shipping_rule not in shipping_rules:
+		quotation.doc.shipping_rule = shipping_rules[0]
+	
 	quotation.run_method("apply_shipping_rule")
+	quotation.run_method("calculate_taxes_and_totals")
+	
+def get_applicable_shipping_rules(party=None, quotation=None):
+	shipping_rules = get_shipping_rules(party, quotation)
+	
+	if shipping_rules:
+		rule_label_map = webnotes.conn.get_values("Shipping Rule", shipping_rules, "label")
+		# we need this in sorted order as per the position of the rule in the settings page
+		return [[rule, rule_label_map.get(rule)] for rule in shipping_rules]
+		
+def get_shipping_rules(party=None, quotation=None, cart_settings=None):
+	if not party:
+		party = get_lead_or_customer()
+	if not quotation:
+		quotation = _get_cart_quotation()
+	if not cart_settings:
+		cart_settings = webnotes.get_obj("Shopping Cart Settings")
+		
+	# set shipping rule based on shipping territory	
+	shipping_territory = get_address_territory(quotation.doc.shipping_address_name) or \
+		party.territory
+	
+	shipping_rules = cart_settings.get_shipping_rules(shipping_territory)
+	
+	return shipping_rules
 	
 def get_address_territory(address_name):
 	"""Tries to match city, state and country of address to existing territory"""
@@ -254,9 +301,6 @@
 	
 	return territory
 	
-def get_cart_price_list(territory_list, territory_name_map):
-	pass
-	
 @webnotes.whitelist()
 def checkout():
 	quotation = _get_cart_quotation()
diff --git a/website/templates/js/cart.js b/website/templates/js/cart.js
index db6c571..f1002fc 100644
--- a/website/templates/js/cart.js
+++ b/website/templates/js/cart.js
@@ -38,7 +38,6 @@
 			} else {
 				wn.cart.render(r.message);
 			}
-			
 		}
 	});
 });
@@ -81,9 +80,10 @@
 	render: function(out) {
 		var doclist = out.doclist;
 		var addresses = out.addresses;
-
+		
 		var $cart_items = $("#cart-items").empty();
 		var $cart_taxes = $("#cart-taxes").empty();
+		var $cart_totals = $("#cart-totals").empty();
 		var $cart_billing_address = $("#cart-billing-address").empty();
 		var $cart_shipping_address = $("#cart-shipping-address").empty();
 		
@@ -94,12 +94,39 @@
 			return;
 		}
 		
+		var shipping_rule_added = false;
+		var taxes_exist = false;
+		var shipping_rule_labels = $.map(out.shipping_rules, function(rule) { return rule[1]; });
 		$.each(doclist, function(i, doc) {
 			if(doc.doctype === "Quotation Item") {
 				wn.cart.render_item_row($cart_items, doc);
+			} else if (doc.doctype === "Sales Taxes and Charges") {
+				if(out.shipping_rules && out.shipping_rules.length && 
+					shipping_rule_labels.indexOf(doc.description)!==-1) {
+						shipping_rule_added = true;
+						wn.cart.render_tax_row($cart_taxes, doc, out.shipping_rules);
+				} else {
+					wn.cart.render_tax_row($cart_taxes, doc);
+				}
+				
+				taxes_exist = true;
 			}
 		});
 		
+		if(out.shipping_rules && out.shipping_rules.length && !shipping_rule_added) {
+			wn.cart.render_tax_row($cart_taxes, {description: "", formatted_tax_amount: ""},
+				out.shipping_rules);
+			taxes_exist = true;
+		}
+		
+		if(taxes_exist)
+			$('<hr>').appendTo($cart_taxes);
+			
+		wn.cart.render_tax_row($cart_totals, {
+			description: "<strong>Total</strong>", 
+			formatted_tax_amount: "<strong>" + doclist[0].formatted_grand_total_export + "</strong>"
+		});
+		
 		if(!(addresses && addresses.length)) {
 			$cart_shipping_address.html('<div class="well">Hey! Go ahead and add an address</div>');
 		} else {
@@ -125,10 +152,10 @@
 					</div>\
 				</div>\
 			</div>\
-			<div class="col col-lg-3 col-sm-3">\
+			<div class="col col-lg-3 col-sm-3 text-right">\
 				<div class="input-group item-update-cart">\
 					<input type="text" placeholder="Qty" value="%(qty)s" \
-						data-item-code="%(item_code)s">\
+						data-item-code="%(item_code)s" class="text-right">\
 					<div class="input-group-btn">\
 						<button class="btn btn-primary" data-item-code="%(item_code)s">\
 							<i class="icon-ok"></i></button>\
@@ -140,6 +167,53 @@
 		</div><hr>', doc)).appendTo($cart_items);
 	},
 	
+	render_tax_row: function($cart_taxes, doc, shipping_rules) {
+		var shipping_selector;
+		if(shipping_rules) {
+			shipping_selector = '<select>' + $.map(shipping_rules, function(rule) { 
+					return '<option value="' + rule[0] + '">' + rule[1] + '</option>' }).join("\n") + 
+				'</select>';
+		}
+		
+		var $tax_row = $(repl('<div class="row">\
+			<div class="col col-lg-9 col-sm-9">\
+				<div class="row">\
+					<div class="col col-lg-9 col-offset-3">' +
+					(shipping_selector || '<p>%(description)s</p>') +
+					'</div>\
+				</div>\
+			</div>\
+			<div class="col col-lg-3 col-sm-3 text-right">\
+				<p' + (shipping_selector ? ' style="margin-top: 5px;"' : "") + '>%(formatted_tax_amount)s</p>\
+			</div>\
+		</div>', doc)).appendTo($cart_taxes);
+		
+		if(shipping_selector) {
+			$tax_row.find('select option').each(function(i, opt) {
+				if($(opt).html() == doc.description) {
+					$(opt).attr("selected", "selected");
+				}
+			});
+			$tax_row.find('select').on("change", function() {
+				wn.cart.apply_shipping_rule($(this).val(), this);
+			});
+		}
+	},
+	
+	apply_shipping_rule: function(rule, btn) {
+		wn.call({
+			btn: btn,
+			type: "POST",
+			method: "website.helpers.cart.apply_shipping_rule",
+			args: { shipping_rule: rule },
+			callback: function(r) {
+				if(!r.exc) {
+					wn.cart.render(r.message);
+				}
+			}
+		});
+	},
+	
 	render_address: function($address_wrapper, addresses, address_name) {
 		$.each(addresses, function(i, address) {
 			$(repl('<div class="accordion-group"> \
diff --git a/website/templates/pages/cart.html b/website/templates/pages/cart.html
index 9d5f100..cefcb5a 100644
--- a/website/templates/pages/cart.html
+++ b/website/templates/pages/cart.html
@@ -19,16 +19,17 @@
 		<div class="row">
 			<div class="col col-lg-9 col-sm-9">
 				<div class="row">
-					<div class="col col-lg-3"></div>
-					<div class="col col-lg-9"><h4>Item Details</h4></div>
+					<div class="col col-lg-9 col-offset-3"><h4>Item Details</h4></div>
 				</div>
 			</div>
-			<div class="col col-lg-3 col-sm-3"><h4>Qty</h4></div>
+			<div class="col col-lg-3 col-sm-3 text-right"><h4>Qty, Amount</h4></div>
 		</div><hr>
 		<div id="cart-items">
 		</div>
 		<div id="cart-taxes">
 		</div>
+		<div id="cart-totals">
+		</div>
 		<hr>
 		<div id="cart-addresses">
 			<div class="row">
@@ -37,16 +38,15 @@
 					<div id="cart-shipping-address" class="accordion" 
 						data-fieldname="shipping_address_name"></div>
 					<button class="btn btn-default" type="button" id="cart-add-shipping-address">
-						<span class="icon icon-plus"></span> New Address</button>
+						<span class="icon icon-plus"></span> New Shipping Address</button>
 				</div>
 				<div class="col col-lg-6">
 					<h4>Billing Address</h4>
 					<div id="cart-billing-address" class="accordion"
 						data-fieldname="customer_address"></div>
 					<button class="btn btn-default" type="button" id="cart-add-billing-address">
-						<span class="icon icon-plus"></span> New Address</button>
+						<span class="icon icon-plus"></span> New Billing Address</button>
 				</div>
-				
 			</div>
 			<hr>
 		</div>