Merge pull request #4174 from saurabh6790/shopping_cart

[fix] order.html rate display
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 5697b77..d4a5fa1 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -421,7 +421,7 @@
 		if self.bill_no:
 			if cint(frappe.db.get_single_value("Accounts Settings", "check_supplier_invoice_uniqueness")):
 				pi = frappe.db.exists("Purchase Invoice", {"bill_no": self.bill_no,
-					"fiscal_year": self.fiscal_year, "name": ("!=", self.name)})
+					"fiscal_year": self.fiscal_year, "name": ("!=", self.name), "docstatus": ("<", 2)})
 				if pi:
 					frappe.throw("Supplier Invoice No exists in Purchase Invoice {0}".format(pi))
 
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 4b58b3c..2a9fa17 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -47,7 +47,7 @@
 						# from warehouse account
 
 						self.check_expense_account(detail)
-						
+
 						gl_list.append(self.get_gl_dict({
 							"account": warehouse_account[sle.warehouse]["name"],
 							"against": detail.expense_account,
@@ -56,7 +56,7 @@
 							"debit": flt(sle.stock_value_difference, 2),
 						}, warehouse_account[sle.warehouse]["account_currency"]))
 
-						# to target warehouse / expense account						
+						# to target warehouse / expense account
 						gl_list.append(self.get_gl_dict({
 							"account": detail.expense_account,
 							"against": warehouse_account[sle.warehouse]["name"],
@@ -70,7 +70,7 @@
 		if warehouse_with_no_account:
 			msgprint(_("No accounting entries for the following warehouses") + ": \n" +
 				"\n".join(warehouse_with_no_account))
-		
+
 		return process_gl_map(gl_list)
 
 	def get_voucher_details(self, default_expense_account, default_cost_center, sle_map):
@@ -223,7 +223,7 @@
 		if against_document and item_code:
 			incoming_rate = frappe.db.sql("""select abs(ifnull(stock_value_difference, 0) / actual_qty)
 				from `tabStock Ledger Entry`
-				where voucher_type = %s and voucher_no = %s 
+				where voucher_type = %s and voucher_no = %s
 					and item_code = %s and warehouse=%s limit 1""",
 				(self.doctype, against_document, item_code, warehouse))
 			incoming_rate = incoming_rate[0][0] if incoming_rate else 0.0
@@ -257,25 +257,25 @@
 			if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1 and flt(d.qty):
 				return_rate = 0
 				if cint(self.is_return) and self.return_against and self.docstatus==1:
-					return_rate = self.get_incoming_rate_for_sales_return(d.item_code, 
+					return_rate = self.get_incoming_rate_for_sales_return(d.item_code,
 						d.warehouse, self.return_against)
-				
+
 				# On cancellation or if return entry submission, make stock ledger entry for
 				# target warehouse first, to update serial no values properly
 
-				if d.warehouse and ((not cint(self.is_return) and self.docstatus==1) 
+				if d.warehouse and ((not cint(self.is_return) and self.docstatus==1)
 					or (cint(self.is_return) and self.docstatus==2)):
 						sl_entries.append(self.get_sl_entries(d, {
 							"actual_qty": -1*flt(d.qty),
 							"incoming_rate": return_rate
 						}))
-				
+
 				if d.target_warehouse:
 					target_warehouse_sle = self.get_sl_entries(d, {
 						"actual_qty": flt(d.qty),
 						"warehouse": d.target_warehouse
 					})
-					
+
 					if self.docstatus == 1:
 						if not cint(self.is_return):
 							args = frappe._dict({
@@ -294,14 +294,14 @@
 								"outgoing_rate": return_rate
 							})
 					sl_entries.append(target_warehouse_sle)
-					
-				if d.warehouse and ((not cint(self.is_return) and self.docstatus==2) 
+
+				if d.warehouse and ((not cint(self.is_return) and self.docstatus==2)
 					or (cint(self.is_return) and self.docstatus==1)):
 						sl_entries.append(self.get_sl_entries(d, {
 							"actual_qty": -1*flt(d.qty),
 							"incoming_rate": return_rate
 						}))
-				
+
 		self.make_sl_entries(sl_entries)
 
 def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,
@@ -374,7 +374,7 @@
 
 def get_warehouse_account():
 	warehouse_account = frappe._dict()
-	
+
 	for d in frappe.db.sql("""select warehouse, name, account_currency from tabAccount
 		where account_type = 'Warehouse' and ifnull(warehouse, '') != ''""", as_dict=1):
 			warehouse_account.setdefault(d.warehouse, d)
diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py
index 5480613..cb642f9 100644
--- a/erpnext/setup/utils.py
+++ b/erpnext/setup/utils.py
@@ -63,21 +63,30 @@
 
 @frappe.whitelist()
 def get_exchange_rate(from_currency, to_currency):
-	try:
-		cache = frappe.cache()
-		key = "currency_exchange_rate:{0}:{1}".format(from_currency, to_currency)
-		value = cache.get(key)
-		if not value:
-			import requests
-			response = requests.get("http://api.fixer.io/latest", params={
-				"base": from_currency,
-				"symbols": to_currency
-			})
-			# expire in 24 hours
-			response.raise_for_status()
-			value = response.json()["rates"][to_currency]
-			cache.setex(key, value, 24 * 60 * 60)
-		return flt(value)
-	except:
-		exchange = "%s-%s" % (from_currency, to_currency)
-		return flt(frappe.db.get_value("Currency Exchange", exchange, "exchange_rate"))
+	exchange = "%s-%s" % (from_currency, to_currency)
+	value = flt(frappe.db.get_value("Currency Exchange", exchange, "exchange_rate"))
+
+	if not value:
+		try:
+			cache = frappe.cache()
+			key = "currency_exchange_rate:{0}:{1}".format(from_currency, to_currency)
+			value = cache.get(key)
+
+			print value
+
+			if not value:
+				import requests
+				response = requests.get("http://api.fixer.io/latest", params={
+					"base": from_currency,
+					"symbols": to_currency
+				})
+				# expire in 24 hours
+				response.raise_for_status()
+				value = response.json()["rates"][to_currency]
+				cache.setex(key, value, 24 * 60 * 60)
+			return flt(value)
+		except:
+			frappe.msgprint(_("Unable to find exchange rate"))
+			return 0.0
+	else:
+		return value
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index af52c7a..760d63b 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -181,99 +181,99 @@
 		serial_no = frappe.get_doc("Serial No", serial_no)
 		for field, value in field_values.items():
 			self.assertEquals(cstr(serial_no.get(field)), value)
-			
+
 	def test_sales_return_for_non_bundled_items(self):
 		set_perpetual_inventory()
-		
+
 		make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, basic_rate=100)
-		
+
 		actual_qty_0 = get_qty_after_transaction()
-		
+
 		dn = create_delivery_note(qty=5, rate=500)
 
 		actual_qty_1 = get_qty_after_transaction()
 		self.assertEquals(actual_qty_0 - 5, actual_qty_1)
-		
+
 		# outgoing_rate
-		outgoing_rate = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Delivery Note", 
+		outgoing_rate = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Delivery Note",
 			"voucher_no": dn.name}, "stock_value_difference") / 5
-		
+
 		# return entry
 		dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-2, rate=500)
 
 		actual_qty_2 = get_qty_after_transaction()
-			
+
 		self.assertEquals(actual_qty_1 + 2, actual_qty_2)
-		
-		incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry", 
-			{"voucher_type": "Delivery Note", "voucher_no": dn1.name}, 
+
+		incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
+			{"voucher_type": "Delivery Note", "voucher_no": dn1.name},
 			["incoming_rate", "stock_value_difference"])
-			
+
 		self.assertEquals(flt(incoming_rate, 3), abs(flt(outgoing_rate, 3)))
-		
-		gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Delivery Note", 
+
+		gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Delivery Note",
 			"voucher_no": dn1.name, "account": "_Test Warehouse - _TC"}, "debit")
