ERPNext v16: Dynamically change Item Grid columns based on selected Company after initial render

Description

I’m using ERPNext/Frappe v16 and I’m trying to implement company-wise dynamic column visibility in the Purchase Order Items child table.

Example

Company A should display:

  • Item Code

  • Qty

  • Rate

  • Price List Rate

  • GST HSN Code

  • Discount %

  • Amount

Company B should display:

  • Item Code

  • Qty

  • UOM

  • Conversion Factor

  • Rate per Kg (custom field)

  • Amount

When the user changes the Company on the Purchase Order, the visible columns in the Items grid should update accordingly without reloading the form.

What I’ve tried

  • Implemented the logic in a Client Script.

  • Moved the same logic into a custom app JavaScript.

  • Triggered the logic on both refresh and company field change events.

  • Used the Grid API to toggle column visibility and refreshed the Items grid afterward.

The script executes successfully, and I confirmed that the underlying DocField metadata (such as the hidden property) is updated correctly. However, the rendered Items grid does not visually update—the same columns remain visible.

I also verified that:

  • There is no saved Assign Columns configuration.

  • The affected fields are not mandatory.

  • The JavaScript is loading and executing correctly.

Additionally, I tried forcing the grid to rebuild by:

  • Refreshing the grid.

  • Refreshing individual grid rows.

  • Rebuilding the grid header.

  • Clearing and rebuilding the grid DOM.

These approaches either had no effect or resulted in an inconsistent grid state.

Question

Is there a supported or documented way in Frappe/ERPNext v16 to force the Grid component to rebuild its visible columns after the initial render?

Or is dynamic column switching after the grid has been rendered not supported, meaning the recommended approach is to use static field visibility (depends_on, permissions, etc.) instead of changing grid columns programmatically?

Has anyone implemented company-specific Item grid columns in ERPNext v16 without reloading the form?

I have implemented similar behavior for the Accounts table in the Journal Entry.

I changed my code a bit to make it easier for you to understand. Maybe you can simplify the code further by removing the lines for updating the in_list_view and hidden properties, but it depends on your use-case:

Just remember that sum of the columns field should be 10.

frappe.ui.form.on(“Journal Entry”, {
  refresh(frm) {
    frappe.after_ajax(() => {
      apply_accounts_columns(frm, frm.doc.company);
    });
  },
  company(frm) {
    apply_accounts_columns(frm, frm.doc.company);
  }
})

function apply_accounts_columns(frm, company) {
let grid = frm.get_field(“accounts”).grid;
const is_test_company = company == 'Test'
grid.update_docfield_property("account", "columns", 2);
grid.update_docfield_property("party_type", "columns", is_test_company  ? 1 : 2);
grid.update_docfield_property("party", "columns", is_test_company  ? 1 : 2);
grid.update_docfield_property("debit_in_account_currency", "columns", is_expense ? 1 : 2);
grid.update_docfield_property("credit_in_account_currency", "columns", is_expense ? 1 : 2);

grid.update_docfield_property("field_1", "hidden", is_test_company ? 0 : 1);
grid.update_docfield_property("field_2", "hidden", is_test_company ? 0 : 1);
grid.update_docfield_property("field_3", "hidden", is_test_company ? 0 : 1);

grid.update_docfield_property("field_1", "in_list_view", is_test_company ? 1 : 0);
grid.update_docfield_property("field_2", "in_list_view", is_test_company ? 1 : 0);
grid.update_docfield_property("field_3", "in_list_view", is_test_company ? 1 : 0);

grid.update_docfield_property("field_1", "columns", is_test_company ? 1 : 0);
grid.update_docfield_property("field_2", "columns", is_test_company ? 1 : 0);
grid.update_docfield_property("field_3", "columns", is_test_company ? 1 : 0);


grid.reset_grid();
}

Thanks for the suggestion. My requirement is a little different. I don’t want to change the column width (columns) or the grid layout. I need to completely show or hide specific fields in the Item Grid based on the selected Company.

For example:

Company A

  • Show: Item Code, Qty, Rate, Price List Rate, Discount %, Amount

  • Hide: UOM, Conversion Factor, Rate Per Kg

Company B

  • Show: Item Code, Qty, UOM, Conversion Factor, Rate Per Kg, Amount

  • Hide: Rate, Price List Rate, Discount %

Have you successfully implemented dynamic field visibility (not just column width) in a child table grid in ERPNext/Frappe v16? If so, could you share a working example?

Put refresh in .js file.

item_unmanual_subcontracting_order(frm)//item_code, bom, no_of_boxes, qty_per_pack, qty

