Merge pull request #48440 from frappe/mergify/bp/version-15-hotfix/pr-48324

fix: update payment request outstanding on unreconciliation (backport #48324)
This commit is contained in:
ruthra kumar
2025-07-08 10:33:36 +05:30
committed by GitHub
3 changed files with 116 additions and 67 deletions

View File

@@ -829,8 +829,7 @@ def update_payment_requests_as_per_pe_references(references=None, cancel=False):
if not references: if not references:
return return
precision = references[0].precision("allocated_amount") precision = frappe.get_precision("Payment Entry Reference", "allocated_amount")
referenced_payment_requests = frappe.get_all( referenced_payment_requests = frappe.get_all(
"Payment Request", "Payment Request",
filters={"name": ["in", {row.payment_request for row in references if row.payment_request}]}, filters={"name": ["in", {row.payment_request for row in references if row.payment_request}]},

View File

@@ -630,7 +630,6 @@ class TestPaymentRequest(FrappeTestCase):
pr = make_payment_request(dt="Sales Invoice", dn=si.name, mute_email=1) pr = make_payment_request(dt="Sales Invoice", dn=si.name, mute_email=1)
self.assertEqual(pr.grand_total, si.outstanding_amount) self.assertEqual(pr.grand_total, si.outstanding_amount)
def test_partial_paid_invoice_with_submitted_payment_entry(self): def test_partial_paid_invoice_with_submitted_payment_entry(self):
pi = make_purchase_invoice(currency="INR", qty=1, rate=5000) pi = make_purchase_invoice(currency="INR", qty=1, rate=5000)
pi.save() pi.save()
@@ -656,3 +655,33 @@ def test_partial_paid_invoice_with_submitted_payment_entry(self):
pi.load_from_db() pi.load_from_db()
pr = make_payment_request(dt="Purchase Invoice", dn=pi.name, mute_email=1) pr = make_payment_request(dt="Purchase Invoice", dn=pi.name, mute_email=1)
self.assertEqual(pr.grand_total, pi.outstanding_amount) self.assertEqual(pr.grand_total, pi.outstanding_amount)
def test_payment_request_on_unreconcile(self):
pi = make_purchase_invoice(currency="INR", qty=1, rate=500)
pi.submit()
pr = make_payment_request(
dt=pi.doctype,
dn=pi.name,
mute_email=1,
submit_doc=True,
return_doc=True,
)
self.assertEqual(pr.grand_total, pi.outstanding_amount)
pe = pr.create_payment_entry()
unreconcile = frappe.get_doc(
{
"doctype": "Unreconcile Payment",
"company": pe.company,
"voucher_type": pe.doctype,
"voucher_no": pe.name,
}
)
unreconcile.add_references()
unreconcile.submit()
pi.load_from_db()
pr.load_from_db()
self.assertEqual(pr.grand_total, pi.outstanding_amount)

View File

@@ -997,41 +997,58 @@ def remove_ref_doc_link_from_pe(
per = qb.DocType("Payment Entry Reference") per = qb.DocType("Payment Entry Reference")
pay = qb.DocType("Payment Entry") pay = qb.DocType("Payment Entry")
linked_pe = ( query = (
qb.from_(per) qb.from_(per)
.select(per.parent) .select("*")
.where((per.reference_doctype == ref_type) & (per.reference_name == ref_no) & (per.docstatus.lt(2))) .where(
.run(as_list=1) (per.reference_doctype == ref_type)
& (per.reference_name == ref_no)
& (per.docstatus.lt(2))
& (per.parenttype == "Payment Entry")
)
) )
linked_pe = convert_to_list(linked_pe)
# remove reference only from specified payment
linked_pe = [x for x in linked_pe if x == payment_name] if payment_name else linked_pe
if linked_pe: # update reference only from specified payment
update_query = ( if payment_name:
query = query.where(per.parent == payment_name)
reference_rows = query.run(as_dict=True)
if not reference_rows:
return
linked_pe = set()
row_names = set()
for row in reference_rows:
linked_pe.add(row.parent)
row_names.add(row.name)
from erpnext.accounts.doctype.payment_request.payment_request import (
update_payment_requests_as_per_pe_references,
)
# Update payment request amount
update_payment_requests_as_per_pe_references(reference_rows, cancel=True)
# Update allocated amounts and modified fields in one go
(
qb.update(per) qb.update(per)
.set(per.allocated_amount, 0) .set(per.allocated_amount, 0)
.set(per.modified, now()) .set(per.modified, now())
.set(per.modified_by, frappe.session.user) .set(per.modified_by, frappe.session.user)
.where(per.docstatus.lt(2) & (per.reference_doctype == ref_type) & (per.reference_name == ref_no)) .where(per.name.isin(row_names))
.where(per.parenttype == "Payment Entry")
.run()
) )
if payment_name:
update_query = update_query.where(per.parent == payment_name)
update_query.run()
for pe in linked_pe: for pe in linked_pe:
try: try:
pe_doc = frappe.get_doc("Payment Entry", pe) pe_doc = frappe.get_doc("Payment Entry", pe)
pe_doc.set_amounts() pe_doc.set_amounts()
# Call cancel on only removed reference # Call cancel on only removed reference
references = [ references = [x for x in pe_doc.references if x.name in row_names]
x
for x in pe_doc.references
if x.reference_doctype == ref_type and x.reference_name == ref_no
]
[pe_doc.make_advance_gl_entries(x, cancel=1) for x in references] [pe_doc.make_advance_gl_entries(x, cancel=1) for x in references]
pe_doc.clear_unallocated_reference_document_rows() pe_doc.clear_unallocated_reference_document_rows()
@@ -1042,12 +1059,16 @@ def remove_ref_doc_link_from_pe(
msg += _("Please cancel payment entry manually first") msg += _("Please cancel payment entry manually first")
frappe.throw(msg, exc=PaymentEntryUnlinkError, title=_("Payment Unlink Error")) frappe.throw(msg, exc=PaymentEntryUnlinkError, title=_("Payment Unlink Error"))
qb.update(pay).set(pay.total_allocated_amount, pe_doc.total_allocated_amount).set( (
pay.base_total_allocated_amount, pe_doc.base_total_allocated_amount qb.update(pay)
).set(pay.unallocated_amount, pe_doc.unallocated_amount).set(pay.modified, now()).set( .set(pay.total_allocated_amount, pe_doc.total_allocated_amount)
pay.modified_by, frappe.session.user .set(pay.base_total_allocated_amount, pe_doc.base_total_allocated_amount)
).where(pay.name == pe).run() .set(pay.unallocated_amount, pe_doc.unallocated_amount)
.set(pay.modified, now())
.set(pay.modified_by, frappe.session.user)
.where(pay.name == pe)
.run()
)
frappe.msgprint(_("Payment Entries {0} are un-linked").format("\n".join(linked_pe))) frappe.msgprint(_("Payment Entries {0} are un-linked").format("\n".join(linked_pe)))