Merge branch 'develop' into refactor-call-popup
diff --git a/erpnext/config/crm.py b/erpnext/config/crm.py
index e49fc60..70784f3 100644
--- a/erpnext/config/crm.py
+++ b/erpnext/config/crm.py
@@ -143,6 +143,11 @@
 				},
 				{
 					"type": "doctype",
+					"name": "Email Campaign",
+					"description": _("Sends Mails to lead or contact based on a Campaign schedule"),
+				},
+				{
+					"type": "doctype",
 					"name": "SMS Center",
 					"description":_("Send mass SMS to your contacts"),
 				},
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index d8c50b2..b2057ca 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -40,7 +40,6 @@
 		["To Bill", "eval:self.per_delivered == 100 and self.per_billed < 100 and self.docstatus == 1"],
 		["To Deliver", "eval:self.per_delivered < 100 and self.per_billed == 100 and self.docstatus == 1"],
 		["Completed", "eval:self.per_delivered == 100 and self.per_billed == 100 and self.docstatus == 1"],
-		["Completed", "eval:self.order_type == 'Maintenance' and self.per_billed == 100 and self.docstatus == 1"],
 		["Cancelled", "eval:self.docstatus==2"],
 		["Closed", "eval:self.status=='Closed'"],
 		["On Hold", "eval:self.status=='On Hold'"],
diff --git a/erpnext/crm/doctype/campaign_email_schedule/__init__.py b/erpnext/crm/doctype/campaign_email_schedule/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/crm/doctype/campaign_email_schedule/__init__.py
diff --git a/erpnext/crm/doctype/campaign_email_schedule/campaign_email_schedule.json b/erpnext/crm/doctype/campaign_email_schedule/campaign_email_schedule.json
new file mode 100644
index 0000000..1481a32
--- /dev/null
+++ b/erpnext/crm/doctype/campaign_email_schedule/campaign_email_schedule.json
@@ -0,0 +1,38 @@
+{
+ "creation": "2019-06-30 15:56:20.306901",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "email_template",
+  "send_after_days"
+ ],
+ "fields": [
+  {
+   "fieldname": "send_after_days",
+   "fieldtype": "Int",
+   "in_list_view": 1,
+   "label": "Send After (days)",
+   "reqd": 1
+  },
+  {
+   "fieldname": "email_template",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Email Template",
+   "options": "Email Template",
+   "reqd": 1
+  }
+ ],
+ "istable": 1,
+ "modified": "2019-07-12 11:46:43.184123",
+ "modified_by": "Administrator",
+ "module": "CRM",
+ "name": "Campaign Email Schedule",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/crm/doctype/campaign_email_schedule/campaign_email_schedule.py b/erpnext/crm/doctype/campaign_email_schedule/campaign_email_schedule.py
new file mode 100644
index 0000000..8445b8a
--- /dev/null
+++ b/erpnext/crm/doctype/campaign_email_schedule/campaign_email_schedule.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class CampaignEmailSchedule(Document):
+	pass
diff --git a/erpnext/crm/doctype/email_campaign/__init__.py b/erpnext/crm/doctype/email_campaign/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/crm/doctype/email_campaign/__init__.py
diff --git a/erpnext/crm/doctype/email_campaign/email_campaign.js b/erpnext/crm/doctype/email_campaign/email_campaign.js
new file mode 100644
index 0000000..b0e9353
--- /dev/null
+++ b/erpnext/crm/doctype/email_campaign/email_campaign.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Email Campaign', {
+	email_campaign_for: function(frm) {
+		frm.set_value('recipient', '');
+	}
+});
diff --git a/erpnext/crm/doctype/email_campaign/email_campaign.json b/erpnext/crm/doctype/email_campaign/email_campaign.json
new file mode 100644
index 0000000..3259136
--- /dev/null
+++ b/erpnext/crm/doctype/email_campaign/email_campaign.json
@@ -0,0 +1,95 @@
+{
+ "autoname": "format:MAIL-CAMP-{YYYY}-{#####}",
+ "creation": "2019-06-30 16:05:30.015615",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "campaign_name",
+  "email_campaign_for",
+  "recipient",
+  "sender",
+  "column_break_4",
+  "start_date",
+  "end_date",
+  "status"
+ ],
+ "fields": [
+  {
+   "fieldname": "campaign_name",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Campaign",
+   "options": "Campaign",
+   "reqd": 1
+  },
+  {
+   "fieldname": "status",
+   "fieldtype": "Select",
+   "label": "Status",
+   "options": "\nScheduled\nIn Progress\nCompleted\nUnsubscribed",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_4",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "start_date",
+   "fieldtype": "Date",
+   "label": "Start Date",
+   "reqd": 1
+  },
+  {
+   "fieldname": "end_date",
+   "fieldtype": "Date",
+   "label": "End Date",
+   "read_only": 1
+  },
+  {
+   "default": "Lead",
+   "fieldname": "email_campaign_for",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "label": "Email Campaign For ",
+   "options": "\nLead\nContact"
+  },
+  {
+   "fieldname": "recipient",
+   "fieldtype": "Dynamic Link",
+   "label": "Recipient",
+   "options": "email_campaign_for",
+   "reqd": 1
+  },
+  {
+   "default": "__user",
+   "fieldname": "sender",
+   "fieldtype": "Link",
+   "label": "Sender",
+   "options": "User"
+  }
+ ],
+ "modified": "2019-07-12 13:47:37.261213",
+ "modified_by": "Administrator",
+ "module": "CRM",
+ "name": "Email Campaign",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/crm/doctype/email_campaign/email_campaign.py b/erpnext/crm/doctype/email_campaign/email_campaign.py
new file mode 100644
index 0000000..98e4927
--- /dev/null
+++ b/erpnext/crm/doctype/email_campaign/email_campaign.py
@@ -0,0 +1,102 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+from frappe.utils import getdate, add_days, today, nowdate, cstr
+from frappe.model.document import Document
+from frappe.core.doctype.communication.email import make
+
+class EmailCampaign(Document):
+	def validate(self):
+		self.set_date()
+		#checking if email is set for lead. Not checking for contact as email is a mandatory field for contact.
+		if self.email_campaign_for == "Lead":
+			self.validate_lead()
+		self.validate_email_campaign_already_exists()
+		self.update_status()
+
+	def set_date(self):
+		if getdate(self.start_date) < getdate(today()):
+			frappe.throw(_("Start Date cannot be before the current date"))
+		#set the end date as start date + max(send after days) in campaign schedule
+		send_after_days = []
+		campaign = frappe.get_doc("Campaign", self.campaign_name)
+		for entry in campaign.get("campaign_schedules"):
+			send_after_days.append(entry.send_after_days)
+		try:
+			end_date = add_days(getdate(self.start_date), max(send_after_days))
+		except ValueError:
+			frappe.throw(_("Please set up the Campaign Schedule in the Campaign {0}").format(self.campaign_name))
+
+	def validate_lead(self):
+		lead_email_id = frappe.db.get_value("Lead", self.recipient, 'email_id')
+		if not lead_email_id:
+			lead_name = frappe.db.get_value("Lead", self.recipient, 'lead_name')
+			frappe.throw(_("Please set an email id for the Lead {0}").format(lead_name))
+
+	def validate_email_campaign_already_exists(self):
+		email_campaign_exists = frappe.db.exists("Email Campaign", {
+			"campaign_name": self.campaign_name,
+			"recipient": self.recipient,
+			"status": ("in", ["In Progress", "Scheduled"])
+		})
+		if email_campaign_exists:
+			frappe.throw(_("The Campaign '{0}' already exists for the {1} '{2}'").format(self.campaign_name, self.email_campaign_for, self.recipient))
+
+	def update_status(self):
+		start_date = getdate(self.start_date)
+		end_date = getdate(self.end_date)
+		today_date = getdate(today())
+		if start_date > today_date:
+			self.status = "Scheduled"
+		elif end_date >= today_date:
+			self.status = "In Progress"
+		elif end_date < today_date:
+			self.status = "Completed"
+
+#called through hooks to send campaign mails to leads
+def send_email_to_leads_or_contacts():
+	email_campaigns = frappe.get_all("Email Campaign", filters = { 'status': ('not in', ['Unsubscribed', 'Completed', 'Scheduled']) })
+	for camp in email_campaigns:
+		email_campaign = frappe.get_doc("Email Campaign", camp.name)
+		campaign = frappe.get_cached_doc("Campaign", email_campaign.campaign_name)
+		for entry in campaign.get("campaign_schedules"):
+			scheduled_date = add_days(email_campaign.get('start_date'), entry.get('send_after_days'))
+			if scheduled_date == getdate(today()):
+				send_mail(entry, email_campaign)
+
+def send_mail(entry, email_campaign):
+	recipient = frappe.db.get_value(email_campaign.email_campaign_for, email_campaign.get("recipient"), 'email_id')
+
+	email_template = frappe.get_doc("Email Template", entry.get("email_template"))
+	sender = frappe.db.get_value("User", email_campaign.get("sender"), 'email')
+
+	# send mail and link communication to document
+	comm = make(
+		doctype = "Email Campaign",
+		name = email_campaign.name,
+		subject = email_template.get("subject"),
+		content = email_template.get("response"),
+		sender = sender,
+		recipients = recipient,
+		communication_medium = "Email",
+		sent_or_received = "Sent",
+		send_email = True,
+		email_template = email_template.name
+	)
+	return comm
+
+#called from hooks on doc_event Email Unsubscribe
+def unsubscribe_recipient(unsubscribe, method):
+	if unsubscribe.reference_doctype == 'Email Campaign':
+		frappe.db.set_value("Email Campaign", unsubscribe.reference_name, "status", "Unsubscribed")
+
+#called through hooks to update email campaign status daily
+def set_email_campaign_status():
+	email_campaigns = frappe.get_all("Email Campaign", filters = { 'status': ('!=', 'Unsubscribed')})
+	for entry in email_campaigns:
+		email_campaign = frappe.get_doc("Email Campaign", entry.name)
+		email_campaign.update_status()
diff --git a/erpnext/crm/doctype/email_campaign/email_campaign_list.js b/erpnext/crm/doctype/email_campaign/email_campaign_list.js
new file mode 100644
index 0000000..adc399d
--- /dev/null
+++ b/erpnext/crm/doctype/email_campaign/email_campaign_list.js
@@ -0,0 +1,11 @@
+frappe.listview_settings['Email Campaign'] = {
+	get_indicator: function(doc) {
+		var colors = {
+			"Unsubscribed": "red",
+			"Scheduled": "blue",
+			"In Progress": "orange",
+			"Completed": "green"
+		};
+		return [__(doc.status), colors[doc.status], "status,=," + doc.status];
+	}
+};
diff --git a/erpnext/crm/doctype/email_campaign/test_email_campaign.py b/erpnext/crm/doctype/email_campaign/test_email_campaign.py
new file mode 100644
index 0000000..f5eab48
--- /dev/null
+++ b/erpnext/crm/doctype/email_campaign/test_email_campaign.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestEmailCampaign(unittest.TestCase):
+	pass
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index d814700..47d1a68 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -233,6 +233,9 @@
 	},
 	"Contact":{
 		"on_trash": "erpnext.support.doctype.issue.issue.update_issue"
+	},
+	"Email Unsubscribe": {
+		"after_insert": "erpnext.crm.doctype.email_campaign.email_campaign.unsubscribe_recipient"
 	}
 }
 
