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);