Merge branch 'develop' into updated-requirements
diff --git a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py
index 716bef3..43acded 100644
--- a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py
+++ b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py
@@ -93,7 +93,8 @@
 		fields = ['posting_date', 'debit', 'credit'],
 		filters = [
 			dict(posting_date = ('<', to_date)),
-			dict(account = ('in', child_accounts))
+			dict(account = ('in', child_accounts)),
+			dict(voucher_type = ('!=', 'Period Closing Voucher'))
 		],
 		order_by = 'posting_date asc')
 
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 0ebca8b..fefd36a 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -991,10 +991,8 @@
 				continue
 
 			for serial_no in item.serial_no.split("\n"):
-				if serial_no and frappe.db.exists('Serial No', serial_no):
-					sno = frappe.get_doc('Serial No', serial_no)
-					sno.sales_invoice = invoice
-					sno.db_update()
+				if serial_no and frappe.db.get_value('Serial No', serial_no, 'item_code') == item.item_code:
+					frappe.db.set_value('Serial No', serial_no, 'sales_invoice', invoice)
 
 	def validate_serial_numbers(self):
 		"""
@@ -1040,8 +1038,9 @@
 				continue
 
 			for serial_no in item.serial_no.split("\n"):
-				sales_invoice = frappe.db.get_value("Serial No", serial_no, "sales_invoice")
-				if sales_invoice and self.name != sales_invoice:
+				sales_invoice, item_code = frappe.db.get_value("Serial No", serial_no,
+					["sales_invoice", "item_code"])
+				if sales_invoice and item_code == item.item_code and self.name != sales_invoice:
 					sales_invoice_company = frappe.db.get_value("Sales Invoice", sales_invoice, "company")
 					if sales_invoice_company == self.company:
 						frappe.throw(_("Serial Number: {0} is already referenced in Sales Invoice: {1}"
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index d4dac72..38f283c 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -163,9 +163,16 @@
 						.format(account), StockAccountInvalidTransaction)
 
 			elif account_bal != stock_bal:
-				frappe.throw(_("Account Balance ({0}) and Stock Value ({1}) is out of sync for account {2} and linked warehouse ({3}). Please create adjustment Journal Entry for amount {4}.")
-					.format(account_bal, stock_bal, account, comma_and(warehouse_list), stock_bal - account_bal),
-					StockValueAndAccountBalanceOutOfSync)
+				error_reason = _("Account Balance ({0}) and Stock Value ({1}) is out of sync for account {2} and it's linked warehouses.").format(
+					account_bal, stock_bal, frappe.bold(account))
+				error_resolution = _("Please create adjustment Journal Entry for amount {0} ").format(frappe.bold(stock_bal - account_bal))
+				button_text = _("Make Adjustment Entry")
+
+				frappe.throw("""{0}<br></br>{1}<br></br>
+					<div style="text-align:right;">
+					<button class="btn btn-primary" onclick="frappe.new_doc('Journal Entry')">{2}</button>
+					</div>""".format(error_reason, error_resolution, button_text),
+					StockValueAndAccountBalanceOutOfSync, title=_('Account Balance Out Of Sync'))
 
 def validate_cwip_accounts(gl_map):
 	cwip_enabled = cint(frappe.get_cached_value("Company",
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index bcbd427..14906f2 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -188,7 +188,11 @@
 		self.data.append(row)
 
 	def set_invoice_details(self, row):
-		row.update(self.invoice_details.get(row.voucher_no, {}))
+		invoice_details = self.invoice_details.get(row.voucher_no, {})
+		if row.due_date:
+			invoice_details.pop("due_date", None)
+		row.update(invoice_details)
+
 		if row.voucher_type == 'Sales Invoice':
 			if self.filters.show_delivery_notes:
 				self.set_delivery_notes(row)
diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
index b90a7a9..8955830 100644
--- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
+++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
@@ -36,6 +36,9 @@
 			self.filters.report_date) or {}
 
 		for party, party_dict in iteritems(self.party_total):
+			if party_dict.outstanding <= 0:
+				continue
+
 			row = frappe._dict()
 
 			row.party = party
diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.js b/erpnext/accounts/report/balance_sheet/balance_sheet.js
index 4bc29da..8c11514 100644
--- a/erpnext/accounts/report/balance_sheet/balance_sheet.js
+++ b/erpnext/accounts/report/balance_sheet/balance_sheet.js
@@ -2,7 +2,7 @@
 // License: GNU General Public License v3. See license.txt
 
 frappe.require("assets/erpnext/js/financial_statements.js", function() {
-	frappe.query_reports["Balance Sheet"] = erpnext.financial_statements;
+	frappe.query_reports["Balance Sheet"] = $.extend({}, erpnext.financial_statements);
 
 	frappe.query_reports["Balance Sheet"]["filters"].push({
 		"fieldname": "accumulated_values",
diff --git a/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py b/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py
index bb9045c..3e51933 100644
--- a/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py
+++ b/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py
@@ -51,27 +51,25 @@
 		self.create_task(disease_doc.treatment_task, self.name, start_date)
 
 	def create_project(self, period, crop_tasks):
-		project = frappe.new_doc("Project")
-		project.update({
+		project = frappe.get_doc({
+			"doctype": "Project",
 			"project_name": self.title,
 			"expected_start_date": self.start_date,
 			"expected_end_date": add_days(self.start_date, period - 1)
-		})
-		project.insert()
+		}).insert()
 
 		return project.name
 
 	def create_task(self, crop_tasks, project_name, start_date):
 		for crop_task in crop_tasks:
-			task = frappe.new_doc("Task")
-			task.update({
+			frappe.get_doc({
+				"doctype": "Task",
 				"subject": crop_task.get("task_name"),
 				"priority": crop_task.get("priority"),
 				"project": project_name,
 				"exp_start_date": add_days(start_date, crop_task.get("start_day") - 1),
 				"exp_end_date": add_days(start_date, crop_task.get("end_day") - 1)
-			})
-			task.insert()
+			}).insert()
 
 	def reload_linked_analysis(self):
 		linked_doctypes = ['Soil Texture', 'Soil Analysis', 'Plant Analysis']
diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json
index 8fda330..6882f6a 100644
--- a/erpnext/assets/doctype/asset/asset.json
+++ b/erpnext/assets/doctype/asset/asset.json
@@ -33,6 +33,7 @@
   "available_for_use_date",
   "column_break_18",
   "calculate_depreciation",
+  "allow_monthly_depreciation",
   "is_existing_asset",
   "opening_accumulated_depreciation",
   "number_of_depreciations_booked",
@@ -216,8 +217,7 @@
   {
    "fieldname": "available_for_use_date",
    "fieldtype": "Date",
-   "label": "Available-for-use Date",
-   "reqd": 1
+   "label": "Available-for-use Date"
   },
   {
    "fieldname": "column_break_18",
@@ -450,12 +450,19 @@
   {
    "fieldname": "dimension_col_break",
    "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "depends_on": "calculate_depreciation",
+   "fieldname": "allow_monthly_depreciation",
+   "fieldtype": "Check",
+   "label": "Allow Monthly Depreciation"
   }
  ],
  "idx": 72,
  "image_field": "image",
  "is_submittable": 1,
- "modified": "2019-10-07 15:34:30.976208",
+ "modified": "2019-10-22 15:47:36.050828",
  "modified_by": "Administrator",
  "module": "Assets",
  "name": "Asset",
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 94e6f61..d1f8c1a 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -6,7 +6,7 @@
 import frappe, erpnext, math, json
 from frappe import _
 from six import string_types
-from frappe.utils import flt, add_months, cint, nowdate, getdate, today, date_diff, add_days
+from frappe.utils import flt, add_months, cint, nowdate, getdate, today, date_diff, month_diff, add_days
 from frappe.model.document import Document
 from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
 from erpnext.assets.doctype.asset.depreciation \
@@ -149,19 +149,31 @@
 					schedule_date = add_months(d.depreciation_start_date,
 						n * cint(d.frequency_of_depreciation))
 
+					# schedule date will be a year later from start date
+					# so monthly schedule date is calculated by removing 11 months from it
+					monthly_schedule_date = add_months(schedule_date, - d.frequency_of_depreciation + 1)
+
 				# For first row
 				if has_pro_rata and n==0:
-					depreciation_amount, days = get_pro_rata_amt(d, depreciation_amount,
+					depreciation_amount, days, months = get_pro_rata_amt(d, depreciation_amount,
 						self.available_for_use_date, d.depreciation_start_date)
+					
+					# For first depr schedule date will be the start date
+					# so monthly schedule date is calculated by removing month difference between use date and start date
+					monthly_schedule_date = add_months(d.depreciation_start_date, - months + 1)
+
 				# For last row
 				elif has_pro_rata and n == cint(number_of_pending_depreciations) - 1:
 					to_date = add_months(self.available_for_use_date,
 						n * cint(d.frequency_of_depreciation))
 
-					depreciation_amount, days = get_pro_rata_amt(d,
+					depreciation_amount, days, months = get_pro_rata_amt(d,
 						depreciation_amount, schedule_date, to_date)
 
+					monthly_schedule_date = add_months(schedule_date, 1)
+
 					schedule_date = add_days(schedule_date, days)
+					last_schedule_date = schedule_date
 
 				if not depreciation_amount: continue
 				value_after_depreciation -= flt(depreciation_amount,
@@ -175,13 +187,50 @@
 					skip_row = True
 
 				if depreciation_amount > 0:
-					self.append("schedules", {
-						"schedule_date": schedule_date,
-						"depreciation_amount": depreciation_amount,
-						"depreciation_method": d.depreciation_method,
-						"finance_book": d.finance_book,
-						"finance_book_id": d.idx
-					})
+					# With monthly depreciation, each depreciation is divided by months remaining until next date
+					if self.allow_monthly_depreciation:
+						# month range is 1 to 12
+						# In pro rata case, for first and last depreciation, month range would be different
+						month_range = months \
+							if (has_pro_rata and n==0) or (has_pro_rata and n == cint(number_of_pending_depreciations) - 1) \
+							else d.frequency_of_depreciation
+
+						for r in range(month_range):
+							if (has_pro_rata and n == 0):
+								# For first entry of monthly depr
+								if r == 0:
+									days_until_first_depr = date_diff(monthly_schedule_date, self.available_for_use_date)
+									per_day_amt = depreciation_amount / days
+									depreciation_amount_for_current_month = per_day_amt * days_until_first_depr
+									depreciation_amount -= depreciation_amount_for_current_month
+									date = monthly_schedule_date
+									amount = depreciation_amount_for_current_month
+								else:
+									date = add_months(monthly_schedule_date, r)
+									amount = depreciation_amount / (month_range - 1)
+							elif (has_pro_rata and n == cint(number_of_pending_depreciations) - 1) and r == cint(month_range) - 1:
+								# For last entry of monthly depr
+								date = last_schedule_date
+								amount = depreciation_amount / month_range
+							else:
+								date = add_months(monthly_schedule_date, r)
+								amount = depreciation_amount / month_range
+							
+							self.append("schedules", {
+								"schedule_date": date,
+								"depreciation_amount": amount,
+								"depreciation_method": d.depreciation_method,
+								"finance_book": d.finance_book,
+								"finance_book_id": d.idx
+							})
+					else:
+						self.append("schedules", {
+							"schedule_date": schedule_date,
+							"depreciation_amount": depreciation_amount,
+							"depreciation_method": d.depreciation_method,
+							"finance_book": d.finance_book,
+							"finance_book_id": d.idx
+						})
 
 	def check_is_pro_rata(self, row):
 		has_pro_rata = False
@@ -588,9 +637,10 @@
 
 def get_pro_rata_amt(row, depreciation_amount, from_date, to_date):
 	days = date_diff(to_date, from_date)
+	months = month_diff(to_date, from_date)
 	total_days = get_total_days(to_date, row.frequency_of_depreciation)
 
-	return (depreciation_amount * flt(days)) / flt(total_days), days
+	return (depreciation_amount * flt(days)) / flt(total_days), days, months
 
 def get_total_days(date, frequency):
 	period_start_date = add_months(date,
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
index 9ad06f9..2f0cfa6 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
@@ -134,7 +134,7 @@
 			if (args.search_type === "Tag" && args.tag) {
 				return frappe.call({
 					type: "GET",
-					method: "frappe.desk.tags.get_tagged_docs",
+					method: "frappe.desk.doctype.tag.tag.get_tagged_docs",
 					args: {
 						"doctype": "Supplier",
 						"tag": args.tag
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
index a10ce46..95db33b 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
@@ -344,13 +344,9 @@
 
 @frappe.whitelist()
 def get_supplier_tag():
-	data = frappe.db.sql("select _user_tags from `tabSupplier`")
+	if not frappe.cache().hget("Supplier", "Tags"):
+		filters = {"document_type": "Supplier"}
+		tags = list(set([tag.tag for tag in frappe.get_all("Tag Link", filters=filters, fields=["tag"]) if tag]))
+		frappe.cache().hset("Supplier", "Tags", tags)
 
-	tags = []
-	for tag in data:
-		tags += filter(bool, tag[0].split(","))
-
-	tags = list(set(tags))
-
-	return tags
-
+	return frappe.cache().hget("Supplier", "Tags")
diff --git a/erpnext/hr/doctype/department_approver/department_approver.py b/erpnext/hr/doctype/department_approver/department_approver.py
index 9f2f201..d6b66da 100644
--- a/erpnext/hr/doctype/department_approver/department_approver.py
+++ b/erpnext/hr/doctype/department_approver/department_approver.py
@@ -19,14 +19,20 @@
 	approvers = []
 	department_details = {}
 	department_list = []
-	employee_department = filters.get("department") or frappe.get_value("Employee", filters.get("employee"), "department")
+	employee = frappe.get_value("Employee", filters.get("employee"), ["department", "leave_approver"], as_dict=True)
+	if employee.leave_approver:
+		approver = frappe.db.get_value("User", employee.leave_approver, ['name', 'first_name', 'last_name'])
+		approvers.append(approver)
+		return approvers
+
+	employee_department = filters.get("department") or employee.department
 	if employee_department:
 		department_details = frappe.db.get_value("Department", {"name": employee_department}, ["lft", "rgt"], as_dict=True)
 	if department_details:
 		department_list = frappe.db.sql("""select name from `tabDepartment` where lft <= %s
 			and rgt >= %s
 			and disabled=0
