[webshop] shopping cart settings, update price list, taxes and shipping rule on change of address, apply defaults on creation of fresh quotation
diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py
index 59c84d4..aad79da 100644
--- a/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/accounts/doctype/sales_invoice/sales_invoice.py
@@ -251,7 +251,7 @@
 			
 			# fetch charges
 			if self.doc.charge and not len(self.doclist.get({"parentfield": "other_charges"})):
-				self.set_taxes()
+				self.set_taxes("other_charges", "charge")
 
 	def get_customer_account(self):
 		"""Get Account Head to which amount needs to be Debited based on Customer"""
diff --git a/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.py b/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.py
index bc55701..27316ca 100644
--- a/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.py
+++ b/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.py
@@ -16,12 +16,10 @@
 
 from __future__ import unicode_literals
 import webnotes
+from webnotes.utils import cint
+from webnotes.model.controller import DocListController
 
-class DocType:
-	def __init__(self, doc, doclist=[]):
-		self.doc = doc
-		self.doclist = doclist
-
+class DocType(DocListController):
 	def get_rate(self, arg):
 		from webnotes.model.code import get_obj
 		return get_obj('Sales Common').get_rate(arg)
@@ -30,4 +28,12 @@
 		if self.doc.is_default == 1:
 			webnotes.conn.sql("""update `tabSales Taxes and Charges Master` set is_default = 0 
 				where ifnull(is_default,0) = 1 and name != %s and company = %s""", 
-				(self.doc.name, self.doc.company))
\ No newline at end of file
+				(self.doc.name, self.doc.company))
+				
+		# at least one territory
+		self.validate_table_has_rows("valid_for_territories")
+		
+	def on_update(self):
+		cart_settings = webnotes.get_obj("Shopping Cart Settings")
+		if cint(cart_settings.doc.enabled):
+			cart_settings.validate_tax_masters()
\ No newline at end of file
diff --git a/accounts/doctype/sales_taxes_and_charges_master/test_sales_taxes_and_charges_master.py b/accounts/doctype/sales_taxes_and_charges_master/test_sales_taxes_and_charges_master.py
new file mode 100644
index 0000000..46ae842
--- /dev/null
+++ b/accounts/doctype/sales_taxes_and_charges_master/test_sales_taxes_and_charges_master.py
@@ -0,0 +1,146 @@
+test_records = [
+	[
+		{
+			"doctype": "Sales Taxes and Charges Master",
+			"title": "_Test Sales Taxes and Charges Master",
+			"company": "_Test Company"
+		},
+		{
+			"account_head": "_Test Account VAT - _TC", 
+			"charge_type": "On Net Total", 
+			"description": "VAT", 
+			"doctype": "Sales Taxes and Charges", 
+			"parentfield": "other_charges",
+			"rate": 6,
+		}, 
+		{
+			"account_head": "_Test Account Service Tax - _TC", 
+			"charge_type": "On Net Total", 
+			"description": "Service Tax", 
+			"doctype": "Sales Taxes and Charges", 
+			"parentfield": "other_charges",
+			"rate": 6.36,
+		},
+		{
+			"doctype": "For Territory",
+			"parentfield": "valid_for_territories",
+			"territory": "All Territories"
+		}
+	],
+	[
+		{
+			"doctype": "Sales Taxes and Charges Master",
+			"title": "_Test India Tax Master",
+			"company": "_Test Company"
+		},
+		{
+			"doctype": "Sales Taxes and Charges",
+			"parentfield": "other_charges",
+			"charge_type": "Actual",
+			"account_head": "_Test Account Shipping Charges - _TC",
+			"cost_center": "_Test Cost Center - _TC",
+			"description": "Shipping Charges",
+			"rate": 100
+		},
+		{
+			"doctype": "Sales Taxes and Charges",
+			"parentfield": "other_charges",
+			"charge_type": "On Net Total",
+			"account_head": "_Test Account Customs Duty - _TC",
+			"cost_center": "_Test Cost Center - _TC",
+			"description": "Customs Duty",
+			"rate": 10
+		},
+		{
+			"doctype": "Sales Taxes and Charges",
+			"parentfield": "other_charges",
+			"charge_type": "On Net Total",
+			"account_head": "_Test Account Excise Duty - _TC",
+			"cost_center": "_Test Cost Center - _TC",
+			"description": "Excise Duty",
+			"rate": 12
+		},
+		{
+			"doctype": "Sales Taxes and Charges",
+			"parentfield": "other_charges",
+			"charge_type": "On Previous Row Amount",
+			"account_head": "_Test Account Education Cess - _TC",
+			"cost_center": "_Test Cost Center - _TC",
+			"description": "Education Cess",
+			"rate": 2,
+			"row_id": 3
+		},
+		{
+			"doctype": "Sales Taxes and Charges",
+			"parentfield": "other_charges",
+			"charge_type": "On Previous Row Amount",
+			"account_head": "_Test Account S&H Education Cess - _TC",
+			"cost_center": "_Test Cost Center - _TC",
+			"description": "S&H Education Cess",
+			"rate": 1,
+			"row_id": 3
+		},
+		{
+			"doctype": "Sales Taxes and Charges",
+			"parentfield": "other_charges",
+			"charge_type": "On Previous Row Total",
+			"account_head": "_Test Account CST - _TC",
+			"cost_center": "_Test Cost Center - _TC",
+			"description": "CST",
+			"rate": 2,
+			"row_id": 5
+		},
+		{
+			"doctype": "Sales Taxes and Charges",
+			"parentfield": "other_charges",
+			"charge_type": "On Net Total",
+			"account_head": "_Test Account VAT - _TC",
+			"cost_center": "_Test Cost Center - _TC",
+			"description": "VAT",
+			"rate": 12.5
+		},
+		{
+			"doctype": "Sales Taxes and Charges",
+			"parentfield": "other_charges",
+			"charge_type": "On Previous Row Total",
+			"account_head": "_Test Account Discount - _TC",
+			"cost_center": "_Test Cost Center - _TC",
+			"description": "Discount",
+			"rate": -10,
+			"row_id": 7
+		},
+		{
+			"doctype": "For Territory",
+			"parentfield": "valid_for_territories",
+			"territory": "India"
+		}
+	],
+	[
+		{
+			"doctype": "Sales Taxes and Charges Master",
+			"title": "_Test Sales Taxes and Charges Master 2",
+			"company": "_Test Company"
+		},
+		{
+			"account_head": "_Test Account VAT - _TC", 
+			"charge_type": "On Net Total", 
+			"description": "VAT", 
+			"doctype": "Sales Taxes and Charges", 
+			"parentfield": "other_charges",
+			"rate": 12,
+		}, 
+		{
+			"account_head": "_Test Account Service Tax - _TC", 
+			"charge_type": "On Net Total", 
+			"description": "Service Tax", 
+			"doctype": "Sales Taxes and Charges", 
+			"parentfield": "other_charges",
+			"rate": 4,
+		},
+		{
+			"doctype": "For Territory",
+			"parentfield": "valid_for_territories",
+			"territory": "All Territories"
+		}
+	],
+]
\ No newline at end of file
diff --git a/accounts/doctype/shipping_rule/shipping_rule.py b/accounts/doctype/shipping_rule/shipping_rule.py
index 8714865..ade76e5 100644
--- a/accounts/doctype/shipping_rule/shipping_rule.py
+++ b/accounts/doctype/shipping_rule/shipping_rule.py
@@ -16,6 +16,7 @@
 		self.doc, self.doclist = d, dl
 		
 	def validate(self):
+		self.validate_value("calculate_based_on", "in", ["Net Total", "Net Weight"])
 		self.shipping_rule_conditions = self.doclist.get({"parentfield": "shipping_rule_conditions"})
 		self.validate_from_to_values()
 		self.sort_shipping_rule_conditions()
