Merge pull request #39591 from ruthra-kumar/conversion_on_future_payments

refactor: Do proper currency conversion on Future Payments column in AR/AP report
This commit is contained in:
ruthra kumar 2024-01-27 12:52:18 +05:30 committed by GitHub
commit 5be868c7f6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 119 additions and 7 deletions

View File

@ -5,7 +5,7 @@
from collections import OrderedDict
import frappe
from frappe import _, qb, scrub
from frappe import _, qb, query_builder, scrub
from frappe.query_builder import Criterion
from frappe.query_builder.functions import Date, Substring, Sum
from frappe.utils import cint, cstr, flt, getdate, nowdate
@ -576,6 +576,8 @@ class ReceivablePayableReport(object):
def get_future_payments_from_payment_entry(self):
pe = frappe.qb.DocType("Payment Entry")
pe_ref = frappe.qb.DocType("Payment Entry Reference")
ifelse = query_builder.CustomFunction("IF", ["condition", "then", "else"])
return (
frappe.qb.from_(pe)
.inner_join(pe_ref)
@ -587,6 +589,11 @@ class ReceivablePayableReport(object):
(pe.posting_date).as_("future_date"),
(pe_ref.allocated_amount).as_("future_amount"),
(pe.reference_no).as_("future_ref"),
ifelse(
pe.payment_type == "Receive",
pe.source_exchange_rate * pe_ref.allocated_amount,
pe.target_exchange_rate * pe_ref.allocated_amount,
).as_("future_amount_in_base_currency"),
)
.where(
(pe.docstatus < 2)
@ -623,13 +630,24 @@ class ReceivablePayableReport(object):
query = query.select(
Sum(jea.debit_in_account_currency - jea.credit_in_account_currency).as_("future_amount")
)
query = query.select(Sum(jea.debit - jea.credit).as_("future_amount_in_base_currency"))
else:
query = query.select(
Sum(jea.credit_in_account_currency - jea.debit_in_account_currency).as_("future_amount")
)
query = query.select(Sum(jea.credit - jea.debit).as_("future_amount_in_base_currency"))
else:
query = query.select(
Sum(jea.debit if self.account_type == "Payable" else jea.credit).as_("future_amount")
Sum(jea.debit if self.account_type == "Payable" else jea.credit).as_(
"future_amount_in_base_currency"
)
)
query = query.select(
Sum(
jea.debit_in_account_currency
if self.account_type == "Payable"
else jea.credit_in_account_currency
).as_("future_amount")
)
query = query.having(qb.Field("future_amount") > 0)
@ -645,14 +663,19 @@ class ReceivablePayableReport(object):
row.remaining_balance = row.outstanding
row.future_amount = 0.0
for future in self.future_payments.get((row.voucher_no, row.party), []):
if row.remaining_balance > 0 and future.future_amount:
if future.future_amount > row.outstanding:
if self.filters.in_party_currency:
future_amount_field = "future_amount"
else:
future_amount_field = "future_amount_in_base_currency"
if row.remaining_balance > 0 and future.get(future_amount_field):
if future.get(future_amount_field) > row.outstanding:
row.future_amount = row.outstanding
future.future_amount = future.future_amount - row.outstanding
future[future_amount_field] = future.get(future_amount_field) - row.outstanding
row.remaining_balance = 0
else:
row.future_amount += future.future_amount
future.future_amount = 0
row.future_amount += future.get(future_amount_field)
future[future_amount_field] = 0
row.remaining_balance = row.outstanding - row.future_amount
row.setdefault("future_ref", []).append(

View File

@ -772,3 +772,92 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
# post sorting output should be [[Additional Debtors, ...], [Debtors, ...]]
report_output = sorted(report_output, key=lambda x: x[0])
self.assertEqual(expected_data, report_output)
def test_future_payments_on_foreign_currency(self):
self.customer2 = (
frappe.get_doc(
{
"doctype": "Customer",
"customer_name": "Jane Doe",
"type": "Individual",
"default_currency": "USD",
}
)
.insert()
.submit()
)
si = self.create_sales_invoice(do_not_submit=True)
si.posting_date = add_days(today(), -1)
si.customer = self.customer2
si.currency = "USD"
si.conversion_rate = 80
si.debit_to = self.debtors_usd
si.save().submit()
# full payment in USD
pe = get_payment_entry(si.doctype, si.name)
pe.posting_date = add_days(today(), 1)
pe.base_received_amount = 7500
pe.received_amount = 7500
pe.source_exchange_rate = 75
pe.save().submit()
filters = frappe._dict(
{
"company": self.company,
"report_date": today(),
"range1": 30,
"range2": 60,
"range3": 90,
"range4": 120,
"show_future_payments": True,
"in_party_currency": False,
}
)
report = execute(filters)[1]
self.assertEqual(len(report), 1)
expected_data = [8000.0, 8000.0, 500.0, 7500.0]
row = report[0]
self.assertEqual(
expected_data, [row.invoiced, row.outstanding, row.remaining_balance, row.future_amount]
)
filters.in_party_currency = True
report = execute(filters)[1]
self.assertEqual(len(report), 1)
expected_data = [100.0, 100.0, 0.0, 100.0]
row = report[0]
self.assertEqual(
expected_data, [row.invoiced, row.outstanding, row.remaining_balance, row.future_amount]
)
pe.cancel()
# partial payment in USD on a future date
pe = get_payment_entry(si.doctype, si.name)
pe.posting_date = add_days(today(), 1)
pe.base_received_amount = 6750
pe.received_amount = 6750
pe.source_exchange_rate = 75
pe.paid_amount = 90 # in USD
pe.references[0].allocated_amount = 90
pe.save().submit()
filters.in_party_currency = False
report = execute(filters)[1]
self.assertEqual(len(report), 1)
expected_data = [8000.0, 8000.0, 1250.0, 6750.0]
row = report[0]
self.assertEqual(
expected_data, [row.invoiced, row.outstanding, row.remaining_balance, row.future_amount]
)
filters.in_party_currency = True
report = execute(filters)[1]
self.assertEqual(len(report), 1)
expected_data = [100.0, 100.0, 10.0, 90.0]
row = report[0]
self.assertEqual(
expected_data, [row.invoiced, row.outstanding, row.remaining_balance, row.future_amount]
)