fix: better gls for purchases with tax witholding (#42743)
* fix: better gls for purchases with tax witholding
* test: test case for purchase invoice gl entries with tax witholding
* fix: use flag `_skip_merge` instead of skipping merge based on against account
* test: fix test `test_single_threshold_tds` for newer implementation
(cherry picked from commit e3cd6539c3)
This commit is contained in:
@@ -863,6 +863,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
|
|
||||||
self.make_tax_gl_entries(gl_entries)
|
self.make_tax_gl_entries(gl_entries)
|
||||||
self.make_internal_transfer_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)
|
gl_entries = make_regional_gl_entries(gl_entries, self)
|
||||||
|
|
||||||
@@ -896,32 +897,37 @@ class PurchaseInvoice(BuyingController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if grand_total and not self.is_internal_transfer():
|
if grand_total and not self.is_internal_transfer():
|
||||||
against_voucher = self.name
|
self.add_supplier_gl_entry(gl_entries, base_grand_total, grand_total)
|
||||||
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
|
def add_supplier_gl_entry(
|
||||||
gl_entries.append(
|
self, gl_entries, base_grand_total, grand_total, against_account=None, remarks=None, skip_merge=False
|
||||||
self.get_gl_dict(
|
):
|
||||||
{
|
against_voucher = self.name
|
||||||
"account": self.credit_to,
|
if self.is_return and self.return_against and not self.update_outstanding_for_self:
|
||||||
"party_type": "Supplier",
|
against_voucher = self.return_against
|
||||||
"party": self.supplier,
|
|
||||||
"due_date": self.due_date,
|
# Did not use base_grand_total to book rounding loss gle
|
||||||
"against": self.against_expense_account,
|
gl = {
|
||||||
"credit": base_grand_total,
|
"account": self.credit_to,
|
||||||
"credit_in_account_currency": base_grand_total
|
"party_type": "Supplier",
|
||||||
if self.party_account_currency == self.company_currency
|
"party": self.supplier,
|
||||||
else grand_total,
|
"due_date": self.due_date,
|
||||||
"against_voucher": against_voucher,
|
"against": against_account or self.against_expense_account,
|
||||||
"against_voucher_type": self.doctype,
|
"credit": base_grand_total,
|
||||||
"project": self.project,
|
"credit_in_account_currency": base_grand_total
|
||||||
"cost_center": self.cost_center,
|
if self.party_account_currency == self.company_currency
|
||||||
},
|
else grand_total,
|
||||||
self.party_account_currency,
|
"against_voucher": against_voucher,
|
||||||
item=self,
|
"against_voucher_type": self.doctype,
|
||||||
)
|
"project": self.project,
|
||||||
)
|
"cost_center": self.cost_center,
|
||||||
|
"_skip_merge": skip_merge,
|
||||||
|
}
|
||||||
|
|
||||||
|
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):
|
def make_item_gl_entries(self, gl_entries):
|
||||||
# item gl entries
|
# item gl entries
|
||||||
@@ -1413,6 +1419,31 @@ class PurchaseInvoice(BuyingController):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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):
|
def make_payment_gl_entries(self, gl_entries):
|
||||||
# Make Cash GL Entries
|
# Make Cash GL Entries
|
||||||
if cint(self.is_paid) and self.cash_bank_account and self.paid_amount:
|
if cint(self.is_paid) and self.cash_bank_account and self.paid_amount:
|
||||||
|
|||||||
@@ -1544,6 +1544,61 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
|||||||
payment_entry.load_from_db()
|
payment_entry.load_from_db()
|
||||||
self.assertEqual(payment_entry.taxes[0].allocated_amount, 0)
|
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):
|
def test_provisional_accounting_entry(self):
|
||||||
setup_provisional_accounting()
|
setup_provisional_accounting()
|
||||||
|
|
||||||
|
|||||||
@@ -74,11 +74,17 @@ class TestTaxWithholdingCategory(FrappeTestCase):
|
|||||||
self.assertEqual(pi.grand_total, 18000)
|
self.assertEqual(pi.grand_total, 18000)
|
||||||
|
|
||||||
# check gl entry for the purchase invoice
|
# 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)
|
self.assertEqual(len(gl_entries), 3)
|
||||||
for d in gl_entries:
|
for d in gl_entries:
|
||||||
if d.account == pi.credit_to:
|
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"):
|
elif d.account == pi.items[0].get("expense_account"):
|
||||||
self.assertEqual(d.debit, 20000)
|
self.assertEqual(d.debit, 20000)
|
||||||
elif d.account == pi.taxes[0].get("account_head"):
|
elif d.account == pi.taxes[0].get("account_head"):
|
||||||
|
|||||||
@@ -234,6 +234,10 @@ def merge_similar_entries(gl_map, precision=None):
|
|||||||
merge_properties = get_merge_properties(accounting_dimensions)
|
merge_properties = get_merge_properties(accounting_dimensions)
|
||||||
|
|
||||||
for entry in gl_map:
|
for entry in gl_map:
|
||||||
|
if entry._skip_merge:
|
||||||
|
merged_gl_map.append(entry)
|
||||||
|
continue
|
||||||
|
|
||||||
entry.merge_key = get_merge_key(entry, merge_properties)
|
entry.merge_key = get_merge_key(entry, merge_properties)
|
||||||
# if there is already an entry in this account then just add it
|
# if there is already an entry in this account then just add it
|
||||||
# to that entry
|
# to that entry
|
||||||
|
|||||||
Reference in New Issue
Block a user