From 6d9d730759af258eadf93bc105a0bd159f2b7ba2 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 14 Dec 2022 16:05:15 +0530 Subject: [PATCH 1/3] fix: cost_center filter gives incorrect output filtering on cost center gives invoices that are reconciled as having outstanding --- .../doctype/payment_reconciliation/payment_reconciliation.py | 4 +++- erpnext/accounts/utils.py | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 52efd33fef..ff212f2a35 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -23,6 +23,7 @@ class PaymentReconciliation(Document): def __init__(self, *args, **kwargs): super(PaymentReconciliation, self).__init__(*args, **kwargs) self.common_filter_conditions = [] + self.accounting_dimension_filter_conditions = [] self.ple_posting_date_filter = [] @frappe.whitelist() @@ -193,6 +194,7 @@ class PaymentReconciliation(Document): posting_date=self.ple_posting_date_filter, min_outstanding=self.minimum_invoice_amount if self.minimum_invoice_amount else None, max_outstanding=self.maximum_invoice_amount if self.maximum_invoice_amount else None, + accounting_dimensions=self.accounting_dimension_filter_conditions, ) if self.invoice_limit: @@ -381,7 +383,7 @@ class PaymentReconciliation(Document): self.common_filter_conditions.append(ple.company == self.company) if self.get("cost_center") and (get_invoices or get_return_invoices): - self.common_filter_conditions.append(ple.cost_center == self.cost_center) + self.accounting_dimension_filter_conditions.append(ple.cost_center == self.cost_center) if get_invoices: if self.from_invoice_date: diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 41702d65b4..1e573b01ba 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -836,6 +836,7 @@ def get_outstanding_invoices( posting_date=None, min_outstanding=None, max_outstanding=None, + accounting_dimensions=None, ): ple = qb.DocType("Payment Ledger Entry") @@ -866,6 +867,7 @@ def get_outstanding_invoices( min_outstanding=min_outstanding, max_outstanding=max_outstanding, get_invoices=True, + accounting_dimensions=accounting_dimensions or [], ) for d in invoice_list: @@ -1615,6 +1617,7 @@ class QueryPaymentLedger(object): .where(ple.delinked == 0) .where(Criterion.all(filter_on_voucher_no)) .where(Criterion.all(self.common_filter)) + .where(Criterion.all(self.dimensions_filter)) .where(Criterion.all(self.voucher_posting_date)) .groupby(ple.voucher_type, ple.voucher_no, ple.party_type, ple.party) ) @@ -1702,6 +1705,7 @@ class QueryPaymentLedger(object): max_outstanding=None, get_payments=False, get_invoices=False, + accounting_dimensions=None, ): """ Fetch voucher amount and outstanding amount from Payment Ledger using Database CTE @@ -1717,6 +1721,7 @@ class QueryPaymentLedger(object): self.reset() self.vouchers = vouchers self.common_filter = common_filter or [] + self.dimensions_filter = accounting_dimensions or [] self.voucher_posting_date = posting_date or [] self.min_outstanding = min_outstanding self.max_outstanding = max_outstanding From 8eb93004f7f5947ab535cb42c377f2d795dac255 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 14 Dec 2022 16:16:22 +0530 Subject: [PATCH 2/3] fix: cost_center filter fix for 'Get Outstanding Invoice' in PE --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 9354e44d24..e7e7ce5c9d 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1188,6 +1188,7 @@ def get_outstanding_reference_documents(args): ple = qb.DocType("Payment Ledger Entry") common_filter = [] + accounting_dimensions_filter = [] posting_and_due_date = [] # confirm that Supplier is not blocked @@ -1217,7 +1218,7 @@ def get_outstanding_reference_documents(args): # Add cost center condition if args.get("cost_center"): condition += " and cost_center='%s'" % args.get("cost_center") - common_filter.append(ple.cost_center == args.get("cost_center")) + accounting_dimensions_filter.append(ple.cost_center == args.get("cost_center")) date_fields_dict = { "posting_date": ["from_posting_date", "to_posting_date"], @@ -1243,6 +1244,7 @@ def get_outstanding_reference_documents(args): posting_date=posting_and_due_date, min_outstanding=args.get("outstanding_amt_greater_than"), max_outstanding=args.get("outstanding_amt_less_than"), + accounting_dimensions=accounting_dimensions_filter, ) outstanding_invoices = split_invoices_based_on_payment_terms(outstanding_invoices) From a998a8a2dad20e2671de5dbf70816c31c09e4c0e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 15 Dec 2022 12:57:53 +0530 Subject: [PATCH 3/3] test: cost center should not affect outstanding calculation --- .../test_payment_reconciliation.py | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index dae029b408..6030134fff 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -8,6 +8,8 @@ from frappe import qb from frappe.tests.utils import FrappeTestCase from frappe.utils import add_days, nowdate +from erpnext import get_default_cost_center +from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.party import get_party_account @@ -20,6 +22,7 @@ class TestPaymentReconciliation(FrappeTestCase): self.create_item() self.create_customer() self.create_account() + self.create_cost_center() self.clear_old_entries() def tearDown(self): @@ -216,6 +219,22 @@ class TestPaymentReconciliation(FrappeTestCase): ) return je + def create_cost_center(self): + # Setup cost center + cc_name = "Sub" + + self.main_cc = frappe.get_doc("Cost Center", get_default_cost_center(self.company)) + + cc_exists = frappe.db.get_list("Cost Center", filters={"cost_center_name": cc_name}) + if cc_exists: + self.sub_cc = frappe.get_doc("Cost Center", cc_exists[0].name) + else: + sub_cc = frappe.new_doc("Cost Center") + sub_cc.cost_center_name = "Sub" + sub_cc.parent_cost_center = self.main_cc.parent_cost_center + sub_cc.company = self.main_cc.company + self.sub_cc = sub_cc.save() + def test_filter_min_max(self): # check filter condition minimum and maximum amount self.create_sales_invoice(qty=1, rate=300) @@ -578,3 +597,24 @@ class TestPaymentReconciliation(FrappeTestCase): self.assertEqual(len(pr.payments), 1) self.assertEqual(pr.payments[0].amount, amount) self.assertEqual(pr.payments[0].currency, "EUR") + + def test_differing_cost_center_on_invoice_and_payment(self): + """ + Cost Center filter should not affect outstanding amount calculation + """ + + si = self.create_sales_invoice(qty=1, rate=100, do_not_submit=True) + si.cost_center = self.main_cc.name + si.submit() + pr = get_payment_entry(si.doctype, si.name) + pr.cost_center = self.sub_cc.name + pr = pr.save().submit() + + pr = self.create_payment_reconciliation() + pr.cost_center = self.main_cc.name + + pr.get_unreconciled_entries() + + # check PR tool output + self.assertEqual(len(pr.get("invoices")), 0) + self.assertEqual(len(pr.get("payments")), 0)