Merge branch 'develop' into po-payment-terms
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index 11465b7..0844995 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -58,8 +58,8 @@
 			if not self.get(k):
 				frappe.throw(_("{0} is required").format(_(self.meta.get_label(k))))
 
-		account_type = frappe.get_cached_value("Account", self.account, "account_type")
 		if not (self.party_type and self.party):
+			account_type = frappe.get_cached_value("Account", self.account, "account_type")
 			if account_type == "Receivable":
 				frappe.throw(_("{0} {1}: Customer is required against Receivable account {2}")
 					.format(self.voucher_type, self.voucher_no, self.account))
@@ -73,15 +73,19 @@
 				.format(self.voucher_type, self.voucher_no, self.account))
 
 	def pl_must_have_cost_center(self):
-		if frappe.get_cached_value("Account", self.account, "report_type") == "Profit and Loss":
-			if not self.cost_center and self.voucher_type != 'Period Closing Voucher':
-				msg = _("{0} {1}: Cost Center is required for 'Profit and Loss' account {2}.").format(
-					self.voucher_type, self.voucher_no, self.account)
-				msg += " "
-				msg += _("Please set the cost center field in {0} or setup a default Cost Center for the Company.").format(
-					self.voucher_type)
+		"""Validate that profit and loss type account GL entries have a cost center."""
 
-				frappe.throw(msg, title=_("Missing Cost Center"))
+		if self.cost_center or self.voucher_type == 'Period Closing Voucher':
+			return
+
+		if frappe.get_cached_value("Account", self.account, "report_type") == "Profit and Loss":
+			msg = _("{0} {1}: Cost Center is required for 'Profit and Loss' account {2}.").format(
+				self.voucher_type, self.voucher_no, self.account)
+			msg += " "
+			msg += _("Please set the cost center field in {0} or setup a default Cost Center for the Company.").format(
+				self.voucher_type)
+
+			frappe.throw(msg, title=_("Missing Cost Center"))
 
 	def validate_dimensions_for_pl_and_bs(self):
 		account_type = frappe.db.get_value("Account", self.account, "report_type")
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 863c104..25f42bc 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -22,7 +22,7 @@
 from frappe.model.mapper import get_mapped_doc
 from six import iteritems
 from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_doc,\
-	unlink_inter_company_doc
+	unlink_inter_company_doc, check_if_return_invoice_linked_with_payment_entry
 from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
 from erpnext.accounts.deferred_revenue import validate_service_stop_date
 from erpnext.stock.doctype.purchase_receipt.purchase_receipt import get_item_account_wise_additional_cost
@@ -1014,6 +1014,8 @@
 				}, item=self))
 
 	def on_cancel(self):
+		check_if_return_invoice_linked_with_payment_entry(self)
+
 		super(PurchaseInvoice, self).on_cancel()
 
 		self.check_on_hold_or_closed_status()
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 8889913..cecc1a1 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -290,6 +290,8 @@
 		self.update_time_sheet(None)
 
 	def on_cancel(self):
+		check_if_return_invoice_linked_with_payment_entry(self)
+
 		super(SalesInvoice, self).on_cancel()
 
 		self.check_sales_order_on_hold_or_close("sales_order")
@@ -480,7 +482,7 @@
 		if not self.pos_profile:
 			pos_profile = get_pos_profile(self.company) or {}
 			if not pos_profile:
-				frappe.throw(_("No POS Profile found. Please create a New POS Profile first"))
+				return
 			self.pos_profile = pos_profile.get('name')
 
 		pos = {}
@@ -922,7 +924,7 @@
 						asset = frappe.get_doc("Asset", item.asset)
 					else:
 						frappe.throw(_(
-							"Row #{0}: You must select an Asset for Item {1}.").format(item.idx, item.item_name), 
+							"Row #{0}: You must select an Asset for Item {1}.").format(item.idx, item.item_name),
 							title=_("Missing Asset")
 						)
 					if (len(asset.finance_books) > 1 and not item.finance_book
@@ -944,7 +946,7 @@
 						gl_entries.append(self.get_gl_dict(gle, item=item))
 
 					self.set_asset_status(asset)
-				
+
 				else:
 					# Do not book income for transfer within same company
 					if not self.is_internal_transfer():
@@ -973,7 +975,7 @@
 	def set_asset_status(self, asset):
 		if self.is_return:
 			asset.set_status()
-		else: 	
+		else:
 			asset.set_status("Sold" if self.docstatus==1 else None)
 
 	def make_loyalty_point_redemption_gle(self, gl_entries):
@@ -1941,3 +1943,41 @@
 		}
 	}, target_doc, set_missing_values)
 	return doclist
