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 hours 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 = 'iris-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