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.

Create RITM through script in CSM scope

jainankit07
Tera Contributor

I have a requirement where I need to create a RITM on insert of a Case.
I have a Business Rule which in turn is calling a script include
and script include has below code:

createRitmFromCase: function(caseGr) {

        var catItem;

        var catSrch = new GlideRecord("sn_customerservice_case_itsm_decision");
        catSrch.addQuery("u_category", caseGr.category);
        catSrch.query();
        if (catSrch.next()) {
            catItem = catSrch.u_catalog_item;
        }

        var cart = new sn_sc.CartJS();
        var item = {
            'sysparm_id': catItem,
            'sysparm_quantity': '1',
            'variables': {
                'customer_name': caseGr.contact
            }
        };
gs.info("Testing, item created);
 
        var cartVals = cart.addToCart(item);
        var checkoutVals = cart.checkoutCart();
gs.info("Testing, RITM created);
          }
});

BR is calling the function and infact first part of the code is also working fine where I am gliding a table to get the sys_id of catalog item.
It's the new sn_sc.CartJS API which seems to be creating issue.
I am getting the log till - gs.info("Testing, item created);
I am not getting last log.

If somebody has previously done this or knows how to do it, please assist.


1 ACCEPTED SOLUTION

MaxMixali
Kilo Sage

ServiceNow – Create RITM on Case Insert using sn_sc.CartJS (Working Pattern)

Your symptoms
- You see the log “Testing, item created” but not “Testing, RITM created”.
- The CartJS step is likely erroring out (and your gs.info lines also have syntax issues).

Top 8 pitfalls that usually break CartJS from a Business Rule / Script Include
1) Syntax errors in logs
- You have missing closing quotes: gs.info("Testing, item created); and gs.info("Testing, RITM created);
2) Wrong execution context
- CartJS executes as the current session user. If your BR is async/system, it may lack catalog access or requester context. Prefer AFTER INSERT, synchronous.
3) Missing catalog item or wrong sys_id type
- Ensure catItem is a string (sys_id), not a GlideElement. Use catItem = String(catSrch.getValue('u_catalog_item'));
4) Variables must use the **internal variable names**
- E.g., 'customer_name' must match the variable Name on the catalog item (not the label).
5) Required variables not provided
- If the item has mandatory variables, CartJS will fail. Provide all required ones or default them.
6) Requested for / cart user context
- If needed, set 'sysparm_requested_for' and/or impersonate the case contact user when creating the cart.
7) Use try/catch and check return objects
- CartJS returns objects with IDs; log them and handle failures.
😎 Scope / API availability
- Ensure plugin com.glideapp.servicecatalog and API sn_sc.CartJS are accessible in your scope. If not, use GlideRecord insert on sc_cart_item + sc_req_item via sn_sc.CartJS only.

Recommended, robust pattern
- Business Rule (Table: sn_customerservice_case) – When: after insert – Condition: your criteria – Calls Script Include function.
- Script Include (server-side, not client-callable).

