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