Merge branch 'v4.x.x'
diff --git a/erpnext/__version__.py b/erpnext/__version__.py
index 4898e44..7b6f237 100644
--- a/erpnext/__version__.py
+++ b/erpnext/__version__.py
@@ -1,2 +1,2 @@
 from __future__ import unicode_literals
-__version__ = '4.24.2'
+__version__ = '4.24.3'
diff --git a/erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.json b/erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.json
index e3a658e..42e37fd 100644
--- a/erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.json
+++ b/erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.json
@@ -6,12 +6,12 @@
  "doctype": "Report", 
  "idx": 1, 
  "is_standard": "Yes", 
- "modified": "2014-06-03 07:18:17.244501", 
+ "modified": "2015-03-26 11:00:48.720037", 
  "modified_by": "Administrator", 
  "module": "Accounts", 
  "name": "Purchase Order Items To Be Billed", 
  "owner": "Administrator", 
- "query": "select \n    `tabPurchase Order`.`name` as \"Purchase Order:Link/Purchase Order:120\",\n    `tabPurchase Order`.`transaction_date` as \"Date:Date:100\",\n\t`tabPurchase Order`.`supplier` as \"Supplier:Link/Supplier:120\",\n\t`tabPurchase Order Item`.`project_name` as \"Project\",\n\t`tabPurchase Order Item`.item_code as \"Item Code:Link/Item:120\",\n\t`tabPurchase Order Item`.base_amount as \"Amount:Currency:100\",\n\t`tabPurchase Order Item`.billed_amt as \"Billed Amount:Currency:100\", \n\t(`tabPurchase Order Item`.base_amount - ifnull(`tabPurchase Order Item`.billed_amt, 0)) as \"Amount to Bill:Currency:100\",\n\t`tabPurchase Order Item`.item_name as \"Item Name::150\",\n\t`tabPurchase Order Item`.description as \"Description::200\"\nfrom\n\t`tabPurchase Order`, `tabPurchase Order Item`\nwhere\n\t`tabPurchase Order Item`.`parent` = `tabPurchase Order`.`name`\n\tand `tabPurchase Order`.docstatus = 1\n\tand `tabPurchase Order`.status != \"Stopped\"\n\tand ifnull(`tabPurchase Order Item`.billed_amt, 0) < ifnull(`tabPurchase Order Item`.base_amount, 0)\norder by `tabPurchase Order`.transaction_date asc", 
+ "query": "select \n    `tabPurchase Order`.`name` as \"Purchase Order:Link/Purchase Order:120\",\n    `tabPurchase Order`.`transaction_date` as \"Date:Date:100\",\n\t`tabPurchase Order`.`supplier` as \"Supplier:Link/Supplier:120\",\n\t`tabPurchase Order Item`.`project_name` as \"Project\",\n\t`tabPurchase Order Item`.item_code as \"Item Code:Link/Item:120\",\n\t`tabPurchase Order Item`.base_amount as \"Amount:Currency:100\",\n\t(`tabPurchase Order Item`.billed_amt * ifnull(`tabPurchase Order`.conversion_rate, 1)) as \"Billed Amount:Currency:100\", \n\t(`tabPurchase Order Item`.base_amount - (ifnull(`tabPurchase Order Item`.billed_amt, 0) * ifnull(`tabPurchase Order`.conversion_rate, 1))) as \"Amount to Bill:Currency:100\",\n\t`tabPurchase Order Item`.item_name as \"Item Name::150\",\n\t`tabPurchase Order Item`.description as \"Description::200\"\nfrom\n\t`tabPurchase Order`, `tabPurchase Order Item`\nwhere\n\t`tabPurchase Order Item`.`parent` = `tabPurchase Order`.`name`\n\tand `tabPurchase Order`.docstatus = 1\n\tand `tabPurchase Order`.status != \"Stopped\"\n\tand (ifnull(`tabPurchase Order Item`.billed_amt, 0) * ifnull(`tabPurchase Order`.conversion_rate, 1)) < ifnull(`tabPurchase Order Item`.base_amount, 0)\norder by `tabPurchase Order`.transaction_date asc", 
  "ref_doctype": "Purchase Invoice", 
  "report_name": "Purchase Order Items To Be Billed", 
  "report_type": "Query Report"
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 7fed736..487f406 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -197,9 +197,10 @@
 		sl_dict.update(args)
 		return sl_dict
 