Sample Script Include function (drop-in)
---------------------------------------
createRitmFromCase: function(caseGr) {
// 0) Guard: find catalog item
var catId = '';
var catSrch = new GlideRecord('sn_customerservice_case_itsm_decision');
catSrch.addQuery('u_category', caseGr.getValue('category'));
catSrch.query();
if (catSrch.next()) {
catId = String(catSrch.getValue('u_catalog_item'));
}
if (!catId) {
gs.error('RITM create: No catalog item found for category=' + caseGr.getDisplayValue('category'));
return null;
}

try {
gs.info('RITM create: building cart for case ' + caseGr.getValue('number') + ', catItem=' + catId);

// 1) Prepare item payload
var vars = {
// Use variable INTERNAL NAMES here:
'customer_name': caseGr.getDisplayValue('contact') || caseGr.getValue('contact'),
// add other required vars...
};

var item = {
'sysparm_id': catId,
'sysparm_quantity': '1',
'variables': vars,
// optionally set who it's for:
'sysparm_requested_for': caseGr.getValue('opened_for') || gs.getUserID()
};

// 2) Create cart and add item
var cart = new sn_sc.CartJS();
var addRes = cart.addToCart(item);
// addRes may include 'sys_id' of sc_cart_item and 'cart_id'
gs.info('RITM create: addToCart result = ' + new global.JSON().encode(addRes));

// 3) Checkout / place order
var orderRes = cart.checkoutCart(); // places the order for current user context
gs.info('RITM create: checkout result = ' + new global.JSON().encode(orderRes));

// orderRes typically includes: number, request_id (REQ), ritm_ids (array)
if (orderRes && orderRes.ritm_ids && orderRes.ritm_ids.length > 0) {
gs.info('RITM create: created RITMs = ' + orderRes.ritm_ids.join(','));
// Optionally relate Case↔RITM:
try {
var ritmGR = new GlideRecord('sc_req_item');
if (ritmGR.get(orderRes.ritm_ids[0])) {
// e.g., make a related record
caseGr.u_ritm = ritmGR.getUniqueValue(); // if you have a field to store the link
caseGr.update();
}
} catch (e2) {
gs.warn('RITM create: could not link case to RITM: ' + e2.message);
}
return orderRes;
} else {
gs.error('RITM create: checkout returned no RITM IDs');
return null;
}
} catch (e) {
gs.error('RITM create: exception: ' + e.message);
return null;
}
}

Key notes
- Fix your logging lines (close quotes).
- Ensure the BR runs AFTER INSERT and not async for the first pass. If you must run async, confirm the user context has catalog access.
- If you need the RITM to be created as the Case’s contact or opened_for, consider impersonating for the operation:
```js
var sess = gs.getSession();
var original = gs.getUserID();
if (caseGr.opened_for) sess.impersonate(caseGr.opened_for);
// run CartJS here
sess.impersonate(original); // restore
```
- If checkoutCart() is blocked by required variables, supply them all or set defaults at the item level.

Minimal fix to your original snippet
------------------------------------
- Close the quotes on gs.info lines.
- Convert u_catalog_item to string sys_id and verify variable names.

Example:
var catItem = String(catSrch.getValue('u_catalog_item'));
var cart = new sn_sc.CartJS();
var item = {
'sysparm_id': catItem,
'sysparm_quantity': '1',
'variables': {
'customer_name': caseGr.getDisplayValue('contact')
}
};
gs.info('Testing, item created');
var cartVals = cart.addToCart(item);
var checkoutVals = cart.checkoutCart();
gs.info('Testing, RITM created');

Troubleshooting checklist
- Confirm catItem sys_id exists and is Active.
- Confirm the current execution user has permission to order that item.
- Check if the item requires additional variables (UI Policy/mandatory).
- Use System Logs → All to capture CartJS errors (they are server-side).
- Temporarily wrap with try/catch and gs.error(e.message) for visibility.

View solution in original post

8 REPLIES 8

MaxMixali
Kilo Sage

ServiceNow – Create a RITM on Case insert (use server-side Cart API, not CartJS)

Problem
You’re calling sn_sc.CartJS() from a Business Rule / Script Include to create a RITM when a Case is inserted. You see the first gs.info but not the final one. Root causes:
1) CartJS is a client-side (portal) facade; in server-side code use sn_sc.Cart().
2) Minor syntax issue in your sample (missing closing quotes in gs.info lines).
3) Variable values must be passed as strings/sys_ids appropriate to the variable type.

Correct approach (server-side)
Use the server-side Cart API: sn_sc.Cart(). This creates REQ/RITM in one transaction.

When to run
- Business Rule on sn_customerservice_case (or your case table)
- When: AFTER INSERT (or AFTER INSERT/UPDATE with guard conditions)
- Synchronous or Async? Prefer Async for external calls; for straight creation, After Insert is fine.

