Merge pull request #37630 from ruthra-kumar/provision_to_set_for_gain_loss_journal
refactor: configurable exchange gain or loss posting date
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
index d9f00be..fc90c3d 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
@@ -229,6 +229,7 @@
this.data = [];
const dialog = new frappe.ui.Dialog({
title: __("Select Difference Account"),
+ size: 'extra-large',
fields: [
{
fieldname: "allocation",
@@ -252,6 +253,13 @@
in_list_view: 1,
read_only: 1
}, {
+ fieldtype:'Date',
+ fieldname:"gain_loss_posting_date",
+ label: __("Posting Date"),
+ in_list_view: 1,
+ reqd: 1,
+ }, {
+
fieldtype:'Link',
options: 'Account',
in_list_view: 1,
@@ -285,6 +293,9 @@
args.forEach(d => {
frappe.model.set_value("Payment Reconciliation Allocation", d.docname,
"difference_account", d.difference_account);
+ frappe.model.set_value("Payment Reconciliation Allocation", d.docname,
+ "gain_loss_posting_date", d.gain_loss_posting_date);
+
});
this.reconcile_payment_entries();
@@ -300,6 +311,7 @@
'reference_name': d.reference_name,
'difference_amount': d.difference_amount,
'difference_account': d.difference_account,
+ 'gain_loss_posting_date': d.gain_loss_posting_date
});
}
});
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
index 3285a52..1626f25 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -328,6 +328,7 @@
res.difference_amount = self.get_difference_amount(pay, inv, res["allocated_amount"])
res.difference_account = default_exchange_gain_loss_account
res.exchange_rate = inv.get("exchange_rate")
+ res.update({"gain_loss_posting_date": pay.get("posting_date")})
if pay.get("amount") == 0:
entries.append(res)
@@ -434,6 +435,7 @@
"allocated_amount": flt(row.get("allocated_amount")),
"difference_amount": flt(row.get("difference_amount")),
"difference_account": row.get("difference_account"),
+ "difference_posting_date": row.get("gain_loss_posting_date"),
"cost_center": row.get("cost_center"),
}
)
diff --git a/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json
index ec718aa..5b8556e 100644
--- a/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json
+++ b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json
@@ -19,6 +19,7 @@
"is_advance",
"section_break_5",
"difference_amount",
+ "gain_loss_posting_date",
"column_break_7",
"difference_account",
"exchange_rate",
@@ -151,11 +152,16 @@
"fieldtype": "Link",
"label": "Cost Center",
"options": "Cost Center"
+ },
+ {
+ "fieldname": "gain_loss_posting_date",
+ "fieldtype": "Date",
+ "label": "Difference Posting Date"
}
],
"istable": 1,
"links": [],
- "modified": "2023-09-03 07:52:33.684217",
+ "modified": "2023-10-23 10:44:56.066303",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Reconciliation Allocation",
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 555ed4f..f2691fb 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -679,7 +679,9 @@
if not skip_ref_details_update_for_pe:
payment_entry.set_missing_ref_details()
payment_entry.set_amounts()
- payment_entry.make_exchange_gain_loss_journal()
+ payment_entry.make_exchange_gain_loss_journal(
+ frappe._dict({"difference_posting_date": d.difference_posting_date})
+ )
if not do_not_save:
payment_entry.save(ignore_permissions=True)
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index cc5d643..6efe631 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -1178,7 +1178,9 @@
self.name,
arg.get("referenced_row"),
):
- posting_date = frappe.db.get_value(arg.voucher_type, arg.voucher_no, "posting_date")
+ posting_date = arg.get("difference_posting_date") or frappe.db.get_value(
+ arg.voucher_type, arg.voucher_no, "posting_date"
+ )
je = create_gain_loss_journal(
self.company,
posting_date,
@@ -1261,7 +1263,7 @@
je = create_gain_loss_journal(
self.company,
- self.posting_date,
+ args.get("difference_posting_date") if args else self.posting_date,
self.party_type,
self.party,
party_account,
diff --git a/erpnext/controllers/tests/test_accounts_controller.py b/erpnext/controllers/tests/test_accounts_controller.py
index 391258f..97d3c5c 100644
--- a/erpnext/controllers/tests/test_accounts_controller.py
+++ b/erpnext/controllers/tests/test_accounts_controller.py
@@ -7,7 +7,7 @@
from frappe import qb
from frappe.query_builder.functions import Sum
from frappe.tests.utils import FrappeTestCase, change_settings
-from frappe.utils import add_days, flt, nowdate
+from frappe.utils import add_days, flt, getdate, nowdate
from erpnext import get_default_cost_center
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
@@ -614,6 +614,73 @@
self.assertEqual(exc_je_for_si, [])
self.assertEqual(exc_je_for_pe, [])
+ def test_15_gain_loss_on_different_posting_date(self):
+ # Invoice in Foreign Currency
+ si = self.create_sales_invoice(
+ posting_date=add_days(nowdate(), -2), qty=2, conversion_rate=80, rate=1
+ )
+ # Payment
+ pe = (
+ self.create_payment_entry(posting_date=add_days(nowdate(), -1), amount=2, source_exc_rate=75)
+ .save()
+ .submit()
+ )
+
+ # There should be outstanding in both currencies
+ si.reload()
+ self.assertEqual(si.outstanding_amount, 2)
+ self.assert_ledger_outstanding(si.doctype, si.name, 160.0, 2.0)
+
+ # Reconcile the remaining amount
+ pr = frappe.get_doc("Payment Reconciliation")
+ pr.company = self.company
+ pr.party_type = "Customer"
+ pr.party = self.customer
+ pr.receivable_payable_account = self.debit_usd
+ pr.get_unreconciled_entries()
+ self.assertEqual(len(pr.invoices), 1)
+ self.assertEqual(len(pr.payments), 1)
+ invoices = [x.as_dict() for x in pr.invoices]
+ payments = [x.as_dict() for x in pr.payments]
+ pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
+ pr.allocation[0].gain_loss_posting_date = add_days(nowdate(), 1)
+ pr.reconcile()
+
+ # Exchange Gain/Loss Journal should've been created.
+ exc_je_for_si = self.get_journals_for(si.doctype, si.name)
+ exc_je_for_pe = self.get_journals_for(pe.doctype, pe.name)
+ self.assertNotEqual(exc_je_for_si, [])
+ self.assertEqual(len(exc_je_for_si), 1)
+ self.assertEqual(len(exc_je_for_pe), 1)
+ self.assertEqual(exc_je_for_si[0], exc_je_for_pe[0])
+
+ self.assertEqual(
+ frappe.db.get_value("Journal Entry", exc_je_for_si[0].parent, "posting_date"),
+ getdate(add_days(nowdate(), 1)),
+ )
+
+ self.assertEqual(len(pr.invoices), 0)
+ self.assertEqual(len(pr.payments), 0)
+
+ # There should be no outstanding
+ si.reload()
+ self.assertEqual(si.outstanding_amount, 0)
+ self.assert_ledger_outstanding(si.doctype, si.name, 0.0, 0.0)
+
+ # Cancel Payment
+ pe.reload()
+ pe.cancel()
+
+ si.reload()
+ self.assertEqual(si.outstanding_amount, 2)
+ self.assert_ledger_outstanding(si.doctype, si.name, 160.0, 2.0)
+
+ # Exchange Gain/Loss Journal should've been cancelled
+ exc_je_for_si = self.get_journals_for(si.doctype, si.name)
+ exc_je_for_pe = self.get_journals_for(pe.doctype, pe.name)
+ self.assertEqual(exc_je_for_si, [])
+ self.assertEqual(exc_je_for_pe, [])
+
def test_20_journal_against_sales_invoice(self):
# Invoice in Foreign Currency
si = self.create_sales_invoice(qty=1, conversion_rate=80, rate=1)