Marty Zigman Marty Zigman
Prolecto Labs Accelerator Templates

Learn to Code a Simple NetSuite Suitelet with the MVC Pattern

NetSuite Technical



This article is relevant if you are looking to understand how to develop a NetSuite Suitelet and would like to understand the Model View Controller pattern.

Background

Approximately a year ago, I wrote an article about to code a SuiteLet using the Model View Controller pattern.   Upon contemplation of that article, I recognized that the code itself might be difficult to understand.  In the back of my mind, I thought I would like to create an even simpler example to help learn from.

More recently, I needed to create a quick utility for our team to lookup cases.  In our practice, we built a tool called “Prolecto Task Manager” (PTM) which is effectively a completely different way to use NetSuite Cases.  In this model, we wanted to expose case-like functionality to individuals that did not have standard permissions (such as our clients).   For our internal team, we needed a quick way to lookup PTMs (cases), but we had not exposed that in our native application.  So I went to work to code it up.

The Simple Lookup Application

Just as NetSuite provides a global keyword search, we too needed a simple keyword search that would be isolated to our PTMs (cases) and provide hyperlinks to our specific PTM records.  Thus, the simple app does the following:

  1. Single Lookup Field: users enter keywords
  2. Sortable Resultlist: return the results sortable with links that direct to the specific PTM (case)

Thus, we have a very easy to understand application that is great for learning SuiteLet development.  At the same time, we can introduce the Model View Controller Pattern, which was talked about more thoroughly in the previous article.  Click on images to see the basics of the application.

The NetSuite Controller SuiteLet Pattern

Below is the code for the Controller pattern.  You can also click here to download the program.

//-----------------------------------------------------------------------------------------------------------
// Data:        20200509
// Authors: 	Marty Zigman, Principal, Prolecto Resources, Inc.
// Application: PTM Search Suitelet acting as a controller
// Purpose: 	A lookup PTMs (cases) using global search as a stand alone suitlet
//-----------------------------------------------------------------------------------------------------------

/**
 * @NApiVersion 2.x
 * @NScriptType Suitelet
 * @NModuleScope Public
 */

define(
    ['./ptm-search-model-20', './ptm-search-view-20'],
    function (model, view) {

    	function onRequest(context) {
    		log.debug({title:'Controller onRequest Method', details: context.request.method});
            var params = context.request.parameters
            var m = model.load(params);
            var v = view.load(m);
            
            context.response.writePage(v.form)
    	}
    	
        return {
            onRequest: onRequest
        };

    });

 

The NetSuite Model SuiteLet Pattern

Below is the code for the Model pattern.  You can also click here to download the program.

//-----------------------------------------------------------------------------------------------------------
// Data:        20200509
// Authors:     Marty Zigman, Principal, Prolecto Resources, Inc.
// Application: PTM Search Model
// Purpose: 	take input and use global search to find PTMs (case records)
//-----------------------------------------------------------------------------------------------------------

/**
  * @NApiVersion  2.x
  * @NModuleScope Public
*/

define(['N/search'],
  
  function(search) {
    
	//start function
    function entry(params){
    	log.debug({title:'model entry params', details: JSON.stringify(params)});
    	return new Model(params);	
    }
    
    //build the model
    function Model(params){
    	this.input = params.input
    	if (!params.input){
    		this.data = [];
    	} else {
    		this.data = lookupPTM(params.input);
    	}
    }
    
    //perform the lookup
    function lookupPTM(input){
		log.debug({title:'model lookupPTM', details: 'value of input: ' + input});
		
		if (input){
			 // note, 'ptm:' is the alias keyword used in the NetSuite account for 'cases'
             return search.global({
			    keywords: 'ptm:' + input
			});
		} else {
			return []
		}

    }

    // Entry Points
    return {
      load : entry,
    };  // Return Entry Points
    
  } // Function 
); // Define

 

The NetSuite View SuiteLet Pattern

Below is the code for the View pattern.  You can also click here to download the program.

//-----------------------------------------------------------------------------------------------------------
// Data:        20200509
// Authors: 	Marty Zigman, Principal, Prolecto Resources, Inc.
// Application: PTM Search View
// Purpose: 	given a model, build a presentation view
//-----------------------------------------------------------------------------------------------------------

/**
  * @NApiVersion  2.x
  * @NModuleScope Public
*/

const PTMLINK = '/app/site/hosting/scriptlet.nl?script=783&deploy=1&compid=700889&mode=detail&unlayered=F&taskid=';

