Merge pull request #33380 from ruthra-kumar/err_for_invoices_should_reflect_in_ar_ap_report
fix: ERR journals should reported in AR/AP
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index a195c57..9d96843 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -99,6 +99,9 @@
# Get return entries
self.get_return_entries()
+ # Get Exchange Rate Revaluations
+ self.get_exchange_rate_revaluations()
+
self.data = []
for ple in self.ple_entries:
@@ -251,7 +254,8 @@
row.invoice_grand_total = row.invoiced
if (abs(row.outstanding) > 1.0 / 10**self.currency_precision) and (
- abs(row.outstanding_in_account_currency) > 1.0 / 10**self.currency_precision
+ (abs(row.outstanding_in_account_currency) > 1.0 / 10**self.currency_precision)
+ or (row.voucher_no in self.err_journals)
):
# non-zero oustanding, we must consider this row
@@ -1028,3 +1032,17 @@
"data": {"labels": self.ageing_column_labels, "datasets": rows},
"type": "percentage",
}
+
+ def get_exchange_rate_revaluations(self):
+ je = qb.DocType("Journal Entry")
+ results = (
+ qb.from_(je)
+ .select(je.name)
+ .where(
+ (je.company == self.filters.company)
+ & (je.posting_date.lte(self.filters.report_date))
+ & (je.voucher_type == "Exchange Rate Revaluation")
+ )
+ .run()
+ )
+ self.err_journals = [x[0] for x in results] if results else []
diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
index bac8bee..97a9c15 100644
--- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
@@ -1,9 +1,10 @@
import unittest
import frappe
-from frappe.tests.utils import FrappeTestCase
-from frappe.utils import add_days, getdate, today
+from frappe.tests.utils import FrappeTestCase, change_settings
+from frappe.utils import add_days, flt, getdate, today
+from erpnext import get_default_cost_center
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.report.accounts_receivable.accounts_receivable import execute
@@ -17,10 +18,37 @@
frappe.db.sql("delete from `tabPayment Entry` where company='_Test Company 2'")
frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 2'")
frappe.db.sql("delete from `tabPayment Ledger Entry` where company='_Test Company 2'")
+ frappe.db.sql("delete from `tabJournal Entry` where company='_Test Company 2'")
+ frappe.db.sql("delete from `tabExchange Rate Revaluation` where company='_Test Company 2'")
+
+ self.create_usd_account()
def tearDown(self):
frappe.db.rollback()
+ def create_usd_account(self):
+ name = "Debtors USD"
+ exists = frappe.db.get_list(
+ "Account", filters={"company": "_Test Company 2", "account_name": "Debtors USD"}
+ )
+ if exists:
+ self.debtors_usd = exists[0].name
+ else:
+ debtors = frappe.get_doc(
+ "Account",
+ frappe.db.get_list(
+ "Account", filters={"company": "_Test Company 2", "account_name": "Debtors"}
+ )[0].name,
+ )
+
+ debtors_usd = frappe.new_doc("Account")
+ debtors_usd.company = debtors.company
+ debtors_usd.account_name = "Debtors USD"
+ debtors_usd.account_currency = "USD"
+ debtors_usd.parent_account = debtors.parent_account
+ debtors_usd.account_type = debtors.account_type
+ self.debtors_usd = debtors_usd.save().name
+
def test_accounts_receivable(self):
filters = {
"company": "_Test Company 2",
@@ -33,7 +61,7 @@
}
# check invoice grand total and invoiced column's value for 3 payment terms
- name = make_sales_invoice()
+ name = make_sales_invoice().name
report = execute(filters)
expected_data = [[100, 30], [100, 50], [100, 20]]
@@ -118,8 +146,74 @@
],
)
+ @change_settings(
+ "Accounts Settings", {"allow_multi_currency_invoices_against_single_party_account": 1}
+ )
+ def test_exchange_revaluation_for_party(self):
+ """
+ Exchange Revaluation for party on Receivable/Payable shoule be included
+ """
-def make_sales_invoice():
+ company = "_Test Company 2"
+ customer = "_Test Customer 2"
+
+ # Using Exchange Gain/Loss account for unrealized as well.
+ company_doc = frappe.get_doc("Company", company)
+ company_doc.unrealized_exchange_gain_loss_account = company_doc.exchange_gain_loss_account
+ company_doc.save()
+
+ si = make_sales_invoice(no_payment_schedule=True, do_not_submit=True)
+ si.currency = "USD"
+ si.conversion_rate = 0.90
+ si.debit_to = self.debtors_usd
+ si = si.save().submit()
+
+ # Exchange Revaluation
+ err = frappe.new_doc("Exchange Rate Revaluation")
+ err.company = company
+ err.posting_date = today()
+ accounts = err.get_accounts_data()
+ err.extend("accounts", accounts)
+ err.accounts[0].new_exchange_rate = 0.95
+ row = err.accounts[0]
+ row.new_balance_in_base_currency = flt(
+ row.new_exchange_rate * flt(row.balance_in_account_currency)
+ )
+ row.gain_loss = row.new_balance_in_base_currency - flt(row.balance_in_base_currency)
+ err.set_total_gain_loss()
+ err = err.save().submit()
+
+ # Submit JV for ERR
+ jv = frappe.get_doc(err.make_jv_entry())
+ jv = jv.save()
+ for x in jv.accounts:
+ x.cost_center = get_default_cost_center(jv.company)
+ jv.submit()
+
+ filters = {
+ "company": company,
+ "report_date": today(),
+ "range1": 30,
+ "range2": 60,
+ "range3": 90,
+ "range4": 120,
+ }
+ report = execute(filters)
+
+ expected_data_for_err = [0, -5, 0, 5]
+ row = [x for x in report[1] if x.voucher_type == jv.doctype and x.voucher_no == jv.name][0]
+ self.assertEqual(
+ expected_data_for_err,
+ [
+ row.invoiced,
+ row.paid,
+ row.credit_note,
+ row.outstanding,
+ ],
+ )
+
+
+def make_sales_invoice(no_payment_schedule=False, do_not_submit=False):
frappe.set_user("Administrator")
si = create_sales_invoice(
@@ -134,22 +228,26 @@
do_not_save=1,
)
- si.append(
- "payment_schedule",
- dict(due_date=getdate(add_days(today(), 30)), invoice_portion=30.00, payment_amount=30),
- )
- si.append(
- "payment_schedule",
- dict(due_date=getdate(add_days(today(), 60)), invoice_portion=50.00, payment_amount=50),
- )
- si.append(
- "payment_schedule",
- dict(due_date=getdate(add_days(today(), 90)), invoice_portion=20.00, payment_amount=20),
- )
+ if not no_payment_schedule:
+ si.append(
+ "payment_schedule",
+ dict(due_date=getdate(add_days(today(), 30)), invoice_portion=30.00, payment_amount=30),
+ )
+ si.append(
+ "payment_schedule",
+ dict(due_date=getdate(add_days(today(), 60)), invoice_portion=50.00, payment_amount=50),
+ )
+ si.append(
+ "payment_schedule",
+ dict(due_date=getdate(add_days(today(), 90)), invoice_portion=20.00, payment_amount=20),
+ )
- si.submit()
+ si = si.save()
- return si.name
+ if not do_not_submit:
+ si = si.submit()
+
+ return si
def make_payment(docname):