feat: item wise tds calculation
This commit is contained in:
parent
4aff2a32ad
commit
2ca0cf6fc4
@ -57,6 +57,8 @@
|
|||||||
"column_break_28",
|
"column_break_28",
|
||||||
"total",
|
"total",
|
||||||
"net_total",
|
"net_total",
|
||||||
|
"tax_withholding_net_total",
|
||||||
|
"base_tax_withholding_net_total",
|
||||||
"taxes_section",
|
"taxes_section",
|
||||||
"taxes_and_charges",
|
"taxes_and_charges",
|
||||||
"column_break_58",
|
"column_break_58",
|
||||||
@ -1421,6 +1423,24 @@
|
|||||||
"label": "Is Old Subcontracting Flow",
|
"label": "Is Old Subcontracting Flow",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "tax_withholding_net_total",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Tax Withholding Net Total",
|
||||||
|
"no_copy": 1,
|
||||||
|
"options": "currency",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "base_tax_withholding_net_total",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Base Tax Withholding Net Total",
|
||||||
|
"no_copy": 1,
|
||||||
|
"options": "currency",
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"collapsible_depends_on": "tax_withheld_vouchers",
|
"collapsible_depends_on": "tax_withheld_vouchers",
|
||||||
"fieldname": "tax_withheld_vouchers_section",
|
"fieldname": "tax_withheld_vouchers_section",
|
||||||
|
@ -1574,35 +1574,6 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin):
|
|||||||
|
|
||||||
self.assertTrue(return_pi.docstatus == 1)
|
self.assertTrue(return_pi.docstatus == 1)
|
||||||
|
|
||||||
def test_without_tds(self):
|
|
||||||
make_purchase_invoice_tds()
|
|
||||||
|
|
||||||
def test_total_tds(self):
|
|
||||||
supplier = create_supplier(
|
|
||||||
supplier_name="_Test TDS Advance Supplier",
|
|
||||||
tax_withholding_category="TDS - 194 - Dividends - Individual",
|
|
||||||
)
|
|
||||||
pi = make_purchase_invoice_tds(supplier= "_Test TDS Advance Supplier",total_tds = 1)
|
|
||||||
|
|
||||||
sum_tds = 0
|
|
||||||
for item in pi.items:
|
|
||||||
sum_tds += item.net_amount
|
|
||||||
|
|
||||||
self.assertEqual(pi.tax_withholding_net_total, sum_tds)
|
|
||||||
for tax in pi.taxes:
|
|
||||||
self.assertEqual(tax.tax_amount, pi.tax_withholding_net_total * 0.10)
|
|
||||||
|
|
||||||
def test_partial_tds(self):
|
|
||||||
pi = make_purchase_invoice_tds(supplier= "_Test TDS Advance Supplier",partial_tds = 1)
|
|
||||||
|
|
||||||
sum_tds = 0
|
|
||||||
for item in pi.items:
|
|
||||||
if item.apply_tds:
|
|
||||||
sum_tds += item.net_amount
|
|
||||||
|
|
||||||
self.assertEqual(pi.tax_withholding_net_total, sum_tds)
|
|
||||||
for tax in pi.taxes:
|
|
||||||
self.assertEqual(tax.tax_amount, pi.tax_withholding_net_total * 0.10)
|
|
||||||
|
|
||||||
def check_gl_entries(doc, voucher_no, expected_gle, posting_date):
|
def check_gl_entries(doc, voucher_no, expected_gle, posting_date):
|
||||||
gl_entries = frappe.db.sql(
|
gl_entries = frappe.db.sql(
|
||||||
@ -1711,86 +1682,6 @@ def make_purchase_invoice(**args):
|
|||||||
pi.submit()
|
pi.submit()
|
||||||
return pi
|
return pi
|
||||||
|
|
||||||
def make_purchase_invoice_tds(**args):
|
|
||||||
pi = frappe.new_doc("Purchase Invoice")
|
|
||||||
args = frappe._dict(args)
|
|
||||||
pi.posting_date = args.posting_date or today()
|
|
||||||
if args.posting_time:
|
|
||||||
pi.posting_time = args.posting_time
|
|
||||||
if args.update_stock:
|
|
||||||
pi.update_stock = 1
|
|
||||||
if args.is_paid:
|
|
||||||
pi.is_paid = 1
|
|
||||||
|
|
||||||
if args.cash_bank_account:
|
|
||||||
pi.cash_bank_account = args.cash_bank_account
|
|
||||||
|
|
||||||
pi.company = args.company or "_Test Company"
|
|
||||||
pi.supplier = args.supplier or "_Test Supplier"
|
|
||||||
pi.currency = args.currency or "INR"
|
|
||||||
pi.conversion_rate = args.conversion_rate or 1
|
|
||||||
pi.is_return = args.is_return
|
|
||||||
pi.return_against = args.return_against
|
|
||||||
pi.is_subcontracted = args.is_subcontracted or 0
|
|
||||||
pi.supplier_warehouse = args.supplier_warehouse or "_Test Warehouse 1 - _TC"
|
|
||||||
pi.cost_center = args.parent_cost_center
|
|
||||||
|
|
||||||
if args.total_tds or args.partial_tds:
|
|
||||||
pi.apply_tds = 1
|
|
||||||
|
|
||||||
pi.extend(
|
|
||||||
"items",
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"item_code": args.item or args.item_code or "_Test Item",
|
|
||||||
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
|
||||||
"qty": args.qty or 5,
|
|
||||||
"received_qty": args.received_qty or 0,
|
|
||||||
"rejected_qty": args.rejected_qty or 0,
|
|
||||||
"rate": args.rate or 5000,
|
|
||||||
"price_list_rate": args.price_list_rate or 5000,
|
|
||||||
"expense_account": args.expense_account or "_Test Account Cost for Goods Sold - _TC",
|
|
||||||
"discount_account": args.discount_account or None,
|
|
||||||
"discount_amount": args.discount_amount or 0,
|
|
||||||
"conversion_factor": 1.0,
|
|
||||||
"serial_no": args.serial_no,
|
|
||||||
"stock_uom": args.uom or "_Test UOM",
|
|
||||||
"cost_center": args.cost_center or "_Test Cost Center - _TC",
|
|
||||||
"project": args.project,
|
|
||||||
"rejected_warehouse": args.rejected_warehouse or "",
|
|
||||||
"rejected_serial_no": args.rejected_serial_no or "",
|
|
||||||
"asset_location": args.location or "",
|
|
||||||
"allow_zero_valuation_rate": args.get("allow_zero_valuation_rate") or 0,
|
|
||||||
"apply_tds": 1 if (args.total_tds or args.partial_tds) else 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"item_code": args.item or args.item_code or "_Test Item",
|
|
||||||
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
|
||||||
"qty": args.qty or 5,
|
|
||||||
"received_qty": args.received_qty or 0,
|
|
||||||
"rejected_qty": args.rejected_qty or 0,
|
|
||||||
"rate": args.rate or 5000,
|
|
||||||
"price_list_rate": args.price_list_rate or 5000,
|
|
||||||
"expense_account": args.expense_account or "_Test Account Cost for Goods Sold - _TC",
|
|
||||||
"discount_account": args.discount_account or None,
|
|
||||||
"discount_amount": args.discount_amount or 0,
|
|
||||||
"conversion_factor": 1.0,
|
|
||||||
"serial_no": args.serial_no,
|
|
||||||
"stock_uom": args.uom or "_Test UOM",
|
|
||||||
"cost_center": args.cost_center or "_Test Cost Center - _TC",
|
|
||||||
"project": args.project,
|
|
||||||
"rejected_warehouse": args.rejected_warehouse or "",
|
|
||||||
"rejected_serial_no": args.rejected_serial_no or "",
|
|
||||||
"asset_location": args.location or "",
|
|
||||||
"allow_zero_valuation_rate": args.get("allow_zero_valuation_rate") or 0,
|
|
||||||
"apply_tds": 1 if (args.total_tds) else 0
|
|
||||||
},
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
pi.save()
|
|
||||||
pi.submit()
|
|
||||||
return pi
|
|
||||||
|
|
||||||
def make_purchase_invoice_against_cost_center(**args):
|
def make_purchase_invoice_against_cost_center(**args):
|
||||||
pi = frappe.new_doc("Purchase Invoice")
|
pi = frappe.new_doc("Purchase Invoice")
|
||||||
|
@ -275,6 +275,11 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
|
|||||||
|
|
||||||
def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
|
def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
|
||||||
doctype = "Purchase Invoice" if party_type == "Supplier" else "Sales Invoice"
|
doctype = "Purchase Invoice" if party_type == "Supplier" else "Sales Invoice"
|
||||||
|
field = (
|
||||||
|
"base_tax_withholding_net_total as base_net_total"
|
||||||
|
if party_type == "Supplier"
|
||||||
|
else "base_net_total"
|
||||||
|
)
|
||||||
voucher_wise_amount = {}
|
voucher_wise_amount = {}
|
||||||
vouchers = []
|
vouchers = []
|
||||||
|
|
||||||
@ -291,7 +296,7 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
|
|||||||
{"apply_tds": 1, "tax_withholding_category": tax_details.get("tax_withholding_category")}
|
{"apply_tds": 1, "tax_withholding_category": tax_details.get("tax_withholding_category")}
|
||||||
)
|
)
|
||||||
|
|
||||||
invoices_details = frappe.get_all(doctype, filters=filters, fields=["name", "base_net_total"])
|
invoices_details = frappe.get_all(doctype, filters=filters, fields=["name", field])
|
||||||
|
|
||||||
for d in invoices_details:
|
for d in invoices_details:
|
||||||
vouchers.append(d.name)
|
vouchers.append(d.name)
|
||||||
@ -431,11 +436,11 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers):
|
|||||||
):
|
):
|
||||||
# Get net total again as TDS is calculated on net total
|
# Get net total again as TDS is calculated on net total
|
||||||
# Grand is used to just check for threshold breach
|
# Grand is used to just check for threshold breach
|
||||||
net_total = 0
|
net_total = (
|
||||||
if vouchers:
|
frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(tax_withholding_net_total)")
|
||||||
net_total = frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(net_total)")
|
or 0.0
|
||||||
|
)
|
||||||
net_total += inv.net_total
|
net_total += inv.tax_withholding_net_total
|
||||||
supp_credit_amt = net_total - cumulative_threshold
|
supp_credit_amt = net_total - cumulative_threshold
|
||||||
|
|
||||||
if ldc and is_valid_certificate(
|
if ldc and is_valid_certificate(
|
||||||
|
@ -186,6 +186,46 @@ class TestTaxWithholdingCategory(unittest.TestCase):
|
|||||||
for d in reversed(invoices):
|
for d in reversed(invoices):
|
||||||
d.cancel()
|
d.cancel()
|
||||||
|
|
||||||
|
def test_tds_calculation_on_net_total_partial_tds(self):
|
||||||
|
frappe.db.set_value(
|
||||||
|
"Supplier", "Test TDS Supplier4", "tax_withholding_category", "Cumulative Threshold TDS"
|
||||||
|
)
|
||||||
|
invoices = []
|
||||||
|
|
||||||
|
pi = create_purchase_invoice(supplier="Test TDS Supplier4", rate=20000, do_not_save=True)
|
||||||
|
pi.extend(
|
||||||
|
"items",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"doctype": "Purchase Invoice Item",
|
||||||
|
"item_code": frappe.db.get_value("Item", {"item_name": "TDS Item"}, "name"),
|
||||||
|
"qty": 1,
|
||||||
|
"rate": 20000,
|
||||||
|
"cost_center": "Main - _TC",
|
||||||
|
"expense_account": "Stock Received But Not Billed - _TC",
|
||||||
|
"apply_tds": 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"doctype": "Purchase Invoice Item",
|
||||||
|
"item_code": frappe.db.get_value("Item", {"item_name": "TDS Item"}, "name"),
|
||||||
|
"qty": 1,
|
||||||
|
"rate": 35000,
|
||||||
|
"cost_center": "Main - _TC",
|
||||||
|
"expense_account": "Stock Received But Not Billed - _TC",
|
||||||
|
"apply_tds": 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
pi.save()
|
||||||
|
pi.submit()
|
||||||
|
invoices.append(pi)
|
||||||
|
|
||||||
|
self.assertEqual(pi.taxes[0].tax_amount, 5500)
|
||||||
|
|
||||||
|
# cancel invoices to avoid clashing
|
||||||
|
for d in reversed(invoices):
|
||||||
|
d.cancel()
|
||||||
|
|
||||||
def test_multi_category_single_supplier(self):
|
def test_multi_category_single_supplier(self):
|
||||||
frappe.db.set_value(
|
frappe.db.set_value(
|
||||||
"Supplier", "Test TDS Supplier5", "tax_withholding_category", "Test Service Category"
|
"Supplier", "Test TDS Supplier5", "tax_withholding_category", "Test Service Category"
|
||||||
|
@ -67,13 +67,15 @@ class calculate_taxes_and_totals(object):
|
|||||||
|
|
||||||
def calculate_tax_withholding_net_total(self):
|
def calculate_tax_withholding_net_total(self):
|
||||||
if hasattr(self.doc, "tax_withholding_net_total"):
|
if hasattr(self.doc, "tax_withholding_net_total"):
|
||||||
|
|
||||||
sum_net_amount = 0
|
sum_net_amount = 0
|
||||||
|
sum_base_net_amount = 0
|
||||||
for item in self.doc.get("items"):
|
for item in self.doc.get("items"):
|
||||||
if hasattr(item, "apply_tds") and item.apply_tds:
|
if hasattr(item, "apply_tds") and item.apply_tds:
|
||||||
sum_net_amount += item.net_amount
|
sum_net_amount += item.net_amount
|
||||||
|
sum_base_net_amount += item.base_net_amount
|
||||||
|
|
||||||
self.doc.tax_withholding_net_total = sum_net_amount
|
self.doc.tax_withholding_net_total = sum_net_amount
|
||||||
|
self.doc.base_tax_withholding_net_total = sum_base_net_amount
|
||||||
|
|
||||||
def validate_item_tax_template(self):
|
def validate_item_tax_template(self):
|
||||||
for item in self.doc.get("items"):
|
for item in self.doc.get("items"):
|
||||||
|
@ -317,3 +317,4 @@ erpnext.patches.v14_0.fix_subcontracting_receipt_gl_entries
|
|||||||
erpnext.patches.v14_0.migrate_remarks_from_gl_to_payment_ledger
|
erpnext.patches.v14_0.migrate_remarks_from_gl_to_payment_ledger
|
||||||
erpnext.patches.v13_0.update_schedule_type_in_loans
|
erpnext.patches.v13_0.update_schedule_type_in_loans
|
||||||
erpnext.patches.v14_0.create_accounting_dimensions_for_asset_capitalization
|
erpnext.patches.v14_0.create_accounting_dimensions_for_asset_capitalization
|
||||||
|
erpnext.patches.v14_0.update_tds_fields
|
||||||
|
25
erpnext/patches/v14_0/update_tds_fields.py
Normal file
25
erpnext/patches/v14_0/update_tds_fields.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import frappe
|
||||||
|
|
||||||
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
# Only do for current fiscal year, no need to repost for all years
|
||||||
|
for company in frappe.get_all("Company"):
|
||||||
|
fiscal_year_details = get_fiscal_year(company=company.name, as_dict=True)
|
||||||
|
|
||||||
|
purchase_invoice = frappe.qb.DocType("Purchase Invoice")
|
||||||
|
|
||||||
|
frappe.qb.update(purchase_invoice).set(
|
||||||
|
purchase_invoice.tax_withholding_net_total, purchase_invoice.net_total
|
||||||
|
).set(
|
||||||
|
purchase_invoice.base_tax_withholding_net_total, purchase_invoice.base_net_total
|
||||||
|
).where(
|
||||||
|
purchase_invoice.company == company.name
|
||||||
|
).where(
|
||||||
|
purchase_invoice.apply_tds == 1
|
||||||
|
).where(
|
||||||
|
purchase_invoice.posting_date >= fiscal_year_details.year_start_date
|
||||||
|
).where(
|
||||||
|
purchase_invoice.docstatus == 1
|
||||||
|
).run()
|
@ -1200,7 +1200,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
|||||||
"base_rounding_adjustment"], company_currency);
|
"base_rounding_adjustment"], company_currency);
|
||||||
|
|
||||||
this.frm.set_currency_labels(["total", "net_total", "total_taxes_and_charges", "discount_amount",
|
this.frm.set_currency_labels(["total", "net_total", "total_taxes_and_charges", "discount_amount",
|
||||||
"grand_total", "taxes_and_charges_added", "taxes_and_charges_deducted",
|
"grand_total", "taxes_and_charges_added", "taxes_and_charges_deducted","tax_withholding_net_total",
|
||||||
"rounded_total", "in_words", "paid_amount", "write_off_amount", "operating_cost",
|
"rounded_total", "in_words", "paid_amount", "write_off_amount", "operating_cost",
|
||||||
"scrap_material_cost", "rounding_adjustment", "raw_material_cost",
|
"scrap_material_cost", "rounding_adjustment", "raw_material_cost",
|
||||||
"total_cost"], this.frm.doc.currency);
|
"total_cost"], this.frm.doc.currency);
|
||||||
@ -1217,7 +1217,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
|||||||
}
|
}
|
||||||
|
|
||||||
// toggle fields
|
// toggle fields
|
||||||
this.frm.toggle_display(["conversion_rate", "base_total", "base_net_total",
|
this.frm.toggle_display(["conversion_rate", "base_total", "base_net_total", "base_tax_withholding_net_total",
|
||||||
"base_total_taxes_and_charges", "base_taxes_and_charges_added", "base_taxes_and_charges_deducted",
|
"base_total_taxes_and_charges", "base_taxes_and_charges_added", "base_taxes_and_charges_deducted",
|
||||||
"base_grand_total", "base_rounded_total", "base_in_words", "base_discount_amount",
|
"base_grand_total", "base_rounded_total", "base_in_words", "base_discount_amount",
|
||||||
"base_paid_amount", "base_write_off_amount", "base_operating_cost", "base_raw_material_cost",
|
"base_paid_amount", "base_write_off_amount", "base_operating_cost", "base_raw_material_cost",
|
||||||
|
Loading…
Reference in New Issue
Block a user