Merge branch 'develop' into loan_exposure_report_fix
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index 5a5c448..199a183 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -109,7 +109,7 @@
 	'''
 	if company or frappe.flags.company:
 		return frappe.get_cached_value('Company',
-			company or frappe.flags.company,  'country')
+			company or frappe.flags.company, 'country')
 	elif frappe.flags.country:
 		return frappe.flags.country
 	else:
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
index 94573f9..76e0092 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
@@ -179,10 +179,18 @@
 			if d.get("serial_no"):
 				serial_nos = get_serial_nos(d.serial_no)
 				for sr in serial_nos:
-					serial_no_exists = frappe.db.exists("POS Invoice Item", {
-						"parent": self.return_against, 
-						"serial_no": ["like", d.get("serial_no")]
-					})
+					serial_no_exists = frappe.db.sql("""
+						SELECT name
+						FROM `tabPOS Invoice Item`
+						WHERE
+							parent = %s
+							and (serial_no = %s
+								or serial_no like %s
+								or serial_no like %s
+								or serial_no like %s
+							)
+					""", (self.return_against, sr, sr+'\n%', '%\n'+sr, '%\n'+sr+'\n%'))
+
 					if not serial_no_exists:
 						bold_return_against = frappe.bold(self.return_against)
 						bold_serial_no = frappe.bold(sr)
@@ -190,7 +198,7 @@
 							_("Row #{}: Serial No {} cannot be returned since it was not transacted in original invoice {}")
 							.format(d.idx, bold_serial_no, bold_return_against)
 						)
-	
+
 	def validate_non_stock_items(self):
 		for d in self.get("items"):
 			is_stock_item = frappe.get_cached_value("Item", d.get("item_code"), "is_stock_item")
@@ -292,7 +300,7 @@
 
 		if not self.get('payments') and not for_validate:
 			update_multi_mode_option(self, profile)
-		
+
 		if self.is_return and not for_validate:
 			add_return_modes(self, profile)
 
diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
index 57a23af..15875af 100644
--- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
@@ -198,6 +198,65 @@
 		self.assertEqual(pos_return.get('payments')[0].amount, -500)
 		self.assertEqual(pos_return.get('payments')[1].amount, -500)
 
+	def test_pos_return_for_serialized_item(self):
+		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
+		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+
+		se = make_serialized_item(company='_Test Company',
+			target_warehouse="Stores - _TC", cost_center='Main - _TC', expense_account='Cost of Goods Sold - _TC')
+
+		serial_nos = get_serial_nos(se.get("items")[0].serial_no)
+
+		pos = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC',
+			account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC',
+			expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC',
+			item=se.get("items")[0].item_code, rate=1000, do_not_save=1)
+
+		pos.get("items")[0].serial_no = serial_nos[0]
+		pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 1000, 'default': 1})
+
+		pos.insert()
+		pos.submit()
+
+		pos_return = make_sales_return(pos.name)
+
+		pos_return.insert()
+		pos_return.submit()
+		self.assertEqual(pos_return.get('items')[0].serial_no, serial_nos[0])
+
+	def test_partial_pos_returns(self):
+		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
+		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+
+		se = make_serialized_item(company='_Test Company',
+			target_warehouse="Stores - _TC", cost_center='Main - _TC', expense_account='Cost of Goods Sold - _TC')
+
+		serial_nos = get_serial_nos(se.get("items")[0].serial_no)
+
+		pos = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC',
+			account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC',
+			expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC',
+			item=se.get("items")[0].item_code, qty=2, rate=1000, do_not_save=1)
+
+		pos.get("items")[0].serial_no = serial_nos[0] + "\n" + serial_nos[1]
+		pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 1000, 'default': 1})
+
+		pos.insert()
+		pos.submit()
+
+		pos_return1 = make_sales_return(pos.name)
+
+		# partial return 1
+		pos_return1.get('items')[0].qty = -1
+		pos_return1.get('items')[0].serial_no = serial_nos[0]
+		pos_return1.insert()
+		pos_return1.submit()
+
+		# partial return 2
+		pos_return2 = make_sales_return(pos.name)
+		self.assertEqual(pos_return2.get('items')[0].qty, -1)
+		self.assertEqual(pos_return2.get('items')[0].serial_no, serial_nos[1])
+
 	def test_pos_change_amount(self):
 		pos = create_pos_invoice(company= "_Test Company", debit_to="Debtors - _TC",
 			income_account = "Sales - _TC", expense_account = "Cost of Goods Sold - _TC", rate=105,
diff --git a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json
index 2b6e7de..8b71eb0 100644
--- a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json
+++ b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json
@@ -87,6 +87,7 @@
   "edit_references",
   "sales_order",
   "so_detail",
+  "pos_invoice_item",
   "column_break_74",
   "delivery_note",
   "dn_detail",
@@ -790,11 +791,20 @@
    "fieldtype": "Link",
    "label": "Project",
    "options": "Project"
+  },
+  {
+   "fieldname": "pos_invoice_item",
+   "fieldtype": "Data",
+   "ignore_user_permissions": 1,
+   "label": "POS Invoice Item",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1
   }
  ],
  "istable": 1,
  "links": [],
- "modified": "2020-07-22 13:40:34.418346",
+ "modified": "2021-01-04 17:34:49.924531",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "POS Invoice Item",
diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
index c88d679..40f77b4 100644
--- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
+++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
@@ -29,7 +29,7 @@
 		for d in self.pos_invoices:
 			status, docstatus, is_return, return_against = frappe.db.get_value(
 				'POS Invoice', d.pos_invoice, ['status', 'docstatus', 'is_return', 'return_against'])
-			
+
 			bold_pos_invoice = frappe.bold(d.pos_invoice)
 			bold_status = frappe.bold(status)
 			if docstatus != 1:
@@ -58,7 +58,7 @@
 		sales_invoice, credit_note = "", ""
 		if sales:
 			sales_invoice = self.process_merging_into_sales_invoice(sales)
-		
+
 		if returns:
 			credit_note = self.process_merging_into_credit_note(returns)
 
@@ -74,7 +74,7 @@
 
 	def process_merging_into_sales_invoice(self, data):
 		sales_invoice = self.get_new_sales_invoice()
-		
+
 		sales_invoice = self.merge_pos_invoice_into(sales_invoice, data)
 
 		sales_invoice.is_consolidated = 1
@@ -98,19 +98,19 @@
 		self.consolidated_credit_note = credit_note.name
 
 		return credit_note.name
-	
+
 	def merge_pos_invoice_into(self, invoice, data):
 		items, payments, taxes = [], [], []
 		loyalty_amount_sum, loyalty_points_sum = 0, 0
 		for doc in data:
 			map_doc(doc, invoice, table_map={ "doctype": invoice.doctype })
-			
+
 			if doc.redeem_loyalty_points:
 				invoice.loyalty_redemption_account = doc.loyalty_redemption_account
 				invoice.loyalty_redemption_cost_center = doc.loyalty_redemption_cost_center
 				loyalty_points_sum += doc.loyalty_points
 				loyalty_amount_sum += doc.loyalty_amount
-			
+
 			for item in doc.get('items'):
 				found = False
 				for i in items:
@@ -118,12 +118,13 @@
 						i.uom == item.uom and i.net_rate == item.net_rate):
 						found = True
 						i.qty = i.qty + item.qty
+
 				if not found:
 					item.rate = item.net_rate
 					item.price_list_rate = 0
 					si_item = map_child_doc(item, invoice, {"doctype": "Sales Invoice Item"})
 					items.append(si_item)
-			
+
 			for tax in doc.get('taxes'):
 				found = False
 				for t in taxes:
@@ -162,7 +163,7 @@
 		invoice.ignore_pricing_rule = 1
 
 		return invoice
-	
+
 	def get_new_sales_invoice(self):
 		sales_invoice = frappe.new_doc('Sales Invoice')
 		sales_invoice.customer = self.customer
@@ -194,7 +195,7 @@
 	}
 	pos_invoices = frappe.db.get_all('POS Invoice', filters=filters,
 		fields=["name as pos_invoice", 'posting_date', 'grand_total', 'customer'])
-	
+
 	return pos_invoices
 
 def get_invoice_customer_map(pos_invoices):
@@ -204,7 +205,7 @@
 		customer = invoice.get('customer')
 		pos_invoice_customer_map.setdefault(customer, [])
 		pos_invoice_customer_map[customer].append(invoice)
-	
+
 	return pos_invoice_customer_map
 
 def consolidate_pos_invoices(pos_invoices=[], closing_entry={}):
diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
index d011689..76f3c50 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
@@ -222,7 +222,7 @@
 
 		set_gl_entries_by_account(start_date,
 			end_date, root.lft, root.rgt, filters,
-			gl_entries_by_account, accounts_by_name, ignore_closing_entries=False)
+			gl_entries_by_account, accounts_by_name, accounts, ignore_closing_entries=False)
 
 	calculate_values(accounts_by_name, gl_entries_by_account, companies, start_date, filters)
 	accumulate_values_into_parents(accounts, accounts_by_name, companies)
@@ -339,7 +339,7 @@
 	return data
 
 def set_gl_entries_by_account(from_date, to_date, root_lft, root_rgt, filters, gl_entries_by_account,
-	accounts_by_name, ignore_closing_entries=False):
+	accounts_by_name, accounts, ignore_closing_entries=False):
 	"""Returns a dict like { "account": [gl entries], ... }"""
 
 	company_lft, company_rgt = frappe.get_cached_value('Company',
@@ -382,15 +382,31 @@
 
 		for entry in gl_entries:
 			key = entry.account_number or entry.account_name
-			validate_entries(key, entry, accounts_by_name)
+			validate_entries(key, entry, accounts_by_name, accounts)
 			gl_entries_by_account.setdefault(key, []).append(entry)
 
 	return gl_entries_by_account
 
-def validate_entries(key, entry, accounts_by_name):
+def get_account_details(account):
+	return frappe.get_cached_value('Account', account, ['name', 'report_type', 'root_type', 'company',
+		'is_group', 'account_name', 'account_number', 'parent_account', 'lft', 'rgt'], as_dict=1)
+
+def validate_entries(key, entry, accounts_by_name, accounts):
 	if key not in accounts_by_name:
-		field = "Account number" if entry.account_number else "Account name"
-		frappe.throw(_("{0} {1} is not present in the parent company").format(field, key))
+		args = get_account_details(entry.account)
+
+		if args.parent_account:
+			parent_args = get_account_details(args.parent_account)
+
+			args.update({
+				'lft': parent_args.lft + 1,
+				'rgt': parent_args.rgt - 1,
+				'root_type': parent_args.root_type,
+				'report_type': parent_args.report_type
+			})
+
+		accounts_by_name.setdefault(key, args)
+		accounts.append(args)
 
 def get_additional_conditions(from_date, ignore_closing_entries, filters):
 	additional_conditions = []
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 67c7fd2..60d1e20 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -82,7 +82,7 @@
 	error_msg = _("""{0} {1} is not in any active Fiscal Year""").format(label, formatdate(transaction_date))
 	if company:
 		error_msg = _("""{0} for {1}""").format(error_msg, frappe.bold(company))
-	
+
 	if verbose==1: frappe.msgprint(error_msg)
 	raise FiscalYearError(error_msg)
 
@@ -888,17 +888,22 @@
 
 def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,
 		warehouse_account=None, company=None):
+	stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items, company)
+	repost_gle_for_stock_vouchers(stock_vouchers, posting_date, company, warehouse_account)
+
+
+def repost_gle_for_stock_vouchers(stock_vouchers, posting_date, company=None, warehouse_account=None):
 	def _delete_gl_entries(voucher_type, voucher_no):
 		frappe.db.sql("""delete from `tabGL Entry`
 			where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
 
+
 	if not warehouse_account:
 		warehouse_account = get_warehouse_account_map(company)
 
-	future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items)
-	gle = get_voucherwise_gl_entries(future_stock_vouchers, posting_date)
+	gle = get_voucherwise_gl_entries(stock_vouchers, posting_date)
 
-	for voucher_type, voucher_no in future_stock_vouchers:
+	for voucher_type, voucher_no in stock_vouchers:
 		existing_gle = gle.get((voucher_type, voucher_no), [])
 		voucher_obj = frappe.get_doc(voucher_type, voucher_no)
 		expected_gle = voucher_obj.get_gl_entries(warehouse_account)
@@ -909,7 +914,7 @@
 		else:
 			_delete_gl_entries(voucher_type, voucher_no)
 
-def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, for_items=None):
+def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, for_items=None, company=None):
 	future_stock_vouchers = []
 
 	values = []
@@ -922,6 +927,10 @@
 		condition += " and warehouse in ({})".format(", ".join(["%s"] * len(for_warehouses)))
 		values += for_warehouses
 
+	if company:
+		condition += " and company = %s"
+		values.append(company)
+
 	for d in frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
 		from `tabStock Ledger Entry` sle
 		where
@@ -982,7 +991,7 @@
 			error_reason = _("Stock Value ({0}) and Account Balance ({1}) are out of sync for account {2} and it's linked warehouses as on {3}.").format(
 				stock_bal, account_bal, frappe.bold(account), posting_date)
 			error_resolution = _("Please create an adjustment Journal Entry for amount {0} on {1}")\
-				.format(frappe.bold(diff), frappe.bold(posting_date))			
+				.format(frappe.bold(diff), frappe.bold(posting_date))
 
 			frappe.msgprint(
 				msg="""{0}<br></br>{1}<br></br>""".format(error_reason, error_resolution),
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 a51498e..7cf22f8 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
@@ -127,6 +127,10 @@
 				'link_doctype': 'Supplier',
 				'link_name': rfq_supplier.supplier
 			})
+			contact.append('email_ids', {
+				'email_id': user.name,
+				'is_primary': 1
+			})
 
 		if not contact.email_id and not contact.user:
 			contact.email_id = user.name
diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json
index 40362b1..4cc5753 100644
--- a/erpnext/buying/doctype/supplier/supplier.json
+++ b/erpnext/buying/doctype/supplier/supplier.json
@@ -26,7 +26,6 @@
   "supplier_group",
   "supplier_type",
   "pan",
-  "language",
   "allow_purchase_invoice_creation_without_purchase_order",
   "allow_purchase_invoice_creation_without_purchase_receipt",
   "disabled",
@@ -57,6 +56,7 @@
   "website",
   "supplier_details",
   "column_break_30",
+  "language",
   "is_frozen"
  ],
  "fields": [
@@ -384,7 +384,7 @@
  "idx": 370,
  "image_field": "image",
  "links": [],
- "modified": "2020-06-17 23:18:20",
+ "modified": "2021-01-06 19:51:40.939087",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Supplier",
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 991eef1..12a81c7 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -1309,45 +1309,28 @@
 					})
 				tax_row.db_insert()
 
-def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, trans_item):
+def set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child_docname, trans_item):
 	"""
-	Returns a Sales Order Item child item containing the default values
+	Returns a Sales/Purchase Order Item child item containing the default values
 	"""
 	p_doc = frappe.get_doc(parent_doctype, parent_doctype_name)
-	child_item = frappe.new_doc('Sales Order Item', p_doc, child_docname)
+	child_item = frappe.new_doc(child_doctype, p_doc, child_docname)
 	item = frappe.get_doc("Item", trans_item.get('item_code'))
-	child_item.item_code = item.item_code
-	child_item.item_name = item.item_name
-	child_item.description = item.description
-	child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date
+	for field in ("item_code", "item_name", "description", "item_group"):
+	    child_item.update({field: item.get(field)})
+	date_fieldname = "delivery_date" if child_doctype == "Sales Order Item" else "schedule_date"
+	child_item.update({date_fieldname: trans_item.get(date_fieldname) or p_doc.get(date_fieldname)})
 	child_item.uom = trans_item.get("uom") or item.stock_uom
 	conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor"))
 	child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or conversion_factor
-	set_child_tax_template_and_map(item, child_item, p_doc)
-	add_taxes_from_tax_template(child_item, p_doc)
-	child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True)
-	if not child_item.warehouse:
-		frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.")
-			.format(frappe.bold("default warehouse"), frappe.bold(item.item_code)))
-	return child_item
-
-
-def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, trans_item):
-	"""
-	Returns a Purchase Order Item child item containing the default values
-	"""
-	p_doc = frappe.get_doc(parent_doctype, parent_doctype_name)
-	child_item = frappe.new_doc('Purchase Order Item', p_doc, child_docname)
-	item = frappe.get_doc("Item", trans_item.get('item_code'))
-	child_item.item_code = item.item_code
-	child_item.item_name = item.item_name
-	child_item.description = item.description
-	child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date
-	child_item.uom = trans_item.get("uom") or item.stock_uom
-	conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor"))
-	child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or conversion_factor
-	child_item.base_rate = 1 # Initiallize value will update in parent validation
-	child_item.base_amount = 1 # Initiallize value will update in parent validation
+	if child_doctype == "Purchase Order Item":
+		child_item.base_rate = 1 # Initiallize value will update in parent validation
+		child_item.base_amount = 1 # Initiallize value will update in parent validation
+	if child_doctype == "Sales Order Item":
+		child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True)
+		if not child_item.warehouse:
+			frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.")
+				.format(frappe.bold("default warehouse"), frappe.bold(item.item_code)))
 	set_child_tax_template_and_map(item, child_item, p_doc)
 	add_taxes_from_tax_template(child_item, p_doc)
 	return child_item
@@ -1411,8 +1394,8 @@
 			)
 
 	def get_new_child_item(item_row):
-		new_child_function = set_sales_order_defaults if parent_doctype == "Sales Order" else set_purchase_order_defaults
-		return new_child_function(parent_doctype, parent_doctype_name, child_docname, item_row)
+		child_doctype = "Sales Order Item" if parent_doctype == "Sales Order" else "Purchase Order Item" 
+		return set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child_docname, item_row)
 
 	def validate_quantity(child_item, d):
 		if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty):
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index 0e1829a..de61b35 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -204,8 +204,6 @@
 	return items
 
 def get_returned_qty_map_for_row(row_name, doctype):
-	if doctype == "POS Invoice": return {}
-
 	child_doctype = doctype + " Item"
 	reference_field = "dn_detail" if doctype == "Delivery Note" else frappe.scrub(child_doctype)
 
@@ -354,7 +352,12 @@
 			target_doc.so_detail = source_doc.so_detail
 			target_doc.dn_detail = source_doc.dn_detail
 			target_doc.expense_account = source_doc.expense_account
-			target_doc.sales_invoice_item = source_doc.name
+
+			if doctype == "Sales Invoice":
+				target_doc.sales_invoice_item = source_doc.name
+			else:
+				target_doc.pos_invoice_item = source_doc.name
+
 			target_doc.price_list_rate = 0
 			if default_warehouse_for_sales_return:
 				target_doc.warehouse = default_warehouse_for_sales_return
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 70e8d8c..ea9659c 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -488,13 +488,12 @@
 			"voucher_no": self.name,
 			"company": self.company
 		})
-
 		if check_if_future_sle_exists(args):
 			create_repost_item_valuation_entry(args)
 		elif not is_reposting_pending():
 			check_if_stock_and_account_balance_synced(self.posting_date,
 				self.company, self.doctype, self.name)
-
+	
 def is_reposting_pending():
 	return frappe.db.exists("Repost Item Valuation",
 		{'docstatus': 1, 'status': ['in', ['Queued','In Progress']]})
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index cfa4991..10271cb 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -15,6 +15,8 @@
 class calculate_taxes_and_totals(object):
 	def __init__(self, doc):
 		self.doc = doc
+		frappe.flags.round_off_applicable_accounts = []
+		get_round_off_applicable_accounts(self.doc.company, frappe.flags.round_off_applicable_accounts)
 		self.calculate()
 
 	def calculate(self):
@@ -332,10 +334,18 @@
 		elif tax.charge_type == "On Item Quantity":
 			current_tax_amount = tax_rate * item.qty
 
+		current_tax_amount = self.get_final_current_tax_amount(tax, current_tax_amount)
 		self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
 
 		return current_tax_amount
 
+	def get_final_current_tax_amount(self, tax, current_tax_amount):
+		# Some countries need individual tax components to be rounded
+		# Handeled via regional doctypess
+		if tax.account_head in frappe.flags.round_off_applicable_accounts:
+			current_tax_amount = round(current_tax_amount, 0)
+		return current_tax_amount
+
 	def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
 		# store tax breakup for each item
 		key = item.item_code or item.item_name
@@ -693,6 +703,15 @@
 		)
 	)
 
+@frappe.whitelist()
+def get_round_off_applicable_accounts(company, account_list):
+	account_list = get_regional_round_off_accounts(company, account_list)
+
+	return account_list
+
+@erpnext.allow_regional
+def get_regional_round_off_accounts(company, account_list):
+	pass
 
 @erpnext.allow_regional
 def update_itemised_tax_data(doc):
diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json
index 2df1793..1b33fd7 100644
--- a/erpnext/crm/doctype/lead/lead.json
+++ b/erpnext/crm/doctype/lead/lead.json
@@ -49,6 +49,7 @@
   "phone",
   "mobile_no",
   "fax",
+  "website",
   "more_info",
   "type",
   "market_segment",
@@ -56,8 +57,8 @@
   "request_type",
   "column_break3",
   "company",
-  "website",
   "territory",
+  "language",
   "unsubscribed",
   "blog_subscriber",
   "title"
@@ -447,13 +448,19 @@
    "fieldtype": "Select",
    "label": "Address Type",
    "options": "Billing\nShipping\nOffice\nPersonal\nPlant\nPostal\nShop\nSubsidiary\nWarehouse\nCurrent\nPermanent\nOther"
+  },
+  {
+   "fieldname": "language",
+   "fieldtype": "Link",
+   "label": "Print Language",
+   "options": "Language"
   }
  ],
  "icon": "fa fa-user",
  "idx": 5,
  "image_field": "image",
  "links": [],
- "modified": "2020-10-13 15:24:00.094811",
+ "modified": "2021-01-06 19:39:58.748978",
  "modified_by": "Administrator",
  "module": "CRM",
  "name": "Lead",
diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json
index eee13f7..2e09a76 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.json
+++ b/erpnext/crm/doctype/opportunity/opportunity.json
@@ -54,6 +54,7 @@
   "campaign",
   "column_break1",
   "transaction_date",
+  "language",
   "amended_from",
   "lost_reasons"
  ],
@@ -419,12 +420,18 @@
    "fieldtype": "Duration",
    "label": "First Response Time",
    "read_only": 1
+  },
+  {
+   "fieldname": "language",
+   "fieldtype": "Link",
+   "label": "Print Language",
+   "options": "Language"
   }
  ],
  "icon": "fa fa-info-sign",
  "idx": 195,
  "links": [],
- "modified": "2020-08-12 17:34:35.066961",
+ "modified": "2021-01-06 19:42:46.190051",
  "modified_by": "Administrator",
  "module": "CRM",
  "name": "Opportunity",
diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type.js b/erpnext/healthcare/doctype/appointment_type/appointment_type.js
index 15916a5..861675a 100644
--- a/erpnext/healthcare/doctype/appointment_type/appointment_type.js
+++ b/erpnext/healthcare/doctype/appointment_type/appointment_type.js
@@ -2,4 +2,82 @@
 // For license information, please see license.txt
 
 frappe.ui.form.on('Appointment Type', {
+	refresh: function(frm) {
+		frm.set_query('price_list', function() {
+			return {
+				filters: {'selling': 1}
+			};
+		});
+
+		frm.set_query('medical_department', 'items', function(doc) {
+			let item_list = doc.items.map(({medical_department}) => medical_department);
+			return {
+				filters: [
+					['Medical Department', 'name', 'not in', item_list]
+				]
+			};
+		});
+
+		frm.set_query('op_consulting_charge_item', 'items', function() {
+			return {
+				filters: {
+					is_stock_item: 0
+				}
+			};
+		});
+
+		frm.set_query('inpatient_visit_charge_item', 'items', function() {
+			return {
+				filters: {
+					is_stock_item: 0
+				}
+			};
+		});
+	}
 });
+
+frappe.ui.form.on('Appointment Type Service Item', {
+	op_consulting_charge_item: function(frm, cdt, cdn) {
+		let d = locals[cdt][cdn];
+		if (frm.doc.price_list && d.op_consulting_charge_item) {
+			frappe.call({
+				'method': 'frappe.client.get_value',
+				args: {
+					'doctype': 'Item Price',
+					'filters': {
+						'item_code': d.op_consulting_charge_item,
+						'price_list': frm.doc.price_list
+					},
+					'fieldname': ['price_list_rate']
+				},
+				callback: function(data) {
+					if (data.message.price_list_rate) {
+						frappe.model.set_value(cdt, cdn, 'op_consulting_charge', data.message.price_list_rate);
+					}
+				}
+			});
+		}
+	},
+
+	inpatient_visit_charge_item: function(frm, cdt, cdn) {
+		let d = locals[cdt][cdn];
+		if (frm.doc.price_list && d.inpatient_visit_charge_item) {
+			frappe.call({
+				'method': 'frappe.client.get_value',
+				args: {
+					'doctype': 'Item Price',
+					'filters': {
+						'item_code': d.inpatient_visit_charge_item,
+						'price_list': frm.doc.price_list
+					},
+					'fieldname': ['price_list_rate']
+				},
+				callback: function (data) {
+					if (data.message.price_list_rate) {
+						frappe.model.set_value(cdt, cdn, 'inpatient_visit_charge', data.message.price_list_rate);
+					}
+				}
+			});
+		}
+	}
+});
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type.json b/erpnext/healthcare/doctype/appointment_type/appointment_type.json
index 58753bb..3872318 100644
--- a/erpnext/healthcare/doctype/appointment_type/appointment_type.json
+++ b/erpnext/healthcare/doctype/appointment_type/appointment_type.json
@@ -12,7 +12,10 @@
   "appointment_type",
   "ip",
   "default_duration",
-  "color"
+  "color",
+  "billing_section",
+  "price_list",
+  "items"
  ],
  "fields": [
   {
@@ -52,10 +55,27 @@
    "label": "Color",
    "no_copy": 1,
    "report_hide": 1
+  },
+  {
+   "fieldname": "billing_section",
+   "fieldtype": "Section Break",
+   "label": "Billing"
+  },
+  {
+   "fieldname": "price_list",
+   "fieldtype": "Link",
+   "label": "Price List",
+   "options": "Price List"
+  },
+  {
+   "fieldname": "items",
+   "fieldtype": "Table",
+   "label": "Appointment Type Service Items",
+   "options": "Appointment Type Service Item"
   }
  ],
  "links": [],
- "modified": "2020-02-03 21:06:05.833050",
+ "modified": "2021-01-22 09:41:05.010524",
  "modified_by": "Administrator",
  "module": "Healthcare",
  "name": "Appointment Type",
diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type.py b/erpnext/healthcare/doctype/appointment_type/appointment_type.py
index 1dacffa..67a24f3 100644
--- a/erpnext/healthcare/doctype/appointment_type/appointment_type.py
+++ b/erpnext/healthcare/doctype/appointment_type/appointment_type.py
@@ -4,6 +4,53 @@
 
 from __future__ import unicode_literals
 from frappe.model.document import Document
+import frappe
 
 class AppointmentType(Document):
-	pass
+	def validate(self):
+		if self.items and self.price_list:
+			for item in self.items:
+				existing_op_item_price = frappe.db.exists('Item Price', {
+					'item_code': item.op_consulting_charge_item,
+					'price_list': self.price_list
+				})
+
+				if not existing_op_item_price and item.op_consulting_charge_item and item.op_consulting_charge:
+					make_item_price(self.price_list, item.op_consulting_charge_item, item.op_consulting_charge)
+
+				existing_ip_item_price = frappe.db.exists('Item Price', {
+					'item_code': item.inpatient_visit_charge_item,
+					'price_list': self.price_list
+				})
+
+				if not existing_ip_item_price and item.inpatient_visit_charge_item and item.inpatient_visit_charge:
+					make_item_price(self.price_list, item.inpatient_visit_charge_item, item.inpatient_visit_charge)
+
+@frappe.whitelist()
+def get_service_item_based_on_department(appointment_type, department):
+	item_list = frappe.db.get_value('Appointment Type Service Item',
+		filters = {'medical_department': department, 'parent': appointment_type},
+		fieldname = ['op_consulting_charge_item',
+			'inpatient_visit_charge_item', 'op_consulting_charge', 'inpatient_visit_charge'],
+		as_dict = 1
+	)
+
+	# if department wise items are not set up
+	# use the generic items
+	if not item_list:
+		item_list = frappe.db.get_value('Appointment Type Service Item',
+			filters = {'parent': appointment_type},
+			fieldname = ['op_consulting_charge_item',
+				'inpatient_visit_charge_item', 'op_consulting_charge', 'inpatient_visit_charge'],
+			as_dict = 1
+		)
+
+	return item_list
+
+def make_item_price(price_list, item, item_price):
+	frappe.get_doc({
+		'doctype': 'Item Price',
+		'price_list': price_list,
+		'item_code': item,
+		'price_list_rate': item_price
+	}).insert(ignore_permissions=True, ignore_mandatory=True)
diff --git a/erpnext/healthcare/doctype/appointment_type_service_item/__init__.py b/erpnext/healthcare/doctype/appointment_type_service_item/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/healthcare/doctype/appointment_type_service_item/__init__.py
diff --git a/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.json b/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.json
new file mode 100644
index 0000000..5ff68cd
--- /dev/null
+++ b/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.json
@@ -0,0 +1,67 @@
+{
+ "actions": [],
+ "creation": "2021-01-22 09:34:53.373105",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "medical_department",
+  "op_consulting_charge_item",
+  "op_consulting_charge",
+  "column_break_4",
+  "inpatient_visit_charge_item",
+  "inpatient_visit_charge"
+ ],
+ "fields": [
+  {
+   "fieldname": "medical_department",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Medical Department",
+   "options": "Medical Department"
+  },
+  {
+   "fieldname": "op_consulting_charge_item",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Out Patient Consulting Charge Item",
+   "options": "Item"
+  },
+  {
+   "fieldname": "op_consulting_charge",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Out Patient Consulting Charge"
+  },
+  {
+   "fieldname": "column_break_4",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "inpatient_visit_charge_item",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Inpatient Visit Charge Item",
+   "options": "Item"
+  },
+  {
+   "fieldname": "inpatient_visit_charge",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Inpatient Visit Charge Item"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-01-22 09:35:26.503443",
+ "modified_by": "Administrator",
+ "module": "Healthcare",
+ "name": "Appointment Type Service 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/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.py b/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.py
new file mode 100644
index 0000000..b2e0e82
--- /dev/null
+++ b/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, 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 AppointmentTypeServiceItem(Document):
+	pass
diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py
index c324228..325c209 100644
--- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py
+++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py
@@ -121,6 +121,7 @@
 
 		stock_entry.stock_entry_type = 'Material Receipt'
 		stock_entry.to_warehouse = self.warehouse
+		stock_entry.company = self.company
 		expense_account = get_account(None, 'expense_account', 'Healthcare Settings', self.company)
 		for item in self.items:
 			if item.qty > item.actual_qty:
diff --git a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py
index 4ee5f6b..fb72073 100644
--- a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py
+++ b/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py
@@ -1,4 +1,4 @@
-# -*- coding: utf-8 -*-
+	# -*- coding: utf-8 -*-
 # Copyright (c) 2017, ESS LLP and Contributors
 # See license.txt
 from __future__ import unicode_literals
@@ -60,6 +60,7 @@
 	procedure.practitioner = practitioner
 	procedure.consume_stock = procedure_template.allow_stock_consumption
 	procedure.items = procedure_template.items
-	procedure.warehouse = frappe.db.get_single_value('Stock Settings', 'default_warehouse')
+	procedure.company = "_Test Company"
+	procedure.warehouse = "_Test Warehouse - _TC"
 	procedure.submit()
 	return procedure
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json
index cb747f9..8162f03 100644
--- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json
+++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json
@@ -159,6 +159,7 @@
    "fieldname": "op_consulting_charge",
    "fieldtype": "Currency",
    "label": "Out Patient Consulting Charge",
+   "mandatory_depends_on": "op_consulting_charge_item",
    "options": "Currency"
   },
   {
@@ -174,7 +175,8 @@
   {
    "fieldname": "inpatient_visit_charge",
    "fieldtype": "Currency",
-   "label": "Inpatient Visit Charge"
+   "label": "Inpatient Visit Charge",
+   "mandatory_depends_on": "inpatient_visit_charge_item"
   },
   {
    "depends_on": "eval: !doc.__islocal",
@@ -280,7 +282,7 @@
  ],
  "image_field": "image",
  "links": [],
- "modified": "2020-04-06 13:44:24.759623",
+ "modified": "2021-01-22 10:14:43.187675",
  "modified_by": "Administrator",
  "module": "Healthcare",
  "name": "Healthcare Practitioner",
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
index 3d5073b..0354733 100644
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
@@ -24,11 +24,13 @@
 		});
 
 		frm.set_query('practitioner', function() {
-			return {
-				filters: {
-					'department': frm.doc.department
-				}
-			};
+			if (frm.doc.department) {
+				return {
+					filters: {
+						'department': frm.doc.department
+					}
+				};
+			}
 		});
 
 		frm.set_query('service_unit', function() {
@@ -140,6 +142,20 @@
 	patient: function(frm) {
 		if (frm.doc.patient) {
 			frm.trigger('toggle_payment_fields');
+			frappe.call({
+				method: 'frappe.client.get',
+				args: {
+					doctype: 'Patient',
+					name: frm.doc.patient
+				},
+				callback: function (data) {
+					let age = null;
+					if (data.message.dob) {
+						age = calculate_age(data.message.dob);
+					}
+					frappe.model.set_value(frm.doctype, frm.docname, 'patient_age', age);
+				}
+			});
 		} else {
 			frm.set_value('patient_name', '');
 			frm.set_value('patient_sex', '');
@@ -148,6 +164,37 @@
 		}
 	},
 
+	practitioner: function(frm) {
+		if (frm.doc.practitioner ) {
+			frm.events.set_payment_details(frm);
+		}
+	},
+
+	appointment_type: function(frm) {
+		if (frm.doc.appointment_type) {
+			frm.events.set_payment_details(frm);
+		}
+	},
+
+	set_payment_details: function(frm) {
+		frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing').then(val => {
+			if (val) {
+				frappe.call({
+					method: 'erpnext.healthcare.utils.get_service_item_and_practitioner_charge',
+					args: {
+						doc: frm.doc
+					},
+					callback: function(data) {
+						if (data.message) {
+							frappe.model.set_value(frm.doctype, frm.docname, 'paid_amount', data.message.practitioner_charge);
+							frappe.model.set_value(frm.doctype, frm.docname, 'billing_item', data.message.service_item);
+						}
+					}
+				});
+			}
+		});
+	},
+
 	therapy_plan: function(frm) {
 		frm.trigger('set_therapy_type_filter');
 	},
@@ -190,14 +237,18 @@
 					// show payment fields as non-mandatory
 					frm.toggle_display('mode_of_payment', 0);
 					frm.toggle_display('paid_amount', 0);
+					frm.toggle_display('billing_item', 0);
 					frm.toggle_reqd('mode_of_payment', 0);
 					frm.toggle_reqd('paid_amount', 0);
+					frm.toggle_reqd('billing_item', 0);
 				} else {
 					// if automated appointment invoicing is disabled, hide fields
 					frm.toggle_display('mode_of_payment', data.message ? 1 : 0);
 					frm.toggle_display('paid_amount', data.message ? 1 : 0);
+					frm.toggle_display('billing_item', data.message ? 1 : 0);
 					frm.toggle_reqd('mode_of_payment', data.message ? 1 : 0);
 					frm.toggle_reqd('paid_amount', data.message ? 1 :0);
+					frm.toggle_reqd('billing_item', data.message ? 1 : 0);
 				}
 			}
 		});
@@ -540,57 +591,6 @@
 	);
 };
 
-frappe.ui.form.on('Patient Appointment', 'practitioner', function(frm) {
-	if (frm.doc.practitioner) {
-		frappe.call({
-			method: 'frappe.client.get',
-			args: {
-				doctype: 'Healthcare Practitioner',
-				name: frm.doc.practitioner
-			},
-			callback: function (data) {
-				frappe.model.set_value(frm.doctype, frm.docname, 'department', data.message.department);
-				frappe.model.set_value(frm.doctype, frm.docname, 'paid_amount', data.message.op_consulting_charge);
-				frappe.model.set_value(frm.doctype, frm.docname, 'billing_item', data.message.op_consulting_charge_item);
-			}
-		});
-	}
-});
-
-frappe.ui.form.on('Patient Appointment', 'patient', function(frm) {
-	if (frm.doc.patient) {
-		frappe.call({
-			method: 'frappe.client.get',
-			args: {
-				doctype: 'Patient',
-				name: frm.doc.patient
-			},
-			callback: function (data) {
-				let age = null;
-				if (data.message.dob) {
-					age = calculate_age(data.message.dob);
-				}
-				frappe.model.set_value(frm.doctype,frm.docname, 'patient_age', age);
-			}
-		});
-	}
-});
-
-frappe.ui.form.on('Patient Appointment', 'appointment_type', function(frm) {
-	if (frm.doc.appointment_type) {
-		frappe.call({
-			method: 'frappe.client.get',
-			args: {
-				doctype: 'Appointment Type',
-				name: frm.doc.appointment_type
-			},
-			callback: function(data) {
-				frappe.model.set_value(frm.doctype,frm.docname, 'duration',data.message.default_duration);
-			}
-		});
-	}
-});
-
 let calculate_age = function(birth) {
 	let ageMS = Date.parse(Date()) - Date.parse(birth);
 	let age = new Date();
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json
index 35600e4..83c92af 100644
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json
@@ -19,19 +19,19 @@
   "inpatient_record",
   "column_break_1",
   "company",
+  "practitioner",
+  "practitioner_name",
+  "department",
   "service_unit",
+  "section_break_12",
+  "appointment_type",
+  "duration",
   "procedure_template",
   "get_procedure_from_encounter",
   "procedure_prescription",
   "therapy_plan",
   "therapy_type",
   "get_prescribed_therapies",
-  "practitioner",
-  "practitioner_name",
-  "department",
-  "section_break_12",
-  "appointment_type",
-  "duration",
   "column_break_17",
   "appointment_date",
   "appointment_time",
@@ -79,6 +79,7 @@
    "set_only_once": 1
   },
   {
+   "fetch_from": "appointment_type.default_duration",
    "fieldname": "duration",
    "fieldtype": "Int",
    "in_filter": 1,
@@ -144,7 +145,6 @@
    "in_standard_filter": 1,
    "label": "Healthcare Practitioner",
    "options": "Healthcare Practitioner",
-   "read_only": 1,
    "reqd": 1,
    "search_index": 1,
    "set_only_once": 1
@@ -158,7 +158,6 @@
    "in_standard_filter": 1,
    "label": "Department",
    "options": "Medical Department",
-   "read_only": 1,
    "search_index": 1,
    "set_only_once": 1
   },
@@ -227,12 +226,14 @@
    "fieldname": "mode_of_payment",
    "fieldtype": "Link",
    "label": "Mode of Payment",
-   "options": "Mode of Payment"
+   "options": "Mode of Payment",
+   "read_only_depends_on": "invoiced"
   },
   {
    "fieldname": "paid_amount",
    "fieldtype": "Currency",
-   "label": "Paid Amount"
+   "label": "Paid Amount",
+   "read_only_depends_on": "invoiced"
   },
   {
    "fieldname": "column_break_2",
@@ -302,7 +303,8 @@
    "fieldname": "therapy_plan",
    "fieldtype": "Link",
    "label": "Therapy Plan",
-   "options": "Therapy Plan"
+   "options": "Therapy Plan",
+   "set_only_once": 1
   },
   {
    "fieldname": "ref_sales_invoice",
@@ -347,7 +349,7 @@
   }
  ],
  "links": [],
- "modified": "2020-12-16 13:16:58.578503",
+ "modified": "2021-02-08 13:13:15.116833",
  "modified_by": "Administrator",
  "module": "Healthcare",
  "name": "Patient Appointment",
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
index f2b94b8..1f76cd6 100755
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
@@ -26,6 +26,7 @@
 
 	def after_insert(self):
 		self.update_prescription_details()
+		self.set_payment_details()
 		invoice_appointment(self)
 		self.update_fee_validity()
 		send_confirmation_msg(self)
@@ -85,6 +86,13 @@
 	def set_appointment_datetime(self):
 		self.appointment_datetime = "%s %s" % (self.appointment_date, self.appointment_time or "00:00:00")
 
+	def set_payment_details(self):
+		if frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing'):
+			details = get_service_item_and_practitioner_charge(self)
+			self.db_set('billing_item', details.get('service_item'))
+			if not self.paid_amount:
+				self.db_set('paid_amount', details.get('practitioner_charge'))
+
 	def validate_customer_created(self):
 		if frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing'):
 			if not frappe.db.get_value('Patient', self.patient, 'customer'):
@@ -148,31 +156,37 @@
 		fee_validity = None
 
 	if automate_invoicing and not appointment_invoiced and not fee_validity:
-		sales_invoice = frappe.new_doc('Sales Invoice')
-		sales_invoice.patient = appointment_doc.patient
-		sales_invoice.customer = frappe.get_value('Patient', appointment_doc.patient, 'customer')
-		sales_invoice.appointment = appointment_doc.name
-		sales_invoice.due_date = getdate()
-		sales_invoice.company = appointment_doc.company
-		sales_invoice.debit_to = get_receivable_account(appointment_doc.company)
+		create_sales_invoice(appointment_doc)
 
-		item = sales_invoice.append('items', {})
-		item = get_appointment_item(appointment_doc, item)
 
-		# Add payments if payment details are supplied else proceed to create invoice as Unpaid
-		if appointment_doc.mode_of_payment and appointment_doc.paid_amount:
-			sales_invoice.is_pos = 1
-			payment = sales_invoice.append('payments', {})
-			payment.mode_of_payment = appointment_doc.mode_of_payment
-			payment.amount = appointment_doc.paid_amount
+def create_sales_invoice(appointment_doc):
+	sales_invoice = frappe.new_doc('Sales Invoice')
+	sales_invoice.patient = appointment_doc.patient
+	sales_invoice.customer = frappe.get_value('Patient', appointment_doc.patient, 'customer')
+	sales_invoice.appointment = appointment_doc.name
+	sales_invoice.due_date = getdate()
+	sales_invoice.company = appointment_doc.company
+	sales_invoice.debit_to = get_receivable_account(appointment_doc.company)
 
-		sales_invoice.set_missing_values(for_validate=True)
-		sales_invoice.flags.ignore_mandatory = True
-		sales_invoice.save(ignore_permissions=True)
-		sales_invoice.submit()
-		frappe.msgprint(_('Sales Invoice {0} created').format(sales_invoice.name), alert=True)
-		frappe.db.set_value('Patient Appointment', appointment_doc.name, 'invoiced', 1)
-		frappe.db.set_value('Patient Appointment', appointment_doc.name, 'ref_sales_invoice', sales_invoice.name)
+	item = sales_invoice.append('items', {})
+	item = get_appointment_item(appointment_doc, item)
+
+	# Add payments if payment details are supplied else proceed to create invoice as Unpaid
+	if appointment_doc.mode_of_payment and appointment_doc.paid_amount:
+		sales_invoice.is_pos = 1
+		payment = sales_invoice.append('payments', {})
+		payment.mode_of_payment = appointment_doc.mode_of_payment
+		payment.amount = appointment_doc.paid_amount
+
+	sales_invoice.set_missing_values(for_validate=True)
+	sales_invoice.flags.ignore_mandatory = True
+	sales_invoice.save(ignore_permissions=True)
+	sales_invoice.submit()
+	frappe.msgprint(_('Sales Invoice {0} created').format(sales_invoice.name), alert=True)
+	frappe.db.set_value('Patient Appointment', appointment_doc.name, {
+		'invoiced': 1,
+		'ref_sales_invoice': sales_invoice.name
+	})
 
 
 def check_is_new_patient(patient, name=None):
@@ -187,13 +201,14 @@
 
 
 def get_appointment_item(appointment_doc, item):
-	service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment_doc)
-	item.item_code = service_item
+	details = get_service_item_and_practitioner_charge(appointment_doc)
+	charge = appointment_doc.paid_amount or details.get('practitioner_charge')
+	item.item_code = details.get('service_item')
 	item.description = _('Consulting Charges: {0}').format(appointment_doc.practitioner)
 	item.income_account = get_income_account(appointment_doc.practitioner, appointment_doc.company)
 	item.cost_center = frappe.get_cached_value('Company', appointment_doc.company, 'cost_center')
-	item.rate = practitioner_charge
-	item.amount = practitioner_charge
+	item.rate = charge
+	item.amount = charge
 	item.qty = 1
 	item.reference_dt = 'Patient Appointment'
 	item.reference_dn = appointment_doc.name
diff --git a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py
index f7ec6f5..2bb8a53 100644
--- a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py
+++ b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py
@@ -32,7 +32,8 @@
 		patient, medical_department, practitioner = create_healthcare_docs()
 		frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
 		appointment = create_appointment(patient, practitioner, add_days(nowdate(), 4), invoice = 1)
-		self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'invoiced'), 1)
+		appointment.reload()
+		self.assertEqual(appointment.invoiced, 1)
 		encounter = make_encounter(appointment.name)
 		self.assertTrue(encounter)
 		self.assertEqual(encounter.company, appointment.company)
@@ -41,7 +42,7 @@
 		# invoiced flag mapped from appointment
 		self.assertEqual(encounter.invoiced, frappe.db.get_value('Patient Appointment', appointment.name, 'invoiced'))
 
-	def test_invoicing(self):
+	def test_auto_invoicing(self):
 		patient, medical_department, practitioner = create_healthcare_docs()
 		frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
 		frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 0)
@@ -57,6 +58,50 @@
 		self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'patient'), appointment.patient)
 		self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'paid_amount'), appointment.paid_amount)
 
+	def test_auto_invoicing_based_on_department(self):
+		patient, medical_department, practitioner = create_healthcare_docs()
+		frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
+		frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
+		appointment_type = create_appointment_type()
+
+		appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2),
+			invoice=1, appointment_type=appointment_type.name, department='_Test Medical Department')
+		appointment.reload()
+
+		self.assertEqual(appointment.invoiced, 1)
+		self.assertEqual(appointment.billing_item, 'HLC-SI-001')
+		self.assertEqual(appointment.paid_amount, 200)
+
+		sales_invoice_name = frappe.db.get_value('Sales Invoice Item', {'reference_dn': appointment.name}, 'parent')
+		self.assertTrue(sales_invoice_name)
+		self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'paid_amount'), appointment.paid_amount)
+
+	def test_auto_invoicing_according_to_appointment_type_charge(self):
+		patient, medical_department, practitioner = create_healthcare_docs()
+		frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
+		frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
+
+		item = create_healthcare_service_items()
+		items = [{
+				'op_consulting_charge_item': item,
+				'op_consulting_charge': 300
+		}]
+		appointment_type = create_appointment_type(args={
+				'name': 'Generic Appointment Type charge',
+				'items': items
+			})
+
+		appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2),
+			invoice=1, appointment_type=appointment_type.name)
+		appointment.reload()
+
+		self.assertEqual(appointment.invoiced, 1)
+		self.assertEqual(appointment.billing_item, item)
+		self.assertEqual(appointment.paid_amount, 300)
+
+		sales_invoice_name = frappe.db.get_value('Sales Invoice Item', {'reference_dn': appointment.name}, 'parent')
+		self.assertTrue(sales_invoice_name)
+
 	def test_appointment_cancel(self):
 		patient, medical_department, practitioner = create_healthcare_docs()
 		frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 1)
@@ -178,14 +223,15 @@
 		encounter.submit()
 		return encounter
 
-def create_appointment(patient, practitioner, appointment_date, invoice=0, procedure_template=0, service_unit=None, save=1):
+def create_appointment(patient, practitioner, appointment_date, invoice=0, procedure_template=0,
+	service_unit=None, appointment_type=None, save=1, department=None):
 	item = create_healthcare_service_items()
 	frappe.db.set_value('Healthcare Settings', None, 'inpatient_visit_charge_item', item)
 	frappe.db.set_value('Healthcare Settings', None, 'op_consulting_charge_item', item)
 	appointment = frappe.new_doc('Patient Appointment')
 	appointment.patient = patient
 	appointment.practitioner = practitioner
-	appointment.department = '_Test Medical Department'
+	appointment.department = department or '_Test Medical Department'
 	appointment.appointment_date = appointment_date
 	appointment.company = '_Test Company'
 	appointment.duration = 15
@@ -193,7 +239,8 @@
 		appointment.service_unit = service_unit
 	if invoice:
 		appointment.mode_of_payment = 'Cash'
-		appointment.paid_amount = 500
+	if appointment_type:
+		appointment.appointment_type = appointment_type
 	if procedure_template:
 		appointment.procedure_template = create_clinical_procedure_template().get('name')
 	if save:
@@ -223,4 +270,29 @@
 	template.description = 'Knee Surgery and Rehab'
 	template.rate = 50000
 	template.save()
-	return template
\ No newline at end of file
+	return template
+
+def create_appointment_type(args=None):
+	if not args:
+		args =  frappe.local.form_dict
+
+	name = args.get('name') or 'Test Appointment Type wise Charge'
+
+	if frappe.db.exists('Appointment Type', name):
+		return frappe.get_doc('Appointment Type', name)
+
+	else:
+		item = create_healthcare_service_items()
+		items = [{
+				'medical_department': '_Test Medical Department',
+				'op_consulting_charge_item': item,
+				'op_consulting_charge': 200
+		}]
+		return frappe.get_doc({
+			'doctype': 'Appointment Type',
+			'appointment_type': args.get('name') or 'Test Appointment Type wise Charge',
+			'default_duration': args.get('default_duration') or 20,
+			'color': args.get('color') or '#7575ff',
+			'price_list': args.get('price_list') or frappe.db.get_value("Price List", {"selling": 1}),
+			'items': args.get('items') or items
+		}).insert()
\ No newline at end of file
diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py
index d4027df..d3d22c8 100644
--- a/erpnext/healthcare/utils.py
+++ b/erpnext/healthcare/utils.py
@@ -5,9 +5,11 @@
 from __future__ import unicode_literals
 import math
 import frappe
+import json
 from frappe import _
 from frappe.utils.formatters import format_value
 from frappe.utils import time_diff_in_hours, rounded
+from six import string_types
 from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_income_account
 from erpnext.healthcare.doctype.fee_validity.fee_validity import create_fee_validity
 from erpnext.healthcare.doctype.lab_test.lab_test import create_multiple
@@ -64,7 +66,9 @@
 			income_account = None
 			service_item = None
 			if appointment.practitioner:
-				service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment)
+				details = get_service_item_and_practitioner_charge(appointment)
+				service_item = details.get('service_item')
+				practitioner_charge = details.get('practitioner_charge')
 				income_account = get_income_account(appointment.practitioner, appointment.company)
 			appointments_to_invoice.append({
 				'reference_type': 'Patient Appointment',
@@ -97,7 +101,9 @@
 						frappe.db.get_single_value('Healthcare Settings', 'do_not_bill_inpatient_encounters'):
 						continue
 
-					service_item, practitioner_charge = get_service_item_and_practitioner_charge(encounter)
+					details = get_service_item_and_practitioner_charge(encounter)
+					service_item = details.get('service_item')
+					practitioner_charge = details.get('practitioner_charge')
 					income_account = get_income_account(encounter.practitioner, encounter.company)
 
 				encounters_to_invoice.append({
@@ -173,7 +179,7 @@
 		if procedure.invoice_separately_as_consumables and procedure.consume_stock \
 			and procedure.status == 'Completed' and not procedure.consumption_invoiced:
 
-			service_item = get_healthcare_service_item('clinical_procedure_consumable_item')
+			service_item = frappe.db.get_single_value('Healthcare Settings', 'clinical_procedure_consumable_item')
 			if not service_item:
 				msg = _('Please Configure Clinical Procedure Consumable Item in ')
 				msg += '''<b><a href='/app/Form/Healthcare Settings'>Healthcare Settings</a></b>'''
@@ -304,24 +310,50 @@
 
 	return therapy_sessions_to_invoice
 
-
+@frappe.whitelist()
 def get_service_item_and_practitioner_charge(doc):
+	if isinstance(doc, string_types):
+		doc = json.loads(doc)
+		doc = frappe.get_doc(doc)
+
+	service_item = None
+	practitioner_charge = None
+	department = doc.medical_department if doc.doctype == 'Patient Encounter' else doc.department
+
 	is_inpatient = doc.inpatient_record
-	if is_inpatient:
-		service_item = get_practitioner_service_item(doc.practitioner, 'inpatient_visit_charge_item')
+
+	if doc.get('appointment_type'):
+		service_item, practitioner_charge = get_appointment_type_service_item(doc.appointment_type, department, is_inpatient)
+
+	if not service_item and not practitioner_charge:
+		service_item, practitioner_charge = get_practitioner_service_item(doc.practitioner, is_inpatient)
 		if not service_item:
-			service_item = get_healthcare_service_item('inpatient_visit_charge_item')
-	else:
-		service_item = get_practitioner_service_item(doc.practitioner, 'op_consulting_charge_item')
-		if not service_item:
-			service_item = get_healthcare_service_item('op_consulting_charge_item')
+			service_item = get_healthcare_service_item(is_inpatient)
+
 	if not service_item:
 		throw_config_service_item(is_inpatient)
 
-	practitioner_charge = get_practitioner_charge(doc.practitioner, is_inpatient)
 	if not practitioner_charge:
 		throw_config_practitioner_charge(is_inpatient, doc.practitioner)
 
+	return {'service_item': service_item, 'practitioner_charge': practitioner_charge}
+
+
+def get_appointment_type_service_item(appointment_type, department, is_inpatient):
+	from erpnext.healthcare.doctype.appointment_type.appointment_type import get_service_item_based_on_department
+
+	item_list = get_service_item_based_on_department(appointment_type, department)
+	service_item = None
+	practitioner_charge = None
+
+	if item_list:
+		if is_inpatient:
+			service_item = item_list.get('inpatient_visit_charge_item')
+			practitioner_charge = item_list.get('inpatient_visit_charge')
+		else:
+			service_item = item_list.get('op_consulting_charge_item')
+			practitioner_charge = item_list.get('op_consulting_charge')
+
 	return service_item, practitioner_charge
 
 
@@ -345,12 +377,27 @@
 	frappe.throw(msg, title=_('Missing Configuration'))
 
 
-def get_practitioner_service_item(practitioner, service_item_field):
-	return frappe.db.get_value('Healthcare Practitioner', practitioner, service_item_field)
+def get_practitioner_service_item(practitioner, is_inpatient):
+	service_item = None
+	practitioner_charge = None
+
+	if is_inpatient:
+		service_item, practitioner_charge = frappe.db.get_value('Healthcare Practitioner', practitioner, ['inpatient_visit_charge_item', 'inpatient_visit_charge'])
+	else:
+		service_item, practitioner_charge = frappe.db.get_value('Healthcare Practitioner', practitioner, ['op_consulting_charge_item', 'op_consulting_charge'])
+
+	return service_item, practitioner_charge
 
 
-def get_healthcare_service_item(service_item_field):
-	return frappe.db.get_single_value('Healthcare Settings', service_item_field)
+def get_healthcare_service_item(is_inpatient):
+	service_item = None
+
+	if is_inpatient:
+		service_item = frappe.db.get_single_value('Healthcare Settings', 'inpatient_visit_charge_item')
+	else:
+		service_item = frappe.db.get_single_value('Healthcare Settings', 'op_consulting_charge_item')
+
+	return service_item
 
 
 def get_practitioner_charge(practitioner, is_inpatient):
@@ -381,7 +428,8 @@
 		invoiced = True
 
 	if item.reference_dt == 'Clinical Procedure':
-		if get_healthcare_service_item('clinical_procedure_consumable_item') == item.item_code:
+		service_item = frappe.db.get_single_value('Healthcare Settings', 'clinical_procedure_consumable_item')
+		if service_item == item.item_code:
 			frappe.db.set_value(item.reference_dt, item.reference_dn, 'consumption_invoiced', invoiced)
 		else:
 			frappe.db.set_value(item.reference_dt, item.reference_dn, 'invoiced', invoiced)
@@ -403,7 +451,8 @@
 
 
 def validate_invoiced_on_submit(item):
-	if item.reference_dt == 'Clinical Procedure' and get_healthcare_service_item('clinical_procedure_consumable_item') == item.item_code:
+	if item.reference_dt == 'Clinical Procedure' and \
+		frappe.db.get_single_value('Healthcare Settings', 'clinical_procedure_consumable_item') == item.item_code:
 		is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, 'consumption_invoiced')
 	else:
 		is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, 'invoiced')
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 109d921..347da6b 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -399,6 +399,7 @@
 		'erpnext.controllers.taxes_and_totals.get_itemised_tax_breakup_header': 'erpnext.regional.india.utils.get_itemised_tax_breakup_header',
 		'erpnext.controllers.taxes_and_totals.get_itemised_tax_breakup_data': 'erpnext.regional.india.utils.get_itemised_tax_breakup_data',
 		'erpnext.accounts.party.get_regional_address_details': 'erpnext.regional.india.utils.get_regional_address_details',
+		'erpnext.controllers.taxes_and_totals.get_regional_round_off_accounts': 'erpnext.regional.india.utils.get_regional_round_off_accounts',
 		'erpnext.hr.utils.calculate_annual_eligible_hra_exemption': 'erpnext.regional.india.utils.calculate_annual_eligible_hra_exemption',
 		'erpnext.hr.utils.calculate_hra_exemption_for_period': 'erpnext.regional.india.utils.calculate_hra_exemption_for_period',
 		'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_regional_gl_entries': 'erpnext.regional.india.utils.make_regional_gl_entries',
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index ec28eb7..662a06b 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -267,6 +267,17 @@
 			fields = ["sum(total_time_in_mins) as time_in_mins", "sum(total_completed_qty) as completed_qty"],
 			filters = {"docstatus": 1, "work_order": self.work_order, "operation_id": self.operation_id})
 
+	def set_transferred_qty_in_job_card(self, ste_doc):
+		for row in ste_doc.items:
+			if not row.job_card_item: continue
+
+			qty = frappe.db.sql(""" SELECT SUM(qty) from `tabStock Entry Detail` sed, `tabStock Entry` se
+				WHERE  sed.job_card_item = %s and se.docstatus = 1 and sed.parent = se.name and
+				se.purpose = 'Material Transfer for Manufacture'
+			""", (row.job_card_item))[0][0]
+
+			frappe.db.set_value('Job Card Item', row.job_card_item, 'transferred_qty', flt(qty))
+
 	def set_transferred_qty(self, update_status=False):
 		if not self.items:
 			self.transferred_qty = self.for_quantity if self.docstatus == 1 else 0
@@ -279,7 +290,8 @@
 			self.transferred_qty = frappe.db.get_value('Stock Entry', {
 				'job_card': self.name,
 				'work_order': self.work_order,
-				'docstatus': 1
+				'docstatus': 1,
+				'purpose': 'Material Transfer for Manufacture'
 			}, 'sum(fg_completed_qty)') or 0
 
 		self.db_set("transferred_qty", self.transferred_qty)
@@ -420,6 +432,7 @@
 		target.purpose = "Material Transfer for Manufacture"
 		target.from_bom = 1
 		target.fg_completed_qty = source.get('for_quantity', 0) - source.get('transferred_qty', 0)
+		target.set_transfer_qty()
 		target.calculate_rate_and_amount()
 		target.set_missing_values()
 		target.set_stock_entry_type()
@@ -437,9 +450,10 @@
 			"field_map": {
 				"source_warehouse": "s_warehouse",
 				"required_qty": "qty",
-				"uom": "stock_uom"
+				"name": "job_card_item"
 			},
 			"postprocess": update_item,
+			"condition": lambda doc: doc.required_qty > 0
 		}
 	}, target_doc, set_missing_values)
 
diff --git a/erpnext/manufacturing/doctype/job_card_item/job_card_item.json b/erpnext/manufacturing/doctype/job_card_item/job_card_item.json
index bc9fe10..100ef4c 100644
--- a/erpnext/manufacturing/doctype/job_card_item/job_card_item.json
+++ b/erpnext/manufacturing/doctype/job_card_item/job_card_item.json
@@ -1,363 +1,120 @@
 {
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2018-07-09 17:20:44.737289", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
+ "actions": [],
+ "creation": "2018-07-09 17:20:44.737289",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "item_code",
+  "source_warehouse",
+  "uom",
+  "item_group",
+  "column_break_3",
+  "stock_uom",
+  "item_name",
+  "description",
+  "qty_section",
+  "required_qty",
+  "column_break_9",
+  "transferred_qty",
+  "allow_alternative_item"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "item_code", 
-   "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": "Item Code", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Item", 
-   "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, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "item_code",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Item Code",
+   "options": "Item",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "source_warehouse", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 1, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Source 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": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "source_warehouse",
+   "fieldtype": "Link",
+   "ignore_user_permissions": 1,
+   "in_list_view": 1,
+   "label": "Source Warehouse",
+   "options": "Warehouse"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "uom", 
-   "fieldtype": "Link", 
-   "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": "UOM", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "UOM", 
-   "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, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "uom",
+   "fieldtype": "Link",
+   "label": "UOM",
+   "options": "UOM"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_3", 
-   "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, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 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": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Item Name", 
-   "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, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "item_name",
+   "fieldtype": "Data",
+   "label": "Item Name",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "description", 
-   "fieldtype": "Text", 
-   "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": "Description", 
-   "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, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "description",
+   "fieldtype": "Text",
+   "label": "Description",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "qty_section", 
-   "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": "Qty", 
-   "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, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "qty_section",
+   "fieldtype": "Section Break",
+   "label": "Qty"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "required_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": "Required 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, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "required_qty",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Required Qty",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 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, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_9",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "allow_alternative_item", 
-   "fieldtype": "Check", 
-   "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": "Allow Alternative Item", 
-   "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, 
-   "translatable": 0, 
-   "unique": 0
+   "default": "0",
+   "fieldname": "allow_alternative_item",
+   "fieldtype": "Check",
+   "label": "Allow Alternative Item"
+  },
+  {
+   "fetch_from": "item_code.item_group",
+   "fieldname": "item_group",
+   "fieldtype": "Link",
+   "label": "Item Group",
+   "options": "Item Group",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "item_code.stock_uom",
+   "fieldname": "stock_uom",
+   "fieldtype": "Link",
+   "label": "Stock UOM",
+   "options": "UOM"
+  },
+  {
+   "fieldname": "transferred_qty",
+   "fieldtype": "Float",
+   "label": "Transferred Qty",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1
   }
- ], 
- "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, 
- "modified": "2018-08-28 15:23:48.099459", 
- "modified_by": "Administrator", 
- "module": "Manufacturing", 
- "name": "Job Card 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, 
- "track_views": 0
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-02-11 13:50:13.804108",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Job Card 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/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index 06a8e19..00e8c54 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -94,11 +94,11 @@
 		wo_order = make_wo_order_test_record(item="_Test FG Item", qty=2,
 			source_warehouse=warehouse, skip_transfer=1)
 
-		bin1_on_submit = get_bin(item, warehouse)
+		reserved_qty_on_submission = cint(get_bin(item, warehouse).reserved_qty_for_production)
 
 		# reserved qty for production is updated
-		self.assertEqual(cint(bin1_at_start.reserved_qty_for_production) + 2,
-			cint(bin1_on_submit.reserved_qty_for_production))
+		self.assertEqual(cint(bin1_at_start.reserved_qty_for_production) + 2, reserved_qty_on_submission)
+
 
 		test_stock_entry.make_stock_entry(item_code="_Test Item",
 			target=warehouse, qty=100, basic_rate=100)
@@ -109,9 +109,9 @@
 		s.submit()
 
 		bin1_at_completion = get_bin(item, warehouse)
-
+		
 		self.assertEqual(cint(bin1_at_completion.reserved_qty_for_production),
-			cint(bin1_on_submit.reserved_qty_for_production) - 1)
+			reserved_qty_on_submission - 1)
 
 	def test_production_item(self):
 		wo_order = make_wo_order_test_record(item="_Test FG Item", qty=1, do_not_save=True)
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 3b7c6ab..e5ee551 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -752,3 +752,5 @@
 erpnext.patches.v13_0.convert_qi_parameter_to_link_field
 erpnext.patches.v13_0.setup_patient_history_settings_for_standard_doctypes
 erpnext.patches.v13_0.add_naming_series_to_old_projects # 1-02-2021
+erpnext.patches.v12_0.add_state_code_for_ladakh
+erpnext.patches.v13_0.item_reposting_for_incorrect_sl_and_gl
diff --git a/erpnext/patches/v12_0/add_state_code_for_ladakh.py b/erpnext/patches/v12_0/add_state_code_for_ladakh.py
new file mode 100644
index 0000000..d41101c
--- /dev/null
+++ b/erpnext/patches/v12_0/add_state_code_for_ladakh.py
@@ -0,0 +1,16 @@
+import frappe
+from erpnext.regional.india import states
+
+def execute():
+
+	company = frappe.get_all('Company', filters = {'country': 'India'})
+	if not company:
+		return
+
+	custom_fields = ['Address-gst_state', 'Tax Category-gst_state']
+
+	# Update options in gst_state custom fields
+	for field in custom_fields:
+		gst_state_field = frappe.get_doc('Custom Field', field)
+		gst_state_field.options = '\n'.join(states)
+		gst_state_field.save()
diff --git a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py
new file mode 100644
index 0000000..f60e0d3
--- /dev/null
+++ b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py
@@ -0,0 +1,27 @@
+import frappe
+from frappe import _
+from erpnext.stock.stock_ledger import update_entries_after
+from erpnext.accounts.utils import update_gl_entries_after
+
+def execute():
+	data = frappe.db.sql(''' SELECT name, item_code, warehouse, voucher_type, voucher_no, posting_date, posting_time
+		from `tabStock Ledger Entry` where creation > '2020-12-26 12:58:55.903836' and is_cancelled = 0
+		order by timestamp(posting_date, posting_time) asc, creation asc''', as_dict=1)
+
+	for index, d in enumerate(data):
+		update_entries_after({
+			"item_code": d.item_code,
+			"warehouse": d.warehouse,
+			"posting_date": d.posting_date,
+			"posting_time": d.posting_time,
+			"voucher_type": d.voucher_type,
+			"voucher_no": d.voucher_no,
+			"sle_id": d.name
+		}, allow_negative_stock=True)
+
+	frappe.db.auto_commit_on_many_writes = 1
+
+	for row in frappe.get_all('Company', filters= {'enable_perpetual_inventory': 1}):
+		update_gl_entries_after('2020-12-25', '01:58:55', company=row.name)
+
+	frappe.db.auto_commit_on_many_writes = 0
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py
index 2d3bc57..60aff02 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py
@@ -1103,10 +1103,10 @@
 			self.calculate_total_for_salary_slip_based_on_timesheet()
 		else:
 			self.total_deduction = 0.0
-			if self.earnings:
+			if hasattr(self, "earnings"):
 				for earning in self.earnings:
 					self.gross_pay += flt(earning.amount, earning.precision("amount"))
-			if self.deductions:
+			if hasattr(self, "deductions"):
 				for deduction in self.deductions:
 					self.total_deduction += flt(deduction.amount, deduction.precision("amount"))
 			self.net_pay = flt(self.gross_pay) - flt(self.total_deduction) - flt(self.total_loan_repayment)
diff --git a/erpnext/public/images/erpnext-logo.png b/erpnext/public/images/erpnext-logo.png
new file mode 100644
index 0000000..3090727
--- /dev/null
+++ b/erpnext/public/images/erpnext-logo.png
Binary files differ
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 416495c..d81321b 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -2,7 +2,9 @@
 // License: GNU General Public License v3. See license.txt
 
 erpnext.taxes_and_totals = erpnext.payments.extend({
-	setup: function() {},
+	setup: function() {
+		this.fetch_round_off_accounts();
+	},
 
 	apply_pricing_rule_on_item: function(item) {
 		let effective_item_rate = item.price_list_rate;
@@ -152,6 +154,22 @@
 		});
 	},
 
+	fetch_round_off_accounts: function() {
+		let me = this;
+		frappe.flags.round_off_applicable_accounts = [];
+
+		return frappe.call({
+			"method": "erpnext.controllers.taxes_and_totals.get_round_off_applicable_accounts",
+			"args": {
+				"company": me.frm.doc.company,
+				"account_list": frappe.flags.round_off_applicable_accounts
+			},
+			callback: function(r) {
+				frappe.flags.round_off_applicable_accounts.push(...r.message);
+			}
+		});
+	},
+
 	determine_exclusive_rate: function() {
 		var me = this;
 
@@ -372,11 +390,21 @@
 		} else if (tax.charge_type == "On Item Quantity") {
 			current_tax_amount = tax_rate * item.qty;
 		}
+
+		current_tax_amount = this.get_final_tax_amount(tax, current_tax_amount);
 		this.set_item_wise_tax(item, tax, tax_rate, current_tax_amount);
 
 		return current_tax_amount;
 	},
 
+	get_final_tax_amount: function(tax, current_tax_amount) {
+		if (frappe.flags.round_off_applicable_accounts.includes(tax.account_head)) {
+			current_tax_amount = Math.round(current_tax_amount);
+		}
+
+		return current_tax_amount;
+	},
+
 	set_item_wise_tax: function(item, tax, tax_rate, current_tax_amount) {
 		// store tax breakup for each item
 		let tax_detail = tax.item_wise_tax_detail;
diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js
index 2623c3c..d49a813 100644
--- a/erpnext/public/js/utils/serial_no_batch_selector.js
+++ b/erpnext/public/js/utils/serial_no_batch_selector.js
@@ -140,6 +140,7 @@
 					() => me.update_batch_serial_no_items(),
 					() => {
 						refresh_field("items");
+						refresh_field("packed_items");
 						if (me.callback) {
 							return me.callback(me.item);
 						}
@@ -154,7 +155,7 @@
 			if (this.item.serial_no) {
 				this.dialog.fields_dict.serial_no.set_value(this.item.serial_no);
 			}
-			
+
 			if (this.has_batch && !this.has_serial_no && d.batch_no) {
 				this.frm.doc.items.forEach(data => {
 					if(data.item_code == d.item_code) {
@@ -231,7 +232,7 @@
 				this.map_row_values(row, batch, 'batch_no',
 					'selected_qty', this.values.warehouse);
 			});
-		} 
+		}
 	},
 
 	update_serial_no_item() {
@@ -250,7 +251,7 @@
 				filters: { 'name': ["in", selected_serial_nos]},
 				fields: ["batch_no", "name"]
 			}).then((data) => {
-				// data = [{batch_no: 'batch-1', name: "SR-001"}, 
+				// data = [{batch_no: 'batch-1', name: "SR-001"},
 				// 	{batch_no: 'batch-2', name: "SR-003"}, {batch_no: 'batch-2', name: "SR-004"}]
 				const batch_serial_map = data.reduce((acc, d) => {
 					if (!acc[d['batch_no']]) acc[d['batch_no']] = [];
@@ -298,6 +299,8 @@
 		} else {
 			row.warehouse = values.warehouse || warehouse;
 		}
+
+		this.frm.dirty();
 	},
 
 	update_total_qty: function() {
diff --git a/erpnext/public/less/hub.less b/erpnext/public/less/hub.less
index 8cb7a9c..29deada 100644
--- a/erpnext/public/less/hub.less
+++ b/erpnext/public/less/hub.less
@@ -32,7 +32,12 @@
 	}
 
 	.hub-image-loading, .hub-image-broken {
-		.img-background();
+		content: " ";
+		position: absolute;
+		left: 0;
+		height: 100%;
+		width: 100%;
+		background-color: var(--bg-light-gray);
 		display: flex;
 		align-items: center;
 		justify-content: center;
diff --git a/erpnext/regional/doctype/gst_settings/gst_settings.json b/erpnext/regional/doctype/gst_settings/gst_settings.json
index 98c33ad..95b930c 100644
--- a/erpnext/regional/doctype/gst_settings/gst_settings.json
+++ b/erpnext/regional/doctype/gst_settings/gst_settings.json
@@ -1,222 +1,86 @@
 {
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2017-06-27 15:09:01.318003", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
+ "actions": [],
+ "creation": "2017-06-27 15:09:01.318003",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "gst_summary",
+  "column_break_2",
+  "round_off_gst_values",
+  "gstin_email_sent_on",
+  "section_break_4",
+  "gst_accounts",
+  "b2c_limit"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "gst_summary", 
-   "fieldtype": "HTML", 
-   "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": "GST Summary", 
-   "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": "gst_summary",
+   "fieldtype": "HTML",
+   "label": "GST Summary",
+   "show_days": 1,
+   "show_seconds": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_2", 
-   "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_2",
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "gstin_email_sent_on", 
-   "fieldtype": "Date", 
-   "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": "GSTIN Email Sent On", 
-   "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": "gstin_email_sent_on",
+   "fieldtype": "Date",
+   "label": "GSTIN Email Sent On",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "section_break_4", 
-   "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": "section_break_4",
+   "fieldtype": "Section Break",
+   "show_days": 1,
+   "show_seconds": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "gst_accounts", 
-   "fieldtype": "Table", 
-   "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": "GST Accounts", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "GST Account", 
-   "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": "gst_accounts",
+   "fieldtype": "Table",
+   "label": "GST Accounts",
+   "options": "GST Account",
+   "show_days": 1,
+   "show_seconds": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "250000", 
-   "description": "Set Invoice Value for B2C. B2CL and B2CS calculated based on this invoice value.", 
-   "fieldname": "b2c_limit", 
-   "fieldtype": "Data", 
-   "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": "B2C Limit", 
-   "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": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
+   "default": "250000",
+   "description": "Set Invoice Value for B2C. B2CL and B2CS calculated based on this invoice value.",
+   "fieldname": "b2c_limit",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "B2C Limit",
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "default": "0",
+   "description": "Enabling this option will round off individual GST components in all the Invoices",
+   "fieldname": "round_off_gst_values",
+   "fieldtype": "Check",
+   "label": "Round Off GST Values",
+   "show_days": 1,
+   "show_seconds": 1
   }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 1, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2018-02-14 08:14:15.375181", 
- "modified_by": "Administrator", 
- "module": "Regional", 
- "name": "GST Settings", 
- "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
-}
\ No newline at end of file
+ ],
+ "index_web_pages_for_search": 1,
+ "issingle": 1,
+ "links": [],
+ "modified": "2021-01-28 17:19:47.969260",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "GST Settings",
+ "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/regional/doctype/gstr_3b_report/test_gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/test_gstr_3b_report.py
index 8174da2..023b4ed 100644
--- a/erpnext/regional/doctype/gstr_3b_report/test_gstr_3b_report.py
+++ b/erpnext/regional/doctype/gstr_3b_report/test_gstr_3b_report.py
@@ -14,8 +14,20 @@
 test_dependencies = ["Territory", "Customer Group", "Supplier Group", "Item"]
 
 class TestGSTR3BReport(unittest.TestCase):
-	def test_gstr_3b_report(self):
+	def setUp(self):
+		frappe.set_user("Administrator")
 
+		frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company GST'")
+		frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company GST'")
+		frappe.db.sql("delete from `tabGSTR 3B Report` where company='_Test Company GST'")
+
+		make_company()
+		make_item("Milk", properties = {"is_nil_exempt": 1, "standard_rate": 0.000000})
+		set_account_heads()
+		make_customers()
+		make_suppliers()
+
+	def test_gstr_3b_report(self):
 		month_number_mapping = {
 			1: "January",
 			2: "February",
@@ -31,17 +43,6 @@
 			12: "December"
 		}
 
-		frappe.set_user("Administrator")
-
-		frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company GST'")
-		frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company GST'")
-		frappe.db.sql("delete from `tabGSTR 3B Report` where company='_Test Company GST'")
-
-		make_company()
-		make_item("Milk", properties = {"is_nil_exempt": 1, "standard_rate": 0.000000})
-		set_account_heads()
-		make_customers()
-		make_suppliers()
 		make_sales_invoice()
 		create_purchase_invoices()
 
@@ -67,6 +68,42 @@
 		self.assertEqual(output["itc_elg"]["itc_avl"][4]["samt"], 22.50)
 		self.assertEqual(output["itc_elg"]["itc_avl"][4]["camt"], 22.50)
 
+	def test_gst_rounding(self):
+		gst_settings = frappe.get_doc('GST Settings')
+		gst_settings.round_off_gst_values = 1
+		gst_settings.save()
+
+		current_country = frappe.flags.country
+		frappe.flags.country = 'India'
+
+		si = create_sales_invoice(company="_Test Company GST",
+			customer = '_Test GST Customer',
+			currency = 'INR',
+			warehouse = 'Finished Goods - _GST',
+			debit_to = 'Debtors - _GST',
+			income_account = 'Sales - _GST',
+			expense_account = 'Cost of Goods Sold - _GST',
+			cost_center = 'Main - _GST',
+			rate=216,
+			do_not_save=1
+		)
+
+		si.append("taxes", {
+			"charge_type": "On Net Total",
+			"account_head": "IGST - _GST",
+			"cost_center": "Main - _GST",
+			"description": "IGST @ 18.0",
+			"rate": 18
+		})
+
+		si.save()
+		# Check for 39 instead of 38.88
+		self.assertEqual(si.taxes[0].base_tax_amount_after_discount_amount, 39)
+
+		frappe.flags.country = current_country
+		gst_settings.round_off_gst_values = 1
+		gst_settings.save()
+
 def make_sales_invoice():
 	si = create_sales_invoice(company="_Test Company GST",
 			customer = '_Test GST Customer',
@@ -145,7 +182,6 @@
 	si3.submit()
 
 def create_purchase_invoices():
-
 	pi = make_purchase_invoice(
 			company="_Test Company GST",
 			supplier = '_Test Registered Supplier',
@@ -193,7 +229,6 @@
 	pi1.submit()
 
 def make_suppliers():
-
 	if not frappe.db.exists("Supplier", "_Test Registered Supplier"):
 		frappe.get_doc({
 			"supplier_group": "_Test Supplier Group",
@@ -257,7 +292,6 @@
 		address.save()
 
 def make_customers():
-
 	if not frappe.db.exists("Customer", "_Test GST Customer"):
 		frappe.get_doc({
 			"customer_group": "_Test Customer Group",
@@ -354,9 +388,9 @@
 		address.save()
 
 def make_company():
-
 	if frappe.db.exists("Company", "_Test Company GST"):
 		return
+
 	company = frappe.new_doc("Company")
 	company.company_name = "_Test Company GST"
 	company.abbr = "_GST"
@@ -388,7 +422,6 @@
 		address.save()
 
 def set_account_heads():
-
 	gst_settings = frappe.get_doc("GST Settings")
 
 	gst_account = frappe.get_all(
diff --git a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py
index e8a8ed8..ad60db0 100644
--- a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py
+++ b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py
@@ -5,12 +5,16 @@
 from __future__ import unicode_literals
 import frappe
 from frappe import _
-from frappe.utils import getdate
+from frappe.utils import getdate, get_link_to_form
 from frappe.model.document import Document
 from erpnext.accounts.utils import get_fiscal_year
 
 class LowerDeductionCertificate(Document):
 	def validate(self):
+		self.validate_dates()
+		self.validate_supplier_against_section_code()
+		
+	def validate_dates(self):
 		if getdate(self.valid_upto) < getdate(self.valid_from):
 			frappe.throw(_("Valid Upto date cannot be before Valid From date"))
 
@@ -24,3 +28,20 @@
 			<= fiscal_year.year_end_date):
 			frappe.throw(_("Valid Upto date not in Fiscal Year {0}").format(frappe.bold(self.fiscal_year)))
 
+	def validate_supplier_against_section_code(self):
+		duplicate_certificate = frappe.db.get_value('Lower Deduction Certificate', {'supplier': self.supplier, 'section_code': self.section_code}, ['name', 'valid_from', 'valid_upto'], as_dict=True)
+		if duplicate_certificate and self.are_dates_overlapping(duplicate_certificate):
+			certificate_link = get_link_to_form('Lower Deduction Certificate', duplicate_certificate.name)
+			frappe.throw(_("There is already a valid Lower Deduction Certificate {0} for Supplier {1} against Section Code {2} for this time period.")
+				.format(certificate_link, frappe.bold(self.supplier), frappe.bold(self.section_code)))
+
+	def are_dates_overlapping(self,duplicate_certificate):
+		valid_from = duplicate_certificate.valid_from
+		valid_upto = duplicate_certificate.valid_upto
+		if valid_from <= getdate(self.valid_from) <= valid_upto:
+			return True
+		elif valid_from <= getdate(self.valid_upto) <= valid_upto:
+			return True
+		elif getdate(self.valid_from) <= valid_from and valid_upto <= getdate(self.valid_upto):
+			return True
+		return False
\ No newline at end of file
diff --git a/erpnext/regional/india/__init__.py b/erpnext/regional/india/__init__.py
index d6221a8..378b735 100644
--- a/erpnext/regional/india/__init__.py
+++ b/erpnext/regional/india/__init__.py
@@ -20,6 +20,7 @@
  'Jharkhand',
  'Karnataka',
  'Kerala',
+ 'Ladakh',
  'Lakshadweep Islands',
  'Madhya Pradesh',
  'Maharashtra',
@@ -59,6 +60,7 @@
  "Jharkhand": "20",
  "Karnataka": "29",
  "Kerala": "32",
+ "Ladakh": "38",
  "Lakshadweep Islands": "31",
  "Madhya Pradesh": "23",
  "Maharashtra": "27",
@@ -80,4 +82,4 @@
  "West Bengal": "19",
 }
 
-number_state_mapping = {v: k for k, v in iteritems(state_numbers)}
\ No newline at end of file
+number_state_mapping = {v: k for k, v in iteritems(state_numbers)}
diff --git a/erpnext/regional/india/gst_state_code_data.json b/erpnext/regional/india/gst_state_code_data.json
index ff88e0f..8481c27 100644
--- a/erpnext/regional/india/gst_state_code_data.json
+++ b/erpnext/regional/india/gst_state_code_data.json
@@ -168,5 +168,10 @@
   "state_number": "37",
   "state_code": "AD",
   "state_name": "Andhra Pradesh (New)"
+ },
+ {
+  "state_number": "38",
+  "state_code": "LA",
+  "state_name": "Ladakh"
  }
 ]
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index e89885f..09bafdd 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -772,3 +772,24 @@
 				)
 
 	return gl_entries
+
+@frappe.whitelist()
+def get_regional_round_off_accounts(company, account_list):
+	country = frappe.get_cached_value('Company', company, 'country')
+
+	if country != 'India':
+		return
+
+	if isinstance(account_list, string_types):
+		account_list = json.loads(account_list)
+
+	if not frappe.db.get_single_value('GST Settings', 'round_off_gst_values'):
+		return
+
+	gst_accounts = get_gst_accounts(company)
+	gst_account_list = gst_accounts.get('cgst_account') + gst_accounts.get('sgst_account') \
+		+ gst_accounts.get('igst_account')
+
+	account_list.extend(gst_account_list)
+
+	return account_list
\ No newline at end of file
diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py
index 96dc3f7..09b04ff 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.py
+++ b/erpnext/regional/report/gstr_1/gstr_1.py
@@ -236,6 +236,7 @@
 		self.cgst_sgst_invoices = []
 
 		unidentified_gst_accounts = []
+		unidentified_gst_accounts_invoice = []
 		for parent, account, item_wise_tax_detail, tax_amount in self.tax_details:
 			if account in self.gst_accounts.cess_account:
 				self.invoice_cess.setdefault(parent, tax_amount)
@@ -251,6 +252,7 @@
 						if not (cgst_or_sgst or account in self.gst_accounts.igst_account):
 							if "gst" in account.lower() and account not in unidentified_gst_accounts:
 								unidentified_gst_accounts.append(account)
+								unidentified_gst_accounts_invoice.append(parent)
 							continue
 
 						for item_code, tax_amounts in item_wise_tax_detail.items():
@@ -273,7 +275,7 @@
 
 		# Build itemised tax for export invoices where tax table is blank
 		for invoice, items in iteritems(self.invoice_items):
-			if invoice not in self.items_based_on_tax_rate \
+			if invoice not in self.items_based_on_tax_rate and invoice not in unidentified_gst_accounts_invoice \
 				and frappe.db.get_value(self.doctype, invoice, "export_type") == "Without Payment of Tax":
 					self.items_based_on_tax_rate.setdefault(invoice, {}).setdefault(0, items.keys())
 
diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json
index 557c715..3c0652eb 100644
--- a/erpnext/selling/doctype/customer/customer.json
+++ b/erpnext/selling/doctype/customer/customer.json
@@ -34,9 +34,8 @@
   "companies",
   "currency_and_price_list",
   "default_currency",
-  "default_price_list",
   "column_break_14",
-  "language",
+  "default_price_list",
   "address_contacts",
   "address_html",
   "website",
@@ -59,6 +58,7 @@
   "column_break_45",
   "market_segment",
   "industry",
+  "language",
   "is_frozen",
   "column_break_38",
   "loyalty_program",
@@ -485,7 +485,7 @@
  "idx": 363,
  "image_field": "image",
  "links": [],
- "modified": "2020-03-17 11:03:42.706907",
+ "modified": "2021-01-06 19:35:25.418017",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Customer",
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index e492377..e3b41e6 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -514,7 +514,7 @@
 	make_delivery_note: function() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.selling.doctype.sales_order.sales_order.make_delivery_note",
-			frm: me.frm
+			frm: this.frm
 		})
 	},
 
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 1516dd6..e561291 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -180,6 +180,7 @@
 			update_coupon_code_count(self.coupon_code,'used')
 
 	def on_cancel(self):
+		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
 		super(SalesOrder, self).on_cancel()
 
 		# Cannot cancel closed SO
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index cbfab82..52a0174 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -17,6 +17,18 @@
 from erpnext.stock.doctype.item.test_item import make_item
 
 class TestSalesOrder(unittest.TestCase):
+
+	@classmethod
+	def setUpClass(cls):
+		cls.unlink_setting = int(frappe.db.get_value("Accounts Settings", "Accounts Settings",
+			"unlink_advance_payment_on_cancelation_of_order"))
+
+	@classmethod
+	def tearDownClass(cls) -> None:
+		# reset config to previous state
+		frappe.db.set_value("Accounts Settings", "Accounts Settings",
+			"unlink_advance_payment_on_cancelation_of_order", cls.unlink_setting)
+
 	def tearDown(self):
 		frappe.set_user("Administrator")
 
@@ -1049,6 +1061,38 @@
 
 		self.assertRaises(frappe.LinkExistsError, so_doc.cancel)
 
+	def test_cancel_sales_order_after_cancel_payment_entry(self):
+		from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
+		# make a sales order
+		so = make_sales_order()
+
+		# disable unlinking of payment entry
+		frappe.db.set_value("Accounts Settings", "Accounts Settings",
+			"unlink_advance_payment_on_cancelation_of_order", 0)
+
+		# create a payment entry against sales order
+		pe = get_payment_entry("Sales Order", so.name, bank_account="_Test Bank - _TC")
+		pe.reference_no = "1"
+		pe.reference_date = nowdate()
+		pe.paid_from_account_currency = so.currency
+		pe.paid_to_account_currency = so.currency
+		pe.source_exchange_rate = 1
+		pe.target_exchange_rate = 1
+		pe.paid_amount = so.grand_total
+		pe.save(ignore_permissions=True)
+		pe.submit()
+
+		# Cancel payment entry
+		po_doc = frappe.get_doc("Payment Entry", pe.name)
+		po_doc.cancel()
+
+		# Cancel sales order
+		try:
+			so_doc = frappe.get_doc('Sales Order', so.name)
+			so_doc.cancel()
+		except Exception:
+			self.fail("Can not cancel sales order with linked cancelled payment entry")
+
 	def test_request_for_raw_materials(self):
 		item = make_item("_Test Finished Item", {"is_stock_item": 1,
 			"maintain_stock": 1,
@@ -1207,4 +1251,4 @@
 	))
 	workflow.insert(ignore_permissions=True)
 
-	return workflow
\ No newline at end of file
+	return workflow
diff --git a/erpnext/setup/doctype/company/delete_company_transactions.py b/erpnext/setup/doctype/company/delete_company_transactions.py
index 7a72fe3..0df4c87 100644
--- a/erpnext/setup/doctype/company/delete_company_transactions.py
+++ b/erpnext/setup/doctype/company/delete_company_transactions.py
@@ -28,7 +28,7 @@
 			"Party Account", "Employee", "Sales Taxes and Charges Template",
 			"Purchase Taxes and Charges Template", "POS Profile", "BOM",
 			"Company", "Bank Account", "Item Tax Template", "Mode Of Payment",
-			"Item Default", "Customer", "Supplier"):
+			"Item Default", "Customer", "Supplier", "GST Account"):
 				delete_for_doctype(doctype, company_name)
 
 	# reset company values
diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py
index 3c9f849..681d161 100644
--- a/erpnext/shopping_cart/cart.py
+++ b/erpnext/shopping_cart/cart.py
@@ -462,6 +462,9 @@
 		return customer
 
 def get_debtors_account(cart_settings):
+	if not cart_settings.payment_gateway_account:
+		frappe.throw(_("Payment Gateway Account not set"), _("Mandatory"))
+
 	payment_gateway_account_currency = \
 		frappe.get_doc("Payment Gateway Account", cart_settings.payment_gateway_account).currency
 
diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json
index 702583a..3691721 100644
--- a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json
+++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json
@@ -26,10 +26,10 @@
   "quotation_series",
   "section_break_8",
   "enable_checkout",
-  "payment_success_url",
-  "column_break_11",
   "save_quotations_as_draft",
-  "payment_gateway_account"
+  "column_break_11",
+  "payment_gateway_account",
+  "payment_success_url"
  ],
  "fields": [
   {
@@ -143,10 +143,12 @@
   },
   {
    "default": "Orders",
+   "depends_on": "enable_checkout",
    "description": "After payment completion redirect user to selected page.",
    "fieldname": "payment_success_url",
    "fieldtype": "Select",
    "label": "Payment Success Url",
+   "mandatory_depends_on": "enable_checkout",
    "options": "\nOrders\nInvoices\nMy Account"
   },
   {
@@ -154,9 +156,11 @@
    "fieldtype": "Column Break"
   },
   {
+   "depends_on": "enable_checkout",
    "fieldname": "payment_gateway_account",
    "fieldtype": "Link",
    "label": "Payment Gateway Account",
+   "mandatory_depends_on": "enable_checkout",
    "options": "Payment Gateway Account"
   },
   {
@@ -186,7 +190,7 @@
  "idx": 1,
  "issingle": 1,
  "links": [],
- "modified": "2021-02-01 18:18:54.606535",
+ "modified": "2021-02-11 18:48:30.433058",
  "modified_by": "Administrator",
  "module": "Shopping Cart",
  "name": "Shopping Cart Settings",
diff --git a/erpnext/stock/__init__.py b/erpnext/stock/__init__.py
index 8d64efe..9e240cc 100644
--- a/erpnext/stock/__init__.py
+++ b/erpnext/stock/__init__.py
@@ -64,10 +64,10 @@
 	if not account and warehouse.company:
 		account = get_company_default_inventory_account(warehouse.company)
 
-	if not account and warehouse.company:
+	if not account and warehouse.company and not warehouse.is_group:
 		frappe.throw(_("Please set Account in Warehouse {0} or Default Inventory Account in Company {1}")
 			.format(warehouse.name, warehouse.company))
 	return account
 
 def get_company_default_inventory_account(company):
-	return frappe.get_cached_value('Company',  company,  'default_inventory_account')
+	return frappe.get_cached_value('Company',  company, 'default_inventory_account')
diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py
index 97f85ba..cbd272d 100644
--- a/erpnext/stock/doctype/batch/test_batch.py
+++ b/erpnext/stock/doctype/batch/test_batch.py
@@ -298,9 +298,9 @@
 		self.assertEqual(details.get('price_list_rate'), 400)
 
 def create_batch(item_code, rate, create_item_price_for_batch):
-	pi = make_purchase_invoice(company="_Test Company with perpetual inventory",
-		warehouse= "Stores - TCP1", cost_center = "Main - TCP1", update_stock=1,
-		expense_account ="_Test Account Cost for Goods Sold - TCP1", item_code=item_code)
+	pi = make_purchase_invoice(company="_Test Company",
+		warehouse= "Stores - _TC", cost_center = "Main - _TC", update_stock=1,
+		expense_account ="_Test Account Cost for Goods Sold - _TC", item_code=item_code)
 
 	batch = frappe.db.get_value('Batch', {'item': item_code, 'reference_name': pi.name})
 
diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py
index 1088b41..0514bd2 100644
--- a/erpnext/stock/doctype/bin/bin.py
+++ b/erpnext/stock/doctype/bin/bin.py
@@ -16,8 +16,9 @@
 	def update_stock(self, args, allow_negative_stock=False, via_landed_cost_voucher=False):
 		'''Called from erpnext.stock.utils.update_bin'''
 		self.update_qty(args)
+
 		if args.get("actual_qty") or args.get("voucher_type") == "Stock Reconciliation":
-			from erpnext.stock.stock_ledger import update_entries_after, validate_negative_qty_in_future_sle
+			from erpnext.stock.stock_ledger import update_entries_after, update_qty_in_future_sle
 
 			if not args.get("posting_date"):
 				args["posting_date"] = nowdate()
@@ -34,11 +35,13 @@
 				"posting_time": args.get("posting_time"),
 				"voucher_type": args.get("voucher_type"),
 				"voucher_no": args.get("voucher_no"),
-				"sle_id": args.name
+				"sle_id": args.name,
+				"creation": args.creation
 			}, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher)
 
-			# Validate negative qty in future transactions
-			validate_negative_qty_in_future_sle(args)
+			# update qty in future ale and Validate negative qty
+			update_qty_in_future_sle(args, allow_negative_stock)
+
 
 	def update_qty(self, args):
 		# update the stock values (for current quantities)
@@ -51,7 +54,7 @@
 		self.reserved_qty = flt(self.reserved_qty) + flt(args.get("reserved_qty"))
 		self.indented_qty = flt(self.indented_qty) + flt(args.get("indented_qty"))
 		self.planned_qty = flt(self.planned_qty) + flt(args.get("planned_qty"))
-
+		
 		self.set_projected_qty()
 		self.db_update()
 
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index 559f8be..d39b229 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -489,7 +489,10 @@
 	def test_closed_delivery_note(self):
 		from erpnext.stock.doctype.delivery_note.delivery_note import update_delivery_note_status
 
-		dn = create_delivery_note(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', cost_center = 'Main - TCP1', expense_account = "Cost of Goods Sold - TCP1", do_not_submit=True)
+		make_stock_entry(target="Stores - TCP1", qty=5, basic_rate=100)
+
+		dn = create_delivery_note(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1',
+			cost_center = 'Main - TCP1', expense_account = "Cost of Goods Sold - TCP1", do_not_submit=True)
 
 		dn.submit()
 
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 144101c..984ae46 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
@@ -148,7 +148,6 @@
 
 
 	def test_landed_cost_voucher_for_odd_numbers (self):
-
 		pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", do_not_save=True)
 		pr.items[0].cost_center = "Main - TCP1"
 		for x in range(2):
@@ -208,6 +207,10 @@
 		self.assertEqual(pr.items[1].landed_cost_voucher_amount, 100)
 
 	def test_multi_currency_lcv(self):
+		from erpnext.setup.doctype.currency_exchange.test_currency_exchange import test_records, save_new_records
+
+		save_new_records(test_records)
+
 		## Create USD Shipping charges_account
 		usd_shipping = create_account(account_name="Shipping Charges USD",
 			parent_account="Duties and Taxes - TCP1", company="_Test Company with perpetual inventory",
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index ca58ab2..7741ee7 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -94,10 +94,15 @@
 		frappe.get_doc('Payment Terms Template', '_Test Payment Terms Template For Purchase Invoice').delete()
 
 	def test_purchase_receipt_no_gl_entry(self):
+		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+
 		company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company')
 
-		existing_bin_stock_value = frappe.db.get_value("Bin", {"item_code": "_Test Item",
-			"warehouse": "_Test Warehouse - _TC"}, "stock_value")
+		existing_bin_qty, existing_bin_stock_value = frappe.db.get_value("Bin", {"item_code": "_Test Item",
+			"warehouse": "_Test Warehouse - _TC"}, ["actual_qty", "stock_value"])
+
+		if existing_bin_qty < 0:
+			make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=abs(existing_bin_qty))
 
 		pr = make_purchase_receipt()
 
diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
index ba2c2c6..8436acb 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -46,6 +46,9 @@
 
 def repost(doc):
 	try:
+		if not frappe.db.exists("Repost Item Valuation", doc.name):
+			return
+
 		doc.set_status('In Progress')
 		frappe.db.commit()
 
@@ -64,7 +67,7 @@
 			message += "<br>" + "Traceback: <br>" + traceback
 		frappe.db.set_value(doc.doctype, doc.name, 'error_log', message)
 
-		notify_error_to_stock_managers(doc)
+		notify_error_to_stock_managers(doc, message)
 		doc.set_status('Failed')
 		raise
 	finally:
diff --git a/erpnext/stock/doctype/shipment/test_shipment.py b/erpnext/stock/doctype/shipment/test_shipment.py
index e1fa207..9c3e22f 100644
--- a/erpnext/stock/doctype/shipment/test_shipment.py
+++ b/erpnext/stock/doctype/shipment/test_shipment.py
@@ -190,6 +190,7 @@
 	company.abbr = abbr
 	company.default_currency = 'EUR'
 	company.country = 'Germany'
+	company.enable_perpetual_inventory = 0
 	company.insert()
 	return company
 
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index d77b70f..9cdc3cf 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -163,7 +163,7 @@
 		if self.purpose not in valid_purposes:
 			frappe.throw(_("Purpose must be one of {0}").format(comma_or(valid_purposes)))
 
-		if self.job_card and self.purpose != 'Material Transfer for Manufacture':
+		if self.job_card and self.purpose not in ['Material Transfer for Manufacture', 'Repack']:
 			frappe.throw(_("For job card {0}, you can only make the 'Material Transfer for Manufacture' type stock entry")
 				.format(self.job_card))
 
@@ -823,6 +823,7 @@
 		if self.job_card:
 			job_doc = frappe.get_doc('Job Card', self.job_card)
 			job_doc.set_transferred_qty(update_status=True)
+			job_doc.set_transferred_qty_in_job_card(self)
 
 		if self.work_order:
 			pro_doc = frappe.get_doc("Work Order", self.work_order)
diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
index 988ae92..864ff48 100644
--- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
+++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
@@ -69,7 +69,8 @@
   "putaway_rule",
   "column_break_51",
   "reference_purchase_receipt",
-  "quality_inspection"
+  "quality_inspection",
+  "job_card_item"
  ],
  "fields": [
   {
@@ -532,13 +533,22 @@
    "fieldname": "is_finished_item",
    "fieldtype": "Check",
    "label": "Is Finished Item"
+  },
+  {
+   "fieldname": "job_card_item",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Job Card Item",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1
   }
  ],
  "idx": 1,
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-12-30 15:00:44.489442",
+ "modified": "2021-02-11 13:47:50.158754",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Entry Detail",
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 36d09ef..b0e7440 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -38,6 +38,7 @@
 		self.block_transactions_against_group_warehouse()
 		self.validate_with_last_transaction_posting_time()
 
+
 	def on_submit(self):
 		self.check_stock_frozen_date()
 		self.actual_amt_check()
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 95f8c43..e4f5725 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -23,6 +23,7 @@
 
 		cancel = sl_entries[0].get("is_cancelled")
 		if cancel:
+			validate_cancellation(sl_entries)
 			set_as_cancel(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no'))
 
 		for sle in sl_entries:
@@ -45,6 +46,20 @@
 			args = sle_doc.as_dict()
 			update_bin(args, allow_negative_stock, via_landed_cost_voucher)
 
+def validate_cancellation(args):
+	if args[0].get("is_cancelled"):
+		repost_entry = frappe.db.get_value("Repost Item Valuation", {
+			'voucher_type': args[0].voucher_type,
+			'voucher_no': args[0].voucher_no,
+			'docstatus': 1
+		}, ['name', 'status'], as_dict=1)
+
+		if repost_entry:
+			if repost_entry.status == 'In Progress':
+				frappe.throw(_("Cannot cancel the transaction. Reposting of item valuation on submission is not completed yet."))
+			if repost_entry.status == 'Queued':
+				frappe.delete_doc("Repost Item Valuation", repost_entry.name)
+
 
 def set_as_cancel(voucher_type, voucher_no):
 	frappe.db.sql("""update `tabStock Ledger Entry` set is_cancelled=1,
@@ -74,7 +89,8 @@
 			"item_code": args[i].item_code,
 			"warehouse": args[i].warehouse,
 			"posting_date": args[i].posting_date,
-			"posting_time": args[i].posting_time
+			"posting_time": args[i].posting_time,
+			"creation": args[i].get("creation")
 		}, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher)
 
 		for item_wh, new_sle in iteritems(obj.new_items):
@@ -86,7 +102,7 @@
 def get_args_for_voucher(voucher_type, voucher_no):
 	return frappe.db.get_all("Stock Ledger Entry",
 		filters={"voucher_type": voucher_type, "voucher_no": voucher_no},
-		fields=["item_code", "warehouse", "posting_date", "posting_time"],
+		fields=["item_code", "warehouse", "posting_date", "posting_time", "creation"],
 		order_by="creation asc",
 		group_by="item_code, warehouse"
 	)
@@ -117,7 +133,7 @@
 		self.item_code = args.get("item_code")
 		if self.args.sle_id:
 			self.args['name'] = self.args.sle_id
-
+		
 		self.company = frappe.get_cached_value("Warehouse", self.args.warehouse, "company")
 		self.get_precision()
 		self.valuation_method = get_valuation_method(self.item_code)
@@ -155,7 +171,7 @@
 		"""
 		self.data.setdefault(args.warehouse, frappe._dict())
 		warehouse_dict = self.data[args.warehouse]
-		previous_sle = self.get_sle_before_datetime(args)
+		previous_sle = self.get_previous_sle_of_current_voucher(args)
 		warehouse_dict.previous_sle = previous_sle
 
 		for key in ("qty_after_transaction", "valuation_rate", "stock_value"):
@@ -167,9 +183,35 @@
 			"stock_value_difference": 0.0
 		})
 
+	def get_previous_sle_of_current_voucher(self, args):
+		"""get stock ledger entries filtered by specific posting datetime conditions"""
+
+		args['time_format'] = '%H:%i:%s'
+		if not args.get("posting_date"):
+			args["posting_date"] = "1900-01-01"
+		if not args.get("posting_time"):
+			args["posting_time"] = "00:00"
+
+		sle = frappe.db.sql("""
+			select *, timestamp(posting_date, posting_time) as "timestamp"
+			from `tabStock Ledger Entry`
+			where item_code = %(item_code)s
+				and warehouse = %(warehouse)s
+				and is_cancelled = 0
+				and timestamp(posting_date, time_format(posting_time, %(time_format)s)) < timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s))
+			order by timestamp(posting_date, posting_time) desc, creation desc
+			limit 1""", args, as_dict=1)
+		
+		return sle[0] if sle else frappe._dict()
+
+
 	def build(self):
+		from erpnext.controllers.stock_controller import check_if_future_sle_exists
+
 		if self.args.get("sle_id"):
-			self.process_sle_against_current_voucher()
+			self.process_sle_against_current_timestamp()
+			if not check_if_future_sle_exists(self.args):
+				self.update_bin()
 		else:
 			entries_to_fix = self.get_future_entries_to_fix()
 
@@ -182,18 +224,20 @@
 
 				if sle.dependant_sle_voucher_detail_no:
 					entries_to_fix = self.get_dependent_entries_to_fix(entries_to_fix, sle)
+			
+			self.update_bin()
 
 		if self.exceptions:
 			self.raise_exceptions()
 
-		self.update_bin()
-
-	def process_sle_against_current_voucher(self):
+	def process_sle_against_current_timestamp(self):
 		sl_entries = self.get_sle_against_current_voucher()
 		for sle in sl_entries:
 			self.process_sle(sle)
 
 	def get_sle_against_current_voucher(self):
+		self.args['time_format'] = '%H:%i:%s'
+
 		return frappe.db.sql("""
 			select
 				*, timestamp(posting_date, posting_time) as "timestamp"
@@ -202,7 +246,8 @@
 			where
 				item_code = %(item_code)s
 				and warehouse = %(warehouse)s
-				and timestamp(posting_date, time_format(posting_time, '%H:%i:%s')) = timestamp(%(posting_date)s, time_format(%(posting_time)s, '%H:%i:%s'))
+				and timestamp(posting_date, time_format(posting_time, %(time_format)s)) = timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s))
+
 			order by
 				creation ASC
 			for update
@@ -229,7 +274,6 @@
 			return entries_to_fix
 		elif dependant_sle.item_code == self.item_code and dependant_sle.warehouse in self.data:
 			return entries_to_fix
-
 		self.initialize_previous_data(dependant_sle)
 
 		args = self.data[dependant_sle.warehouse].previous_sle \
@@ -636,7 +680,6 @@
 		# update bin for each warehouse
 		for warehouse, data in iteritems(self.data):
 			bin_doc = get_bin(self.item_code, warehouse)
-
 			bin_doc.update({
 				"valuation_rate": data.valuation_rate,
 				"actual_qty": data.qty_after_transaction,
@@ -762,6 +805,25 @@
 
 	return valuation_rate
 
+def update_qty_in_future_sle(args, allow_negative_stock=None):
+	frappe.db.sql("""
+		update `tabStock Ledger Entry`
+		set qty_after_transaction = qty_after_transaction + {qty}
+		where
+			item_code = %(item_code)s
+			and warehouse = %(warehouse)s
+			and voucher_no != %(voucher_no)s
+			and is_cancelled = 0
+			and (timestamp(posting_date, posting_time) > timestamp(%(posting_date)s, %(posting_time)s)
+				or (
+					timestamp(posting_date, posting_time) = timestamp(%(posting_date)s, %(posting_time)s)
+					and creation > %(creation)s
+				)
+			)
+	""".format(qty=args.actual_qty), args)
+
+	validate_negative_qty_in_future_sle(args, allow_negative_stock)
+
 def validate_negative_qty_in_future_sle(args, allow_negative_stock=None):
 	allow_negative_stock = allow_negative_stock \
 		or cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock"))
@@ -790,7 +852,7 @@
 			and voucher_no != %(voucher_no)s
 			and timestamp(posting_date, posting_time) >= timestamp(%(posting_date)s, %(posting_time)s)
 			and is_cancelled = 0
-			and qty_after_transaction + {0} < 0
+			and qty_after_transaction < 0
 		order by timestamp(posting_date, posting_time) asc
 		limit 1
-	""".format(args.actual_qty), args, as_dict=1)
+	""", args, as_dict=1)
\ No newline at end of file