Script Include function (drop-in)
---------------------------------
createRitmFromCase: function(caseGr) {
// 1) Resolve catalog item by your mapping table
var catItemId = '';
var catSrch = new GlideRecord('sn_customerservice_case_itsm_decision');
catSrch.addQuery('u_category', caseGr.getValue('category'));
catSrch.setLimit(1);
catSrch.query();
if (catSrch.next()) {
catItemId = catSrch.getValue('u_catalog_item'); // sc_cat_item.sys_id
}
if (!catItemId) {
gs.warn('No catalog item mapping found for category=' + caseGr.getDisplayValue('category'));
return;
}

// 2) Build variable hash (keys must be variable "Name" values on the cat item)
var vars = {};
// Example variable "customer_name" expects a reference to sys_user or a string (adjust to your variable type)
// If it is a reference to sys_user and caseGr.contact is a reference, pass sys_id:
vars.customer_name = caseGr.getValue('contact'); // sys_id
// Add more variables as needed, e.g.:
// vars.case_number = caseGr.getValue('number');
// vars.case_short_description = caseGr.getValue('short_description');

gs.info('Creating RITM from Case ' + caseGr.getDisplayValue('number') + ' for item ' + catItemId);

// 3) Create item under a new Request (Cart API always creates a new REQ)
var cart = new sn_sc.Cart();
var itemSysId = cart.addItem(catItemId, 1, vars);

// (Optional) set Requested For / special fields by updating the RITM before checkout
// var ritm = new GlideRecord('sc_req_item');
// if (ritm.get(itemSysId)) {
// ritm.requested_for = caseGr.getValue('contact') || caseGr.getValue('opened_by');
// ritm.short_description = 'Auto-created for Case ' + caseGr.getValue('number');
// ritm.update();
// }

var reqSysId = cart.placeOrder();

gs.info('RITM created from Case ' + caseGr.getValue('number') + ' → REQ=' + reqSysId + ', RITM item_sys_id=' + itemSysId);
}

Business Rule (After Insert on Case)
------------------------------------
Table: sn_customerservice_case (or your extension)
When: after insert
Filter condition: (category IS NOT EMPTY) AND (whatever your business condition is)

Script:
(function executeRule(current, previous /*null when async*/) {
try {
new x_your_scope.YourUtils().createRitmFromCase(current);
} catch (e) {
gs.error('Failed to create RITM from Case ' + current.getValue('number') + ': ' + e);
}
})(current, previous);

Important details & tips
------------------------
1) Use sn_sc.Cart() (server-side). CartJS is for client/portal via GlideAjax.
2) Variable keys must match the variable “Name” on the target catalog item (not the label).
3) Pass correct value types:
- Reference variable → pass sys_id string
- Choice variable → pass the underlying value (not display)
- Checkbox → 'true'/'false' (string) or true/false (boolean)
- Date/Time → GlideDateTime-compatible string
4) User permissions: the executing user must have rights to order that item. In BR context, the system runs as that session user. If needed, wrap in gs.getSession().impersonate('some_user') or use GlideSystem elevated privileges (with caution).
5) Availability: ensure the sc_cat_item is Active, visible to the user, and in an orderable catalog/category.
6) If you must attach the new RITM to an existing REQ (e.g., one REQ per Case), the Cart API cannot do that; you’d have to create sc_req_item yourself and populate variable rows (sc_item_option_mtom). Cart always creates a new REQ on checkout.
7) Logging: ensure gs.info lines have closing quotes (your sample had syntax errors):
gs.info('Testing, item created');
gs.info('Testing, RITM created');

