Framework for Generating Custom NetSuite PDF Files

Inspired by the work completed in a recent article, How To: Leverage NetSuite’s Advanced PDF / HTML Generation Tools, and from a LinkedIn NetSuite user group discussion and suggestions by Daniel JR Arena, Troy Oltmanns, Corey Hunt, and David Smith, I solved a client request to generate custom PDF files based on a custom record object.

PDF Generation Architecture

The architecture summary is as follows:

  1. PDF templates will be stored in a special user designated NetSuite folder.  We can then use this for a template selection list on the custom record itself.  The client can add more templates as needed without a change to the code.
  2. The basic PDF templates will conform to NetSuite’s implementation of the Big Faceless Report Generator.  Because it is not available by NetSuite for custom PDF files, it will not use the FreeMarket syntax as referenced in NetSuite’s Advanced PDF / HTML implementation.
  3. The data to be merged into the template will take advantage of JavaScript JSON and the capacities built into the TrimPath Template engine.  This saves us time by not writing our own parsing routine and it gives us control and looping logic, if needed.
  4. Users can select the template they are interested in while working with the record; By simply clicking a button, a custom SuiteLet will open in a new browser window to display the resulting PDF.

 

Custom SuiteLet to Generate the PDF File

Below is the custom SuiteLet to generate the PDF file.  The key to this function is to get the URL parameters to reference the custom record internal ID and the template ID.  The rest is processing logic.  It is assumed that the TrimPath Template Engine has been uploaded and is referenced as a Library function.

function id002_bfo_create_note()

{
	var func = 'id002_bfo_create_note ';
	var id = request.getParameter('id');
	var template = request.getParameter('template');

	nlapiLogExecution('AUDIT', func + 'with id | template:', id + '|' + template);

	try {
		// load the custom record
		var rec = nlapiLoadRecord('customrecord_dffg_note', id);

		//at this time, we have one node called "note".  But we can add more data elements as needed later
		if (rec){
			var recJSON = JSON.stringify(rec) ;
			var data = {note : eval('(' + recJSON + ')') };

			nlapiLogExecution('AUDIT', func + 'JSON Data:', JSON.stringify(data));
		} else {
			return;
		}

		//load the template data (JST plus BFO Syntax)
		var templateJST = nlapiLoadFile(template);
		var xml = templateJST.getValue();

		// now we want to call TrimPath to parse the XML in JST format
		// remember that the String object has been prototyped to call TrimPath.parseTemplate on our behalf
		var result = xml.process(data);

		nlapiLogExecution('DEBUG', func + 'XML:', nlapiEscapeXML(xml));
		nlapiLogExecution('DEBUG', func + 'RESULT:', nlapiEscapeXML(result));
		var file = nlapiXMLToPDF( result );
		response.setContentType('PDF',id + '.pdf', 'inline');
		response.write( file.getValue() );   

	}
	catch(ex)
	{
		var errorStr = (ex.getCode != null) ? ex.getCode() + ' ' + ex.getDetails() + ' ' + ex.getStackTrace().join(' ') : ex.toString();
		nlapiLogExecution('DEBUG', func, 'A problem occurred: ' + ' ' + errorStr);
	}
}

Sample PDF Template Illustrates JSON Data

To help understand what is happening, I have screen-shotted the JSON data that is returned from NetSuite’s standard nlapiLoadRecord call.  How convenient because we simply supply the entire row to the template engine without any extra work.  Resist the urge to touch the data — the TrimPath JST framework has utilities to format your data.

With the JSON data, you then can have very simple calls to your PDF XML template file following Big Faceless Organization (BFO) guidelines. 

Finally, the SuiteLet outputs the result which gave our client full capacity to produce the template and PDF without needing a SuiteScript developer.  They like that.

Get Help Optimizing NetSuite

NetSuite was designed to be optimized to fit requirements well. If you would like help to get the most out of your NetSuite system, contact us.

