Make Record Producer variables editable on the ESC "My Submission" tab based on record State
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 weeks ago - last edited 3 weeks ago
What I'm trying to do
On the Employee Center (ESC) ticket page — /esc?id=ticket&table=incident&sys_id=<sys_id> — the "My Submission" tab shows the Record Producer variables read-only (standard OOB behavior of the Standard Ticket framework).
My requirement is:
- Make those variables editable only when the target record State = New (1).
- On Save, update both:
- the Record Producer variable answers (question_answer), and
- the mapped fields on the target record (e.g. incident.urgency, incident.short_description).
- In any other state, keep everything read-only, exactly like OOB.
Constraint: the business does not want new ACLs, UI Policies, or Client Scripts — editability should be driven by record state on the server.
What I've built so far (and it's working in DEV)
I avoided touching OOB widgets. Instead I cloned two widgets:
- Cloned std_ticket_tab → in the variable_editor case, I branch on state and load either my editable clone or the OOB read-only widget:
case 'variable_editor':
case 'variable_summary':
if (record.variables.getElements(true).length == 0)
return;
var widgetParams = tabConfig.widget_param ? JSON.parse(tabConfig.widget_param) : {};
widgetParams.table = data.table;
widgetParams.sys_id = data.sys_id;if (record.getValue('state') == 1) { // New -> editable
widgetParams.readonly_variable_editor = 'false';
tab.widgetId = 'custom-sp-variable-editor';
} else { // other states -> OOB read-only
tab.widgetId = tabConfig.widget;
}
tab.widgetParams = widgetParams;
break;
- Cloned the OOB SC variable editor (custom-sp-variable-editor). In the GET branch I drive read-only from state and set the flag explicitly on each field:
var editableByState = (gr.isValidField('state') && gr.getValue('state') == 1);
var isQuestionReadOnly = (options.readonly_variable_editor == 'true') || !editableByState;for (var f in data.sc_cat_item._fields) {
data.sc_cat_item._fields[f].sys_readonly = isQuestionReadOnly;
data.sc_cat_item._fields[f].readonly = isQuestionReadOnly;
// ...OOB value population unchanged
}
And in the SAVE (input) branch I keep $sp.saveVariables() and add a second write to the mapped target fields, using the declarative mapping on item_option_new (map_to_field = true, field):
// after $sp.saveVariables(input.table, input.sys_id, vars);
var producerId = (input.sc_cat_item && input.sc_cat_item.sys_id)
? input.sc_cat_item.sys_id
: getProducerId(input.table, input.sys_id); // sc_item_produced_record.record_key -> producer
syncMappedFields(input.table, input.sys_id, producerId, fields);
function syncMappedFields(table, sysId, producerId, fields) {
if (!producerId || !fields) return;
var target = new GlideRecord(table);
if (!target.get(sysId) || !target.canWrite()) return;
if (target.isValidField('state') && target.getValue('state') != '1') return; // safeguard
var io = new GlideRecord('item_option_new');
io.addQuery('cat_item', producerId);
io.addQuery('map_to_field', true);
io.query();
var changed = false;
while (io.next()) {
var vName = io.getValue('name');
var col = io.getValue('field');
if (!col || !target.isValidField(col)) continue;
if (fields[vName] && typeof fields[vName].value != 'undefined') {
target.setValue(col, fields[vName].value);
changed = true;
}
}
if (changed) target.update();
}
Two things that tripped me up and are now fixed:
- The tab reuses the OOB widget_param, which carries readonly_variable_editor: "true" — so I had to override it to 'false' in the New branch (otherwise fields stay read-only and the Save button is hidden).
- The OOB loop only ever set sys_readonly = true and never cleared it, and $sp.getCatalogItem() returns fields pre-flagged read-only — so I had to set the boolean explicitly.
My questions
- Is cloning std_ticket_tab + the SC variable editor the recommended, upgrade-safe way to do this in modern ESC, or is there a cleaner supported extension point (e.g. a ticket_tab_configuration custom tab, or a configuration I'm missing) that avoids cloning the tab widget?
- Is writing variable answers with $sp.saveVariables() and then updating mapped target fields with a direct GlideRecord the right pattern, or is there a supported API that updates a produced record's Record Producer answers and its mapped fields together?
- For the variable → field mapping, is relying on item_option_new.map_to_field / field correct? How should I handle producers that map variables only in the producer script (no map_to_field)?
- Any concerns with MRVS, List Collector, reference, or date variables when writing back to mapped columns via setValue()?
- Since I'm intentionally not using ACLs/UI Policies/Client Scripts, is enforcing editability + write security purely server-side (state check + canWrite() at save) considered acceptable/best practice here?
- Anything that could break OOB Employee Center behavior (Activity/Attachments tabs, record watcher, etc.) that I should watch for?
Environment: (add your version, e.g., Washington/Xanadu/Yokohama), Employee Center / Service Portal, Record Producer target table = incident.
Thanks in advance — happy to share the full widget code if useful.
#Employee Center, #Service Portal, #Record Producer, #Standard Ticket, #Variable Editor, #question_answer, #item_opt
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 weeks ago
My thoughts
-> portal is for end users and allowing them to edit the variables are submission is not good practice as there would be some business logic based on that variables such as Flow approvals, tasks etc
-> don't allow them to edit
-> let them cancel and raise a new request with that it helps in tracking and auditing purpose
💡 If my response helped, please mark it as correct ✅ and close the thread 🔒— this helps future readers find the solution faster! 🙏
Ankur
✨ Certified Technical Architect || ✨ 10x ServiceNow MVP || ✨ ServiceNow Community Leader
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 weeks ago
Hi @Leonie1
firstly, you ask if some of the things you would like to do is good practice / upgrade friendly…. Nothing of what your thinking here is good practice. My recommendation - don’t do it…. Secondly, I fully agree with @Ankur Bawiskar
If my answer has helped with your question, please mark my answer as the accepted solution and give a thumbs up.
Best regards
Anders
Rising star 2024
MVP 2025
linkedIn: https://www.linkedin.com/in/andersskovbjerg/
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 weeks ago
you can share the complete widget as this will help future members if needed
Ankur
✨ Certified Technical Architect || ✨ 10x ServiceNow MVP || ✨ ServiceNow Community Leader
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 weeks ago
@Ankur Bawiskar Thanks for your suggestion. I agree that's the recommended approach in most cases. However, our requirement is to allow updates only when the request is in an "New" state. This avoids creating duplicate requests while preserving the existing request context. All updates will be controlled and audited to maintain traceability.