@@ -272,6 +275,8 @@
 		"erpnext.projects.doctype.project.project.send_project_status_email_to_users",
 		"erpnext.quality_management.doctype.quality_review.quality_review.review",
 		"erpnext.support.doctype.service_level_agreement.service_level_agreement.check_agreement_status",
+		"erpnext.crm.doctype.email_campaign.email_campaign.send_email_to_leads_or_contacts",
+		"erpnext.crm.doctype.email_campaign.email_campaign.set_email_campaign_status"
 	],
 	"daily_long": [
 		"erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_latest_price_in_all_boms"
diff --git a/erpnext/selling/doctype/campaign/campaign.json b/erpnext/selling/doctype/campaign/campaign.json
index d120699..986ac13 100644
--- a/erpnext/selling/doctype/campaign/campaign.json
+++ b/erpnext/selling/doctype/campaign/campaign.json
@@ -6,18 +6,13 @@
  "description": "Keep Track of Sales Campaigns. Keep track of Leads, Quotations, Sales Order etc from Campaigns to gauge Return on Investment. ",
  "doctype": "DocType",
  "document_type": "Setup",
+ "engine": "InnoDB",
  "field_order": [
   "campaign",
   "campaign_name",
   "naming_series",
-  "from_date",
-  "column_break1",
-  "status",
-  "to_date",
-  "budget_section",
-  "currency",
-  "column_break2",
-  "budget",
+  "campaign_schedules_section",
+  "campaign_schedules",
   "description_section",
   "description"
  ],
@@ -53,56 +48,24 @@
    "width": "300px"
   },
   {
-   "fieldname": "status",
-   "fieldtype": "Select",
-   "in_list_view": 1,
-   "label": "Status",
-   "options": "\nPlanned\nIn Progress\nCompleted\nCancelled",
-   "reqd": 1,
-   "default": "Planned"
-  },
-  {
-   "fieldname": "from_date",
-   "fieldtype": "Date",
-   "label": "From Date"
-  },
-  {
-   "fieldname": "to_date",
-   "fieldtype": "Date",
-   "label": "To Date"
-  },
-  {
-   "fieldname": "column_break1",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "budget",
-   "fieldtype": "Currency",
-   "label": "Budget"
-  },
-  {
    "fieldname": "description_section",
    "fieldtype": "Section Break"
   },
   {
-   "fieldname": "currency",
-   "fieldtype": "Link",
-   "label": "Currency",
-   "options": "Currency"
+   "fieldname": "campaign_schedules",
+   "fieldtype": "Table",
+   "label": "Campaign Schedules",
+   "options": "Campaign Email Schedule"
   },
   {
-   "fieldname": "column_break2",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "budget_section",
+   "fieldname": "campaign_schedules_section",
    "fieldtype": "Section Break",
-   "label": "BUDGET"
+   "label": "Campaign Schedules"
   }
  ],
  "icon": "fa fa-bullhorn",
  "idx": 1,