define(['N/error', 'N/ui/serverWidget'],

	function(error, ui) {
    
		//expecting a model, even if empty
		function entry(model){
	    	if (!model){
	    		log.error({title:'view entry', details: 'no model passed'});
	    		error.create({
	                name: 'model view entry',
	                message: 'no model passed',
	                notifyOff: true
	            });
	    	}
	    	return new View(model);
	    }
		
		function View(model){
			if (model){
				log.debug({title:'view View', details: 'value of model.input: ' + model.input});	
			} else {
				log.debug({title:'view View', details: 'no model available'});
			}
			this.form = createForm(model);
	    }

	    function createForm(model){
	    	
	    	//create the form
	    	var objForm = ui.createForm({
            	title : 'PTM Lookup Utility', 
            	hideNavBar: false
            });
	    	
	    	if (!model){
	    		return objForm;
	    	}
	    	
	    	objForm.addSubmitButton({
	    	    label : 'Lookup'
	    	});
	    	
	    	//create the header elements
	    	var lookupGroup = []
	    	var t = {
	    		col : 'input',
	    		type : ui.FieldType.TEXT,
	    		displaytype : ui.FieldDisplayType.NORMAL,
	    		label : 'Keywords',
	    		value : model.input
	    	}
	    	lookupGroup.push(t)
            
	    	//this approach is overkill for a single value array
	    	lookupGroup.forEach(function(f){
	    		var fld = objForm.addField({
                    id: f.col,
                    type: f.type,
                    label: f.label
                }).updateDisplayType({
                    displayType: f.displaytype
                });
            });
            
	    	//add sublist; no tab required
            var objSublist = objForm.addSublist({
              id : 'sublist',
              type : ui.SublistType.LIST,
              label : 'Results'
            });
        
            if (!isEmpty(model.data)){
            	//create result set list
				objSublist.addField({
					id : 'client',
					label : 'Client',
					type : ui.FieldType.TEXT
				}).updateDisplayType({
				    displayType: ui.FieldDisplayType.INLINE
				});

				objSublist.addField({
					id : 'ptm',
					label : 'PTM',
					type : ui.FieldType.TEXTAREA
				}).updateDisplayType({
				    displayType: ui.FieldDisplayType.INLINE
				});
				
				objSublist.addField({
					id : 'incident',
					label : 'Incident',
					type : ui.FieldType.DATE
				}).updateDisplayType({
				    displayType: ui.FieldDisplayType.INLINE
				});
            	
            	
            	//add the data
            	model.data.forEach(function(row, i){
            		//to work with this data, we must stringify and parse it
            		var o = JSON.parse(JSON.stringify(row))
        			
               		objSublist.setSublistValue({ 
  	                  id : 'client', 
  	                  value : o.values.info2,
  	                  line : i
  	                	});
          		
              		objSublist.setSublistValue({ 
  	  	                  id : 'ptm', 
  	  	                  value :  '<a target=_blank class=dottedlink href=' + PTMLINK + o.id + '>'+  o.values.name + '</a>',
  	  	                  line : i
  	  	                });
              		
              		objSublist.setSublistValue({ 
  	  	                  id : 'incident', 
  	  	                  value : o.values.info1,
  	  	                  line : i
  	  	                });
              		
              		objSublist.label = i + 1 + ' Results';
            	});
            }
            return objForm;
	    }

    // Entry Points
    return {
      load : entry
    };  // Return Entry Points
  } // Function 
); // Define

 

Work with NetSuite Professionals

My hope is that all of us learn to wield the power of the NetSuite development platform.   The capacity to invent and solve for challenges is one of my most favorite notions when offering expertise to help our clients realize the value of their NetSuite investment.

If you found this article valuable, feel free to sign up for new articles as I post them.  If you are looking to work with a team that holds high standards for care and values NetSuite leadership, let’s have a conversation.

Marty Zigman

Holding all three official certifications, Marty is regarded as the top NetSuite expert and leads a team of senior professionals at Prolecto Resources, Inc. He is a former Deloitte & Touche CPA and has held CTO roles. For over 30 years, Marty has produced leadership in ERP, CRM and eCommerce business systems. Contact Marty to set up a conversation.

More Posts - Website - Twitter - Facebook - LinkedIn - YouTube

About Marty Zigman

Marty Zigman

Holding all three official certifications, Marty is regarded as the top NetSuite expert and leads a team of senior professionals at Prolecto Resources, Inc. He is a former Deloitte & Touche CPA and has held CTO roles. For over 30 years, Marty has produced leadership in ERP, CRM and eCommerce business systems. Contact Marty to set up a conversation.

Biography • Website • X (Twitter) • Facebook • LinkedIn • YouTube

Leave a Reply

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