From 00518eb384df35d331633c3860931152fc666734 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 21 Mar 2023 16:16:36 +0530 Subject: [PATCH] fix: exchange gain/loss GL's should be removed if advance is cancelled (#34529) * fix: report GL for invoice when advance has different exchange rate If deferred revenue/expense is enabled for any item, don't repost. * test: cancelling advance should remove exchange gain/loss If there are no deferred revenue/expense cancelling advance should cancel the exchange gain/loss booked due to different exchange rates of payment and its linked invoice --- .../doctype/payment_entry/payment_entry.py | 25 ++++++- .../sales_invoice/test_sales_invoice.py | 72 +++++++++++++++++++ 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index f4367cdafd6..f0d7d57fc64 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -7,7 +7,7 @@ from functools import reduce import frappe from frappe import ValidationError, _, scrub, throw -from frappe.utils import cint, comma_or, flt, getdate, nowdate +from frappe.utils import cint, comma_or, flt, get_link_to_form, getdate, nowdate from six import iteritems, string_types import erpnext @@ -168,8 +168,31 @@ class PaymentEntry(AccountsController): for reference in self.references: if reference.reference_doctype in ("Sales Invoice", "Purchase Invoice"): doc = frappe.get_doc(reference.reference_doctype, reference.reference_name) + + repost_required = False + for adv_reference in doc.get("advances"): + if adv_reference.exchange_gain_loss != 0: + repost_required = True + break + if repost_required: + for item in doc.get("items"): + if item.get("enable_deferred_revenue") or item.get("enable_deferred_expense"): + frappe.msgprint( + _( + "Linked Invoice {0} has Exchange Gain/Loss GL entries due to this Payment. Submit a Journal manually to reverse its effects." + ).format(get_link_to_form(doc.doctype, doc.name)) + ) + repost_required = False + doc.delink_advance_entries(self.name) + if repost_required: + doc.reload() + doc.docstatus = 2 + doc.make_gl_entries() + doc.docstatus = 1 + doc.make_gl_entries() + def set_missing_values(self): if self.payment_type == "Internal Transfer": for field in ( diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 6035e86d067..46ffd7e18d0 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3470,6 +3470,78 @@ class TestSalesInvoice(unittest.TestCase): "Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice", unlink_enabled ) + def test_gain_loss_on_advance_cancellation(self): + unlink_enabled = frappe.db.get_single_value( + "Accounts Settings", "unlink_payment_on_cancellation_of_invoice" + ) + + frappe.db.set_single_value("Accounts Settings", "unlink_payment_on_cancellation_of_invoice", 1) + + pe = frappe.get_doc( + { + "doctype": "Payment Entry", + "payment_type": "Receive", + "party_type": "Customer", + "party": "_Test Customer USD", + "company": "_Test Company", + "paid_from_account_currency": "USD", + "paid_to_account_currency": "INR", + "source_exchange_rate": 70, + "target_exchange_rate": 1, + "reference_no": "1", + "reference_date": nowdate(), + "received_amount": 70, + "paid_amount": 1, + "paid_from": "_Test Receivable USD - _TC", + "paid_to": "_Test Cash - _TC", + } + ) + pe.insert() + pe.submit() + + si = create_sales_invoice( + customer="_Test Customer USD", + debit_to="_Test Receivable USD - _TC", + currency="USD", + conversion_rate=75, + do_not_save=1, + rate=1, + ) + si = si.save() + + si.append( + "advances", + { + "reference_type": "Payment Entry", + "reference_name": pe.name, + "advance_amount": 1, + "allocated_amount": 1, + "ref_exchange_rate": 70, + }, + ) + si.save() + si.submit() + expected_gle = [ + ["_Test Receivable USD - _TC", 75.0, 5.0], + ["Exchange Gain/Loss - _TC", 5.0, 0.0], + ["Sales - _TC", 0.0, 75.0], + ] + check_gl_entries(self, si.name, expected_gle, nowdate()) + + # cancel advance payment + pe.reload() + pe.cancel() + + expected_gle_after = [ + ["_Test Receivable USD - _TC", 75.0, 0.0], + ["Sales - _TC", 0.0, 75.0], + ] + check_gl_entries(self, si.name, expected_gle_after, nowdate()) + + frappe.db.set_single_value( + "Accounts Settings", "unlink_payment_on_cancellation_of_invoice", unlink_enabled + ) + def test_batch_expiry_for_sales_invoice_return(self): from erpnext.controllers.sales_and_purchase_return import make_return_doc from erpnext.stock.doctype.item.test_item import make_item