-	def make_sl_entries(self, sl_entries, is_amended=None, allow_negative_stock=False):
+	def make_sl_entries(self, sl_entries, is_amended=None, allow_negative_stock=False, 
+			via_landed_cost_voucher=False):
 		from erpnext.stock.stock_ledger import make_sl_entries
-		make_sl_entries(sl_entries, is_amended, allow_negative_stock)
+		make_sl_entries(sl_entries, is_amended, allow_negative_stock, via_landed_cost_voucher)
 
 	def make_gl_entries_on_cancel(self):
 		if frappe.db.sql("""select name from `tabGL Entry` where voucher_type=%s
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 00c752b..ea13670 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -5,7 +5,7 @@
 app_description = "Open Source Enterprise Resource Planning for Small and Midsized Organizations"
 app_icon = "icon-th"
 app_color = "#e74c3c"
-app_version = "4.24.2"
+app_version = "4.24.3"
 
 error_report_email = "support@erpnext.com"
 
diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py
index 1fb1e2d..242d4fa 100644
--- a/erpnext/stock/doctype/bin/bin.py
+++ b/erpnext/stock/doctype/bin/bin.py
@@ -23,7 +23,7 @@
 			if (not getattr(self, f, None)) or (not self.get(f)):
 				self.set(f, 0.0)
 
-	def update_stock(self, args, allow_negative_stock=False):
+	def update_stock(self, args, allow_negative_stock=False, via_landed_cost_voucher=False):
 		self.update_qty(args)
 
 		if args.get("actual_qty") or args.get("voucher_type") == "Stock Reconciliation":
@@ -38,7 +38,7 @@
 				"warehouse": self.warehouse,
 				"posting_date": args.get("posting_date"),
 				"posting_time": args.get("posting_time")
-			}, allow_negative_stock=allow_negative_stock)
+			}, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher)
 
 	def update_qty(self, args):
 		# update the stock values (for current quantities)
diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
index 16f0f1c..d35fed8 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
@@ -6,6 +6,7 @@
 from frappe import _
 from frappe.utils import flt
 from frappe.model.document import Document
+from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 
 class LandedCostVoucher(Document):
 	def get_items_from_purchase_receipts(self):
@@ -92,13 +93,25 @@
 			# save will update landed_cost_voucher_amount and voucher_amount in PR,
 			# as those fields are allowed to edit after submit
 			pr.save()
+			
+			# update latest valuation rate in serial no
+			self.update_rate_in_serial_no(pr)
 
 			# update stock & gl entries for cancelled state of PR
 			pr.docstatus = 2
-			pr.update_stock_ledger(allow_negative_stock=True)
+			pr.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True)
 			pr.make_gl_entries_on_cancel()
+			
 
 			# update stock & gl entries for submit state of PR
 			pr.docstatus = 1
-			pr.update_stock_ledger()
+			pr.update_stock_ledger(via_landed_cost_voucher=True)
 			pr.make_gl_entries()
+			
+	def update_rate_in_serial_no(self, purchase_receipt):
+		for item in purchase_receipt.get("purchase_receipt_details"):
+			if item.serial_no:
+				serial_nos = get_serial_nos(item.serial_no)
+				if serial_nos:
+					frappe.db.sql("update `tabSerial No` set purchase_rate=%s where name in ({0})"
+						.format(", ".join(["%s"]*len(serial_nos))), tuple([item.valuation_rate] + serial_nos))
\ No newline at end of file
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index abc0859..448581c 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -129,7 +129,7 @@
 				 if not d.prevdoc_docname:
 					 frappe.throw(_("Purchase Order number required for Item {0}").format(d.item_code))
 
-	def update_stock_ledger(self, allow_negative_stock=False):
+	def update_stock_ledger(self, allow_negative_stock=False, via_landed_cost_voucher=False):
 		sl_entries = []
 		stock_items = self.get_stock_items()
 
@@ -154,7 +154,8 @@
 					}))
 
 		self.bk_flush_supp_wh(sl_entries)
-		self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock)
+		self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock, 
+			via_landed_cost_voucher=via_landed_cost_voucher)
 
 	def update_ordered_qty(self):
 		po_map = {}
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
index b5de255..4d6e6b0 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -27,8 +27,9 @@
 		self.check_stock_frozen_date()
 		self.actual_amt_check()
 
-		from erpnext.stock.doctype.serial_no.serial_no import process_serial_no
-		process_serial_no(self)
+		if not self.get("via_landed_cost_voucher"):
+			from erpnext.stock.doctype.serial_no.serial_no import process_serial_no
+			process_serial_no(self)
 
 	#check for item quantity available in stock
 	def actual_amt_check(self):
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 97a1f82..6546945 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -14,7 +14,7 @@
 _exceptions = frappe.local('stockledger_exceptions')
 # _exceptions = []
 
-def make_sl_entries(sl_entries, is_amended=None, allow_negative_stock=False):
+def make_sl_entries(sl_entries, is_amended=None, allow_negative_stock=False, via_landed_cost_voucher=False):
 	if sl_entries:
 		from erpnext.stock.utils import update_bin
 
@@ -28,14 +28,14 @@
 				sle['actual_qty'] = -flt(sle['actual_qty'])
 
 			if sle.get("actual_qty") or sle.get("voucher_type")=="Stock Reconciliation":
-				sle_id = make_entry(sle, allow_negative_stock)
+				sle_id = make_entry(sle, allow_negative_stock, via_landed_cost_voucher)
 
 			args = sle.copy()
 			args.update({
 				"sle_id": sle_id,
 				"is_amended": is_amended
 			})
-			update_bin(args, allow_negative_stock)
+			update_bin(args, allow_negative_stock, via_landed_cost_voucher)
 
 		if cancel:
 			delete_cancelled_entry(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no'))
@@ -46,11 +46,12 @@
 		where voucher_no=%s and voucher_type=%s""",
 		(now(), frappe.session.user, voucher_type, voucher_no))
 