-			
+
 		self.assertEquals(gle_warehouse_amount, stock_value_difference)
-		
+
 		set_perpetual_inventory(0)
-		
+
 	def test_return_single_item_from_bundled_items(self):
 		set_perpetual_inventory()
-		
+
 		create_stock_reconciliation(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, rate=100)
-		create_stock_reconciliation(item_code="_Test Item Home Desktop 100", target="_Test Warehouse - _TC", 
+		create_stock_reconciliation(item_code="_Test Item Home Desktop 100", target="_Test Warehouse - _TC",
 			qty=50, rate=100)
-				
+
 		dn = create_delivery_note(item_code="_Test Product Bundle Item", qty=5, rate=500)
 
 		# Qty after delivery
 		actual_qty_1 = get_qty_after_transaction()
 		self.assertEquals(actual_qty_1,  25)
-		
+
 		# outgoing_rate
-		outgoing_rate = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Delivery Note", 
+		outgoing_rate = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Delivery Note",
 			"voucher_no": dn.name, "item_code": "_Test Item"}, "stock_value_difference") / 25
-		
+
 		# return 'test item' from packed items
 		dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-10, rate=500)
 
 		# qty after return
 		actual_qty_2 = get_qty_after_transaction()
 		self.assertEquals(actual_qty_2, 35)
