mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-13 18:21:22 +00:00
Merge pull request #43202 from frappe/mergify/bp/version-15-hotfix/pr-43191
fix: delete exchange gain loss journal entry while deleting payment entry (backport #43191)
This commit is contained in:
@@ -1791,6 +1791,79 @@ class TestPaymentEntry(FrappeTestCase):
|
|||||||
# 'Is Opening' should always be 'No' for normal advance payments
|
# 'Is Opening' should always be 'No' for normal advance payments
|
||||||
self.assertEqual(gl_with_opening_set, [])
|
self.assertEqual(gl_with_opening_set, [])
|
||||||
|
|
||||||
|
@change_settings("Accounts Settings", {"delete_linked_ledger_entries": 1})
|
||||||
|
def test_delete_linked_exchange_gain_loss_journal(self):
|
||||||
|
from erpnext.accounts.doctype.account.test_account import create_account
|
||||||
|
from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import (
|
||||||
|
make_customer,
|
||||||
|
)
|
||||||
|
|
||||||
|
debtors = create_account(
|
||||||
|
account_name="Debtors USD",
|
||||||
|
parent_account="Accounts Receivable - _TC",
|
||||||
|
company="_Test Company",
|
||||||
|
account_currency="USD",
|
||||||
|
account_type="Receivable",
|
||||||
|
)
|
||||||
|
|
||||||
|
# create a customer
|
||||||
|
customer = make_customer(customer="_Test Party USD")
|
||||||
|
cust_doc = frappe.get_doc("Customer", customer)
|
||||||
|
cust_doc.default_currency = "USD"
|
||||||
|
test_account_details = {
|
||||||
|
"company": "_Test Company",
|
||||||
|
"account": debtors,
|
||||||
|
}
|
||||||
|
cust_doc.append("accounts", test_account_details)
|
||||||
|
cust_doc.save()
|
||||||
|
|
||||||
|
# create a sales invoice
|
||||||
|
si = create_sales_invoice(
|
||||||
|
customer=customer,
|
||||||
|
currency="USD",
|
||||||
|
conversion_rate=83.970000000,
|
||||||
|
debit_to=debtors,
|
||||||
|
do_not_save=1,
|
||||||
|
)
|
||||||
|
si.party_account_currency = "USD"
|
||||||
|
si.save()
|
||||||
|
si.submit()
|
||||||
|
|
||||||
|
# create a payment entry for the invoice
|
||||||
|
pe = get_payment_entry("Sales Invoice", si.name)
|
||||||
|
pe.reference_no = "1"
|
||||||
|
pe.reference_date = frappe.utils.nowdate()
|
||||||
|
pe.paid_amount = 100
|
||||||
|
pe.source_exchange_rate = 90
|
||||||
|
pe.append(
|
||||||
|
"deductions",
|
||||||
|
{
|
||||||
|
"account": "_Test Exchange Gain/Loss - _TC",
|
||||||
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
|
"amount": 2710,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
pe.save()
|
||||||
|
pe.submit()
|
||||||
|
|
||||||
|
# check creation of journal entry
|
||||||
|
jv = frappe.get_all(
|
||||||
|
"Journal Entry Account",
|
||||||
|
{"reference_type": pe.doctype, "reference_name": pe.name, "docstatus": 1},
|
||||||
|
pluck="parent",
|
||||||
|
)
|
||||||
|
self.assertTrue(jv)
|
||||||
|
|
||||||
|
# check cancellation of payment entry and journal entry
|
||||||
|
pe.cancel()
|
||||||
|
self.assertTrue(pe.docstatus == 2)
|
||||||
|
self.assertTrue(frappe.db.get_value("Journal Entry", {"name": jv[0]}, "docstatus") == 2)
|
||||||
|
|
||||||
|
# check deletion of payment entry and journal entry
|
||||||
|
pe.delete()
|
||||||
|
self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, pe.doctype, pe.name)
|
||||||
|
self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, "Journal Entry", jv[0])
|
||||||
|
|
||||||
|
|
||||||
def create_payment_entry(**args):
|
def create_payment_entry(**args):
|
||||||
payment_entry = frappe.new_doc("Payment Entry")
|
payment_entry = frappe.new_doc("Payment Entry")
|
||||||
|
|||||||
@@ -745,40 +745,74 @@ def cancel_exchange_gain_loss_journal(
|
|||||||
Cancel Exchange Gain/Loss for Sales/Purchase Invoice, if they have any.
|
Cancel Exchange Gain/Loss for Sales/Purchase Invoice, if they have any.
|
||||||
"""
|
"""
|
||||||
if parent_doc.doctype in ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"]:
|
if parent_doc.doctype in ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"]:
|
||||||
journals = frappe.db.get_all(
|
gain_loss_journals = get_linked_exchange_gain_loss_journal(
|
||||||
"Journal Entry Account",
|
referenced_dt=parent_doc.doctype, referenced_dn=parent_doc.name, je_docstatus=1
|
||||||
filters={
|
|
||||||
"reference_type": parent_doc.doctype,
|
|
||||||
"reference_name": parent_doc.name,
|
|
||||||
"docstatus": 1,
|
|
||||||
},
|
|
||||||
fields=["parent"],
|
|
||||||
as_list=1,
|
|
||||||
)
|
)
|
||||||
|
for doc in gain_loss_journals:
|
||||||
if journals:
|
gain_loss_je = frappe.get_doc("Journal Entry", doc)
|
||||||
gain_loss_journals = frappe.db.get_all(
|
if referenced_dt and referenced_dn:
|
||||||
"Journal Entry",
|
references = [(x.reference_type, x.reference_name) for x in gain_loss_je.accounts]
|
||||||
filters={
|
if (
|
||||||
"name": ["in", [x[0] for x in journals]],
|
len(references) == 2
|
||||||
"voucher_type": "Exchange Gain Or Loss",
|
and (referenced_dt, referenced_dn) in references
|
||||||
"docstatus": 1,
|
and (parent_doc.doctype, parent_doc.name) in references
|
||||||
},
|
):
|
||||||
as_list=1,
|
# only cancel JE generated against parent_doc and referenced_dn
|
||||||
)
|
|
||||||
for doc in gain_loss_journals:
|
|
||||||
gain_loss_je = frappe.get_doc("Journal Entry", doc[0])
|
|
||||||
if referenced_dt and referenced_dn:
|
|
||||||
references = [(x.reference_type, x.reference_name) for x in gain_loss_je.accounts]
|
|
||||||
if (
|
|
||||||
len(references) == 2
|
|
||||||
and (referenced_dt, referenced_dn) in references
|
|
||||||
and (parent_doc.doctype, parent_doc.name) in references
|
|
||||||
):
|
|
||||||
# only cancel JE generated against parent_doc and referenced_dn
|
|
||||||
gain_loss_je.cancel()
|
|
||||||
else:
|
|
||||||
gain_loss_je.cancel()
|
gain_loss_je.cancel()
|
||||||
|
else:
|
||||||
|
gain_loss_je.cancel()
|
||||||
|
|
||||||
|
|
||||||
|
def delete_exchange_gain_loss_journal(
|
||||||
|
parent_doc: dict | object, referenced_dt: str | None = None, referenced_dn: str | None = None
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Delete Exchange Gain/Loss for Sales/Purchase Invoice, if they have any.
|
||||||
|
"""
|
||||||
|
if parent_doc.doctype in ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"]:
|
||||||
|
gain_loss_journals = get_linked_exchange_gain_loss_journal(
|
||||||
|
referenced_dt=parent_doc.doctype, referenced_dn=parent_doc.name, je_docstatus=2
|
||||||
|
)
|
||||||
|
for doc in gain_loss_journals:
|
||||||
|
gain_loss_je = frappe.get_doc("Journal Entry", doc)
|
||||||
|
if referenced_dt and referenced_dn:
|
||||||
|
references = [(x.reference_type, x.reference_name) for x in gain_loss_je.accounts]
|
||||||
|
if (
|
||||||
|
len(references) == 2
|
||||||
|
and (referenced_dt, referenced_dn) in references
|
||||||
|
and (parent_doc.doctype, parent_doc.name) in references
|
||||||
|
):
|
||||||
|
# only delete JE generated against parent_doc and referenced_dn
|
||||||
|
gain_loss_je.delete()
|
||||||
|
else:
|
||||||
|
gain_loss_je.delete()
|
||||||
|
|
||||||
|
|
||||||
|
def get_linked_exchange_gain_loss_journal(referenced_dt: str, referenced_dn: str, je_docstatus: int) -> list:
|
||||||
|
"""
|
||||||
|
Get all the linked exchange gain/loss journal entries for a given document.
|
||||||
|
"""
|
||||||
|
gain_loss_journals = []
|
||||||
|
if journals := frappe.db.get_all(
|
||||||
|
"Journal Entry Account",
|
||||||
|
{
|
||||||
|
"reference_type": referenced_dt,
|
||||||
|
"reference_name": referenced_dn,
|
||||||
|
"docstatus": je_docstatus,
|
||||||
|
},
|
||||||
|
pluck="parent",
|
||||||
|
):
|
||||||
|
gain_loss_journals = frappe.db.get_all(
|
||||||
|
"Journal Entry",
|
||||||
|
{
|
||||||
|
"name": ["in", journals],
|
||||||
|
"voucher_type": "Exchange Gain Or Loss",
|
||||||
|
"is_system_generated": 1,
|
||||||
|
"docstatus": je_docstatus,
|
||||||
|
},
|
||||||
|
pluck="name",
|
||||||
|
)
|
||||||
|
return gain_loss_journals
|
||||||
|
|
||||||
|
|
||||||
def cancel_common_party_journal(self):
|
def cancel_common_party_journal(self):
|
||||||
|
|||||||
@@ -346,12 +346,17 @@ class AccountsController(TransactionBase):
|
|||||||
repost_doc.save(ignore_permissions=True)
|
repost_doc.save(ignore_permissions=True)
|
||||||
|
|
||||||
def on_trash(self):
|
def on_trash(self):
|
||||||
|
from erpnext.accounts.utils import delete_exchange_gain_loss_journal
|
||||||
|
|
||||||
self._remove_references_in_repost_doctypes()
|
self._remove_references_in_repost_doctypes()
|
||||||
self._remove_references_in_unreconcile()
|
self._remove_references_in_unreconcile()
|
||||||
self.remove_serial_and_batch_bundle()
|
self.remove_serial_and_batch_bundle()
|
||||||
|
|
||||||
# delete sl and gl entries on deletion of transaction
|
# delete sl and gl entries on deletion of transaction
|
||||||
if frappe.db.get_single_value("Accounts Settings", "delete_linked_ledger_entries"):
|
if frappe.db.get_single_value("Accounts Settings", "delete_linked_ledger_entries"):
|
||||||
|
# delete linked exchange gain/loss journal
|
||||||
|
delete_exchange_gain_loss_journal(self)
|
||||||
|
|
||||||
ple = frappe.qb.DocType("Payment Ledger Entry")
|
ple = frappe.qb.DocType("Payment Ledger Entry")
|
||||||
frappe.qb.from_(ple).delete().where(
|
frappe.qb.from_(ple).delete().where(
|
||||||
(ple.voucher_type == self.doctype) & (ple.voucher_no == self.name)
|
(ple.voucher_type == self.doctype) & (ple.voucher_no == self.name)
|
||||||
|
|||||||
Reference in New Issue
Block a user