Troubleshooting checklist
-------------------------
- Error in system logs about “CartJS” or missing class → switch to sn_sc.Cart().
- No RITM created: check item availability & user permissions; verify mapping table returns a valid cat item sys_id.
- Variables blank: ensure variable names match and values are valid types; verify in sc_item_option_mtom after order.
- Multiple items per case: call cart.addItem repeatedly before cart.placeOrder() to create a single REQ with multiple RITMs.

TL;DR
-----
Don’t use CartJS on the server. Use the server-side **sn_sc.Cart()** to add the item + variables and **placeOrder()**. Ensure variable names/values match your catalog item definitions and that the executing context has permission to order the item.

MaxMixali
Kilo Sage

ServiceNow – Create RITM on Case Insert using sn_sc.CartJS (Working Pattern)

Your symptoms
- You see the log “Testing, item created” but not “Testing, RITM created”.
- The CartJS step is likely erroring out (and your gs.info lines also have syntax issues).

Top 8 pitfalls that usually break CartJS from a Business Rule / Script Include
1) Syntax errors in logs
- You have missing closing quotes: gs.info("Testing, item created); and gs.info("Testing, RITM created);
2) Wrong execution context
- CartJS executes as the current session user. If your BR is async/system, it may lack catalog access or requester context. Prefer AFTER INSERT, synchronous.
3) Missing catalog item or wrong sys_id type
- Ensure catItem is a string (sys_id), not a GlideElement. Use catItem = String(catSrch.getValue('u_catalog_item'));
4) Variables must use the **internal variable names**
- E.g., 'customer_name' must match the variable Name on the catalog item (not the label).
5) Required variables not provided
- If the item has mandatory variables, CartJS will fail. Provide all required ones or default them.
6) Requested for / cart user context
- If needed, set 'sysparm_requested_for' and/or impersonate the case contact user when creating the cart.
7) Use try/catch and check return objects
- CartJS returns objects with IDs; log them and handle failures.
😎 Scope / API availability
- Ensure plugin com.glideapp.servicecatalog and API sn_sc.CartJS are accessible in your scope. If not, use GlideRecord insert on sc_cart_item + sc_req_item via sn_sc.CartJS only.

Recommended, robust pattern
- Business Rule (Table: sn_customerservice_case) – When: after insert – Condition: your criteria – Calls Script Include function.
- Script Include (server-side, not client-callable).

Sample Script Include function (drop-in)
---------------------------------------
createRitmFromCase: function(caseGr) {
// 0) Guard: find catalog item
var catId = '';
var catSrch = new GlideRecord('sn_customerservice_case_itsm_decision');
catSrch.addQuery('u_category', caseGr.getValue('category'));
catSrch.query();
if (catSrch.next()) {
catId = String(catSrch.getValue('u_catalog_item'));
}
if (!catId) {
gs.error('RITM create: No catalog item found for category=' + caseGr.getDisplayValue('category'));
return null;
}

try {
gs.info('RITM create: building cart for case ' + caseGr.getValue('number') + ', catItem=' + catId);

// 1) Prepare item payload
var vars = {
// Use variable INTERNAL NAMES here:
'customer_name': caseGr.getDisplayValue('contact') || caseGr.getValue('contact'),
// add other required vars...
};

var item = {
'sysparm_id': catId,
'sysparm_quantity': '1',
'variables': vars,
// optionally set who it's for:
'sysparm_requested_for': caseGr.getValue('opened_for') || gs.getUserID()
};

// 2) Create cart and add item
var cart = new sn_sc.CartJS();
var addRes = cart.addToCart(item);
// addRes may include 'sys_id' of sc_cart_item and 'cart_id'
gs.info('RITM create: addToCart result = ' + new global.JSON().encode(addRes));

// 3) Checkout / place order
var orderRes = cart.checkoutCart(); // places the order for current user context
gs.info('RITM create: checkout result = ' + new global.JSON().encode(orderRes));

// orderRes typically includes: number, request_id (REQ), ritm_ids (array)
if (orderRes && orderRes.ritm_ids && orderRes.ritm_ids.length > 0) {
gs.info('RITM create: created RITMs = ' + orderRes.ritm_ids.join(','));
// Optionally relate Case↔RITM:
try {
var ritmGR = new GlideRecord('sc_req_item');
if (ritmGR.get(orderRes.ritm_ids[0])) {
// e.g., make a related record
caseGr.u_ritm = ritmGR.getUniqueValue(); // if you have a field to store the link
caseGr.update();
}
} catch (e2) {
gs.warn('RITM create: could not link case to RITM: ' + e2.message);
}
return orderRes;
} else {
gs.error('RITM create: checkout returned no RITM IDs');
return null;
}
} catch (e) {
gs.error('RITM create: exception: ' + e.message);
return null;
}
}