-def make_entry(args, allow_negative_stock=False):
+def make_entry(args, allow_negative_stock=False, via_landed_cost_voucher=False):
 	args.update({"doctype": "Stock Ledger Entry"})
 	sle = frappe.get_doc(args)
 	sle.ignore_permissions = 1
 	sle.allow_negative_stock=allow_negative_stock
+	sle.via_landed_cost_voucher = via_landed_cost_voucher
 	sle.insert()
 	sle.submit()
 	return sle.name
@@ -59,7 +60,9 @@
 	frappe.db.sql("""delete from `tabStock Ledger Entry`
 		where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
 
-def update_entries_after(args, allow_zero_rate=False, allow_negative_stock=False, verbose=1):
+def update_entries_after(args, allow_zero_rate=False, allow_negative_stock=False, 
+	via_landed_cost_voucher=False, verbose=1):
+	
 	"""
 		update valution rate and qty after transaction
 		from the current time-bucket onwards
@@ -91,7 +94,7 @@
 	stock_value_difference = 0.0
 
 	for sle in entries_to_fix:
-		if sle.serial_no or not allow_negative_stock:
+		if (sle.serial_no and not via_landed_cost_voucher) or not allow_negative_stock:
 			# validate negative stock for serialized items, fifo valuation
 			# or when negative stock is not allowed for moving average
 			if not validate_negative_stock(qty_after_transaction, sle):
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index b5638c8..37326b7 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -53,11 +53,11 @@
 	bin_obj.ignore_permissions = True
 	return bin_obj
 
-def update_bin(args, allow_negative_stock=False):
+def update_bin(args, allow_negative_stock=False, via_landed_cost_voucher=False):
 	is_stock_item = frappe.db.get_value('Item', args.get("item_code"), 'is_stock_item')
 	if is_stock_item == 'Yes':
 		bin = get_bin(args.get("item_code"), args.get("warehouse"))
-		bin.update_stock(args, allow_negative_stock)
+		bin.update_stock(args, allow_negative_stock, via_landed_cost_voucher)
 		return bin
 	else:
 		frappe.msgprint(_("Item {0} ignored since it is not a stock item").format(args.get("item_code")))
diff --git a/setup.py b/setup.py
index 5b1dab7..217cc84 100644
--- a/setup.py
+++ b/setup.py
@@ -1,7 +1,7 @@
 from setuptools import setup, find_packages
 import os
 
-version = "4.24.2"
+version = "4.24.3"
 
 with open("requirements.txt", "r") as f:
 	install_requires = f.readlines()