-			order by lft desc""", (department_details.lft, department_details.rgt), as_list = True)
+			order by lft desc""", (department_details.lft, department_details.rgt), as_list=True)
 
 	if filters.get("doctype") == "Leave Application":
 		parentfield = "leave_approvers"
@@ -41,4 +47,4 @@
 				and approver.parentfield = %s
 				and approver.approver=user.name""",(d, "%" + txt + "%", parentfield), as_list=True)
 
-	return approvers
\ No newline at end of file
+	return approvers
diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py
index e1e5e80..0e66305 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.py
+++ b/erpnext/hr/doctype/leave_application/leave_application.py
@@ -55,11 +55,11 @@
 		self.reload()
 
 	def on_cancel(self):
+		self.create_leave_ledger_entry(submit=False)
 		self.status = "Cancelled"
 		# notify leave applier about cancellation
 		self.notify_employee()
 		self.cancel_attendance()
-		self.create_leave_ledger_entry(submit=False)
 
 	def validate_applicable_after(self):
 		if self.leave_type:
@@ -351,6 +351,9 @@
 				pass
 
 	def create_leave_ledger_entry(self, submit=True):
+		if self.status != 'Approved':
+			return
+
 		expiry_date = get_allocation_expiry(self.employee, self.leave_type,
 			self.to_date, self.from_date)
 
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js
index 5198937..3b24d0f 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.js
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js
@@ -3,6 +3,11 @@
 
 frappe.ui.form.on('Production Plan', {
 	setup: function(frm) {
+		frm.custom_make_buttons = {
+			'Work Order': 'Work Order',
+			'Material Request': 'Material Request',
+		};
+
 		frm.fields_dict['po_items'].grid.get_field('warehouse').get_query = function(doc) {
 			return {
 				filters: {
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js
index cdbce33..107c79b 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.js
+++ b/erpnext/manufacturing/doctype/work_order/work_order.js
@@ -395,6 +395,11 @@
 				}
 			});
 		}
+	},
+
+	additional_operating_cost: function(frm) {
+		erpnext.work_order.calculate_cost(frm.doc);
+		erpnext.work_order.calculate_total_cost(frm);
 	}
 });
 
@@ -534,8 +539,7 @@
 	},
 
 	calculate_total_cost: function(frm) {
-		var variable_cost = frm.doc.actual_operating_cost ?
-			flt(frm.doc.actual_operating_cost) : flt(frm.doc.planned_operating_cost);
+		let variable_cost = flt(frm.doc.actual_operating_cost) || flt(frm.doc.planned_operating_cost);
 		frm.set_value("total_operating_cost", (flt(frm.doc.additional_operating_cost) + variable_cost));
 	},
 
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index ae4d9be..089cb80 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -216,14 +216,24 @@
 			self.db_set(fieldname, qty)
 
 			from erpnext.selling.doctype.sales_order.sales_order import update_produced_qty_in_so_item
-			update_produced_qty_in_so_item(self.sales_order_item)
+
+			if self.sales_order and self.sales_order_item:
+				update_produced_qty_in_so_item(self.sales_order, self.sales_order_item)
 
 		if self.production_plan:
 			self.update_production_plan_status()
 
 	def update_production_plan_status(self):
 		production_plan = frappe.get_doc('Production Plan', self.production_plan)
-		production_plan.run_method("update_produced_qty", self.produced_qty, self.production_plan_item)
+		produced_qty = 0
+		if self.production_plan_item:
+			total_qty = frappe.get_all("Work Order", fields = "sum(produced_qty) as produced_qty",
+				filters = {'docstatus': 1, 'production_plan': self.production_plan,
+					'production_plan_item': self.production_plan_item}, as_list=1)
+
+			produced_qty = total_qty[0][0] if total_qty else 0
+
+		production_plan.run_method("update_produced_qty", produced_qty, self.production_plan_item)
 
 	def on_submit(self):
 		if not self.wip_warehouse:
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 0155b27..9e4dc12 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -638,11 +638,11 @@
 erpnext.patches.v12_0.rename_bank_account_field_in_journal_entry_account
 erpnext.patches.v12_0.create_default_energy_point_rules
 erpnext.patches.v12_0.set_produced_qty_field_in_sales_order_for_work_order
-erpnext.patches.v12_0.generate_leave_ledger_entries
 erpnext.patches.v12_0.set_default_shopify_app_type
 erpnext.patches.v12_0.set_cwip_and_delete_asset_settings
 erpnext.patches.v12_0.set_expense_account_in_landed_cost_voucher_taxes
 erpnext.patches.v12_0.replace_accounting_with_accounts_in_home_settings
 erpnext.patches.v12_0.set_payment_entry_status
 erpnext.patches.v12_0.update_owner_fields_in_acc_dimension_custom_fields
-erpnext.patches.v12_0.set_default_for_add_taxes_from_item_tax_template
\ No newline at end of file
+erpnext.patches.v12_0.set_default_for_add_taxes_from_item_tax_template
+erpnext.patches.v12_0.remove_denied_leaves_from_leave_ledger
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py b/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py
index 412f320..f25b9ea 100644
--- a/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py
+++ b/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py
@@ -1,20 +1,30 @@
 import frappe
 import json
 from six import iteritems
+from frappe.model.naming import make_autoname
 
 def execute():
 	if "tax_type" not in frappe.db.get_table_columns("Item Tax"):
 		return
 	old_item_taxes = {}
 	item_tax_templates = {}
-	rename_template_to_untitled = []
+
+	frappe.reload_doc("accounts", "doctype", "item_tax_template_detail", force=1)
+	frappe.reload_doc("accounts", "doctype", "item_tax_template", force=1)
+	existing_templates = frappe.db.sql("""select template.name, details.tax_type, details.tax_rate
+		from `tabItem Tax Template` template, `tabItem Tax Template Detail` details
+		where details.parent=template.name
+		""", as_dict=1)
+
+	if len(existing_templates):
+		for d in existing_templates:
+			item_tax_templates.setdefault(d.name, {})
+			item_tax_templates[d.name][d.tax_type] = d.tax_rate
 
 	for d in frappe.db.sql("""select parent as item_code, tax_type, tax_rate from `tabItem Tax`""", as_dict=1):
 		old_item_taxes.setdefault(d.item_code, [])
 		old_item_taxes[d.item_code].append(d)
 
-	frappe.reload_doc("accounts", "doctype", "item_tax_template_detail", force=1)
-	frappe.reload_doc("accounts", "doctype", "item_tax_template", force=1)
 	frappe.reload_doc("stock", "doctype", "item", force=1)
 	frappe.reload_doc("stock", "doctype", "item_tax", force=1)
 	frappe.reload_doc("selling", "doctype", "quotation_item", force=1)
@@ -27,6 +37,8 @@
 	frappe.reload_doc("accounts", "doctype", "purchase_invoice_item", force=1)
 	frappe.reload_doc("accounts", "doctype", "accounts_settings", force=1)
 
+	frappe.db.auto_commit_on_many_writes = True
+
 	# for each item that have item tax rates
 	for item_code in old_item_taxes.keys():
 		# make current item's tax map
@@ -34,8 +46,7 @@
 		for d in old_item_taxes[item_code]:
 			item_tax_map[d.tax_type] = d.tax_rate
 
-		item_tax_template_name = get_item_tax_template(item_tax_templates, rename_template_to_untitled,
-			item_tax_map, item_code)
+		item_tax_template_name = get_item_tax_template(item_tax_templates, item_tax_map, item_code)
 
 		# update the item tax table
 		item = frappe.get_doc("Item", item_code)
@@ -49,35 +60,33 @@
 		'Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice',
 		'Supplier Quotation', 'Purchase Order', 'Purchase Receipt', 'Purchase Invoice'
 	]
+	
 	for dt in doctypes:
 		for d in frappe.db.sql("""select name, parent, item_code, item_tax_rate from `tab{0} Item`
