Merge branch 'develop'
diff --git a/erpnext/__version__.py b/erpnext/__version__.py
index 124e1ca..75c77f1 100644
--- a/erpnext/__version__.py
+++ b/erpnext/__version__.py
@@ -1 +1 @@
-__version__ = '4.19.0'
+__version__ = '4.20.0'
diff --git a/erpnext/accounts/doctype/sales_invoice/pos.js b/erpnext/accounts/doctype/sales_invoice/pos.js
index e7cbcb7..bee83fc 100644
--- a/erpnext/accounts/doctype/sales_invoice/pos.js
+++ b/erpnext/accounts/doctype/sales_invoice/pos.js
@@ -114,7 +114,7 @@
 		});
 
 		this.wrapper.find('input.discount-amount').on("change", function() {
-			frappe.model.set_value(me.frm.doctype, me.frm.docname, "discount_amount", this.value);
+			frappe.model.set_value(me.frm.doctype, me.frm.docname, "discount_amount", flt(this.value));
 		});
 
 		this.call_function("remove-items", function() {me.remove_selected_items();});
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 9ffa0ad..90ac2e7 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -4,7 +4,7 @@
 app_description = "Open Source Enterprise Resource Planning for Small and Midsized Organizations"
 app_icon = "icon-th"
 app_color = "#e74c3c"
-app_version = "4.19.0"
+app_version = "4.20.0"
 
 error_report_email = "support@erpnext.com"
 
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 2773d69..8978a3b 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -92,3 +92,4 @@
 erpnext.patches.v4_2.recalculate_bom_costs
 erpnext.patches.v4_2.discount_amount
 erpnext.patches.v4_2.update_landed_cost_voucher
+erpnext.patches.v4_2.set_item_batch
diff --git a/erpnext/patches/v4_2/set_item_batch.py b/erpnext/patches/v4_2/set_item_batch.py
new file mode 100644
index 0000000..3e84a59
--- /dev/null
+++ b/erpnext/patches/v4_2/set_item_batch.py
@@ -0,0 +1,65 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+	frappe.db.sql("update tabItem set has_batch_no = 'No' where ifnull(has_batch_no, '') = ''")
+	frappe.db.sql("update tabItem set has_serial_no = 'No' where ifnull(has_serial_no, '') = ''")
+	
+	item_list = frappe.db.sql("""select name, has_batch_no, has_serial_no from tabItem 
+		where ifnull(is_stock_item, 'No') = 'Yes'""", as_dict=1)
+		
+	sle_count = get_sle_count()
+	sle_with_batch = get_sle_with_batch()
+	sle_with_serial = get_sle_with_serial()
+	
+	batch_items = get_items_with_batch()
+	serialized_items = get_items_with_serial()
+		
+	for d in item_list:	
+		if d.has_batch_no == 'Yes':
+			if d.name not in batch_items and sle_count.get(d.name) and sle_count.get(d.name) != sle_with_batch.get(d.name):
+					frappe.db.set_value("Item", d.name, "has_batch_no", "No")
+		else:
+			if d.name in batch_items or (sle_count.get(d.name) and sle_count.get(d.name) == sle_with_batch.get(d.name)):
+				frappe.db.set_value("Item", d.name, "has_batch_no", "Yes")
+		
+		if d.has_serial_no == 'Yes':
+			if d.name not in serialized_items and sle_count.get(d.name) and sle_count.get(d.name) != sle_with_serial.get(d.name):
+				frappe.db.set_value("Item", d.name, "has_serial_no", "No")
+		else:
+			if d.name in serialized_items or (sle_count.get(d.name) and sle_count.get(d.name) == sle_with_serial.get(d.name)):
+				frappe.db.set_value("Item", d.name, "has_serial_no", "Yes")
+		
+				
+def get_sle_count():
+	sle_count = {}
+	for d in frappe.db.sql("""select item_code, count(name) as cnt from `tabStock Ledger Entry` group by item_code""", as_dict=1):
+		sle_count.setdefault(d.item_code, d.cnt)
+		
+	return sle_count
+	
+def get_sle_with_batch():
+	sle_with_batch = {}
+	for d in frappe.db.sql("""select item_code, count(name) as cnt from `tabStock Ledger Entry` 
+		where batch_no != '' group by item_code""", as_dict=1):
+			sle_with_batch.setdefault(d.item_code, d.cnt)
+		
+	return sle_with_batch
+	
+
+def get_sle_with_serial():
+	sle_with_serial = {}
+	for d in frappe.db.sql("""select item_code, count(name) as cnt from `tabStock Ledger Entry` 
+		where serial_no != '' group by item_code""", as_dict=1):
+			sle_with_serial.setdefault(d.item_code, d.cnt)
+	
+	return sle_with_serial
+	
+def get_items_with_batch():
+	return frappe.db.sql_list("select item from tabBatch")
+	
+def get_items_with_serial():
+	return frappe.db.sql_list("select item_code from `tabSerial No`")
\ No newline at end of file
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 6202e94..58a4412 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -32,16 +32,15 @@
 
 	#check for item quantity available in stock
 	def actual_amt_check(self):
-		if self.batch_no:
+		if self.batch_no and not self.get("allow_negative_stock"):
 			batch_bal_after_transaction = flt(frappe.db.sql("""select sum(actual_qty)
 				from `tabStock Ledger Entry`
 				where warehouse=%s and item_code=%s and batch_no=%s""",
 				(self.warehouse, self.item_code, self.batch_no))[0][0])
 
 			if batch_bal_after_transaction < 0:
-				frappe.throw(_("Negative balance {0} in Batch {1} for Item {2} at Warehouse {3} on {4} {5}")
-					.format(batch_bal_after_transaction - self.actual_qty, self.batch_no, self.item_code, self.warehouse,
-						formatdate(self.posting_date), self.posting_time))
+				frappe.throw(_("Stock balance in Batch {0} will become negative {1} for Item {2} at Warehouse {3}")
+					.format(self.batch_no, batch_bal_after_transaction, self.item_code, self.warehouse))
 
 	def validate_mandatory(self):
 		mandatory = ['warehouse','posting_date','voucher_type','voucher_no','company']
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 7bbf8fc..97a1f82 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -28,7 +28,7 @@
 				sle['actual_qty'] = -flt(sle['actual_qty'])
 
 			if sle.get("actual_qty") or sle.get("voucher_type")=="Stock Reconciliation":
-				sle_id = make_entry(sle)
+				sle_id = make_entry(sle, allow_negative_stock)
 
 			args = sle.copy()
 			args.update({
@@ -46,10 +46,11 @@
 		where voucher_no=%s and voucher_type=%s""",
 		(now(), frappe.session.user, voucher_type, voucher_no))
 
-def make_entry(args):
+def make_entry(args, allow_negative_stock=False):
 	args.update({"doctype": "Stock Ledger Entry"})
 	sle = frappe.get_doc(args)
 	sle.ignore_permissions = 1
+	sle.allow_negative_stock=allow_negative_stock
 	sle.insert()
 	sle.submit()
 	return sle.name
diff --git a/setup.py b/setup.py
index 0d31ff0..f2c3988 100644
--- a/setup.py
+++ b/setup.py
@@ -1,7 +1,7 @@
 from setuptools import setup, find_packages
 import os
 
-version = "4.19.0"
+version = "4.20.0"
 
 with open("requirements.txt", "r") as f:
 	install_requires = f.readlines()