Firebase does not support patching, so for any updates, we need to delete the existing data and create it again.

Customer Sync to Firebase

/**
 * @NApiVersion 2.1
 * @NScriptType UserEventScript
 */
/**************************************************************************************************************************************
* Madi International Co LLC-UAE-SCA
*
* MICL-774 : E-receipt Management System for Payment Collection- NetSuite
* MICL-873 : Userevent Script to send the customer data to Sales Web App
* ************************************************************************************************************************************
 * Author : Jobin and Jismi IT Services
 *
 * Date Created : 13-February-2024
 *
 * Description 1.0.0 : This script is for sending customer data to firebase realtime
 *
 * REVISION HISTORY
 * Version 1.0.0 : 13-February-2024 : Created the initial build by JJ0300
 **************************************************************************************************************************************/
define(['N/https', 'N/record', '../Model/jj_cm_customer_model_micl_873.js', '../Library/jj_cm_ns_utility.js', '../ConfigManager/jj_cm_api_configurator_micl_774.js', 'N/runtime', 'N/format'],

    (https, record, model, jjUtil, config, runtime, format) => {
        /**
         * Defines the function definition that is executed before record is loaded.
         * @param {Object} scriptContext
         * @param {Record} scriptContext.newRecord - New record
         * @param {string} scriptContext.type - Trigger type; use values from the context.UserEventType enum
         * @param {Form} scriptContext.form - Current form
         * @param {ServletRequest} scriptContext.request - HTTP request information sent from the browser for a client action only.
         * @since 2015.2
         */
        const beforeLoad = (scriptContext) => {
            if (scriptContext.type == 'copy') {
                try {
                    log.debug('Executed beforeLoad', '');
                    let newRec = scriptContext.form;
                    newRec.setValue({
                        fieldId: 'custentity_jj_firebase_sync_status',
                        value: false
                    });
                    newRec.setValue({
                        fieldId: 'custentity_jj_last_firebase_sync_date',
                        value: ''
                    });
                    newRec.setValue({
                        fieldId: 'custentity_jj_fire_base_sync_error',
                        value: ''
                    });
                } catch (e) {
                    log.error('error @ beforeLoad', e);
                }
            }
        }




        /**
 * Sends customer data to Firebase, and based on the response from Firebase,
 * updates the custom fields in the customer record to indicate whether the synchronization was successful.
 *
 * @param {Object} customerData - The customer data object containing the information to be sent.
 * @param {String} recordType - The type of NetSuite record being processed (e.g., "Customer").
 * @param {String} customerInternalId - The internal ID of the customer in NetSuite.
 */
        function sendCustomerData(customerData, recordType, customerInternalId) {
            if (customerData) {
                const requestBody = createCustomerDocumentRequestBody(customerData);
                log.debug("customer requestBody", requestBody);
                let headerObj = {
                    name: 'Accept-Language',
                    value: 'en-us'
                };
                let url;
                url = config.generateUrl('customer', customerInternalId, 'createEntityCollection');
                log.debug("url", url);
                https.request.promise({
                    method: https.Method.POST,
                    url: url,
                    body: JSON.stringify(requestBody),
                    headers: headerObj
                })
                    .then(function (response) {
                        log.debug({
                            title: 'Customer Response',
                            details: response
                        });
                        let responseGot = JSON.parse(response.code);
                        log.debug('responseGot', responseGot);
                        if (responseGot == '200') {
                            record.submitFields({
                                type: 'customer',
                                id: customerInternalId,
                                values: {
                                    custentity_jj_firebase_sync_status: true,
                                    custentity_jj_last_firebase_sync_date: new Date(),
                                    custentity_jj_fire_base_sync_error: ''
                                }
                            });
                            sendAddressData(customerData);
                        }
                        else {
                            record.submitFields({
                                type: 'customer',
                                id: customerInternalId,
                                values: {
                                    custentity_jj_firebase_sync_status: false,
                                    custentity_jj_fire_base_sync_error: response
                                }
                            });
                        }
                    })
                    .catch(function onRejected(reason) {
                        log.debug({
                            title: 'Invalid Request: ',
                            details: reason
                        });
                    })


            }
        }
        /**
 * Deletes customer data from Firebase and triggers the address deletion if successful.
 *
 * @param {String} recordType - The type of NetSuite record being processed (e.g., "Customer").
 * @param {String} customerInternalId - The internal ID of the customer in NetSuite.
 * @param {Array} addressArray - An array of address internal IDs to be deleted.
 * @param {Object} customerData - The customer data object containing address information.
 */
        function deleteCustomerData(recordType, customerInternalId, addressArray, customerData) {
            let url = config.generateUrl(recordType, customerInternalId, 'deleteEntity');
            log.debug("Delete URL", url);

            https.request.promise({
                method: https.Method.DELETE,
                url: url,
            })
                .then(response => {
                    log.debug('Delete Customer Response', response);
                    if (response.code === 200) {
                        deleteAddressData(addressArray, customerData, customerInternalId);
                    } else {
                        log.error('Failed to delete the existing record in Firebase', response.body);
                    }
                })
                .catch(reason => {
                    log.error('Error deleting record from Firebase', reason);
                });
        };

        /**
         * Sends address data of the customer to the firebase.
         *
         * @param {Object} customerData - The customer data object containing address information to be sent.
         * @param {String} addressInternalId - The internal ID of the address in NetSuite to be sent.
         */
        function sendAddressData(customerData) {

            if (customerData.addresses) {
                log.debug('customer Data Address', customerData)
                for (i = 0; i < customerData.addresses.length; i++) {
                    log.debug('customerData.addresses[i]', customerData.addresses[i])
                    const requestBody = createAddressDocumentRequestBody(customerData.addresses[i], customerData.internalId);
                    log.debug("address requestBody", requestBody);
                    let headerObj = {
                        name: 'Accept-Language',
                        value: 'en-us'
                    };

                    let url;
                    let recordType = 'address';
                    url = config.generateUrl(recordType, customerData.addresses[i].addressInternalId, 'createEntityCollection');
                    log.debug("url", url);
                    https.request.promise({
                        method: https.Method.POST,
                        url: url,
                        body: JSON.stringify(requestBody),
                        headers: headerObj
                    })
                        .then(function (response) {
                            if (response.code == 200) {
                                record.submitFields({
                                    type: 'customer',
                                    id: customerData.internalId,
                                    values: {
                                        custentity_jj_firebase_sync_status: true,
                                        custentity_jj_last_firebase_sync_date: new Date(),
                                        custentity_jj_fire_base_sync_error: ''
                                    }
                                });
                                log.debug({
                                    title: ' Address Response',
                                    details: response
                                });
                            }
                        })
                        .catch(function onRejected(reason) {
                            log.debug({
                                title: 'Invalid Request: ',
                                details: reason
                            });
                        })
                }
            }
        }
        /**
 * Deletes address data from Firebase and triggers customer data sync.
 *
 * @param {Array} addressArray - An array of address internal IDs to be deleted.
 * @param {Object} customerData - The customer data object containing address information.
 * @param {String} customerInternalId - The internal ID of the customer in NetSuite.
 */
        function deleteAddressData(addressArray, customerData, customerInternalId) {
            if (customerData.addresses[0]) {

                for (i = 0; i < addressArray.length; i++) {
                    let url;
                    let recordType = 'address';
                    log.debug('addressArray[i]', addressArray[i])
                    log.debug('customerData.addresses.addressInternalId', customerData.addresses[0].addressInternalId)
                    if (addressArray[i] === customerData.addresses[0].addressInternalId) {
                        url = config.generateUrl(recordType, addressArray[i], 'deleteEntity');
                        log.debug("Delete Address url", url);
                        https.request.promise({
                            method: https.Method.DELETE,
                            url: url,
                        })
                            .then(function (response) {
                                log.debug('Delete Address Response', response);
                                if (response.code === 200) {
                                    record.submitFields({
                                        type: 'customer',
                                        id: customerInternalId,
                                        values: {
                                            custentity_jj_firebase_sync_status: false,
                                            custentity_jj_fire_base_sync_error: response
                                        }
                                    });
                                    recordType = 'customer'
                                    sendCustomerData(customerData, recordType, customerInternalId);

                                }
                            })
                            .catch(function onRejected(reason) {
                                log.debug({
                                    title: 'Invalid Request: ',
                                    details: reason
                                });
                            })
                    }
                }
            } else {
                log.debug('addresselse')
                recordType = 'customer'
                sendCustomerData(customerData, recordType, customerInternalId);
            }
        }

        /**
         * Creates a request body for sending customer data.
         *
         * @param {Object} customerData - The customer data to be sent.
         * @returns {Object} The request body formatted for Firebase.
         */
        const createCustomerDocumentRequestBody = (customerData) => {
            log.debug('dateCreated', customerData.dateCreated);
            let dateCreate = format.parse({
                value: customerData.dateCreated,
                type: format.Type.DATE
            });
            log.debug('dateCreate', dateCreate);
            let dateModify = format.parse({
                value: customerData.dateModified,
                type: format.Type.DATE
            });
            log.debug('dateModify', dateModify);

            // Extracting only the last part of the subsidiary name
            // let subsidiaryName = customerData?.subsidiary ? customerData.subsidiary.split(':').pop().trim() : "";
            let countryCode = getCountryCode(customerData.country)
            let subsidiariesArray = customerData.subsidiaries ? customerData.subsidiaries.split(',').map(subsidiary => subsidiary.trim()) : [];
            let subsidiaryStringArray = subsidiariesArray.map(subsidiary => ({ stringValue: subsidiary }))
            //let currencyStringArray = customerData.currency.map(item => ({ stringValue: item.currency }));
            let currencyStringArray = customerData.currency.map(item => item.currency).join(',');
            log.debug("currencyStringArray",currencyStringArray)
            currencyStringArray = [...new Set(customerData.currency.map(item => item.currency))].join(',');
            log.debug('after format',currencyStringArray);
            let requestBody = {
                fields: {
                    date_created: {
                        timestampValue: dateCreate
                    },
                    date_modified: {
                        timestampValue: dateModify
                    },
                    full_name: {
                        stringValue: customerData?.companyName || ""
                    },
                    email: {
                        stringValue: customerData?.email || ""
                    },
                    primary_subsidiary: {
                        stringValue: customerData?.subsidiary // Set only the last part of the subsidiary
                    },
                    subsidiaries: {
                        arrayValue: {
                            values: subsidiaryStringArray
                        }
                    },
                    salesrep: {
                        stringValue: customerData?.salesRep || ""
                    },
                    country: {
                        stringValue: countryCode || ""
                    },
                    city: {
                        stringValue: customerData?.city || ""
                    },
                    state: {
                        stringValue: customerData?.state || ""
                    },
                    status: {
                        stringValue: customerData?.status || ""
                    },
                    code: {
                        stringValue: customerData?.code || ""
                    },
                    vat_number: {
                        stringValue: customerData?.vat || ""
                    },
                    currency: {
                        stringValue: currencyStringArray || ""
                    }
                }
            };

            return requestBody;
        }

        /**
 * Creates a request body for sending address data of a customer.
 *
 * @param {Object} addresses - The customer data including address information.
 * @param {String} customerInternalId - The internal ID of the customer in NetSuite.
 * @returns {Object} The request body formatted for Firebase, focusing on address information.
 */
        const createAddressDocumentRequestBody = (addresses, customerInternalId) => {
            let requestBody = {
                fields: {
                    country: {
                        stringValue: addresses?.country || ""
                    },
                    attention: {
                        stringValue: addresses?.attention || ""
                    },
                    addressee: {
                        stringValue: addresses?.addressee || ""
                    },
                    phone: {
                        stringValue: addresses?.addressPhone || ""
                    },
                    address_line_1: {
                        stringValue: addresses?.address1 || ""
                    },
                    address_line_2: {
                        stringValue: addresses?.address2 || ""
                    },
                    city: {
                        stringValue: addresses?.city || ""
                    },
                    zip: {
                        stringValue: addresses?.zipCode || ""
                    },
                    state: {
                        stringValue: addresses?.state || ""
                    },
                    cu_id: {
                        referenceValue: `projects/e-receipts-app-efcfe/databases/(default)/documents/customer/${customerInternalId}` || ""
                    }
                }
            };

            return requestBody;

        }
        /**
 * Compares two objects or arrays recursively to check if they are deeply equal.
 *
 * This function checks if two objects (or arrays) have the same structure and content.
 * @param {Object|Array} obj1 - The first object or array to compare.
 * @param {Object|Array} obj2 - The second object or array to compare.
 * @returns {Boolean} - Returns `true` if both objects/arrays are deeply equal, otherwise `false`.
 */
        function compareObjects(obj1, obj2) {
            // Check if both arguments are objects and not null
            if (typeof obj1 === 'object' && obj1 !== null && typeof obj2 === 'object' && obj2 !== null) {

                // Iterate through the properties of obj1
                for (let key in obj1) {
                    // Ensure obj2 has the same key
                    if (!obj2.hasOwnProperty(key)) {
                        return false;
                    }

                    // Recursively compare the values if they are objects or arrays
                    if (!compareObjects(obj1[key], obj2[key])) {
                        return false;
                    }
                }

                return true;
            } else {
                // If the values are not objects (i.e., primitives), directly compare them
                return obj1 === obj2;
            }
        }

        /**
         * Retrieves the country code for a given country name.
         *
         * This function takes a country name as input and returns the corresponding
         * ISO 3166-1 alpha-2 country code. If the country name is not found in the
         * predefined list, it returns "Country code not found".
         *
         * @param {string} countryName - The name of the country for which to retrieve the code.
         * @returns {string} The corresponding country code or a message indicating the code was not found.
         *
         */
        function getCountryCode(countryName) {
            const countryCodes = {
                "Afghanistan": "AF",
                "Albania": "AL",
                "Aland Islands": "AX"
            };


            return countryCodes[countryName] || "Country code not found";
        }

        /**
         * Defines the function definition that is executed after record is submitted.
         * @param {Object} scriptContext
         * @param {Record} scriptContext.newRecord - New record
         * @param {Record} scriptContext.oldRecord - Old record
         * @param {string} scriptContext.type - Trigger type; use values from the context.UserEventType enum
         * @since 2015.2
         */
        const afterSubmit = (scriptContext) => {
            try {
                log.debug('Script Executed', '');
                let customerRecord = scriptContext.newRecord;
                let customerInternalId = scriptContext.newRecord.id;
                let recordType = scriptContext.newRecord.type;
                let contextType = scriptContext.type;
                let executionContext = runtime.executionContext;
                let subsidiary = customerRecord.getValue('subsidiary')
                let entityStatus = customerRecord.getValue({ fieldId: 'entitystatus' });
                const CLOSED_WON_ID = '13';
                const lookupCurrencies = {
                    1: "AED",
                    3: "Canadian Dollar",
                    4: "Euro",
                    5: "USD",
                    6: "OMR",
                    7: "BHD",
                    8: "LBP",
                    9: "CHF",
                    10: "GBP",
                    11: "QAR",
                    12: "ZAR",
                    13: "SAR",
                    14: "KWD",
                    15: "SEK"
                };

                if (recordType === 'prospect') {
                    if (contextType !== 'edit') {
                        log.debug("Skipping: Not an edit operation for Prospect");
                        return;
                    }
                    if (entityStatus !== CLOSED_WON_ID) {
                        log.debug("Skipping: Prospect is not changed to Closed - Won");
                        return;
                    }
                }

                if (contextType == 'create' || contextType == 'xedit' || contextType == 'edit' || executionContext == runtime.ContextType.CSV_IMPORT) {
                    log.debug("recordType", recordType)

                    let syncStatus = customerRecord.getValue({ fieldId: 'custentity_jj_firebase_sync_status' });
                    let customerData = model.getCustomerData({
                        filterConfig: {
                            customerInternalId: customerInternalId
                        },
                        searchConfig: {},
                        contextType
                    });

                    if (contextType === 'edit') {
                        let oldRecord = scriptContext.oldRecord;
                        let oldEntityStatus = oldRecord.getValue({ fieldId: 'entitystatus' });

                        if (recordType === 'prospect' && oldEntityStatus === entityStatus) {
                            log.debug("Skipping: Entity Status was not changed");
                            return;
                        }

                        let oldRecordObj = {}

                        oldRecordObj.internalId = oldRecord.getValue({ fieldId: 'id' })
                        oldRecordObj.dateCreated = oldRecord.getText({ fieldId: 'datecreated' }).split(':').slice(0, 2).join(':') + ' ' + oldRecord.getText({ fieldId: 'datecreated' }).split(' ')[2];
                        // oldRecordObj.dateModified = oldRecord.getText({ fieldId: 'lastmodifieddate' }).split(':').slice(0, 2).join(':') + ' ' + oldRecord.getText({ fieldId: 'lastmodifieddate' }).split(' ')[2];
                        oldRecordObj.email = oldRecord.getValue({ fieldId: 'email' });
                        oldRecordObj.salesRep = oldRecord.getText({ fieldId: 'salesrep' });
                        oldRecordObj.isPerson = oldRecord.getValue({ fieldId: 'isperson' }) == 'F' ? false : true
                        oldRecordObj.companyName = oldRecord.getText({ fieldId: 'companyname' });
                        oldRecordObj.subsidiary = oldRecord.getText({ fieldId: 'subsidiary' });
                        oldRecordObj.phone = oldRecord.getText({ fieldId: 'phone' });
                        oldRecordObj.status = oldRecord.getValue({ fieldId: 'isinactive' }) ? "INACTIVE" : "ACTIVE"
                        oldRecordObj.syncStatus = oldRecord.getValue({ fieldId: 'custentity_jj_firebase_sync_status' });
                        oldRecordObj.city = oldRecord.getValue({ fieldId: 'custentity_city' });
                        oldRecordObj.state = oldRecord.getText({ fieldId: 'custentity33' });
                        oldRecordObj.country = oldRecord.getText({ fieldId: 'custentity_country' });
                        oldRecordObj.code = oldRecord.getValue({ fieldId: 'entityid' }).split(' ')[0];
                        oldRecordObj.vat = oldRecord.getText({ fieldId: 'vatregnumber' });

                        let addressArray = []
                        let addressLineCount = oldRecord.getLineCount('addressbook')
                        log.debug('addressLineCount', addressLineCount)

                        for (let i = 0; i < addressLineCount; i++) {
                            log.debug(oldRecord.getSublistValue({
                                sublistId: 'addressbook',
                                fieldId: 'defaultbilling',
                                line: i
                            }))
                            if (oldRecord.getSublistValue({
                                sublistId: 'addressbook',
                                fieldId: 'defaultbilling',
                                line: i
                            })) {
                                addressArray.push({
                                    addressInternalId: oldRecord.getSublistValue({
                                        sublistId: 'addressbook',
                                        fieldId: 'id',
                                        line: i
                                    }).toString(),
                                    address1: oldRecord.getSublistValue({
                                        sublistId: 'addressbook',
                                        fieldId: 'addr1_initialvalue',
                                        line: i
                                    }),
                                    address2: oldRecord.getSublistValue({
                                        sublistId: 'addressbook',
                                        fieldId: 'addr2_initialvalue',
                                        line: i
                                    }),
                                    addressee: oldRecord.getSublistValue({
                                        sublistId: 'addressbook',
                                        fieldId: 'addressee_initialvalue',
                                        line: i
                                    }),
                                    addressPhone: oldRecord.getSublistValue({
                                        sublistId: 'addressbook',
                                        fieldId: 'phone_initialvalue',
                                        line: i
                                    }),
                                    attention: oldRecord.getSublistValue({
                                        sublistId: 'addressbook',
                                        fieldId: 'attention_initialvalue',
                                        line: i
                                    }),
                                    city: oldRecord.getSublistValue({
                                        sublistId: 'addressbook',
                                        fieldId: 'city_initialvalue',
                                        line: i
                                    }),
                                    state: oldRecord.getSublistValue({
                                        sublistId: 'addressbook',
                                        fieldId: 'state_initialvalue',
                                        line: i
                                    }),
                                    zipCode: oldRecord.getSublistValue({
                                        sublistId: 'addressbook',
                                        fieldId: 'zip_initialvalue',
                                        line: i
                                    }),
                                    country: oldRecord.getSublistText({
                                        sublistId: 'addressbook',
                                        fieldId: 'addressbookaddress_text',
                                        line: i
                                    }).trim().split(/s+/).pop(),

                                })
                            }
                        }
                        let currencyArray = []
                        let currencyLineCount = oldRecord.getLineCount('currency')
                        log.debug('currencyLineCount', currencyLineCount)

                        for (let i = currencyLineCount-1; i > -1; i--) {
                            let curId = oldRecord.getSublistValue({
                                sublistId: 'currency',
                                fieldId: 'currency',
                                line: i
                            });
                            currencyArray.push({
                                currency: lookupCurrencies[curId] || "Unknown Currency"
                            });
                        }

                        oldRecordObj.currency = currencyArray
                        oldRecordObj.addresses = addressArray



                        let subsidiaryArray = []
                        let subsidiarylineCount = oldRecord.getLineCount("submachine");
                        log.debug('lineCount', subsidiarylineCount)


                        for (let i = 0; i < subsidiarylineCount; i++) {

                            let subsidiary = oldRecord.getSublistText({
                                sublistId: 'submachine',
                                fieldId: 'subsidiary',
                                line: i
                            })
                            log.debug('subsidiary', subsidiary)
                            subsidiaryArray.push(subsidiary)

                        }

                        oldRecordObj.subsidiaries = subsidiaryArray.join(",");

                        log.debug('C oldRecordObj', oldRecordObj)
                        log.debug('C customerData', customerData)
                        // // Compare fields of interest between old and new records
                        let compareResult = compareObjects(oldRecordObj, customerData)

                        log.debug('compareResult', compareResult)



                        // if (compareResult && syncStatus === true) {
                        //     log.debug('No changes detected. Skipping sync.');
                        //     return; // Exit the script if no changes are detected
                        // }
                    }





                    let addressArray = []
                    let addressLineCount = customerRecord.getLineCount({
                        sublistId: 'addressbook'
                    })
                    let addressId;
                    for (let i = 0; i < addressLineCount; i++) {
                        addressId = customerRecord.getSublistValue({
                            sublistId: 'addressbook',
                            fieldId: 'addressid',
                            line: i
                        })
                        addressArray.push(addressId)
                    }

                    log.debug('addressArray', addressArray)
                    log.debug("customerData", customerData);
                    let length = customerData.addresses.length;
                    log.debug('length', length);


                    if (contextType === 'edit' && syncStatus != true) {
                        log.debug('if1')
                        sendCustomerData(customerData, recordType, customerInternalId);
                    } else if (contextType == 'create' && customerData) {
                        log.debug('if2')
                        sendCustomerData(customerData, recordType, customerInternalId);
                    } else if (contextType === 'edit' && syncStatus === true) {
                        log.debug('if3')
                        deleteCustomerData(recordType, customerInternalId, addressArray, customerData)


                    } else {
                        log.debug('', 'Record is already synced or no data found.');
                    }



                }

            } catch (error) {
                log.error("Error @afterSubmit", error)
            }
        }

        return { beforeLoad, afterSubmit }

    });

Leave a comment

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