diff --git a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py index ed4a223311b..70813aaeb1d 100644 --- a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py +++ b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py @@ -68,8 +68,10 @@ def get_tax_withholding_data(filters): } data.append(row) - # Sort by section code and transaction date - data.sort(key=lambda x: (x["section_code"] or "", x["transaction_date"] or "")) + # Sort by section code, transaction date, then withholding_name for deterministic ordering + data.sort( + key=lambda x: (x["section_code"] or "", x["transaction_date"] or "", x["withholding_name"] or "") + ) return data diff --git a/erpnext/accounts/report/tax_withholding_details/test_tax_withholding_details.py b/erpnext/accounts/report/tax_withholding_details/test_tax_withholding_details.py index 49e50b7ff32..91683867188 100644 --- a/erpnext/accounts/report/tax_withholding_details/test_tax_withholding_details.py +++ b/erpnext/accounts/report/tax_withholding_details/test_tax_withholding_details.py @@ -5,10 +5,12 @@ import frappe from frappe.utils import add_to_date, today from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry -from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice -from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.doctype.tax_withholding_category.test_tax_withholding_category import ( + create_purchase_invoice, + create_records, + create_sales_invoice, create_tax_withholding_category, + make_journal_entry_with_tax_withholding, ) from erpnext.accounts.report.tax_withholding_details.tax_withholding_details import execute from erpnext.accounts.test.accounts_mixin import AccountsTestMixin @@ -20,43 +22,45 @@ class TestTaxWithholdingDetails(ERPNextTestSuite, AccountsTestMixin): def setUp(self): self.create_company() self.clear_old_entries() - create_tax_accounts() + create_records() def test_tax_withholding_for_customers(self): create_tax_category(cumulative_threshold=300) - frappe.db.set_value("Customer", "_Test Customer", "tax_withholding_category", "TCS") - si = create_sales_invoice(rate=1000) - pe = create_tcs_payment_entry() + frappe.db.set_value("Customer", "Test TCS Customer", "tax_withholding_category", "TCS") + si = create_sales_invoice(customer="Test TCS Customer", rate=1000) + si.submit() + + create_tcs_payment_entry() jv = create_tcs_journal_entry() filters = frappe._dict( company="_Test Company", party_type="Customer", from_date=today(), to_date=today() ) result = execute(filters)[1] + expected_values = [ - # Check for JV totals using back calculation logic - [jv.name, "TCS", 0.075, -10000.0, -7.5, -10000.0], - [pe.name, "TCS", 0.075, 2550, 0.53, 2550.53], - [si.name, "TCS", 0.075, 1000, 0.52, 1000.52], + [jv.name, "TCS", 0.075, 1000.75, 0.75, 1000.75], + ["", "TCS", 0.075, 0, 0.75, 0], + [si.name, "TCS", 0.075, 1000.0, 0.75, 1000.75], ] self.check_expected_values(result, expected_values) def test_single_account_for_multiple_categories(self): - create_tax_category("TDS - 1", rate=10, account="TDS - _TC") - inv_1 = make_purchase_invoice(rate=1000, do_not_submit=True) - inv_1.tax_withholding_category = "TDS - 1" + create_tax_category("TDS - 1", rate=10, account="TDS - _TC", cumulative_threshold=1) + frappe.db.set_value("Supplier", "Test TDS Supplier", "tax_withholding_category", "TDS - 1") + inv_1 = create_purchase_invoice(supplier="Test TDS Supplier", rate=5000) inv_1.submit() - create_tax_category("TDS - 2", rate=20, account="TDS - _TC") - inv_2 = make_purchase_invoice(rate=1000, do_not_submit=True) - inv_2.tax_withholding_category = "TDS - 2" + create_tax_category("TDS - 2", rate=20, account="TDS - _TC", cumulative_threshold=1) + frappe.db.set_value("Supplier", "Test TDS Supplier", "tax_withholding_category", "TDS - 2") + inv_2 = create_purchase_invoice(supplier="Test TDS Supplier", rate=5000) inv_2.submit() result = execute( frappe._dict(company="_Test Company", party_type="Supplier", from_date=today(), to_date=today()) )[1] expected_values = [ - [inv_1.name, "TDS - 1", 10, 5000, 500, 5500], - [inv_2.name, "TDS - 2", 20, 5000, 1000, 6000], + [inv_1.name, "TDS - 1", 10, 5000, 500, 4500], + [inv_2.name, "TDS - 2", 20, 5000, 1000, 4000], ] self.check_expected_values(result, expected_values) @@ -81,20 +85,21 @@ class TestTaxWithholdingDetails(ERPNextTestSuite, AccountsTestMixin): tds_doc.save() - inv_1 = make_purchase_invoice( - rate=1000, posting_date=add_to_date(fiscal_year[1], days=1), do_not_save=True, do_not_submit=True + frappe.db.set_value("Supplier", "Test TDS Supplier", "tax_withholding_category", tds_doc.name) + inv_1 = create_purchase_invoice( + supplier="Test TDS Supplier", + rate=5000, + posting_date=add_to_date(fiscal_year[1], days=1), + set_posting_time=True, ) - inv_1.set_posting_time = 1 - inv_1.apply_tds = 1 - inv_1.tax_withholding_category = tds_doc.name - inv_1.save() inv_1.submit() - inv_2 = make_purchase_invoice(rate=1000, posting_date=from_date, do_not_save=True, do_not_submit=True) - inv_2.set_posting_time = 1 - inv_2.apply_tds = 1 - inv_2.tax_withholding_category = tds_doc.name - inv_2.save() + inv_2 = create_purchase_invoice( + supplier="Test TDS Supplier", + rate=5000, + posting_date=from_date, + set_posting_time=True, + ) inv_2.submit() result = execute( @@ -113,6 +118,7 @@ class TestTaxWithholdingDetails(ERPNextTestSuite, AccountsTestMixin): self.check_expected_values(result, expected_values) def check_expected_values(self, result, expected_values): + self.assertEqual(len(result), len(expected_values)) for i in range(len(result)): voucher = frappe._dict(result[i]) voucher_expected_values = expected_values[i] @@ -127,21 +133,6 @@ class TestTaxWithholdingDetails(ERPNextTestSuite, AccountsTestMixin): self.assertSequenceEqual(voucher_actual_values, voucher_expected_values) -def create_tax_accounts(): - account_names = ["TCS", "TDS"] - for account in account_names: - frappe.get_doc( - { - "doctype": "Account", - "company": "_Test Company", - "account_name": account, - "parent_account": "Duties and Taxes - _TC", - "report_type": "Balance Sheet", - "root_type": "Liability", - } - ).insert(ignore_if_duplicate=True) - - def create_tax_category(category="TCS", rate=0.075, account="TCS - _TC", cumulative_threshold=0): fiscal_year = get_fiscal_year(today(), company="_Test Company") from_date = fiscal_year[1] @@ -157,55 +148,34 @@ def create_tax_category(category="TCS", rate=0.075, account="TCS - _TC", cumulat ) -def create_tcs_payment_entry(): +def create_tcs_payment_entry(party="Test TCS Customer", category="TCS", amount=1000): + """Create a TCS Payment Entry that generates a Tax Withholding Entry (Over Withheld).""" payment_entry = create_payment_entry( payment_type="Receive", party_type="Customer", - party="_Test Customer", + party=party, paid_from="Debtors - _TC", paid_to="Cash - _TC", - paid_amount=2550, - ) - - payment_entry.append( - "taxes", - { - "account_head": "TCS - _TC", - "charge_type": "Actual", - "tax_amount": 0.53, - "add_deduct_tax": "Add", - "description": "Test", - "cost_center": "Main - _TC", - }, + paid_amount=amount, ) + payment_entry.apply_tds = 1 + payment_entry.tax_withholding_category = category + payment_entry.save() payment_entry.submit() return payment_entry -def create_tcs_journal_entry(): - jv = frappe.new_doc("Journal Entry") - jv.posting_date = today() - jv.company = "_Test Company" - jv.set( - "accounts", - [ - { - "account": "Debtors - _TC", - "party_type": "Customer", - "party": "_Test Customer", - "credit_in_account_currency": 10000, - }, - { - "account": "Debtors - _TC", - "party_type": "Customer", - "party": "_Test Customer", - "debit_in_account_currency": 9992.5, - }, - { - "account": "TCS - _TC", - "debit_in_account_currency": 7.5, - }, - ], +def create_tcs_journal_entry(party="Test TCS Customer", category="TCS", amount=1000): + """Create a TCS Credit Note Journal Entry that generates a Tax Withholding Entry.""" + jv = make_journal_entry_with_tax_withholding( + party_type="Customer", + party=party, + voucher_type="Credit Note", + amount=amount, + save=False, ) - jv.insert() - return jv.submit() + jv.apply_tds = 1 + jv.tax_withholding_category = category + jv.save() + jv.submit() + return jv