Skip to Content

Dynamic Catalog Item Helper

Dynamically create service catalog items with variable sets based on configuration, enabling template-driven catalog management and bulk catalog operations.

#script-include #catalog #dynamic-creation #automation #variables #bulk-operations

Script Code

JavaScript
1var DynamicCatalogHelper = Class.create();
2DynamicCatalogHelper.prototype = {
3    initialize: function() {
4        this.defaultCategory = 'e15706fc0a0a0aa7007fc21e1ab70c2f'; // IT Services
5        this.defaultCatalog = 'e0d08b13c3330100c8b837659bba8fb4'; // Service Catalog
6    },
7
8    /**
9     * Create a new catalog item from template
10     * @param {Object} config - Configuration object
11     * @returns {String} sys_id of created catalog item or null if error
12     */
13    createCatalogItem: function(config) {
14        try {
15            // Validate required configuration
16            if (!config.name || !config.short_description) {
17                gs.error('DynamicCatalogHelper: name and short_description are required');
18                return null;
19            }
20
21            var grCatItem = new GlideRecord('sc_cat_item');
22            grCatItem.initialize();
23
24            // Set basic properties
25            grCatItem.name = config.name;
26            grCatItem.short_description = config.short_description;
27            grCatItem.description = config.description || config.short_description;
28            grCatItem.category = config.category || this.defaultCategory;
29            grCatItem.sc_catalogs = config.catalog || this.defaultCatalog;
30
31            // Set item properties
32            grCatItem.active = config.active !== undefined ? config.active : true;
33            grCatItem.available_for = config.available_for || 'employee';
34            grCatItem.type = config.type || 'item';
35
36            // Set workflow and fulfillment
37            grCatItem.workflow = config.workflow || '';
38            grCatItem.flow_designer_flow = config.flow || '';
39
40            // Set ordering and display
41            grCatItem.order = config.order || 100;
42            grCatItem.picture = config.picture || '';
43            grCatItem.icon = config.icon || '';
44
45            // Set request settings
46            grCatItem.request_method = config.request_method || '';
47            grCatItem.template = config.template || '';
48
49            // Set access controls
50            grCatItem.roles = config.roles || '';
51            grCatItem.user_criteria = config.user_criteria || '';
52
53            // Set custom fields if provided
54            if (config.custom_fields) {
55                for (var field in config.custom_fields) {
56                    if (grCatItem.isValidField(field)) {
57                        grCatItem.setValue(field, config.custom_fields[field]);
58                    }
59                }
60            }
61
62            var catItemId = grCatItem.insert();
63
64            if (catItemId) {
65                gs.info('DynamicCatalogHelper: Created catalog item - ' + config.name + ' (' + catItemId + ')');
66
67                // Add variable sets if specified
68                if (config.variable_sets && config.variable_sets.length > 0) {
69                    this.addVariableSets(catItemId, config.variable_sets);
70                }
71
72                // Create custom variables if specified
73                if (config.variables && config.variables.length > 0) {
74                    this.createVariables(catItemId, config.variables);
75                }
76
77                return catItemId;
78            }
79
80            return null;
81        } catch (e) {
82            gs.error('DynamicCatalogHelper.createCatalogItem error: ' + e.message);
83            return null;
84        }
85    },
86
87    /**
88     * Add variable sets to catalog item
89     * @param {String} catItemId - Catalog item sys_id
90     * @param {Array} variableSets - Array of variable set sys_ids or names
91     */
92    addVariableSets: function(catItemId, variableSets) {
93        try {
94            variableSets.forEach(function(setId) {
95                var grVarSet = new GlideRecord('io_set_item');
96                grVarSet.initialize();
97                grVarSet.sc_cat_item = catItemId;
98
99                // Check if setId is a sys_id or name
100                if (setId.length === 32) {
101                    grVarSet.variable_set = setId;
102                } else {
103                    // Look up variable set by name
104                    var grLookup = new GlideRecord('item_option_new_set');
105                    grLookup.addQuery('title', setId);
106                    grLookup.setLimit(1);
107                    grLookup.query();
108                    
109                    if (grLookup.next()) {
110                        grVarSet.variable_set = grLookup.sys_id;
111                    } else {
112                        gs.warn('Variable set not found: ' + setId);
113                        return;
114                    }
115                }
116
117                grVarSet.insert();
118                gs.info('Added variable set to catalog item: ' + setId);
119            });
120        } catch (e) {
121            gs.error('DynamicCatalogHelper.addVariableSets error: ' + e.message);
122        }
123    },
124
125    /**
126     * Create custom variables for catalog item
127     * @param {String} catItemId - Catalog item sys_id
128     * @param {Array} variables - Array of variable definitions
129     */
130    createVariables: function(catItemId, variables) {
131        try {
132            var order = 100;
133            
134            variables.forEach(function(varConfig) {
135                var grVariable = new GlideRecord('item_option_new');
136                grVariable.initialize();
137
138                // Set basic properties
139                grVariable.cat_item = catItemId;
140                grVariable.name = varConfig.name;
141                grVariable.question_text = varConfig.question_text || varConfig.name;
142                grVariable.type = varConfig.type || 'string';
143                grVariable.order = varConfig.order || order;
144
145                // Set validation and requirements
146                grVariable.mandatory = varConfig.mandatory || false;
147                grVariable.read_only = varConfig.read_only || false;
148                grVariable.active = varConfig.active !== undefined ? varConfig.active : true;
149
150                // Set display properties
151                grVariable.default_value = varConfig.default_value || '';
152                grVariable.help_text = varConfig.help_text || '';
153                grVariable.tooltip = varConfig.tooltip || '';
154
155                // Set type-specific properties
156                switch (varConfig.type) {
157                    case 'select_box':
158                    case 'radio':
159                        grVariable.list_table = varConfig.list_table || '';
160                        grVariable.lookup_select_window = varConfig.lookup_select_window || false;
161                        break;
162                    case 'reference':
163                        grVariable.reference = varConfig.reference_table || '';
164                        grVariable.reference_qual = varConfig.reference_qualifier || '';
165                        break;
166                    case 'string':
167                    case 'multi_line_text':
168                        grVariable.max_length = varConfig.max_length || '';
169                        break;
170                    case 'integer':
171                    case 'decimal':
172                        grVariable.min_length = varConfig.min_value || '';
173                        grVariable.max_length = varConfig.max_value || '';
174                        break;
175                }
176
177                var variableId = grVariable.insert();
178
179                if (variableId && varConfig.choices) {
180                    this.createChoices(variableId, varConfig.choices);
181                }
182
183                order += 100;
184            }.bind(this));
185        } catch (e) {
186            gs.error('DynamicCatalogHelper.createVariables error: ' + e.message);
187        }
188    },
189
190    /**
191     * Create choices for a variable
192     * @param {String} variableId - Variable sys_id
193     * @param {Array} choices - Array of choice objects
194     */
195    createChoices: function(variableId, choices) {
196        try {
197            var order = 100;
198            
199            choices.forEach(function(choice) {
200                var grChoice = new GlideRecord('question_choice');
201                grChoice.initialize();
202
203                grChoice.question = variableId;
204                grChoice.text = choice.text;
205                grChoice.value = choice.value || choice.text;
206                grChoice.order = choice.order || order;
207                grChoice.inactive = choice.inactive || false;
208
209                grChoice.insert();
210                order += 100;
211            });
212        } catch (e) {
213            gs.error('DynamicCatalogHelper.createChoices error: ' + e.message);
214        }
215    },
216
217    /**
218     * Bulk create catalog items from configuration array
219     * @param {Array} configs - Array of catalog item configurations
220     * @returns {Array} Array of created sys_ids
221     */
222    bulkCreateCatalogItems: function(configs) {
223        var results = [];
224        
225        configs.forEach(function(config) {
226            var itemId = this.createCatalogItem(config);
227            if (itemId) {
228                results.push({
229                    name: config.name,
230                    sys_id: itemId,
231                    success: true
232                });
233            } else {
234                results.push({
235                    name: config.name,
236                    sys_id: null,
237                    success: false,
238                    error: 'Failed to create catalog item'
239                });
240            }
241        }.bind(this));
242
243        return results;
244    },
245
246    /**
247     * Update catalog item from configuration
248     * @param {String} catItemId - Catalog item sys_id
249     * @param {Object} config - Configuration updates
250     * @returns {Boolean} Success status
251     */
252    updateCatalogItem: function(catItemId, config) {
253        try {
254            var grCatItem = new GlideRecord('sc_cat_item');
255            if (!grCatItem.get(catItemId)) {
256                gs.error('Catalog item not found: ' + catItemId);
257                return false;
258            }
259
260            // Update allowed fields
261            var allowedFields = [
262                'name', 'short_description', 'description', 'category',
263                'active', 'order', 'picture', 'icon', 'workflow',
264                'flow_designer_flow', 'roles', 'user_criteria'
265            ];
266
267            allowedFields.forEach(function(field) {
268                if (config[field] !== undefined) {
269                    grCatItem.setValue(field, config[field]);
270                }
271            });
272
273            // Update custom fields
274            if (config.custom_fields) {
275                for (var field in config.custom_fields) {
276                    if (grCatItem.isValidField(field)) {
277                        grCatItem.setValue(field, config.custom_fields[field]);
278                    }
279                }
280            }
281
282            grCatItem.update();
283            gs.info('Updated catalog item: ' + grCatItem.name);
284            return true;
285
286        } catch (e) {
287            gs.error('DynamicCatalogHelper.updateCatalogItem error: ' + e.message);
288            return false;
289        }
290    },
291
292    /**
293     * Clone catalog item with modifications
294     * @param {String} sourceItemId - Source catalog item sys_id
295     * @param {Object} modifications - Modifications to apply
296     * @returns {String} New catalog item sys_id or null
297     */
298    cloneCatalogItem: function(sourceItemId, modifications) {
299        try {
300            var grSource = new GlideRecord('sc_cat_item');
301            if (!grSource.get(sourceItemId)) {
302                gs.error('Source catalog item not found: ' + sourceItemId);
303                return null;
304            }
305
306            // Create new item with source data
307            var grNew = new GlideRecord('sc_cat_item');
308            grNew.initialize();
309
310            // Copy all fields from source
311            var fields = grSource.getFields();
312            for (var i = 0; i < fields.size(); i++) {
313                var field = fields.get(i);
314                var fieldName = field.getName();
315                
316                // Skip system fields
317                if (fieldName !== 'sys_id' && fieldName !== 'sys_created_on' && 
318                    fieldName !== 'sys_created_by' && fieldName !== 'sys_updated_on' && 
319                    fieldName !== 'sys_updated_by') {
320                    grNew.setValue(fieldName, grSource.getValue(fieldName));
321                }
322            }
323
324            // Apply modifications
325            for (var modField in modifications) {
326                if (grNew.isValidField(modField)) {
327                    grNew.setValue(modField, modifications[modField]);
328                }
329            }
330
331            var newItemId = grNew.insert();
332
333            if (newItemId) {
334                // Clone variables
335                this.cloneVariables(sourceItemId, newItemId);
336                
337                // Clone variable sets
338                this.cloneVariableSets(sourceItemId, newItemId);
339
340                gs.info('Cloned catalog item: ' + grSource.name + ' -> ' + grNew.name);
341            }
342
343            return newItemId;
344
345        } catch (e) {
346            gs.error('DynamicCatalogHelper.cloneCatalogItem error: ' + e.message);
347            return null;
348        }
349    },
350
351    /**
352     * Clone variables from source to target catalog item
353     * @param {String} sourceItemId - Source catalog item
354     * @param {String} targetItemId - Target catalog item
355     */
356    cloneVariables: function(sourceItemId, targetItemId) {
357        try {
358            var grSourceVars = new GlideRecord('item_option_new');
359            grSourceVars.addQuery('cat_item', sourceItemId);
360            grSourceVars.orderBy('order');
361            grSourceVars.query();
362
363            while (grSourceVars.next()) {
364                var grNewVar = new GlideRecord('item_option_new');
365                grNewVar.initialize();
366
367                // Copy all variable fields except sys_id and cat_item
368                var fields = grSourceVars.getFields();
369                for (var i = 0; i < fields.size(); i++) {
370                    var field = fields.get(i);
371                    var fieldName = field.getName();
372                    
373                    if (fieldName !== 'sys_id' && fieldName !== 'cat_item' && 
374                        !fieldName.startsWith('sys_')) {
375                        grNewVar.setValue(fieldName, grSourceVars.getValue(fieldName));
376                    }
377                }
378
379                grNewVar.cat_item = targetItemId;
380                var newVarId = grNewVar.insert();
381
382                // Clone choices for this variable
383                if (newVarId) {
384                    this.cloneChoices(grSourceVars.sys_id, newVarId);
385                }
386            }
387        } catch (e) {
388            gs.error('DynamicCatalogHelper.cloneVariables error: ' + e.message);
389        }
390    },
391
392    /**
393     * Clone variable sets from source to target catalog item
394     * @param {String} sourceItemId - Source catalog item
395     * @param {String} targetItemId - Target catalog item
396     */
397    cloneVariableSets: function(sourceItemId, targetItemId) {
398        try {
399            var grSourceSets = new GlideRecord('io_set_item');
400            grSourceSets.addQuery('sc_cat_item', sourceItemId);
401            grSourceSets.query();
402
403            while (grSourceSets.next()) {
404                var grNewSet = new GlideRecord('io_set_item');
405                grNewSet.initialize();
406                grNewSet.sc_cat_item = targetItemId;
407                grNewSet.variable_set = grSourceSets.variable_set;
408                grNewSet.insert();
409            }
410        } catch (e) {
411            gs.error('DynamicCatalogHelper.cloneVariableSets error: ' + e.message);
412        }
413    },
414
415    /**
416     * Clone choices from source to target variable
417     * @param {String} sourceVarId - Source variable sys_id
418     * @param {String} targetVarId - Target variable sys_id
419     */
420    cloneChoices: function(sourceVarId, targetVarId) {
421        try {
422            var grSourceChoices = new GlideRecord('question_choice');
423            grSourceChoices.addQuery('question', sourceVarId);
424            grSourceChoices.orderBy('order');
425            grSourceChoices.query();
426
427            while (grSourceChoices.next()) {
428                var grNewChoice = new GlideRecord('question_choice');
429                grNewChoice.initialize();
430
431                var fields = grSourceChoices.getFields();
432                for (var i = 0; i < fields.size(); i++) {
433                    var field = fields.get(i);
434                    var fieldName = field.getName();
435                    
436                    if (fieldName !== 'sys_id' && fieldName !== 'question' && 
437                        !fieldName.startsWith('sys_')) {
438                        grNewChoice.setValue(fieldName, grSourceChoices.getValue(fieldName));
439                    }
440                }
441
442                grNewChoice.question = targetVarId;
443                grNewChoice.insert();
444            }
445        } catch (e) {
446            gs.error('DynamicCatalogHelper.cloneChoices error: ' + e.message);
447        }
448    },
449
450    type: 'DynamicCatalogHelper'
451};

How to Use

1. Create a Script Include with Name 'DynamicCatalogHelper'\n2. Set API Name to 'DynamicCatalogHelper'\n3. Check 'Accessible from' checkbox\n4. Copy the code above\n5. Test creation: var helper = new DynamicCatalogHelper(); var itemId = helper.createCatalogItem({name: 'Test Item', short_description: 'Test catalog item'});\n6. Use for bulk catalog operations, template-based creation, and dynamic catalog management\n7. Extend with additional catalog operations as needed

Explore More Scripts

Browse our complete library of ServiceNow scripts

View All Scripts