- "modified": "2019-04-29 22:09:39.251884",
+ "modified": "2019-07-22 12:03:39.832342",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Campaign",
@@ -140,5 +103,7 @@
    "write": 1
   }
  ],
- "quick_entry": 1
-}
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/erpnext/selling/doctype/campaign/campaign_dashboard.py b/erpnext/selling/doctype/campaign/campaign_dashboard.py
new file mode 100644
index 0000000..a9d8eca
--- /dev/null
+++ b/erpnext/selling/doctype/campaign/campaign_dashboard.py
@@ -0,0 +1,13 @@
+from __future__ import unicode_literals
+from frappe import _
+
+def get_data():
+	return {
+		'fieldname': 'campaign_name',
+		'transactions': [
+			{
+				'label': _('Email Campaigns'),
+				'items': ['Email Campaign']
+			}
+		],
+	}
diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py
index 8fbeac8..cab2116 100644
--- a/erpnext/setup/doctype/item_group/item_group.py
+++ b/erpnext/setup/doctype/item_group/item_group.py
@@ -69,8 +69,7 @@
 			"items": get_product_list_for_group(product_group = self.name, start=start,
 				limit=context.page_length + 1, search=frappe.form_dict.get("search")),
 			"parents": get_parent_item_groups(self.parent_item_group),
-			"title": self.name,
-			"products_as_list": cint(frappe.db.get_single_value('Products Settings', 'products_as_list'))
+			"title": self.name
 		})
 
 		if self.slideshow:
diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py
index bd24257..f609a0b 100644
--- a/erpnext/stock/doctype/batch/batch.py
+++ b/erpnext/stock/doctype/batch/batch.py
@@ -122,8 +122,8 @@
 			self.expiry_date = add_days(self.manufacturing_date, shelf_life_in_days)
 
 		if has_expiry_date and not self.expiry_date:
-			frappe.throw(_('Expiry date is mandatory for selected item'))
-			frappe.msgprint(_('Set items shelf life in days, to set expiry based on manufacturing_date plus self life'))
+			frappe.msgprint(_('Expiry date is mandatory for selected item.'))
+			frappe.throw(_("Set item's shelf life in days, to set expiry based on manufacturing date plus shelf-life."))
 
 	def get_name_from_naming_series(self):
 		"""
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index 2df3d98..c203f8b 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -222,7 +222,7 @@
 						frappe.throw(_("Serial No {0} has already been received").format(serial_no),
 							SerialNoDuplicateError)
 
-					if (sr.delivery_document_no and sle.voucher_type != 'Stock Entry'
+					if (sr.delivery_document_no and sle.voucher_type not in ['Stock Entry', 'Stock Reconciliation']
 						and sle.voucher_type == sr.delivery_document_type):
 						return_against = frappe.db.get_value(sle.voucher_type, sle.voucher_no, 'return_against')
 						if return_against and return_against != sr.delivery_document_no:
@@ -299,7 +299,7 @@
 		be delivered""").format(sales_order, sr.item_code, sr.name))
 
 def has_duplicate_serial_no(sn, sle):
-	if sn.warehouse:
+	if sn.warehouse and sle.voucher_type != 'Stock Reconciliation':
 		return True
 
 	if sn.company != sle.company:
@@ -415,16 +415,20 @@
 	if not stock_ledger_entries: return
 
 	for d in controller.get(parentfield):
+		if d.serial_no:
+			continue
+
 		update_rejected_serial_nos = True if (controller.doctype in ("Purchase Receipt", "Purchase Invoice")
 			and d.rejected_qty) else False
 		accepted_serial_nos_updated = False
+
 		if controller.doctype == "Stock Entry":
 			warehouse = d.t_warehouse
 			qty = d.transfer_qty
 		else:
 			warehouse = d.warehouse
-			qty = d.stock_qty
-
+			qty = (d.qty if controller.doctype == "Stock Reconciliation"
+				else d.stock_qty)
 		for sle in stock_ledger_entries:
 			if sle.voucher_detail_no==d.name:
 				if not accepted_serial_nos_updated and qty and abs(sle.actual_qty)==qty \
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 0abcbb3..f40560a 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -359,7 +359,7 @@
 				d.basic_rate = 0.0
 			elif d.t_warehouse and not d.basic_rate:
 				d.basic_rate = get_valuation_rate(d.item_code, d.t_warehouse,
-					self.doctype, d.name, d.allow_zero_valuation_rate,
+					self.doctype, self.name, d.allow_zero_valuation_rate,
 					currency=erpnext.get_company_currency(self.company))
 
 	def set_actual_qty(self):
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 79da70e..5fe89d6 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -38,7 +38,7 @@
 		self.check_stock_frozen_date()
 		self.actual_amt_check()
 
-		if not self.get("via_landed_cost_voucher") and self.voucher_type != 'Stock Reconciliation':
+		if not self.get("via_landed_cost_voucher"):
 			from erpnext.stock.doctype.serial_no.serial_no import process_serial_no
 			process_serial_no(self)
 
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
index ed9d770..5ac0b09 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
@@ -12,8 +12,7 @@
 			return {
 				query: "erpnext.controllers.queries.item_query",
 				filters:{
-					"is_stock_item": 1,
-					"has_serial_no": 0
+					"is_stock_item": 1
 				}
 			}
 		});
