Merge branch 'develop' into fix-job-opening
diff --git a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
index 8e0e62d..3b938ea 100644
--- a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
+++ b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
@@ -78,6 +78,8 @@
 			expense_account="Cost of Goods Sold - TPC",
 			rate=400,
 			debit_to="Debtors - TPC",
+			currency="USD",
+			customer="_Test Customer USD",
 		)
 		create_sales_invoice(
 			company=company,
@@ -86,6 +88,8 @@
 			expense_account="Cost of Goods Sold - TPC",
 			rate=200,
 			debit_to="Debtors - TPC",
+			currency="USD",
+			customer="_Test Customer USD",
 		)
 
 		pcv = self.make_period_closing_voucher(submit=False)
@@ -119,14 +123,17 @@
 		surplus_account = create_account()
 		cost_center = create_cost_center("Test Cost Center 1")
 
-		create_sales_invoice(
+		si = create_sales_invoice(
 			company=company,
 			income_account="Sales - TPC",
 			expense_account="Cost of Goods Sold - TPC",
 			cost_center=cost_center,
 			rate=400,
 			debit_to="Debtors - TPC",
+			currency="USD",
+			customer="_Test Customer USD",
 		)
+
 		jv = make_journal_entry(
 			account1="Cash - TPC",
 			account2="Sales - TPC",
diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
index 4cf19b4..3bd0cd2 100644
--- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
@@ -752,7 +752,7 @@
 			title="_Test Pricing Rule with Min Qty - 2",
 		)
 
-		si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", qty=1, currency="USD")
+		si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", qty=1)
 		item = si.items[0]
 		item.stock_qty = 1
 		si.save()
diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.json b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.json
index b46d2e3..c36efb8 100644
--- a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.json
+++ b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.json
@@ -1,10 +1,12 @@
 {
+ "actions": [],
  "allow_import": 1,
  "allow_rename": 1,
  "creation": "2013-01-10 16:34:08",
  "description": "Standard tax template that can be applied to all Purchase Transactions. This template can contain list of tax heads and also other expense heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n    - This can be on **Net Total** (that is the sum of basic amount).\n    - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n    - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Consider Tax or Charge for: In this section you can specify if the tax / charge is only for valuation (not a part of total) or only for total (does not add value to the item) or for both.\n10. Add or Deduct: Whether you want to add or deduct the tax.",
  "doctype": "DocType",
  "document_type": "Setup",
+ "engine": "InnoDB",
  "field_order": [
   "title",
   "is_default",
@@ -74,7 +76,8 @@
  ],
  "icon": "fa fa-money",
  "idx": 1,
- "modified": "2019-11-25 13:05:26.220275",
+ "links": [],
+ "modified": "2022-05-16 16:15:29.059370",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Purchase Taxes and Charges Template",
@@ -103,6 +106,10 @@
    "role": "Purchase User"
   }
  ],
+ "show_title_field_in_link": 1,
+ "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
+ "title_field": "title",
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index e30289a..9dde85f 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -861,27 +861,44 @@
 
 	set_timesheet_data: function(frm, timesheets) {
 		frm.clear_table("timesheets")
-		timesheets.forEach(timesheet => {
+		timesheets.forEach(async (timesheet) => {
 			if (frm.doc.currency != timesheet.currency) {
-				frappe.call({
-					method: "erpnext.setup.utils.get_exchange_rate",
-					args: {
-						from_currency: timesheet.currency,
-						to_currency: frm.doc.currency
-					},
-					callback: function(r) {
-						if (r.message) {
-							exchange_rate = r.message;
-							frm.events.append_time_log(frm, timesheet, exchange_rate);
-						}
-					}
-				});
+				const exchange_rate = await frm.events.get_exchange_rate(
+					frm, timesheet.currency, frm.doc.currency
+				)
+				frm.events.append_time_log(frm, timesheet, exchange_rate)
 			} else {
 				frm.events.append_time_log(frm, timesheet, 1.0);
 			}
 		});
 	},
 
+	async get_exchange_rate(frm, from_currency, to_currency) {
+		if (
+			frm.exchange_rates
+			&& frm.exchange_rates[from_currency]
+			&& frm.exchange_rates[from_currency][to_currency]
+		) {
+			return frm.exchange_rates[from_currency][to_currency];
+		}
+
+		return frappe.call({
+			method: "erpnext.setup.utils.get_exchange_rate",
+			args: {
+				from_currency,
+				to_currency
+			},
+			callback: function(r) {
+				if (r.message) {
+					// cache exchange rates
+					frm.exchange_rates = frm.exchange_rates || {};
+					frm.exchange_rates[from_currency] = frm.exchange_rates[from_currency] || {};
+					frm.exchange_rates[from_currency][to_currency] = r.message;
+				}
+			}
+		});
+	},
+
 	append_time_log: function(frm, time_log, exchange_rate) {
 		const row = frm.add_child("timesheets");
 		row.activity_type = time_log.activity_type;
@@ -892,7 +909,7 @@
 		row.billing_hours = time_log.billing_hours;
 		row.billing_amount = flt(time_log.billing_amount) * flt(exchange_rate);
 		row.timesheet_detail = time_log.name;
-    row.project_name = time_log.project_name;
+		row.project_name = time_log.project_name;
 
 		frm.refresh_field("timesheets");
 		frm.trigger("calculate_timesheet_totals");
diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json
index 19781bd..408ecbf 100644
--- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json
+++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "allow_import": 1,
  "allow_rename": 1,
  "creation": "2013-01-10 16:34:09",
@@ -77,7 +78,8 @@
  ],
  "icon": "fa fa-money",
  "idx": 1,
- "modified": "2019-11-25 13:06:03.279099",
+ "links": [],
+ "modified": "2022-05-16 16:14:52.061672",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Sales Taxes and Charges Template",
@@ -113,7 +115,10 @@
    "write": 1
   }
  ],
