Generating PDFs from Netsuite Sales Invoices

We have a need to generate PDFs based on invoices for a select set of item numbers in Netsuite and automatically transmit them to a partner.  We were thinking this may be a good fit for a Celigo Flow (Netsuite Saved Search to identify the sales invoices --> Celigo Flow --> call to an API to generate each invoice --> pick up the invoices and send).

Wondering if anyone knows whether such functionality exists to generate PDFs to the Netsuite file cabinet via API, and whether it's accessible from Celigo?

0

Comments

9 comments
Date Votes
  • Official comment

    Hey Wade,

    You can write a SuiteScript preSend hook (pretty similar to preSavePage hook on IO), but it runs inside of NetSuite. You can configure a NetSuite export with a search that returns internal ids of the invoices whose PDF needs to be generated, then you can write a preSend hook in SuiteScript with the following sample code to generate PDF, store them in file cabinet and return the internalIds to IO. 

     

    function printRecordAsFile(recordType, recordId, folder) {
        if(!recordType || !recordId || !folder)
           return;

        var response = {}
        try{
            var recordFile = nlapiPrintRecord('transaction', recordId, 'PDF')
            recordFile.setFolder(folder)
            recordFile.setName(recordType+'_' + recordId + '_' + Date.now())
            var fileId = nlapiSubmitFile(recordFile)
            response.success = true
            response.data = fileId

        } catch(ex) {
            logger.error('Error while saving file recordType'+recordType+'| recordId# '+recordId, ex.code)
            response.success = false
            response.error = "Error while saving file"+ex.name + ' | '+ ex.message

        }
        return response
    }

    This will have the PDFs in NS file cabinet and then you can configure another export to get these files from NS, send to destination and delete them from FileCabinet after export, if needed to save space using Purge File After Export. 

     

  • Hi Wade Shelton,

    We have this on our Salesforce-NetSuite integration app which we solved with some SuiteScript code. We are looking into how to solve this for you on a custom flow and should get back to you Monday. Thanks for letting us know about this need and for your patience!

    0
  • Thank you both for the help.  


    We've been looking into using this method, and think we have all but one part of it figured out:  How to pass in parameters for the SuiteScript in the PreSend hook.

    I haven't seen any documentation on it.  Since the suitescript obviously lives in Netsuite, how do we create an argument list to pass to the script?  

    For example - assuming a function called "DoSomething(param1,param2)" in a Netsuite Script with FileID 1, and assuming that I wanted to pass values from Field1 and Field 2 into param1 and param2, would the function call inside of Celigo look like this?

     

    0
  • Hey Wade,

    The preSend hook receives a set of options as defined here (similar to preSave hook).

    You can review the debug logs in a RESTlet that comes with integrator.io bundle called "Celigo Realtime RESTlet Import Runner". It will give you a good idea on what parameters by default is passed by IO and should suffice all your needs 

    function myPreSendHook(options) {
    nlapiLogExecution('DEBUG', 'preSend options', options);
    var searchData = options.data || [];
    var response = { data : [], errors : []}
    //do processing on searchData
    //generate PDF for each id searchData and return FileIds
    //var fileId = printRecordsAsFiles(option[i].data.recordType, option[i].data.id )
    // response.data.push({ fileId : fileId})
    return response
    };


    Though I not 100% sure of the usecase on why you need to pass parameters as you can always set them in your script, ie you script will always get all the search result set. Anyways, there are multiple ways to achieve hooks to receive parameters depending upon the usecase.  

    • You can add some dummy columns in savedsearch with formula(Text) with hardcoded data and this will get passed to the preSend function for each record the search picks.

     

    • The other options is very new feature (pretty cool, I will say :) ), that allows you to define your own settings on any export/import/flow. Its called CustomSettings, and you see an option on every export/import at the bottom. On a high level it allows you to define your own settings/fields on export, and these are passed down to each hook in options.settings. 
    • If you have Dev Mode enabled in your User Profile, you follow the screenshot below to generate a simple form, and then add this form in your Export CustomSetting. Now when the export runs, all of these settings will be available to you in the hook.  (Its brand feature in the latest released, docs are in progress and may take few more weeks due to complexity in finishing them). 

    0
  • Mujtaba - 

     

    I appreciate all of the help, but unfortunately, we're still missing too many pieces here.

     

    We have created the script in our Netsuite environment, and are working on the Saved Search for the invoices we need to generate, but are really lost on how to map the parameters from the Saved Search results into the function call.

     

    In Celigo, I see this on the Presend hook.  I don't see any place to place the javascript for the Presend hook that you referenced above, as it does not have the "pencil" edit icon like the PreSave Page (See highlights below)

     

    In terms of your suggestion re: adding hardcoded text in a textformula inside the saved search, are you suggesting using that text formula to "create" the JSON object to hold the parameter values?  If so, then I still don't see how to add that column to the function call?

    0
  • Mujtaba -

    Not sure if you're seeing these updates, but any help you (or anyone else at Celigo) could provide would be appreciated.

    Thanks.

    0
  • Hi Wade Shelton,

    Apologies for the delay in response! You need to modify the NetSuite presend SuiteScript hook in NetSuite itself. SuiteScript hooks run in NetSuite, so integrator.io only lets you specify the ID of this hook, but you can't modify it in integrator.io.

    0
  • Courtney 

     

    Thanks for the follow up, but I still think I'm missing something here.  

     

    So... I have an export that calls a saved search.  That saved search holds the logic for which invoices I need to generate PDF invoices for... i.e. - each record in that saved search needs an invoice generated to PDF.  

    If the presend hook doesn't allow parameterization from the Celigo side, how do you call the script for each invoice returned by the saved search?

    0
  • Hey Wade,
     
    So, here’s a high level workflow on how IO export runs :
    1. Export gets Triggered by IO and a request is send to NS with searchId
    2. IO’s NS code executes the search in chunks (gets records per your batchSize/pageSize set on NS export), triggers a preSend hook inside NetSuite and sends all the data in that hook. 
    3. Users can then use that hook to manipulate the data before it goes to IO. They can remove records, generate PDFs against the ids coming in that data and replace the data with PDFs ids etc.
    4. The data then goes out of NS to IO, and then is send to the next processor for processing. 

     

    Best Practices when writing SuiteScript hooks 

    • SuiteScript hooks run on RESTlet, so they follow the same NetSuite Governance limits. It's important to keep this in mind as the governance is shared with integrator.io execution as well. It's always a good idea to plan on how many points your hooks will consume.
      • If an Import hook is invoked with one record, a NetSuite RESTlet has 5000 governance points. So, integrator.io will also consume points for all the lookups, create and submit record in NetSuite 
      • If you are running out of points, try tweaking the flow a bit. For example, if you have complicated logic for Import and your hooks are running out of points, you can reduce the pageSize of the corresponding export to have less data passed to the hook for each page.
      • In addition to the limits NetSuite puts on points (amount of API operations you can do), it also puts a limit on execution time. There is a 5 min time limit for a RESTlet, and while writing a hook, ensure that all the processing in hooks is completed within the time limit.
    • The logs from your NetSuite hook can be viewed under the RESTlet Name : “Celigo Realtime Import Restlet Runner” in NetSuite. Set it to debug mode and add logs in your hook file. 
    • Underscore.js javascript library is also available when the hooks are invoked in NetSuite to make it easier to write clean code.
    • integrator.io allows you to attach a single file for a hook. You can build common libraries and build closures into a single file to share common utils across hooks.

     

    To your specific question, attach a hook say myPreSendHook via providing the fileId and function name to the NetSuite Export . The hook looks like below (check the above RestLet to view logs as you run the flow) 

    function myPreSendHook(options) {
    nlapiLogExecution('DEBUG', 'preSend options', JSON.stringify(options.data));
    //var searchData = options.data || [];
    //var response = { data : [], errors : []}
    //do processing on searchData
    //generate PDF for each id searchData and return FileIds
    //var fileId = printRecordsAsFiles(option[i].data.recordType, option[i].data.id )
    // response.data.push({ fileId : fileId})
    //return response
    return options.data;
    };

     

    IO will automatically invoke this function and pass on the options (which contains data fetched from search in options.data). You can do your processing here, and then modify the data being returned. Keep in mind to also set a small pageSize on NS export to make sure that you have smaller data coming up in your hook (so that your don't run into NS governance issues of points) . The point it you entry point is the preSend hook function which gives you all the data it fetches per each export run and then you can call your own function from there. 

    To summarize, IO will invoke the preSend function for  a group of records that are fetched by running the search .The group of records is determined by your batchSize/pageSize property set on export. The function is invoked only once per group , and you can write code in that function to generate pdfs for a group of invoices and send the invoice ids back to IO. 

    Let me know if this helps. 

    0

Please sign in to leave a comment.

 

Didn't find what you were looking for?

New post