Key notes
- Fix your logging lines (close quotes).
- Ensure the BR runs AFTER INSERT and not async for the first pass. If you must run async, confirm the user context has catalog access.
- If you need the RITM to be created as the Case’s contact or opened_for, consider impersonating for the operation:
```js
var sess = gs.getSession();
var original = gs.getUserID();
if (caseGr.opened_for) sess.impersonate(caseGr.opened_for);
// run CartJS here
sess.impersonate(original); // restore
```
- If checkoutCart() is blocked by required variables, supply them all or set defaults at the item level.

Minimal fix to your original snippet
------------------------------------
- Close the quotes on gs.info lines.
- Convert u_catalog_item to string sys_id and verify variable names.

Example:
var catItem = String(catSrch.getValue('u_catalog_item'));
var cart = new sn_sc.CartJS();
var item = {
'sysparm_id': catItem,
'sysparm_quantity': '1',
'variables': {
'customer_name': caseGr.getDisplayValue('contact')
}
};
gs.info('Testing, item created');
var cartVals = cart.addToCart(item);
var checkoutVals = cart.checkoutCart();
gs.info('Testing, RITM created');

Troubleshooting checklist
- Confirm catItem sys_id exists and is Active.
- Confirm the current execution user has permission to order that item.
- Check if the item requires additional variables (UI Policy/mandatory).
- Use System Logs → All to capture CartJS errors (they are server-side).
- Temporarily wrap with try/catch and gs.error(e.message) for visibility.

I checked, and just converted my passed sys_id's to string and it worked.
But, because of this sn_sc.CartJS method, my case creation is all hampered.
I am using a record producer to create case, and a BR is running on case creation inside which I have called a script include with final code as below.
It is causing creation of 70-100 ce and RITMs.
Please assist.




 

createRitmFromCase: function(caseGr) {

        var catItem;

        var catSrch = new GlideRecord("sn_customerservice_case_itsm_decision");
        catSrch.addQuery("u_category", caseGr.category);
        catSrch.query();
        if (catSrch.next()) {
            catItem = catSrch.u_catalog_item.toString();
        }
        var cart = new sn_sc.CartJS();
        var item = {
            'sysparm_id': catItem,
            'sysparm_quantity': '1',
            'variables': {
                'customer_name': caseGr.contact.toString(),
                'approving_data_steward': caseGr.u_approving_data_steward.toString(),
                'organization': caseGr.account.toString(),
                'u_organization_erp': caseGr.u_organization_erp.toString(),
                'category': caseGr.category.getValue(),
                'subcategory': caseGr.subcategory.getValue(),
                'u_priority': caseGr.u_priority.getValue(),
                'u_line_items_included': caseGr.u_line_items_included.getValue(),
                'u_impacted_organization_s': caseGr.u_impacted_organization_s.toString(),
                'notes': caseGr.short_description.getValue(),
                'u_department': caseGr.u_department.getValue()
            }
        };

        var cartVals = cart.addToCart(item);
        var checkoutVals = cart.checkoutCart();

jainankit07
Tera Contributor

Thanks everyone or answering, issue is resolved now, it was stuck as I was directly passing the object without converting to string.