Skip to content
13 changes: 5 additions & 8 deletions erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
Original file line number Diff line number Diff line change
Expand Up @@ -363,9 +363,11 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
},
function () {
me.apply_pricing_rule();
me.frm.doc.apply_tds =
me.frm.tax_withholding_category || me.frm.tax_withholding_group ? 1 : 0;
me.frm.clear_table("tax_withholding_entries");
me.update_item_tax_withholding_categories(me.frm.tax_withholding_category);
me.frm.set_value(
"apply_tds",
me.frm.tax_withholding_category || me.frm.tax_withholding_group ? 1 : 0
);

// while duplicating, don't change payment terms
if (me.frm.doc.__run_link_triggers === false) {
Expand All @@ -376,11 +378,6 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
);
}

apply_tds(frm) {
var me = this;
me.frm.clear_table("tax_withholding_entries");
}

credit_to() {
var me = this;
if (this.frm.doc.credit_to) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -999,6 +999,7 @@
"fieldname": "tax_withholding_category",
"fieldtype": "Link",
"label": "Tax Withholding Category",
"mandatory_depends_on": "eval:doc.apply_tds",
"options": "Tax Withholding Category",
"print_hide": 1
}
Expand All @@ -1007,7 +1008,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2026-04-07 15:40:45.687554",
"modified": "2026-04-23 15:24:07.707385",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",
Expand Down
12 changes: 5 additions & 7 deletions erpnext/accounts/doctype/sales_invoice/sales_invoice.js
Original file line number Diff line number Diff line change
Expand Up @@ -474,9 +474,11 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
),
},
function () {
me.frm.doc.apply_tds =
me.frm.tax_withholding_category || me.frm.tax_withholding_group ? 1 : 0;
me.frm.clear_table("tax_withholding_entries");
me.update_item_tax_withholding_categories(me.frm.tax_withholding_category);
me.frm.set_value(
"apply_tds",
me.frm.tax_withholding_category || me.frm.tax_withholding_group ? 1 : 0
);
me.apply_pricing_rule();
}
);
Expand Down Expand Up @@ -694,10 +696,6 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
this.calculate_taxes_and_totals();
}

apply_tds(frm) {
this.frm.clear_table("tax_withholding_entries");
}

is_return() {
this.toggle_get_items();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1000,6 +1000,7 @@
"fieldname": "tax_withholding_category",
"fieldtype": "Link",
"label": "Tax Withholding Category",
"mandatory_depends_on": "eval:doc.apply_tds",
"options": "Tax Withholding Category",
"print_hide": 1
},
Expand All @@ -1008,15 +1009,14 @@
"fieldname": "apply_tds",
"fieldtype": "Check",
"label": "Consider for Tax Withholding",
"print_hide": 1,
"read_only": 1
"print_hide": 1
}
],
"grid_page_length": 50,
"idx": 1,
"istable": 1,
"links": [],
"modified": "2026-02-24 14:37:16.853941",
"modified": "2026-04-23 15:24:07.707385",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",
Expand Down
2 changes: 2 additions & 0 deletions erpnext/controllers/accounts_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -1104,6 +1104,8 @@ def set_missing_item_details(self, for_validate=False):
):
if not item.get("tax_withholding_category") and ret.get("tax_withholding_category"):
item.set("tax_withholding_category", ret.get("tax_withholding_category"))
apply_tds = ret.get("apply_tds") if ret.get("apply_tds") is not None else 1
item.set("apply_tds", apply_tds)

# Double check for cost center
# Items add via promotional scheme may not have cost center set
Expand Down
35 changes: 35 additions & 0 deletions erpnext/public/js/controllers/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -3183,6 +3183,41 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
}
}

apply_tds(doc, cdt, cdn) {
if (!["Purchase Invoice", "Sales Invoice"].includes(cdt)) return;

var me = this;
me.frm.clear_table("tax_withholding_entries");
$.each(this.frm.doc.items || [], function (i, item) {
item.apply_tds = me.frm.doc.apply_tds;
});
me.frm.refresh_field("tax_withholding_entries");
me.frm.refresh_field("items");
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/**
* Called on party (customer/supplier) change to update tax_withholding_category
*/
update_item_tax_withholding_categories(party_category) {
var me = this;
var item_codes = [...new Set((this.frm.doc.items || []).map((i) => i.item_code).filter(Boolean))];
if (!item_codes.length) return;

frappe.call({
method: "erpnext.stock.get_item_details.get_item_tax_withholding_categories",
args: { item_codes: item_codes, doctype: me.frm.doc.doctype },
callback: function (r) {
if (r.exc || !r.message) return;
var item_categories = r.message;
$.each(me.frm.doc.items || [], function (i, item) {
// Item master takes priority; fall back to party-level category
item.tax_withholding_category = item_categories[item.item_code] || party_category || null;
});
me.frm.refresh_field("items");
},
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

against_blanket_order(doc, cdt, cdn) {
var item = locals[cdt][cdn];
if (!item.against_blanket_order) {
Expand Down
23 changes: 23 additions & 0 deletions erpnext/stock/get_item_details.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ def get_item_details(

if doc:
ctx.transaction_date = doc.get("transaction_date") or doc.get("posting_date")
ctx.apply_tds = doc.get("apply_tds")

if doc.get("doctype") == "Purchase Invoice":
ctx.bill_date = doc.get("bill_date")
Expand Down Expand Up @@ -1394,6 +1395,28 @@ def get_tax_withholding_category(ctx: ItemDetailsCtx, item_doc, out: ItemDetails
)

out.tax_withholding_category = tax_withholding_category
if ctx.get("apply_tds") is not None:
out.apply_tds = ctx.get("apply_tds")


@frappe.whitelist()
def get_item_tax_withholding_categories(item_codes: list | str, doctype: str) -> dict:
"""
Return a mapping of {item_code: tax_withholding_category} from the Item master.
"""
item_codes = frappe.parse_json(item_codes)
field = (
"sales_tax_withholding_category" if doctype in sales_doctypes else "purchase_tax_withholding_category"
)

return frappe._dict(
frappe.get_list(
"Item",
filters={"name": ["in", item_codes]},
fields=["name", field],
as_list=1,
)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
)


@erpnext.normalize_ctx_input(ItemDetailsCtx)
Expand Down
Loading