-								where ifnull(item_tax_rate, '') not in ('', '{{}}')""".format(dt), as_dict=1):
+								where ifnull(item_tax_rate, '') not in ('', '{{}}') 
+								and item_tax_template is NULL""".format(dt), as_dict=1):
 			item_tax_map = json.loads(d.item_tax_rate)
-			item_tax_template = get_item_tax_template(item_tax_templates, rename_template_to_untitled,
+			item_tax_template_name = get_item_tax_template(item_tax_templates,
 				item_tax_map, d.item_code, d.parent)
-			frappe.db.set_value(dt + " Item", d.name, "item_tax_template", item_tax_template)
+			frappe.db.set_value(dt + " Item", d.name, "item_tax_template", item_tax_template_name)
 
-	idx = 1
-	for oldname in rename_template_to_untitled:
-		frappe.rename_doc("Item Tax Template", oldname, "Untitled {}".format(idx))
-		idx += 1
+	frappe.db.auto_commit_on_many_writes = False
 
 	settings = frappe.get_single("Accounts Settings")
 	settings.add_taxes_from_item_tax_template = 0
 	settings.determine_address_tax_category_from = "Billing Address"
 	settings.save()
 
-def get_item_tax_template(item_tax_templates, rename_template_to_untitled, item_tax_map, item_code, parent=None):
+def get_item_tax_template(item_tax_templates, item_tax_map, item_code, parent=None):
 	# search for previously created item tax template by comparing tax maps
 	for template, item_tax_template_map in iteritems(item_tax_templates):
 		if item_tax_map == item_tax_template_map:
-			if not parent:
-				rename_template_to_untitled.append(template)
 			return template
 
 	# if no item tax template found, create one
 	item_tax_template = frappe.new_doc("Item Tax Template")
-	item_tax_template.title = "{}--{}".format(parent, item_code) if parent else "Item-{}".format(item_code)
+	item_tax_template.title = make_autoname("Item Tax Template-.####")
+
 	for tax_type, tax_rate in iteritems(item_tax_map):
 		if not frappe.db.exists("Account", tax_type):
 			parts = tax_type.strip().split(" - ")
diff --git a/erpnext/patches/v12_0/remove_denied_leaves_from_leave_ledger.py b/erpnext/patches/v12_0/remove_denied_leaves_from_leave_ledger.py
new file mode 100644
index 0000000..7859606
--- /dev/null
+++ b/erpnext/patches/v12_0/remove_denied_leaves_from_leave_ledger.py
@@ -0,0 +1,28 @@
+# Copyright (c) 2018, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.utils import getdate, today
+
+def execute():
+	''' Delete leave ledger entry created
+		via leave applications with status != Approved '''
+	if not frappe.db.a_row_exists("Leave Ledger Entry"):
+		return
+
+	leave_application_list = get_denied_leave_application_list()
+	if leave_application_list:
+		delete_denied_leaves_from_leave_ledger_entry(leave_application_list)
+
+def get_denied_leave_application_list():
+	return frappe.db.sql_list(''' Select name from `tabLeave Application` where status <> 'Approved' ''')
+
+def delete_denied_leaves_from_leave_ledger_entry(leave_application_list):
+	if leave_application_list:
+		frappe.db.sql(''' Delete
+			FROM `tabLeave Ledger Entry`
+			WHERE
+				transaction_type = 'Leave Application'
+				AND transaction_name in (%s) ''' % (', '.join(['%s'] * len(leave_application_list))), #nosec
+				tuple(leave_application_list))
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/set_cwip_and_delete_asset_settings.py b/erpnext/patches/v12_0/set_cwip_and_delete_asset_settings.py
index 3d07fe5..5842e9e 100644
--- a/erpnext/patches/v12_0/set_cwip_and_delete_asset_settings.py
+++ b/erpnext/patches/v12_0/set_cwip_and_delete_asset_settings.py
@@ -9,13 +9,12 @@
 
 	if frappe.db.exists("DocType","Asset Settings"):
 		frappe.reload_doctype("Company")
-		cwip_value = frappe.db.sql(""" SELECT value FROM `tabSingles` WHERE doctype='Asset Settings'
-			and field='disable_cwip_accounting' """, as_dict=1)
+		cwip_value = frappe.db.get_single_value("Asset Settings","disable_cwip_accounting")
 
 		companies = [x['name'] for x in frappe.get_all("Company", "name")]
 		for company in companies:
-			enable_cwip_accounting = cint(not cint(cwip_value[0]['value']))
-			frappe.set_value("Company", company, "enable_cwip_accounting", enable_cwip_accounting)
+			enable_cwip_accounting = cint(not cint(cwip_value))
+			frappe.db.set_value("Company", company, "enable_cwip_accounting", enable_cwip_accounting)
 
 		frappe.db.sql(
 			""" DELETE FROM `tabSingles` where doctype = 'Asset Settings' """)
diff --git a/erpnext/patches/v12_0/set_produced_qty_field_in_sales_order_for_work_order.py b/erpnext/patches/v12_0/set_produced_qty_field_in_sales_order_for_work_order.py
index 44d8fa7..0702673 100644
--- a/erpnext/patches/v12_0/set_produced_qty_field_in_sales_order_for_work_order.py
+++ b/erpnext/patches/v12_0/set_produced_qty_field_in_sales_order_for_work_order.py
@@ -3,8 +3,12 @@
 from erpnext.selling.doctype.sales_order.sales_order import update_produced_qty_in_so_item
 
 def execute():
-    frappe.reload_doctype('Sales Order Item')
-    frappe.reload_doctype('Sales Order')
-    sales_order_items = frappe.db.get_all('Sales Order Item', ['name'])
-    for so_item in sales_order_items:
-        update_produced_qty_in_so_item(so_item.get('name'))
\ No newline at end of file
+	frappe.reload_doctype('Sales Order Item')
+	frappe.reload_doctype('Sales Order')
+
+	for d in frappe.get_all('Work Order',
+		fields = ['sales_order', 'sales_order_item'],
+		filters={'sales_order': ('!=', ''), 'sales_order_item': ('!=', '')}):
+
+		# update produced qty in sales order
+		update_produced_qty_in_so_item(d.sales_order, d.sales_order_item)
\ No newline at end of file
diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py
index 90e9f05..54fce8d 100755
--- a/erpnext/projects/doctype/task/task.py
+++ b/erpnext/projects/doctype/task/task.py
@@ -10,6 +10,7 @@
 from frappe.utils import add_days, cstr, date_diff, get_link_to_form, getdate
 from frappe.utils.nestedset import NestedSet
 from frappe.desk.form.assign_to import close_all_assignments, clear
+from frappe.utils import date_diff
 
 class CircularReferenceError(frappe.ValidationError): pass
 class EndDateCannotBeGreaterThanProjectEndDateError(frappe.ValidationError): pass
@@ -28,16 +29,29 @@
 
 	def validate(self):
 		self.validate_dates()
+		self.validate_parent_project_dates()
 		self.validate_progress()
 		self.validate_status()
 		self.update_depends_on()
 
 	def validate_dates(self):
 		if self.exp_start_date and self.exp_end_date and getdate(self.exp_start_date) > getdate(self.exp_end_date):
-			frappe.throw(_("'Expected Start Date' can not be greater than 'Expected End Date'"))
+			frappe.throw(_("{0} can not be greater than {1}").format(frappe.bold("Expected Start Date"), \
+				frappe.bold("Expected End Date")))
 
 		if self.act_start_date and self.act_end_date and getdate(self.act_start_date) > getdate(self.act_end_date):
-			frappe.throw(_("'Actual Start Date' can not be greater than 'Actual End Date'"))
+			frappe.throw(_("{0} can not be greater than {1}").format(frappe.bold("Actual Start Date"), \
+				frappe.bold("Actual End Date")))
+
+	def validate_parent_project_dates(self):
+		if not self.project or frappe.flags.in_test:
+			return
+
+		expected_end_date = getdate(frappe.db.get_value("Project", self.project, "expected_end_date"))
+
+		if expected_end_date:
+			validate_project_dates(expected_end_date, self, "exp_start_date", "exp_end_date", "Expected")
+			validate_project_dates(expected_end_date, self, "act_start_date", "act_end_date", "Actual")
 
 	def validate_status(self):
 		if self.status!=self.get_db_value("status") and self.status == "Completed":
@@ -255,3 +269,10 @@
 
 def on_doctype_update():
 	frappe.db.add_index("Task", ["lft", "rgt"])
+
+def validate_project_dates(project_end_date, task, task_start, task_end, actual_or_expected_date):
+	if task.get(task_start) and date_diff(project_end_date, getdate(task.get(task_start))) < 0:
+		frappe.throw(_("Task's {0} Start Date cannot be after Project's End Date.").format(actual_or_expected_date))
+
+	if task.get(task_end) and date_diff(project_end_date, getdate(task.get(task_end))) < 0:
+		frappe.throw(_("Task's {0} End Date cannot be after Project's End Date.").format(actual_or_expected_date))
\ No newline at end of file
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index c4c3c0f..e12b359 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -1038,14 +1038,18 @@
 
 	return doc
 
-def update_produced_qty_in_so_item(sales_order_item):
+def update_produced_qty_in_so_item(sales_order, sales_order_item):
 	#for multiple work orders against same sales order item
 	linked_wo_with_so_item = frappe.db.get_all('Work Order', ['produced_qty'], {
 		'sales_order_item': sales_order_item,
+		'sales_order': sales_order,
 		'docstatus': 1
 	})
-	if len(linked_wo_with_so_item) > 0:
-		total_produced_qty = 0
-		for wo in linked_wo_with_so_item:
-			total_produced_qty += flt(wo.get('produced_qty'))
-		frappe.db.set_value('Sales Order Item', sales_order_item, 'produced_qty', total_produced_qty)
\ No newline at end of file
+
+	total_produced_qty = 0
+	for wo in linked_wo_with_so_item:
+		total_produced_qty += flt(wo.get('produced_qty'))
+
+	if not total_produced_qty and frappe.flags.in_patch: return
+
+	frappe.db.set_value('Sales Order Item', sales_order_item, 'produced_qty', total_produced_qty)
\ No newline at end of file
diff --git a/erpnext/setup/doctype/currency_exchange/currency_exchange.py b/erpnext/setup/doctype/currency_exchange/currency_exchange.py
index 60d367a..6480f60 100644
--- a/erpnext/setup/doctype/currency_exchange/currency_exchange.py
+++ b/erpnext/setup/doctype/currency_exchange/currency_exchange.py
@@ -14,10 +14,14 @@
 		purpose = ""
 		if not self.date:
 			self.date = nowdate()
+
+		# If both selling and buying enabled
+		purpose = "Selling-Buying"
 		if cint(self.for_buying)==0 and cint(self.for_selling)==1:
 			purpose = "Selling"
 		if cint(self.for_buying)==1 and cint(self.for_selling)==0:
 			purpose = "Buying"
+
 		self.name = '{0}-{1}-{2}{3}'.format(formatdate(get_datetime_str(self.date), "yyyy-MM-dd"),
 			self.from_currency, self.to_currency, ("-" + purpose) if purpose else "")
 
diff --git a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py
index 857f666..c5c01c5 100644
--- a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py
+++ b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py
@@ -11,7 +11,9 @@
 
 def save_new_records(test_records):
 	for record in test_records:
-		purpose = str("")
+		# If both selling and buying enabled
+		purpose = "Selling-Buying"
+
 		if cint(record.get("for_buying"))==0 and cint(record.get("for_selling"))==1:
 			purpose = "Selling"
 		if cint(record.get("for_buying"))==1 and cint(record.get("for_selling"))==0:
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 98a8c59..ca2741c 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -52,9 +52,10 @@
 		def _changed(item):
 			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"))):
+
+			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 == item_dict.get("serial_nos")) )):
 				return False
 			else:
 				# set default as current rates