diff --git a/accounts/doctype/shipping_rule/shipping_rule.txt b/accounts/doctype/shipping_rule/shipping_rule.txt
index dd4fe8d..99886ca 100644
--- a/accounts/doctype/shipping_rule/shipping_rule.txt
+++ b/accounts/doctype/shipping_rule/shipping_rule.txt
@@ -2,11 +2,12 @@
  {
   "creation": "2013-06-25 11:48:03", 
   "docstatus": 0, 
-  "modified": "2013-06-25 12:15:21", 
+  "modified": "2013-07-04 16:28:42", 
   "modified_by": "Administrator", 
   "owner": "Administrator"
  }, 
  {
+  "autoname": "Prompt", 
   "description": "Specify conditions to calculate shipping amount", 
   "doctype": "DocType", 
   "module": "Accounts", 
@@ -37,7 +38,7 @@
  {
   "description": "example: Next Day Shipping", 
   "doctype": "DocField", 
-  "fieldname": "shipping_rule_label", 
+  "fieldname": "label", 
   "fieldtype": "Data", 
   "in_list_view": 1, 
   "label": "Shipping Rule Label", 
@@ -49,14 +50,16 @@
   "fieldtype": "Column Break"
  }, 
  {
-  "default": "Amount", 
+  "default": "Net Total", 
   "doctype": "DocField", 
   "fieldname": "calculate_based_on", 
   "fieldtype": "Select", 
+  "hidden": 1, 
   "in_list_view": 1, 
   "label": "Calculate Based On", 
-  "options": "Amount\nNet Weight", 
-  "reqd": 1
+  "options": "Net Total\nNet Weight", 
+  "read_only": 1, 
+  "reqd": 0
  }, 
  {
   "doctype": "DocField", 
diff --git a/accounts/doctype/shipping_rule/test_shipping_rule.py b/accounts/doctype/shipping_rule/test_shipping_rule.py
index ff217bc..23d204d 100644
--- a/accounts/doctype/shipping_rule/test_shipping_rule.py
+++ b/accounts/doctype/shipping_rule/test_shipping_rule.py
@@ -32,7 +32,7 @@
 	[
 		{
 			"doctype": "Shipping Rule",
-			"calculate_based_on": "Amount",
+			"calculate_based_on": "Net Total",
 			"company": "_Test Company",
 			"account": "_Test Account Shipping Charges - _TC",
 			"cost_center": "_Test Cost Center - _TC"
diff --git a/accounts/page/accounts_home/accounts_home.js b/accounts/page/accounts_home/accounts_home.js
index 3ca51b8..1f41fcc 100644
--- a/accounts/page/accounts_home/accounts_home.js
+++ b/accounts/page/accounts_home/accounts_home.js
@@ -107,6 +107,11 @@
 				"description": wn._("Rules to calculate shipping amount for a sale")
 			},
 			{
+				"label": wn._("Currency Exchange"),
+				"doctype":"Currency Exchange",
+				"description": wn._("Manage exchange rates for currency conversion")
+			},
+			{
 				"label": wn._("Point-of-Sale Setting"),
 				"doctype":"POS Setting",
 				"description": "User settings for Point-of-sale (POS)"
diff --git a/buying/doctype/purchase_common/purchase_common.js b/buying/doctype/purchase_common/purchase_common.js
index f9694d0..0c74ca1 100644
--- a/buying/doctype/purchase_common/purchase_common.js
+++ b/buying/doctype/purchase_common/purchase_common.js
@@ -355,7 +355,7 @@
 		
 		var setup_field_label_map = function(fields_list, currency) {
 			$.each(fields_list, function(i, fname) {
-				var docfield = wn.meta.get_docfield(me.frm.doc.doctype, fname);
+				var docfield = wn.meta.docfield_map[me.frm.doc.doctype][fname];
 				if(docfield) {
 					var label = wn._(docfield.label || "").replace(/\([^\)]*\)/g, "");
 					field_label_map[fname] = label.trim() + " (" + currency + ")";
@@ -401,7 +401,7 @@
 		var setup_field_label_map = function(fields_list, currency, parentfield) {
 			var grid_doctype = me.frm.fields_dict[parentfield].grid.doctype;
 			$.each(fields_list, function(i, fname) {
-				var docfield = wn.meta.get_docfield(grid_doctype, fname);
+				var docfield = wn.meta.docfield_map[grid_doctype][fname];
 				if(docfield) {
 					var label = wn._(docfield.label || "").replace(/\([^\)]*\)/g, "");
 					field_label_map[grid_doctype + "-" + fname] = 
diff --git a/buying/doctype/purchase_order/test_purchase_order.py b/buying/doctype/purchase_order/test_purchase_order.py
index be2e294..34286db 100644
--- a/buying/doctype/purchase_order/test_purchase_order.py
+++ b/buying/doctype/purchase_order/test_purchase_order.py
@@ -31,6 +31,7 @@
 		from controllers.buying_controller import WrongWarehouseCompany
 		po = webnotes.bean(copy=test_records[0])
 		po.doc.company = "_Test Company 1"
+		po.doc.conversion_rate = 0.0167
 		self.assertRaises(WrongWarehouseCompany, po.insert)
 
 
diff --git a/controllers/accounts_controller.py b/controllers/accounts_controller.py
index 69522c4..5439030 100644
--- a/controllers/accounts_controller.py
+++ b/controllers/accounts_controller.py
@@ -55,14 +55,18 @@
 
 				if self.doc.price_list_currency:
 					if not self.doc.plc_conversion_rate:
-						exchange = self.doc.price_list_currency + "-" + get_company_currency(self.doc.company)
-						self.doc.plc_conversion_rate = flt(webnotes.conn.get_value("Currency Exchange",
-							exchange, "exchange_rate"))
+						company_currency = get_company_currency(self.doc.company)
+						if self.doc.price_list_currency == company_currency:
+							self.doc.plc_conversion_rate = 1.0
+						else:
+							exchange = self.doc.price_list_currency + "-" + company_currency
+							self.doc.plc_conversion_rate = flt(webnotes.conn.get_value("Currency Exchange",
+								exchange, "exchange_rate"))
 						
 					if not self.doc.currency:
 						self.doc.currency = self.doc.price_list_currency
 						self.doc.conversion_rate = self.doc.plc_conversion_rate
-				
+						
 	def set_missing_item_details(self, get_item_details):
 		"""set missing item values"""
 		for item in self.doclist.get({"parentfield": self.fname}):
@@ -71,34 +75,44 @@
 				ret = get_item_details(args)
 				for fieldname, value in ret.items():
 					if self.meta.get_field(fieldname, parentfield=self.fname) and \
-						item.fields.get(fieldname) is None:
+						item.fields.get(fieldname) is None and value is not None:
 							item.fields[fieldname] = value
 							
-	def set_taxes(self, tax_doctype, tax_parentfield, tax_master_field):
+	def set_taxes(self, tax_parentfield, tax_master_field):
 		if not self.meta.get_field(tax_parentfield):
 			return
 			
+		tax_master_doctype = self.meta.get_field(tax_master_field).options
+			
 		if not self.doclist.get({"parentfield": tax_parentfield}):
 			if not self.doc.fields.get(tax_master_field):
 				# get the default tax master
 				self.doc.fields[tax_master_field] = \
-					webnotes.conn.get_value(tax_doctype + " Master", {"is_default": 1})
-				
-			if self.doc.fields.get(tax_master_field):
-				from webnotes.model import default_fields
-				tax_master = webnotes.bean(tax_doctype + " Master", self.doc.fields.get(tax_master_field))
-				
-				for i, tax in enumerate(tax_master.doclist.get({"parentfield": tax_parentfield})):
-					for fieldname in default_fields:
-						tax.fields[fieldname] = None
+					webnotes.conn.get_value(tax_master_doctype, {"is_default": 1})
 					
-					tax.fields.update({
-						"doctype": tax_doctype,
-						"parentfield": tax_parentfield,
-						"idx": i+1
-					})
-					
-					self.doclist.append(tax)
+			self.append_taxes_from_master(tax_parentfield, tax_master_field, tax_master_doctype)
+				
+	def append_taxes_from_master(self, tax_parentfield, tax_master_field, tax_master_doctype=None):
+		if self.doc.fields.get(tax_master_field):
+			if not tax_master_doctype:
+				tax_master_doctype = self.meta.get_field(tax_master_field).options
+			
+			tax_doctype = self.meta.get_field(tax_parentfield).options
+			
+			from webnotes.model import default_fields
+			tax_master = webnotes.bean(tax_master_doctype, self.doc.fields.get(tax_master_field))
+			
+			for i, tax in enumerate(tax_master.doclist.get({"parentfield": tax_parentfield})):
+				for fieldname in default_fields:
+					tax.fields[fieldname] = None
+				
+				tax.fields.update({
+					"doctype": tax_doctype,
+					"parentfield": tax_parentfield,
+					"idx": i+1
+				})
+				
+				self.doclist.append(tax)
 					
 	def calculate_taxes_and_totals(self):
 		self.doc.conversion_rate = flt(self.doc.conversion_rate)
diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py
index 885f9e5..e58eb03 100644
--- a/controllers/buying_controller.py
+++ b/controllers/buying_controller.py
@@ -30,7 +30,7 @@
 	def onload_post_render(self):
 		# contact, address, item details
 		self.set_missing_values()
-		self.set_taxes("Purchase Taxes and Charges", "purchase_tax_details", "purchase_other_charges")
+		self.set_taxes("purchase_tax_details", "purchase_other_charges")
 	
 	def validate(self):
 		super(BuyingController, self).validate()
@@ -55,7 +55,7 @@
 						
 	def get_purchase_tax_details(self):
 		self.doclist = self.doc.clear_table(self.doclist, "purchase_tax_details")
-		self.set_taxes("Purchase Taxes and Charges", "purchase_tax_details", "purchase_other_charges")
+		self.set_taxes("purchase_tax_details", "purchase_other_charges")
 		
 	def validate_warehouse_belongs_to_company(self):
 		for warehouse, company in webnotes.conn.get_values("Warehouse", 
diff --git a/controllers/selling_controller.py b/controllers/selling_controller.py
index 087df34..8f1f421 100644
--- a/controllers/selling_controller.py
+++ b/controllers/selling_controller.py
@@ -28,48 +28,62 @@
 		# contact, address, item details and pos details (if applicable)
 		self.set_missing_values()
 		
-		self.set_taxes("Sales Taxes and Charges", "other_charges", "charge")
+		self.set_taxes("other_charges", "charge")
 			
 	def set_missing_values(self, for_validate=False):
 		super(SellingController, self).set_missing_values(for_validate)
 		
-		self.set_price_list_currency("Selling")
-		
 		# set contact and address details for customer, if they are not mentioned
 		self.set_missing_lead_customer_details()
-					
-		self.set_missing_item_details(get_item_details)
+		
+		self.set_price_list_and_item_details()
 		
 	def set_missing_lead_customer_details(self):
 		if self.doc.customer:
-			if not (self.doc.contact_person and self.doc.customer_address):
-				for fieldname, val in self.get_default_address_and_contact("customer").items():
+			if not (self.doc.contact_person and self.doc.customer_address and self.doc.customer_name):
+				for fieldname, val in self.get_customer_defaults().items():
 					if not self.doc.fields.get(fieldname) and self.meta.get_field(fieldname):
 						self.doc.fields[fieldname] = val
 		
-			customer_fetch = webnotes.conn.get_value("Customer", self.doc.customer,
-				['customer_name', 'customer_group', 'territory'], as_dict=True)
-			for fieldname in ['customer_name', 'customer_group', 'territory']:
-				if not self.doc.fields.get(fieldname):
-					self.doc.fields[fieldname] = customer_fetch[fieldname]
-			
 		elif self.doc.lead:
-			lead_fetch = webnotes.conn.get_value("Lead", self.doc.lead,
-				['company_name', 'lead_name', 'territory'], as_dict=True)
-			if not self.doc.customer_name:
-				self.doc.customer_name = lead_fetch.company_name or lead_fetch.lead_name
-			if not self.doc.territory:
-				self.doc.territory = lead_fetch.territory
-							
+			if not (self.doc.customer_address and self.doc.customer_name and \
+				self.doc.contact_display):
+				for fieldname, val in self.get_lead_defaults().items():
+					if not self.doc.fields.get(fieldname) and self.meta.get_field(fieldname):
+						self.doc.fields[fieldname] = val
+						
+	def set_price_list_and_item_details(self):
+		self.set_price_list_currency("Selling")
+		self.set_missing_item_details(get_item_details)
+								
 	def get_other_charges(self):
 		self.doclist = self.doc.clear_table(self.doclist, "other_charges")
-		self.set_taxes("Sales Taxes and Charges", "other_charges", "charge")
+		self.set_taxes("other_charges", "charge")
 		
-	def set_customer_defaults(self):
-		self.get_default_customer_address()
-		
-		if self.meta.get_field("shipping_address"):
-			self.doc.fields.update(self.get_shipping_address(self.doc.customer))
+	def apply_shipping_rule(self):
+		if self.doc.shipping_rule:
+			shipping_rule = webnotes.bean("Shipping Rule", self.doc.shipping_rule)
+			value = self.doc.net_total
+			
+			# TODO
+			# shipping rule calculation based on item's net weight
+			
+			shipping_amount = 0.0
+			for condition in shipping_rule.doclist.get({"parentfield": "shipping_rule_conditions"}):
+				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
+				})
 		
 	def set_total_in_words(self):
 		from webnotes.utils import money_in_words
diff --git a/public/js/website_utils.js b/public/js/website_utils.js
index 59614c5..e714514 100644
--- a/public/js/website_utils.js
+++ b/public/js/website_utils.js
@@ -55,6 +55,15 @@
 					$(opts.btn).addClass("btn-danger");
 					setTimeout(function() { $(opts.btn).removeClass("btn-danger"); }, 1000);
 				}
+				try {
+					var err = JSON.parse(data.exc);
+					if($.isArray(err)) {
+						err = err.join("\n");
+					}
+					console.error ? console.error(err) : console.log(err);
+				} catch(e) {
+					console.log(data.exc);
+				}
 			} else{
 				if(opts.btn) {
 					$(opts.btn).addClass("btn-success");
@@ -66,6 +75,9 @@
 			}
 			if(opts.callback)
 				opts.callback(data);
+		},
+		error: function(response) {
+			console.error ? console.error(response) : console.log(response);
 		}
 	});
 	
diff --git a/selling/doctype/installation_note/installation_note.js b/selling/doctype/installation_note/installation_note.js
index b0e005e..61d0484 100644
--- a/selling/doctype/installation_note/installation_note.js
+++ b/selling/doctype/installation_note/installation_note.js
@@ -8,69 +8,82 @@
 // 
 // This program is distributed in the hope that it will be useful,
 // but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
 // GNU General Public License for more details.
 // 
 // You should have received a copy of the GNU General Public License
-// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+// along with this program.	If not, see <http://www.gnu.org/licenses/>.
 
 cur_frm.cscript.tname = "Installation Note Item";
 cur_frm.cscript.fname = "installed_item_details";
 
+wn.provide("erpnext.selling");
+// TODO commonify this code
+erpnext.selling.InstallationNote = wn.ui.form.Controller.extend({
+	customer: function() {
+		var me = this;
+		if(this.frm.doc.customer) {
+			this.frm.call({
+				doc: this.frm.doc,
+				method: "set_customer_defaults",
+				callback: function(r) {
+					if(!r.exc) me.frm.refresh_fields();
+				}
+			});
+			
+			// TODO shift this to depends_on
+			unhide_field(['customer_address', 'contact_person', 'customer_name',
+				'address_display', 'contact_display', 'contact_mobile', 'contact_email', 
+				'territory', 'customer_group']);
+		}
+	}
+});
+
+$.extend(cur_frm.cscript, new erpnext.selling.InstallationNote({frm: cur_frm}));
+
 cur_frm.cscript.onload = function(doc, dt, dn) {
-  if(!doc.status) set_multiple(dt,dn,{status:'Draft'});
-  if(doc.__islocal){
-    set_multiple(dt,dn,{inst_date:get_today()});
-    hide_field(['customer_address','contact_person','customer_name','address_display','contact_display','contact_mobile','contact_email','territory','customer_group']);        
-  }
-  if (doc.customer) {
-     unhide_field(['customer_address','contact_person','customer_name','address_display','contact_display','contact_mobile','contact_email','territory','customer_group']);
-  }   
+	if(!doc.status) set_multiple(dt,dn,{status:'Draft'});
+	if(doc.__islocal){
+		set_multiple(dt,dn,{inst_date:get_today()});
+		hide_field(['customer_address','contact_person','customer_name','address_display','contact_display','contact_mobile','contact_email','territory','customer_group']);				
+	}
+	if (doc.customer) {
+		 unhide_field(['customer_address','contact_person','customer_name','address_display','contact_display','contact_mobile','contact_email','territory','customer_group']);
+	}	 
 }
 
 cur_frm.fields_dict['delivery_note_no'].get_query = function(doc) {
-  doc = locals[this.doctype][this.docname];
-  var cond = '';
-  if(doc.customer) {
-    cond = '`tabDelivery Note`.customer = "'+doc.customer+'" AND';
-  }
-  return repl('SELECT DISTINCT `tabDelivery Note`.name, `tabDelivery Note`.customer_name  FROM `tabDelivery Note`, `tabDelivery Note Item` WHERE `tabDelivery Note`.company = "%(company)s" AND `tabDelivery Note`.docstatus = 1 AND ifnull(`tabDelivery Note`.per_installed,0) < 99.99 AND %(cond)s `tabDelivery Note`.name LIKE "%s" ORDER BY `tabDelivery Note`.name DESC LIMIT 50', {company:doc.company, cond:cond});
+	doc = locals[this.doctype][this.docname];
+	var cond = '';
+	if(doc.customer) {
+		cond = '`tabDelivery Note`.customer = "'+doc.customer+'" AND';
+	}
+	return repl('SELECT DISTINCT `tabDelivery Note`.name, `tabDelivery Note`.customer_name	FROM `tabDelivery Note`, `tabDelivery Note Item` WHERE `tabDelivery Note`.company = "%(company)s" AND `tabDelivery Note`.docstatus = 1 AND ifnull(`tabDelivery Note`.per_installed,0) < 99.99 AND %(cond)s `tabDelivery Note`.name LIKE "%s" ORDER BY `tabDelivery Note`.name DESC LIMIT 50', {company:doc.company, cond:cond});
 }
 
 
 cur_frm.fields_dict['territory'].get_query = function(doc,cdt,cdn) {
-  return 'SELECT `tabTerritory`.`name`,`tabTerritory`.`parent_territory` FROM `tabTerritory` WHERE `tabTerritory`.`is_group` = "No" AND `tabTerritory`.`docstatus`!= 2 AND `tabTerritory`.%(key)s LIKE "%s"  ORDER BY  `tabTerritory`.`name` ASC LIMIT 50';
+	return 'SELECT `tabTerritory`.`name`,`tabTerritory`.`parent_territory` FROM `tabTerritory` WHERE `tabTerritory`.`is_group` = "No" AND `tabTerritory`.`docstatus`!= 2 AND `tabTerritory`.%(key)s LIKE "%s"	ORDER BY	`tabTerritory`.`name` ASC LIMIT 50';
 }
 
 cur_frm.cscript.get_items = function(doc, dt, dn) {
-  var callback = function(r,rt) { 
-	  unhide_field(['customer_address','contact_person','customer_name','address_display','contact_display','contact_mobile','contact_email','territory','customer_group']);
-	  cur_frm.refresh();
-  }
-  get_server_fields('pull_delivery_note_details','','',doc, dt, dn,1,callback);
+	var callback = function(r,rt) { 
+		unhide_field(['customer_address','contact_person','customer_name','address_display','contact_display','contact_mobile','contact_email','territory','customer_group']);
+		cur_frm.refresh();
+	}
+	get_server_fields('pull_delivery_note_details','','',doc, dt, dn,1,callback);
 }
 
-//customer
-cur_frm.cscript.customer = function(doc,dt,dn) {
-  var callback = function(r,rt) {
-      var doc = locals[cur_frm.doctype][cur_frm.docname];
-      cur_frm.refresh();
-  }   
-
-  if(doc.customer) $c_obj(make_doclist(doc.doctype, doc.name), 'get_default_customer_address', '', callback);
-  if(doc.customer) unhide_field(['customer_address','contact_person','customer_name','address_display','contact_display','contact_mobile','contact_email','territory','customer_group']);
-}
-
-cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) {    
-  if(doc.customer) get_server_fields('get_customer_address', JSON.stringify({customer: doc.customer, address: doc.customer_address, contact: doc.contact_person}),'', doc, dt, dn, 1);
+cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) {		
+	if(doc.customer) get_server_fields('get_customer_address', JSON.stringify({customer: doc.customer, address: doc.customer_address, contact: doc.contact_person}),'', doc, dt, dn, 1);
 }
 
 cur_frm.fields_dict['customer_address'].get_query = function(doc, cdt, cdn) {
-  return 'SELECT name,address_line1,city FROM tabAddress WHERE customer = "'+ doc.customer +'" AND docstatus != 2 AND name LIKE "%s" ORDER BY name ASC LIMIT 50';
+	return 'SELECT name,address_line1,city FROM tabAddress WHERE customer = "'+ doc.customer +'" AND docstatus != 2 AND name LIKE "%s" ORDER BY name ASC LIMIT 50';
 }
 
 cur_frm.fields_dict['contact_person'].get_query = function(doc, cdt, cdn) {
-  return 'SELECT name,CONCAT(first_name," ",ifnull(last_name,"")) As FullName,department,designation FROM tabContact WHERE customer = "'+ doc.customer +'" AND docstatus != 2 AND name LIKE "%s" ORDER BY name ASC LIMIT 50';
+	return 'SELECT name,CONCAT(first_name," ",ifnull(last_name,"")) As FullName,department,designation FROM tabContact WHERE customer = "'+ doc.customer +'" AND docstatus != 2 AND name LIKE "%s" ORDER BY name ASC LIMIT 50';
 }
 
 cur_frm.fields_dict.customer.get_query = erpnext.utils.customer_query;
\ No newline at end of file
diff --git a/selling/doctype/lead/lead.txt b/selling/doctype/lead/lead.txt
index eed87ba..57cb038 100644
--- a/selling/doctype/lead/lead.txt
+++ b/selling/doctype/lead/lead.txt
@@ -2,7 +2,7 @@
  {
   "creation": "2013-04-10 11:45:37", 
   "docstatus": 0, 
-  "modified": "2013-06-28 15:08:26", 
+  "modified": "2013-07-02 16:03:50", 
   "modified_by": "Administrator", 
   "owner": "Administrator"
  }, 
@@ -305,13 +305,6 @@
  }, 
  {
   "doctype": "DocField", 
-  "fieldname": "default_price_list", 
-  "fieldtype": "Link", 
-  "label": "Default Price List", 
-  "options": "Price List"
- }, 
- {
-  "doctype": "DocField", 
   "fieldname": "fiscal_year", 
   "fieldtype": "Select", 
   "hidden": 1, 
diff --git a/selling/doctype/opportunity/opportunity.js b/selling/doctype/opportunity/opportunity.js
index 3a56d4d..4a28e7e 100644
--- a/selling/doctype/opportunity/opportunity.js
+++ b/selling/doctype/opportunity/opportunity.js
@@ -16,6 +16,30 @@
 
 wn.require('app/utilities/doctype/sms_control/sms_control.js');
 
+wn.provide("erpnext.selling");
+// TODO commonify this code
+erpnext.selling.Opportunity = wn.ui.form.Controller.extend({
+	customer: function() {
+		var me = this;
+		if(this.frm.doc.customer) {
+			this.frm.call({
+				doc: this.frm.doc,
+				method: "set_customer_defaults",
+				callback: function(r) {
+					if(!r.exc) me.frm.refresh_fields();
+				}
+			});
+			
+			// TODO shift this to depends_on
+			unhide_field(['customer_address', 'contact_person', 'customer_name',
+				'address_display', 'contact_display', 'contact_mobile', 'contact_email', 
+				'territory', 'customer_group']);
+		}
+	}
+});
+
+$.extend(cur_frm.cscript, new erpnext.selling.Opportunity({frm: cur_frm}));
+
 cur_frm.cscript.refresh = function(doc, cdt, cdn){
 	erpnext.hide_naming_series();
 	
@@ -96,28 +120,6 @@
 	}
 }
 
-// customer
-cur_frm.cscript.customer = function(doc,dt,dn) {
-	cur_frm.toggle_display("contact_info", doc.customer || doc.lead);
-	
-	if(doc.customer) {
-		cur_frm.call({
-			doc: cur_frm.doc,
-			method: "get_default_customer_address",
-			args: { customer: doc.customer },
-			callback: function(r) {
-				if(!r.exc) {
-					cur_frm.refresh();
-				}
-			}
-		});
-		
-		unhide_field(["customer_name", "customer_address", "contact_person",
-			"address_display", "contact_display", "contact_mobile", "contact_email",
-			"territory", "customer_group"]);
-	}
-}
-
 cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) {		
 	if(doc.customer) get_server_fields('get_customer_address', JSON.stringify({customer: doc.customer, address: doc.customer_address, contact: doc.contact_person}),'', doc, dt, dn, 1);
 }
@@ -146,7 +148,15 @@
 	cur_frm.toggle_display("contact_info", doc.customer || doc.lead);
 	
 	if(doc.lead) {
-		get_server_fields('get_lead_details', doc.lead,'', doc, cdt, cdn, 1);
+		cur_frm.call({
+			doc: cur_frm.doc,
+			method: "set_lead_defaults",
+			callback: function(r) {
+				if(!r.exc) {
+					cur_frm.refresh_fields();
+				}
+			}
+		});
 		unhide_field(['customer_name', 'address_display','contact_mobile', 'contact_email', 
 			'territory']);	
 	}
diff --git a/selling/doctype/opportunity/opportunity.py b/selling/doctype/opportunity/opportunity.py
index 9fb061b..c569157 100644
--- a/selling/doctype/opportunity/opportunity.py
+++ b/selling/doctype/opportunity/opportunity.py
@@ -24,9 +24,8 @@
 sql = webnotes.conn.sql
 	
 from utilities.transaction_base import TransactionBase
-
 class DocType(TransactionBase):
-	def __init__(self,doc,doclist=[]):
+	def __init__(self,doc,doclist):
 		self.doc = doc
 		self.doclist = doclist
 		self.fname = 'enq_details'
diff --git a/selling/doctype/quotation/quotation.js b/selling/doctype/quotation/quotation.js
index 4276193..d574aac 100644
--- a/selling/doctype/quotation/quotation.js
+++ b/selling/doctype/quotation/quotation.js
@@ -80,7 +80,15 @@
 
 cur_frm.cscript.lead = function(doc, cdt, cdn) {
 	if(doc.lead) {
-		get_server_fields('get_lead_details', doc.lead,'', doc, cdt, cdn, 1);
+		cur_frm.call({
+			doc: cur_frm.doc,
+			method: "set_lead_defaults",
+			callback: function(r) {
+				if(!r.exc) {
+					cur_frm.refresh_fields();
+				}
+			}
+		});
 		unhide_field('territory');
 	}
 }
diff --git a/selling/doctype/sales_common/sales_common.js b/selling/doctype/sales_common/sales_common.js
index 77e7759..8f501d8 100644
--- a/selling/doctype/sales_common/sales_common.js
+++ b/selling/doctype/sales_common/sales_common.js
@@ -26,10 +26,78 @@
 
 erpnext.selling.SellingController = erpnext.TransactionController.extend({
 	setup: function() {
+		var me = this;
+		
 		this.frm.add_fetch("sales_partner", "commission_rate", "commission_rate");
 		
-		if(this.frm.fields_dict.shipping_address_name && this.frm.fields_dict.customer_address)
-			this.frm.fields_dict.shipping_address_name.get_query = this.frm.fields_dict['customer_address'].get_query;
+		if(this.frm.fields_dict.shipping_address_name && this.frm.fields_dict.customer_address) {
+			this.frm.fields_dict.shipping_address_name.get_query = 
+				this.frm.fields_dict['customer_address'].get_query;
+		}
+		
+		this.frm.set_query("customer_address", function() {
+			return 'SELECT name, address_line1, city FROM tabAddress \
+				WHERE customer = "'+ me.frm.doc.customer +'" AND docstatus != 2 AND \
+				%(key)s LIKE "%s" ORDER BY name ASC LIMIT 50';
+		});
+		
+		this.frm.set_query("contact_person", function() {
+			return 'SELECT name, CONCAT(first_name," ",ifnull(last_name,"")) As FullName, \
+				department, designation FROM tabContact WHERE customer = "'+ me.frm.doc.customer + 
+				'" AND docstatus != 2 AND %(key)s LIKE "%s" ORDER BY name ASC LIMIT 50';
+		});
+		
+		if(this.frm.fields_dict.charge) {
+			this.frm.set_query("charge", function() {
+				return 'SELECT DISTINCT `tabSales Taxes and Charges Master`.name FROM \
+					`tabSales Taxes and Charges Master` \
+					WHERE `tabSales Taxes and Charges Master`.company = "' + me.frm.doc.company +
+					'" AND `tabSales Taxes and Charges Master`.company is not NULL \
+					AND `tabSales Taxes and Charges Master`.docstatus != 2 \
+					AND `tabSales Taxes and Charges Master`.%(key)s LIKE "%s" \
+					ORDER BY `tabSales Taxes and Charges Master`.name LIMIT 50';
+			});
+		}
+		
+		this.frm.fields_dict.customer.get_query = erpnext.utils.customer_query;
+
+		this.frm.fields_dict.lead && this.frm.set_query("lead", erpnext.utils.lead_query);
+
+		if(!this.fname) {
+			return;
+		}
+		
+		if(this.frm.fields_dict[this.fname].grid.get_field('item_code')) {
+			this.frm.set_query("item_code", this.fname, function() {
+				return me.frm.doc.order_type === "Maintenance" ?
+					erpnext.queries.item({'ifnull(tabItem.is_service_item, "No")': "Yes"}) :
+					erpnext.queries.item({'ifnull(tabItem.is_sales_item, "No")': "Yes"});
+			});
+		}
+		
+		if(this.frm.fields_dict[this.fname].grid.get_field('batch_no')) {
+			this.frm.set_query("batch_no", this.fname, function(doc, cdt, cdn) {
+				var item = wn.model.get_doc(cdt, cdn);
+				if(!item.item_code) {
+					wn.throw("Please enter Item Code to get batch no");
+				} else {
+					if(item.warehouse) {
+						return "select batch_no from `tabStock Ledger Entry` sle \
+							where item_code = '" + item.item_code + 
+							"' and warehouse = '" + item.warehouse +
+							"' and ifnull(is_cancelled, 'No') = 'No' and batch_no like '%s' \
+							and exists(select * from `tabBatch` where \
+							name = sle.batch_no and expiry_date >= '" + me.frm.doc.posting_date + 
+							"' and docstatus != 2) group by batch_no having sum(actual_qty) > 0 \
+							order by batch_no desc limit 50";
+					} else {
+						return "SELECT name FROM tabBatch WHERE docstatus != 2 AND item = '" + 
+							item.item_code + "' and expiry_date >= '" + me.frm.doc.posting_date + 
+							"' AND name like '%s' ORDER BY name DESC LIMIT 50";
+					}
+				}
+			});
+		}
 	},
 	
 	onload: function() {
@@ -397,7 +465,7 @@
 		
 		var setup_field_label_map = function(fields_list, currency) {
 			$.each(fields_list, function(i, fname) {
-				var docfield = wn.meta.get_docfield(me.frm.doc.doctype, fname);
+				var docfield = wn.meta.docfield_map[me.frm.doc.doctype][fname];
 				if(docfield) {
 					var label = wn._(docfield.label || "").replace(/\([^\)]*\)/g, "");
 					field_label_map[fname] = label.trim() + " (" + currency + ")";
@@ -442,7 +510,7 @@
 		var setup_field_label_map = function(fields_list, currency, parentfield) {
 			var grid_doctype = me.frm.fields_dict[parentfield].grid.doctype;
 			$.each(fields_list, function(i, fname) {
-				var docfield = wn.meta.get_docfield(grid_doctype, fname);
+				var docfield = wn.meta.docfield_map[grid_doctype][fname];
 				if(docfield) {
 					var label = wn._(docfield.label || "").replace(/\([^\)]*\)/g, "");
 					field_label_map[grid_doctype + "-" + fname] = 
@@ -502,15 +570,6 @@
 	}
 });
 
-// to save previous state of cur_frm.cscript
-var prev_cscript = {};
-$.extend(prev_cscript, cur_frm.cscript);
-
-cur_frm.cscript = new erpnext.selling.SellingController({frm: cur_frm});
-
-// for backward compatibility: combine new and previous states
-$.extend(cur_frm.cscript, prev_cscript);
-
 // Help for Sales BOM items
 var set_sales_bom_help = function(doc) {
 	if(!cur_frm.fields_dict.packing_list) return;
@@ -533,62 +592,4 @@
 		}
 	}
 	refresh_field('sales_bom_help');
-}
-
-cur_frm.fields_dict[cur_frm.cscript.fname].grid.get_field("item_code").get_query = function(doc, cdt, cdn) {
-	if (doc.order_type == "Maintenance") {
-	 	return erpnext.queries.item({
-			'ifnull(tabItem.is_service_item, "No")': 'Yes'
-		});
-	} else {
-		return erpnext.queries.item({
-			'ifnull(tabItem.is_sales_item, "No")': 'Yes'
-		});
-	}
-}
-
-cur_frm.fields_dict[cur_frm.cscript.fname].grid.get_field('batch_no').get_query = 
-	function(doc, cdt, cdn) {
-		var d = locals[cdt][cdn];
-		if(d.item_code) {
-			if (d.warehouse) {
-				return "select batch_no from `tabStock Ledger Entry` sle \
-					where item_code = '" + d.item_code + "' and warehouse = '" + d.warehouse +
-					"' and ifnull(is_cancelled, 'No') = 'No' and batch_no like '%s' \
-					and exists(select * from `tabBatch` where \
-					name = sle.batch_no and expiry_date >= '" + doc.posting_date + 
-					"' and docstatus != 2) group by batch_no having sum(actual_qty) > 0 \
-					order by batch_no desc limit 50";
-			} else {
-				return "SELECT name FROM tabBatch WHERE docstatus != 2 AND item = '" + 
-					d.item_code + "' and expiry_date >= '" + doc.posting_date + 
-					"' AND name like '%s' ORDER BY name DESC LIMIT 50";
-			}		
-		} else {
-			msgprint("Please enter Item Code to get batch no");
-		}
-	}
-
-cur_frm.fields_dict['customer_address'].get_query = function(doc, cdt, cdn) {
-	return 'SELECT name, address_line1, city FROM tabAddress \
-		WHERE customer = "'+ doc.customer +'" AND docstatus != 2 AND \
-		%(key)s LIKE "%s" ORDER BY name ASC LIMIT 50';
-}
-
-cur_frm.fields_dict['contact_person'].get_query = function(doc, cdt, cdn) {
-	return 'SELECT name, CONCAT(first_name," ",ifnull(last_name,"")) As FullName, \
-		department, designation FROM tabContact WHERE customer = "'+ doc.customer + 
-		'" AND docstatus != 2 AND %(key)s LIKE "%s" ORDER BY name ASC LIMIT 50';
-}
-
-// ************* GET OTHER CHARGES BASED ON COMPANY *************
-cur_frm.fields_dict.charge.get_query = function(doc) {
-	return 'SELECT DISTINCT `tabSales Taxes and Charges Master`.name FROM \
-		`tabSales Taxes and Charges Master` WHERE `tabSales Taxes and Charges Master`.company = "'
-		+doc.company+'" AND `tabSales Taxes and Charges Master`.company is not NULL \
-		AND `tabSales Taxes and Charges Master`.docstatus != 2 \
-		AND `tabSales Taxes and Charges Master`.%(key)s LIKE "%s" \
-		ORDER BY `tabSales Taxes and Charges Master`.name LIMIT 50';
-}
-
-cur_frm.fields_dict.customer.get_query = erpnext.utils.customer_query;
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/setup/doctype/currency_exchange/currency_exchange.js b/setup/doctype/currency_exchange/currency_exchange.js
new file mode 100644
index 0000000..02cba11
--- /dev/null
+++ b/setup/doctype/currency_exchange/currency_exchange.js
@@ -0,0 +1,39 @@
+// ERPNext - web based ERP (http://erpnext.com)
+// Copyright (C) 2012 Web Notes Technologies Pvt Ltd
+// 
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+$.extend(cur_frm.cscript, {
+	refresh: function() {
+		cur_frm.cscript.set_exchange_rate_label();
+	},
+	
+	from_currency: function() {
+		cur_frm.cscript.set_exchange_rate_label();
+	},
+	
+	to_currency: function() {
+		cur_frm.cscript.set_exchange_rate_label();
+	},
+	
+	set_exchange_rate_label: function() {
+		if(cur_frm.doc.from_currency && cur_frm.doc.to_currency) {
+			var default_label = wn._(wn.meta.docfield_map[cur_frm.doctype]["exchange_rate"].label);
+			console.log(default_label + 
+				repl(" (1 %(from_currency)s = [?] %(to_currency)s)", cur_frm.doc));
+			cur_frm.fields_dict.exchange_rate.set_label(default_label + 
+				repl(" (1 %(from_currency)s = [?] %(to_currency)s)", cur_frm.doc));
+		}
+	}
+});
\ No newline at end of file
diff --git a/setup/doctype/currency_exchange/currency_exchange.txt b/setup/doctype/currency_exchange/currency_exchange.txt
index 1a05ebe..94bb28a 100644
--- a/setup/doctype/currency_exchange/currency_exchange.txt
+++ b/setup/doctype/currency_exchange/currency_exchange.txt
@@ -2,7 +2,7 @@
  {
   "creation": "2013-06-20 15:40:29", 
   "docstatus": 0, 
-  "modified": "2013-06-20 15:40:29", 
+  "modified": "2013-07-03 17:11:40", 
   "modified_by": "Administrator", 
   "owner": "Administrator"
  }, 
@@ -24,6 +24,16 @@
   "reqd": 1
  }, 
  {
+  "doctype": "DocPerm", 
+  "name": "__common__", 
+  "parent": "Currency Exchange", 
+  "parentfield": "permissions", 
+  "parenttype": "DocType", 
+  "permlevel": 0, 
+  "read": 1, 
+  "report": 1
+ }, 
+ {
   "doctype": "DocType", 
   "name": "Currency Exchange"
  }, 
@@ -46,5 +56,24 @@
   "fieldname": "exchange_rate", 
   "fieldtype": "Float", 
   "label": "Exchange Rate"
+ }, 
+ {
+  "cancel": 1, 
+  "create": 1, 
+  "doctype": "DocPerm", 
+  "role": "Accounts Manager", 
+  "write": 1
+ }, 
+ {
+  "doctype": "DocPerm", 
+  "role": "Accounts User"
+ }, 
+ {
+  "doctype": "DocPerm", 
+  "role": "Sales User"
+ }, 
+ {
+  "doctype": "DocPerm", 
+  "role": "Purchase User"
  }
 ]
\ No newline at end of file
diff --git a/setup/doctype/currency_exchange/test_currency_exchange.py b/setup/doctype/currency_exchange/test_currency_exchange.py
new file mode 100644
index 0000000..cd0ac21
--- /dev/null
+++ b/setup/doctype/currency_exchange/test_currency_exchange.py
@@ -0,0 +1,14 @@
+test_records = [
+	[{
+		"doctype": "Currency Exchange",
+		"from_currency": "USD",
+		"to_currency": "INR",
+		"exchange_rate": 1
+	}],
+	[{
+		"doctype": "Currency Exchange",
+		"from_currency": "USD",
+		"to_currency": "EUR",
+		"exchange_rate": 0.773
+	}]
+]
\ No newline at end of file
diff --git a/setup/doctype/price_list/price_list.py b/setup/doctype/price_list/price_list.py
index b40a46f..a5f22d9 100644
--- a/setup/doctype/price_list/price_list.py
+++ b/setup/doctype/price_list/price_list.py
@@ -17,7 +17,7 @@
 from __future__ import unicode_literals
 import webnotes
 from webnotes import msgprint, _
-from webnotes.utils import comma_or
+from webnotes.utils import comma_or, cint
 from webnotes.model.controller import DocListController
 
 class DocType(DocListController):
@@ -30,6 +30,14 @@
 			msgprint(_(self.meta.get_label("buying_or_selling")) + " " + _("must be one of") + " " +
 				comma_or(["Buying", "Selling"]), raise_exception=True)
 				
+		# at least one territory
+		self.validate_table_has_rows("valid_for_territories")
+		
+	def on_update(self):
+		cart_settings = webnotes.get_obj("Shopping Cart Settings")
+		if cint(cart_settings.doc.enabled):
+			cart_settings.validate_price_lists()
+				
 	def on_trash(self):
 		webnotes.conn.sql("""delete from `tabItem Price` where price_list_name = %s""", 
 			self.doc.name)
\ No newline at end of file
diff --git a/setup/doctype/price_list/test_price_list.py b/setup/doctype/price_list/test_price_list.py
index 68d9a35..c3633e7 100644
--- a/setup/doctype/price_list/test_price_list.py
+++ b/setup/doctype/price_list/test_price_list.py
@@ -1,8 +1,59 @@
 test_records = [
-	[{
-		"doctype": "Price List",
-		"price_list_name": "_Test Price List",
-		"currency": "INR",
-		"buying_or_selling": "Selling"
-	}]
+	[
+		{
+			"doctype": "Price List",
+			"price_list_name": "_Test Price List",
+			"currency": "INR",
+			"buying_or_selling": "Selling"
+		},
+		{
+			"doctype": "For Territory",
+			"parentfield": "valid_for_territories",
+			"territory": "All Territories"
+		}
+	],
+	[
+		{
+			"doctype": "Price List",
+			"price_list_name": "_Test Price List 2",
+			"currency": "INR",
+			"buying_or_selling": "Selling"
+		},
+		{
+			"doctype": "For Territory",
+			"parentfield": "valid_for_territories",
+			"territory": "_Test Territory Rest of the World"
+		}
+	],
+	[
+		{
+			"doctype": "Price List",
+			"price_list_name": "_Test Price List India",
+			"currency": "INR",
+			"buying_or_selling": "Selling"
+		},
+		{
+			"doctype": "For Territory",
+			"parentfield": "valid_for_territories",
+			"territory": "_Test Territory India"
+		}
+	],
+	[
+		{
+			"doctype": "Price List",
+			"price_list_name": "_Test Price List Rest of the World",
+			"currency": "USD",
+			"buying_or_selling": "Selling"
+		},
+		{
+			"doctype": "For Territory",
+			"parentfield": "valid_for_territories",
+			"territory": "_Test Territory Rest of the World"
+		},
+		{
+			"doctype": "For Territory",
+			"parentfield": "valid_for_territories",
+			"territory": "_Test Territory United States"
+		}
+	],
 ]
\ No newline at end of file
diff --git a/setup/doctype/territory/test_territory.py b/setup/doctype/territory/test_territory.py
index 2124199..75003f2 100644
--- a/setup/doctype/territory/test_territory.py
+++ b/setup/doctype/territory/test_territory.py
@@ -4,5 +4,29 @@
 		"territory_name": "_Test Territory",
 		"parent_territory": "All Territories",
 		"is_group": "No",
-	}]
+	}],
+	[{
+		"doctype": "Territory",
+		"territory_name": "_Test Territory India",
+		"parent_territory": "All Territories",
+		"is_group": "Yes",
+	}],
+	[{
+		"doctype": "Territory",
+		"territory_name": "_Test Territory Maharashtra",
+		"parent_territory": "_Test Territory India",
+		"is_group": "No",
+	}],
+	[{
+		"doctype": "Territory",
+		"territory_name": "_Test Territory Rest of the World",
+		"parent_territory": "All Territories",
+		"is_group": "No",
+	}],
+	[{
+		"doctype": "Territory",
+		"territory_name": "_Test Territory United States",
+		"parent_territory": "All Territories",
+		"is_group": "No",
+	}],
 ]
\ No newline at end of file
diff --git a/setup/page/setup/setup.py b/setup/page/setup/setup.py
index 348fce9..baee676 100644
--- a/setup/page/setup/setup.py
+++ b/setup/page/setup/setup.py
@@ -91,6 +91,8 @@
 	},
 	{ "doctype": "Sales Taxes and Charges Master" },
 	{ "doctype": "Purchase Taxes and Charges Master" },
+	{ "doctype": "Shipping Rule" },
+	{ "doctype": "Currency Exchange" },
 	{
 		"type": "Section",
 		"title": "Opening Accounts and Stock",
diff --git a/setup/utils.py b/setup/utils.py
index 04c4527..812289c 100644
--- a/setup/utils.py
+++ b/setup/utils.py
@@ -39,8 +39,8 @@
 	"""Get ancestor elements of a DocType with a tree structure"""
 	lft, rgt = webnotes.conn.get_value(doctype, name, ["lft", "rgt"])
 	result = webnotes.conn.sql_list("""select name from `tab%s` 
-		where lft<%s and rgt>%s""" % (doctype, "%s", "%s"), (lft, rgt))
-	return result or None
+		where lft<%s and rgt>%s order by lft desc""" % (doctype, "%s", "%s"), (lft, rgt))
+	return result or []
 
 @webnotes.whitelist()
 def get_price_list_currency(args):
diff --git a/stock/doctype/delivery_note/delivery_note.py b/stock/doctype/delivery_note/delivery_note.py
index 30647a1..0ef185b 100644
--- a/stock/doctype/delivery_note/delivery_note.py
+++ b/stock/doctype/delivery_note/delivery_note.py
@@ -48,9 +48,6 @@
 			'keyword': 'Delivered'
 		}]
 		
-	def set_customer_defaults(self):
-		self.get_default_customer_shipping_address()
-
 	def validate_fiscal_year(self):
 		get_obj('Sales Common').validate_fiscal_year(self.doc.fiscal_year,self.doc.posting_date,'Posting Date')
 
diff --git a/support/doctype/customer_issue/customer_issue.js b/support/doctype/customer_issue/customer_issue.js
index 0f3007e..3a602b8 100644
--- a/support/doctype/customer_issue/customer_issue.js
+++ b/support/doctype/customer_issue/customer_issue.js
@@ -14,6 +14,28 @@
 // You should have received a copy of the GNU General Public License
 // along with this program.	If not, see <http://www.gnu.org/licenses/>.
 
+wn.provide("erpnext.support");
+// TODO commonify this code
+erpnext.support.CustomerIssue = wn.ui.form.Controller.extend({
+	customer: function() {
+		var me = this;
+		if(this.frm.doc.customer) {
+			this.frm.call({
+				doc: this.frm.doc,
+				method: "set_customer_defaults",
+				callback: function(r) {
+					if(!r.exc) me.frm.refresh_fields();
+				}
+			});
+			
+			// TODO shift this to depends_on
+			unhide_field(['customer_address', 'contact_person']);
+		}
+	}
+});
+
+$.extend(cur_frm.cscript, new erpnext.support.CustomerIssue({frm: cur_frm}));
+
 cur_frm.cscript.onload = function(doc,cdt,cdn){
 	if(!doc.status) 
 		set_multiple(dt,dn,{status:'Open'});	
@@ -28,18 +50,6 @@
 			cur_frm.cscript['Make Maintenance Visit']);
 }
 
-
-//customer
-cur_frm.cscript.customer = function(doc,dt,dn) {
-	var callback = function(r,rt) {
-			var doc = locals[cur_frm.doctype][cur_frm.docname];
-			cur_frm.refresh();
-	}
-
-	if(doc.customer) $c_obj(make_doclist(doc.doctype, doc.name), 'get_default_customer_address', '', callback);
-	if(doc.customer) unhide_field(['customer_address','contact_person']);
-}
-
 cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) {		
 	if(doc.customer) 
 		get_server_fields('get_customer_address', 
diff --git a/support/doctype/maintenance_schedule/maintenance_schedule.js b/support/doctype/maintenance_schedule/maintenance_schedule.js
index 30fbecf..6b3ce10 100644
--- a/support/doctype/maintenance_schedule/maintenance_schedule.js
+++ b/support/doctype/maintenance_schedule/maintenance_schedule.js
@@ -14,6 +14,31 @@
 // You should have received a copy of the GNU General Public License
 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+wn.provide("erpnext.support");
+// TODO commonify this code
+erpnext.support.MaintenanceSchedule = wn.ui.form.Controller.extend({
+	customer: function() {
+		var me = this;
+		if(this.frm.doc.customer) {
+			this.frm.call({
+				doc: this.frm.doc,
+				method: "set_customer_defaults",
+				callback: function(r) {
+					if(!r.exc) me.frm.refresh_fields();
+				}
+			});
+			
+			// TODO shift this to depends_on
+			unhide_field(['customer_address', 'contact_person', 'customer_name',
+				'address_display', 'contact_display', 'contact_mobile', 'contact_email', 
+				'territory', 'customer_group']);
+			
+		}
+	}
+});
+
+$.extend(cur_frm.cscript, new erpnext.support.MaintenanceSchedule({frm: cur_frm}));
+
 cur_frm.cscript.onload = function(doc, dt, dn) {
   if(!doc.status) set_multiple(dt,dn,{status:'Draft'});
   
@@ -23,18 +48,6 @@
   }   
 }
 
-
-//customer
-cur_frm.cscript.customer = function(doc,dt,dn) {
-  var callback = function(r,rt) {
-      var doc = locals[cur_frm.doctype][cur_frm.docname];
-      cur_frm.refresh();
-  }   
-
-  if(doc.customer) $c_obj(make_doclist(doc.doctype, doc.name), 'get_default_customer_address', '', callback);
-  if(doc.customer) unhide_field(['customer_address','contact_person','customer_name','address_display','contact_display','contact_mobile','contact_email','territory','customer_group']);
-}
-
 cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) {    
   if(doc.customer) get_server_fields('get_customer_address', JSON.stringify({customer: doc.customer, address: doc.customer_address, contact: doc.contact_person}),'', doc, dt, dn, 1);
 }
diff --git a/support/doctype/maintenance_visit/maintenance_visit.js b/support/doctype/maintenance_visit/maintenance_visit.js
index 3514a3a..3b8de75 100644
--- a/support/doctype/maintenance_visit/maintenance_visit.js
+++ b/support/doctype/maintenance_visit/maintenance_visit.js
@@ -8,16 +8,38 @@
 // 
 // This program is distributed in the hope that it will be useful,
 // but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
 // GNU General Public License for more details.
 // 
 // You should have received a copy of the GNU General Public License
-// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+// along with this program.	If not, see <http://www.gnu.org/licenses/>.
+
+wn.provide("erpnext.support");
+// TODO commonify this code
+erpnext.support.MaintenanceVisit = wn.ui.form.Controller.extend({
+	customer: function() {
+		var me = this;
+		if(this.frm.doc.customer) {
+			this.frm.call({
+				doc: this.frm.doc,
+				method: "set_customer_defaults",
+				callback: function(r) {
+					if(!r.exc) me.frm.refresh_fields();
+				}
+			});
+			
+			// TODO shift this to depends_on
+			hide_contact_info(this.frm.doc);			
+		}
+	}
+});
+
+$.extend(cur_frm.cscript, new erpnext.support.MaintenanceVisit({frm: cur_frm}));
 
 cur_frm.cscript.onload = function(doc, dt, dn) {
-  if(!doc.status) set_multiple(dt,dn,{status:'Draft'});
-  if(doc.__islocal) set_multiple(dt,dn,{mntc_date:get_today()});
-  hide_contact_info(doc);
+	if(!doc.status) set_multiple(dt,dn,{status:'Draft'});
+	if(doc.__islocal) set_multiple(dt,dn,{mntc_date:get_today()});
+	hide_contact_info(doc);
 }
 
 var hide_contact_info = function(doc) {
@@ -30,79 +52,68 @@
 	hide_contact_info(doc);
 }
 
-//customer
-cur_frm.cscript.customer = function(doc,dt,dn) {
-  var callback = function(r,rt) {
-      var doc = locals[cur_frm.doctype][cur_frm.docname];
-      cur_frm.refresh();
-  }   
-
-  if(doc.customer) $c_obj(make_doclist(doc.doctype, doc.name), 'get_default_customer_address', '', callback);
-  hide_contact_info(doc);
-}
-
-cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) {    
-  if(doc.customer) get_server_fields('get_customer_address', JSON.stringify({customer: doc.customer, address: doc.customer_address, contact: doc.contact_person}),'', doc, dt, dn, 1);
+cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) {		
+	if(doc.customer) get_server_fields('get_customer_address', JSON.stringify({customer: doc.customer, address: doc.customer_address, contact: doc.contact_person}),'', doc, dt, dn, 1);
 }
 
 cur_frm.fields_dict['customer_address'].get_query = function(doc, cdt, cdn) {
-  return 'SELECT name,address_line1,city FROM tabAddress WHERE customer = "'+ doc.customer +'" AND docstatus != 2 AND name LIKE "%s" ORDER BY name ASC LIMIT 50';
+	return 'SELECT name,address_line1,city FROM tabAddress WHERE customer = "'+ doc.customer +'" AND docstatus != 2 AND name LIKE "%s" ORDER BY name ASC LIMIT 50';
 }
 
 cur_frm.fields_dict['contact_person'].get_query = function(doc, cdt, cdn) {
-  return 'SELECT name,CONCAT(first_name," ",ifnull(last_name,"")) As FullName,department,designation FROM tabContact WHERE customer = "'+ doc.customer +'" AND docstatus != 2 AND name LIKE "%s" ORDER BY name ASC LIMIT 50';
+	return 'SELECT name,CONCAT(first_name," ",ifnull(last_name,"")) As FullName,department,designation FROM tabContact WHERE customer = "'+ doc.customer +'" AND docstatus != 2 AND name LIKE "%s" ORDER BY name ASC LIMIT 50';
 }
 
 cur_frm.cscript.get_items = function(doc, dt, dn) {
-  var callback = function(r,rt) { 
-	  hide_contact_info(doc);
-	  cur_frm.refresh();
-  }
-  get_server_fields('fetch_items','','',doc, dt, dn,1,callback);
+	var callback = function(r,rt) { 
+		hide_contact_info(doc);
+		cur_frm.refresh();
+	}
+	get_server_fields('fetch_items','','',doc, dt, dn,1,callback);
 }
 
 cur_frm.fields_dict['maintenance_visit_details'].grid.get_field('item_code').get_query = function(doc, cdt, cdn) {
-  return 'SELECT tabItem.name,tabItem.item_name,tabItem.description FROM tabItem WHERE tabItem.is_service_item="Yes" AND tabItem.docstatus != 2 AND tabItem.%(key)s LIKE "%s" LIMIT 50';
+	return 'SELECT tabItem.name,tabItem.item_name,tabItem.description FROM tabItem WHERE tabItem.is_service_item="Yes" AND tabItem.docstatus != 2 AND tabItem.%(key)s LIKE "%s" LIMIT 50';
 }
 
 cur_frm.cscript.item_code = function(doc, cdt, cdn) {
-  var fname = cur_frm.cscript.fname;
-  var d = locals[cdt][cdn];
-  if (d.item_code) {
-    get_server_fields('get_item_details',d.item_code, 'maintenance_visit_details',doc,cdt,cdn,1);
-  }
+	var fname = cur_frm.cscript.fname;
+	var d = locals[cdt][cdn];
+	if (d.item_code) {
+		get_server_fields('get_item_details',d.item_code, 'maintenance_visit_details',doc,cdt,cdn,1);
+	}
 }
 
 cur_frm.fields_dict['sales_order_no'].get_query = function(doc) {
-  doc = locals[this.doctype][this.docname];
-  var cond = '';
-  if(doc.customer) {
-    cond = '`tabSales Order`.customer = "'+doc.customer+'" AND';
-  }
-  return repl('SELECT DISTINCT `tabSales Order`.name FROM `tabSales Order`, `tabSales Order Item`, `tabItem` WHERE `tabSales Order`.company = "%(company)s" AND `tabSales Order`.docstatus = 1 AND `tabSales Order Item`.parent = `tabSales Order`.name AND `tabSales Order Item`.item_code = `tabItem`.name AND `tabItem`.is_service_item = "Yes" AND %(cond)s `tabSales Order`.name LIKE "%s" ORDER BY `tabSales Order`.name DESC LIMIT 50', {company:doc.company, cond:cond});
+	doc = locals[this.doctype][this.docname];
+	var cond = '';
+	if(doc.customer) {
+		cond = '`tabSales Order`.customer = "'+doc.customer+'" AND';
+	}
+	return repl('SELECT DISTINCT `tabSales Order`.name FROM `tabSales Order`, `tabSales Order Item`, `tabItem` WHERE `tabSales Order`.company = "%(company)s" AND `tabSales Order`.docstatus = 1 AND `tabSales Order Item`.parent = `tabSales Order`.name AND `tabSales Order Item`.item_code = `tabItem`.name AND `tabItem`.is_service_item = "Yes" AND %(cond)s `tabSales Order`.name LIKE "%s" ORDER BY `tabSales Order`.name DESC LIMIT 50', {company:doc.company, cond:cond});
 }
 
 cur_frm.fields_dict['customer_issue_no'].get_query = function(doc) {
-  doc = locals[this.doctype][this.docname];
-  var cond = '';
-  if(doc.customer) {
-    cond = '`tabCustomer Issue`.customer = "'+doc.customer+'" AND';
-  }
-  return repl('SELECT `tabCustomer Issue`.name FROM `tabCustomer Issue` WHERE `tabCustomer Issue`.company = "%(company)s" AND %(cond)s `tabCustomer Issue`.docstatus = 1 AND (`tabCustomer Issue`.status = "Open" OR `tabCustomer Issue`.status = "Work In Progress") AND `tabCustomer Issue`.name LIKE "%s" ORDER BY `tabCustomer Issue`.name DESC LIMIT 50', {company:doc.company, cond:cond});
+	doc = locals[this.doctype][this.docname];
+	var cond = '';
+	if(doc.customer) {
+		cond = '`tabCustomer Issue`.customer = "'+doc.customer+'" AND';
+	}
+	return repl('SELECT `tabCustomer Issue`.name FROM `tabCustomer Issue` WHERE `tabCustomer Issue`.company = "%(company)s" AND %(cond)s `tabCustomer Issue`.docstatus = 1 AND (`tabCustomer Issue`.status = "Open" OR `tabCustomer Issue`.status = "Work In Progress") AND `tabCustomer Issue`.name LIKE "%s" ORDER BY `tabCustomer Issue`.name DESC LIMIT 50', {company:doc.company, cond:cond});
 }
 
 cur_frm.fields_dict['maintenance_schedule'].get_query = function(doc) {
-  doc = locals[this.doctype][this.docname];
-  var cond = '';
-  if(doc.customer) {
-    cond = '`tabMaintenance Schedule`.customer = "'+doc.customer+'" AND';
-  }
-  return repl('SELECT `tabMaintenance Schedule`.name FROM `tabMaintenance Schedule` WHERE `tabMaintenance Schedule`.company = "%(company)s" AND %(cond)s `tabMaintenance Schedule`.docstatus = 1 AND `tabMaintenance Schedule`.name LIKE "%s" ORDER BY `tabMaintenance Schedule`.name DESC LIMIT 50', {company:doc.company, cond:cond});
+	doc = locals[this.doctype][this.docname];
+	var cond = '';
+	if(doc.customer) {
+		cond = '`tabMaintenance Schedule`.customer = "'+doc.customer+'" AND';
+	}
+	return repl('SELECT `tabMaintenance Schedule`.name FROM `tabMaintenance Schedule` WHERE `tabMaintenance Schedule`.company = "%(company)s" AND %(cond)s `tabMaintenance Schedule`.docstatus = 1 AND `tabMaintenance Schedule`.name LIKE "%s" ORDER BY `tabMaintenance Schedule`.name DESC LIMIT 50', {company:doc.company, cond:cond});
 }
 
 //get query select Territory
 cur_frm.fields_dict['territory'].get_query = function(doc,cdt,cdn) {
-  return 'SELECT `tabTerritory`.`name`,`tabTerritory`.`parent_territory` FROM `tabTerritory` WHERE `tabTerritory`.`is_group` = "No" AND `tabTerritory`.`docstatus`!= 2 AND `tabTerritory`.%(key)s LIKE "%s"  ORDER BY  `tabTerritory`.`name` ASC LIMIT 50';
+	return 'SELECT `tabTerritory`.`name`,`tabTerritory`.`parent_territory` FROM `tabTerritory` WHERE `tabTerritory`.`is_group` = "No" AND `tabTerritory`.`docstatus`!= 2 AND `tabTerritory`.%(key)s LIKE "%s"	ORDER BY	`tabTerritory`.`name` ASC LIMIT 50';
 }
 
 cur_frm.fields_dict.customer.get_query = erpnext.utils.customer_query;
\ No newline at end of file
diff --git a/support/doctype/support_ticket/support_ticket.js b/support/doctype/support_ticket/support_ticket.js
index beff192..903f41c 100644
--- a/support/doctype/support_ticket/support_ticket.js
+++ b/support/doctype/support_ticket/support_ticket.js
@@ -16,6 +16,25 @@
 
 cur_frm.fields_dict.customer.get_query = erpnext.utils.customer_query;
 
+wn.provide("erpnext.support");
+// TODO commonify this code
+erpnext.support.CustomerIssue = wn.ui.form.Controller.extend({
+	customer: function() {
+		var me = this;
+		if(this.frm.doc.customer) {
+			this.frm.call({
+				doc: this.frm.doc,
+				method: "set_customer_defaults",
+				callback: function(r) {
+					if(!r.exc) me.frm.refresh_fields();
+				}
+			});			
+		}
+	}
+});
+
+$.extend(cur_frm.cscript, new erpnext.support.CustomerIssue({frm: cur_frm}));
+
 $.extend(cur_frm.cscript, {
 	onload: function(doc, dt, dn) {
 		if(in_list(user_roles,'System Manager')) {
@@ -67,17 +86,6 @@
 
 	},
 		
-	customer: function(doc, dt, dn) {
-		var callback = function(r,rt) {
-			var doc = locals[cur_frm.doctype][cur_frm.docname];
-			if(!r.exc) {
-				cur_frm.refresh();
-			}
-		}
-		if(doc.customer) $c_obj(make_doclist(doc.doctype, doc.name), 
-			'get_default_customer_address', '', callback);
-	}, 
-	
 	'Close Ticket': function() {
 		cur_frm.cscript.set_status("Closed");
 	},
diff --git a/utilities/transaction_base.py b/utilities/transaction_base.py
index ab53575..7794444 100644
--- a/utilities/transaction_base.py
+++ b/utilities/transaction_base.py
@@ -23,46 +23,89 @@
 from controllers.status_updater import StatusUpdater
 
 class TransactionBase(StatusUpdater):
-	def get_default_address_and_contact(self, party_type):
+	def get_default_address_and_contact(self, party_field, party_name=None):
 		"""get a dict of default field values of address and contact for a given party type
 			party_type can be one of: customer, supplier"""
-		ret = {}
+		if not party_name:
+			party_name = self.doc.fields.get(party_field)
 		
-		# {customer: self.doc.fields.get("customer")}
-		args = {party_type: self.doc.fields.get(party_type)}
+		return get_default_address_and_contact(party_field, party_name,
+			fetch_shipping_address=True if self.meta.get_field("shipping_address_name") else False)
+			
+	def get_customer_defaults(self):
+		out = self.get_default_address_and_contact("customer")
+
+		customer = webnotes.doc("Customer", self.doc.customer)
+		for f in ['customer_name', 'customer_group', 'territory']:
+			out[f] = customer.fields.get(f)
 		
-		address_text, address_name = self.get_address_text(**args)
-		ret.update({
-			# customer_address
-			(party_type + "_address"): address_name,
-			"address_display": address_text
-		})
-		ret.update(self.get_contact_text(**args))
-		return ret
+		# fields prepended with default in Customer doctype
+		for f in ['sales_partner', 'commission_rate', 'currency', 'price_list']:
+			out[f] = customer.fields.get("default_" + f)
+			
+		return out
+				
+	def set_customer_defaults(self):
+		"""
+			For a customer:
+			1. Sets default address and contact
+			2. Sets values like Territory, Customer Group, etc.
+			3. Clears existing Sales Team and fetches the one mentioned in Customer
+		"""
+		customer_defaults = self.get_customer_defaults()
+		
+		# hack! TODO - add shipping_address_field in Delivery Note
+		if self.doc.doctype == "Delivery Note":
+			customer_defaults["customer_address"] = customer_defaults["shipping_address_name"]
+			customer_defaults["address_display"] = customer_defaults["shipping_address"]
+			
+		customer_defaults["price_list"] = customer_defaults["price_list"] or \
+			webnotes.conn.get_value("Customer Group", self.doc.customer_group, "default_price_list") or \
+			self.doc.price_list
+			
+		self.doc.fields.update(customer_defaults)
+		
+		if self.meta.get_field("sales_team"):
+			self.set_sales_team_for_customer()
+			
+	def set_sales_team_for_customer(self):
+		from webnotes.model import default_fields
+		
+		# clear table
+		self.doclist = self.doc.clear_table(self.doclist, "sales_team")
+
+		sales_team = webnotes.conn.sql("""select * from `tabSales Team`
+			where parenttype="Customer" and parent=%s""", self.doc.customer, as_dict=True)
+		for i, sales_person in enumerate(sales_team):
+			# remove default fields
+			for fieldname in default_fields:
+				if fieldname in sales_person:
+					del sales_person[fieldname]
+			
+			sales_person.update({
+				"doctype": "Sales Team",
+				"parentfield": "sales_team",
+				"idx": i+1
+			})
+			
+			# add child
+			self.doclist.append(sales_person)
 	
-	# Get Customer Default Primary Address - first load
-	def get_default_customer_address(self, args=''):
-		address_text, address_name = self.get_address_text(customer=self.doc.customer)
-		self.doc.customer_address = address_name or ''
-		self.doc.address_display = address_text or ''
-		self.doc.fields.update(self.get_contact_text(customer=self.doc.customer))
-
-		if args != 'onload':
-			self.get_customer_details(self.doc.customer)
-			self.get_sales_person(self.doc.customer)
+	def get_lead_defaults(self):
+		out = self.get_default_address_and_contact("lead")
 		
-	# Get Customer Default Shipping Address - first load
-	# -----------------------
-	def get_default_customer_shipping_address(self, args=''):		
-		address_text, address_name = self.get_address_text(customer=self.doc.customer,is_shipping_address=1)
-		self.doc.customer_address = address_name or ''
-		self.doc.address_display = address_text or ''
-		self.doc.fields.update(self.get_contact_text(customer=self.doc.customer))
-		
-		if self.doc.doctype != 'Quotation' and args != 'onload':
-			self.get_customer_details(self.doc.customer)
-			self.get_sales_person(self.doc.customer)			
+		lead = webnotes.conn.get_value("Lead", self.doc.lead, 
+			["territory", "company_name", "lead_name"], as_dict=True) or {}
 
+		out["territory"] = lead.get("territory")
+		out["customer_name"] = lead.get("company_name") or lead.get("lead_name")
+
+		return out
+		
+	def set_lead_defaults(self):
+		self.doc.fields.update(self.get_lead_defaults())
+			
+	
 	# Get Customer Address
 	# -----------------------
 	def get_customer_address(self, args):
@@ -91,13 +134,14 @@
 			details = webnotes.conn.sql("select name, address_line1, address_line2, city, country, pincode, state, phone, fax from `tabAddress` where %s and docstatus != 2 order by is_shipping_address desc, is_primary_address desc limit 1" % cond, as_dict = 1)
 		else:
 			details = webnotes.conn.sql("select name, address_line1, address_line2, city, country, pincode, state, phone, fax from `tabAddress` where %s and docstatus != 2 order by is_primary_address desc limit 1" % cond, as_dict = 1)
-			
-		extract = lambda x: details and details[0] and details[0].get(x,'') or ''
-		address_fields = [('','address_line1'),('\n','address_line2'),('\n','city'),('\n','state'),(' ','pincode'),('\n','country'),('\nPhone: ','phone'),('\nFax: ', 'fax')]
-		address_display = ''.join([a[0]+extract(a[1]) for a in address_fields if extract(a[1])])
-		if address_display.startswith('\n'): address_display = address_display[1:]		
+		
+		address_display = ""
+		
+		if details:
+			address_display = get_address_display(details[0])			
 
 		address_name = details and details[0]['name'] or ''
+		
 		return address_display, address_name
 
 	# Get Contact Text
@@ -126,41 +170,13 @@
 			"contact_department": details and details[0]["department"] or "",
 		}
 		
-	def get_customer_details(self, name):
-		"""
-			Get customer details like name, group, territory
-			and other such defaults
-		"""
-		customer_details = webnotes.conn.sql("""\
-			select
-				customer_name, customer_group, territory,
-				default_sales_partner, default_commission_rate, default_currency,
-				default_price_list
-			from `tabCustomer`
-			where name = %s and docstatus < 2""", name, as_dict=1)
-		if customer_details:
-			for f in ['customer_name', 'customer_group', 'territory']:
-				self.doc.fields[f] = customer_details[0][f] or self.doc.fields.get(f)
-			
-			# fields prepended with default in Customer doctype
-			for f in ['sales_partner', 'commission_rate', 'currency']:
-				self.doc.fields[f] = customer_details[0]["default_%s" % f] or self.doc.fields.get(f)
-			
-			# optionally fetch default price list from Customer Group
-			self.doc.price_list_name = (customer_details[0]['default_price_list']
-				or webnotes.conn.get_value('Customer Group', self.doc.customer_group,
-					'default_price_list')
-				or self.doc.fields.get('price_list_name'))
-
-	# Get Customer Shipping Address
-	# -----------------------
+	# TODO deprecate this - used only in sales_order.js
 	def get_shipping_address(self, name):
 		details = webnotes.conn.sql("select name, address_line1, address_line2, city, country, pincode, state, phone from `tabAddress` where customer = '%s' and docstatus != 2 order by is_shipping_address desc, is_primary_address desc limit 1" %(name), as_dict = 1)
 		
-		extract = lambda x: details and details[0] and details[0].get(x,'') or ''
-		address_fields = [('','address_line1'),('\n','address_line2'),('\n','city'),(' ','pincode'),('\n','state'),('\n','country'),('\nPhone: ','phone')]
-		address_display = ''.join([a[0]+extract(a[1]) for a in address_fields if extract(a[1])])
-		if address_display.startswith('\n'): address_display = address_display[1:]
+		address_display = ""
+		if details:
+			address_display = get_address_display(details[0])
 		
 		ret = {
 			'shipping_address_name' : details and details[0]['name'] or '',
@@ -168,36 +184,6 @@
 		}
 		return ret
 		
-	# Get Lead Details
-	# -----------------------
-	def get_lead_details(self, name):
-		details = webnotes.conn.sql("""select name, lead_name, address_line1, address_line2, city, country, state, pincode
-			from `tabAddress` where lead=%s""", name, as_dict=True)
-		lead = webnotes.conn.get_value("Lead", name, 
-			["territory", "phone", "mobile_no", "email_id", "company_name", "lead_name"], as_dict=True) or {}
-
-		address_display = ""
-		if details:
-			details = details[0]
-			for separator, fieldname in (('','address_line1'), ('\n','address_line2'), ('\n','city'), 
-				(' ','pincode'), ('\n','state'), ('\n','country'), ('\nPhone: ', 'phone')):
-					if details.get(fieldname):
-						address_display += separator + details.get(fieldname)
-
-		if address_display.startswith('\n'):
-			address_display = address_display[1:]
-		
-		ret = {
-			'contact_display' : lead.get('lead_name'),
-			'address_display' : address_display,
-			'territory' : lead.get('territory'),
-			'contact_mobile' : lead.get('mobile_no'),
-			'contact_email' : lead.get('email_id'),
-			'customer_name' : lead.get('company_name') or lead.get('lead_name')
-		}
-		return ret
-		
-		
 	# Get Supplier Default Primary Address - first load
 	# -----------------------
 	def get_default_supplier_address(self, args):
@@ -312,20 +298,112 @@
 				})
 			
 			webnotes.bean(event_doclist).insert()
-			
+
+def get_default_address_and_contact(party_field, party_name, fetch_shipping_address=False):
+	out = {}
+	
+	# get addresses
+	billing_address = get_address_dict(party_field, party_name)
+	if billing_address:
+		out[party_field + "_address"] = billing_address["name"]
+		out["address_display"] = get_address_display(billing_address)
+	else:
+		out[party_field + "_address"] = out["address_display"] = None
+	
+	if fetch_shipping_address:
+		shipping_address = get_address_dict(party_field, party_name, is_shipping_address=True)
+		if shipping_address:
+			out["shipping_address_name"] = shipping_address["name"]
+			out["shipping_address"] = get_address_display(shipping_address)
+		else:
+			out["shipping_address_name"] = out["shipping_address"] = None
+	
+	# get contact
+	if party_field == "lead":
+		out["customer_address"] = out.get("lead_address")
+		out.update(map_lead_fields(party_name))
+	else:
+		out.update(map_contact_fields(party_field, party_name))
+	
+	return out
+
+def get_address_dict(party_field, party_name, is_shipping_address=None):
+	order_by = "is_shipping_address desc, is_primary_address desc, name asc" if \
+		is_shipping_address else "is_primary_address desc, name asc"
+
+	address = webnotes.conn.sql("""select * from `tabAddress` where `%s`=%s order by %s
+		limit 1""" % (party_field, "%s", order_by), party_name, as_dict=True,
+		update={"doctype": "Address"})
+	
+	return address[0] if address else None
 			
 def get_address_display(address_dict):
+	def _prepare_for_display(a_dict, sequence):
+		display = ""
+		for separator, fieldname in sequence:
+			if a_dict.get(fieldname):
+				display += separator + a_dict.get(fieldname)
+			
+		return display.strip()
+	
 	meta = webnotes.get_doctype("Address")
 	address_sequence = (("", "address_line1"), ("\n", "address_line2"), ("\n", "city"),
 		("\n", "state"), ("\n" + meta.get_label("pincode") + ": ", "pincode"), ("\n", "country"),
 		("\n" + meta.get_label("phone") + ": ", "phone"), ("\n" + meta.get_label("fax") + ": ", "fax"))
 	
-	address_display = ""
-	for separator, fieldname in address_sequence:
-		if address_dict.get(fieldname):
-			address_display += separator + address_dict.get(fieldname)
-			
-	return address_display
+	return _prepare_for_display(address_dict, address_sequence)
+	
+def map_lead_fields(party_name):
+	out = {}
+	for fieldname in ["contact_display", "contact_email", "contact_mobile", "contact_phone"]:
+		out[fieldname] = None
+	
+	lead = webnotes.conn.sql("""select * from `tabLead` where name=%s""", party_name, as_dict=True)
+	if lead:
+		lead = lead[0]
+		out.update({
+			"contact_display": lead.get("lead_name"),
+			"contact_email": lead.get("email_id"),
+			"contact_mobile": lead.get("mobile_no"),
+			"contact_phone": lead.get("phone"),
+		})
+
+	return out
+
+def map_contact_fields(party_field, party_name):
+	out = {}
+	for fieldname in ["contact_person", "contact_display", "contact_email",
+		"contact_mobile", "contact_phone", "contact_designation", "contact_department"]:
+			out[fieldname] = None
+	
+	contact = webnotes.conn.sql("""select * from `tabContact` where `%s`=%s
+		order by is_primary_contact desc, name asc limit 1""" % (party_field, "%s"), 
+		(party_name,), as_dict=True)
+	if contact:
+		contact = contact[0]
+		out.update({
+			"contact_person": contact.get("name"),
+			"contact_display": " ".join(filter(None, 
+				[contact.get("first_name"), contact.get("last_name")])),
+			"contact_email": contact.get("email_id"),
+			"contact_mobile": contact.get("mobile_no"),
+			"contact_phone": contact.get("phone"),
+			"contact_designation": contact.get("designation"),
+			"contact_department": contact.get("department")
+		})
+	
+	return out
+	
+def get_address_territory(address_doc):
+	territory = None
+	for fieldname in ("city", "state", "country"):
+		value = address_doc.fields.get(fieldname)
+		if value:
+			territory = webnotes.conn.get_value("Territory", value.strip())
+			if territory:
+				break
+	
+	return territory
 	
 def validate_conversion_rate(currency, conversion_rate, conversion_rate_label, company):
 	"""common validation for currency and price list currency"""
diff --git a/website/doctype/shopping_cart_settings/shopping_cart_settings.py b/website/doctype/shopping_cart_settings/shopping_cart_settings.py
index 516c968..1424823 100644
--- a/website/doctype/shopping_cart_settings/shopping_cart_settings.py
+++ b/website/doctype/shopping_cart_settings/shopping_cart_settings.py
@@ -9,42 +9,67 @@
 class ShoppingCartSetupError(webnotes.ValidationError): pass
 
 class DocType(DocListController):
-	def __init__(self, d, dl):
-		self.doc, self.doclist = d, dl
-		
 	def validate(self):
 		if self.doc.enabled:
-			self.validate_overlapping_territories("shopping_cart_price_lists", "price_list")
-			self.validate_overlapping_territories("shopping_cart_taxes_and_charges_masters", 
-				"sales_taxes_and_charges_master")
-			self.validate_shipping_rules()
+			self.validate_price_lists()
+			self.validate_tax_masters()
 			self.validate_exchange_rates_exist()
-	
-	def validate_overlapping_territories(self, parentfield, fieldname):
-		names = [doc.fields(fieldname) for doc in self.doclist.get({"parentfield": parentfield})]
-		doctype = self.meta.get_field(parentfield).options
-		parenttype = self.meta.get_field(fieldname, parentfield=parentfield).options
-		
-		if not names:
-			msgprint(_("Please specify at least one") + " " + _(doctype),
-				raise_exception=ShoppingCartSetupError)
-		
-		territory_name = webnotes.conn.sql("""select territory, parent from `tabFor Territory`
-			where parenttype=%s and parent in (%s)""" % ("%s", ", ".join(["%s"]*names)), 
-			tuple([parenttype] + names))
-		
-		territory_name_map = {}
-		for territory, name in territory_name:
-			territory_name_map.setdefault(territory, []).append(name)
 			
+	def validate_overlapping_territories(self, parentfield, fieldname):
+		# for displaying message
+		doctype = self.meta.get_field(parentfield).options
+		
+		# specify atleast one entry in the table
+		self.validate_table_has_rows(parentfield, raise_exception=ShoppingCartSetupError)
+		
+		territory_name_map = self.get_territory_name_map(parentfield, fieldname)
 		for territory, names in territory_name_map.items():
 			if len(names) > 1:
 				msgprint(_("Error for") + " " + _(doctype) + ": " + comma_and(names) +
 					" " + _("have a common territory") + ": " + territory,
 					raise_exception=ShoppingCartSetupError)
 					
-	def validate_shipping_rules(self):
-		pass
+		return territory_name_map
+		
+	def validate_price_lists(self):
+		territory_name_map = self.validate_overlapping_territories("price_lists",
+			"price_list")
+		
+		# validate that a Shopping Cart Price List exists for the root territory
+		# as a catch all!
+		from setup.utils import get_root_of
+		root_territory = get_root_of("Territory")
+		
+		if root_territory not in territory_name_map.keys():
+			msgprint(_("Please specify a Price List which is valid for Territory") + 
+				": " + root_territory, raise_exception=ShoppingCartSetupError)
+		
+	def validate_tax_masters(self):
+		self.validate_overlapping_territories("sales_taxes_and_charges_masters", 
+			"sales_taxes_and_charges_master")
+		
+	def get_territory_name_map(self, parentfield, fieldname):
+		territory_name_map = {}
+		
+		# entries in table
+		names = [doc.fields.get(fieldname) for doc in self.doclist.get({"parentfield": parentfield})]
+		
+		if names:
+			# for condition in territory check
+			parenttype = self.meta.get_field(fieldname, parentfield=parentfield).options
+		
+			# to validate territory overlap
+			# make a map of territory: [list of names]
+			# 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) """ %
+				("%s", ", ".join(["%s"]*len(names))), tuple([parenttype] + names))
+		
+			for territory, name in territory_name:
+				territory_name_map.setdefault(territory, []).append(name)
+			
+		return territory_name_map
 					
 	def validate_exchange_rates_exist(self):
 		"""check if exchange rates exist for all Price List currencies (to company's currency)"""
@@ -54,7 +79,7 @@
 				raise_exception=ShoppingCartSetupError)
 		
 		price_list_currency_map = webnotes.conn.get_values("Price List", 
-			[d.price_list for d in self.doclist.get({"parentfield": "shopping_cart_price_lists"})],
+			[d.price_list for d in self.doclist.get({"parentfield": "price_lists"})],
 			"currency")
 		
 		expected_to_exist = [currency + "-" + company_currency 
@@ -70,5 +95,39 @@
 		if missing:
 			msgprint(_("Missing Currency Exchange Rates for" + ": " + comma_and(missing)),
 				raise_exception=ShoppingCartSetupError)
+				
+	def get_name_from_territory(self, territory, parentfield, fieldname):
+		name = None
+		territory_name_map = self.get_territory_name_map(parentfield, fieldname)
 		
-		
\ No newline at end of file
+		if territory_name_map.get(territory):
+			name = territory_name_map.get(territory)[0]
+		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]
+					break
+		
+		return name
+				
+	def get_price_list(self, billing_territory):
+		return self.get_name_from_territory(billing_territory, "price_lists", "price_list")
+		
+	def get_tax_master(self, billing_territory):
+		return self.get_name_from_territory(billing_territory, "sales_taxes_and_charges_masters", 
+			"sales_taxes_and_charges_master")
+		
+	def get_shipping_rule(self, shipping_territory):
+		return self.get_name_from_territory(shipping_territory, "shipping_rules", "shipping_rule")
+		
+	def get_territory_ancestry(self, territory):
+		from setup.utils import get_ancestors_of
+		
+		if not hasattr(self, "_territory_ancestry"):
+			self._territory_ancestry = {}
+			
+		if not self._territory_ancestry.get(territory):
+			self._territory_ancestry[territory] = get_ancestors_of("Territory", territory)
+
+		return self._territory_ancestry[territory]
\ No newline at end of file
diff --git a/website/doctype/shopping_cart_settings/shopping_cart_settings.txt b/website/doctype/shopping_cart_settings/shopping_cart_settings.txt
index 82e3bae..fa13f18 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-06-21 12:59:30", 
+  "modified": "2013-07-03 21:00:00", 
   "modified_by": "Administrator", 
   "owner": "Administrator"
  }, 