+
+def check_if_return_invoice_linked_with_payment_entry(self):
+	# If a Return invoice is linked with payment entry along with other invoices,
+	# the cancellation of the Return causes allocated amount to be greater than paid
+
+	if not frappe.db.get_single_value('Accounts Settings', 'unlink_payment_on_cancellation_of_invoice'):
+		return
+
+	payment_entries = []
+	if self.is_return and self.return_against:
+		invoice = self.return_against
+	else:
+		invoice = self.name
+
+	payment_entries = frappe.db.sql_list("""
+		SELECT
+			t1.name
+		FROM
+			`tabPayment Entry` t1, `tabPayment Entry Reference` t2
+		WHERE
+			t1.name = t2.parent
+			and t1.docstatus = 1
+			and t2.reference_name = %s
+			and t2.allocated_amount < 0
+		""", invoice)
+
+	links_to_pe = []
+	if payment_entries:
+		for payment in payment_entries:
+			payment_entry = frappe.get_doc("Payment Entry", payment)
+			if len(payment_entry.references) > 1:
+				links_to_pe.append(payment_entry.name)
+		if links_to_pe:
+			payment_entries_link = [get_link_to_form('Payment Entry', name, label=name) for name in links_to_pe]
+			message = _("Please cancel and amend the Payment Entry")
+			message += " " + ", ".join(payment_entries_link) + " "
+			message += _("to unallocate the amount of this Return Invoice before cancelling it.")
+			frappe.throw(message)
diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py
index 52d19d5..8f9eb65 100644
--- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py
+++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py
@@ -6,7 +6,7 @@
 from frappe import _
 from frappe.utils import flt
 from frappe.model.document import Document
-from erpnext.controllers.accounts_controller import validate_taxes_and_charges, validate_inclusive_tax
+from erpnext.controllers.accounts_controller import validate_taxes_and_charges, validate_inclusive_tax, validate_cost_center, validate_account_head
 
 class SalesTaxesandChargesTemplate(Document):
 	def validate(self):
@@ -39,6 +39,8 @@
 
 	for tax in doc.get("taxes"):
 		validate_taxes_and_charges(tax)
+		validate_account_head(tax, doc)
+		validate_cost_center(tax, doc)
 		validate_inclusive_tax(tax, doc)
 
 def validate_disabled(doc):
diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_records.json b/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_records.json
index 2b737b9..74db08d 100644
--- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_records.json
+++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_records.json
@@ -8,6 +8,7 @@
     "charge_type": "On Net Total",
     "description": "VAT",
     "doctype": "Sales Taxes and Charges",
+    "cost_center": "Main - _TC",
     "parentfield": "taxes",
     "rate": 6
    },
@@ -16,6 +17,7 @@
     "charge_type": "On Net Total",
     "description": "Service Tax",
     "doctype": "Sales Taxes and Charges",
+    "cost_center": "Main - _TC",
     "parentfield": "taxes",
     "rate": 6.36
    }
@@ -114,6 +116,7 @@
     "charge_type": "On Net Total",
     "description": "VAT",
     "doctype": "Sales Taxes and Charges",
+    "cost_center": "Main - _TC",
     "parentfield": "taxes",
     "rate": 12
    },
@@ -122,6 +125,7 @@
     "charge_type": "On Net Total",
     "description": "Service Tax",
     "doctype": "Sales Taxes and Charges",
+    "cost_center": "Main - _TC",
     "parentfield": "taxes",
     "rate": 4
    }
@@ -137,6 +141,7 @@
     "charge_type": "On Net Total",
     "description": "VAT",
     "doctype": "Sales Taxes and Charges",
+    "cost_center": "Main - _TC",
     "parentfield": "taxes",
     "rate": 12
    },
@@ -145,6 +150,7 @@
     "charge_type": "On Net Total",
     "description": "Service Tax",
     "doctype": "Sales Taxes and Charges",
+    "cost_center": "Main - _TC",
     "parentfield": "taxes",
     "rate": 4
    }
@@ -160,6 +166,7 @@
     "charge_type": "On Net Total",
     "description": "VAT",
     "doctype": "Sales Taxes and Charges",
+    "cost_center": "Main - _TC",
     "parentfield": "taxes",
     "rate": 12
    },
@@ -168,6 +175,7 @@
     "charge_type": "On Net Total",
     "description": "Service Tax",
     "doctype": "Sales Taxes and Charges",
+    "cost_center": "Main - _TC",
     "parentfield": "taxes",
     "rate": 4
    }
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index 25d2cf1..4c7c567 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -100,8 +100,8 @@
 	return merged_gl_map
 
 def check_if_in_list(gle, gl_map, dimensions=None):
-	account_head_fieldnames = ['party_type', 'party', 'against_voucher', 'against_voucher_type',
-		'cost_center', 'project', 'voucher_detail_no']
+	account_head_fieldnames = ['voucher_detail_no', 'party', 'against_voucher',
+			'cost_center', 'against_voucher_type', 'party_type', 'project']
 
 	if dimensions:
 		account_head_fieldnames = account_head_fieldnames + dimensions
@@ -110,10 +110,12 @@
 		same_head = True
 		if e.account != gle.account:
 			same_head = False
+			continue
 
 		for fieldname in account_head_fieldnames:
 			if cstr(e.get(fieldname)) != cstr(gle.get(fieldname)):
 				same_head = False
+				break
 
 		if same_head:
 			return e
@@ -143,16 +145,19 @@
 		validate_expense_against_budget(args)
 
 def validate_cwip_accounts(gl_map):