@@ -182,9 +183,11 @@
 		from erpnext.stock.stock_ledger import get_previous_sle
 
 		sl_entries = []
+		has_serial_no = False
 		for row in self.items:
 			item = frappe.get_doc("Item", row.item_code)
 			if item.has_serial_no or item.has_batch_no:
+				has_serial_no = True
 				self.get_sle_for_serialized_items(row, sl_entries)
 			else:
 				previous_sle = get_previous_sle({
@@ -212,8 +215,14 @@
 				sl_entries.append(self.get_sle_for_items(row))
 
 		if sl_entries:
+			if has_serial_no:
+				sl_entries = self.merge_similar_item_serial_nos(sl_entries)
+
 			self.make_sl_entries(sl_entries)
 
+		if has_serial_no and sl_entries:
+			self.update_valuation_rate_for_serial_no()
+
 	def get_sle_for_serialized_items(self, row, sl_entries):
 		from erpnext.stock.stock_ledger import get_previous_sle
 
@@ -275,8 +284,18 @@
 			# update valuation rate
 			self.update_valuation_rate_for_serial_nos(row, serial_nos)
 
+	def update_valuation_rate_for_serial_no(self):
+		for d in self.items:
+			if not d.serial_no: continue
+
+			serial_nos = get_serial_nos(d.serial_no)
+			self.update_valuation_rate_for_serial_nos(d, 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
+		if valuation_rate is None:
+			return
+
 		for d in serial_nos:
 			frappe.db.set_value("Serial No", d, 'purchase_rate', valuation_rate)
 
@@ -321,11 +340,17 @@
 			where voucher_type=%s and voucher_no=%s""", (self.doctype, self.name))
 
 		sl_entries = []
+
+		has_serial_no = False
 		for row in self.items:
 			if row.serial_no or row.batch_no or row.current_serial_no:
+				has_serial_no = True
 				self.get_sle_for_serialized_items(row, sl_entries)
 
 		if sl_entries:
+			if has_serial_no:
+				sl_entries = self.merge_similar_item_serial_nos(sl_entries)
+
 			sl_entries.reverse()
 			allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock")
 			self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock)
@@ -339,6 +364,35 @@
 				"posting_time": self.posting_time
 			})
 
+	def merge_similar_item_serial_nos(self, sl_entries):
+		# If user has put the same item in multiple row with different serial no
+		new_sl_entries = []
+		merge_similar_entries = {}
+
+		for d in sl_entries:
+			if not d.serial_no or d.actual_qty < 0:
+				new_sl_entries.append(d)
+				continue
+
+			key = (d.item_code, d.warehouse)
+			if key not in merge_similar_entries:
+				merge_similar_entries[key] = d
+			elif d.serial_no:
+				data = merge_similar_entries[key]
+				data.actual_qty += d.actual_qty
+				data.qty_after_transaction += d.qty_after_transaction
+
+				data.valuation_rate = (data.valuation_rate + d.valuation_rate) / data.actual_qty
+				data.serial_no += '\n' + d.serial_no
+
+				if data.incoming_rate:
+					data.incoming_rate = (data.incoming_rate + d.incoming_rate) / data.actual_qty
+
+		for key, value in merge_similar_entries.items():
+			new_sl_entries.append(value)
+
+		return new_sl_entries
+
 	def get_gl_entries(self, warehouse_account=None):
 		if not self.cost_center:
 			msgprint(_("Please enter Cost Center"), raise_exception=1)
@@ -456,7 +510,7 @@
 	}
 
 	serial_nos_list = [serial_no.get("name")
-			for serial_no in get_available_serial_nos(item_code, warehouse)]
+			for serial_no in get_available_serial_nos(args)]
 
 	qty = len(serial_nos_list)
 	serial_nos = '\n'.join(serial_nos_list)
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index d762917..2c6c953 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -293,9 +293,11 @@
 		row, key, value = data
 		row[key] = value
 
-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 []
+def get_available_serial_nos(args):
+	return frappe.db.sql(""" SELECT name from `tabSerial No`
+		WHERE item_code = %(item_code)s and warehouse = %(warehouse)s
+		 and timestamp(purchase_date, purchase_time) <= timestamp(%(posting_date)s, %(posting_time)s)
+	""", args, as_dict=1)
 
 def add_additional_uom_columns(columns, result, include_uom, conversion_factors):
 	if not include_uom or not conversion_factors: