From 8fcd5d1cfabc4908a4f7bcfc5477833298d2cc1b Mon Sep 17 00:00:00 2001 From: Sandeep Kakde <72334178+kakde-sandeep@users.noreply.github.com> Date: Sun, 6 Apr 2025 21:18:23 +0530 Subject: [PATCH] feat: Adding option to trim custom tables (#31180) * feat: Adding option to trim custom tables * refactor: extracting trim_table to index.js from doctype.js & customize_form.js * refactor: running pre-commit --- frappe/core/doctype/doctype/doctype.js | 6 +++ frappe/core/doctype/doctype/doctype.py | 11 +++++ .../doctype/customize_form/customize_form.js | 34 -------------- frappe/public/js/frappe/doctype/index.js | 47 +++++++++++++++++++ 4 files changed, 64 insertions(+), 34 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.js b/frappe/core/doctype/doctype/doctype.js index 62b9932355..b91db43c54 100644 --- a/frappe/core/doctype/doctype/doctype.js +++ b/frappe/core/doctype/doctype/doctype.js @@ -32,6 +32,12 @@ frappe.ui.form.on("DocType", { }, refresh: function (frm) { + if (frm.doc.custom === 1) { + frm.add_custom_button(__("Trim Table"), function () { + frm.trigger("trim_table"); + }); + } + frm.set_query("role", "permissions", function (doc) { if (doc.custom && frappe.session.user != "Administrator") { return { diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 0860e80297..260e1494b4 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -1058,6 +1058,17 @@ class DocType(Document): ) return True + @frappe.whitelist() + def trim_table(self): + from frappe.model.meta import trim_table + + """Removes database fields that don't exist in the doctype. + + This may be needed as maintenance since removing a field in a DocType + doesn't automatically delete the db field. + """ + trim_table(self.name, dry_run=False) + def validate_series(dt, autoname=None, name=None): """Validate if `autoname` property is correctly set.""" diff --git a/frappe/custom/doctype/customize_form/customize_form.js b/frappe/custom/doctype/customize_form/customize_form.js index 2d047d4c30..44fe8e249c 100644 --- a/frappe/custom/doctype/customize_form/customize_form.js +++ b/frappe/custom/doctype/customize_form/customize_form.js @@ -202,40 +202,6 @@ frappe.ui.form.on("Customize Form", { ); }, - async trim_table(frm) { - let dropped_columns = await frappe.xcall( - "frappe.custom.doctype.customize_form.customize_form.get_orphaned_columns", - { doctype: frm.doc.doc_type } - ); - - if (!dropped_columns?.length) { - frappe.toast(__("This doctype has no orphan fields to trim")); - return; - } - let msg = __( - "Warning: DATA LOSS IMMINENT! Proceeding will permanently delete following database columns from doctype {0}:", - [frm.doc.doc_type.bold()] - ); - msg += "
    " + dropped_columns.map((col) => `
  1. ${col}
  2. `).join("") + "
"; - msg += __("This action is irreversible. Do you wish to continue?"); - - frappe.confirm(msg, () => { - return frm.call({ - doc: frm.doc, - method: "trim_table", - callback: function (r) { - if (!r.exc) { - frappe.show_alert({ - message: __("Table Trimmed"), - indicator: "green", - }); - frappe.customize_form.clear_locals_and_refresh(frm); - } - }, - }); - }); - }, - setup_export(frm) { if (frappe.boot.developer_mode) { frm.add_custom_button( diff --git a/frappe/public/js/frappe/doctype/index.js b/frappe/public/js/frappe/doctype/index.js index 4d8e437c1b..d5136d4fba 100644 --- a/frappe/public/js/frappe/doctype/index.js +++ b/frappe/public/js/frappe/doctype/index.js @@ -222,4 +222,51 @@ frappe.model.DocTypeController = class DocTypeController extends frappe.ui.form. update_fieldname_options(); } } + + async trim_table() { + let frm = this.frm; + let doctype = null; + if (frm.doc.doctype === "DocType") { + doctype = frm.doc.name; + } else { + // In customize form name field is "Customize Form". Doctype name is stored in doc_type. + doctype = frm.doc.doc_type; + } + + let dropped_columns = await frappe.xcall( + "frappe.custom.doctype.customize_form.customize_form.get_orphaned_columns", + { doctype: doctype } + ); + + if (!dropped_columns?.length) { + frappe.toast(__("This doctype has no orphan fields to trim")); + return; + } + let msg = __( + "Warning: DATA LOSS IMMINENT! Proceeding will permanently delete following database columns from doctype {0}:", + [frm.doc.name.bold()] + ); + msg += "
    " + dropped_columns.map((col) => `
  1. ${col}
  2. `).join("") + "
"; + msg += __("This action is irreversible. Do you wish to continue?"); + + frappe.confirm(msg, () => { + return frm.call({ + doc: frm.doc, + method: "trim_table", + callback: function (r) { + if (!r.exc) { + frappe.show_alert({ + message: __("Table Trimmed"), + indicator: "green", + }); + if (frm.doc.doctype === "DocType") { + frm.refresh(); + } else { + frappe.customize_form.clear_locals_and_refresh(frm); + } + } + }, + }); + }); + } };