-	cwip_enabled = any(cint(ac.enable_cwip_accounting) for ac in frappe.db.get_all("Asset Category","enable_cwip_accounting"))
+	"""Validate that CWIP account are not used in Journal Entry"""
+	if gl_map and gl_map[0].voucher_type != "Journal Entry":
+		return
 
-	if cwip_enabled and gl_map[0].voucher_type == "Journal Entry":
-			cwip_accounts = [d[0] for d in frappe.db.sql("""select name from tabAccount
-				where account_type = 'Capital Work in Progress' and is_group=0""")]
+	cwip_enabled = any(cint(ac.enable_cwip_accounting) for ac in frappe.db.get_all("Asset Category", "enable_cwip_accounting"))
+	if cwip_enabled:
+		cwip_accounts = [d[0] for d in frappe.db.sql("""select name from tabAccount
+			where account_type = 'Capital Work in Progress' and is_group=0""")]
 
-			for entry in gl_map:
-				if entry.account in cwip_accounts:
-					frappe.throw(
-						_("Account: <b>{0}</b> is capital Work in progress and can not be updated by Journal Entry").format(entry.account))
+		for entry in gl_map:
+			if entry.account in cwip_accounts:
+				frappe.throw(
+					_("Account: <b>{0}</b> is capital Work in progress and can not be updated by Journal Entry").format(entry.account))
 
 def round_off_debit_credit(gl_map):
 	precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"),
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 9272bc4..5b58e87 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -920,7 +920,6 @@
 			_delete_gl_entries(voucher_type, voucher_no)
 
 def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, for_items=None, company=None):
-	future_stock_vouchers = []
 
 	values = []
 	condition = ""
@@ -936,30 +935,46 @@
 		condition += " and company = %s"
 		values.append(company)
 
