From 9fabfa30547930c1c7afcfdb0b0e7248bf5616ab Mon Sep 17 00:00:00 2001 From: Sagar Vora <16315650+sagarvora@users.noreply.github.com> Date: Thu, 19 Feb 2026 00:48:15 +0530 Subject: [PATCH] fix: prevent docfield copy contamination across grid rows on deletion/reorder set_docfields was passing the row's mutated docfields (with stale reqd/read_only/hidden from dependency evaluation) as source to make_docfield_copy_for, poisoning the copy store for whichever doc landed at that position after deletion/reorder. - Remove make_docfield_copy_for call from set_docfields (the copy store seeds itself lazily from clean originals in get_docfield_copy) - Always call set_docfields() on refresh so rows pick up correct copies when doc identity changes at a position - Thread grid.docfields through meta.js so new copy-store entries include grid-level customizations (set_df_property on child fields) Closes #36921 --- frappe/public/js/frappe/form/grid_row.js | 24 +++++------------------- frappe/public/js/frappe/model/meta.js | 8 ++++---- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index 41b28e9238..dd83548873 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -53,25 +53,14 @@ export default class GridRow { this.wrapper.appendTo(this.parent); } - set_docfields(update = false) { + set_docfields() { if (this.doc && this.parent_df.options) { - frappe.meta.make_docfield_copy_for( + this.docfields = frappe.meta.get_docfields( this.parent_df.options, this.doc.name, - this.docfields + null, + this.grid.docfields ); - const docfields = frappe.meta.get_docfields(this.parent_df.options, this.doc.name); - if (update) { - // to maintain references - this.docfields.forEach((df) => { - Object.assign( - df, - docfields.find((d) => d.fieldname === df.fieldname) - ); - }); - } else { - this.docfields = docfields; - } } } @@ -198,10 +187,7 @@ export default class GridRow { ); } refresh() { - // update docfields for new record - if (this.frm && this.doc && this.doc.__islocal) { - this.set_docfields(true); - } + this.set_docfields(); if (this.frm && this.doc) { this.doc = locals[this.doc.doctype][this.doc.name]; diff --git a/frappe/public/js/frappe/model/meta.js b/frappe/public/js/frappe/model/meta.js index 58291e2924..d393f33cb7 100644 --- a/frappe/public/js/frappe/model/meta.js +++ b/frappe/public/js/frappe/model/meta.js @@ -91,8 +91,8 @@ $.extend(frappe.meta, { }; }, - get_docfields: function (doctype, name, filters) { - var docfield_map = frappe.meta.get_docfield_copy(doctype, name); + get_docfields: function (doctype, name, filters, docfield_list = null) { + var docfield_map = frappe.meta.get_docfield_copy(doctype, name, docfield_list); var docfields = frappe.meta.sort_docfields(docfield_map); @@ -125,11 +125,11 @@ $.extend(frappe.meta, { }); }, - get_docfield_copy: function (doctype, name) { + get_docfield_copy: function (doctype, name, docfield_list = null) { if (!name) return frappe.meta.docfield_map[doctype]; if (!(frappe.meta.docfield_copy[doctype] && frappe.meta.docfield_copy[doctype][name])) { - frappe.meta.make_docfield_copy_for(doctype, name); + frappe.meta.make_docfield_copy_for(doctype, name, docfield_list); } return frappe.meta.docfield_copy[doctype][name];