Merge branch 'develop' into fix-minor-custom-cash-flow
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py
index fe04efb..12a3112 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.py
@@ -42,10 +42,18 @@
 			"2": "Cancelled"
 		}[cstr(self.docstatus or 0)]
 
-		paid_amount = flt(self.total_amount_reimbursed) + flt(self.total_advance_amount)
 		precision = self.precision("grand_total")
-		if (self.is_paid or (flt(self.total_sanctioned_amount) > 0 and self.docstatus == 1
-			and flt(self.grand_total, precision) == flt(paid_amount, precision))) and self.approval_status == 'Approved':
+
+		if (
+			# set as paid
+			self.is_paid
+			or (flt(self.total_sanctioned_amount > 0) and (
+				# grand total is reimbursed
+				(self.docstatus == 1 and flt(self.grand_total, precision) == flt(self.total_amount_reimbursed, precision))
+				# grand total (to be paid) is 0 since linked advances already cover the claimed amount
+				or (flt(self.grand_total, precision) == 0)
+			))
+		) and self.approval_status == "Approved":
 			status = "Paid"
 		elif flt(self.total_sanctioned_amount) > 0 and self.docstatus == 1 and self.approval_status == 'Approved':
 			status = "Unpaid"
diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py
index 2a07920..1244cc4 100644
--- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.py
@@ -72,6 +72,72 @@
 		expense_claim = frappe.get_doc("Expense Claim", expense_claim.name)
 		self.assertEqual(expense_claim.status, "Unpaid")
 
+		# expense claim without any sanctioned amount should not have status as Paid
+		claim = make_expense_claim(payable_account, 1000, 0, "_Test Company", "Travel Expenses - _TC")
+		self.assertEqual(claim.total_sanctioned_amount, 0)
+		self.assertEqual(claim.status, "Submitted")
+
+		# no gl entries created
+		gl_entry = frappe.get_all('GL Entry', {'voucher_type': 'Expense Claim', 'voucher_no': claim.name})
+		self.assertEqual(len(gl_entry), 0)
+
+	def test_expense_claim_against_fully_paid_advances(self):
+		from erpnext.hr.doctype.employee_advance.test_employee_advance import (
+			get_advances_for_claim,
+			make_employee_advance,
+			make_payment_entry,
+		)
+
+		frappe.db.delete("Employee Advance")
+
+		payable_account = get_payable_account("_Test Company")
+		claim = make_expense_claim(payable_account, 1000, 1000, "_Test Company", "Travel Expenses - _TC", do_not_submit=True)
+
+		advance = make_employee_advance(claim.employee)
+		pe = make_payment_entry(advance)
+		pe.submit()
+
+		# claim for already paid out advances
+		claim = get_advances_for_claim(claim, advance.name)
+		claim.save()
+		claim.submit()
+
+		self.assertEqual(claim.grand_total, 0)
+		self.assertEqual(claim.status, "Paid")
+
+	def test_expense_claim_partially_paid_via_advance(self):
+		from erpnext.hr.doctype.employee_advance.test_employee_advance import (
+			get_advances_for_claim,
+			make_employee_advance,
+		)
+		from erpnext.hr.doctype.employee_advance.test_employee_advance import (
+			make_payment_entry as make_advance_payment,
+		)
+
+		frappe.db.delete("Employee Advance")
+
+		payable_account = get_payable_account("_Test Company")
+		claim = make_expense_claim(payable_account, 1000, 1000, "_Test Company", "Travel Expenses - _TC", do_not_submit=True)
+
+		# link advance for partial amount
+		advance = make_employee_advance(claim.employee, {'advance_amount': 500})
+		pe = make_advance_payment(advance)
+		pe.submit()
+
+		claim = get_advances_for_claim(claim, advance.name)
+		claim.save()
+		claim.submit()
+
+		self.assertEqual(claim.grand_total, 500)
+		self.assertEqual(claim.status, "Unpaid")
+
+		# reimburse remaning amount
+		make_payment_entry(claim, payable_account, 500)
+		claim.reload()
+
+		self.assertEqual(claim.total_amount_reimbursed, 500)
+		self.assertEqual(claim.status, "Paid")
+
 	def test_expense_claim_gl_entry(self):
 		payable_account = get_payable_account(company_name)
 		taxes = generate_taxes()
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json
index 339c6af..85f9843 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.json
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json
@@ -351,7 +351,6 @@
    "hide_border": 1
   },
   {
-   "depends_on": "get_items_from",
    "fieldname": "sub_assembly_items",
    "fieldtype": "Table",
    "label": "Sub Assembly Items",
@@ -385,7 +384,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2022-03-14 03:56:23.209247",
+ "modified": "2022-03-25 09:15:25.017664",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Production Plan",
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index dc1d692..028834a 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -362,3 +362,4 @@
 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
+erpnext.patches.v13_0.update_expense_claim_status_for_paid_advances
diff --git a/erpnext/patches/v13_0/update_expense_claim_status_for_paid_advances.py b/erpnext/patches/v13_0/update_expense_claim_status_for_paid_advances.py
new file mode 100644
index 0000000..063de16
--- /dev/null
+++ b/erpnext/patches/v13_0/update_expense_claim_status_for_paid_advances.py
@@ -0,0 +1,22 @@
+import frappe
+
+
+def execute():
+	"""
+	Update Expense Claim status to Paid if:
+		- the entire required amount is already covered via linked advances
+		- the claim is partially paid via advances and the rest is reimbursed
+	"""
+
+	ExpenseClaim = frappe.qb.DocType('Expense Claim')
+
+	(frappe.qb
+		.update(ExpenseClaim)
+		.set(ExpenseClaim.status, 'Paid')
+		.where(
+			((ExpenseClaim.grand_total == 0) | (ExpenseClaim.grand_total == ExpenseClaim.total_amount_reimbursed))
+			& (ExpenseClaim.approval_status == 'Approved')
+			& (ExpenseClaim.docstatus == 1)
+			& (ExpenseClaim.total_sanctioned_amount > 0)
+		)
+	).run()
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 19e12e3..a8cec0a 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -1044,7 +1044,7 @@
 		var company_currency = this.get_company_currency();
 		// Added `ignore_price_list` to determine if document is loading after mapping from another doc
 		if(this.frm.doc.currency && this.frm.doc.currency !== company_currency
-				&& !this.frm.doc.__onload.ignore_price_list) {
+				&& !(this.frm.doc.__onload && this.frm.doc.__onload.ignore_price_list)) {
 
 			this.get_exchange_rate(transaction_date, this.frm.doc.currency, company_currency,
 				function(exchange_rate) {
@@ -1145,7 +1145,8 @@
 
 		var company_currency = this.get_company_currency();
 		// Added `ignore_price_list` to determine if document is loading after mapping from another doc
-		if(this.frm.doc.price_list_currency !== company_currency  && !this.frm.doc.__onload.ignore_price_list) {
+		if(this.frm.doc.price_list_currency !== company_currency  &&
+				!(this.frm.doc.__onload && this.frm.doc.__onload.ignore_price_list)) {
 			this.get_exchange_rate(this.frm.doc.posting_date, this.frm.doc.price_list_currency, company_currency,
 				function(exchange_rate) {
 					me.frm.set_value("plc_conversion_rate", exchange_rate);