UI/UILib.js

/**
 * @description UI utility to create vaious parts of UI in the connector.
 * It contains various important utils.
 *
 * @NScriptContext Server
 * @NApiVersion 2.x
 *
 * @module UI/UILib
 */

define(['N/ui/serverWidget', 'N/search', 'N/record', 'N/url', 'N/format', '../Utilities/utility', './moment.js', '../Utilities/RESTGetFields', '../Utilities/RESTSearch'], function (serverWidget, search, record, url, format, utility, moment, RESTGetFields, RESTSearch) {

    // TODO: Figure way to handle invalid fields
    var invalidFields = [
        '_eml_nkey_',
        'generatetranidonsave',
        'advance',
        'advanceaccount',
        'id',
        'complete',
        'entity_nexus_country',
        'transactionnumber',
        'totalbasecurrency',
        'entitynexus',
        'nonreimbursable',
        'numrules',
        'orderline',
        'password2',
        'customform',
        'newaccesshelp',
        'giveaccess',
        'fillpassword',
        'password',
        'requirepwdchange',
        'sendtransactionsvia',
        'sendemail',
    ];

    var sublistLabels = {
        'vendorbill': {'item': 'Item', 'expense': 'Expense'},
        'journalentry': {'line': 'Line'},
        'advintercompanyjournalentry': {'line': 'Line'},
        'expensereport': {'expense': 'Expense'}
    };

    var vendorSyncMappingFields = {
        'Internal ID': 'vendor_internal_id',
        'External ID': 'vendor_external_id',
        'All Subsidiary Ids': 'vendor_allsubsidiaries_ids',
        'All Subsidiary Legal Names': 'vendor_allsubsidiaries_legalnames',
        'All Subsidiary Names': 'vendor_allsubsidiaries_names',
        'Expense Account Name': 'vendor_expenseaccount_acctname',
        'Expense Account Number': 'vendor_expenseaccount_acctnumber',
        'Subsidiary Name': 'vendor_subsidiary_name',
        'Subsidiary Legal Name': 'vendor_subsidiary_legalname',
        'Subsidiary Ids': 'vendor_subsidiaries_ids',
        'Subsidiary Names': 'vendor_subsidiaries_names',
        'Subsidiary Legal Names': 'vendor_subsidiaries_legalnames',
        'Term Days Until Net Due': 'vendor_terms_daysuntilnetdue'
    };

    var vendorBillHeaderFields = {
        'PO ID': 'poid',
        'Purchase Order Number': 'purchaseordernumber',
        'Vendor Internal ID': 'entityid',
        'Vendor External ID': 'entity_external_id',
        'Account Internal ID': 'accountid',
        'Account External ID': 'account_external_id',
        'Location Internal ID': 'locationid',
        'Location External ID': 'location_external_id',
        'Class Internal ID': 'classid',
        'Class External ID': 'class_external_id',
        'Currency ID': 'currencyid'
    };
    var vendorBillLineFields = {
        'item': {
            'PO ID': 'poid',
            'Receipt ID': 'receiptid',
            'Receipt Number': 'receiptnumber',
            'Customer Internal ID': 'customerid',
            'Customer External ID': 'customer_external_id',
            'Project Internal ID': 'projectid',
            'Project External ID': 'project_external_id',
            'Department Internal ID': 'departmentid',
            'Department External ID': 'department_external_id',
            'Location Internal ID': 'locationid',
            'Location External ID': 'location_external_id',
            'Journal ID': 'journalid',
            'Journal External ID': 'journal_external_id'
        },
        'expense': {
            'Customer Internal ID': 'customerid',
            'Customer External ID': 'customer_external_id',
            'Project Internal ID': 'projectid',
            'Project External ID': 'project_external_id',
            'Account Internal ID': 'accountid',
            'Account External ID': 'account_external_id',
            'Department Internal ID': 'departmentid',
            'Department External ID': 'department_external_id',
            'Class Internal ID': 'classid',
            'Class External ID': 'class_external_id',
            'Location Internal ID': 'locationid',
            'Location External ID': 'location_external_id',
            'Shipping Amount': 'shippingamount',
            'Shipping Account': 'shippingaccount',
            'Shipping Account ID': 'shippingaccountid',
            'Shipping Memo': 'shippingmemo',
            'Tax Amount': 'taxamount',
            'Tax Account': 'taxaccount',
            'Tax Account ID': 'taxaccountid',
            'Tax Memo': 'taxmemo',
            'Journal ID': 'journalid',
            'Journal External ID': 'journal_external_id'
        }
    };

    var vendorPaymentHeaderFields = {
        'Vendor Internal ID': 'entityid',
        'Vendor External ID': 'entity_external_id',
        'Account Internal ID': 'accountid',
        'Account External ID': 'account_external_id',
        'Location Internal ID': 'locationid',
        'Location External ID': 'location_external_id',
        'Department Internal ID': 'departmentid',
        'Department External ID': 'department_external_id',
        'Class Internal ID': 'classid',
        'Class External ID': 'class_external_id',
        'Dynamic': 'isDynamic'
    };

    var expenseReportHeaderFields = {
        'Employee Internal ID': 'entityid',
        'Employee External ID': 'entity_external_id',
        'Account Internal ID': 'accountid',
        'Account External ID': 'account_external_id',
        'Location Internal ID': 'locationid',
        'Location External ID': 'location_external_id',
        'Currency ID': 'expensereportcurrencyid'
    };
    var expenseReportLineFields = {
        'expense': {
            'Category ID': 'categoryid',
            'Customer Internal ID': 'customerid',
            'Customer External ID': 'customer_external_id',
            'Class Internal ID': 'classid',
            'Class External ID': 'class_external_id',
            'Department Internal ID': 'departmentid',
            'Department External ID': 'department_external_id',
            'Location Internal ID': 'locationid',
            'Location External ID': 'location_external_id',
            'Journal ID': 'journalid',
            'Journal External ID': 'journal_external_id',
            'Currency ID': 'currencyid',
        }
    };

    var journalEntryFields = {
        'Subsidiary Internal ID': 'subsidiaryid',
        'Subsidiary External ID': 'subsidiary_external_id',
    };
    var journalEntryLineFields = {
        'line': {
            'Entity Internal ID': 'entityid',
            'Entity External ID': 'entity_external_id',
            'Account Internal ID': 'accountid',
            'Account External ID': 'account_external_id',
            'Credit Account': 'creditaccount',
            'Credit Account Internal ID': 'creditaccountid',
            'Credit Account External ID': 'creditaccount_external_id',
            'Location Internal ID': 'locationid',
            'Location External ID': 'location_external_id',
            'Department Internal ID': 'departmentid',
            'Department External ID': 'department_external_id',
            'Class Internal ID': 'classid',
            'Class External ID': 'class_external_id',
            'Journal ID': 'journalid',
            'Journal External ID': 'journal_external_id',
        }
    };
    var advJournalEntryLineFields = {
        'line': {
            'Subsidiary Internal ID': 'linesubsidiaryid',
            'Subsidiary External ID': 'linesubsidiary_external_id',

            'AP Account': 'apaccount',
            'AP Account Internal ID': 'apaccountid',
            'AP Account External ID': 'apaccount_external_id',

            'AR Account': 'araccount',
            'AR Account Internal ID': 'araccountid',
            'AR Account External ID': 'araccount_external_id',

            'AP Entity': 'apentity',
            'AP Entity Internal ID': 'apentityid',
            'AP Entity External ID': 'apentity_external_id',

            'AR Entity': 'arentity',
            'AR Entity Internal ID': 'arentityid',
            'AR Entity External ID': 'arentity_external_id',

            'AR Department': 'ardepartment',
            'AR Department Internal ID': 'ardepartmentid',
            'AR Department External ID': 'ardepartment_external_id',
        }
    };

    var applySublistFields = {
        'Amount Due': 'due',
        'Apply': 'apply',
        'Date Due': 'applydate',
        'Disc. Amount': 'discamt',
        'Disc. Date': 'discdate',
        'Disc. Taken': 'disc',
        'Orig. Amt': 'total',
        'Payment': 'amount',
        'Ref No.': 'refnum'
    };

    var addressBookSublistFields = {
        'Address ID': 'addressbook_addressid',
        'Address': 'addressbook_addrtext',
        'Attention': 'addressbook_attention',
        'City': 'addressbook_city',
        'Country': 'addressbook_country',
        'Default Billing': 'addressbook_defaultbilling',
        'Default Shipping': 'addressbook_defaultshipping',
        'State/Province': 'addressbook_state',
        'Phone': 'addressbook_phone',
        'Zip': 'addressbook_zip'
    };

    var achAccountSublistFields = {
        'Account Number': 'achacct_accountnumber',
        'Addenda': 'achacct_addenda',
        'Bank Name': 'achacct_bankname',
        'Include Transaction In Addenda': 'achacct_includetransaction',
        'Limit': 'achacct_limit',
        'Routing Number': 'achacct_routingnumber',
        'Savings Account': 'achacct_issavings',
        'Send Addenda': 'achacct_sendaddenda'
    };

    var bankAccountSublistFields = null;

    var itemReceiptSublistFields = {
        'Class': 'class',
        'Department': 'department',
        'Description': 'itemdescription',
        'Item': 'item',
        'Item Name': 'itemname',
        'Location': 'location',
        'On Hand': 'onhand',
        'Options': 'options',
        'Quantity': 'quantity',
        'Receive': 'itemreceive',
    };

    var purchaseOrderVendorSublistFields = {
        'Vendor ID': 'purchaseorder_vendor_entityid',
        'Vendor External ID': 'purchaseorder_vendor_entity_external_id',
        'Vendor Name': 'purchaseorder_vendor_companyname',
        'Vendor Category': 'purchaseorder_vendor_category',
        'Vendor Billing Address ID': 'purchaseorder_vendor_billaddressid',
        'Vendor Billing Label': 'purchaseorder_vendor_billlabel',
        'Vendor Billing Address1': 'purchaseorder_vendor_billaddr1',
        'Vendor Billing Addressee': 'purchaseorder_vendor_billaddressee',
        'Vendor Billing City': 'purchaseorder_vendor_billcity',
        'Vendor Billing Country': 'purchaseorder_vendor_billcountry',
        'Vendor Billing State': 'purchaseorder_vendor_billstate',
        'Vendor Billing Zip': 'purchaseorder_vendor_billzip'
    };

    var purchaseOrderItemSublistFields = {
        'Item ID': 'itemid',
        'Item Type Name': 'itemtypename',
        'Item Display Name': 'displayname',
        'Item Subsidiary': 'subsidiary',
        'Item COGS Account ID': 'cogsacctid',
        'Item COGS Account': 'cogsacctnumber',
        'Item COGS Account Name': 'cogsacctname',
        'Item Expense Account ID': 'expenseacctid',
        'Item Expense Account': 'expenseacctnumber',
        'Item Expense Account Name': 'expenseacctname',
        'Item Asset Account ID': 'assetacctid',
        'Item Asset Account': 'assetacctnumber',
        'Item Asset Account Name': 'assetacctname',
        'Item Account ID': 'acctid',
        'Item Account': 'acctnumber',
        'Item Account Name': 'acctname',
    };

    function getLinkValue(linkText, url) {
        return '<span class="uir-list-row-cell"><a class="dottedlink" href="' + url + '" target="fldUrlWindow">' + linkText + '</a></span>';
    }

    /**
     * Create Sublist on the Form based on the provided config
     * @memberof module:UI/UILib
     * @param {Form} form
     * @param {ResultSet} searchResults
     * @param {Object} sublistConfigObj
     * @param {String} sublistConfigObj.sublistId
     * @param {String} sublistConfigObj.sublistLabel
     * @param {Boolean} sublistConfigObj.createButton
     * @param {Boolean} sublistConfigObj.exportButton
     * @param {Boolean} sublistConfigObj.refreshButton
     * @param {Boolean} sublistConfigObj.deleteButton
     * @param {Boolean} sublistConfigObj.deleteBox
     * @param {Boolean} sublistConfigObj.previousButton
     * @param {Boolean} sublistConfigObj.nextButton
     * @param {Number} sublistConfigObj.currPage
     * @param {String} sublistConfigObj.scriptId
     * @param {String} sublistConfigObj.deploymentId
     * @param {String} sublistConfigObj.exportFunctionName Function to be called for export button e.g. `export()`
     * @param {Object} sublistConfigObj.editLinkParams
     * @param {Object} sublistConfigObj.exportJSONParams
     * @param {Boolean} sublistConfigObj.liveReloadEnabled Should live reload be enabled
     * @param {String} sublistConfigObj.tab Tab ID
     * @returns {Sublist} sublist
     */
    function createSublist(form, searchResults, sublistConfigObj) {
        var id = sublistConfigObj.sublistId;
        var label = sublistConfigObj.sublistLabel;
        var scriptId = sublistConfigObj.scriptId;
        var deploymentId = sublistConfigObj.deploymentId;
        var createButton = sublistConfigObj.createButton;
        var deleteButton = sublistConfigObj.deleteButton;
        var refreshButton = sublistConfigObj.refreshButton;
        var importButton = sublistConfigObj.importButton;
        var exportButton = sublistConfigObj.exportButton;
        var previousButton = sublistConfigObj.previousButton;
        var nextButton = sublistConfigObj.nextButton;
        var currPage = sublistConfigObj.currPage;
        var exportFunctionName = sublistConfigObj.exportFunctionName;
        var editLinkParams = sublistConfigObj.editLinkParams;
        var exportJSONParams = sublistConfigObj.exportJSONParams;
        var liveReloadEnabled = sublistConfigObj.liveReloadEnabled;
        var tab = sublistConfigObj.tab;
        var sublist = form.addSublist({id: id, label: label, type: serverWidget.SublistType.LIST, tab: tab});
        if (createButton) sublist.addButton({id: 'custbutton_create', label: 'Create', functionName: 'redirectToCreateForm();'});
        if (deleteButton) sublist.addButton({id: 'custbutton_delete', label: 'Delete', functionName: 'deleteRecords(\"' + id + '\");'});
        if (refreshButton) sublist.addRefreshButton();
        if (importButton) sublist.addButton({id: 'custbutton_import', label: 'Import', functionName: 'importCSVButtonOnClick();'});
        if (exportButton) sublist.addButton({id: 'custbutton_export', label: 'Export', functionName: exportFunctionName});
        var newPage = 0;
        if (previousButton) {
            newPage = parseInt(currPage) - 1;
            sublist.addButton({id: 'custbutton_previous', label: 'Prev', functionName: 'redirectToPage(' + newPage + ');'});
        }
        if (nextButton) {
            newPage = parseInt(currPage) + 1;
            sublist.addButton({id: 'custbutton_next', label: 'Next', functionName: 'redirectToPage(' + newPage + ');'});
        }

        if (liveReloadEnabled) {
            form.addField({
                id: 'cust_live_reload_' + id,
                label: 'Should Live Reload ' + id,
                type: serverWidget.FieldType.CHECKBOX,
            }).updateDisplayType({displayType: serverWidget.FieldDisplayType.HIDDEN})
                .defaultValue = 'T'
        };

        for (var i = 0; i < searchResults.length; i++) {
            var columns = searchResults[i].columns;
            if (i === 0) {
                createSublistFields(columns, sublist, sublistConfigObj, editLinkParams);
            }
            var internalId = searchResults[i].id;
            var recordType = searchResults[i].recordType;
            sublist.setSublistValue({id: 'internalid', line: i, value: internalId});
            sublist.setSublistValue({id: 'recordtype', line: i, value: recordType});
            if (!isEmpty(editLinkParams)) {
                editLinkParams.record_id = internalId;
                var link = url.resolveScript({scriptId: scriptId, deploymentId: deploymentId, params: editLinkParams});
                if (recordType === 'customrecordbritt_ec_batch_report_vouche') {
                    editLinkParams.record_id = searchResults[i].getValue({name: 'custrecordbatchreportvoucher'});
                    link = url.resolveScript({scriptId: scriptId, deploymentId: deploymentId, params: editLinkParams});
                }
                else if (recordType === 'customrecordbritt_ic_payment_batch_req_v') {
                    editLinkParams.record_id = searchResults[i].getValue({name: 'custrecordreqvouchervoucher'});
                    link = url.resolveScript({scriptId: scriptId, deploymentId: deploymentId, params: editLinkParams});
                }
                if (recordType === 'scriptdeployment') {
                    link = url.resolveRecord({recordType: 'scriptdeployment', recordId: internalId, isEditMode: true});
                }
                sublist.setSublistValue({id: 'link', line: i, value: link});
            }
            if (!isEmpty(exportJSONParams)) {
                exportJSONParams.record_id = internalId;
                var exportLink = url.resolveScript({scriptId: 'customscript_ec_sl_json_export', deploymentId: 'customdeploy_ec_sl_json_export', params: exportJSONParams});
                sublist.setSublistValue({id: 'exportlink', line: i, value: exportLink});
            }
            for (var j = 0; j < columns.length; j++) {
                var column = columns[j];
                var fieldId = column.name;
                var fieldType = JSON.parse(JSON.stringify(column)).type;
                var value = isEmpty(searchResults[i].getText(column)) ? searchResults[i].getValue(column) : searchResults[i].getText(column);
                if (!isEmpty(value)) {
                    switch (fieldType) {
                        case 'checkbox':
                            sublist.setSublistValue({id: fieldId, line: i, value: value ? 'T' : 'F'});
                            break;
                        default:
                            if (fieldId === 'custrecordbatchreportvoucher' || fieldId === 'custrecordreqvouchervoucher') {
                                var voucherRecordDetails = value.split(':');
                                if (!isEmpty(voucherRecordDetails) && voucherRecordDetails.length > 1 && value !== 'undefined:') {
                                    var urlLink = url.resolveRecord({
                                        recordType: voucherRecordDetails[0],
                                        recordId: voucherRecordDetails[1]
                                    });
                                    // To dynamically show the Link label we have to use either textarea or text.
                                    // We can embedd html in them.
                                    sublist.setSublistValue({id: fieldId, line: i, value: getLinkValue(value, urlLink)});
                                }
                            } else if (fieldId === 'custrecordvendorsyncvendor') {
                                var vendorId = searchResults[i].getValue(column);
                                var urlLink = url.resolveRecord({recordType: 'vendor', recordId: vendorId});
                                // Field is TextAarea so add custom HTML link instaed of setting url directly
                                sublist.setSublistValue({id: fieldId, line: i, value: getLinkValue('Details', urlLink)});
                            } else if (fieldId === 'custrecordposyncpurchaseorder') {
                                var purchaseOrderId = searchResults[i].getValue(column);
                                var urlLink = url.resolveRecord({recordType: 'purchaseorder', recordId: purchaseOrderId});
                                var poRecordDetails = search.lookupFields({type: 'purchaseorder', id: purchaseOrderId, columns: ['tranid']});
                                var tranId = poRecordDetails.tranid;
                                sublist.setSublistValue({id: 'polink', line: i, value: urlLink});
                                sublist.setSublistValue({id: 'poid', line: i, value: purchaseOrderId});
                                sublist.setSublistValue({id: 'potranid', line: i, value: tranId});
                                sublist.getField({id: 'polink'}).linkText = 'Details';
                            } else if (fieldId === 'custrecordposyncreceipt') {
                                var receiptId = searchResults[i].getValue(column);
                                var urlLink = url.resolveRecord({recordType: 'itemreceipt', recordId: receiptId});
                                var receiptDetails = search.lookupFields({type: 'itemreceipt', id: receiptId, columns: ['tranid']});
                                var tranId = receiptDetails.tranid;
                                sublist.setSublistValue({id: 'receiptlink', line: i, value: urlLink});
                                sublist.setSublistValue({id: 'receiptid', line: i, value: receiptId});
                                sublist.setSublistValue({id: 'receipttranid', line: i, value: tranId});
                                sublist.getField({id: 'receiptlink'}).linkText = 'Details';
                            }
                            else if (fieldId === 'custrecordresultmessage') {
                                if (value.length > 100) value = value.substring(0, 100) + (' ...more');
                                else {
                                    var gapLength = 108 - value.length;
                                    value = value + new Array(gapLength + 1).join(' ');
                                }
                                sublist.setSublistValue({id: fieldId, line: i, value: value});
                            } else if (fieldId === 'formulatext') {
                                var startTime = searchResults[i].getValue({name: 'custrecordsync_starttime'});
                                var endTime = searchResults[i].getValue({name: 'custrecordsync_finishtime'});
                                if (!isEmpty(startTime) && !isEmpty(endTime)) {
                                    var durationInMinutes = calculateDurationInMinutes(startTime, endTime);
                                    sublist.setSublistValue({id: 'formulatext', line: i, value: durationInMinutes});
                                } else {
                                    sublist.setSublistValue({id: 'formulatext', line: i, value: ' '});
                                }
                            }
                            else {
                                sublist.setSublistValue({id: fieldId, line: i, value: value});
                            }
                            break;
                    }
                }
            }
        }
        return sublist;
    }

    function createSublistFields(columns, sublist, sublistConfigObj, editLinkParams) {
        var deleteBox = sublistConfigObj.deleteBox;
        var inactiveBox = sublistConfigObj.inactiveBox;
        var exportJSONParams = sublistConfigObj.exportJSONParams;
        if (deleteBox) sublist.addField({id: 'delete', label: 'Delete', type: serverWidget.FieldType.CHECKBOX});
        if (editLinkParams) {
            var linkSublistField = sublist.addField({id: 'link', label: 'Link', type: serverWidget.FieldType.URL});
            linkSublistField.linkText = editLinkParams.label;
        }
        if (inactiveBox) sublist.addField({id: 'isinactive', label: 'Inactive', type: serverWidget.FieldType.CHECKBOX}).updateDisplayType({displayType: serverWidget.FieldDisplayType.INLINE});
        var internalIdSublistField = sublist.addField({id: 'internalid', label: 'Internal Id', type: serverWidget.FieldType.TEXT});
        var recordTypeSublistField = sublist.addField({id: 'recordtype', label: 'Record Type', type: serverWidget.FieldType.TEXT});
        internalIdSublistField.updateDisplayType({displayType: serverWidget.FieldDisplayType.HIDDEN});
        recordTypeSublistField.updateDisplayType({displayType: serverWidget.FieldDisplayType.HIDDEN});

        for (var i = 0; i < columns.length; i++) {
            var column = columns[i];
            var fieldId = column.name;
            var fieldLabel = column.label;
            var fieldType = JSON.parse(JSON.stringify(column)).type;
            if (fieldId !== 'internalid' && fieldId !== 'recordtype' && fieldId !== 'isinactive' && fieldLabel !== 'HIDDEN_COLUMN') {
                switch (fieldType) {
                    case 'checkbox':
                        sublist.addField({id: fieldId, label: fieldLabel, type: 'checkbox'}).updateDisplayType({displayType: serverWidget.FieldDisplayType.INLINE});
                        break;
                    default:
                        if (fieldId === 'custrecordbatchreportvoucher' || fieldId === 'custrecordreqvouchervoucher' || fieldId === 'custrecordvendorsyncvendor') {
                            sublist.addField({id: fieldId, label: fieldLabel, type: serverWidget.FieldType.TEXTAREA});
                        } else if (fieldId === 'custrecordposyncpurchaseorder') {
                            sublist.addField({id: 'polink', label: 'Link', type: serverWidget.FieldType.URL});
                            sublist.addField({id: 'poid', label: 'Purchase Order ID', type: serverWidget.FieldType.TEXTAREA});
                            sublist.addField({id: 'potranid', label: 'Purchase Order Number', type: serverWidget.FieldType.TEXTAREA});
                        } else if (fieldId === 'custrecordposyncreceipt') {
                            sublist.addField({id: 'receiptlink', label: 'Link', type: serverWidget.FieldType.URL});
                            sublist.addField({id: 'receiptid', label: 'Receipt ID', type: serverWidget.FieldType.TEXTAREA});
                            sublist.addField({id: 'receipttranid', label: 'Receipt Number', type: serverWidget.FieldType.TEXTAREA});
                        } else {
                            sublist.addField({id: fieldId, label: fieldLabel, type: serverWidget.FieldType.TEXTAREA});
                        }
                        break;
                }
            }
        }
        if (exportJSONParams) sublist.addField({id: 'exportlink', label: 'Details', type: serverWidget.FieldType.URL}).linkText = 'Export';
    }

    function createBackendFields(form, clientScriptPath, scriptId, deploymentId, suiteletPage, recordId, parentId) {
        var scriptIdField = form.addField({id: 'custbody_script_id', label: 'Script ID', type: serverWidget.FieldType.TEXT});
        var deploymentIdField = form.addField({id: 'custbody_deployment_id', label: 'Deployment ID', type: serverWidget.FieldType.TEXT});
        var suiteletPageField = form.addField({id: 'custbody_suitelet_page', label: 'Suitelet Page', type: serverWidget.FieldType.INTEGER});
        var recordIdField = form.addField({id: 'custbody_record_id', label: 'Record ID', type: serverWidget.FieldType.INTEGER});
        var parentIdField = form.addField({id: 'custbody_parent_id', label: 'Parent ID', type: serverWidget.FieldType.INTEGER});
        scriptIdField.updateDisplayType({displayType: serverWidget.FieldDisplayType.HIDDEN});
        deploymentIdField.updateDisplayType({displayType: serverWidget.FieldDisplayType.HIDDEN});
        suiteletPageField.updateDisplayType({displayType: serverWidget.FieldDisplayType.HIDDEN});
        recordIdField.updateDisplayType({displayType: serverWidget.FieldDisplayType.HIDDEN});
        parentIdField.updateDisplayType({displayType: serverWidget.FieldDisplayType.HIDDEN});
        scriptIdField.defaultValue = scriptId;
        deploymentIdField.defaultValue = deploymentId;
        suiteletPageField.defaultValue = suiteletPage;
        recordIdField.defaultValue = recordId;
        parentIdField.defaultValue = parentId;
        form.clientScriptModulePath = clientScriptPath;
    }

    function createRecord(parameters, recordType) {
        var newRecord = record.create({type: recordType});
        for (var fieldId in parameters) {
            var fieldValue = parameters[fieldId];
            if (!isEmpty(fieldValue)) {
                newRecord.setValue({fieldId: fieldId, value: parseValue(fieldValue, fieldId)});
            }
        }
        newRecord.save();
    }

    function editRecord(parameters, recordType, recordId) {
        var editRecord = record.load({type: recordType, id: recordId});
        for (var fieldId in parameters) {
            var fieldValue = parameters[fieldId];
            if (!isEmpty(fieldValue)) {
                editRecord.setValue({fieldId: fieldId, value: parseValue(fieldValue, fieldId)});
            }
            if (fieldId.indexOf('custrecord') >= 0 && isEmpty(fieldValue)) {
                editRecord.setValue({fieldId: fieldId, value: ''});
            }
        }
        editRecord.save();
    }

    function loadRecord(form, recordType, recordId) {
        var loadRecord = record.load({type: recordType, id: recordId});
        var fieldIds = loadRecord.getFields();
        for (var i = 0; i < fieldIds.length; i++) {
            var fieldId = fieldIds[i];
            var formField = form.getField({id: fieldId});
            if (!isEmpty(formField)) {
                if (formField.type === 'checkbox') {
                    formField.defaultValue = loadRecord.getValue({fieldId: fieldId}) ? 'T' : 'F';
                } else {
                    formField.defaultValue = loadRecord.getValue({fieldId: fieldId});
                }
            }
        }
    }

    function deleteRecord(request, sublistId) {
        var lineCount = request.getLineCount({group: sublistId});
        for (var i = 0; i < lineCount; i++) {
            if (request.getSublistValue({group: sublistId, name: 'delete', line: i}) === 'T') {
                var recordId = request.getSublistValue({group: sublistId, name: 'internalid', line: i});
                var recordType = request.getSublistValue({group: sublistId, name: 'recordtype', line: i});
                record.delete({type: recordType, id: recordId});
            }
        }
    }

    function populateNSObjectList(form, fieldId, recordType) {
        var filters = [];
        var columns = [];
        if (fieldId === 'custrecordicvtnetsuiteobject') filters.push(search.createFilter({name: 'custrecordicshowinvts', operator: 'is', values: 'T'}));
        filters.push(search.createFilter({name: 'isinactive', operator: 'is', values: 'F'}));
        columns.push(search.createColumn({name: 'name'}));
        var recordResult = search.create({type: recordType, filters: filters, columns: columns}).run().getRange({start: 0, end: 1000});
        var fieldObj = form.getField({id: fieldId});
        for (var i = 0; i < recordResult.length; i++) {
            if (i === 0) fieldObj.addSelectOption({text: '', value: ''});
            var recordLabel = recordResult[i].getValue({name: 'name'});
            var recordId = recordResult[i].id;
            fieldObj.addSelectOption({text: recordLabel, value: recordId});
        }
    }

    function populateFieldSelectList(form, fieldId, recordType, recordTypeId) {
        var selectedRecord, sublistId, sublistIds;
        var thisRecordType = recordType;
        if (recordType === 'customrecordbritt_ec_record_types') {
            selectedRecord = search.lookupFields({
                type: recordType,
                id: recordTypeId,
                columns: ['custrecordrecordid', 'custrecordsublistid']
            });
            recordType = selectedRecord.custrecordrecordid;
            sublistId = selectedRecord.custrecordsublistid;
        }
        if (recordType === 'customrecordbritt_ic_record_types') {
            selectedRecord = search.lookupFields({
                type: recordType,
                id: recordTypeId,
                columns: ['custrecordicrecordid', 'custrecordicsublistid']
            });
            recordType = selectedRecord.custrecordicrecordid;
            sublistId = selectedRecord.custrecordicsublistid;
        }
        sublistIds = sublistId.split(',');
        var fieldObj = form.getField({id: fieldId});
        fieldObj.addSelectOption({text: '', value: ''});
        var newRecord = record.create({type: recordType});
        var additionalFields = [];
        if (recordType === 'vendorbill') {
            additionalFields = vendorBillHeaderFields;
        } else if (recordType === 'expensereport') {
            additionalFields = expenseReportHeaderFields;
        } else if (recordType === 'vendorpayment') {
            additionalFields = vendorPaymentHeaderFields;
        } else if (recordType === 'journalentry' || recordType === 'advintercompanyjournalentry') {
            additionalFields = journalEntryFields;
        }
        var headerFields = newRecord.getFields();
        var sortedHeaderFieldObj = sortLabels(headerFields, newRecord, null, additionalFields);
        for (var fieldLabel in sortedHeaderFieldObj) {
            try {
                var fieldId = sortedHeaderFieldObj[fieldLabel];
                fieldObj.addSelectOption({text: fieldLabel, value: recordType + '_' + fieldId});
            } catch (error) {
                log.error({title: 'Error adding field', details: error});
            }
        }
        var additionalFields = [];
        if (recordType === 'vendorbill') {
            additionalFields = vendorBillLineFields;
        } else if (recordType === 'expensereport') {
            additionalFields = expenseReportLineFields;
        } else if (recordType === 'journalentry' || recordType === 'advintercompanyjournalentry') {
            additionalFields = journalEntryLineFields;
            if (recordType === 'advintercompanyjournalentry') {
                Object.assign(additionalFields['line'], advJournalEntryLineFields['line']);
            }
        }
        for (var i = 0; i < sublistIds.length; i++) {
            sublistId = sublistIds[i];
            var sublistFields = newRecord.getSublistFields({sublistId: sublistId});
            var sortedSublistFieldObj = sortLabels(sublistFields, newRecord, sublistId, additionalFields);
            log.debug({title: 'Sorted Sublist Fields', details: sortedSublistFieldObj});
            for (var fieldLabel in sortedSublistFieldObj) {
                try {
                    var fieldId = sortedSublistFieldObj[fieldLabel];
                    fieldObj.addSelectOption({text: fieldLabel + ' (' + sublistLabels[recordType][sublistId] + ' Sublist Field)', value: recordType + '_' + sublistId + '_' + fieldId});
                } catch (error) {
                    log.error({title: 'Error adding field', details: error});
                }
            }
        }
        if (thisRecordType === 'customrecordbritt_ic_record_types' && recordTypeId === '5') {
            for (var fieldLabel in vendorSyncMappingFields) {
                var fieldId = vendorSyncMappingFields[fieldLabel];
                fieldObj.addSelectOption({text: fieldLabel, value: fieldId});
            }
        }
        if (recordType === 'vendor') {
            for (var fieldLabel in addressBookSublistFields) {
                var fieldId = addressBookSublistFields[fieldLabel];
                fieldObj.addSelectOption({text: fieldLabel + ' (Address Book Sublist Field)', value: recordType + '_' + fieldId});
            }
            for (var fieldLabel in achAccountSublistFields) {
                var fieldId = achAccountSublistFields[fieldLabel];
                fieldObj.addSelectOption({text: fieldLabel + ' (ACH Sublist Field)', value: recordType + '_' + fieldId});
            }
            if (bankAccountSublistFields === null) {
                // get the fields for Bank payment Details records
                bankAccountSublistFields = RESTGetFields.get({recordType: 'customrecord_2663_entity_bank_details', withLabel: true});
                utility.logDebug('Bank fields', bankAccountSublistFields);
            }

            for (var fieldLabel in bankAccountSublistFields) {
                var fieldId = bankAccountSublistFields[fieldLabel];
                // Bank Details is a custom record so only customfields are valid ones
                if (
                    (!fieldId.startsWith('custrecord_') || !fieldLabel)
                    && fieldId !== 'internalid'
                    && fieldId !== 'externalid') {
                    continue;
                }
                fieldObj.addSelectOption({text: fieldLabel + ' (Bank Payment Details Sublist Field)', value: recordType + '_cbank_' + fieldId});
            }
        }
        if (recordType === 'vendorpayment') {
            for (var fieldLabel in applySublistFields) {
                var fieldId = applySublistFields[fieldLabel];
                fieldObj.addSelectOption({text: fieldLabel + ' (Apply Sublist Field)', value: recordType + '_' + sublistId + '_' + fieldId});
            }
        }
        if (recordType === 'itemreceipt') {
            for (var fieldLabel in itemReceiptSublistFields) {
                var fieldId = itemReceiptSublistFields[fieldLabel];
                fieldObj.addSelectOption({text: fieldLabel + ' (Item Sublist Field)', value: recordType + '_' + sublistId + '_' + fieldId});
            }
        }
        if (recordType === 'purchaseorder') {
            for (var fieldLabel in purchaseOrderItemSublistFields) {
                var fieldId = purchaseOrderItemSublistFields[fieldLabel];
                fieldObj.addSelectOption({text: fieldLabel + ' (Item Sublist Field)', value: recordType + '_' + sublistId + '_' + fieldId});
            }
            for (var fieldLabel in purchaseOrderVendorSublistFields) {
                var fieldId = purchaseOrderVendorSublistFields[fieldLabel];
                fieldObj.addSelectOption({text: fieldLabel + ' (Vendor Sublist Field)', value: fieldId});
            }
        }

        fieldObj.addSelectOption({text: 'Custom Header Field', value: recordType + '_customfield'});
        fieldObj.addSelectOption({text: 'Custom Sublist Field', value: recordType + '_' + sublistId + '_customfield'});
    }

    function populateSearchFieldSelectList(form, fieldId, searchId) {
        var field = form.getField({id: fieldId});
        var searchObj = search.load({id: searchId});
        var columns = searchObj.columns;
        for (var i = 0; i < columns.length; i++) {
            var column = columns[i];
            var fieldLabel = column.label;
            var fieldId = utility.getSearchResultFieldID(column.name, column.join, column.summary);
            if (i === 0) field.addSelectOption({text: '', value: ''});
            field.addSelectOption({text: fieldLabel, value: fieldId});
        }
    }

    function populateList(form, fieldId, listId) {
        var columns = [];
        var filters = [];
        filters.push(search.createFilter({name: 'isinactive', operator: 'is', values: 'F'}));
        columns.push(search.createColumn({name: 'name'}));
        var sourceResult = search.create({type: listId, columns: columns, filters: filters}).run().getRange({start: 0, end: 1000});
        var fieldObj = form.getField({id: fieldId});
        for (var i = 0; i < sourceResult.length; i++) {
            var name = sourceResult[i].getValue({name: 'name'});
            var id = sourceResult[i].id;
            fieldObj.addSelectOption({text: name, value: id});
        }
    }

    function populatePageList(form, fieldId, pagedData, pageIndex) {
        var pageField = form.getField({id: fieldId});
        if (pagedData.pageRanges.length === 0) {
            pageField.updateDisplayType({displayType: serverWidget.FieldDisplayType.HIDDEN});
        }
        for (var i = 0; i < pagedData.pageRanges.length; i++) {
            var pageRange = pagedData.pageRanges[i];
            var label = pageRange.compoundLabel;
            var index = pageRange.index;
            pageField.addSelectOption({text: label, value: index, isSelected: i === parseInt(pageIndex)});
        }
    }

    function getSetupConfiguration(product) {
        if (product === 'expense' || product === 'EC') {
            var columns = [];
            columns.push(search.createColumn({name: 'custrecordapikey'}));
            columns.push(search.createColumn({name: 'custrecordstartdate'}));
            columns.push(search.createColumn({name: 'custrecordsource'}));
            columns.push(search.createColumn({name: 'custrecorddestination'}));
            columns.push(search.createColumn({name: 'custrecordpaymentstatuses'}));
            columns.push(search.createColumn({name: 'custrecordconcurdomain'}));
            columns.push(search.createColumn({name: 'custrecordgroupconfigids'}));
            columns.push(search.createColumn({name: 'custrecordmonthlimitreceiptimagedelete'}));
            return search.create({type: 'customrecordbritt_ec_setup', columns: columns}).run().getRange({start: 0, end: 1})[0];
        }
        else {
            var columns = [];
            columns.push(search.createColumn({name: 'custrecordicapikey'}));
            columns.push(search.createColumn({name: 'custrecordicstartdate'}));
            columns.push(search.createColumn({name: 'custrecordicdestination'}));
            columns.push(search.createColumn({name: 'custrecordicconcurdomain'}));
            columns.push(search.createColumn({name: 'custrecordmonthlimitrequestimagedelete'}));
            return search.create({type: 'customrecordbritt_ic_setup', columns: columns}).run().getRange({start: 0, end: 1})[0];
        }
    }

    function parseValue(value, fieldId) {
        if (value === 'T') value = true;
        else if (value === 'F') value = false;
        if (!isValueTransformation(fieldId)) {
            if (Date.parse(value)) value = format.parse(value, format.Type.DATE);
            else value = utility.formatDate(value);
        }
        return value;
    }

    function isValidField(field) {
        if (isEmpty(field)) return false;
        var fieldLabel = field.label;
        var fieldId = field.id;
        if (fieldLabel === 'Untitled' || fieldLabel === '') return false;
        if (invalidFields.indexOf(fieldId) >= 0) return false;
        return true;
    }

    function calculateDurationInMinutes(startTime, endTime) {
        var startTimeMoment = moment(startTime);
        var endTimeMoment = moment(endTime);
        var diffTime = endTimeMoment.diff(startTimeMoment);
        var duration = moment.duration(diffTime);
        return duration.hours() + " hour(s), " + duration.minutes() + " minute(s), " + duration.seconds() + " second(s)";
    }

    /**
     * Sort the Fields labels as follows;
     *
     * 1. First add the header or sublist main fields.
     * 2. if additionalFields, add keys of it if not sublistId is passed else add the keys of the sublistId sub property
     *
     *
     * @param {String[]} fieldIds Array of Field Ids Either headerFields or Sublist Fields
     * @param {Any} newRecord Record whose Fields we are Sorting
     * @param {String} sublistId SublistId to fetch fields for
     * @param {Object} additionalFields Any AdditionalFields label-id pairs
     * @returns {Object} map of Fields where keys are fieldLabels and values are their Ids.
     */
    function sortLabels(fieldIds, newRecord, sublistId, additionalFields) {
        //log.debug({title: 'SortLabels', details: [fieldIds, sublistId, additionalFields]});
        var sortedObj = {};
        var labelToFieldIdMap = {};
        var sortedLabels = [];
        for (var i = 0; i < fieldIds.length; i++) {
            try {
                var fieldId = fieldIds[i];
                var field = newRecord.getField({fieldId: fieldId});
                if (!isEmpty(sublistId)) field = newRecord.getSublistField({sublistId: sublistId, fieldId: fieldId, line: 0});
                if (isValidField(field)) {
                    var fieldLabel = field.label;
                    labelToFieldIdMap[fieldLabel] = fieldId;
                    sortedLabels.push(fieldLabel);
                }
            } catch (error) {}
        }
        if (!isEmpty(additionalFields)) {
            if (isEmpty(sublistId)) {
                for (var fieldLabel in additionalFields) {
                    sortedLabels.push(fieldLabel);
                }
            } else if (!isEmpty(additionalFields[sublistId])) {
                var thisSublist = additionalFields[sublistId];
                for (var fieldLabel in thisSublist) {
                    var fieldId = thisSublist[fieldLabel];
                    labelToFieldIdMap[fieldLabel] = fieldId;
                    sortedLabels.push(fieldLabel);
                }
            }
        }
        sortedLabels = sortedLabels.sort();
        for (var i = 0; i < sortedLabels.length; i++) {
            var fieldLabel = sortedLabels[i];
            if (isEmpty(sublistId))
                sortedObj[fieldLabel] = !isEmpty(labelToFieldIdMap[fieldLabel]) ? labelToFieldIdMap[fieldLabel] : additionalFields[fieldLabel];
            else
                sortedObj[fieldLabel] = !isEmpty(labelToFieldIdMap[fieldLabel]) ? labelToFieldIdMap[fieldLabel] : additionalFields[sublistId][fieldLabel];
        }
        return sortedObj;
    }

    function isValueTransformation(fieldId) {
        // log.debug({title: 'isValueTransformation', details: fieldId});
        return fieldId === 'custrecordtransformationvalue' || fieldId === 'custrecordsourcevalue' || fieldId === 'custrecordictransformationvalue' || fieldId === 'custrecordicsourcevalue';
    }

    /**
     * Get the running instances of pased scripts
     * @param {Array<String>} scriptIDs
     * @returns {Array<*>}
     */
    function getRunningTasks(scriptIDs) {
        var filterExpression = [[]];
        scriptIDs.forEach(function (scriptID, index) {
            filterExpression[0].push(["script.scriptid", search.Operator.IS, scriptID]);
            if (index !== scriptIDs.length - 1) {
                filterExpression[0].push("OR");
            }
        });
        var params = {
            searchId: 'customsearch_britt_running_script_ins',
            filterExpression: filterExpression,
        };
        return RESTSearch.get(params);
    }

    function isEmpty(value) {
        return value === '' || value === null || value === undefined;
    }

    return {
        createSublist: createSublist,
        createBackendFields: createBackendFields,
        createRecord: createRecord,
        editRecord: editRecord,
        loadRecord: loadRecord,
        deleteRecord: deleteRecord,
        populateNSObjectList: populateNSObjectList,
        populateFieldSelectList: populateFieldSelectList,
        populateSearchFieldSelectList: populateSearchFieldSelectList,
        populateList: populateList,
        populatePageList: populatePageList,
        calculateDurationInMinutes: calculateDurationInMinutes,
        getSetupConfiguration: getSetupConfiguration,
        getRunningTasks: getRunningTasks,
        isEmpty: isEmpty
    }

});