MAP REDUCE SCRIPT SAMPLE THAT SETS THE CUSTOM FIELD VALUE

define([‘N/record’, ‘N/search’],

    function (record, search) {

        “use strict”;

        const LEAVE_PROJECT = ‘26088’;

        const PROJECT_ALLOCATION_IDS = [“31442”, “31433”, “31443”];

        /**

         * Fetch Employee Work Calendar ID.

         * @param {string} employeeId – Employee internal ID.

         * @returns {string|null} Work calendar ID.

         */

        function getEmployeeWorkCalendar(employeeId) {

            try {

                let employeeData = search.lookupFields({

                    type: search.Type.EMPLOYEE,

                    id: employeeId,

                    columns: [‘workcalendar’]

                });

                return employeeData.workcalendar?.[0]?.value || null;

            } catch (e) {

                log.error(‘Error fetching Employee Work Calendar’, e);

                return null;

            }

        }

        /**

         * Fetch Work Calendar Hours Per Day.

         * @param {string} calendarId – Work calendar internal ID.

         * @returns {number} Work hours per day.

         */

        function getWorkCalendarHours(calendarId) {

            try {

                let workCalendarData = search.lookupFields({

                    type: “workcalendar”,

                    id: calendarId,

                    columns: [‘workhoursperday’]

                });

                return parseFloat(workCalendarData.workhoursperday) || 0;

            } catch (e) {

                log.error(‘Error fetching Work Calendar Hours’, e);

                return 0;

            }

        }

        /**

         * Fetch Project Allocations.

         * @returns {Array} List of project allocations.

         */

        function getProjectAllocations() {

            try {

                let projectAllocSearch = search.create({

                    type: “customrecord_jj_project_alloc”,

                    filters: [

                        [“isinactive”, “is”, “F”], // Only active records

                        “AND”,

                        [“internalid”, “anyof”, PROJECT_ALLOCATION_IDS], // Specific project allocation IDs

                        “AND”,

                        [“custrecord_jj_project”, “noneof”, LEAVE_PROJECT] // Exclude specific project

                    ],

                    columns: [

                        search.createColumn({ name: “internalid” }),

                        search.createColumn({ name: “custrecord_jj_project_period” }),

                        search.createColumn({ name: “custrecord_jj_project” }),

                        search.createColumn({ name: “custrecord_jj_time_tracked_project” }),

                        search.createColumn({

                            name: “custrecord_jj_payroll_emp”,

                            join: “CUSTRECORD_JJ_PROJECT_PERIOD”

                        })

                    ]

                });

                let results = [];

                projectAllocSearch.run().each(function (result) {

                    results.push({

                        id: result.getValue({ name: “internalid” }),

                        projectPeriod: result.getValue({ name: “custrecord_jj_project_period” }), // Payroll ID

                        projectId: result.getValue({ name: “custrecord_jj_project” }),

                        trackedHours: parseFloat(result.getValue({ name: “custrecord_jj_time_tracked_project” }) || 0),

                        payrollEmpId: result.getValue({

                            name: “custrecord_jj_payroll_emp”,

                            join: “CUSTRECORD_JJ_PROJECT_PERIOD”

                        })

                    });

                    return true;

                });

                log.debug(“Project Allocations Retrieved”, results);

                return results;

            } catch (e) {

                log.error(‘Error in getProjectAllocations’, e);

                return e;

            }

        }

        /**

         * Defines the function that is executed at the beginning of the map/reduce process and generates the input data.

         * @param {Object} inputContext

         * @param {boolean} inputContext.isRestarted – Indicates whether the current invocation of this function is the first

         *     invocation (if true, the current invocation is not the first invocation and this function has been restarted)

         * @param {Object} inputContext.ObjectRef – Object that references the input data

         * @typedef {Object} ObjectRef

         * @property {string|number} ObjectRef.id – Internal ID of the record instance that contains the input data

         * @property {string} ObjectRef.type – Type of the record instance that contains the input data

         * @returns {Array|Object|Search|ObjectRef|File|Query} The input data to use in the map/reduce process

         * @since 2015.2

         */

        function getInputData() {

            try {

                log.debug(‘Fetching Project Allocations’, ‘Starting to retrieve project allocation records…’);

                return getProjectAllocations();

            } catch (e) {

                log.error(‘Error in getInputData’, e);

                return e;

            }

        }

        /**

         * Defines the function that is executed when the map entry point is triggered. This entry point is triggered automatically

         * when the associated getInputData stage is complete. This function is applied to each key-value pair in the provided

         * context.

         * @param {Object} mapContext – Data collection containing the key-value pairs to process in the map stage. This parameter

         *     is provided automatically based on the results of the getInputData stage.

         * @param {Iterator} mapContext.errors – Serialized errors that were thrown during previous attempts to execute the map

         *     function on the current key-value pair

         * @param {number} mapContext.executionNo – Number of times the map function has been executed on the current key-value

         *     pair

         * @param {boolean} mapContext.isRestarted – Indicates whether the current invocation of this function is the first

         *     invocation (if true, the current invocation is not the first invocation and this function has been restarted)

         * @param {string} mapContext.key – Key to be processed during the map stage

         * @param {string} mapContext.value – Value to be processed during the map stage

         * @since 2015.2

         */

        function map(context) {

            try {

                let result = JSON.parse(context.value);

                let projectAllocationId = result.id;

                let trackedHours = result.trackedHours;

                let payrollId = result.projectPeriod; // This is the Payroll ID

                let payrollEmpId = result.payrollEmpId; // Already retrieved in getProjectAllocations()

                log.debug(‘Calculated Tracked Hours’, {

                    projectAllocationId: projectAllocationId,

                    payrollId: payrollId,

                    trackedHours: trackedHours,

                    payrollEmpId: payrollEmpId

                });

                context.write({

                    key: payrollId,

                    value: {

                        projectAllocationId: projectAllocationId,

                        payrollEmpId: payrollEmpId,

                        totalTrackedHours: trackedHours

                    }

                });

            } catch (e) {

                log.error(‘Error in map’, e);

                return e;

            }

        }

        /**

          * Defines the function that is executed when the reduce entry point is triggered. This entry point is triggered

          * automatically when the associated map stage is complete. This function is applied to each group in the provided context.

          * @param {Object} reduceContext – Data collection containing the groups to process in the reduce stage. This parameter is

          *     provided automatically based on the results of the map stage.

          * @param {Iterator} reduceContext.errors – Serialized errors that were thrown during previous attempts to execute the

          *     reduce function on the current group

          * @param {number} reduceContext.executionNo – Number of times the reduce function has been executed on the current group

          * @param {boolean} reduceContext.isRestarted – Indicates whether the current invocation of this function is the first

          *     invocation (if true, the current invocation is not the first invocation and this function has been restarted)

          * @param {string} reduceContext.key – Key to be processed during the reduce stage

          * @param {List<String>} reduceContext.values – All values associated with a unique key that was passed to the reduce stage

          *     for processing

          * @since 2015.2

          */

        function reduce(context) {

            try {

                let payrollId = context.key;

                let totalTrackedHoursForEmployee = 0;

                let payrollEmpId = null;

                context.values.forEach(function (value) {

                    let parsedValue = JSON.parse(value);

                    totalTrackedHoursForEmployee += parsedValue.totalTrackedHours;

                    payrollEmpId = parsedValue.payrollEmpId;

                });

                if (!payrollEmpId) {

                    log.error(‘Payroll Employee ID Not Found’, { payrollId });

                    return;

                }

                // Fetch Employee’s Work Calendar & Min Work Hours

                let workCalendarId = getEmployeeWorkCalendar(payrollEmpId);

                let minWorkHours = workCalendarId ? getWorkCalendarHours(workCalendarId) : 0; // Default to 0 hours if not found

                if (minWorkHours > 0) {

                    totalTrackedHoursForEmployee = Math.ceil(totalTrackedHoursForEmployee / minWorkHours) * minWorkHours;

                }

                log.debug(‘Reducing Employee Data’, {

                    payrollEmpId: payrollEmpId,

                    payrollId: payrollId,

                    totalTrackedHoursForEmployee: totalTrackedHoursForEmployee,

                    minWorkHours: minWorkHours

                });

                // Update Payroll Record

                record.submitFields({

                    type: ‘customrecord_jj_payroll_details’,

                    id: payrollId,

                    values: {

                        ‘custrecord_jj_time_tracked’: totalTrackedHoursForEmployee

                    },

                    options: { ignoreMandatoryFields: true }

                });

                log.debug(‘Updated Payroll Details’, {

                    payrollEmpId: payrollEmpId,

                    payrollId: payrollId,

                    totalTrackedHoursForEmployee: totalTrackedHoursForEmployee

                });

            } catch (e) {

                log.error(‘Error in reduce’, e);

                return e;

            }

        }

        /**

         * Defines the function that is executed when the summarize entry point is triggered. This entry point is triggered

         * automatically when the associated reduce stage is complete. This function is applied to the entire result set.

         * @param {Object} summaryContext – Statistics about the execution of a map/reduce script

         * @param {number} summaryContext.concurrency – Maximum concurrency number when executing parallel tasks for the map/reduce

         *     script

         * @param {Date} summaryContext.dateCreated – The date and time when the map/reduce script began running

         * @param {boolean} summaryContext.isRestarted – Indicates whether the current invocation of this function is the first

         *     invocation (if true, the current invocation is not the first invocation and this function has been restarted)

         * @param {Iterator} summaryContext.output – Serialized keys and values that were saved as output during the reduce stage

         * @param {number} summaryContext.seconds – Total seconds elapsed when running the map/reduce script

         * @param {number} summaryContext.usage – Total number of governance usage units consumed when running the map/reduce

         *     script

         * @param {number} summaryContext.yields – Total number of yields when running the map/reduce script

         * @param {Object} summaryContext.inputSummary – Statistics about the input stage

         * @param {Object} summaryContext.mapSummary – Statistics about the map stage

         * @param {Object} summaryContext.reduceSummary – Statistics about the reduce stage

         * @since 2015.2

         */

        function summarize(summary) {

            try {

                log.audit(‘Map/Reduce Execution Summary’, summary);

                if (summary.error) {

                    log.error(‘Execution Error’, summary.error);

                }

            } catch (e) {

                log.error(‘Error in summarize’, e);

                return e;

            }

        }

        return {

            getInputData: getInputData,

            map: map,

            reduce: reduce,

            summarize: summarize

        };

    });