-	for d in frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
+	future_stock_vouchers = frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
 		from `tabStock Ledger Entry` sle
 		where
 			timestamp(sle.posting_date, sle.posting_time) >= timestamp(%s, %s)
 			and is_cancelled = 0
 			{condition}
 		order by timestamp(sle.posting_date, sle.posting_time) asc, creation asc for update""".format(condition=condition),
-		tuple([posting_date, posting_time] + values), as_dict=True):
-			future_stock_vouchers.append([d.voucher_type, d.voucher_no])
+		tuple([posting_date, posting_time] + values), as_dict=True)
 
-	return future_stock_vouchers
+	return [(d.voucher_type, d.voucher_no) for d in future_stock_vouchers]
 
 def get_voucherwise_gl_entries(future_stock_vouchers, posting_date):
+	""" Get voucherwise list of GL entries.
+
+	Only fetches GLE fields required for comparing with new GLE.
+	Check compare_existing_and_expected_gle function below.
+	"""
 	gl_entries = {}
-	if future_stock_vouchers:
-		for d in frappe.db.sql("""select * from `tabGL Entry`
-			where posting_date >= %s and voucher_no in (%s)""" %
-			('%s', ', '.join(['%s']*len(future_stock_vouchers))),
-			tuple([posting_date] + [d[1] for d in future_stock_vouchers]), as_dict=1):
-				gl_entries.setdefault((d.voucher_type, d.voucher_no), []).append(d)
+	if not future_stock_vouchers:
+		return gl_entries
+
+	voucher_nos = [d[1] for d in future_stock_vouchers]
+
+	gles = frappe.db.sql("""
+		select name, account, credit, debit, cost_center, project
+			from `tabGL Entry`
+		where
+			posting_date >= %s and voucher_no in (%s)""" %
+		('%s', ', '.join(['%s'] * len(voucher_nos))),
+		tuple([posting_date] + voucher_nos), as_dict=1)
+
+	for d in gles:
+		gl_entries.setdefault((d.voucher_type, d.voucher_no), []).append(d)
 
 	return gl_entries
 
 def compare_existing_and_expected_gle(existing_gle, expected_gle, precision):
+	if len(existing_gle) != len(expected_gle):
+		return False
+
 	matched = True
 	for entry in expected_gle:
 		account_existed = False
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index 59fbe3b..e23a715 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -639,7 +639,7 @@
 		asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
 		asset = frappe.get_doc('Asset', asset_name)
 		asset.calculate_depreciation = 1
-		asset.available_for_use_date = '2030-06-12'
+		asset.available_for_use_date = '2030-07-12'
 		asset.purchase_date = '2030-01-01'
 		asset.append("finance_books", {
 			"expected_value_after_useful_life": 1000,
@@ -653,10 +653,10 @@
 		self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
 
 		expected_schedules = [
-			["2030-12-31", 1106.85, 1106.85],
-			["2031-12-31", 3446.58, 4553.43],
-			["2032-12-31", 1723.29, 6276.72],
-			["2033-06-12", 723.28, 7000.00]
+			["2030-12-31", 942.47, 942.47],
+			["2031-12-31", 3528.77, 4471.24],
+			["2032-12-31", 1764.38, 6235.62],
+			["2033-07-12", 764.38, 7000.00]
 		]
 
 		schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
diff --git a/erpnext/assets/doctype/asset_movement/test_asset_movement.py b/erpnext/assets/doctype/asset_movement/test_asset_movement.py
index cddee5f..2b2d2b4 100644
--- a/erpnext/assets/doctype/asset_movement/test_asset_movement.py
+++ b/erpnext/assets/doctype/asset_movement/test_asset_movement.py
@@ -15,6 +15,7 @@
 
 class TestAssetMovement(unittest.TestCase):
 	def setUp(self):
+		frappe.db.set_value("Company", "_Test Company", "capital_work_in_progress_account", "CWIP Account - _TC")
 		create_asset_data()
 		make_location()
 
@@ -45,12 +46,12 @@
 				'location_name': 'Test Location 2'
 			}).insert()
 
-		movement1 = create_asset_movement(purpose = 'Transfer', company = asset.company, 
+		movement1 = create_asset_movement(purpose = 'Transfer', company = asset.company,
 			assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'target_location': 'Test Location 2'}],
 			reference_doctype = 'Purchase Receipt', reference_name = pr.name)
 		self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location 2")
 
-		movement2 = create_asset_movement(purpose = 'Transfer', company = asset.company, 
+		create_asset_movement(purpose = 'Transfer', company = asset.company,
 			assets = [{ 'asset': asset.name , 'source_location': 'Test Location 2', 'target_location': 'Test Location'}],
 			reference_doctype = 'Purchase Receipt', reference_name = pr.name)
 		self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
@@ -59,18 +60,18 @@
 		self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
 
 		employee = make_employee("testassetmovemp@example.com", company="_Test Company")
-		movement3 = create_asset_movement(purpose = 'Issue', company = asset.company, 
+		create_asset_movement(purpose = 'Issue', company = asset.company,
 			assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'to_employee': employee}],
 			reference_doctype = 'Purchase Receipt', reference_name = pr.name)
-		
+
 		# after issuing asset should belong to an employee not at a location
 		self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), None)
 		self.assertEqual(frappe.db.get_value("Asset", asset.name, "custodian"), employee)
-	
+
 	def test_last_movement_cancellation(self):
 		pr = make_purchase_receipt(item_code="Macbook Pro",
 			qty=1, rate=100000.0, location="Test Location")
-		
+
 		asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
 		asset = frappe.get_doc('Asset', asset_name)
 		asset.calculate_depreciation = 1
@@ -85,17 +86,17 @@
 		})
 		if asset.docstatus == 0:
 			asset.submit()
-		
+
 		if not frappe.db.exists("Location", "Test Location 2"):
 			frappe.get_doc({
 				'doctype': 'Location',
 				'location_name': 'Test Location 2'
 			}).insert()
-		
+
 		movement = frappe.get_doc({'doctype': 'Asset Movement', 'reference_name': pr.name })
 		self.assertRaises(frappe.ValidationError, movement.cancel)
 
-		movement1 = create_asset_movement(purpose = 'Transfer', company = asset.company, 
+		movement1 = create_asset_movement(purpose = 'Transfer', company = asset.company,
 			assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'target_location': 'Test Location 2'}],
 			reference_doctype = 'Purchase Receipt', reference_name = pr.name)
 		self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location 2")
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index e10a9e7..a092905 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -1354,6 +1354,27 @@
 		tax.rate = None
 
 
+def validate_account_head(tax, doc):
+	company = frappe.get_cached_value('Account',
+		tax.account_head, 'company')
+
+	if company != doc.company:
+		frappe.throw(_('Row {0}: Account {1} does not belong to Company {2}')
+			.format(tax.idx, frappe.bold(tax.account_head), frappe.bold(doc.company)), title=_('Invalid Account'))
+
+
+def validate_cost_center(tax, doc):
+	if not tax.cost_center:
+		return
+
+	company = frappe.get_cached_value('Cost Center',
+		tax.cost_center, 'company')
+
+	if company != doc.company:
+		frappe.throw(_('Row {0}: Cost Center {1} does not belong to Company {2}')
+			.format(tax.idx, frappe.bold(tax.cost_center), frappe.bold(doc.company)), title=_('Invalid Cost Center'))
+
+
 def validate_inclusive_tax(tax, doc):
 	def _on_previous_row_error(row_range):
 		throw(_("To include tax in row {0} in Item rate, taxes in rows {1} must also be included").format(tax.idx, row_range))
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 17bd735..17707ec 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -27,6 +27,7 @@
 		if not self.get('is_return'):
 			self.validate_inspection()
 		self.validate_serialized_batch()
+		self.clean_serial_nos()
 		self.validate_customer_provided_item()
 		self.set_rate_of_stock_uom()
 		self.validate_internal_transfer()
@@ -72,6 +73,12 @@
 					frappe.throw(_("Row #{0}: The batch {1} has already expired.")
 						.format(d.idx, get_link_to_form("Batch", d.get("batch_no"))))
 
+	def clean_serial_nos(self):
+		for row in self.get("items"):
+			if hasattr(row, "serial_no") and row.serial_no:
+				# replace commas by linefeed and remove all spaces in string
+				row.serial_no = row.serial_no.replace(",", "\n").replace(" ", "")
+
 	def get_gl_entries(self, warehouse_account=None, default_expense_account=None,
 			default_cost_center=None):
 
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index 099c7d4..05edb25 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -679,17 +679,13 @@
 		default_mode_of_payment = frappe.db.get_value('POS Payment Method',
 			{'parent': self.doc.pos_profile, 'default': 1}, ['mode_of_payment'], as_dict=1)
 
-		self.doc.payments = []
-
 		if default_mode_of_payment:
+			self.doc.payments = []
 			self.doc.append('payments', {
 				'mode_of_payment': default_mode_of_payment.mode_of_payment,
 				'amount': total_amount_to_pay,
 				'default': 1
 			})
-		else:
-			self.doc.is_pos = 0
-			self.doc.pos_profile = ''
 
 		self.calculate_paid_amount()
 
diff --git a/erpnext/hr/doctype/attendance_request/test_attendance_request.py b/erpnext/hr/doctype/attendance_request/test_attendance_request.py
index 3c42bd9..9e668aa 100644
--- a/erpnext/hr/doctype/attendance_request/test_attendance_request.py
+++ b/erpnext/hr/doctype/attendance_request/test_attendance_request.py
@@ -15,7 +15,11 @@
 		for doctype in ["Attendance Request", "Attendance"]:
 			frappe.db.sql("delete from `tab{doctype}`".format(doctype=doctype))
 
+	def tearDown(self):
+		frappe.db.rollback()
+
 	def test_on_duty_attendance_request(self):
+		"Test creation/updation of Attendace from Attendance Request, on duty."
 		today = nowdate()
 		employee = get_employee()
 		attendance_request = frappe.new_doc("Attendance Request")
@@ -26,17 +30,36 @@
 		attendance_request.company = "_Test Company"
 		attendance_request.insert()
 		attendance_request.submit()
-		attendance = frappe.get_doc('Attendance', {
-			'employee': employee.name,
-			'attendance_date': date(date.today().year, 1, 1),
-			'docstatus': 1
-		})
-		self.assertEqual(attendance.status, 'Present')
+
+		attendance = frappe.db.get_value(
+			"Attendance",
+			filters={
+				"attendance_request": attendance_request.name,
+				"attendance_date": date(date.today().year, 1, 1)
+			},
+			fieldname=["status", "docstatus"],
+			as_dict=True
+		)
+		self.assertEqual(attendance.status, "Present")
+		self.assertEqual(attendance.docstatus, 1)
+
+		# cancelling attendance request cancels linked attendances
 		attendance_request.cancel()
-		attendance.reload()
-		self.assertEqual(attendance.docstatus, 2)
+
+		# cancellation alters docname
+		# fetch attendance value again to avoid stale docname
+		attendance_docstatus = frappe.db.get_value(
+			"Attendance",
+			filters={
+				"attendance_request": attendance_request.name,
+				"attendance_date": date(date.today().year, 1, 1)
+			},
+			fieldname="docstatus"
+		)
+		self.assertEqual(attendance_docstatus, 2)
 
 	def test_work_from_home_attendance_request(self):
+		"Test creation/updation of Attendace from Attendance Request, work from home."
 		today = nowdate()
 		employee = get_employee()
 		attendance_request = frappe.new_doc("Attendance Request")
@@ -47,15 +70,30 @@
 		attendance_request.company = "_Test Company"
 		attendance_request.insert()
 		attendance_request.submit()
-		attendance = frappe.get_doc('Attendance', {
-			'employee': employee.name,
-			'attendance_date': date(date.today().year, 1, 1),
-			'docstatus': 1
-		})
-		self.assertEqual(attendance.status, 'Work From Home')
+
+		attendance_status = frappe.db.get_value(
+			"Attendance",
+			filters={
+				"attendance_request": attendance_request.name,
+				"attendance_date": date(date.today().year, 1, 1)
+			},
+			fieldname="status"
+		)
+		self.assertEqual(attendance_status, 'Work From Home')
+
 		attendance_request.cancel()
-		attendance.reload()
-		self.assertEqual(attendance.docstatus, 2)
+
+		# cancellation alters docname
+		# fetch attendance value again to avoid stale docname
+		attendance_docstatus = frappe.db.get_value(
+			"Attendance",
+			filters={
+				"attendance_request": attendance_request.name,
+				"attendance_date": date(date.today().year, 1, 1)
+			},
+			fieldname="docstatus"
+		)
+		self.assertEqual(attendance_docstatus, 2)
 
 def get_employee():
 	return frappe.get_doc("Employee", "_T-Employee-00001")
diff --git a/erpnext/hr/doctype/shift_request/test_shift_request.py b/erpnext/hr/doctype/shift_request/test_shift_request.py
index 9c0d8e3..3525540 100644
--- a/erpnext/hr/doctype/shift_request/test_shift_request.py
+++ b/erpnext/hr/doctype/shift_request/test_shift_request.py
@@ -15,24 +15,35 @@
 		for doctype in ["Shift Request", "Shift Assignment"]:
 			frappe.db.sql("delete from `tab{doctype}`".format(doctype=doctype))
 
+	def tearDown(self):
+		frappe.db.rollback()
+
 	def test_make_shift_request(self):
+		"Test creation/updation of Shift Assignment from Shift Request."
 		department = frappe.get_value("Employee", "_T-Employee-00001", 'department')
 		set_shift_approver(department)
 		approver = frappe.db.sql("""select approver from `tabDepartment Approver` where parent= %s and parentfield = 'shift_request_approver'""", (department))[0][0]
 
 		shift_request = make_shift_request(approver)
 
-		shift_assignments = frappe.db.sql('''
-				SELECT shift_request, employee
-				FROM `tabShift Assignment`
-				WHERE shift_request = '{0}'
-			'''.format(shift_request.name), as_dict=1)
-		for d in shift_assignments:
-			employee = d.get('employee')
-			self.assertEqual(shift_request.employee, employee)
-			shift_request.cancel()
-			shift_assignment_doc = frappe.get_doc("Shift Assignment", {"shift_request": d.get('shift_request')})
-			self.assertEqual(shift_assignment_doc.docstatus, 2)
+		# Only one shift assignment is created against a shift request
+		shift_assignment = frappe.db.get_value(
+			"Shift Assignment",
+			filters={"shift_request": shift_request.name},
+			fieldname=["employee", "docstatus"],
+			as_dict=True
+		)
+		self.assertEqual(shift_request.employee, shift_assignment.employee)
+		self.assertEqual(shift_assignment.docstatus, 1)
+
+		shift_request.cancel()
+
+		shift_assignment_docstatus = frappe.db.get_value(
+			"Shift Assignment",
+			filters={"shift_request": shift_request.name},
+			fieldname="docstatus"
+		)
+		self.assertEqual(shift_assignment_docstatus, 2)
 
 	def test_shift_request_approver_perms(self):
 		employee = frappe.get_doc("Employee", "_T-Employee-00001")
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 35b248c..86356e3 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -294,6 +294,7 @@
 erpnext.patches.v13_0.add_missing_fg_item_for_stock_entry
 erpnext.patches.v13_0.update_subscription_status_in_memberships
 erpnext.patches.v13_0.update_amt_in_work_order_required_items
+erpnext.patches.v12_0.show_einvoice_irn_cancelled_field
 erpnext.patches.v13_0.delete_orphaned_tables
 erpnext.patches.v13_0.update_export_type_for_gst
 erpnext.patches.v13_0.update_tds_check_field #3
diff --git a/erpnext/patches/v12_0/show_einvoice_irn_cancelled_field.py b/erpnext/patches/v12_0/show_einvoice_irn_cancelled_field.py
new file mode 100644
index 0000000..2319c17
--- /dev/null
+++ b/erpnext/patches/v12_0/show_einvoice_irn_cancelled_field.py
@@ -0,0 +1,12 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+	company = frappe.get_all('Company', filters = {'country': 'India'})
+	if not company:
+		return
+
+	irn_cancelled_field = frappe.db.exists('Custom Field', {'dt': 'Sales Invoice', 'fieldname': 'irn_cancelled'})
+	if irn_cancelled_field:
+		frappe.db.set_value('Custom Field', irn_cancelled_field, 'depends_on', 'eval: doc.irn')
+		frappe.db.set_value('Custom Field', irn_cancelled_field, 'read_only', 0)
diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js
index 7b997a1..84c7176 100644
--- a/erpnext/public/js/controllers/accounts.js
+++ b/erpnext/public/js/controllers/accounts.js
@@ -31,6 +31,14 @@
 					}
 				}
 			});
+			frm.set_query("cost_center", "taxes", function(doc) {
+				return {
+					filters: {
+						"company": doc.company,
+						"is_group": 0
+					}
+				};
+			});
 		}
 	},
 	validate: function(frm) {
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index a495a9b..84697e0 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -751,8 +751,6 @@
 		this.frm.doc.payments.find(pay => {
 			if (pay.default) {
 				pay.amount = total_amount_to_pay;
-			} else {
-				pay.amount = 0.0
 			}
 		});
 		this.frm.refresh_fields();
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 33366db..3c6c347 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -752,7 +752,7 @@
 				this.frm.trigger("item_code", cdt, cdn);
 			}
 			else {
-				// Replacing all occurences of comma with carriage return
+				// Replace all occurences of comma with line feed
 				item.serial_no = item.serial_no.replace(/,/g, '\n');
 				item.conversion_factor = item.conversion_factor || 1;
 				refresh_field("serial_no", item.name, item.parentfield);
diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py
index e9372f9..b4f146c 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -457,7 +457,7 @@
 			depends_on='eval:in_list(["Registered Regular", "SEZ", "Overseas", "Deemed Export"], doc.gst_category) && doc.irn_cancelled === 0'),
 
 		dict(fieldname='irn_cancelled', label='IRN Cancelled', fieldtype='Check', no_copy=1, print_hide=1,
-			depends_on='eval:(doc.irn_cancelled === 1)', read_only=1, allow_on_submit=1, insert_after='customer'),
+			depends_on='eval: doc.irn', allow_on_submit=1, insert_after='customer'),
 
 		dict(fieldname='eway_bill_validity', label='E-Way Bill Validity', fieldtype='Data', no_copy=1, print_hide=1,
 			depends_on='ewaybill', read_only=1, allow_on_submit=1, insert_after='ewaybill'),
@@ -985,4 +985,4 @@
 
 def update_accounts_settings_for_taxes():
 	if frappe.db.count('Company') == 1:
-		frappe.db.set_value('Accounts Settings', None, "add_taxes_from_item_tax_template", 0)
\ No newline at end of file
+		frappe.db.set_value('Accounts Settings', None, "add_taxes_from_item_tax_template", 0)
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 88c350a..a152797 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -851,7 +851,7 @@
 		# if its the first depreciation
 		if depreciable_value == asset.gross_purchase_amount:
 			# as per IT act, if the asset is purchased in the 2nd half of fiscal year, then rate is divided by 2
-			diff = date_diff(asset.available_for_use_date, row.depreciation_start_date)
+			diff = date_diff(row.depreciation_start_date, asset.available_for_use_date)
 			if diff <= 180:
 				rate_of_depreciation = rate_of_depreciation / 2
 				frappe.msgprint(
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index a226da7..a0a21ee 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -673,6 +673,8 @@
 
 		so.cancel()
 
+		dn.load_from_db()
+
 		self.assertRaises(frappe.CancelledLinkError, dn.submit)
 
 	def test_service_type_product_bundle(self):
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index 8755125..95cbf51 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -108,6 +108,9 @@
 				frappe.flags.country_change = True
 				self.create_default_accounts()
 				self.create_default_warehouses()
+		
+		if not frappe.db.get_value("Cost Center", {"is_group": 0, "company": self.name}):
+			self.create_default_cost_center()
 
 		if frappe.flags.country_change:
 			install_country_fixtures(self.name, self.country)
@@ -117,9 +120,6 @@
 			from erpnext.setup.setup_wizard.operations.install_fixtures import install_post_company_fixtures
 			install_post_company_fixtures(frappe._dict({'company_name': self.name}))
 
-		if not frappe.db.get_value("Cost Center", {"is_group": 0, "company": self.name}):
-			self.create_default_cost_center()
-
 		if not frappe.local.flags.ignore_chart_of_accounts:
 			self.set_default_accounts()
 			if self.default_cash_account:
diff --git a/erpnext/setup/setup_wizard/operations/taxes_setup.py b/erpnext/setup/setup_wizard/operations/taxes_setup.py
index cbb3dc8..bacada9 100644
--- a/erpnext/setup/setup_wizard/operations/taxes_setup.py
+++ b/erpnext/setup/setup_wizard/operations/taxes_setup.py
@@ -124,7 +124,8 @@
 		account_data = tax_row.get('account_head')
 		tax_row_defaults = {
 			'category': 'Total',
-			'charge_type': 'On Net Total'
+			'charge_type': 'On Net Total',
+			'cost_center': frappe.db.get_value('Company', company_name, 'cost_center')
 		}
 
 		if doctype == 'Purchase Taxes and Charges Template':
diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
index 128a2ab..cb09d93 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
@@ -253,6 +253,8 @@
 
 	def test_asset_lcv(self):
 		"Check if LCV for an Asset updates the Assets Gross Purchase Amount correctly."
+		frappe.db.set_value("Company", "_Test Company", "capital_work_in_progress_account", "CWIP Account - _TC")
+
 		if not frappe.db.exists("Asset Category", "Computers"):
 			create_asset_category()
 
@@ -265,7 +267,6 @@
 		assets = frappe.db.get_all('Asset', filters={'purchase_receipt': pr.name})
 		self.assertEqual(len(assets), 1)
 
-		frappe.db.set_value("Company", pr.company, "capital_work_in_progress_account", "CWIP Account - _TC")
 		lcv = make_landed_cost_voucher(
 			company = pr.company,
 			receipt_document_type = "Purchase Receipt",
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index bad7b60..70312bc 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -165,8 +165,14 @@
 				)
 			ORDER BY
 				posting_date desc, posting_time desc, creation desc""",