@@ -53,7 +53,7 @@
  }, 
  {
   "doctype": "DocField", 
-  "fieldname": "shopping_cart_price_lists", 
+  "fieldname": "price_lists", 
   "fieldtype": "Table", 
   "label": "Shopping Cart Price Lists", 
   "options": "Shopping Cart Price List", 
@@ -61,7 +61,7 @@
  }, 
  {
   "doctype": "DocField", 
-  "fieldname": "shopping_cart_taxes_and_charges_masters", 
+  "fieldname": "sales_taxes_and_charges_masters", 
   "fieldtype": "Table", 
   "label": "Shopping Cart Taxes and Charges Masters", 
   "options": "Shopping Cart Taxes and Charges Master", 
@@ -69,6 +69,14 @@
  }, 
  {
   "doctype": "DocField", 
+  "fieldname": "shopping_cart_shipping_rules", 
+  "fieldtype": "Table", 
+  "label": "Shopping Cart Shipping Rules", 
+  "options": "Shopping Cart Shipping Rule", 
+  "reqd": 0
+ }, 
+ {
+  "doctype": "DocField", 
   "fieldname": "company", 
   "fieldtype": "Link", 
   "label": "Company", 
diff --git a/website/doctype/shopping_cart_settings/test_shopping_cart_settings.py b/website/doctype/shopping_cart_settings/test_shopping_cart_settings.py
new file mode 100644
index 0000000..b4ae6fc
--- /dev/null
+++ b/website/doctype/shopping_cart_settings/test_shopping_cart_settings.py
@@ -0,0 +1,78 @@
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import webnotes
+import unittest
+from website.doctype.shopping_cart_settings.shopping_cart_settings import ShoppingCartSetupError
+
+class TestShoppingCartSettings(unittest.TestCase):
+	def setUp(self):
+		webnotes.conn.sql("""delete from `tabSingles` where doctype="Shipping Cart Settings" """)
+		webnotes.conn.sql("""delete from `tabShopping Cart Price List`""")
+		webnotes.conn.sql("""delete from `tabShopping Cart Taxes and Charges Master`""")
+		webnotes.conn.sql("""delete from `tabShopping Cart Shipping Rule`""")
+		
+	def get_cart_settings(self):
+		return webnotes.bean({"doctype": "Shopping Cart Settings",
+			"company": "_Test Company"})
+		
+	def test_price_list_territory_overlap(self):
+		cart_settings = self.get_cart_settings()
+		
+		def _add_price_list(price_list):
+			cart_settings.doclist.append({
+				"doctype": "Shopping Cart Price List",
+				"parentfield": "price_lists",
+				"price_list": price_list
+			})
+		
+		for price_list in ("_Test Price List Rest of the World", "_Test Price List India",
+			"_Test Price List"):
+			_add_price_list(price_list)
+		
+		controller = cart_settings.make_controller()
+		controller.validate_overlapping_territories("price_lists", "price_list")
+		
+		_add_price_list("_Test Price List 2")
+		
+		controller = cart_settings.make_controller()
+		self.assertRaises(ShoppingCartSetupError, controller.validate_overlapping_territories,
+			"price_lists", "price_list")
+			
+		return cart_settings
+		
+	def test_taxes_territory_overlap(self):
+		cart_settings = self.get_cart_settings()
+		
+		def _add_tax_master(tax_master):
+			cart_settings.doclist.append({
+				"doctype": "Shopping Cart Taxes and Charges Master",
+				"parentfield": "sales_taxes_and_charges_masters",
+				"sales_taxes_and_charges_master": tax_master
+			})
+		
+		for tax_master in ("_Test Sales Taxes and Charges Master", "_Test India Tax Master"):
+			_add_tax_master(tax_master)
+			
+		controller = cart_settings.make_controller()
+		controller.validate_overlapping_territories("sales_taxes_and_charges_masters",
+			"sales_taxes_and_charges_master")
+			
+		_add_tax_master("_Test Sales Taxes and Charges Master 2")
+		
+		controller = cart_settings.make_controller()
+		self.assertRaises(ShoppingCartSetupError, controller.validate_overlapping_territories,
+			"sales_taxes_and_charges_masters", "sales_taxes_and_charges_master")
+		
+	def test_exchange_rate_exists(self):
+		webnotes.conn.sql("""delete from `tabCurrency Exchange`""")
+		
+		cart_settings = self.test_price_list_territory_overlap()
+		controller = cart_settings.make_controller()
+		self.assertRaises(ShoppingCartSetupError, controller.validate_exchange_rates_exist)
+		
+		from setup.doctype.currency_exchange.test_currency_exchange import test_records as \
+			currency_exchange_records
+		webnotes.bean(currency_exchange_records[0]).insert()
+		controller.validate_exchange_rates_exist()
+		
diff --git a/website/doctype/shopping_cart_shipping_rule/__init__.py b/website/doctype/shopping_cart_shipping_rule/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/website/doctype/shopping_cart_shipping_rule/__init__.py
diff --git a/website/doctype/shopping_cart_shipping_rule/shopping_cart_shipping_rule.py b/website/doctype/shopping_cart_shipping_rule/shopping_cart_shipping_rule.py
new file mode 100644
index 0000000..928aa9f
--- /dev/null
+++ b/website/doctype/shopping_cart_shipping_rule/shopping_cart_shipping_rule.py
@@ -0,0 +1,8 @@
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import webnotes
+
+class DocType:
+	def __init__(self, d, dl):
+		self.doc, self.doclist = d, dl
\ No newline at end of file
diff --git a/website/doctype/shopping_cart_shipping_rule/shopping_cart_shipping_rule.txt b/website/doctype/shopping_cart_shipping_rule/shopping_cart_shipping_rule.txt
new file mode 100644
index 0000000..caeba3d
--- /dev/null
+++ b/website/doctype/shopping_cart_shipping_rule/shopping_cart_shipping_rule.txt
@@ -0,0 +1,35 @@
+[
+ {
+  "creation": "2013-07-03 13:15:34", 
+  "docstatus": 0, 
+  "modified": "2013-07-03 13:19:02", 
+  "modified_by": "Administrator", 
+  "owner": "Administrator"
+ }, 
+ {
+  "doctype": "DocType", 
+  "istable": 1, 
+  "module": "Website", 
+  "name": "__common__"
+ }, 
+ {
+  "doctype": "DocField", 
+  "fieldname": "shipping_rule", 
+  "fieldtype": "Link", 
+  "label": "Shipping Rule", 
+  "name": "__common__", 
+  "options": "Shipping Rule", 
+  "parent": "Shopping Cart Shipping Rule", 
+  "parentfield": "fields", 
+  "parenttype": "DocType", 
+  "permlevel": 0, 
+  "reqd": 1
+ }, 
+ {
+  "doctype": "DocType", 
+  "name": "Shopping Cart Shipping Rule"
+ }, 
+ {
+  "doctype": "DocField"
+ }
+]
\ No newline at end of file
diff --git a/website/helpers/cart.py b/website/helpers/cart.py
index bc99e7b..0338709 100644
--- a/website/helpers/cart.py
+++ b/website/helpers/cart.py
@@ -69,6 +69,8 @@
 	
 	quotation.ignore_permissions = True
 	quotation.save()
+	
+	apply_cart_settings(quotation=quotation)
 		
 	return get_cart_quotation(quotation.doclist)
 
@@ -131,14 +133,23 @@
 			"doctype": "Lead",
 			"email_id": webnotes.session.user,
 			"lead_name": get_fullname(webnotes.session.user),
-			"territory": webnotes.conn.get_value("Shopping Cart Settings", None, "territory") or \
-				"All Territories",
+			"territory": guess_territory(),
 			"status": "Open" # TODO: set something better???
 		})
 		lead_bean.ignore_permissions = True
 		lead_bean.insert()
 		
 		return lead_bean.doc