1. Constants Definition

  • Defines a specific Leave Project ID (LEAVE_PROJECT = '26088').
  • Specifies a list of Project Allocation IDs to be processed.

2. Fetching Employee Work Calendar Details

  • Retrieves the Work Calendar ID for a given employee.
  • Fetches the Work Hours Per Day from the Work Calendar record.

3. Fetching Project Allocations

  • Searches for active Project Allocations that match specific criteria:
  • Project allocation IDs from the predefined list.
  • Excludes leave projects (i.e., projects related to employee leaves).
  • Retrieves relevant details:
  • Project Allocation ID
  • Payroll ID (Project Period)
  • Project ID
  • Tracked Time
  • Employee ID from Payroll

4. Map/Reduce Execution Flow

a) Get Input Data (getInputData)

  • Calls the function to retrieve project allocation records.
  • Logs the retrieval process.

b) Map Phase (map)

  • Extracts project allocation details from the input data.
  • Logs details like Project Allocation ID, Payroll ID, Tracked Hours, and Payroll Employee ID.
  • Writes data to be processed in the reduce phase, grouped by Payroll ID.

c) Reduce Phase (reduce)

  • Aggregates Total Tracked Hours for each Payroll ID.
  • Fetches the employee’s Work Calendar ID and Work Hours Per Day.
  • Adjusts total tracked hours based on minimum work hours for accurate payroll tracking.
  • Logs the computed values.
  • Updates the Payroll Details Record with the new tracked hours.

d) Summarize Phase (summarize)

  • Logs the execution summary of the script.
  • Captures any errors encountered during execution.

Leave a comment

Your email address will not be published. Required fields are marked *