@@ -77,6 +76,7 @@
 
 	set_valuation_rate_and_qty: function(frm, cdt, cdn) {
 		var d = frappe.model.get_doc(cdt, cdn);
+
 		if(d.item_code && d.warehouse) {
 			frappe.call({
 				method: "erpnext.stock.doctype.stock_reconciliation.stock_reconciliation.get_stock_balance_for",
@@ -84,7 +84,8 @@
 					item_code: d.item_code,
 					warehouse: d.warehouse,
 					posting_date: frm.doc.posting_date,
-					posting_time: frm.doc.posting_time
+					posting_time: frm.doc.posting_time,
+					batch_no: d.batch_no
 				},
 				callback: function(r) {
 					frappe.model.set_value(cdt, cdn, "qty", r.message.qty);
@@ -93,7 +94,7 @@
 					frappe.model.set_value(cdt, cdn, "current_valuation_rate", r.message.rate);
 					frappe.model.set_value(cdt, cdn, "current_amount", r.message.rate * r.message.qty);
 					frappe.model.set_value(cdt, cdn, "amount", r.message.rate * r.message.qty);
-
+					frappe.model.set_value(cdt, cdn, "current_serial_no", r.message.serial_nos);
 				}
 			});
 		}
@@ -152,17 +153,44 @@
 	barcode: function(frm, cdt, cdn) {
 		frm.events.set_item_code(frm, cdt, cdn);
 	},
+
 	warehouse: function(frm, cdt, cdn) {
+		var child = locals[cdt][cdn];
+		if (child.batch_no) {
+			frappe.model.set_value(child.cdt, child.cdn, "batch_no", "");
+		}
+
 		frm.events.set_valuation_rate_and_qty(frm, cdt, cdn);
 	},
+
 	item_code: function(frm, cdt, cdn) {
+		var child = locals[cdt][cdn];
+		if (child.batch_no) {
+			frappe.model.set_value(cdt, cdn, "batch_no", "");
+		}
+
 		frm.events.set_valuation_rate_and_qty(frm, cdt, cdn);
 	},
+
+	batch_no: function(frm, cdt, cdn) {
+		frm.events.set_valuation_rate_and_qty(frm, cdt, cdn);
+	},
+
 	qty: function(frm, cdt, cdn) {
 		frm.events.set_amount_quantity(frm, cdt, cdn);
 	},
+
 	valuation_rate: function(frm, cdt, cdn) {
 		frm.events.set_amount_quantity(frm, cdt, cdn);
+	},
+
+	serial_no: function(frm, cdt, cdn) {
+		var child = locals[cdt][cdn];
+
+		if (child.serial_no) {
+			const serial_nos = child.serial_no.trim().split('\n');
+			frappe.model.set_value(cdt, cdn, "qty", serial_nos.length);
+		}
 	}
 
 });
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 205beed..d9e62c7 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -9,7 +9,9 @@
 from erpnext.stock.stock_ledger import update_entries_after
 from erpnext.controllers.stock_controller import StockController
 from erpnext.accounts.utils import get_company_default
-from erpnext.stock.utils import get_stock_balance
+from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+from erpnext.stock.utils import get_stock_balance, get_incoming_rate, get_available_serial_nos
+from erpnext.stock.doctype.batch.batch import get_batch_qty
 
 class OpeningEntryAccountError(frappe.ValidationError): pass
 class EmptyStockReconciliationItemsError(frappe.ValidationError): pass
@@ -30,10 +32,16 @@
 		self.validate_expense_account()
 		self.set_total_qty_and_amount()
 
+		if self._action=="submit":
+			self.make_batches('warehouse')
+
 	def on_submit(self):
 		self.update_stock_ledger()
 		self.make_gl_entries()
 
+		from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
+		update_serial_nos_after_submit(self, "items")
+
 	def on_cancel(self):
 		self.delete_and_repost_sle()
 		self.make_gl_entries_on_cancel()
@@ -42,23 +50,28 @@
 		"""Remove items if qty or rate is not changed"""
 		self.difference_amount = 0.0
 		def _changed(item):
-			qty, rate = get_stock_balance(item.item_code, item.warehouse,
-					self.posting_date, self.posting_time, with_valuation_rate=True)
-			if (item.qty==None or item.qty==qty) and (item.valuation_rate==None or item.valuation_rate==rate):
+			item_dict = get_stock_balance_for(item.item_code, item.warehouse,
+				self.posting_date, self.posting_time, batch_no=item.batch_no)
+			if (((item.qty is None or item.qty==item_dict.get("qty")) and
+				(item.valuation_rate is None or item.valuation_rate==item_dict.get("rate")) and not item.serial_no)
+				or (item.serial_no and item.serial_no == item_dict.get("serial_nos"))):
 				return False
 			else:
 				# set default as current rates
-				if item.qty==None:
-					item.qty = qty
+				if item.qty is None:
+					item.qty = item_dict.get("qty")
 
-				if item.valuation_rate==None:
-					item.valuation_rate = rate
+				if item.valuation_rate is None:
+					item.valuation_rate = item_dict.get("rate")
 
-				item.current_qty = qty
-				item.current_valuation_rate = rate
+				if item_dict.get("serial_nos"):
+					item.current_serial_no = item_dict.get("serial_nos")
+
+				item.current_qty = item_dict.get("qty")
+				item.current_valuation_rate = item_dict.get("rate")
 				self.difference_amount += (flt(item.qty, item.precision("qty")) * \
-					flt(item.valuation_rate or rate, item.precision("valuation_rate")) \
-					- flt(qty, item.precision("qty")) * flt(rate, item.precision("valuation_rate")))
+					flt(item.valuation_rate or item_dict.get("rate"), item.precision("valuation_rate")) \
+					- flt(item_dict.get("qty"), item.precision("qty")) * flt(item_dict.get("rate"), item.precision("valuation_rate")))
 				return True
 
 		items = list(filter(lambda d: _changed(d), self.items))
@@ -84,12 +97,17 @@
 
 		for row_num, row in enumerate(self.items):
 			# find duplicates
-			if [row.item_code, row.warehouse] in item_warehouse_combinations:
+			key = [row.item_code, row.warehouse]
+			for field in ['serial_no', 'batch_no']:
+				if row.get(field):
+					key.append(row.get(field))
+
+			if key in item_warehouse_combinations:
 				self.validation_messages.append(_get_msg(row_num, _("Duplicate entry")))
 			else:
-				item_warehouse_combinations.append([row.item_code, row.warehouse])
+				item_warehouse_combinations.append(key)
 
-			self.validate_item(row.item_code, row_num+1)
+			self.validate_item(row.item_code, row)
 
 			# validate warehouse
 			if not frappe.db.get_value("Warehouse", row.warehouse):
@@ -131,7 +149,7 @@
 
 			raise frappe.ValidationError(self.validation_messages)
 
