mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-06 21:59:13 +00:00
Merge pull request #38205 from vorasmit/partial-cancel
fix: partial cancel of gle and ple (backport #35609)
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# See license.txt
|
# See license.txt
|
||||||
|
|
||||||
|
import json
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
@@ -21,6 +22,7 @@ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import (
|
|||||||
create_sales_invoice,
|
create_sales_invoice,
|
||||||
create_sales_invoice_against_cost_center,
|
create_sales_invoice_against_cost_center,
|
||||||
)
|
)
|
||||||
|
from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries
|
||||||
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||||
from erpnext.setup.doctype.employee.test_employee import make_employee
|
from erpnext.setup.doctype.employee.test_employee import make_employee
|
||||||
|
|
||||||
@@ -1219,6 +1221,76 @@ class TestPaymentEntry(FrappeTestCase):
|
|||||||
so.reload()
|
so.reload()
|
||||||
self.assertEqual(so.advance_paid, so.rounded_total)
|
self.assertEqual(so.advance_paid, so.rounded_total)
|
||||||
|
|
||||||
|
def test_partial_cancel_for_payment_entry(self):
|
||||||
|
si = create_sales_invoice()
|
||||||
|
|
||||||
|
pe = get_payment_entry(si.doctype, si.name)
|
||||||
|
pe.save()
|
||||||
|
pe.submit()
|
||||||
|
|
||||||
|
# Additional GL Entry
|
||||||
|
tax_amount = 10
|
||||||
|
reference_row = pe.references[0]
|
||||||
|
gl_args = {
|
||||||
|
"party_type": pe.party_type,
|
||||||
|
"party": pe.party,
|
||||||
|
"against_voucher_type": reference_row.reference_doctype,
|
||||||
|
"against_voucher": reference_row.reference_name,
|
||||||
|
"voucher_detail_no": reference_row.name,
|
||||||
|
}
|
||||||
|
|
||||||
|
gl_dicts = []
|
||||||
|
|
||||||
|
gl_dicts.extend(
|
||||||
|
[
|
||||||
|
pe.get_gl_dict(
|
||||||
|
{
|
||||||
|
"account": pe.paid_to,
|
||||||
|
"credit": tax_amount,
|
||||||
|
"credit_in_account_currency": tax_amount,
|
||||||
|
**gl_args,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
pe.get_gl_dict(
|
||||||
|
{
|
||||||
|
"account": pe.paid_from,
|
||||||
|
"debit": tax_amount,
|
||||||
|
"debit_in_account_currency": tax_amount,
|
||||||
|
**gl_args,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
make_gl_entries(gl_dicts)
|
||||||
|
|
||||||
|
# Assert PLEs Before
|
||||||
|
self.assertPLEntries(
|
||||||
|
pe,
|
||||||
|
[
|
||||||
|
{"amount": -100.0, "against_voucher_no": si.name},
|
||||||
|
{"amount": 10.0, "against_voucher_no": si.name},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Partially cancel Payment Entry
|
||||||
|
make_reverse_gl_entries(gl_dicts, partial_cancel=True)
|
||||||
|
self.assertPLEntries(pe, [{"amount": -100.0, "against_voucher_no": si.name}])
|
||||||
|
|
||||||
|
def assertPLEntries(self, payment_doc, expected_pl_entries):
|
||||||
|
pl_entries = frappe.get_all(
|
||||||
|
"Payment Ledger Entry",
|
||||||
|
filters={
|
||||||
|
"voucher_type": payment_doc.doctype,
|
||||||
|
"voucher_no": payment_doc.name,
|
||||||
|
"delinked": 0,
|
||||||
|
},
|
||||||
|
fields=["amount", "against_voucher_no"],
|
||||||
|
)
|
||||||
|
out_str = json.dumps(sorted(pl_entries, key=json.dumps))
|
||||||
|
expected_out_str = json.dumps(sorted(expected_pl_entries, key=json.dumps))
|
||||||
|
self.assertEqual(out_str, expected_out_str)
|
||||||
|
|
||||||
|
|
||||||
def create_payment_entry(**args):
|
def create_payment_entry(**args):
|
||||||
payment_entry = frappe.new_doc("Payment Entry")
|
payment_entry = frappe.new_doc("Payment Entry")
|
||||||
|
|||||||
@@ -556,7 +556,12 @@ def get_round_off_account_and_cost_center(
|
|||||||
|
|
||||||
|
|
||||||
def make_reverse_gl_entries(
|
def make_reverse_gl_entries(
|
||||||
gl_entries=None, voucher_type=None, voucher_no=None, adv_adj=False, update_outstanding="Yes"
|
gl_entries=None,
|
||||||
|
voucher_type=None,
|
||||||
|
voucher_no=None,
|
||||||
|
adv_adj=False,
|
||||||
|
update_outstanding="Yes",
|
||||||
|
partial_cancel=False,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Get original gl entries of the voucher
|
Get original gl entries of the voucher
|
||||||
@@ -576,13 +581,18 @@ def make_reverse_gl_entries(
|
|||||||
|
|
||||||
if gl_entries:
|
if gl_entries:
|
||||||
create_payment_ledger_entry(
|
create_payment_ledger_entry(
|
||||||
gl_entries, cancel=1, adv_adj=adv_adj, update_outstanding=update_outstanding
|
gl_entries,
|
||||||
|
cancel=1,
|
||||||
|
adv_adj=adv_adj,
|
||||||
|
update_outstanding=update_outstanding,
|
||||||
|
partial_cancel=partial_cancel,
|
||||||
)
|
)
|
||||||
validate_accounting_period(gl_entries)
|
validate_accounting_period(gl_entries)
|
||||||
check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
|
check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
|
||||||
|
|
||||||
is_opening = any(d.get("is_opening") == "Yes" for d in gl_entries)
|
is_opening = any(d.get("is_opening") == "Yes" for d in gl_entries)
|
||||||
validate_against_pcv(is_opening, gl_entries[0]["posting_date"], gl_entries[0]["company"])
|
validate_against_pcv(is_opening, gl_entries[0]["posting_date"], gl_entries[0]["company"])
|
||||||
|
if not partial_cancel:
|
||||||
set_as_cancel(gl_entries[0]["voucher_type"], gl_entries[0]["voucher_no"])
|
set_as_cancel(gl_entries[0]["voucher_type"], gl_entries[0]["voucher_no"])
|
||||||
|
|
||||||
for entry in gl_entries:
|
for entry in gl_entries:
|
||||||
|
|||||||
@@ -1617,6 +1617,7 @@ def get_payment_ledger_entries(gl_entries, cancel=0):
|
|||||||
due_date=gle.due_date,
|
due_date=gle.due_date,
|
||||||
voucher_type=gle.voucher_type,
|
voucher_type=gle.voucher_type,
|
||||||
voucher_no=gle.voucher_no,
|
voucher_no=gle.voucher_no,
|
||||||
|
voucher_detail_no=gle.voucher_detail_no,
|
||||||
against_voucher_type=gle.against_voucher_type
|
against_voucher_type=gle.against_voucher_type
|
||||||
if gle.against_voucher_type
|
if gle.against_voucher_type
|
||||||
else gle.voucher_type,
|
else gle.voucher_type,
|
||||||
@@ -1638,7 +1639,7 @@ def get_payment_ledger_entries(gl_entries, cancel=0):
|
|||||||
|
|
||||||
|
|
||||||
def create_payment_ledger_entry(
|
def create_payment_ledger_entry(
|
||||||
gl_entries, cancel=0, adv_adj=0, update_outstanding="Yes", from_repost=0
|
gl_entries, cancel=0, adv_adj=0, update_outstanding="Yes", from_repost=0, partial_cancel=False
|
||||||
):
|
):
|
||||||
if gl_entries:
|
if gl_entries:
|
||||||
ple_map = get_payment_ledger_entries(gl_entries, cancel=cancel)
|
ple_map = get_payment_ledger_entries(gl_entries, cancel=cancel)
|
||||||
@@ -1648,7 +1649,7 @@ def create_payment_ledger_entry(
|
|||||||
ple = frappe.get_doc(entry)
|
ple = frappe.get_doc(entry)
|
||||||
|
|
||||||
if cancel:
|
if cancel:
|
||||||
delink_original_entry(ple)
|
delink_original_entry(ple, partial_cancel=partial_cancel)
|
||||||
|
|
||||||
ple.flags.ignore_permissions = 1
|
ple.flags.ignore_permissions = 1
|
||||||
ple.flags.adv_adj = adv_adj
|
ple.flags.adv_adj = adv_adj
|
||||||
@@ -1695,7 +1696,7 @@ def update_voucher_outstanding(voucher_type, voucher_no, account, party_type, pa
|
|||||||
ref_doc.set_status(update=True)
|
ref_doc.set_status(update=True)
|
||||||
|
|
||||||
|
|
||||||
def delink_original_entry(pl_entry):
|
def delink_original_entry(pl_entry, partial_cancel=False):
|
||||||
if pl_entry:
|
if pl_entry:
|
||||||
ple = qb.DocType("Payment Ledger Entry")
|
ple = qb.DocType("Payment Ledger Entry")
|
||||||
query = (
|
query = (
|
||||||
@@ -1715,6 +1716,10 @@ def delink_original_entry(pl_entry):
|
|||||||
& (ple.against_voucher_no == pl_entry.against_voucher_no)
|
& (ple.against_voucher_no == pl_entry.against_voucher_no)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if partial_cancel:
|
||||||
|
query = query.where(ple.voucher_detail_no == pl_entry.voucher_detail_no)
|
||||||
|
|
||||||
query.run()
|
query.run()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user