function item_unmanual_subcontracting_order(frm) {
const custom_type = frm.doc.custom_type;
const child_table = frm.doc.doctype + " Item"
let sub_assembly_list_view = {};
sub_assembly_list_view[child_table] = [
{
“fieldname”: “item_code”,
“columns”: 3
},
{
“fieldname”: “bom”,
“columns”: 2
},
{
“fieldname”: “no_of_boxes”,
“columns”: 2
},
{
“fieldname”: “qty_per_pack”,
“columns”: 2
},
{
“fieldname”: “qty”,
“columns”: 1
},
]
frappe.model.user_settings.save(frm.doctype, “GridView”, sub_assembly_list_view).then((r) => {
frappe.model.user_settings[frm.doctype] = r.message || r;
frappe.after_ajax(() => {
frm.fields_dict.items.grid.reset_grid();
frm.fields_dict.items.grid.refresh();
});
frm.refresh_fields(“items”)
});
}

I already have the included example with column adjustment, and visibility (hidden) property. I am using Frappe v15, but i guess it would work with v16 also. Try my Client Script with any field in the accounts table to see if it works in Journal Entry as an example.

client script not pooisble or worked this my case ?

your script through all filed show table but conditoon wise read only provides

Above client script is working properly in version 15 but not tested in version 16.

i have try script but all field show condition based read only peorvde not hide ?

this in i want column hide provides this show all column show condition wise

script:

frappe.ui.form.on(“Purchase Order”, {

refresh(frm) {

    frappe.after_ajax(() => {

apply_item_columns(frm, frm.doc.company);

    });

},

company(frm) {

apply_item_columns(frm, frm.doc.company);

}

});

function apply_item_columns(frm, company) {

let grid = frm.get_field(“items”).grid;

const SPECIAL_COMPANY = “Company A”;

const is_special_company = company === SPECIAL_COMPANY;

grid.update_docfield_property("item_code", "columns", 2);

grid.update_docfield_property("qty", "columns", 1);

grid.update_docfield_property("amount", "columns", 1);

grid.update_docfield_property(“gst_hsn_code”, “hidden”, is_special_company ? 1 : 0);

grid.update_docfield_property(“gst_hsn_code”, “in_list_view”, is_special_company ? 0 : 1);

grid.update_docfield_property(“gst_hsn_code”, “columns”, is_special_company ? 0 : 1);

grid.update_docfield_property(“price_list_rate”, “hidden”, is_special_company ? 1 : 0);

grid.update_docfield_property(“price_list_rate”, “in_list_view”, is_special_company ? 0 : 1);

grid.update_docfield_property(“price_list_rate”, “columns”, is_special_company ? 0 : 1);

grid.update_docfield_property(“discount_percentage”, “hidden”, is_special_company ? 1 : 0);

grid.update_docfield_property(“discount_percentage”, “in_list_view”, is_special_company ? 0 : 1);

grid.update_docfield_property(“discount_percentage”, “columns”, is_special_company ? 0 : 1);

grid.update_docfield_property(“rate”, “hidden”, is_special_company ? 1 : 0);

grid.update_docfield_property(“rate”, “in_list_view”, is_special_company ? 0 : 1);

grid.update_docfield_property(“rate”, “columns”, is_special_company ? 0 : 1);

grid.update_docfield_property(“uom”, “hidden”, is_special_company ? 0 : 1);

grid.update_docfield_property(“uom”, “in_list_view”, is_special_company ? 1 : 0);

grid.update_docfield_property(“uom”, “columns”, is_special_company ? 1 : 0);

grid.update_docfield_property(“conversion_factor”, “hidden”, is_special_company ? 0 : 1);

grid.update_docfield_property(“conversion_factor”, “in_list_view”, is_special_company ? 1 : 0);

grid.update_docfield_property(“conversion_factor”, “columns”, is_special_company ? 1 : 0);

grid.update_docfield_property(“custom_custom_rate_per_kg”, “hidden”, is_special_company ? 0 : 1);

grid.update_docfield_property(“custom_custom_rate_per_kg”, “in_list_view”, is_special_company ? 1 : 0);

grid.update_docfield_property(“custom_custom_rate_per_kg”, “columns”, is_special_company ? 1 : 0);

grid.update_docfield_property(“received_qty”, “hidden”, 1);

grid.update_docfield_property(“received_qty”, “in_list_view”, 0);

grid.update_docfield_property(“accepted_qty”, “hidden”, 1);

grid.update_docfield_property(“accepted_qty”, “in_list_view”, 0);

grid.update_docfield_property(“delivery_date”, “hidden”, 1);

grid.update_docfield_property(“delivery_date”, “in_list_view”, 0);

grid.reset_grid();

}