Join the #BuildWithBuildAgent Challenge! Get recognized, earn exclusive swag, and inspire the ServiceNow Community with what you can build using Build Agent.  Join the Challenge.

Is there any way for a Record Producer item to not create a record?

tahnalos
Kilo Sage

We currently have a Record Producer that creates a process entry in a customized table.  This table was created with the intention that the process entry can be updated at any time.  Our service catalog currently sets this up via a record producer which is fine initially, but if the process entry needs to be modified, we want the ability to update it if needed.  While we can do the scriptwork to bring up the already created entry using Catalog Client scripts, we are wondering how do we not create a new record but to update one instead.  In other words, we want to make sure that the Producer part does not create an entry in this specific scenario.

 

Any ideas?

10 REPLIES 10

MaxMixali
Giga Guru

ServiceNow – Update Instead of Insert from a Record Producer (Prevent extra record creation)

Scenario
You use a Record Producer (RP) to create a record in a custom table (e.g., x_app_process). Sometimes you want the RP to UPDATE an existing record rather than INSERT a new one. How can you prevent the RP from creating a new record in that specific scenario?

Key facts about Record Producers
- The RP runs a server-side Script with 'current' pointing at the target table.
- After the Script runs, the platform will INSERT 'current' by default.
- You can override this by explicitly updating another record and telling the platform to ABORT the insert, then redirect.

Recommended patterns
1) Single‑script “upsert” in the Record Producer Script (simplest)
2) BEFORE INSERT Business Rule to intercept RP inserts and switch to update (safer governance)
3) Stop using RP for updates → use a Catalog Item + Flow/Script Action (no auto‑insert)

Pattern 1 – Upsert directly in the Record Producer Script
Use a hidden variable to carry the target sys_id (or a natural key). If present → update that record, abort the RP insert, redirect to the updated record. Otherwise proceed with normal insert.

Example RP Script:
(function() {
// Hidden variables from the RP (add these to your producer variables)
var updateMode = (producer.u_update_mode == 'true'); // checkbox or yes/no
var existingId = (producer.u_existing_sys_id || '').trim(); // sys_id of the record to update

if (updateMode && existingId) {
var gr = new GlideRecord('x_app_process');
if (gr.get(existingId)) {
// Map variables to fields
gr.setValue('short_description', producer.u_short_description);
gr.setValue('u_field_a', producer.u_field_a);
gr.setValue('u_field_b', producer.u_field_b);
gr.update();

// Prevent the producer from inserting its own 'current' record
current.setAbortAction(true);

// Redirect to the updated record (optional)
producer.redirect = true;
producer.setRedirectURL(gr);
gs.addInfoMessage('Updated existing process: ' + gr.getDisplayValue());
return;
} else {
gs.addErrorMessage('Unable to update: record not found.');
// Fall through to normal insert if you prefer, or abort here too
}
}

// --- Create path (no update requested) ---
// Map variables into 'current' as usual; the platform will insert it.
current.setValue('short_description', producer.u_short_description);
current.setValue('u_field_a', producer.u_field_a);
current.setValue('u_field_b', producer.u_field_b);
// No need to call current.insert(); RP will insert automatically
})();

Notes
- current.setAbortAction(true) stops the default insert of the producer’s target record.
- producer.redirect + producer.setRedirectURL(gr) sends the user to the updated record (optional).
- If you don’t have a sys_id, query by a unique key (e.g., code + version) and update the first match.

Pattern 2 – BEFORE INSERT Business Rule (BR) on the target table
If you want tighter control and audit, create a BR to intercept producer inserts and switch to update based on a flag or key.

Condition ideas (any combination):
- current.u_update_mode == true (field populated by the RP via a hidden variable field mapped to the table)
- current.u_existing_sys_id IS NOT EMPTY
- current.u_natural_key matches an existing record

BR Script (Before Insert on x_app_process):
(function executeRule(current, previous /*null when async*/) {
// If not in update mode, do nothing
if (!current.u_update_mode) return;

var targetId = current.u_existing_sys_id + '';
var gr = new GlideRecord('x_app_process');
var found = false;

if (targetId && gr.get(targetId)) {
found = true;
} else if (current.u_natural_key) {
gr.addQuery('u_natural_key', current.u_natural_key);
gr.setLimit(1);
gr.query();
found = gr.next();
}

if (found) {
// Copy values from 'current' to the real record and update
gr.short_description = current.short_description;
gr.u_field_a = current.u_field_a;
gr.u_field_b = current.u_field_b;
gr.update();

// Stop the RP insert
current.setAbortAction(true);

// Optionally pass info message (visible on classic UI; for portal use UI scripts/messages)
gs.addInfoMessage('Updated existing process: ' + gr.getDisplayValue());
}
})(current, previous);

Notes
- This guarantees no unintended inserts, even if someone calls the RP directly.
- Make sure the hidden fields (u_update_mode, u_existing_sys_id, u_natural_key) are mapped by RP into table fields so the BR can read them.

Pattern 3 – Use a Catalog Item + Flow/Action (no auto‑insert at all)
If update‑vs‑insert is always conditional, consider switching to a **Catalog Item** instead of a Record Producer:
- Catalog Items do not auto‑insert into your target table.
- Use a Flow Designer flow or Script Action to:
- Look up existing record by sys_id or key.
- If found → update; else → create.
- This approach avoids having to abort inserts and is cleaner for complex logic.

UX – opening the existing record for edit in Portal
If you want the user to literally edit the existing record (not resubmit a producer):
- Use the Service Portal **spRecord** or **spForm** widget to display the existing record form in the portal.
- Provide an “Edit Process” button that routes to /sp?id=form&table=x_app_process&sys_id=<existingId>.
- This eliminates the need for a Record Producer on the update path.

Gotchas & tips
- Don’t call current.insert() in RP script; the platform does it. Call current.setAbortAction(true) when you’ve handled updates yourself.
- For Service Portal messages, use `spModal`/toasts or redirect with URL parameters; classic gs.addInfoMessage won’t show in portal unless you surface it.
- Ensure security (ACLs): the user must be allowed to update the target record when using update path.
- If multiple matches on natural key, define a unique index or handle it explicitly.

TL;DR
- Use a hidden flag + sys_id/natural key to decide update vs create.
- Update the existing record yourself, then call **current.setAbortAction(true)** to prevent the RP insert and optionally redirect to the updated record.
- For complex scenarios, replace the Record Producer with a Catalog Item + Flow to avoid auto‑insert entirely.