案例展示NetSuite-UI界面中创建动态下拉列表

在NetSuite的主界面或列表中,创建动态下拉子列表,无缝嵌入到商业应用流程中

有什么用

学会如何在NetSuite UI界面中创建动态下拉列表

  • 在主界面(Custom Body Field)中创建动态下拉列表字段
  • 在列表行(Custom Line Item Field)中创建动态下拉列表字段

在现实业务流程中,这样的动态下拉列表应用十分广泛;而NetSuite自带的自定义字段的动态下来过滤功能十分有限。很多的应用场景无法满足,这里就不一一展开说明,懂的都懂。

需求

image-20240618205434547

本案例:要在下拉列表中,将Replacement Vendor字段的内容,动态地根据Replacement Item字段的内容而决定。就是根据Item货品的供应商列表来过滤当前自定义记录界面中的Replacement Vendor字段的下拉内容,从而达到Replacement Vendor字段的下拉列表不展示系统中所有的Vendor,而是限定于特定Item货品(当前行中的货品字段)的供应商列表(这个使用其实也同时可以展示该供应商的此货品采购价)

怎么用

  1. 创建一个系统中的自定义字段
    1. 如果是把动态列表放在主界面,那么该‘容器’字段也可以使用UserEvent的script生成出这个一个动态的自定义字段。
    2. 如果是把动态列表放在主界面下的列表中(比如:销售订单的货品列表中),那么就必须要新建一个自定义列字段;
    3. 而假如是要把动态列表放在主界面下的列表中(比如:主从关系的自定义记录类型Custom Record Type),那么就是新建一个自定字段在在子记录类型中(这个子自定义记录类型在UI中会变成一个列表sublist)
  2. 新建一个Library脚本文件Script(详见下方的PRI_DropDown_lib.js文件全部内容展示)
  3. 创建一个User Event Script记录
    1. 用来新建初始化字段(可选,根据实际需要)
    2. 用来初始化字段的HTML内容;这个初始化内容是根据列表中当前的货品来决定的,UI的编辑状态用户是可以随时在界面中切换货品字段内容,从而需要重新初始化该HTML内容(详见实现在下方的Client Script中)
  4. 创建一个Client Script记录
    1. 在列表的line init事件中,初始化当前行的Vendor列表(根据当前的货品字段内容)
    2. 在用户切换了Item货品字段内容后,重新刷新初始化当前行的Vendor列表

相关内容

实现方法

Create Custom Field

新建自定义字段的定义案例:

image-20240619173308707

说明:这个字段的TYPE,用Free Form Text也是一样的。

User Event

to initial the UI to put-in/inject the dropdown HTML

var intDefProject = currentRecord.getValue('custbody_pri_prj');
var clsProjectDropDown = new CLLIB.PROJECTDROPDOWN(intEntityId,
    '');

// Set entity projects dropdown options HTML
var strInitSelectHtml = clsProjectDropDown.initOptionHtml(
    intDefProject, scriptContext.type);
objFldProject.defaultValue = "<div class=\"uir-field-wrapper\" data-field-type=\"text\"><span id=\"projectdropdown_fs_lbl_uir_label\" class=\"smallgraytextnolink uir-label \"><span id=\"projectdropdown_fs_lbl\" class=\"labelSpanEdit smallgraytextnolink\" style=\"\">"
    + "<a tabindex=\"-1\" title=\"What's this?\" href='javascript:void(\"help\")' style=\"cursor:help\"  class=\"smallgraytextnolink\" onmouseover=\"this.className='smallgraytext'; return true;\" onmouseout=\"this.className='smallgraytextnolink'; \">"
    + strPrjLbl
    + "</a>"
    + "</span></span><span class=\"uir-field\">"
    + strInitSelectHtml + "</span>" + "</div>";

上面这个方案是直接注入到系统的自定义字段中(在NetSuite系统的自定义字段中定义的)

或者使用全新的Script新增的动态的html字段

var objFldProject = objForm.addField({
    id: 'custpage_pri_prj',
    type: ui.FieldType.INLINEHTML,
    label: strPrjLbl
});
objForm.insertField({
    field: objFldProject,
    nextfield: 'custbody_pri_co',// 'job'
});