-			(self.item_code, self.company,
-				serial_no, serial_no+'\n%', '%\n'+serial_no, '%\n'+serial_no+'\n%'), as_dict=1):
+			(
+				self.item_code, self.company,
+				serial_no,
+				serial_no+'\n%',
+				'%\n'+serial_no,
+				'%\n'+serial_no+'\n%'
+			),
+			as_dict=1):
 				if serial_no.upper() in get_serial_nos(sle.serial_no):
 					if cint(sle.actual_qty) > 0:
 						sle_dict.setdefault("incoming", []).append(sle)
diff --git a/erpnext/stock/doctype/serial_no/test_serial_no.py b/erpnext/stock/doctype/serial_no/test_serial_no.py
index cde7fe0..b9a58cf 100644
--- a/erpnext/stock/doctype/serial_no/test_serial_no.py
+++ b/erpnext/stock/doctype/serial_no/test_serial_no.py
@@ -174,5 +174,23 @@
 		self.assertEqual(sn_doc.warehouse, "_Test Warehouse - _TC")
 		self.assertEqual(sn_doc.purchase_document_no, se.name)
 
+	def test_serial_no_sanitation(self):
+		"Test if Serial No input is sanitised before entering the DB."
+		item_code = "_Test Serialized Item"
+		test_records = frappe.get_test_records('Stock Entry')
+
+		se = frappe.copy_doc(test_records[0])
+		se.get("items")[0].item_code = item_code
+		se.get("items")[0].qty = 3
+		se.get("items")[0].serial_no = " _TS1, _TS2 , _TS3  "
+		se.get("items")[0].transfer_qty = 3
+		se.set_stock_entry_type()
+		se.insert()
+		se.submit()
+
+		self.assertEqual(se.get("items")[0].serial_no, "_TS1\n_TS2\n_TS3")
+
+		frappe.db.rollback()
+
 	def tearDown(self):
 		frappe.db.rollback()
