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.

i want to CSA (servicenow) cerificate with my full name

munukuntlak
Tera Contributor

i want to CSA (servicenow) cerificate with my full name

1 ACCEPTED SOLUTION

Dr Atul G- LNG
Tera Patron
Tera Patron

Is issue still open @munukuntlak 

*************************************************************************************************************
If my response proves useful, please indicate its helpfulness by selecting " Accept as Solution" and " Helpful." This action benefits both the community and me.

Regards
Dr. Atul G. - Learn N Grow Together
ServiceNow Techno - Functional Trainer
LinkedIn: https://www.linkedin.com/in/dratulgrover
YouTube: https://www.youtube.com/@LearnNGrowTogetherwithAtulG
Topmate: https://topmate.io/atul_grover_lng [ Connect for 1-1 Session]

****************************************************************************************************************

View solution in original post

25 REPLIES 25

munukuntlak
Tera Contributor
javascript:
var env = current.variables.database_environment;
var app = current.variables.application_name;
if (!env) {
    return 'sys_idISEMPTY';
}

return 'sys_idIN' + new DB_Provisioning_Util().getDatabases(app, env);

munukuntlak
Tera Contributor
// Background Script - Add Catalog Item to Update Set (Current User + Active Only)
// Using GlideUpdateManager2 (CORRECT METHOD)

(function() {

// CONFIGURATION - Change these values
var catalogItemSysId = 'YOUR_CATALOG_ITEM_SYS_ID'; // or use name
var catalogItemName = ''; // Alternative: use name instead of sys_id
var updateSetName = 'YOUR_UPDATE_SET_NAME'; // Specify your update set name

// Get current user
var currentUser = gs.getUserID();
gs.info('Current User: ' + gs.getUserName() + ' (' + currentUser + ')');

// Find the Update Set by name
var updateSetGR = new GlideRecord('sys_update_set');
updateSetGR.addQuery('name', updateSetName);
updateSetGR.addQuery('state', 'in progress');
updateSetGR.query();

if (!updateSetGR.next()) {
    gs.error('Update Set not found or not in progress: ' + updateSetName);
    return;
}

var updateSetId = updateSetGR.sys_id.toString();
gs.info('Using Update Set: ' + updateSetGR.name + ' (' + updateSetId + ')');

// Set this as the current update set for the session
gs.setCurrentUpdateSetId(updateSetId);

// Find catalog item
var catItem = new GlideRecord('sc_cat_item');
if (catalogItemSysId) {
    catItem.get(catalogItemSysId);
} else if (catalogItemName) {
    catItem.get('name', catalogItemName);
}

if (!catItem.isValidRecord()) {
    gs.error('Catalog Item not found!');
    return;
}

gs.info('Processing Catalog Item: ' + catItem.getDisplayValue());

// CORRECT Function to add record to update set
function addToUpdateSet(tableName, sysId) {
    try {
        var rec = new GlideRecord(tableName);
        if (rec.get(sysId)) {
            var um = new GlideUpdateManager2();
            um.saveRecord(rec);
            gs.info('✓ Added: ' + tableName + ' - ' + rec.getDisplayValue());
            return true;
        }
    } catch (e) {
        gs.error('Error adding ' + tableName + ': ' + e);
    }
    return false;
}

// Helper function to check if record was created/updated by current user
function isCurrentUserRecord(gr) {
    var createdBy = gr.sys_created_by.toString();
    var updatedBy = gr.sys_updated_by.toString();
    return (createdBy == gs.getUserName() || updatedBy == gs.getUserName());
}

// Helper function to check if record is active (if active field exists)
function isActiveRecord(gr) {
    if (gr.isValidField('active')) {
        return gr.active == true || gr.active == 'true';
    }
    return true; // If no active field, consider it active
}

// Helper function to validate record before adding
function shouldAddRecord(gr) {
    return isCurrentUserRecord(gr) && isActiveRecord(gr);
}

// 1. Add the Catalog Item itself (if created/updated by current user and active)
if (shouldAddRecord(catItem)) {
    addToUpdateSet('sc_cat_item', catItem.sys_id);
}

// 2. Add Catalog Assignment (sc_cat_item_catalog)
var catalogAssignments = new GlideRecord('sc_cat_item_catalog');
catalogAssignments.addQuery('sc_cat_item', catItem.sys_id);
catalogAssignments.query();
gs.info('Found ' + catalogAssignments.getRowCount() + ' catalog assignments');
while (catalogAssignments.next()) {
    if (shouldAddRecord(catalogAssignments)) {
        addToUpdateSet('sc_cat_item_catalog', catalogAssignments.sys_id);
    }
}

// 3. Add Catalog Item Variables
var variables = new GlideRecord('item_option_new');
variables.addQuery('cat_item', catItem.sys_id);
variables.query();
gs.info('Found ' + variables.getRowCount() + ' variables');
while (variables.next()) {
    if (shouldAddRecord(variables)) {
        addToUpdateSet('item_option_new', variables.sys_id);
    }
}

// 4. Add Variable Sets (if any)
var varSets = new GlideRecord('io_set_item');
varSets.addQuery('sc_cat_item', catItem.sys_id);
varSets.query();
while (varSets.next()) {
    if (shouldAddRecord(varSets)) {
        addToUpdateSet('io_set_item', varSets.sys_id);

        // Add the variable set itself
        if (varSets.variable_set) {
            var varSetRecord = new GlideRecord('item_option_new_set');
            if (varSetRecord.get(varSets.variable_set) && shouldAddRecord(varSetRecord)) {
                addToUpdateSet('item_option_new_set', varSets.variable_set);
            }

            // Add variables within the set
            var setVars = new GlideRecord('item_option_new');
            setVars.addQuery('variable_set', varSets.variable_set);
            setVars.query();
            while (setVars.next()) {
                if (shouldAddRecord(setVars)) {
                    addToUpdateSet('item_option_new', setVars.sys_id);
                }
            }
        }
    }
}

// 5. Add Catalog UI Policies
var uiPolicies = new GlideRecord('catalog_ui_policy');
uiPolicies.addQuery('catalog_item', catItem.sys_id);
uiPolicies.query();
gs.info('Found ' + uiPolicies.getRowCount() + ' UI Policies');
while (uiPolicies.next()) {
    if (shouldAddRecord(uiPolicies)) {
        addToUpdateSet('catalog_ui_policy', uiPolicies.sys_id);

        // Add UI Policy Actions
        var uiActions = new GlideRecord('catalog_ui_policy_action');
        uiActions.addQuery('ui_policy', uiPolicies.sys_id);
        uiActions.query();
        while (uiActions.next()) {
            if (shouldAddRecord(uiActions)) {
                addToUpdateSet('catalog_ui_policy_action', uiActions.sys_id);
            }
        }
    }
}

// 6. Add Catalog Client Scripts
var clientScripts = new GlideRecord('catalog_script_client');
clientScripts.addQuery('cat_item', catItem.sys_id);
clientScripts.query();
gs.info('Found ' + clientScripts.getRowCount() + ' Client Scripts');
while (clientScripts.next()) {
    if (shouldAddRecord(clientScripts)) {
        addToUpdateSet('catalog_script_client', clientScripts.sys_id);
    }
}

// 7. Add Flow Designer Flow (if attached)
if (catItem.flow_designer_flow) {
    var flowRecord = new GlideRecord('sys_hub_flow');
    if (flowRecord.get(catItem.flow_designer_flow) && shouldAddRecord(flowRecord)) {
        gs.info('Found Flow Designer Flow');
        addToUpdateSet('sys_hub_flow', catItem.flow_designer_flow);

        // Add Flow Actions
        var flowActions = new GlideRecord('sys_hub_action_instance');
        flowActions.addQuery('flow', catItem.flow_designer_flow);
        flowActions.query();
        gs.info('Found ' + flowActions.getRowCount() + ' flow actions');
        while (flowActions.next()) {
            if (shouldAddRecord(flowActions)) {
                addToUpdateSet('sys_hub_action_instance', flowActions.sys_id);
            }
        }

        // Add Flow Variables
        var flowVars = new GlideRecord('sys_variable_value');
        flowVars.addQuery('document', catItem.flow_designer_flow);
        flowVars.addQuery('document_key', 'sys_hub_flow');
        flowVars.query();
        while (flowVars.next()) {
            if (shouldAddRecord(flowVars)) {
                addToUpdateSet('sys_variable_value', flowVars.sys_id);
            }
        }

        // Add Flow Triggers
        var flowTriggers = new GlideRecord('sys_hub_flow_trigger');
        flowTriggers.addQuery('flow', catItem.flow_designer_flow);
        flowTriggers.query();
        while (flowTriggers.next()) {
            if (shouldAddRecord(flowTriggers)) {
                addToUpdateSet('sys_hub_flow_trigger', flowTriggers.sys_id);
            }
        }
    }
}

// 8. Add Catalog Categories relationship
var categories = new GlideRecord('sc_cat_item_category');
categories.addQuery('sc_cat_item', catItem.sys_id);
categories.query();
gs.info('Found ' + categories.getRowCount() + ' category assignments');
while (categories.next()) {
    if (shouldAddRecord(categories)) {
        addToUpdateSet('sc_cat_item_category', categories.sys_id);
    }
}

// 9. Add Record Producers (if it's a record producer)
if (catItem.sys_class_name == 'sc_cat_item_producer') {
    var producer = new GlideRecord('sc_cat_item_producer');
    if (producer.get(catItem.sys_id) && shouldAddRecord(producer)) {
        addToUpdateSet('sc_cat_item_producer', producer.sys_id);
    }
}

// 10. Add Execution Plans (if any)
var execPlans = new GlideRecord('sc_ic_item_staging');
execPlans.addQuery('cat_item', catItem.sys_id);
execPlans.query();
while (execPlans.next()) {
    if (shouldAddRecord(execPlans)) {
        addToUpdateSet('sc_ic_item_staging', execPlans.sys_id);
    }
}

// 11. Add Catalog Item Tasks / Delivery Plans
var tasks = new GlideRecord('sc_cat_item_delivery_plan');
tasks.addQuery('sc_cat_item', catItem.sys_id);
tasks.query();
while (tasks.next()) {
    if (shouldAddRecord(tasks)) {
        addToUpdateSet('sc_cat_item_delivery_plan', tasks.sys_id);
    }
}

// 12. Add Variable Choices (for choice lists)
var choices = new GlideRecord('question_choice');
choices.addQuery('question.cat_item', catItem.sys_id);
choices.query();
gs.info('Found ' + choices.getRowCount() + ' variable choices');
while (choices.next()) {
    if (shouldAddRecord(choices)) {
        addToUpdateSet('question_choice', choices.sys_id);
    }
}

// 13. Add Legacy Workflow (if attached)
if (catItem.workflow) {
    var workflowRecord = new GlideRecord('wf_workflow');
    if (workflowRecord.get(catItem.workflow) && shouldAddRecord(workflowRecord)) {
        gs.info('Found Legacy Workflow');
        addToUpdateSet('wf_workflow', catItem.workflow);

        var wfActivities = new GlideRecord('wf_activity');
        wfActivities.addQuery('workflow', catItem.workflow);
        wfActivities.query();
        while (wfActivities.next()) {
            if (shouldAddRecord(wfActivities)) {
                addToUpdateSet('wf_activity', wfActivities.sys_id);
            }
        }
    }
}

gs.info('===== COMPLETED =====');
gs.info('All catalog item components (created/updated by current user and active) added to update set: ' + updateSetName);

})();

munukuntlak
Tera Contributor
(function execute(inputs, outputs) {
    
    var hasOverlap = false;
    
    try {
        var scheduleSpanRecord = inputs.schedule_span_record;
        
        if (!scheduleSpanRecord) {
            outputs.has_overlap = false;
            return;
        }

        var newStartTime = new GlideDateTime();
        newStartTime.setDisplayValue(inputs.start_time_to_compare);
        var newEndTime = new GlideDateTime();
        newEndTime.setDisplayValue(inputs.end_time_to_compare);
        var newDaysNumeric = inputs.days_of_week || '';
        var newDayNames = convertNumericDaysToNames(newDaysNumeric);

        var existingStartStr = parseDateTime(scheduleSpanRecord.start_date_time);
        var existingEndStr = parseDateTime(scheduleSpanRecord.end_date_time);
        var existingStart = new GlideDateTime();
        existingStart.setDisplayValue(existingStartStr);
        var existingEnd = new GlideDateTime();
        existingEnd.setDisplayValue(existingEndStr);
        var existingAllDay = scheduleSpanRecord.all_day == 'true' || scheduleSpanRecord.all_day == true;
        var existingDaysOfWeek = scheduleSpanRecord.days_of_week || '';
        var existingRepeatType = scheduleSpanRecord.repeat_type || '';
        
        var existingDayNames = [];
        if (existingDaysOfWeek) {
            existingDayNames = convertNumericDaysToNames(existingDaysOfWeek);
        } else if (existingRepeatType) {
            existingDayNames = extractDaysFromRepeatType(existingRepeatType);
        } else {
            existingDayNames = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];
        }
        
        
        // Check time overlap
        var timeStartBefore = newStartTime.before(existingEnd);
        var timeEndAfter = newEndTime.after(existingStart);
        var timeOverlaps = timeStartBefore && timeEndAfter;
        
        if (!timeOverlaps) {
            outputs.has_overlap = false;
            return;
        }

        var commonDay = findCommonDay(newDayNames, existingDayNames);
        var daysOverlap = commonDay !== null;
                
        if (daysOverlap) {
            hasOverlap = true;
        }
        
        outputs.has_overlap = hasOverlap;
        
    } catch (ex) {
        outputs.has_overlap = false;
    }
    

    function parseDateTime(dateTimeValue) {
        if (!dateTimeValue) return '';
        
        var dateTimeStr = String(dateTimeValue).trim();
        
        if (dateTimeStr.indexOf('TZID=') === 0) {
            var parts = dateTimeStr.split(';');
            if (parts.length > 1) dateTimeStr = parts[1];
        }
        
        if (dateTimeStr.indexOf('T') > -1 && dateTimeStr.indexOf('-') === -1) {
            var y = dateTimeStr.substring(0, 4);
            var m = dateTimeStr.substring(4, 6);
            var d = dateTimeStr.substring(6, 8);
            var h = dateTimeStr.substring(9, 11);
            var min = dateTimeStr.substring(11, 13);
            var s = dateTimeStr.substring(13, 15);
            dateTimeStr = y + '-' + m + '-' + d + ' ' + h + ':' + min + ':' + s;
        }
        
        return dateTimeStr;
    }
    

    function convertNumericDaysToNames(numericDays) {
        var dayMap = {'1':'monday','2':'tuesday','3':'wednesday','4':'thursday','5':'friday','6':'saturday','7':'sunday'};
        
        if (!numericDays) return ['monday','tuesday','wednesday','thursday','friday','saturday','sunday'];
        
        var numericStr = String(numericDays);
        var dayNumbers = numericStr.indexOf(',') > -1 ? numericStr.split(',') : numericStr.split('');
        var dayNames = [];
        
        for (var i = 0; i < dayNumbers.length; i++) {
            var num = dayNumbers[i].trim();
            if (dayMap[num]) dayNames.push(dayMap[num]);
        }
        
        return dayNames.length > 0 ? dayNames : ['monday','tuesday','wednesday','thursday','friday','saturday','sunday'];
    }
    
    function extractDaysFromRepeatType(repeatType) {
        if (!repeatType || repeatType == 'NULL_OVERRIDE') {
            return ['monday','tuesday','wednesday','thursday','friday','saturday','sunday'];
        }
        
        var allDays = ['monday','tuesday','wednesday','thursday','friday','saturday','sunday'];
        var repeatStr = String(repeatType).toLowerCase();
        
        if (repeatStr.indexOf('daily') > -1) return allDays;
        
        var foundDays = [];
        for (var i = 0; i < allDays.length; i++) {
            if (repeatStr.indexOf(allDays[i]) > -1) foundDays.push(allDays[i]);
        }
        
        return foundDays.length > 0 ? foundDays : allDays;
    }
    
    function findCommonDay(days1, days2) {
        if (!days1 || !days2 || days1.length == 0 || days2.length == 0) return 'all';
        
        for (var i = 0; i < days1.length; i++) {
            for (var j = 0; j < days2.length; j++) {
                if (days1[i] == days2[j]) return days1[i];
            }
        }
        
        return null;
    }
    
})(inputs, outputs);

munukuntlak
Tera Contributor
(function execute(inputs, outputs) {
    
    // This checks for EXACT DUPLICATE weekly schedules
    // Compares only: TIME (HH:MM format) and DAYS OF WEEK (no date comparison)
    
    var isDuplicate = false;
    
    try {
        var scheduleSpanRecord = inputs.schedule_span_record;
        
        if (!scheduleSpanRecord) {
            outputs.has_overlap = false;
            gs.info('DUPLICATE_DUPLICATE: no_record_provided');
            return;
        }

        // Get input times (HH:MM format)
        var newStartTime = inputs.start_time_to_compare || ''; // e.g., "08:00"
        var newEndTime = inputs.end_time_to_compare || ''; // e.g., "17:00"
        var newDaysNumeric = inputs.days_of_week || '';
        var newDayNames = convertNumericDaysToNames(newDaysNumeric);
        
        gs.info('DUPLICATE_DUPLICATE_INPUT: start_time=' + newStartTime + ', end_time=' + newEndTime + ', days=' + newDaysNumeric + '→' + newDayNames.join(','));

        // Parse existing record - extract only TIME from datetime
        var existingStartTime = extractTime(scheduleSpanRecord.start_date_time); // Extract HH:MM
        var existingEndTime = extractTime(scheduleSpanRecord.end_date_time); // Extract HH:MM
        var existingDaysOfWeek = scheduleSpanRecord.days_of_week || '';
        var existingRepeatType = scheduleSpanRecord.repeat_type || '';
        var existingName = scheduleSpanRecord.name || '';
        
        // Get existing days
        var existingDayNames = [];
        if (existingDaysOfWeek) {
            existingDayNames = convertNumericDaysToNames(existingDaysOfWeek);
        } else if (existingRepeatType) {
            existingDayNames = extractDaysFromRepeatType(existingRepeatType);
        } else {
            existingDayNames = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];
        }
        
        gs.info('DUPLICATE_DUPLICATE_EXISTING: name=' + existingName + ', start_time=' + existingStartTime + ', end_time=' + existingEndTime + ', days=' + existingDaysOfWeek + '→' + existingDayNames.join(','));
        
        // Normalize times to HH:MM format for comparison
        var normalizedNewStart = normalizeTime(newStartTime);
        var normalizedNewEnd = normalizeTime(newEndTime);
        var normalizedExistingStart = normalizeTime(existingStartTime);
        var normalizedExistingEnd = normalizeTime(existingEndTime);
        
        // Check if start times match EXACTLY (HH:MM)
        var startTimesMatch = normalizedNewStart === normalizedExistingStart;
        
        // Check if end times match EXACTLY (HH:MM)
        var endTimesMatch = normalizedNewEnd === normalizedExistingEnd;
        
        // Check if days match EXACTLY (all days must be the same)
        var daysMatch = arraysEqual(newDayNames.sort(), existingDayNames.sort());
        
        gs.info('DUPLICATE_DUPLICATE_CHECK: start_time_match=' + startTimesMatch + ' (' + normalizedNewStart + '=' + normalizedExistingStart + '), end_time_match=' + endTimesMatch + ' (' + normalizedNewEnd + '=' + normalizedExistingEnd + '), days_match=' + daysMatch);
        
        // It's a duplicate only if ALL three match (TIME + DAYS, no date check)
        if (startTimesMatch && endTimesMatch && daysMatch) {
            isDuplicate = true;
            gs.warn('DUPLICATE_DUPLICATE_FOUND: exact_duplicate_of=' + existingName + ', matching_time=' + normalizedNewStart + '-' + normalizedNewEnd + ', matching_days=' + newDayNames.join(','));
        } else {
            var reason = [];
            if (!startTimesMatch) reason.push('different_start_time(' + normalizedNewStart + '≠' + normalizedExistingStart + ')');
            if (!endTimesMatch) reason.push('different_end_time(' + normalizedNewEnd + '≠' + normalizedExistingEnd + ')');
            if (!daysMatch) reason.push('different_days(' + newDayNames.join(',') + '≠' + existingDayNames.join(',') + ')');
            
            gs.info('DUPLICATE_DUPLICATE_RESULT: is_duplicate=false, reason=' + reason.join(','));
        }
        
        outputs.has_overlap = isDuplicate;
        gs.info('DUPLICATE_DUPLICATE_FINAL: is_duplicate=' + isDuplicate);
        
    } catch (ex) {
        outputs.has_overlap = false;
        gs.error('DUPLICATE_DUPLICATE_ERROR: ' + ex.message);
    }
    
    // Extract time (HH:MM) from various datetime formats
    function extractTime(dateTimeValue) {
        if (!dateTimeValue) return '';
        
        var dateTimeStr = String(dateTimeValue).trim();
        
        // Remove timezone prefix if present (TZID=US/Central;)
        if (dateTimeStr.indexOf('TZID=') === 0) {
            var parts = dateTimeStr.split(';');
            if (parts.length > 1) dateTimeStr = parts[1];
        }
        
        // Handle compact format: 20250910T020000 → extract "02:00"
        if (dateTimeStr.indexOf('T') > -1 && dateTimeStr.indexOf('-') === -1) {
            // Format: YYYYMMDDTHHMMSS
            var hour = dateTimeStr.substring(9, 11);
            var minute = dateTimeStr.substring(11, 13);
            return hour + ':' + minute;
        }
        
        // Handle standard format: "2025-09-10 02:00:00" → extract "02:00"
        if (dateTimeStr.indexOf(' ') > -1) {
            var timePart = dateTimeStr.split(' ')[1]; // Get time part after space
            if (timePart) {
                var timeComponents = timePart.split(':');
                if (timeComponents.length >= 2) {
                    return timeComponents[0] + ':' + timeComponents[1]; // HH:MM
                }
            }
        }
        
        // If already in HH:MM or HH:MM:SS format, extract HH:MM
        if (dateTimeStr.indexOf(':') > -1) {
            var timeComponents2 = dateTimeStr.split(':');
            if (timeComponents2.length >= 2) {
                return timeComponents2[0] + ':' + timeComponents2[1];
            }
        }
        
        return dateTimeStr;
    }
    
    // Normalize time to HH:MM format (ensure leading zeros)
    function normalizeTime(timeStr) {
        if (!timeStr) return '00:00';
        
        timeStr = String(timeStr).trim();
        
        // Split by colon
        var parts = timeStr.split(':');
        if (parts.length < 2) return timeStr;
        
        var hour = parts[0].padStart(2, '0');
        var minute = parts[1].padStart(2, '0');
        
        return hour + ':' + minute;
    }
    
    // Convert numeric days to names: "1,3,5" or "135" → ["monday","wednesday","friday"]
    function convertNumericDaysToNames(numericDays) {
        var dayMap = {'1':'monday','2':'tuesday','3':'wednesday','4':'thursday','5':'friday','6':'saturday','7':'sunday'};
        
        if (!numericDays) return ['monday','tuesday','wednesday','thursday','friday','saturday','sunday'];
        
        var numericStr = String(numericDays);
        var dayNumbers = numericStr.indexOf(',') > -1 ? numericStr.split(',') : numericStr.split('');
        var dayNames = [];
        
        for (var i = 0; i < dayNumbers.length; i++) {
            var num = dayNumbers[i].trim();
            if (dayMap[num]) dayNames.push(dayMap[num]);
        }
        
        return dayNames.length > 0 ? dayNames : ['monday','tuesday','wednesday','thursday','friday','saturday','sunday'];
    }
    
    // Extract days from repeat type
    function extractDaysFromRepeatType(repeatType) {
        if (!repeatType || repeatType == 'NULL_OVERRIDE') {
            return ['monday','tuesday','wednesday','thursday','friday','saturday','sunday'];
        }
        
        var allDays = ['monday','tuesday','wednesday','thursday','friday','saturday','sunday'];
        var repeatStr = String(repeatType).toLowerCase();
        
        if (repeatStr.indexOf('daily') > -1) return allDays;
        
        var foundDays = [];
        for (var i = 0; i < allDays.length; i++) {
            if (repeatStr.indexOf(allDays[i]) > -1) foundDays.push(allDays[i]);
        }
        
        return foundDays.length > 0 ? foundDays : allDays;
    }
    
    // Check if two arrays are exactly equal
    function arraysEqual(arr1, arr2) {
        if (arr1.length !== arr2.length) return false;
        
        for (var i = 0; i < arr1.length; i++) {
            if (arr1[i] !== arr2[i]) return false;
        }
        
        return true;
    }
    
})(inputs, outputs);

munukuntlak
Tera Contributor
(function execute(inputs, outputs) {
    
    // This checks for EXACT DUPLICATE weekly schedules with timezone conversion
    // Compares: TIME (HH:MM in specified timezone) and DAYS OF WEEK
    
    var isDuplicate = false;
    
    try {
        var scheduleSpanRecord = inputs.schedule_span_record;
        
        if (!scheduleSpanRecord) {
            outputs.has_overlap = false;
            gs.info('ARUN_DUPLICATE: no_record_provided');
            return;
        }

        // Get input values
        var newStartTime = inputs.start_time_to_compare || ''; // e.g., "08:00"
        var newEndTime = inputs.end_time_to_compare || ''; // e.g., "17:00"
        var newDaysNumeric = inputs.days_of_week || '';
        var newTimezone = inputs.timezone || 'US/Eastern'; // US/Eastern, US/Central, US/Pacific
        var existingTimezone = inputs.existing_timezone || 'US/Eastern'; // Existing record's timezone
        var newDayNames = convertNumericDaysToNames(newDaysNumeric);
        
        gs.info('ARUN_DUPLICATE_INPUT: start_time=' + newStartTime + ', end_time=' + newEndTime + ', days=' + newDaysNumeric + '→' + newDayNames.join(',') + ', new_timezone=' + newTimezone + ', existing_timezone=' + existingTimezone);

        // Parse existing record - extract TIME
        var existingStartTimeRaw = scheduleSpanRecord.start_date_time.toString();
        var existingEndTimeRaw = scheduleSpanRecord.end_date_time.toString();
        var existingDaysOfWeek = scheduleSpanRecord.days_of_week || '';
        var existingRepeatType = scheduleSpanRecord.repeat_type || '';
        var existingName = scheduleSpanRecord.name || '';
        
        // Extract time in existing timezone (no need to extract from TZID, using input)
        var existingStartTimeInOwnTZ = extractTime(existingStartTimeRaw);
        var existingEndTimeInOwnTZ = extractTime(existingEndTimeRaw);
        
        // Convert existing times to input timezone for comparison
        var existingStartTimeConverted = convertTimeToTimezone(existingStartTimeInOwnTZ, existingTimezone, newTimezone);
        var existingEndTimeConverted = convertTimeToTimezone(existingEndTimeInOwnTZ, existingTimezone, newTimezone);
        
        // Get existing days
        var existingDayNames = [];
        if (existingDaysOfWeek) {
            existingDayNames = convertNumericDaysToNames(existingDaysOfWeek);
        } else if (existingRepeatType) {
            existingDayNames = extractDaysFromRepeatType(existingRepeatType);
        } else {
            existingDayNames = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];
        }
        
        gs.info('ARUN_DUPLICATE_EXISTING: name=' + existingName + ', start_time=' + existingStartTimeInOwnTZ + ' (' + existingTimezone + '), end_time=' + existingEndTimeInOwnTZ + ' (' + existingTimezone + '), days=' + existingDaysOfWeek + '→' + existingDayNames.join(','));
        gs.info('ARUN_DUPLICATE_CONVERTED: existing_start=' + existingStartTimeConverted + ' (' + newTimezone + '), existing_end=' + existingEndTimeConverted + ' (' + newTimezone + ')');
        
        // Normalize times to HH:MM format for comparison
        var normalizedNewStart = normalizeTime(newStartTime);
        var normalizedNewEnd = normalizeTime(newEndTime);
        var normalizedExistingStart = normalizeTime(existingStartTimeConverted);
        var normalizedExistingEnd = normalizeTime(existingEndTimeConverted);
        
        // Check if start times match EXACTLY (HH:MM in same timezone)
        var startTimesMatch = normalizedNewStart === normalizedExistingStart;
        
        // Check if end times match EXACTLY (HH:MM in same timezone)
        var endTimesMatch = normalizedNewEnd === normalizedExistingEnd;
        
        // Check if days match EXACTLY
        var daysMatch = arraysEqual(newDayNames.sort(), existingDayNames.sort());
        
        gs.info('ARUN_DUPLICATE_CHECK: start_time_match=' + startTimesMatch + ' (' + normalizedNewStart + '=' + normalizedExistingStart + '), end_time_match=' + endTimesMatch + ' (' + normalizedNewEnd + '=' + normalizedExistingEnd + '), days_match=' + daysMatch);
        
        // It's a duplicate only if ALL three match
        if (startTimesMatch && endTimesMatch && daysMatch) {
            isDuplicate = true;
            gs.warn('ARUN_DUPLICATE_FOUND: exact_duplicate_of=' + existingName + ', matching_time=' + normalizedNewStart + '-' + normalizedNewEnd + ' (' + newTimezone + '), matching_days=' + newDayNames.join(','));
        } else {
            var reason = [];
            if (!startTimesMatch) reason.push('different_start_time(' + normalizedNewStart + '≠' + normalizedExistingStart + ')');
            if (!endTimesMatch) reason.push('different_end_time(' + normalizedNewEnd + '≠' + normalizedExistingEnd + ')');
            if (!daysMatch) reason.push('different_days(' + newDayNames.join(',') + '≠' + existingDayNames.join(',') + ')');
            
            gs.info('ARUN_DUPLICATE_RESULT: is_duplicate=false, reason=' + reason.join(','));
        }
        
        outputs.has_overlap = isDuplicate;
        gs.info('ARUN_DUPLICATE_FINAL: is_duplicate=' + isDuplicate);
        
    } catch (ex) {
        outputs.has_overlap = false;
        gs.error('ARUN_DUPLICATE_ERROR: ' + ex.message);
    }
    
    // Extract timezone from datetime string (DEPRECATED - now using input parameter)
    // Keeping for backward compatibility
    function extractTimezone(dateTimeValue) {
        if (!dateTimeValue) return 'US/Eastern';
        
        var dateTimeStr = String(dateTimeValue).trim();
        
        if (dateTimeStr.indexOf('TZID=') === 0) {
            var parts = dateTimeStr.split(';');
            if (parts.length > 0) {
                var tzPart = parts[0].replace('TZID=', '');
                return tzPart;
            }
        }
        
        return 'US/Eastern';
    }
    
    // Extract time (HH:MM) from various datetime formats
    function extractTime(dateTimeValue) {
        if (!dateTimeValue) return '';
        
        var dateTimeStr = String(dateTimeValue).trim();
        
        // Remove timezone prefix if present
        if (dateTimeStr.indexOf('TZID=') === 0) {
            var parts = dateTimeStr.split(';');
            if (parts.length > 1) dateTimeStr = parts[1];
        }
        
        // Handle compact format: 20250910T020000 → extract "02:00"
        if (dateTimeStr.indexOf('T') > -1 && dateTimeStr.indexOf('-') === -1) {
            var hour = dateTimeStr.substring(9, 11);
            var minute = dateTimeStr.substring(11, 13);
            return hour + ':' + minute;
        }
        
        // Handle standard format: "2025-09-10 02:00:00" → extract "02:00"
        if (dateTimeStr.indexOf(' ') > -1) {
            var timePart = dateTimeStr.split(' ')[1];
            if (timePart) {
                var timeComponents = timePart.split(':');
                if (timeComponents.length >= 2) {
                    return timeComponents[0] + ':' + timeComponents[1];
                }
            }
        }
        
        // If already in HH:MM or HH:MM:SS format, extract HH:MM
        if (dateTimeStr.indexOf(':') > -1) {
            var timeComponents2 = dateTimeStr.split(':');
            if (timeComponents2.length >= 2) {
                return timeComponents2[0] + ':' + timeComponents2[1];
            }
        }
        
        return dateTimeStr;
    }
    
    // Convert time from one timezone to another
    function convertTimeToTimezone(timeStr, fromTZ, toTZ) {
        if (!timeStr) return '00:00';
        
        // If same timezone, no conversion needed
        if (fromTZ === toTZ) {
            return timeStr;
        }
        
        // Parse time
        var timeParts = timeStr.split(':');
        if (timeParts.length < 2) return timeStr;
        
        var hour = parseInt(timeParts[0], 10);
        var minute = parseInt(timeParts[1], 10);
        
        // Timezone offset mapping (hours difference from UTC)
        var tzOffsets = {
            'US/Eastern': -5,   // EST (UTC-5)
            'US/Central': -6,   // CST (UTC-6)
            'US/Pacific': -8    // PST (UTC-8)
        };
        
        var fromOffset = tzOffsets[fromTZ] || -5;
        var toOffset = tzOffsets[toTZ] || -5;
        
        // Calculate hour difference
        var hourDiff = toOffset - fromOffset;
        
        // Apply conversion
        var newHour = hour + hourDiff;
        
        // Handle day wrap-around
        if (newHour < 0) {
            newHour += 24;
        } else if (newHour >= 24) {
            newHour -= 24;
        }
        
        return normalizeTime(newHour + ':' + minute);
    }
    
    // Normalize time to HH:MM format (ensure leading zeros)
    function normalizeTime(timeStr) {
        if (!timeStr) return '00:00';
        
        timeStr = String(timeStr).trim();
        
        var parts = timeStr.split(':');
        if (parts.length < 2) return timeStr;
        
        var hour = String(parts[0]);
        var minute = String(parts[1]);
        
        // Pad with leading zeros
        if (hour.length === 1) hour = '0' + hour;
        if (minute.length === 1) minute = '0' + minute;
        
        return hour + ':' + minute;
    }
    
    // Convert numeric days to names: "1,3,5" or "135" → ["monday","wednesday","friday"]
    function convertNumericDaysToNames(numericDays) {
        var dayMap = {'1':'monday','2':'tuesday','3':'wednesday','4':'thursday','5':'friday','6':'saturday','7':'sunday'};
        
        if (!numericDays) return ['monday','tuesday','wednesday','thursday','friday','saturday','sunday'];
        
        var numericStr = String(numericDays);
        var dayNumbers = numericStr.indexOf(',') > -1 ? numericStr.split(',') : numericStr.split('');
        var dayNames = [];
        
        for (var i = 0; i < dayNumbers.length; i++) {
            var num = dayNumbers[i].trim();
            if (dayMap[num]) dayNames.push(dayMap[num]);
        }
        
        return dayNames.length > 0 ? dayNames : ['monday','tuesday','wednesday','thursday','friday','saturday','sunday'];
    }
    
    // Extract days from repeat type
    function extractDaysFromRepeatType(repeatType) {
        if (!repeatType || repeatType == 'NULL_OVERRIDE') {
            return ['monday','tuesday','wednesday','thursday','friday','saturday','sunday'];
        }
        
        var allDays = ['monday','tuesday','wednesday','thursday','friday','saturday','sunday'];
        var repeatStr = String(repeatType).toLowerCase();
        
        if (repeatStr.indexOf('daily') > -1) return allDays;
        
        var foundDays = [];
        for (var i = 0; i < allDays.length; i++) {
            if (repeatStr.indexOf(allDays[i]) > -1) foundDays.push(allDays[i]);
        }
        
        return foundDays.length > 0 ? foundDays : allDays;
    }
    
    // Check if two arrays are exactly equal
    function arraysEqual(arr1, arr2) {
        if (arr1.length !== arr2.length) return false;
        
        for (var i = 0; i < arr1.length; i++) {
            if (arr1[i] !== arr2[i]) return false;
        }
        
        return true;
    }
    
})(inputs, outputs);