objFldProject.defaultValue = "<div class=\"uir-field-wrapper\" data-field-type=\"text\"><span id=\"projectdropdown_fs_lbl_uir_label\" class=\"smallgraytextnolink uir-label \"><span id=\"projectdropdown_fs_lbl\" class=\"labelSpanEdit smallgraytextnolink\" style=\"\">"
    + "<a tabindex=\"-1\" title=\"What's this?\" href='javascript:void(\"help\")' style=\"cursor:help\"  class=\"smallgraytextnolink\" onmouseover=\"this.className='smallgraytext'; return true;\" onmouseout=\"this.className='smallgraytextnolink'; \"> "
    + strPrjLbl
    + "</a>"
    + "</span></span><span class=\"uir-field\">"
    + "<select id=\"custpage_projectdropdown\" name=\"custpage_projectdropdown\" autocomplete=\"off\" lineindex=\"1\" class=\"input uir-custom-field\">"
    + "<option value=\"\"> </option>"
    + "</select>"
    + "</span>" + "</div>" + "";


Client Script dynamic behaviors

pageInit

可选,仅用于使用主界面的系统自带自定义字段来‘加载’动态列表的情况下

/**
			 * Function to be executed after page is initialized.
			 *
			 * @param {Object}
			 *            scriptContext
			 * @param {Record}
			 *            scriptContext.currentRecord - Current form record
			 * @param {string}
			 *            scriptContext.mode - The mode in which the record is
			 *            being accessed (create, copy, or edit)
			 *
			 * @since 2015.2
			 */
function pageInit(scriptContext) {

  ...
  
    // Bind select option change event
    var clsProjectDropDown = new CLLIB.PROJECTDROPDOWN(intEntityId,
          objDropdownDom, {'currentRecord': currentRecord});
    clsProjectDropDown.bindSelectEvent();
  
  ...
}
  

fieldChanged


function fieldChanged(scriptContext) {
  switch (scriptContext.fieldId) {

  case ....
      
      // Bind select option change event
      var clsProjectDropDown = new CLLIB.PROJECTDROPDOWN(
            intEntityId, objDropdownDom, {'currentRecord': currentRecord});

      if (!intEntityId) {
         clsProjectDropDown.clearSelectOptions();
         return true;
      }

      // Draw entity project dropdown options
      clsProjectDropDown.drawDOM();
  		break;
	}
}
      

pri_RetMgr_vendor_refreshSelectOption

function pri_RetMgr_vendor_refreshSelectOption(currentRecord) {

    var lnIdx = currentRecord.getCurrentSublistIndex({
        sublistId: strSublistId_replc
    });
    var intItemId = currentRecord.getCurrentSublistValue(strSublistId_replc, RETMGRLIB.REC_RETURNMGR_REPLC_LINE.ITEM);
    var intVendorId_cur = currentRecord.getCurrentSublistValue(strSublistId_replc, RETMGRLIB.REC_RETURNMGR_REPLC_LINE.VENDOR);

    try {

        var objDropdownDom = document.getElementById('recmachcustrecord_pri_return_mgr_replc_parent_custrecord_pri_return_mgr_replc_selvnd_fs');
        // Bind select option change event
        var clsVendorDropDown = new DDLIB.DROPDOWN(intItemId, objDropdownDom, {'currentRecord': currentRecord});
        jQuery('#recmachcustrecord_pri_return_mgr_replc_parent_custrecord_pri_return_mgr_replc_selvnd_fs').html(clsVendorDropDown.initOptionHtml(intVendorId_cur));
        clsVendorDropDown.bindSelectEvent();

    } catch (ex) {
        console.log(ex);
    }

    // Special case: After you change the item from Item A to Item B, the original vendor value should/might be cleared(since that vendor is not in new Item B’s vendor list)
    if (intVendorId_cur) {
        var bolVendorAvailable = false;
        var arrProjectResObj = clsVendorDropDown.arrProjectResObj;
        for (var i = 0; arrProjectResObj && i < arrProjectResObj.length; i++) {

            if (intVendorId_cur == arrProjectResObj[i].ID){
                bolVendorAvailable = true;
                break;
            }
        }
        if (bolVendorAvailable === false)
            currentRecord.setCurrentSublistValue({
                sublistId: strSublistId_replc,
                fieldId: RETMGRLIB.REC_RETURNMGR_REPLC_LINE.VENDOR,
                value: ''
            });
    }
}

从这个函数可以得知这个自定义的动态下拉字段,初始化的值也是会被加载的,非常顺滑。


Library - PRI_DropDown_lib.js

used in both Client and UserEvent/Server side. 核心公共函数库

//------------------------------------------------------------------
//Developer: Carl
//Description: Need a dynamic Drop Down on the record/transaction of vendor list.
// Thus, the drop down on record/transaction should filter for item that are related.
//------------------------------------------------------------------

/**
 * @NApiVersion 2.x
 * @NModuleScope Public
 */
define(
    ['N/error', 'N/record', 'N/runtime', 'N/search', './PRI_RM_ReturnManager_lib'],
    /**
     * @param {error}
     *            error
     * @param {record}
     *            record
     * @param {runtime}
     *            runtime
     * @param {search}
     *            search
     * @param {dialog}
     *            dialog
     */
    function (error, record, runtime, search, RETMGRLIB) {

        // ---------------------- Drop Down Class-----------------
        function DROPDOWN(intItemId, objDropdownDom, options) {

            this.intItemId = intItemId;
            this.objDropdownDom = objDropdownDom;

            if (intItemId) {

                this.arrProjectResObj = this.lookupVendorRecords();
            }

            this.strInitSelectFld = "<select id=\"custpage_projectdropdown\" name=\"custpage_projectdropdown\" autocomplete=\"off\" lineindex=\"1\" class=\"input uir-custom-field\">"
                + "<option value=\"\"> </option>"
                + 'REPLACE_OPTIONAL_HTML' + "</select>";

            if (options && typeof (options.currentRecord) != 'undefined') {
                this.currentRecord = options.currentRecord;
            }

        }

        /**
         * lookup Vendor Records
         *
         * @param {String}
         *            strLookupKeyWord Lookup Keyword
         * @returns {Object} objOrderStatusSublist
         */
        DROPDOWN.prototype.lookupVendorRecords = function (strLookupKeyWord) {

            var intItemId = this.intItemId;

            if (!intItemId)
                return [];

            var arrProjectRes = [];
            var objFilters = [['isinactive', 'is', 'F']];
            // if (intItemId) {
            // 	objFilters.push('and');
            // 	objFilters.push([ RETMGRLIB.REC_PRIPROJECT.CUSTOMER, 'anyof',
            // 			[ intItemId ] ]);
            // }
            objFilters.push('and');
            objFilters.push(['internalid', 'anyof', [intItemId]]);
            var objColumns = ['itemid', 'displayname', 'othervendor', 'cost'];
            objColumns.push({name: "internalid", join: "vendor"});

            var objRecordRes = search.create(
                {
                    type: 'item',
                    filters: objFilters,
                    columns: objColumns
                }).run().getRange({
                start: 0,
                end: 1000
            });

            for (var i = 0; objRecordRes && i < objRecordRes.length; i++) {

                arrProjectRes.push({
                    ID: objRecordRes[i].getValue({
                        name: "internalid",
                        join: "vendor"
                    }),
                    // NAME: objRecordRes[i].getValue('itemid'),
                    COST: objRecordRes[i].getValue('cost'),
                    VALUE: objRecordRes[i].getText('othervendor')
                });
            }

            return arrProjectRes;
        };

        /**
         * Dynamically clear select options
         *
         * @returns {Boolean}
         */
        DROPDOWN.prototype.clearSelectOptions = function () {

            jQuery("#custpage_projectdropdown").replaceWith(
                this.strInitSelectFld.replace('REPLACE_OPTIONAL_HTML',
                    ''));

            return true;
        };

        /**
         * Draw select option DOM element
         */
        DROPDOWN.prototype.drawDOM = function () {

            var objDropdownDom = this.objDropdownDom;
            var arrProjectResObj = this.arrProjectResObj ? this.arrProjectResObj : this.lookupVendorRecords();

            var strOptionHtml = '';
            for (var idx = 0; idx < arrProjectResObj.length; idx++) {
                strOptionHtml += '<option value="'
                    + arrProjectResObj[idx].ID
                    + '" data-status='
                    + arrProjectResObj[idx].COST
                    + ' title="'
                    + JSON.stringify(arrProjectResObj[idx]).replace(
                        /\"/g, "'") + '">'
                    + arrProjectResObj[idx].VALUE + '</option>';
            }
            var strInitSelectFld = this.strInitSelectFld.replace(
                'REPLACE_OPTIONAL_HTML', strOptionHtml);

            jQuery("#custpage_projectdropdown").replaceWith(
                strInitSelectFld);

            this.bindSelectEvent();
        };

        /**
         * Bind Selection Operation <br>
         * Note: Only used in Client Side
         */
        DROPDOWN.prototype.bindSelectEvent = function () {

            jQuery(document).ready(
                function () {
                    jQuery("#custpage_projectdropdown").change(
                        function () {

                            // alert(jQuery(this).val());
                            nlapiSetCurrentLineItemValue('recmachcustrecord_pri_return_mgr_replc_parent', RETMGRLIB.REC_RETURNMGR_REPLC_LINE.VENDOR,
                                jQuery(this).val());

                            // var strStatus = jQuery(this).find(
                            // ":selected").data("status");
                        });
                });
        };

        /**
         * Initial Option HTML, can use in server side
         *
         * @param {integer}
         *            intDefSelectVal Default Contact record type Id
         * @param {string}
         *            strUserEventType User Event Type
         * @returns {string}
         */
        DROPDOWN.prototype.initOptionHtml = function (
            intDefSelectVal, strUserEventType) {

            // if (!this.intItemId)
            // return '';

            var arrProjectResObj = this.arrProjectResObj ? this.arrProjectResObj : this.lookupVendorRecords();

            var strOptionHtml = '';
            for (var idx = 0; idx < arrProjectResObj.length; idx++) {
                if (intDefSelectVal
                    && arrProjectResObj[idx].ID == intDefSelectVal)
                    strOptionHtml += '<option selected="selected" value="'
                        + arrProjectResObj[idx].ID
                        + '" data-status='
                        + arrProjectResObj[idx].COST
                        + ' title="'
                        + JSON.stringify(arrProjectResObj[idx])
                            .replace(/\"/g, "'") + '">'
                        + arrProjectResObj[idx].VALUE + '</option>';
                else
                    strOptionHtml += '<option value="'
                        + arrProjectResObj[idx].ID
                        + '" data-status='
                        + arrProjectResObj[idx].COST
                        + ' title="'
                        + JSON.stringify(arrProjectResObj[idx])
                            .replace(/\"/g, "'") + '">'
                        + arrProjectResObj[idx].VALUE + '</option>';
            }

            var strInitSelectFld = this.strInitSelectFld;
            if (strUserEventType == 'view')
                strInitSelectFld = strInitSelectFld.replace(
                    '<select id=\"custpage_projectdropdown\"',
                    "<select id=\"custpage_projectdropdown\" disabled");

            strInitSelectFld = strInitSelectFld.replace(
                'REPLACE_OPTIONAL_HTML', strOptionHtml);

            return strInitSelectFld;
        };

        return {
            DROPDOWN: DROPDOWN
        };

    });
  • bindSelectEvent,当完成了HTML展示动态更新的下拉子列表后,绑定新的点击事件;当用户选取新的列表选项后,我们将值传递给另一个系统的字段(或者其他的商业逻辑定义)

衍生阅读

来吧,约个时间当面谈,我很乐意效劳,有什么NetSuite系统定制开发中的疑难杂症我可以帮忙的吗?

image-20240619215304248

随手记录 - 如何下载网页中视频

image-20240615213247707

Firefox插件名称和版本信息

image-20240615213422690

posted @ 2024-07-19 21:36  CarlZeng  阅读(1)  评论(0编辑  收藏  举报