Be Sociable, Share!

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

| Tags: , , , , , , | Category: NetSuite, Reporting, Technical | 12 Comments

12 thoughts on “Framework for Generating Custom NetSuite PDF Files

  1. Corey Hunt says:

    Very nice. That’s much cleaner than my solution. I wish I had know about TrimPath when I made it.

  2. Marty Zigman says:

    Thanks to the contributions you and the others provided in the LinkedIn group discussion, this solution was inspired. I see a class of problems I want to solve under this approach. One is a custom email generator alternative. Sure feels good to take control over our destiny.

  3. Thanks for sharing!

  4. Amol says:

    The above code is nice one but I have one problem I have to create .txt file.
    1.How it is create?
    2.That file have hexadecimal coded language which is generated on button click.which convert whole existing form in hexadecimal code. How it create? please tell me answer.

  5. Amol says:

    How to create custom bar,pie,etc. chart though portlet? please give me example…
    Thank you.

  6. Marty Zigman says:

    Hello Amol,

    The output is really all about strings. So you can change the code element that currently illustrates response.setContentType(‘PDF’,id + ‘.pdf’, ‘inline’) to response.setContentType(‘PLAINTEXT’,id + ‘.txt’, ‘inline’).

    As am not sure what you mean by Hexadecimal coded language. Can you provide an example?

    Marty

  7. Marty Zigman says:

    The Portlet object works with these types:

    1. Lists
    2. Form
    3. HTML
    4. Links

    The only one that can draw those graphical elements is HTML. Yet, the HTML can be anything. You won’t easily be able to draw NetSuite provided graphical elements as these are built with NetSuite provided portlet objects in the drag and drop interface.. Instead, you might want to consider using something like the Google Charts API. What you can do is get the data you need, call the Google APIs and draw it in the HTML portlet. It’s basically server side and client side JavaScript to pull it all together.

  8. Matt Gaspar says:

    Great article and blog! I implemented a solution based on your article using Handlebars.js as the template engine. I noticed that TrimPath hasn’t been updated since 2008 and I try to avoid using any abandoned open source projects! Also Handlebars has a lot more functionality and has an ability to precompile the template for much faster execution.
    There are actually a lot of options for javascript based template engines.

    I also opened an enhancement request with NetSuite to allow passing a custom javascript object into Freemarker (Advanced PDF). It should be possible but NetSuite’s api current only allows native Netsuite object types. I don’t see any reason not to make it more useable.

  9. Marty Zigman says:

    Hey Matt,

    Most interesting. Handlebars looks most useful and I agree, that old TrimPath template engine has not seen any updates in too long. I have been more bullish on FreeMarker and the Advanced PDF because it appears that we can indeed drop in record objects to the template model when we script. We have built a new communications framework that we are plugging into a customer solution but it is not yet ready for market. But I have yet to try this for a generic Javascript JSON object. I want to stay closer to the FreeMarker environment because all NetSuite users are getting acquainted with it. This means our innovations look “best practice”.

    Also, I just reviewed some 2015.1 updates and the PDF template API is becoming more robust.

    Marty

  10. Kishor says:

    Hi Marty,

    Can we put water mark on Advanced PDF layout

  11. Marty Zigman says:

    Hi Kishor,

    Here is an example NetSuite Advanced PDF with the watermark.

    To produce the watermark in your PDF, do the following:

    1. Create a Macro to reference a special tag
    2. Reference it in the Body definition

    See below the code snippet (thanks to Mike, a consultant on our staff, to help with this solution):

    <macro id="watermark">
    <p id="watermarkbody" rotate="-30" valign="middle" align="center">ORIGINAL</p>
    </macro>

    <body background-macro="watermark" header="nlheader" header-height="10%" footer="nlfooter" footer-height="20pt" padding="0.5in 0.5in 0.5in 0.5in" size="Letter">

Leave a Reply

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