\ No newline at end of file
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 95c7311..8293319 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -76,6 +76,7 @@
 		self.validate_difference_account()
 		self.set_job_card_data()
 		self.set_purpose_for_stock_entry()
+		self.clean_serial_nos()
 		self.validate_duplicate_serial_no()
 
 		if not self.from_bom:
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 b4f4583..be1f00e 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -55,8 +55,8 @@
 				"sum(actual_qty)") or 0
 			frappe.db.set_value("Batch", self.batch_no, "batch_qty", batch_qty)
 
-	#check for item quantity available in stock
 	def actual_amt_check(self):
+		"""Validate that qty at warehouse for selected batch is >=0"""
 		if self.batch_no and not self.get("allow_negative_stock"):
 			batch_bal_after_transaction = flt(frappe.db.sql("""select sum(actual_qty)
 				from `tabStock Ledger Entry`
@@ -107,7 +107,7 @@
 		self.stock_uom = item_det.stock_uom
 
 	def check_stock_frozen_date(self):
-		stock_settings = frappe.get_doc('Stock Settings', 'Stock Settings')
+		stock_settings = frappe.get_cached_doc('Stock Settings')
 
 		if stock_settings.stock_frozen_upto:
 			if (getdate(self.posting_date) <= getdate(stock_settings.stock_frozen_upto)
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 0bae7cf..cda7c1d 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -31,6 +31,7 @@
 		self.validate_expense_account()
 		self.validate_customer_provided_item()
 		self.set_zero_value_for_customer_provided_items()
+		self.clean_serial_nos()
 		self.set_total_qty_and_amount()
 		self.validate_putaway_capacity()
 
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index f990ce0..eddd048 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -279,15 +279,13 @@
 			}
 
 		"""
-		self.data.setdefault(args.warehouse, frappe._dict())
-		warehouse_dict = self.data[args.warehouse]
 		previous_sle = get_previous_sle_of_current_voucher(args)
-		warehouse_dict.previous_sle = previous_sle
 
-		for key in ("qty_after_transaction", "valuation_rate", "stock_value"):
-			setattr(warehouse_dict, key, flt(previous_sle.get(key)))
-
-		warehouse_dict.update({
+		self.data[args.warehouse] = frappe._dict({
+			"previous_sle": previous_sle,
+			"qty_after_transaction": flt(previous_sle.qty_after_transaction),
+			"valuation_rate": flt(previous_sle.valuation_rate),
+			"stock_value": flt(previous_sle.stock_value),
 			"prev_stock_value": previous_sle.stock_value or 0.0,
 			"stock_queue": json.loads(previous_sle.stock_queue or "[]"),
 			"stock_value_difference": 0.0
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index b57b2aa..9f6d0a8 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -224,7 +224,7 @@
 
 def get_valuation_method(item_code):
 	"""get valuation method from item or default"""
-	val_method = frappe.db.get_value('Item', item_code, 'valuation_method')
+	val_method = frappe.db.get_value('Item', item_code, 'valuation_method', cache=True)
 	if not val_method:
 		val_method = frappe.db.get_value("Stock Settings", None, "valuation_method") or "FIFO"
 	return val_method
@@ -275,17 +275,17 @@
 	return valid_serial_nos
 
 def validate_warehouse_company(warehouse, company):
-	warehouse_company = frappe.db.get_value("Warehouse", warehouse, "company")
+	warehouse_company = frappe.db.get_value("Warehouse", warehouse, "company", cache=True)
 	if warehouse_company and warehouse_company != company:
 		frappe.throw(_("Warehouse {0} does not belong to company {1}").format(warehouse, company),
 			InvalidWarehouseCompany)
 
 def is_group_warehouse(warehouse):
-	if frappe.db.get_value("Warehouse", warehouse, "is_group"):
+	if frappe.db.get_value("Warehouse", warehouse, "is_group", cache=True):
 		frappe.throw(_("Group node warehouse is not allowed to select for transactions"))
 
 def validate_disabled_warehouse(warehouse):
-	if frappe.db.get_value("Warehouse", warehouse, "disabled"):
+	if frappe.db.get_value("Warehouse", warehouse, "disabled", cache=True):
 		frappe.throw(_("Disabled Warehouse {0} cannot be used for this transaction.").format(get_link_to_form('Warehouse', warehouse)))
 
 def update_included_uom_in_report(columns, result, include_uom, conversion_factors):