+		
+def guess_territory():
+	territory = None
+	geoip_country = webnotes.session.get("session_country")
+	if geoip_country:
+		territory = webnotes.conn.get_value("Territory", geoip_country)
+	
+	return territory or \
+		webnotes.conn.get_value("Shopping Cart Settings", None, "territory") or \
+		"All Territories"
 
 def decorate_quotation_doclist(doclist):
 	for d in doclist:
@@ -168,42 +179,84 @@
 			"order_type": "Shopping Cart",
 			"status": "Draft",
 			"__islocal": 1,
-			"price_list_name": get_price_list(party),
 			(party.doctype.lower()): party.name
 		})
+		qbean.run_method("onload_post_render")
+		apply_cart_settings(party, qbean)
 	
 	return qbean
 	
-def get_price_list(party):
-	if not party.default_price_list:
-		party.default_price_list = get_price_list_using_geoip()
-		party.save()
-		
-	return party.default_price_list
-
-def get_price_list_using_geoip():
-	country = webnotes.session.get("session_country")
-	price_list_name = None
-
-	if country:
-		price_list_name = webnotes.conn.sql("""select parent 
-			from `tabPrice List Country` plc
-			where country=%s and exists (select name from `tabPrice List` pl
-				where use_for_website=1 and ifnull(valid_for_all_countries, 0)=0 and 
-				pl.name = plc.parent)""", country)
+def apply_cart_settings(party=None, quotation=None):
+	if not party:
+		party = get_lead_or_customer()
+	if not quotation:
+		quotation = _get_cart_quotation(party)
 	