-		
+
 		# Check incoming rate for return entry
-		incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry", 
-			{"voucher_type": "Delivery Note", "voucher_no": dn1.name}, 
+		incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
+			{"voucher_type": "Delivery Note", "voucher_no": dn1.name},
 			["incoming_rate", "stock_value_difference"])
-			
+
 		self.assertEquals(flt(incoming_rate, 3), abs(flt(outgoing_rate, 3)))
-		
+
 		# Check gl entry for warehouse
-		gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Delivery Note", 
+		gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Delivery Note",
 			"voucher_no": dn1.name, "account": "_Test Warehouse - _TC"}, "debit")
-			
+
 		self.assertEquals(gle_warehouse_amount, stock_value_difference)
-		
+
 		set_perpetual_inventory(0)
-		
+
 	def test_return_entire_bundled_items(self):
 		set_perpetual_inventory()
-		
+
 		create_stock_reconciliation(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, rate=100)
-		create_stock_reconciliation(item_code="_Test Item Home Desktop 100", target="_Test Warehouse - _TC", 
+		create_stock_reconciliation(item_code="_Test Item Home Desktop 100", target="_Test Warehouse - _TC",
 			qty=50, rate=100)
-		
+
 		dn = create_delivery_note(item_code="_Test Product Bundle Item", qty=5, rate=500)
-		
+
 		#  return bundled item
-		dn1 = create_delivery_note(item_code='_Test Product Bundle Item', is_return=1, 
+		dn1 = create_delivery_note(item_code='_Test Product Bundle Item', is_return=1,
 			return_against=dn.name, qty=-2, rate=500)
 
 		# qty after return
 		actual_qty = get_qty_after_transaction()
 		self.assertEquals(actual_qty, 35)
-		
+
 		# Check incoming rate for return entry
 		incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
 			{"voucher_type": "Delivery Note", "voucher_no": dn1.name},
@@ -284,11 +284,11 @@
 		# Check gl entry for warehouse
 		gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Delivery Note",
 			"voucher_no": dn1.name, "account": "_Test Warehouse - _TC"}, "debit")
-			
+
 		self.assertEquals(gle_warehouse_amount, 1400)
-		
+
 		set_perpetual_inventory(0)
-		
+
 	def test_return_for_serialized_items(self):
 		se = make_serialized_item()
 		serial_no = get_serial_nos(se.get("items")[0].serial_no)[0]
@@ -301,65 +301,71 @@
 		})
 
 		# return entry
-		dn1 = create_delivery_note(item_code="_Test Serialized Item With Series", 
+		dn1 = create_delivery_note(item_code="_Test Serialized Item With Series",
 			is_return=1, return_against=dn.name, qty=-1, rate=500, serial_no=serial_no)
 
 		self.check_serial_no_values(serial_no, {
 			"warehouse": "_Test Warehouse - _TC",
 			"delivery_document_no": ""
 		})
-				
+
 		dn1.cancel()
-		
+
 		self.check_serial_no_values(serial_no, {
 			"warehouse": "",
 			"delivery_document_no": dn.name
 		})
-		
+
 		dn.cancel()
-		
+
 		self.check_serial_no_values(serial_no, {
 			"warehouse": "_Test Warehouse - _TC",
 			"delivery_document_no": "",
 			"purchase_document_no": se.name
 		})
-		
+
 	def test_delivery_of_bundled_items_to_target_warehouse(self):
 		set_perpetual_inventory()
-		
-		create_stock_reconciliation(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, rate=100)
-		create_stock_reconciliation(item_code="_Test Item Home Desktop 100", target="_Test Warehouse - _TC", 
-			qty=50, rate=100)
-			
+		frappe.db.set_value("Item", "_Test Item", "valuation_method", "FIFO")
+
+		for warehouse in ("_Test Warehouse - _TC", "_Test Warehouse 1 - _TC"):
+			create_stock_reconciliation(item_code="_Test Item", target=warehouse,
+				qty=50, rate=100)
+			create_stock_reconciliation(item_code="_Test Item Home Desktop 100",
+				target=warehouse, qty=50, rate=100)
+
 		opening_qty_test_warehouse_1 = get_qty_after_transaction(warehouse="_Test Warehouse 1 - _TC")
-	
-		dn = create_delivery_note(item_code="_Test Product Bundle Item", 
-			qty=5, rate=500, target_warehouse="_Test Warehouse 1 - _TC")
-	
+
+		dn = create_delivery_note(item_code="_Test Product Bundle Item",
+			qty=5, rate=500, target_warehouse="_Test Warehouse 1 - _TC", do_not_submit=True)
+
+		dn.submit()
+
 		# qty after delivery
 		actual_qty = get_qty_after_transaction(warehouse="_Test Warehouse - _TC")
 		self.assertEquals(actual_qty, 25)
-		
+
 		actual_qty = get_qty_after_transaction(warehouse="_Test Warehouse 1 - _TC")
 		self.assertEquals(actual_qty, opening_qty_test_warehouse_1 + 25)
-	
+
 		# stock value diff for source warehouse
-		stock_value_difference = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Delivery Note", 
-			"voucher_no": dn.name, "item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"}, 
+		stock_value_difference = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Delivery Note",
+			"voucher_no": dn.name, "item_code": "_Test Item Home Desktop 100", "warehouse": "_Test Warehouse - _TC"},
 			"stock_value_difference")