+ "show_title_field_in_link": 1,
  "sort_field": "modified",
  "sort_order": "ASC",
+ "states": [],
+ "title_field": "title",
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index db741d9..f4a44bd 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -897,3 +897,18 @@
 			return None
 	else:
 		return None
+
+
+def add_party_account(party_type, party, company, account):
+	doc = frappe.get_doc(party_type, party)
+	account_exists = False
+	for d in doc.get("accounts"):
+		if d.account == account:
+			account_exists = True
+
+	if not account_exists:
+		accounts = {"company": company, "account": account}
+
+		doc.append("accounts", accounts)
+
+		doc.save()
diff --git a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
index f3ccc86..c41d0d1 100644
--- a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
+++ b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
@@ -198,10 +198,12 @@
 			amount_field = (loan_doc.disbursed_amount).as_("credit")
 			posting_date = (loan_doc.disbursement_date).as_("posting_date")
 			account = loan_doc.disbursement_account
+			salary_condition = loan_doc.docstatus == 1
 		else:
 			amount_field = (loan_doc.amount_paid).as_("debit")
 			posting_date = (loan_doc.posting_date).as_("posting_date")
 			account = loan_doc.payment_account
+			salary_condition = loan_doc.repay_from_salary == 0
 
 		query = (
 			frappe.qb.from_(loan_doc)
@@ -214,14 +216,12 @@
 				posting_date,
 			)
 			.where(loan_doc.docstatus == 1)
+			.where(salary_condition)
 			.where(account == filters.get("account"))
 			.where(posting_date <= getdate(filters.get("report_date")))
 			.where(ifnull(loan_doc.clearance_date, "4000-01-01") > getdate(filters.get("report_date")))
 		)
 
-		if doctype == "Loan Repayment":
-			query.where(loan_doc.repay_from_salary == 0)
-
 		entries = query.run(as_dict=1)
 		loan_docs.extend(entries)
 
@@ -267,15 +267,17 @@
 			amount_field = Sum(loan_doc.disbursed_amount)
 			posting_date = (loan_doc.disbursement_date).as_("posting_date")
 			account = loan_doc.disbursement_account
+			salary_condition = loan_doc.docstatus == 1
 		else:
 			amount_field = Sum(loan_doc.amount_paid)
 			posting_date = (loan_doc.posting_date).as_("posting_date")
 			account = loan_doc.payment_account
