From 90c751f64884ed49ec94e22fbf72d7ac18968d55 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 18 Jul 2022 12:39:09 +0530 Subject: [PATCH 1/2] fix: Tax amount not considered in Expense Claim Journal Entry - add a helper function `get_outstanding_amount_for_claim` for uniformity in usages --- .../doctype/journal_entry/journal_entry.py | 16 +++++----- .../doctype/payment_entry/payment_entry.py | 12 ++++---- .../hr/doctype/expense_claim/expense_claim.py | 30 +++++++++++++++---- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index c46b185e60a..8cc20038770 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -25,7 +25,10 @@ from erpnext.accounts.utils import ( get_stock_and_account_balance, ) from erpnext.controllers.accounts_controller import AccountsController -from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount +from erpnext.hr.doctype.expense_claim.expense_claim import ( + get_outstanding_amount_for_claim, + update_reimbursed_amount, +) class StockAccountInvalidTransaction(frappe.ValidationError): @@ -935,15 +938,12 @@ class JournalEntry(AccountsController): def validate_expense_claim(self): for d in self.accounts: if d.reference_type == "Expense Claim": - sanctioned_amount, reimbursed_amount = frappe.db.get_value( - "Expense Claim", d.reference_name, ("total_sanctioned_amount", "total_amount_reimbursed") - ) - pending_amount = flt(sanctioned_amount) - flt(reimbursed_amount) - if d.debit > pending_amount: + outstanding_amt = get_outstanding_amount_for_claim(d.reference_name) + if d.debit > outstanding_amt: frappe.throw( _( - "Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2}" - ).format(d.idx, d.reference_name, pending_amount) + "Row No {0}: Amount cannot be greater than the Outstanding Amount against Expense Claim {1}. Outstanding Amount is {2}" + ).format(d.idx, d.reference_name, outstanding_amt) ) def validate_credit_debit_note(self): diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 2728c37d5b7..d8ce89c59d2 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -30,7 +30,10 @@ from erpnext.controllers.accounts_controller import ( get_supplier_block_status, validate_taxes_and_charges, ) -from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount +from erpnext.hr.doctype.expense_claim.expense_claim import ( + get_outstanding_amount_for_claim, + update_reimbursed_amount, +) from erpnext.setup.utils import get_exchange_rate @@ -1649,12 +1652,7 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre outstanding_amount = ref_doc.get("outstanding_amount") bill_no = ref_doc.get("bill_no") elif reference_doctype == "Expense Claim": - outstanding_amount = ( - flt(ref_doc.get("total_sanctioned_amount")) - + flt(ref_doc.get("total_taxes_and_charges")) - - flt(ref_doc.get("total_amount_reimbursed")) - - flt(ref_doc.get("total_advance_amount")) - ) + outstanding_amount = get_outstanding_amount_for_claim(ref_doc) elif reference_doctype == "Employee Advance": outstanding_amount = flt(ref_doc.advance_amount) - flt(ref_doc.paid_amount) if party_account_currency != ref_doc.currency: diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py index 89d86c1bc7c..ab0e62b378e 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/expense_claim.py @@ -339,6 +339,30 @@ def update_reimbursed_amount(doc, amount): frappe.db.set_value("Expense Claim", doc.name, "status", doc.status) +def get_outstanding_amount_for_claim(claim): + if isinstance(claim, str): + claim = frappe.db.get_value( + "Expense Claim", + claim, + ( + "total_sanctioned_amount", + "total_taxes_and_charges", + "total_amount_reimbursed", + "total_advance_amount", + ), + as_dict=True, + ) + + outstanding_amt = ( + flt(claim.total_sanctioned_amount) + + flt(claim.total_taxes_and_charges) + - flt(claim.total_amount_reimbursed) + - flt(claim.total_advance_amount) + ) + + return outstanding_amt + + @frappe.whitelist() def make_bank_entry(dt, dn): from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account @@ -348,11 +372,7 @@ def make_bank_entry(dt, dn): if not default_bank_cash_account: default_bank_cash_account = get_default_bank_cash_account(expense_claim.company, "Cash") - payable_amount = ( - flt(expense_claim.total_sanctioned_amount) - - flt(expense_claim.total_amount_reimbursed) - - flt(expense_claim.total_advance_amount) - ) + payable_amount = get_outstanding_amount_for_claim(expense_claim) je = frappe.new_doc("Journal Entry") je.voucher_type = "Bank Entry" From 6a7549d4b2ede2b87dc85bb85b4dd709f8b7398e Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 18 Jul 2022 12:57:50 +0530 Subject: [PATCH 2/2] test: Journal Entry against Expense Claim --- .../expense_claim/test_expense_claim.py | 52 +++++++++++++++---- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py index 9b3d53a2105..62df1e02900 100644 --- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.py @@ -4,6 +4,7 @@ import unittest import frappe +from frappe.tests.utils import FrappeTestCase from frappe.utils import flt, nowdate, random_string from erpnext.accounts.doctype.account.test_account import create_account @@ -14,9 +15,18 @@ test_dependencies = ["Employee"] company_name = "_Test Company 3" -class TestExpenseClaim(unittest.TestCase): - def tearDown(self): - frappe.db.rollback() +class TestExpenseClaim(FrappeTestCase): + def setUp(self): + if not frappe.db.get_value("Cost Center", {"company": company_name}): + frappe.get_doc( + { + "doctype": "Cost Center", + "cost_center_name": "_Test Cost Center 3", + "parent_cost_center": "_Test Company 3 - _TC3", + "is_group": 0, + "company": company_name, + } + ).insert() def test_total_expense_claim_for_project(self): frappe.db.sql("""delete from `tabTask`""") @@ -58,12 +68,7 @@ class TestExpenseClaim(unittest.TestCase): payable_account, 300, 200, company_name, "Travel Expenses - _TC3" ) - je_dict = make_bank_entry("Expense Claim", expense_claim.name) - je = frappe.get_doc(je_dict) - je.posting_date = nowdate() - je.cheque_no = random_string(5) - je.cheque_date = nowdate() - je.submit() + je = make_journal_entry(expense_claim) expense_claim = frappe.get_doc("Expense Claim", expense_claim.name) self.assertEqual(expense_claim.status, "Paid") @@ -272,6 +277,24 @@ class TestExpenseClaim(unittest.TestCase): self.assertEqual(outstanding_amount, 0) self.assertEqual(total_amount_reimbursed, 5500) + def test_journal_entry_against_expense_claim(self): + payable_account = get_payable_account(company_name) + taxes = generate_taxes() + expense_claim = make_expense_claim( + payable_account, + 300, + 200, + company_name, + "Travel Expenses - _TC3", + do_not_submit=True, + taxes=taxes, + ) + expense_claim.submit() + + je = make_journal_entry(expense_claim) + + self.assertEqual(je.accounts[0].debit_in_account_currency, expense_claim.grand_total) + def get_payable_account(company): return frappe.get_cached_value("Company", company, "default_payable_account") @@ -370,3 +393,14 @@ def make_payment_entry(expense_claim, payable_account, amt): pe.references[0].allocated_amount = amt pe.insert() pe.submit() + + +def make_journal_entry(expense_claim): + je_dict = make_bank_entry("Expense Claim", expense_claim.name) + je = frappe.get_doc(je_dict) + je.posting_date = nowdate() + je.cheque_no = random_string(5) + je.cheque_date = nowdate() + je.submit() + + return je