-	def validate_item(self, item_code, row_num):
+	def validate_item(self, item_code, row):
 		from erpnext.stock.doctype.item.item import validate_end_of_life, \
 			validate_is_stock_item, validate_cancelled_item
 
@@ -145,51 +163,130 @@
 			validate_is_stock_item(item_code, item.is_stock_item, verbose=0)
 
 			# item should not be serialized
-			if item.has_serial_no == 1:
-				raise frappe.ValidationError(_("Serialized Item {0} cannot be updated using Stock Reconciliation, please use Stock Entry").format(item_code))
+			if item.has_serial_no and not row.serial_no and not item.serial_no_series:
+				raise frappe.ValidationError(_("Serial no(s) required for serialized item {0}").format(item_code))
 
 			# item managed batch-wise not allowed
-			if item.has_batch_no == 1:
-				raise frappe.ValidationError(_("Batched Item {0} cannot be updated using Stock Reconciliation, instead use Stock Entry").format(item_code))
+			if item.has_batch_no and not row.batch_no and not item.create_new_batch:
+				raise frappe.ValidationError(_("Batch no is required for batched item {0}").format(item_code))
 
 			# docstatus should be < 2
 			validate_cancelled_item(item_code, item.docstatus, verbose=0)
 
 		except Exception as e:
-			self.validation_messages.append(_("Row # ") + ("%d: " % (row_num)) + cstr(e))
+			self.validation_messages.append(_("Row # ") + ("%d: " % (row.idx)) + cstr(e))
 
 	def update_stock_ledger(self):
 		"""	find difference between current and expected entries
 			and create stock ledger entries based on the difference"""
 		from erpnext.stock.stock_ledger import get_previous_sle
 
+		sl_entries = []
 		for row in self.items:
+			item = frappe.get_doc("Item", row.item_code)
+			if item.has_serial_no or item.has_batch_no:
+				self.get_sle_for_serialized_items(row, sl_entries)
+			else:
+				previous_sle = get_previous_sle({
+					"item_code": row.item_code,
+					"warehouse": row.warehouse,
+					"posting_date": self.posting_date,
+					"posting_time": self.posting_time
+				})
+
+				if previous_sle:
+					if row.qty in ("", None):
+						row.qty = previous_sle.get("qty_after_transaction", 0)
+
+					if row.valuation_rate in ("", None):
+						row.valuation_rate = previous_sle.get("valuation_rate", 0)
+
+				if row.qty and not row.valuation_rate:
+					frappe.throw(_("Valuation Rate required for Item {0} at row {1}").format(row.item_code, row.idx))
+
+				if ((previous_sle and row.qty == previous_sle.get("qty_after_transaction")
+					and (row.valuation_rate == previous_sle.get("valuation_rate") or row.qty == 0))
+					or (not previous_sle and not row.qty)):
+						continue
+
+				sl_entries.append(self.get_sle_for_items(row))
+
+		if sl_entries:
+			self.make_sl_entries(sl_entries)
+
+	def get_sle_for_serialized_items(self, row, sl_entries):
+		from erpnext.stock.stock_ledger import get_previous_sle
+
+		serial_nos = get_serial_nos(row.serial_no)
+
+
+		# To issue existing serial nos
+		if row.current_qty and (row.current_serial_no or row.batch_no):
+			args = self.get_sle_for_items(row)
+			args.update({
+				'actual_qty': -1 * row.current_qty,
+				'serial_no': row.current_serial_no,
+				'batch_no': row.batch_no,
+				'valuation_rate': row.current_valuation_rate
+			})
+
+			if row.current_serial_no:
+				args.update({
+					'qty_after_transaction': 0,
+				})
+
+			sl_entries.append(args)
+
+		for serial_no in serial_nos:
+			args = self.get_sle_for_items(row, [serial_no])
+
 			previous_sle = get_previous_sle({
 				"item_code": row.item_code,
-				"warehouse": row.warehouse,
 				"posting_date": self.posting_date,
-				"posting_time": self.posting_time
+				"posting_time": self.posting_time,
+				"serial_no": serial_no
 			})
-			if previous_sle:
-				if row.qty in ("", None):
-					row.qty = previous_sle.get("qty_after_transaction", 0)
 
-				if row.valuation_rate in ("", None):
-					row.valuation_rate = previous_sle.get("valuation_rate", 0)
+			if previous_sle and row.warehouse != previous_sle.get("warehouse"):
+				# If serial no exists in different warehouse
 
-			if row.qty and not row.valuation_rate:
-				frappe.throw(_("Valuation Rate required for Item in row {0}").format(row.idx))
+				new_args = args.copy()
+				new_args.update({
+					'actual_qty': -1,
+					'qty_after_transaction': cint(previous_sle.get('qty_after_transaction')) - 1,
+					'warehouse': previous_sle.get("warehouse", '') or row.warehouse,
+					'valuation_rate': previous_sle.get("valuation_rate")
+				})
 
-			if ((previous_sle and row.qty == previous_sle.get("qty_after_transaction")
-				and (row.valuation_rate == previous_sle.get("valuation_rate") or row.qty == 0))
-				or (not previous_sle and not row.qty)):
-					continue
+				sl_entries.append(new_args)
 
-			self.insert_entries(row)
+		if row.qty:
+			args = self.get_sle_for_items(row)
 
-	def insert_entries(self, row):
+			args.update({
+				'actual_qty': row.qty,
+				'incoming_rate': row.valuation_rate,
+				'valuation_rate': row.valuation_rate
+			})
+
+			sl_entries.append(args)
+
+		if serial_nos == get_serial_nos(row.current_serial_no):
+			# update valuation rate
+			self.update_valuation_rate_for_serial_nos(row, serial_nos)
+
+	def update_valuation_rate_for_serial_nos(self, row, serial_nos):
+		valuation_rate = row.valuation_rate if self.docstatus == 1 else row.current_valuation_rate
+		for d in serial_nos:
+			frappe.db.set_value("Serial No", d, 'purchase_rate', valuation_rate)
+
+	def get_sle_for_items(self, row, serial_nos=None):
 		"""Insert Stock Ledger Entries"""