-
+			salary_condition = loan_doc.repay_from_salary == 0
 		amount = (
 			frappe.qb.from_(loan_doc)
 			.select(amount_field)
 			.where(loan_doc.docstatus == 1)
+			.where(salary_condition)
 			.where(account == filters.get("account"))
 			.where(posting_date > getdate(filters.get("report_date")))
 			.where(ifnull(loan_doc.clearance_date, "4000-01-01") <= getdate(filters.get("report_date")))
diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py
index 74926b9..75e983a 100644
--- a/erpnext/accounts/report/cash_flow/cash_flow.py
+++ b/erpnext/accounts/report/cash_flow/cash_flow.py
@@ -262,7 +262,10 @@
 def get_chart_data(columns, data):
 	labels = [d.get("label") for d in columns[2:]]
 	datasets = [
-		{"name": account.get("account").replace("'", ""), "values": [account.get("total")]}
+		{
+			"name": account.get("account").replace("'", ""),
+			"values": [account.get(d.get("fieldname")) for d in columns[2:]],
+		}
 		for account in data
 		if account.get("parent_account") == None and account.get("currency")
 	]
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index c9c2ab1..056084b 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -34,6 +34,7 @@
 from erpnext.accounts.party import (
 	get_party_account,
 	get_party_account_currency,
+	get_party_gle_currency,
 	validate_party_frozen_disabled,
 )
 from erpnext.accounts.utils import get_account_currency, get_fiscal_years, validate_fiscal_year
@@ -168,6 +169,7 @@
 
 		self.validate_party()
 		self.validate_currency()
+		self.validate_party_account_currency()
 
 		if self.doctype in ["Purchase Invoice", "Sales Invoice"]:
 			pos_check_field = "is_pos" if self.doctype == "Sales Invoice" else "is_paid"
@@ -1447,6 +1449,27 @@
 				# at quotation / sales order level and we shouldn't stop someone
 				# from creating a sales invoice if sales order is already created
 
+	def validate_party_account_currency(self):
+		if self.doctype not in ("Sales Invoice", "Purchase Invoice"):
+			return
+
+		if self.is_opening == "Yes":
+			return
+
+		party_type, party = self.get_party()
+		party_gle_currency = get_party_gle_currency(party_type, party, self.company)
+		party_account = (
+			self.get("debit_to") if self.doctype == "Sales Invoice" else self.get("credit_to")
+		)
+		party_account_currency = get_account_currency(party_account)
+
+		if not party_gle_currency and (party_account_currency != self.currency):
+			frappe.throw(
+				_("Party Account {0} currency and document currency should be same").format(
+					frappe.bold(party_account)
+				)
+			)
+
 	def delink_advance_entries(self, linked_doc_name):
 		total_allocated_amount = 0
 		for adv in self.advances:
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
index 8614fcb..dcbdf8a 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -448,8 +448,6 @@
 					"remarks": remarks,
 					"cost_center": self.cost_center,
 					"posting_date": getdate(self.posting_date),
-					"party_type": self.applicant_type if self.repay_from_salary else "",
-					"party": self.applicant if self.repay_from_salary else "",
 				}
 			)
 		)
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 4d9a7e0..8c0ebe7 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -359,7 +359,7 @@
 erpnext.patches.v13_0.update_accounts_in_loan_docs
 erpnext.patches.v14_0.update_batch_valuation_flag
 erpnext.patches.v14_0.delete_non_profit_doctypes
-erpnext.patches.v14_0.update_employee_advance_status
+erpnext.patches.v13_0.update_employee_advance_status
 erpnext.patches.v13_0.add_cost_center_in_loans
 erpnext.patches.v13_0.set_return_against_in_pos_invoice_references
 erpnext.patches.v13_0.remove_unknown_links_to_prod_plan_items # 24-03-2022
diff --git a/erpnext/patches/v14_0/update_employee_advance_status.py b/erpnext/patches/v13_0/update_employee_advance_status.py
similarity index 100%
rename from erpnext/patches/v14_0/update_employee_advance_status.py
rename to erpnext/patches/v13_0/update_employee_advance_status.py
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
index 54d56f9..473fb0d 100644
--- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
@@ -16,6 +16,7 @@
 	comma_and,
 	date_diff,
 	flt,
+	get_link_to_form,
 	getdate,
 )
 
@@ -45,6 +46,7 @@
 
 	def before_submit(self):
 		self.validate_employee_details()
+		self.validate_payroll_payable_account()
 		if self.validate_attendance:
 			if self.validate_employee_attendance():
 				frappe.throw(_("Cannot Submit, Employees left to mark attendance"))
@@ -66,6 +68,14 @@
 		if len(emp_with_sal_slip):
 			frappe.throw(_("Salary Slip already exists for {0}").format(comma_and(emp_with_sal_slip)))
 