-	if price_list_name:
-		price_list_name = price_list_name[0][0]
-	else:
-		price_list_name = webnotes.conn.get_value("Price List", 
-			{"use_for_website": 1, "valid_for_all_countries": 1})
-			
-	if not price_list_name:
-		raise WebsitePriceListMissingError, "No website Price List specified"
+	cart_settings = webnotes.get_obj("Shopping Cart Settings")
 	
-	return price_list_name
+	billing_territory = get_address_territory(quotation.doc.customer_address) or \
+		party.territory
+	
+	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")
+	
+	quotation.save()
+	
+def set_price_list_and_rate(quotation, cart_settings, billing_territory):
+	"""set price list based on billing territory"""
+	quotation.doc.price_list_name = cart_settings.get_price_list(billing_territory)
+	
+	# reset values
+	quotation.doc.price_list_currency = quotation.doc.currency = \
+		quotation.doc.plc_conversion_rate = quotation.doc.conversion_rate = None
+	for item in quotation.doclist.get({"parentfield": "quotation_details"}):
+		item.ref_rate = item.adj_rate = item.export_rate = item.export_amount = None
+	
+	# refetch values
+	quotation.run_method("set_price_list_and_item_details")
+	
+def set_taxes(quotation, cart_settings, billing_territory):
+	"""set taxes based on billing territory"""
+	quotation.doc.charge = cart_settings.get_tax_master(billing_territory)
 
+	# clear table
+	quotation.doclist = quotation.doc.clear_table(quotation.doclist, "other_charges")
 
+	# append taxes
+	controller = quotation.make_controller()
+	controller.append_taxes_from_master("other_charges", "charge")
+	quotation.set_doclist(controller.doclist)
+	
+def apply_shipping_rule(quotation, cart_settings, shipping_territory):
+	quotation.doc.shipping_rule = cart_settings.get_shipping_rule(shipping_territory)
+	quotation.run_method("apply_shipping_rule")
+	
+def get_address_territory(address_name):
+	"""Tries to match city, state and country of address to existing territory"""
+	territory = None
+
+	if address_name:
+		address_fields = webnotes.conn.get_value("Address", address_name, 
+			["city", "state", "country"])
+		for value in address_fields:
+			territory = webnotes.conn.get_value("Territory", value)
+			if territory:
+				break
+	
+	return territory
+	
+def get_cart_price_list(territory_list, territory_name_map):
+	pass
+	
 @webnotes.whitelist()
 def checkout():
 	quotation = _get_cart_quotation()