Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: better gls for purchases with tax witholding #42743

Merged
merged 5 commits into from
Nov 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 56 additions & 25 deletions erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
Original file line number Diff line number Diff line change
Expand Up @@ -865,6 +865,7 @@ def get_gl_entries(self, warehouse_account=None):

self.make_tax_gl_entries(gl_entries)
self.make_internal_transfer_gl_entries(gl_entries)
self.make_gl_entries_for_tax_withholding(gl_entries)

gl_entries = make_regional_gl_entries(gl_entries, self)

Expand Down Expand Up @@ -898,32 +899,37 @@ def make_supplier_gl_entry(self, gl_entries):
)

if grand_total and not self.is_internal_transfer():
against_voucher = self.name
if self.is_return and self.return_against and not self.update_outstanding_for_self:
against_voucher = self.return_against
self.add_supplier_gl_entry(gl_entries, base_grand_total, grand_total)

def add_supplier_gl_entry(
self, gl_entries, base_grand_total, grand_total, against_account=None, remarks=None, skip_merge=False
):
against_voucher = self.name
if self.is_return and self.return_against and not self.update_outstanding_for_self:
against_voucher = self.return_against

# Did not use base_grand_total to book rounding loss gle
gl = {
"account": self.credit_to,
"party_type": "Supplier",
"party": self.supplier,
"due_date": self.due_date,
"against": against_account or self.against_expense_account,
"credit": base_grand_total,
"credit_in_account_currency": base_grand_total
if self.party_account_currency == self.company_currency
else grand_total,
"against_voucher": against_voucher,
"against_voucher_type": self.doctype,
"project": self.project,
"cost_center": self.cost_center,
"_skip_merge": skip_merge,
}

# Did not use base_grand_total to book rounding loss gle
gl_entries.append(
self.get_gl_dict(
{
"account": self.credit_to,
"party_type": "Supplier",
"party": self.supplier,
"due_date": self.due_date,
"against": self.against_expense_account,
"credit": base_grand_total,
"credit_in_account_currency": base_grand_total
if self.party_account_currency == self.company_currency
else grand_total,
"against_voucher": against_voucher,
"against_voucher_type": self.doctype,
"project": self.project,
"cost_center": self.cost_center,
},
self.party_account_currency,
item=self,
)
)
if remarks:
gl["remarks"] = remarks

gl_entries.append(self.get_gl_dict(gl, self.party_account_currency, item=self))

def make_item_gl_entries(self, gl_entries):
# item gl entries
Expand Down Expand Up @@ -1419,6 +1425,31 @@ def make_internal_transfer_gl_entries(self, gl_entries):
)
)

def make_gl_entries_for_tax_withholding(self, gl_entries):
"""
Tax withholding amount is not part of supplier invoice.
Separate supplier GL Entry for correct reporting.
"""
if not self.apply_tds:
return

for row in self.get("taxes"):
if not row.is_tax_withholding_account or not row.tax_amount:
continue

base_tds_amount = row.base_tax_amount_after_discount_amount
tds_amount = row.tax_amount_after_discount_amount

self.add_supplier_gl_entry(gl_entries, base_tds_amount, tds_amount)
self.add_supplier_gl_entry(
gl_entries,
-base_tds_amount,
-tds_amount,
against_account=row.account_head,
remarks=_("TDS Deducted"),
skip_merge=True,
)

def make_payment_gl_entries(self, gl_entries):
# Make Cash GL Entries
if cint(self.is_paid) and self.cash_bank_account and self.paid_amount:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1551,6 +1551,61 @@ def test_purchase_invoice_advance_taxes(self):
payment_entry.load_from_db()
self.assertEqual(payment_entry.taxes[0].allocated_amount, 0)

def test_purchase_gl_with_tax_withholding_tax(self):
company = "_Test Company"

tds_account_args = {
"doctype": "Account",
"account_name": "TDS Payable",
"account_type": "Tax",
"parent_account": frappe.db.get_value(
"Account", {"account_name": "Duties and Taxes", "company": company}
),
"company": company,
}

tds_account = create_account(**tds_account_args)
tax_withholding_category = "Test TDS - 194 - Dividends - Individual"

# Update tax withholding category with current fiscal year and rate details
create_tax_witholding_category(tax_withholding_category, company, tds_account)

# create a new supplier to test
supplier = create_supplier(
supplier_name="_Test TDS Advance Supplier",
tax_withholding_category=tax_withholding_category,
)

pi = make_purchase_invoice(
supplier=supplier.name,
rate=3000,
qty=1,
item="_Test Non Stock Item",
do_not_submit=1,
)
pi.apply_tds = 1
pi.tax_withholding_category = tax_withholding_category
pi.save()
pi.submit()

self.assertEqual(pi.taxes[0].tax_amount, 300)
self.assertEqual(pi.taxes[0].account_head, tds_account)

gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_no": pi.name, "voucher_type": "Purchase Invoice", "account": "Creditors - _TC"},
fields=["account", "against", "debit", "credit"],
)

for gle in gl_entries:
if gle.debit:
# GL Entry with TDS Amount
self.assertEqual(gle.against, tds_account)
self.assertEqual(gle.debit, 300)
else:
# GL Entry with Purchase Invoice Amount
self.assertEqual(gle.credit, 3000)

def test_provisional_accounting_entry(self):
setup_provisional_accounting()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,17 @@ def test_single_threshold_tds(self):
self.assertEqual(pi.grand_total, 18000)

# check gl entry for the purchase invoice
gl_entries = frappe.db.get_all("GL Entry", filters={"voucher_no": pi.name}, fields=["*"])
gl_entries = frappe.db.get_all(
"GL Entry",
filters={"voucher_no": pi.name},
fields=["account", "sum(debit) as debit", "sum(credit) as credit"],
group_by="account",
)
self.assertEqual(len(gl_entries), 3)
for d in gl_entries:
if d.account == pi.credit_to:
self.assertEqual(d.credit, 18000)
self.assertEqual(d.credit, 20000)
self.assertEqual(d.debit, 2000)
elif d.account == pi.items[0].get("expense_account"):
self.assertEqual(d.debit, 20000)
elif d.account == pi.taxes[0].get("account_head"):
Expand Down
4 changes: 4 additions & 0 deletions erpnext/accounts/general_ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,10 @@ def merge_similar_entries(gl_map, precision=None):
merge_properties = get_merge_properties(accounting_dimensions)

for entry in gl_map:
if entry._skip_merge:
merged_gl_map.append(entry)
continue

entry.merge_key = get_merge_key(entry, merge_properties)
# if there is already an entry in this account then just add it
# to that entry
Expand Down
Loading