-			
+
 		# stock value diff for target warehouse
-		stock_value_difference1 = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Delivery Note", 
-			"voucher_no": dn.name, "item_code": "_Test Item", "warehouse": "_Test Warehouse 1 - _TC"}, 
+		stock_value_difference1 = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Delivery Note",
+			"voucher_no": dn.name, "item_code": "_Test Item Home Desktop 100", "warehouse": "_Test Warehouse 1 - _TC"},
 			"stock_value_difference")
+
 		self.assertEquals(abs(stock_value_difference), stock_value_difference1)
 
 		# Check gl entries
 		gl_entries = get_gl_entries("Delivery Note", dn.name)
 		self.assertTrue(gl_entries)
 
-		stock_value_difference = abs(frappe.db.sql("""select sum(stock_value_difference) 
-			from `tabStock Ledger Entry` where voucher_type='Delivery Note' and voucher_no=%s 
+		stock_value_difference = abs(frappe.db.sql("""select sum(stock_value_difference)
+			from `tabStock Ledger Entry` where voucher_type='Delivery Note' and voucher_no=%s
 			and warehouse='_Test Warehouse - _TC'""", dn.name)[0][0])
 
 		expected_values = {
@@ -368,9 +374,9 @@
 		}
 		for i, gle in enumerate(gl_entries):
 			self.assertEquals([gle.debit, gle.credit], expected_values.get(gle.account))
-		
+
 		set_perpetual_inventory(0)
-	
+
 def create_delivery_note(**args):
 	dn = frappe.new_doc("Delivery Note")
 	args = frappe._dict(args)
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index fa2fa13..cfaa499 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -10,6 +10,7 @@
 from erpnext.accounts.utils import get_fiscal_year, get_stock_and_account_difference
 from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
 from erpnext.stock.stock_ledger import get_previous_sle, update_entries_after
+from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import EmptyStockReconciliationItemsError
 
 class TestStockReconciliation(unittest.TestCase):
 	def setUp(self):
@@ -107,7 +108,10 @@
 		"valuation_rate": args.rate
 	})
 
-	sr.submit()
+	try:
+		sr.submit()
+	except EmptyStockReconciliationItemsError:
+		pass
 	return sr
 
 def repost_stock_as_per_valuation_method(valuation_method):
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index c0d5cd0..ce5a0c9 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -109,7 +109,7 @@
 	def build(self):
 		# includes current entry!
 		entries_to_fix = self.get_sle_after_datetime()
-		
+
 		for sle in entries_to_fix:
 			self.process_sle(sle)
 
@@ -231,10 +231,10 @@
 
 	def get_moving_average_values(self, sle):
 		actual_qty = flt(sle.actual_qty)
-		
+
 		if actual_qty > 0 or flt(sle.outgoing_rate) > 0:
 			rate = flt(sle.incoming_rate) if actual_qty > 0 else flt(sle.outgoing_rate)
-			
+
 			if self.qty_after_transaction < 0 and not self.valuation_rate:
 				# if negative stock, take current valuation rate as incoming rate
 				self.valuation_rate = rate
@@ -244,11 +244,11 @@
 
 			if new_stock_qty:
 				self.valuation_rate = new_stock_value / flt(new_stock_qty)
-							
+
 		elif not self.valuation_rate and self.qty_after_transaction <= 0:
 			self.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse, self.allow_zero_rate)
 
-		return abs(flt(self.valuation_rate))
+		self.valuation_rate = abs(flt(self.valuation_rate))
 
 	def get_fifo_values(self, sle):
 		incoming_rate = flt(sle.incoming_rate)
@@ -288,7 +288,7 @@
 						if v[1] == outgoing_rate:
 							index = i
 							break
-							
+
 					# If no entry found with outgoing rate, collapse stack
 					if index == None:
 						new_stock_value = sum((d[0]*d[1] for d in self.stock_queue)) - qty_to_pop*outgoing_rate