简体   繁体   中英

Netsuite: Suitescript 2.X: I need help using searches/getValue to populate custom CPN automatically. Cross referencing item and customer to find CPN

Because Netsuite's native CPN does not let people use spaces in the CPN my company made a custom suitescript to use a custom record type for the CPN. The script below is used to cross reference the customer and the item to generate a list of possible CPNs then it chooses the first and only option in that list. At first we had it routed to the child company, but now we think it might be a better idea to have them connected to the parent company.This way if a company has 12 child companies we only have to upload the CPN one time. Would someone review the code below and let me know why I cant get the code to use the parent customer instead of the child? Or rather it wont populate at all.

define(["N/search", "N/log"], function(Search, _Log) {
    var TAG = "pi_cs_so_v2";

    function Log() {};
    Log.log = function (tag, msg) {
        console.log(tag + " : " + msg);
    };
    Log.debug = function(tag, msg) {
        _Log.debug(tag, msg);
        Log.log(tag, msg);
    };
    Log.error = function(tag, msg) {
        _Log.error(tag, msg);
        Log.log(tag, msg);
    };

    function fieldChanged(context) {
        PartNumber.fieldChanged(context);
    }

    /**
     * Static object, contains customizations relevant to EURO-3
     * @constructor
     */
    function PartNumber () {}
    function cusParent (){}
    /**
     * Handle the native field changed NetSuite call
     * @param context
     * @returns {boolean} True if CPN is updated, else false
     */
    PartNumber.fieldChanged = function(context) {
        var nr = context.currentRecord;
        if (context.sublistId !== "item" || context.fieldId !== "item") return false;
        Log.debug(TAG, "fieldChanged, executing CPN extension");

        var item = nr.getCurrentSublistValue({sublistId: context.sublistId, fieldId: "item"});
        var customer = nr.getValue("entity");
        var parent = nr.getValue({
                        join: "entity",
                        fieldId: "parent",
                        name: "name"
                    });

        Log.debug(TAG, "Item, customer: " + JSON.stringify([item, parent]));
        if (!parent || parent === "" || !item || item === "") return false;
        var cpn = PartNumber.find(parent, item);
        if (!cpn) return false;

        nr.setCurrentSublistValue({sublistId: context.sublistId, fieldId: "custcol_cpn_transaction", value: cpn});
      nr.setCurrentSublistValue({sublistId: context.sublistId, fieldId: "custcol24", value: parent});
        Log.debug(TAG, "Found CPN: " + cpn);
        return true;
    };
    /**
     * Search for the customer part number, assumes there is only ever a single result
     * @param customer
     * @param item
     * @returns {number | undefined} InternalID of the True Customer Part Number record
     */

  PartNumber.find = function(customer, item) {
        var searchObj = Search.create({
            type: "customrecord_true_cpn",
            filters:
                [
                    ["custrecord_cpn_customer","anyof",customer],
                    "AND",
                    ["custrecord_cpn_item","anyof",item]
                ],
            columns: []
        });
        var ans = -1;
        searchObj.run().each(function(result){
            // .run().each has a limit of 4,000 results
            ans = result.id;
            return false;
        });
        return ans !== -1 ? ans : undefined;
    };
 

    return {
        postSourcing: fieldChanged,
    };
});

Assuming a company that has a hierarchy may have a tree and not just a straight line you need a way to efficiently query the hiearchy from top level to the current customer and get the best matched CPN.

We can make use of how Netsuite contatenates names and infer that the longest fully qualified customer name that has a matching CPN is the best one to use.

Although the code below is untested it is based on hierachical searches I've done in other contexts. Note that I found a lot of your pseudo object style to be pretty obfuscatory and does not add anything to code readabilty or type safety. It's just one self contained script.

