mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-27 05:45:19 +00:00
feat(accounts): split bank charges from exchange gain/loss on multi-currency transfers (#58071)
In a multi-currency Internal Transfer, the paid-vs-received difference was booked entirely to Exchange Gain/Loss, so a bank charge entered as a deduction pushed the Difference Amount non-zero and blocked submission. The exchange gain/loss row now absorbs only the residual after user-entered deductions, letting a Bank Charges row and the Exchange Gain/Loss row coexist and net to zero.
This commit is contained in:
@@ -1279,8 +1279,14 @@ frappe.ui.form.on("Payment Entry", {
|
||||
await frappe.after_ajax();
|
||||
const base_paid_amount = frm.doc.base_paid_amount || 0;
|
||||
const base_received_amount = frm.doc.base_received_amount || 0;
|
||||
let other_deductions = 0;
|
||||
if (frm.doc.payment_type === "Internal Transfer") {
|
||||
other_deductions = (frm.doc.deductions || [])
|
||||
.filter((row) => !row.is_exchange_gain_loss)
|
||||
.reduce((sum, row) => sum + flt(row.amount), 0);
|
||||
}
|
||||
const exchange_gain_loss = flt(
|
||||
base_paid_amount - base_received_amount,
|
||||
base_paid_amount - base_received_amount - other_deductions,
|
||||
get_deduction_amount_precision()
|
||||
);
|
||||
|
||||
@@ -1857,11 +1863,19 @@ frappe.ui.form.on("Payment Entry Deduction", {
|
||||
},
|
||||
|
||||
amount: function (frm) {
|
||||
frm.events.set_unallocated_amount(frm);
|
||||
if (frm.doc.payment_type === "Internal Transfer") {
|
||||
frm.events.set_exchange_gain_loss_deduction(frm);
|
||||
} else {
|
||||
frm.events.set_unallocated_amount(frm);
|
||||
}
|
||||
},
|
||||
|
||||
deductions_remove: function (frm) {
|
||||
frm.events.set_unallocated_amount(frm);
|
||||
if (frm.doc.payment_type === "Internal Transfer") {
|
||||
frm.events.set_exchange_gain_loss_deduction(frm);
|
||||
} else {
|
||||
frm.events.set_unallocated_amount(frm);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1118,8 +1118,14 @@ class PaymentEntry(AccountsController):
|
||||
)
|
||||
|
||||
def set_exchange_gain_loss(self):
|
||||
other_deductions = 0
|
||||
if self.payment_type == "Internal Transfer":
|
||||
other_deductions = sum(
|
||||
flt(row.amount) for row in self.get("deductions") if not row.is_exchange_gain_loss
|
||||
)
|
||||
|
||||
exchange_gain_loss = flt(
|
||||
self.base_paid_amount - self.base_received_amount,
|
||||
self.base_paid_amount - self.base_received_amount - other_deductions,
|
||||
self.precision("amount", "deductions"),
|
||||
)
|
||||
|
||||
|
||||
@@ -870,6 +870,64 @@ class TestPaymentEntry(ERPNextTestSuite):
|
||||
|
||||
self.validate_gl_entries(pe.name, expected_gle)
|
||||
|
||||
def test_cross_currency_transfer_splits_bank_charge_and_exchange_gain_loss(self):
|
||||
exchange_gain_loss_account = frappe.db.get_value(
|
||||
"Company", "_Test Company", "exchange_gain_loss_account"
|
||||
)
|
||||
bank_charges_account = create_account(
|
||||
parent_account="Indirect Expenses - _TC",
|
||||
account_name="_Test Bank Charges",
|
||||
company="_Test Company",
|
||||
)
|
||||
|
||||
pe = frappe.new_doc("Payment Entry")
|
||||
pe.payment_type = "Internal Transfer"
|
||||
pe.company = "_Test Company"
|
||||
pe.paid_from = "_Test Bank USD - _TC"
|
||||
pe.paid_to = "_Test Bank - _TC"
|
||||
pe.paid_amount = 100
|
||||
pe.source_exchange_rate = 50
|
||||
pe.received_amount = 4500
|
||||
pe.reference_no = "6"
|
||||
pe.reference_date = nowdate()
|
||||
pe.append(
|
||||
"deductions",
|
||||
{
|
||||
"account": bank_charges_account,
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"amount": 100,
|
||||
},
|
||||
)
|
||||
|
||||
pe.setup_party_account_field()
|
||||
pe.set_missing_values()
|
||||
pe.set_exchange_rate()
|
||||
pe.set_amounts()
|
||||
|
||||
deductions = {d.account: d for d in pe.deductions}
|
||||
self.assertEqual(deductions[bank_charges_account].amount, 100)
|
||||
self.assertEqual(deductions[exchange_gain_loss_account].amount, 400)
|
||||
self.assertTrue(deductions[exchange_gain_loss_account].is_exchange_gain_loss)
|
||||
self.assertEqual(pe.difference_amount, 0)
|
||||
|
||||
for d in pe.deductions:
|
||||
d.cost_center = "_Test Cost Center - _TC"
|
||||
|
||||
pe.insert()
|
||||
pe.submit()
|
||||
|
||||
expected_gle = dict(
|
||||
(d[0], d)
|
||||
for d in [
|
||||
["_Test Bank USD - _TC", 0, 5000, None],
|
||||
["_Test Bank - _TC", 4500, 0, None],
|
||||
[exchange_gain_loss_account, 400.0, 0, None],
|
||||
[bank_charges_account, 100.0, 0, None],
|
||||
]
|
||||
)
|
||||
|
||||
self.validate_gl_entries(pe.name, expected_gle)
|
||||
|
||||
def test_payment_against_negative_sales_invoice(self):
|
||||
si1 = create_sales_invoice()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user