Merge branch 'develop' into mr_allowance_feat
diff --git a/CODEOWNERS b/CODEOWNERS
index 219b6bb..a4a14de 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -21,13 +21,13 @@
 erpnext/shopping_cart/          @marination
 erpnext/stock/                  @marination @rohitwaghchaure @ankush
 
-erpnext/crm/                    @ruchamahabal
-erpnext/education/              @ruchamahabal
-erpnext/healthcare/             @ruchamahabal
-erpnext/hr/                     @ruchamahabal
+erpnext/crm/                    @ruchamahabal @pateljannat
+erpnext/education/              @ruchamahabal @pateljannat
+erpnext/healthcare/             @ruchamahabal @pateljannat @chillaranand
+erpnext/hr/                     @ruchamahabal @pateljannat
 erpnext/non_profit/             @ruchamahabal
-erpnext/payroll                 @ruchamahabal
-erpnext/projects/               @ruchamahabal
+erpnext/payroll                 @ruchamahabal @pateljannat
+erpnext/projects/               @ruchamahabal @pateljannat
 
 erpnext/controllers             @deepeshgarg007 @nextchamp-saqib @rohitwaghchaure @marination
 
diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
index 5619321..f2b0a8c 100644
--- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
+++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
@@ -27,6 +27,9 @@
 		if not (self.company and self.posting_date):
 			frappe.throw(_("Please select Company and Posting Date to getting entries"))
 
+	def on_cancel(self):
+		self.ignore_linked_doctypes = ('GL Entry')
+
 	@frappe.whitelist()
 	def check_journal_entry_condition(self):
 		total_debit = frappe.db.get_value("Journal Entry Account", {
@@ -99,10 +102,12 @@
 					sum(debit) - sum(credit) as balance
 				from `tabGL Entry`
 				where account in (%s)
-				group by account, party_type, party
+				and posting_date <= %s
+				and is_cancelled = 0
+				group by account, NULLIF(party_type,''), NULLIF(party,'')
 				having sum(debit) != sum(credit)
 				order by account
-			""" % ', '.join(['%s']*len(accounts)), tuple(accounts), as_dict=1)
+			""" % (', '.join(['%s']*len(accounts)), '%s'), tuple(accounts + [self.posting_date]), as_dict=1)
 
 		return account_details
 
@@ -143,9 +148,9 @@
 				"party_type": d.get("party_type"),
 				"party": d.get("party"),
 				"account_currency": d.get("account_currency"),
-				"balance": d.get("balance_in_account_currency"),
-				dr_or_cr: abs(d.get("balance_in_account_currency")),
-				"exchange_rate":d.get("new_exchange_rate"),
+				"balance": flt(d.get("balance_in_account_currency"), d.precision("balance_in_account_currency")),
+				dr_or_cr: flt(abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")),
+				"exchange_rate": flt(d.get("new_exchange_rate"), d.precision("new_exchange_rate")),
 				"reference_type": "Exchange Rate Revaluation",
 				"reference_name": self.name,
 				})
@@ -154,9 +159,9 @@
 				"party_type": d.get("party_type"),
 				"party": d.get("party"),
 				"account_currency": d.get("account_currency"),
-				"balance": d.get("balance_in_account_currency"),
-				reverse_dr_or_cr: abs(d.get("balance_in_account_currency")),
-				"exchange_rate": d.get("current_exchange_rate"),
+				"balance": flt(d.get("balance_in_account_currency"), d.precision("balance_in_account_currency")),
+				reverse_dr_or_cr: flt(abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")),
+				"exchange_rate": flt(d.get("current_exchange_rate"), d.precision("current_exchange_rate")),
 				"reference_type": "Exchange Rate Revaluation",
 				"reference_name": self.name
 				})
@@ -185,9 +190,9 @@
 
 	account_details = {}
 	company_currency = erpnext.get_company_currency(company)
-	balance = get_balance_on(account, party_type=party_type, party=party, in_account_currency=False)
+	balance = get_balance_on(account, date=posting_date, party_type=party_type, party=party, in_account_currency=False)
 	if balance:
-		balance_in_account_currency = get_balance_on(account, party_type=party_type, party=party)
+		balance_in_account_currency = get_balance_on(account, date=posting_date, party_type=party_type, party=party)
 		current_exchange_rate = balance / balance_in_account_currency if balance_in_account_currency else 0
 		new_exchange_rate = get_exchange_rate(account_currency, company_currency, posting_date)
 		new_balance_in_base_currency = balance_in_account_currency * new_exchange_rate
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index a11b77a..b54646f 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -99,7 +99,6 @@
 					voucher_no = gle.voucher_no,
 					party = gle.party,
 					posting_date = gle.posting_date,
-					remarks = gle.remarks,
 					account_currency = gle.account_currency,
 					invoiced = 0.0,
 					paid = 0.0,
@@ -579,7 +578,7 @@
 		self.gl_entries = frappe.db.sql("""
 			select
 				name, posting_date, account, party_type, party, voucher_type, voucher_no, cost_center,
-				against_voucher_type, against_voucher, account_currency, remarks, {0}
+				against_voucher_type, against_voucher, account_currency, {0}
 			from
 				`tabGL Entry`
 			where
@@ -792,8 +791,6 @@
 			self.add_column(label=_('Supplier Group'), fieldname='supplier_group', fieldtype='Link',
 				options='Supplier Group')
 
-		self.add_column(label=_('Remarks'), fieldname='remarks', fieldtype='Text', width=200)
-
 	def add_column(self, label, fieldname=None, fieldtype='Currency', options=None, width=120):
 		if not fieldname: fieldname = scrub(label)
 		if fieldtype=='Currency': options='currency'
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 4c313c4..cdd865a 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -1112,8 +1112,11 @@
 			for d in self.get("payment_schedule"):
 				if d.invoice_portion:
 					d.payment_amount = flt(grand_total * flt(d.invoice_portion / 100), d.precision('payment_amount'))
-					d.base_payment_amount = flt(base_grand_total * flt(d.invoice_portion / 100), d.precision('payment_amount'))
+					d.base_payment_amount = flt(base_grand_total * flt(d.invoice_portion / 100), d.precision('base_payment_amount'))
 					d.outstanding = d.payment_amount
+				elif not d.invoice_portion:
+					d.base_payment_amount = flt(base_grand_total * self.get("conversion_rate"), d.precision('base_payment_amount'))
+
 
 	def set_due_date(self):
 		due_dates = [d.due_date for d in self.get("payment_schedule") if d.due_date]
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 2526e6d..17bd735 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -53,12 +53,17 @@
 		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 		for d in self.get("items"):
 			if hasattr(d, 'serial_no') and hasattr(d, 'batch_no') and d.serial_no and d.batch_no:
-				serial_nos = get_serial_nos(d.serial_no)
-				for serial_no_data in frappe.get_all("Serial No",
-					filters={"name": ("in", serial_nos)}, fields=["batch_no", "name"]):
-					if serial_no_data.batch_no != d.batch_no:
+				serial_nos = frappe.get_all("Serial No",
+					fields=["batch_no", "name", "warehouse"],
+					filters={
+						"name": ("in", get_serial_nos(d.serial_no))
+					}
+				)
+
+				for row in serial_nos:
+					if row.warehouse and row.batch_no != d.batch_no:
 						frappe.throw(_("Row #{0}: Serial No {1} does not belong to Batch {2}")
-							.format(d.idx, serial_no_data.name, d.batch_no))
+							.format(d.idx, row.name, d.batch_no))
 
 			if flt(d.qty) > 0.0 and d.get("batch_no") and self.get("posting_date") and self.docstatus < 2:
 				expiry_date = frappe.get_cached_value("Batch", d.get("batch_no"), "expiry_date")
diff --git a/erpnext/crm/doctype/campaign/__init__.py b/erpnext/crm/doctype/campaign/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/crm/doctype/campaign/__init__.py
diff --git a/erpnext/crm/doctype/campaign/campaign.js b/erpnext/crm/doctype/campaign/campaign.js
new file mode 100644
index 0000000..11bfa74
--- /dev/null
+++ b/erpnext/crm/doctype/campaign/campaign.js
@@ -0,0 +1,17 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Campaign', {
+	refresh: function(frm) {
+		erpnext.toggle_naming_series();
+
+		if (frm.doc.__islocal) {
+			frm.toggle_display("naming_series", frappe.boot.sysdefaults.campaign_naming_by=="Naming Series");
+		} else {
+			cur_frm.add_custom_button(__("View Leads"), function() {
+				frappe.route_options = {"source": "Campaign", "campaign_name": frm.doc.name};
+				frappe.set_route("List", "Lead");
+			}, "fa fa-list", true);
+		}
+	}
+});
diff --git a/erpnext/selling/doctype/campaign/campaign.json b/erpnext/crm/doctype/campaign/campaign.json
similarity index 95%
rename from erpnext/selling/doctype/campaign/campaign.json
rename to erpnext/crm/doctype/campaign/campaign.json
index 986ac13..f833f4c 100644
--- a/erpnext/selling/doctype/campaign/campaign.json
+++ b/erpnext/crm/doctype/campaign/campaign.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "allow_import": 1,
  "allow_rename": 1,
  "autoname": "naming_series:",
@@ -39,17 +40,9 @@
    "set_only_once": 1
   },
   {
-   "fieldname": "description",
-   "fieldtype": "Text",
-   "in_list_view": 1,
-   "label": "Description",
-   "oldfieldname": "description",
-   "oldfieldtype": "Text",
-   "width": "300px"
-  },
-  {
-   "fieldname": "description_section",
-   "fieldtype": "Section Break"
+   "fieldname": "campaign_schedules_section",
+   "fieldtype": "Section Break",
+   "label": "Campaign Schedules"
   },
   {
    "fieldname": "campaign_schedules",
@@ -58,16 +51,25 @@
    "options": "Campaign Email Schedule"
   },
   {
-   "fieldname": "campaign_schedules_section",
-   "fieldtype": "Section Break",
-   "label": "Campaign Schedules"
+   "fieldname": "description_section",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "description",
+   "fieldtype": "Text",
+   "in_list_view": 1,
+   "label": "Description",
+   "oldfieldname": "description",
+   "oldfieldtype": "Text",
+   "width": "300px"
   }
  ],
  "icon": "fa fa-bullhorn",
  "idx": 1,
- "modified": "2019-07-22 12:03:39.832342",
+ "links": [],
+ "modified": "2021-06-30 18:05:06.412712",
  "modified_by": "Administrator",
- "module": "Selling",
+ "module": "CRM",
  "name": "Campaign",
  "owner": "Administrator",
  "permissions": [
diff --git a/erpnext/selling/doctype/campaign/campaign.py b/erpnext/crm/doctype/campaign/campaign.py
similarity index 66%
rename from erpnext/selling/doctype/campaign/campaign.py
rename to erpnext/crm/doctype/campaign/campaign.py
index 1094542..e32799f 100644
--- a/erpnext/selling/doctype/campaign/campaign.py
+++ b/erpnext/crm/doctype/campaign/campaign.py
@@ -1,9 +1,7 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
+# 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
 from frappe.model.naming import set_name_by_naming_series
 
diff --git a/erpnext/crm/doctype/campaign/test_campaign.py b/erpnext/crm/doctype/campaign/test_campaign.py
new file mode 100644
index 0000000..7124b8c
--- /dev/null
+++ b/erpnext/crm/doctype/campaign/test_campaign.py
@@ -0,0 +1,8 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+class TestCampaign(unittest.TestCase):
+	pass
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index 420bb00..69c7f5c 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -192,11 +192,11 @@
 						"completed_qty": args.get("completed_qty") or 0.0
 					})
 		elif args.get("start_time"):
-			new_args = {
+			new_args = frappe._dict({
 				"from_time": get_datetime(args.get("start_time")),
 				"operation": args.get("sub_operation"),
 				"completed_qty": 0.0
-			}
+			})
 
 			if employees:
 				for name in employees:
diff --git a/erpnext/patches/v13_0/add_missing_fg_item_for_stock_entry.py b/erpnext/patches/v13_0/add_missing_fg_item_for_stock_entry.py
index 48999e6..d7ad1fc 100644
--- a/erpnext/patches/v13_0/add_missing_fg_item_for_stock_entry.py
+++ b/erpnext/patches/v13_0/add_missing_fg_item_for_stock_entry.py
@@ -10,6 +10,7 @@
 	if not frappe.db.has_column('Work Order', 'has_batch_no'):
 		return
 
+	frappe.reload_doc('manufacturing', 'doctype', 'manufacturing_settings')
 	if cint(frappe.db.get_single_value('Manufacturing Settings', 'make_serial_no_batch_from_work_order')):
 		return
 
@@ -107,4 +108,4 @@
 		"company": doc.company
 	})
 
-	create_repost_item_valuation_entry(args)
\ No newline at end of file
+	create_repost_item_valuation_entry(args)
diff --git a/erpnext/patches/v13_0/rename_issue_doctype_fields.py b/erpnext/patches/v13_0/rename_issue_doctype_fields.py
index fa1dfed..41c51c3 100644
--- a/erpnext/patches/v13_0/rename_issue_doctype_fields.py
+++ b/erpnext/patches/v13_0/rename_issue_doctype_fields.py
@@ -37,7 +37,7 @@
 
 	if frappe.db.exists('DocType', 'Opportunity'):
 		opportunities = frappe.db.get_all('Opportunity', fields=['name', 'mins_to_first_response'], order_by='creation desc')
-		frappe.reload_doc('crm', 'doctype', 'opportunity')
+		frappe.reload_doctype('Opportunity', force=True)
 		rename_field('Opportunity', 'mins_to_first_response', 'first_response_time')
 
 		# change fieldtype to duration
diff --git a/erpnext/payroll/doctype/salary_component/salary_component.js b/erpnext/payroll/doctype/salary_component/salary_component.js
index dbf7514..e9e6f81 100644
--- a/erpnext/payroll/doctype/salary_component/salary_component.js
+++ b/erpnext/payroll/doctype/salary_component/salary_component.js
@@ -4,11 +4,18 @@
 frappe.ui.form.on('Salary Component', {
 	setup: function(frm) {
 		frm.set_query("account", "accounts", function(doc, cdt, cdn) {
-			var d = locals[cdt][cdn];
+			let d = frappe.get_doc(cdt, cdn);
+
+			let root_type = "Liability";
+			if (frm.doc.type == "Deduction") {
+				root_type = "Expense";
+			}
+
 			return {
 				filters: {
 					"is_group": 0,
-					"company": d.company
+					"company": d.company,
+					"root_type": root_type
 				}
 			};
 		});
diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py
index 6415204..ea39fe1 100644
--- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py
+++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py
@@ -214,9 +214,8 @@
 
 			for d in item_details:
 				if d.item_code not in self.invoice_items.get(d.parent, {}):
-					self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code,
-						sum((i.get('taxable_value', 0) or i.get('base_net_amount', 0)) for i in item_details
-							if i.item_code == d.item_code and i.parent == d.parent))
+					self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, 0.0)
+					self.invoice_items[d.parent][d.item_code] += d.get('taxable_value', 0) or d.get('base_net_amount', 0)
 
 				if d.is_nil_exempt and d.item_code not in self.is_nil_exempt:
 					self.is_nil_exempt.append(d.item_code)
@@ -322,6 +321,9 @@
 									inter_state_supply_details[(gst_category, place_of_supply)]['txval'] += taxable_value
 									inter_state_supply_details[(gst_category, place_of_supply)]['iamt'] += (taxable_value * rate /100)
 
+			if self.invoice_cess.get(inv):
+				self.report_dict['sup_details']['osup_det']['csamt'] += flt(self.invoice_cess.get(inv), 2)
+
 		self.set_inter_state_supply(inter_state_supply_details)
 
 	def set_supplies_liable_to_reverse_charge(self):
diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py
index cfcb8c3..b81fa81 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.py
+++ b/erpnext/regional/report/gstr_1/gstr_1.py
@@ -217,9 +217,8 @@
 
 		for d in items:
 			if d.item_code not in self.invoice_items.get(d.parent, {}):
-				self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code,
-					sum((i.get('taxable_value', 0) or i.get('base_net_amount', 0)) for i in items
-						if i.item_code == d.item_code and i.parent == d.parent))
+				self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, 0.0)
+				self.invoice_items[d.parent][d.item_code] += d.get('taxable_value', 0) or d.get('base_net_amount', 0)
 
 				item_tax_rate = {}
 
diff --git a/erpnext/selling/doctype/campaign/README.md b/erpnext/selling/doctype/campaign/README.md
deleted file mode 100644
index a837318..0000000
--- a/erpnext/selling/doctype/campaign/README.md
+++ /dev/null
@@ -1 +0,0 @@
-Sales campaign / promotion, like special discount, exhibition, newsletter etc.
\ No newline at end of file
diff --git a/erpnext/selling/doctype/campaign/__init__.py b/erpnext/selling/doctype/campaign/__init__.py
deleted file mode 100644
index baffc48..0000000
--- a/erpnext/selling/doctype/campaign/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/selling/doctype/campaign/campaign.js b/erpnext/selling/doctype/campaign/campaign.js
deleted file mode 100644
index 72a90d0..0000000
--- a/erpnext/selling/doctype/campaign/campaign.js
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-// License: GNU General Public License v3. See license.txt
-
-frappe.ui.form.on("Campaign", "refresh", function(frm) {
-	erpnext.toggle_naming_series();
-	if(frm.doc.__islocal) {
-		frm.toggle_display("naming_series", frappe.boot.sysdefaults.campaign_naming_by=="Naming Series");
-	}
-	else{
-		cur_frm.add_custom_button(__("View Leads"), function() {
-			frappe.route_options = {"source": "Campaign","campaign_name": frm.doc.name}
-			frappe.set_route("List", "Lead");
-		}, "fa fa-list", true);
-	}
-})
diff --git a/erpnext/selling/doctype/campaign/campaign_dashboard.py b/erpnext/selling/doctype/campaign/campaign_dashboard.py
deleted file mode 100644
index 3cef560..0000000
--- a/erpnext/selling/doctype/campaign/campaign_dashboard.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from __future__ import unicode_literals
-from frappe import _
-
-def get_data():
-	return {
-		'fieldname': 'campaign_name',
-		'transactions': [
-			{
-				'label': _('Email Campaigns'),
-				'items': ['Email Campaign']
-			},
-			{
-				'label': _('Social Media Campaigns'),
-				'items': ['Social Media Post']
-			}
-		]
-	}
diff --git a/erpnext/selling/doctype/campaign/test_campaign.py b/erpnext/selling/doctype/campaign/test_campaign.py
deleted file mode 100644
index 4d062ff..0000000
--- a/erpnext/selling/doctype/campaign/test_campaign.py
+++ /dev/null
@@ -1,7 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
-
-
-import frappe
-test_records = frappe.get_test_records('Campaign')
\ No newline at end of file
diff --git a/erpnext/selling/doctype/campaign/test_records.json b/erpnext/selling/doctype/campaign/test_records.json
deleted file mode 100644
index 625d3b3..0000000
--- a/erpnext/selling/doctype/campaign/test_records.json
+++ /dev/null
@@ -1,10 +0,0 @@
-[
- {
-  "campaign_name": "_Test Campaign", 
-  "doctype": "Campaign"
- }, 
- {
-  "campaign_name": "_Test Campaign 1", 
-  "doctype": "Campaign"
- }
-]
\ No newline at end of file
diff --git a/erpnext/selling/page/sales_funnel/sales_funnel.css b/erpnext/selling/page/sales_funnel/sales_funnel.css
index 89e904f..455d37c 100644
--- a/erpnext/selling/page/sales_funnel/sales_funnel.css
+++ b/erpnext/selling/page/sales_funnel/sales_funnel.css
@@ -1,3 +1,4 @@
 .funnel-wrapper {
 	margin: 15px;
+	width: 100%;
 }
\ No newline at end of file