define(["N/search", "N/log"], function(Search, _Log) {
    var TAG = "pi_cs_so_v2";

    function hasConsole(){
        return typeof window == 'object' && window.console && window.console.log;
    }

    var Log = {

        debug : function(tag, msg) {
            hasConsole ? window.console.log(tag, msg) : _Log.debug(tag, msg);
            
        },
        error : function(tag, msg) {
            hasConsole ? window.console.error(tag, msg) : _Log.error(tag, msg);
        }
    };

    function fieldChanged(context) {

        var nr = context.currentRecord;
        if (context.sublistId !== "item" || context.fieldId !== "item") return;  //return false <- fieldChanged is void
        Log.debug(TAG, "fieldChanged, executing CPN extension");

        var item = nr.getCurrentSublistValue({sublistId: context.sublistId, fieldId: "item"});
        var customer = nr.getValue("entity");

        if(!customer || !item) return;

        //if (!parent || parent === "" || !item || item === "") return false; the === "" will never be evaluated
        // var parent = nr.getValue({ // this call doesn't exist
        //                 join: "entity",
        //                 fieldId: "parent",
        //                 name: "name" // where did you get this field id from?
        //             }); 

        const custInfo = Search.lookupFields({
            type:'customer',
            id:customer,
            columns:['internalid', 'entityid', 'parent']
        });

        // should have fully qualified customer name parent : sub-parent : customer


        var cpn = findPartNumber(custInfo.entityid, item);
        if (!cpn) return;

        nr.setCurrentSublistValue({sublistId: context.sublistId, fieldId: "custcol_cpn_transaction", value: cpn.id});
        nr.setCurrentSublistValue({sublistId: context.sublistId, fieldId: "custcol24", value: cpn.customer});
        Log.debug(TAG, "Found CPN: " + JSON.stringify(cpn));
        return; // field changed is void; no return value
    }


    /**
     * Search for the customer part number, assumes there is only ever a single result
     * @param customer
     * @param item
     * @returns {id: cpn record id, customer: customerId owning the cpn} | null
     */

    function findPartNumber(custInfo, item) {

        var cpnFilters = null;

        var commonColumns = [
                Search.createColumn({'name': 'entityid', join:'custrecord_cpn_customer'}),
                Search.createColumn({'name': 'custrecord_cpn_customer'})
            ];

        if(custInfo.parent && custInfo.parent.length){
            cpnFilters = [
                ["custrecord_cpn_item","anyof",item], 'AND',
                [
                    ["custrecord_cpn_customer","anyof",custInfo.parent[0].value], 'OR', // the top level
                    getCustHierarcyClauses(custInfo)
                ]
            ];

        }else{
           cpnFilters = [
                ["custrecord_cpn_customer","anyof",custInfo.internalid],
                "AND",
                ["custrecord_cpn_item","anyof",item]
            ];
        }

        var bestCPN = null;

        Search.create({
            type: "customrecord_true_cpn",
            filters:cpnFilters,
            columns: commonColumns
        }).run().each(function(result){
            if(!bestCPN) {
                bestCPN = {
                    id:result.id, 
                    entity: result.getValue({name:'entityid', join:'custrecord_cpn_customer'}),
                    customer:result.getValue({name:'custrecord_cpn_customer'})
                };
            } else{ // need to get the closest defined CPN;  assumes lower level of company could have their own preferred CPN.
                var testCPN = {
                    id: result.id,
                    entity: result.getValue({name:'entityid', join:'custrecord_cpn_customer'}),
                    customer:result.getValue({name:'custrecord_cpn_customer'})
                };
                if(testCPN.entity.length > bestCPN.entity.length) bestCPN = testCPN;
            }
            return true;
        });
        return bestCPN;
    }

    function getCustHierarcyClauses(custInfo){
        var fullNames = custInfo.entityid.split(' : ').slice(0, -1); // last name is the direct company name and no inference needed

        var filters =  ["custrecord_cpn_customer","anyof",custInfo.internalid]; 
        var topParentId = custInfo.parent[0].value;

        if(fullNames.length == 1){ // shouldn't have gotten here if only 1
            return filters;
        }

        for(var i = 1; i< fullNames.length; i++){
            filters.push('OR', [
                ["custrecord_cpn_customer.parent","anyof",topParentId], 'AND',
                ["custrecord_cpn_customer.entityid","is",fullNames.slice(0,i).join(' : ')] // need to qualify by name because we only want direct line of hierarchy
            ]);
        }
        return filters;


    }
 

    return {
        postSourcing: fieldChanged
    };
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM