HRIMS Employee Attendance Sync

/**
 * @NApiVersion 2.1
 * @NScriptType MapReduceScript
 */
/***********************************************
 * ATPL-67 - Implementation | HRIMS Integration
 * *********************************************
 *
 * Author: Jobin & Jismi IT Services LLP
 *
 * Date Created : 05-February-2024
 *
 * Created By: JJ0124, Jobin & Jismi IT Services LLP
 *
 * Description : Map Reduce script is used to perfrom the Employee Attendance Sync from HRIMS
 *
 * REVISION HISTORY
 *
 ****************************************************** *****************************************/
define([
    "N/record",
    "N/https",
    "N/format",
    "N/file",
    "./jj_cm_hrims_module"
], (record, https, format, file, _hrims_module) => {

    // Employee Attendance Mapping Fields
    const MAPPING_FIELDS = {
        "TotalTimeinOfficeHours": "custrecord_jj_total_time_in_office",
        "OutTime": "custrecord_jj_out_time",
        "LastModifiedDate": "custrecord_last_modified_date",
        "InTime": "custrecord_jj_in_time",
        "DailyAttendanceRecordId": "custrecord_jj_hrims_attendance_rec_id",
        "AttendanceStatus": "custrecord_jj_attendance_status",
        "AttendanceDate": "custrecord_jj_attendance_date",
        "EmployeeNumber": "custrecord_jj_hrims_emp_number",
        "NetsuiteEmployeeId": "custrecord_jj_netsuite_employee",
    }

    /**
     * Function will prepare the Attendance Data
     * @param {Object} attendanceObj 
     * @param {String} hrimsEmployeeNumber 
     * @param {String} netSuiteEmployeeId 
     * @returns Object
     */
    const prepareAttendanceData = (attendanceObj, hrimsEmployeeNumber, netSuiteEmployeeId) => {
        try {
            return {
                "TotalTimeinOfficeHours": attendanceObj["TotalTimeinOfficeHours"],
                "OutTime": attendanceObj["OutTime"],
                "LastModifiedDate": attendanceObj["LastModifiedDate"],
                "InTime": attendanceObj["InTime"],
                "DailyAttendanceRecordId": attendanceObj["DailyAttendanceRecordId"],
                "AttendanceStatus": attendanceObj["AttendanceStatus"],
                "AttendanceDate": attendanceObj["AttendanceDate"],
                "EmployeeNumber": hrimsEmployeeNumber,
                "NetsuiteEmployeeId": netSuiteEmployeeId
            };
        } catch (error) {
            log.error("Error in prepareAttendanceData", error);
        }
    }

    /**
     * Function will create Attendance Record
     * @param {Object} attendanceObj 
     * @param {String} hrimsEmployeeNumber 
     * @param {String} netSuiteEmployeeId 
     * @returns Object
     */
    const createAttendanceRecord = (attendanceObj, hrimsEmployeeNumber, netSuiteEmployeeId) => {
        try {
            let attendanceData = prepareAttendanceData(attendanceObj, hrimsEmployeeNumber, netSuiteEmployeeId);
            log.error("Attendance Data", attendanceData);

            let createAttendanceRec = record.create({
                type: "customrecord_jj_hrims_emp_attendance",
            });

            for (let key in attendanceData) {
                if (['OutTime', 'InTime', 'LastModifiedDate'].includes(key) && attendanceData[key]) {
                    createAttendanceRec.setValue({
                        fieldId: MAPPING_FIELDS[key],
                        value: format.parse({ type: format.Type.DATE, value: new Date(attendanceData[key]) })
                    });
                } else if (['AttendanceDate'].includes(key) && attendanceData[key]) {
                    let attendanceDate = new Date(attendanceData[key]);
                    attendanceDate.setDate(attendanceDate.getDate() + 1);
                    createAttendanceRec.setValue({ fieldId: MAPPING_FIELDS[key], value: attendanceDate });
                } else {
                    createAttendanceRec.setValue({ fieldId: MAPPING_FIELDS[key], value: attendanceData[key] });
                }
            }

            return { status: "success", recordId: createAttendanceRec.save(), error: [] };
        } catch (error) {
            log.error("Error in createAttendanceRecord", error);
            return { status: "error", recordId: null, error: [error.message] };
        }
    }

    /**
     * // Function will update Attendance Record
     * @param {Object} attendanceObj 
     * @param {String} netSuiteAttendanceId 
     * @returns Object
     */
    const updateAttendanceRecord = (attendanceObj, netSuiteAttendanceId) => {
        try {
            let isRecordUpdated = false;

            let LoadAttendanceRec = record.load({
                type: "customrecord_jj_hrims_emp_attendance",
                id: netSuiteAttendanceId
            });

            // TotalTimeinOfficeHours
            let netsuiteTotalTime = LoadAttendanceRec.getValue({ fieldId: "custrecord_jj_total_time_in_office" }) || null;
            let hrimsTotalTime = attendanceObj["TotalTimeinOfficeHours"];
            if (netsuiteTotalTime != hrimsTotalTime) {
                LoadAttendanceRec.setValue({ fieldId: "custrecord_jj_total_time_in_office", value: hrimsTotalTime });
                isRecordUpdated = true;
            }

            // InTime
            let netsuiteInTime = LoadAttendanceRec.getValue({ fieldId: "custrecord_jj_in_time" }) || null;
            let hrimsInTime = attendanceObj["InTime"];
            if (new Date(netsuiteInTime).getTime() != new Date(hrimsInTime).getTime()) {
                LoadAttendanceRec.setValue({ fieldId: "custrecord_jj_in_time", value: _hrims_module.parseDate(attendanceObj["InTime"]) });
                isRecordUpdated = true;
            }

            // OutTime
            let netsuiteOutTime = LoadAttendanceRec.getValue({ fieldId: "custrecord_jj_out_time" }) || null;
            let hrimsOutTime = attendanceObj["OutTime"];
            if (new Date(netsuiteOutTime).getTime() != new Date(hrimsOutTime).getTime()) {
                LoadAttendanceRec.setValue({ fieldId: "custrecord_jj_out_time", value: _hrims_module.parseDate(attendanceObj["OutTime"]) });
                isRecordUpdated = true;
            }

            // LastModifiedDate
            let netsuiteLastModifiedDate = LoadAttendanceRec.getValue({ fieldId: "custrecord_last_modified_date" }) || null;
            let hrimsLastModifiedDate = attendanceObj["LastModifiedDate"];
            if (new Date(netsuiteLastModifiedDate).getTime() != new Date(hrimsLastModifiedDate).getTime()) {
                LoadAttendanceRec.setValue({ fieldId: "custrecord_last_modified_date", value: _hrims_module.parseDate(attendanceObj["LastModifiedDate"]) });
                isRecordUpdated = true;
            }

            // AttendanceStatus
            let netsuiteAttendanceStatus = LoadAttendanceRec.getValue({ fieldId: "custrecord_jj_attendance_status" }) || null;
            let hrimsAttendanceStatus = attendanceObj["AttendanceStatus"];
            if (netsuiteAttendanceStatus != hrimsAttendanceStatus) {
                LoadAttendanceRec.setValue({ fieldId: "custrecord_jj_attendance_status", value: hrimsAttendanceStatus });
                isRecordUpdated = true;
            }

            // AttendanceDate
            let netsuiteAttendanceDate = LoadAttendanceRec.getText({ fieldId: "custrecord_jj_attendance_date" }) || null;
            let hrimsAttendanceDate = attendanceObj["AttendanceDate"] || null;
            if (hrimsAttendanceDate) {
                hrimsAttendanceDate = new Date(attendanceObj["AttendanceDate"]);
                let hrimsParsedAttendanceDate = hrimsAttendanceDate ? hrimsAttendanceDate.setDate(hrimsAttendanceDate.getDate() + 1) : null;
                hrimsAttendanceDate = _hrims_module.formatDate(hrimsParsedAttendanceDate);
            }
            if (netsuiteAttendanceDate != hrimsAttendanceDate) {
                LoadAttendanceRec.setText({ fieldId: "custrecord_jj_attendance_date", text: hrimsAttendanceDate });
                isRecordUpdated = true;
            }

            log.error(`isRecordUpdated for ${attendanceObj["DailyAttendanceRecordId"]}`, isRecordUpdated);
            if (isRecordUpdated) {
                return { status: "success", recordId: LoadAttendanceRec.save(), error: [] };
            } else {
                return { status: "Updation not required", recordId: LoadAttendanceRec.id, error: [] };
            }
        } catch (error) {
            log.error("Error in updateAttendanceRecord", error);
            return { status: "error", recordId: null, error: [error.message] };
        }
    }

    const getInputData = (inputContext) => {
        try {
            log.error("---ATTENDANCE SYNC STARTS---");

            // Generate the Access Token
            const accessTokenResponse = _hrims_module.generateAccessToken();

            if (accessTokenResponse.status != 'success') {
                log.error('Access Token is Invalid', accessTokenResponse.result);
                return [];
            }
            // Fetch the Attendance Data from HRIMS
            const attendanceApiResponse = _hrims_module.fetchApiData('attendance', accessTokenResponse.result);
            if (attendanceApiResponse.status !== 'success') {
                log.error('Attendance API is Invalid', attendanceApiResponse.result);
                return [];
            }

            // Prepare the Attendance Data for the Map Reduce
            const attendanceData = attendanceApiResponse.result.flatMap(employee =>
                employee.AttendanceList.map(attendance => ({
                    EmployeeNumber: employee.EmployeeNumber,
                    ...attendance
                }))
            );

            return attendanceData;
        } catch (error) {
            log.error("Error in getInputData", error);
            return [];
        }
    }

    const reduce = (reduceContext) => {
        try {
            let attendanceObj = JSON.parse(reduceContext.values);

            let hrimsAttendanceId = attendanceObj['DailyAttendanceRecordId'];
            if (!hrimsAttendanceId) {
                reduceContext.write({
                    key: hrimsAttendanceId || reduceContext.key,
                    value: {
                        status: "error",
                        recordId: '',
                        error: ['Attendance ID is not found in the HRIMS']
                    }
                });
                return;
            }

            let hrimsEmployeeNumber = attendanceObj['EmployeeNumber'];
            if (!hrimsEmployeeNumber) {
                reduceContext.write({
                    key: hrimsAttendanceId,
                    value: {
                        status: "error",
                        recordId: '',
                        error: ['Employee Number is not found in the HRIMS']
                    }
                });
                return false;
            }

            // Search for the HRIMS Employee in NetSuite
            let netSuiteEmployeeObj = _hrims_module.findEmployeeInNetsuite(hrimsEmployeeNumber);
            if (!netSuiteEmployeeObj.id) {
                reduceContext.write({
                    key: hrimsAttendanceId,
                    value: {
                        status: "error",
                        recordId: '',
                        error: [`Employee: ${hrimsEmployeeNumber} is not found in NetSuite`]
                    }
                });
                return false;
            }

            let netSuiteAttendanceId = _hrims_module.findAttendanceInNetsuite(hrimsAttendanceId);

            let recordActionResponse = {};
            if (netSuiteAttendanceId) {
                log.error("---ATTENDANCE UPDATION---", attendanceObj["DailyAttendanceRecordId"]);
                /****************************
                * ATTENDANCE RECORD UPDATION
                *****************************/
                recordActionResponse = updateAttendanceRecord(attendanceObj, netSuiteAttendanceId);
                log.error('Attendance Record Action', recordActionResponse);
            } else {
                log.error("---ATTENDANCE CREATION---", attendanceObj["DailyAttendanceRecordId"]);
                /****************************
                * ATTENDANCE RECORD CREATION
                *****************************/
                recordActionResponse = createAttendanceRecord(attendanceObj, hrimsEmployeeNumber, netSuiteEmployeeObj.id);
                log.error('Attendance Record Action', recordActionResponse);
            }
            reduceContext.write({ key: hrimsAttendanceId, value: recordActionResponse });
        } catch (error) {
            log.error("Error in reduce", error);
            reduceContext.write({ key: hrimsAttendanceId, value: { status: "error", recordId: null, error: [error.message] } });
        }
    }

    const summarize = (summaryContext) => {
        try {
            let titleArray = ["HRIMS ID", "Error Description"];
            let csvFileData = titleArray.toString() + 'rn';
            let flag = 0, fileID = "";
            summaryContext.output.iterator().each(function (key, value) {
                let parseSummary = JSON.parse(value);
                if (parseSummary.error.length > 0) {
                    flag = flag + 1;
                    let hrimsId = key
                    let errors = parseSummary.error.join(' ; ');
                    csvFileData += hrimsId + ',' + errors.replace(',', " ") + 'rn';
                }

                return true;
            });

            if (flag > 0) {
                let fileObj = file.create({
                    name: 'ERROR-File Created' + '-' + Math.floor(Date.now() / 1000) + '.csv',
                    fileType: file.Type.CSV,
                    folder: 1170,
                    contents: csvFileData
                });

                fileID = fileObj.save();
                log.error("Error File Created", fileID);
            }
            log.error("---ATTENDANCE SYNC COMPLETED---");
        } catch (error) {
            log.error("Error in summarize", error);
        }
    }

    return { getInputData, reduce, summarize }
});

Leave a comment

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