-		args = frappe._dict({
+
+		if not serial_nos and row.serial_no:
+			serial_nos = get_serial_nos(row.serial_no)
+
+		data = frappe._dict({
 			"doctype": "Stock Ledger Entry",
 			"item_code": row.item_code,
 			"warehouse": row.warehouse,
@@ -197,13 +294,19 @@
 			"posting_time": self.posting_time,
 			"voucher_type": self.doctype,
 			"voucher_no": self.name,
+			"voucher_detail_no": row.name,
 			"company": self.company,
 			"stock_uom": frappe.db.get_value("Item", row.item_code, "stock_uom"),
-			"is_cancelled": "No",
-			"qty_after_transaction": flt(row.qty, row.precision("qty")),
+			"is_cancelled": "No" if self.docstatus != 2 else "Yes",
+			"serial_no": '\n'.join(serial_nos) if serial_nos else '',
+			"batch_no": row.batch_no,
 			"valuation_rate": flt(row.valuation_rate, row.precision("valuation_rate"))
 		})
-		self.make_sl_entries([args])
+
+		if not row.batch_no:
+			data.qty_after_transaction = flt(row.qty, row.precision("qty"))
+
+		return data
 
 	def delete_and_repost_sle(self):
 		"""	Delete Stock Ledger Entries related to this voucher
@@ -217,6 +320,15 @@
 		frappe.db.sql("""delete from `tabStock Ledger Entry`
 			where voucher_type=%s and voucher_no=%s""", (self.doctype, self.name))
 
+		sl_entries = []
+		for row in self.items:
+			if row.serial_no or row.batch_no or row.current_serial_no:
+				self.get_sle_for_serialized_items(row, sl_entries)
+
+		if sl_entries:
+			sl_entries.reverse()
+			self.make_sl_entries(sl_entries)
+
 		# repost future entries for selected item_code, warehouse
 		for entries in existing_entries:
 			update_entries_after({
@@ -310,17 +422,52 @@
 	return res
 
 @frappe.whitelist()
-def get_stock_balance_for(item_code, warehouse, posting_date, posting_time):
+def get_stock_balance_for(item_code, warehouse,
+	posting_date, posting_time, batch_no=None, with_valuation_rate= True):
 	frappe.has_permission("Stock Reconciliation", "write", throw = True)
 
-	qty, rate = get_stock_balance(item_code, warehouse,
-		posting_date, posting_time, with_valuation_rate=True)
+	item_dict = frappe.db.get_value("Item", item_code,
+		["has_serial_no", "has_batch_no"], as_dict=1)
+
+	serial_nos = ""
+	if item_dict.get("has_serial_no"):
+		qty, rate, serial_nos = get_qty_rate_for_serial_nos(item_code,
+			warehouse, posting_date, posting_time, item_dict)
+	else:
+		qty, rate = get_stock_balance(item_code, warehouse,
+			posting_date, posting_time, with_valuation_rate=with_valuation_rate)
+
+	if item_dict.get("has_batch_no"):
+		qty = get_batch_qty(batch_no, warehouse) or 0
 
 	return {
 		'qty': qty,
-		'rate': rate
+		'rate': rate,
+		'serial_nos': serial_nos
 	}
 
+def get_qty_rate_for_serial_nos(item_code, warehouse, posting_date, posting_time, item_dict):
+	args = {
+		"item_code": item_code,
+		"warehouse": warehouse,
+		"posting_date": posting_date,
+		"posting_time": posting_time,
+	}
+
+	serial_nos_list = [serial_no.get("name")
+			for serial_no in get_available_serial_nos(item_code, warehouse)]
+
+	qty = len(serial_nos_list)
+	serial_nos = '\n'.join(serial_nos_list)
+	args.update({
+		'qty': qty,
+		"serial_nos": serial_nos
+	})
+
+	rate = get_incoming_rate(args, raise_error_if_no_rate=False) or 0
+
+	return qty, rate, serial_nos
+
 @frappe.whitelist()
 def get_difference_account(purpose, company):
 	if purpose == 'Stock Reconciliation':
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index 2dc585b..f0c71cf 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -13,9 +13,12 @@
 from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import EmptyStockReconciliationItemsError, get_items
 from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
 from erpnext.stock.doctype.item.test_item import create_item
+from erpnext.stock.utils import get_stock_balance, get_incoming_rate, get_available_serial_nos, get_stock_value_on
+from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 
 class TestStockReconciliation(unittest.TestCase):
 	def setUp(self):
+		create_batch_or_serial_no_items()
 		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
 		self.insert_existing_sle()
 
@@ -106,6 +109,135 @@
 		make_stock_entry(posting_date="2013-01-05", posting_time="07:00", item_code="_Test Item",
 			target="_Test Warehouse - _TC", qty=15, basic_rate=1200)
 
+	def test_stock_reco_for_serialized_item(self):
+		set_perpetual_inventory()
+
+		to_delete_records = []
+		to_delete_serial_nos = []
+
+		# Add new serial nos
+		serial_item_code = "Stock-Reco-Serial-Item-1"
+		serial_warehouse = "_Test Warehouse for Stock Reco1 - _TC"
+
+		sr = create_stock_reconciliation(item_code=serial_item_code,
+			warehouse = serial_warehouse, qty=5, rate=200)
+
+		# print(sr.name)
+		serial_nos = get_serial_nos(sr.items[0].serial_no)
+		self.assertEqual(len(serial_nos), 5)
+
+		args = {
+			"item_code": serial_item_code,
+			"warehouse": serial_warehouse,
+			"posting_date": nowdate(),
+			"posting_time": nowtime(),
+			"serial_no": sr.items[0].serial_no
+		}
+
+		valuation_rate = get_incoming_rate(args)
+		self.assertEqual(valuation_rate, 200)
+
+		to_delete_records.append(sr.name)
+
+		sr = create_stock_reconciliation(item_code=serial_item_code,
+			warehouse = serial_warehouse, qty=5, rate=300, serial_no = '\n'.join(serial_nos))
+
+		# print(sr.name)
+		serial_nos1 = get_serial_nos(sr.items[0].serial_no)
+		self.assertEqual(len(serial_nos1), 5)
+
+		args = {
+			"item_code": serial_item_code,
+			"warehouse": serial_warehouse,
+			"posting_date": nowdate(),
+			"posting_time": nowtime(),
+			"serial_no": sr.items[0].serial_no
+		}
+
+		valuation_rate = get_incoming_rate(args)
+		self.assertEqual(valuation_rate, 300)
+
+		to_delete_records.append(sr.name)
+		to_delete_records.reverse()
+
+		for d in to_delete_records:
+			stock_doc = frappe.get_doc("Stock Reconciliation", d)
+			stock_doc.cancel()
+			frappe.delete_doc("Stock Reconciliation", stock_doc.name)
+
+		for d in serial_nos + serial_nos1:
+			if frappe.db.exists("Serial No", d):
+				frappe.delete_doc("Serial No", d)
+
+	def test_stock_reco_for_batch_item(self):
+		set_perpetual_inventory()
+
+		to_delete_records = []
+		to_delete_serial_nos = []
+
+		# Add new serial nos
+		item_code = "Stock-Reco-batch-Item-1"
+		warehouse = "_Test Warehouse for Stock Reco2 - _TC"
+
+		sr = create_stock_reconciliation(item_code=item_code,
+			warehouse = warehouse, qty=5, rate=200, do_not_submit=1)
+		sr.save(ignore_permissions=True)
+		sr.submit()
+
+		self.assertTrue(sr.items[0].batch_no)
+		to_delete_records.append(sr.name)
+
+		sr1 = create_stock_reconciliation(item_code=item_code,
+			warehouse = warehouse, qty=6, rate=300, batch_no=sr.items[0].batch_no)
+
+		args = {
+			"item_code": item_code,
+			"warehouse": warehouse,
+			"posting_date": nowdate(),
+			"posting_time": nowtime(),
+		}
+
+		valuation_rate = get_incoming_rate(args)
+		self.assertEqual(valuation_rate, 300)
+		to_delete_records.append(sr1.name)
+
+
+		sr2 = create_stock_reconciliation(item_code=item_code,
+			warehouse = warehouse, qty=0, rate=0, batch_no=sr.items[0].batch_no)
+
+		stock_value = get_stock_value_on(warehouse, nowdate(), item_code)
+		self.assertEqual(stock_value, 0)
+		to_delete_records.append(sr2.name)
+
+		to_delete_records.reverse()
+		for d in to_delete_records:
+			stock_doc = frappe.get_doc("Stock Reconciliation", d)
+			stock_doc.cancel()
+
+		frappe.delete_doc("Batch", sr.items[0].batch_no)
+		for d in to_delete_records:
+			frappe.delete_doc("Stock Reconciliation", d)
+
+def create_batch_or_serial_no_items():
+	create_warehouse("_Test Warehouse for Stock Reco1",
+		{"is_group": 0, "parent_warehouse": "_Test Warehouse Group - _TC"})
+
+	create_warehouse("_Test Warehouse for Stock Reco2",
+		{"is_group": 0, "parent_warehouse": "_Test Warehouse Group - _TC"})
+
+	serial_item_doc = create_item("Stock-Reco-Serial-Item-1", is_stock_item=1)
+	if not serial_item_doc.has_serial_no:
+		serial_item_doc.has_serial_no = 1
+		serial_item_doc.serial_no_series = "SRSI.####"
+		serial_item_doc.save(ignore_permissions=True)
+
+	batch_item_doc = create_item("Stock-Reco-batch-Item-1", is_stock_item=1)
+	if not batch_item_doc.has_batch_no:
+		batch_item_doc.has_batch_no = 1
+		batch_item_doc.create_new_batch = 1
+		serial_item_doc.batch_number_series = "BASR.#####"
+		batch_item_doc.save(ignore_permissions=True)
+
 def create_stock_reconciliation(**args):
 	args = frappe._dict(args)
 	sr = frappe.new_doc("Stock Reconciliation")
@@ -120,11 +252,14 @@
 		"item_code": args.item_code or "_Test Item",
 		"warehouse": args.warehouse or "_Test Warehouse - _TC",
 		"qty": args.qty,
-		"valuation_rate": args.rate
+		"valuation_rate": args.rate,
+		"serial_no": args.serial_no,
+		"batch_no": args.batch_no
 	})
 
 	try:
-		sr.submit()
+		if not args.do_not_submit:
+			sr.submit()
 	except EmptyStockReconciliationItemsError:
 		pass
 	return sr
@@ -140,3 +275,4 @@
 			}, allow_negative_stock=1)
 
 test_dependencies = ["Item", "Warehouse"]
+
diff --git a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
index 0fafe83..e53db07 100644
--- a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
+++ b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
@@ -1,560 +1,182 @@
 {
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2015-02-17 01:06:05.072764", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "Other", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
+ "creation": "2015-02-17 01:06:05.072764",
+ "doctype": "DocType",
+ "document_type": "Other",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "barcode",
+  "item_code",
+  "item_name",
+  "warehouse",
+  "column_break_6",
+  "qty",
+  "valuation_rate",
+  "amount",
+  "serial_no_and_batch_section",
+  "serial_no",
+  "column_break_11",
+  "batch_no",
+  "section_break_3",
+  "current_qty",
+  "current_serial_no",
+  "column_break_9",
+  "current_valuation_rate",
+  "current_amount",
+  "section_break_14",
+  "quantity_difference",
+  "column_break_16",
+  "amount_difference"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "barcode", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Barcode", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "barcode",
+   "fieldtype": "Data",
+   "label": "Barcode",
+   "print_hide": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 3, 
-   "fieldname": "item_code", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 1, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Item Code", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Item", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "columns": 3,
+   "fieldname": "item_code",
+   "fieldtype": "Link",
+   "in_global_search": 1,
+   "in_list_view": 1,
+   "label": "Item Code",
+   "options": "Item",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "item_name", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 1, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Item Name", 
-   "length": 0, 
-   "no_copy": 1, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "item_name",
+   "fieldtype": "Data",
+   "in_global_search": 1,
+   "label": "Item Name",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 3, 
-   "fieldname": "warehouse", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Warehouse", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Warehouse", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "columns": 3,
+   "fieldname": "warehouse",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Warehouse",
+   "options": "Warehouse",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_6", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_6",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 2, 
-   "description": "", 
-   "fieldname": "qty", 
-   "fieldtype": "Float", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Quantity", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "columns": 2,
+   "fieldname": "qty",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Quantity"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 2, 
-   "description": "", 
-   "fieldname": "valuation_rate", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Valuation Rate", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Company:company:default_currency", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "columns": 2,
+   "fieldname": "valuation_rate",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Valuation Rate",
+   "options": "Company:company:default_currency"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "amount", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Amount", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Company:company:default_currency", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "amount",
+   "fieldtype": "Currency",
+   "label": "Amount",
+   "options": "Company:company:default_currency",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "section_break_3", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Before reconciliation", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "serial_no_and_batch_section",
+   "fieldtype": "Section Break",
+   "label": "Serial No and Batch"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "description": "", 
-   "fieldname": "current_qty", 
-   "fieldtype": "Float", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Current Qty", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "serial_no",
+   "fieldtype": "Small Text",
+   "label": "Serial No"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_9", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_11",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "description": "", 
-   "fieldname": "current_valuation_rate", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Current Valuation Rate", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Company:company:default_currency", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "section_break_3",
+   "fieldtype": "Section Break",
+   "label": "Before reconciliation"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "description": "", 
-   "fieldname": "current_amount", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Current Amount", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Company:company:default_currency", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "default": "0",
+   "fieldname": "current_qty",
+   "fieldtype": "Float",
+   "label": "Current Qty",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "section_break_14", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "current_serial_no",
+   "fieldtype": "Small Text",
+   "label": "Current Serial No",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "quantity_difference", 
-   "fieldtype": "Read Only", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Quantity Difference", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_9",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_16", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "current_valuation_rate",
+   "fieldtype": "Currency",
+   "label": "Current Valuation Rate",
+   "options": "Company:company:default_currency",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "amount_difference", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Amount Difference", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Company:company:default_currency", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
+   "fieldname": "current_amount",
+   "fieldtype": "Currency",
+   "label": "Current Amount",
+   "options": "Company:company:default_currency",
+   "read_only": 1
+  },
+  {
+   "fieldname": "section_break_14",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "quantity_difference",
+   "fieldtype": "Read Only",
+   "label": "Quantity Difference"
+  },
+  {
+   "fieldname": "column_break_16",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "amount_difference",
+   "fieldtype": "Currency",
+   "label": "Amount Difference",
+   "options": "Company:company:default_currency",
+   "read_only": 1
+  },
+  {
+   "fieldname": "batch_no",
+   "fieldtype": "Link",
+   "label": "Batch No",
+   "options": "Batch"
   }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "menu_index": 0, 
- "modified": "2017-08-03 00:03:40.412071", 
- "modified_by": "Administrator", 
- "module": "Stock", 
- "name": "Stock Reconciliation Item", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 1, 
- "track_seen": 0
+ ],
+ "istable": 1,
+ "modified": "2019-06-14 17:10:53.188305",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Stock Reconciliation Item",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
index e7cb9ad..7f7835f 100644
--- a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
+++ b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
@@ -55,10 +55,12 @@
 #get all details
 def get_stock_ledger_entries(filters):
 	conditions = get_conditions(filters)
-	return frappe.db.sql("""select item_code, batch_no, warehouse,
-		posting_date, actual_qty
+	return frappe.db.sql("""
+		select item_code, batch_no, warehouse, posting_date, sum(actual_qty) as actual_qty
 		from `tabStock Ledger Entry`
-		where docstatus < 2 and ifnull(batch_no, '') != '' %s order by item_code, warehouse""" %
+		where docstatus < 2 and ifnull(batch_no, '') != '' %s
+		group by voucher_no, batch_no, item_code, warehouse
+		order by item_code, warehouse""" %
 		conditions, as_dict=1)
 
 def get_item_warehouse_batch_map(filters, float_precision):
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index c8706b2..ff5b026 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -157,9 +157,12 @@
 		if sle.serial_no:
 			self.get_serialized_values(sle)
 			self.qty_after_transaction += flt(sle.actual_qty)
+			if sle.voucher_type == "Stock Reconciliation":
+				self.qty_after_transaction = sle.qty_after_transaction
+
 			self.stock_value = flt(self.qty_after_transaction) * flt(self.valuation_rate)
 		else:
-			if sle.voucher_type=="Stock Reconciliation":
+			if sle.voucher_type=="Stock Reconciliation" and not sle.batch_no:
 				# assert
 				self.valuation_rate = sle.valuation_rate
 				self.qty_after_transaction = sle.qty_after_transaction
@@ -371,7 +374,7 @@
 		"""get Stock Ledger Entries after a particular datetime, for reposting"""
 		return get_stock_ledger_entries(self.previous_sle or frappe._dict({
 				"item_code": self.args.get("item_code"), "warehouse": self.args.get("warehouse") }),
-			">", "asc", for_update=True)
+			">", "asc", for_update=True, check_serial_no=False)
 
 	def raise_exceptions(self):
 		deficiency = min(e["diff"] for e in self.exceptions)
@@ -412,7 +415,8 @@
 	sle = get_stock_ledger_entries(args, "<=", "desc", "limit 1", for_update=for_update)
 	return sle and sle[0] or {}
 
-def get_stock_ledger_entries(previous_sle, operator=None, order="desc", limit=None, for_update=False, debug=False):
+def get_stock_ledger_entries(previous_sle, operator=None,
+	order="desc", limit=None, for_update=False, debug=False, check_serial_no=True):
 	"""get stock ledger entries filtered by specific posting datetime conditions"""
 	conditions = " and timestamp(posting_date, posting_time) {0} timestamp(%(posting_date)s, %(posting_time)s)".format(operator)
 	if previous_sle.get("warehouse"):
@@ -420,6 +424,9 @@
 	elif previous_sle.get("warehouse_condition"):
 		conditions += " and " + previous_sle.get("warehouse_condition")
 
+	if check_serial_no and previous_sle.get("serial_no"):
+		conditions += " and serial_no like {}".format(frappe.db.escape('%{0}%'.format(previous_sle.get("serial_no"))))
+
 	if not previous_sle.get("posting_date"):
 		previous_sle["posting_date"] = "1900-01-01"
 	if not previous_sle.get("posting_time"):
@@ -479,6 +486,7 @@
 	if not allow_zero_rate and not valuation_rate and raise_error_if_no_rate \
 			and cint(erpnext.is_perpetual_inventory_enabled(company)):
 		frappe.local.message_log = []
-		frappe.throw(_("Valuation rate not found for the Item {0}, which is required to do accounting entries for {1} {2}. If the item is transacting as a zero valuation rate item in the {1}, please mention that in the {1} Item table. Otherwise, please create an incoming stock transaction for the item or mention valuation rate in the Item record, and then try submiting/cancelling this entry").format(item_code, voucher_type, voucher_no))
+		frappe.throw(_("Valuation rate not found for the Item {0}, which is required to do accounting entries for {1} {2}. If the item is transacting as a zero valuation rate item in the {1}, please mention that in the {1} Item table. Otherwise, please create an incoming stock transaction for the item or mention valuation rate in the Item record, and then try submiting / cancelling this entry.")
+			.format(item_code, voucher_type, voucher_no))
 
 	return valuation_rate
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index 76631fa..6ea3228 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -173,7 +173,7 @@
 		in_rate = get_valuation_rate(args.get('item_code'), args.get('warehouse'),
 			args.get('voucher_type'), voucher_no, args.get('allow_zero_valuation'),
 			currency=erpnext.get_company_currency(args.get('company')), company=args.get('company'),
-			raise_error_if_no_rate=True)
+			raise_error_if_no_rate=raise_error_if_no_rate)
 
 	return in_rate
 
@@ -277,3 +277,7 @@
 					new_row.append(None)
 
 		result[row_idx] = new_row
+
+def get_available_serial_nos(item_code, warehouse):
+	return frappe.get_all("Serial No", filters = {'item_code': item_code,
+		'warehouse': warehouse, 'delivery_document_no': ''}) or []
\ No newline at end of file