+	def validate_payroll_payable_account(self):
+		if frappe.db.get_value("Account", self.payroll_payable_account, "account_type"):
+			frappe.throw(
+				_(
+					"Account type cannot be set for payroll payable account {0}, please remove and try again"
+				).format(frappe.bold(get_link_to_form("Account", self.payroll_payable_account)))
+			)
+
 	def on_cancel(self):
 		frappe.delete_doc(
 			"Salary Slip",
diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json
index 1cda0a0..1790da4 100644
--- a/erpnext/projects/doctype/project/project.json
+++ b/erpnext/projects/doctype/project/project.json
@@ -234,7 +234,7 @@
   },
   {
    "fieldname": "actual_start_date",
-   "fieldtype": "Data",
+   "fieldtype": "Date",
    "label": "Actual Start Date (via Time Sheet)",
    "read_only": 1
   },
@@ -458,7 +458,7 @@
  "index_web_pages_for_search": 1,
  "links": [],
  "max_attachments": 4,
- "modified": "2022-01-29 13:58:27.712714",
+ "modified": "2022-05-25 22:45:06.108499",
  "modified_by": "Administrator",
  "module": "Projects",
  "name": "Project",
@@ -504,4 +504,4 @@
  "timeline_field": "customer",
  "title_field": "project_name",
  "track_seen": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 3dd11f6..16b0b4a 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -789,11 +789,23 @@
 		if(this.frm.doc.is_pos && (update_paid_amount===undefined || update_paid_amount)) {
 			$.each(this.frm.doc['payments'] || [], function(index, data) {
 				if(data.default && payment_status && total_amount_to_pay > 0) {
-					let base_amount = flt(total_amount_to_pay, precision("base_amount", data));
+					let base_amount, amount;
+
+					if (me.frm.doc.party_account_currency == me.frm.doc.currency) {
+						// if customer/supplier currency is same as company currency
+						// total_amount_to_pay is already in customer/supplier currency
+						// so base_amount has to be calculated using total_amount_to_pay
+						base_amount = flt(total_amount_to_pay * me.frm.doc.conversion_rate, precision("base_amount", data));
+						amount = flt(total_amount_to_pay, precision("amount", data));
+					} else {
+						base_amount = flt(total_amount_to_pay, precision("base_amount", data));
+						amount = flt(total_amount_to_pay / me.frm.doc.conversion_rate, precision("amount", data));
+					}
+
 					frappe.model.set_value(data.doctype, data.name, "base_amount", base_amount);
-					let amount = flt(total_amount_to_pay / me.frm.doc.conversion_rate, precision("amount", data));
 					frappe.model.set_value(data.doctype, data.name, "amount", amount);
 					payment_status = false;
+
 				} else if(me.frm.doc.paid_amount) {
 					frappe.model.set_value(data.doctype, data.name, "amount", 0.0);
 				}
diff --git a/erpnext/regional/india/taxes.js b/erpnext/regional/india/taxes.js
index 5f6dcde..88973e3 100644
--- a/erpnext/regional/india/taxes.js
+++ b/erpnext/regional/india/taxes.js
@@ -22,6 +22,7 @@
 				'shipping_address': frm.doc.shipping_address || '',
 				'shipping_address_name': frm.doc.shipping_address_name || '',
 				'customer_address': frm.doc.customer_address || '',
+				'company_address': frm.doc.company_address,
 				'supplier_address': frm.doc.supplier_address,
 				'customer': frm.doc.customer,
 				'supplier': frm.doc.supplier,
diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py
index fd0fe26..0bdbe56 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.py
+++ b/erpnext/regional/report/gstr_1/gstr_1.py
@@ -448,7 +448,7 @@
 					hsn_code = self.item_hsn_map.get(item_code)
 					tax_rate = 0
 					taxable_value = items.get(item_code)
-					for rates in hsn_wise_tax_rate.get(hsn_code):
+					for rates in hsn_wise_tax_rate.get(hsn_code, []):
 						if taxable_value > rates.get("minimum_taxable_value"):
 							tax_rate = rates.get("tax_rate")
 
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index ce3bd56..7fbfa62 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -1285,6 +1285,14 @@
 		from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import (
 			make_purchase_invoice as create_purchase_invoice,
 		)
+		from erpnext.accounts.party import add_party_account
+
+		add_party_account(
+			"Supplier",
+			"_Test Supplier USD",
+			"_Test Company with perpetual inventory",
+			"_Test Payable USD - TCP1",
+		)
 
 		pi = create_purchase_invoice(
 			company="_Test Company with perpetual inventory",
@@ -1293,6 +1301,7 @@
 			expense_account="_Test Account Cost for Goods Sold - TCP1",
 			currency="USD",
 			conversion_rate=70,
+			supplier="_Test Supplier USD",
 		)
 
 		pr = create_purchase_receipt(pi.name)