'NetSuite - Using SuiteScript to Create Button to Print Packing List on Transfer Order
I'm trying to follow SuiteAnswers 41269 (which is for adding a Packing List to an Item Fulfillment) to add a button to print a packing list to a Transfer Order. This is essentially a test to try and be able to print more forms on more transactions that aren't native to NetSuite (VRMAs, etc.).
Note that the SuiteAnswers above seems to be out of date, because it references SS1.0 and SS2.0, but some of the code for SS2.0 also appears to use SS1.0 code. Since that SA was a little off, I've gone through a lot of the NetSuite help files on the different parts of the render and url modules to help build out these scripts.
My issue is that I keep getting thrown back an error when the "Print 1" button is clicked:
Uncaught ReferenceError: print1 is not defined
at trnfrord.nl?id=658266&whence=&cmid=1652279684591_1868:1132:30
at Object.execCb (NsRequire.js:2047:26)
at Ma.check (NsRequire.js:1193:28)
at Ma.enable (NsRequire.js:1475:10)
at Ma.init (NsRequire.js:1106:11)
at NsRequire.js:1771:18
I'm using a UserEvent Script, a Client Script, and a Suitelet Script. I'm currently using a static entity ID of the Transfer Order while I'm testing this, and will add a dynamic one once I get this working.
UPDATE: After taking in the suggestion below, I realized I had the deploymentID set to the actual internal ID of the deployment, not the ID that matched the scriptID field. I also realized my beforeLoad function was using scriptContext instead of context, which was inside the function.
After fixing these issues, I started getting a "You are not allowed to navigate directly to this page" error. My script deployments were set to testing, so I changed them to released, and I still got this error.
I changed the returnExternalUrl from true to false and received an "Invalid PDF Layout" error.
I looked online and realized that part of my mistake was using the formID of the actual PDF, not create a new Custom Transaction Form and assign the PDF to print from that form.
Having done that, I am now receiving this error:
{"type":"error.SuiteScriptError","name":"UNEXPECTED_ERROR","message":null,"stack":["renderPackingSlip(N/render)","onRequest(/SuiteScripts/buttonTest2/print_suitelet_script.js:18)"],"cause":{"type":"internal error","code":"UNEXPECTED_ERROR","details":null,"userEvent":null,"stackTrace":["renderPackingSlip(N/render)","onRequest(/SuiteScripts/buttonTest2/print_suitelet_script.js:18)"],"notifyOff":false},"id":"7fb1b24b-b66f-44bf-b6f3-282444013ca5-2d323032322e30352e3133","notifyOff":false,"userFacing":false}
Here are the the updated scripts as they stand now, which throw the above error:
UPDATED UE SCRIPT:
/**
* @NApiVersion 2.x
* @NScriptType UserEventScript
* @NModuleScope SameAccount
*/
define([
'N/url',
'N/ui/serverWidget',
],
function (url) {
function beforeLoad(scriptContext) {
try {
if (scriptContext.type == 'view') {
const href = url.resolveScript({
scriptId: 699,
deploymentId: 3,
returnExternalUrl: true
});
scriptContext.form.addButton({
id: 'custpage_print1',
label: 'Print 1',
functionName: "(window.location='" + href + "')"
});
}
} catch (error) {
log.debug('ERROR', error);
}
}
return {
beforeLoad: beforeLoad
};
});
UPDATED SS SCRIPT:
/**
* @NApiVersion 2.x
* @NScriptType Suitelet
*/
define(['N/render'],
function(render) {
function onRequest(context) {
var total = 0;
if (context.request.method == 'GET'){
var ifid = context.request.parameters.custparam_ifid;
var fileObj = render.packingSlip({
entityId: 658266,
printMode: render.PrintMode.PDF,
formId: 129
});
fileObj.save()
}
}
return {
onRequest: onRequest
};
});
PDF:
<?xml version="1.0"?><!DOCTYPE pdf PUBLIC "-//big.faceless.org//report" "report-1.1.dtd">
<pdf>
<head>
<link name="NotoSans" type="font" subtype="truetype" src="${nsfont.NotoSans_Regular}" src-bold="${nsfont.NotoSans_Bold}" src-italic="${nsfont.NotoSans_Italic}" src-bolditalic="${nsfont.NotoSans_BoldItalic}" bytes="2" />
<#if .locale == "zh_CN">
<link name="NotoSansCJKsc" type="font" subtype="opentype" src="${nsfont.NotoSansCJKsc_Regular}" src-bold="${nsfont.NotoSansCJKsc_Bold}" bytes="2" />
<#elseif .locale == "zh_TW">
<link name="NotoSansCJKtc" type="font" subtype="opentype" src="${nsfont.NotoSansCJKtc_Regular}" src-bold="${nsfont.NotoSansCJKtc_Bold}" bytes="2" />
<#elseif .locale == "ja_JP">
<link name="NotoSansCJKjp" type="font" subtype="opentype" src="${nsfont.NotoSansCJKjp_Regular}" src-bold="${nsfont.NotoSansCJKjp_Bold}" bytes="2" />
<#elseif .locale == "ko_KR">
<link name="NotoSansCJKkr" type="font" subtype="opentype" src="${nsfont.NotoSansCJKkr_Regular}" src-bold="${nsfont.NotoSansCJKkr_Bold}" bytes="2" />
<#elseif .locale == "th_TH">
<link name="NotoSansThai" type="font" subtype="opentype" src="${nsfont.NotoSansThai_Regular}" src-bold="${nsfont.NotoSansThai_Bold}" bytes="2" />
</#if>
<macrolist>
<macro id="nlheader">
<table style="width: 100%; font-size: 10pt;"><tr>
<td rowspan="3" style="padding: 0px; width: 222px;"><#if companyInformation.logoUrl?length != 0><img src="${companyInformation.logoUrl}" style="margin: 5px; width: 120px; height: 60px; float: left;" /> </#if></td>
<td rowspan="3" style="padding: 0px; width: 248px;">${companyInformation.addressText}</td>
<td align="right" style="padding: 0px; width: 323px;"><span style="font-size: 28pt;">${record@title}</span></td>
</tr>
<tr>
<td align="right" style="width: 337px;"><span class="number">#${record.tranid}</span></td>
</tr>
<tr>
<td align="right" style="width: 337px;">${record.trandate}</td>
</tr></table>
</macro>
<macro id="nlfooter">
<table class="footer"><tr><#if preferences.PRINT_BARCODES>
<td><barcode codetype="code128" showtext="true" value="${record.tranid}"/></td>
</#if>
<td align="right"><pagenumber/> of <totalpages/></td>
</tr></table>
</macro>
</macrolist>
<style type="text/css">* {
<#if .locale == "zh_CN">
font-family: NotoSans, NotoSansCJKsc, sans-serif;
<#elseif .locale == "zh_TW">
font-family: NotoSans, NotoSansCJKtc, sans-serif;
<#elseif .locale == "ja_JP">
font-family: NotoSans, NotoSansCJKjp, sans-serif;
<#elseif .locale == "ko_KR">
font-family: NotoSans, NotoSansCJKkr, sans-serif;
<#elseif .locale == "th_TH">
font-family: NotoSans, NotoSansThai, sans-serif;
<#else>
font-family: NotoSans, sans-serif;
</#if>
}
table {
font-size: 9pt;
table-layout: fixed;
}
th {
font-weight: bold;
font-size: 8pt;
vertical-align: middle;
padding: 5px 6px 3px;
background-color: #e3e3e3;
color: #333333;
}
td {
padding: 4px 6px;
}
td p { align:left }
b {
font-weight: bold;
color: #333333;
}
table.header td {
padding: 0;
font-size: 10pt;
}
table.footer td {
padding: 0;
font-size: 8pt;
}
table.itemtable th {
padding-bottom: 10px;
padding-top: 10px;
}
table.body td {
padding-top: 2px;
}
td.addressheader {
font-size: 8pt;
font-weight: bold;
padding-top: 6px;
padding-bottom: 2px;
}
td.address {
padding-top: 0;
}
span.title {
font-size: 28pt;
}
span.number {
font-size: 16pt;
}
span.itemname {
font-weight: bold;
line-height: 150%;
}
div.returnform {
width: 100%;
/* To ensure minimal height of return form */
height: 200pt;
page-break-inside: avoid;
page-break-after: avoid;
}
hr {
border-top: 1px dashed #d3d3d3;
width: 100%;
color: #ffffff;
background-color: #ffffff;
height: 1px;
}
</style>
</head>
<body header="nlheader" header-height="10%" footer="nlfooter" footer-height="20pt" padding="0.5in 0.5in 0.5in 0.5in" size="Letter">
<table style="width: 100%; margin-top: 10px;"><tr>
</tr>
<tr>
</tr></table>
<table class="body" style="width: 100%; margin-top: 10px;"><tr>
<th style="width: 160px;">${record.shipmethod@label}</th>
<th>${record.shipphone@label}</th>
</tr>
<tr>
<td style="width: 160px;">${record.shipmethod}</td>
<td>${record.shipphone}</td>
</tr></table>
<#if record.item?has_content>
<table class="itemtable" style="margin-top:10px;width:100%;">
<thead>
<tr>
<th colspan="12" style="width: 264px;">Item</th>
<th colspan="4" style="width: 165px;">UPC / ISBN</th>
<th colspan="3" style="width: 90px;">Options</th>
<th colspan="3" style="width: 90px;">Retail</th>
<th align="right" colspan="2" style="width: 89px;">Remaining</th>
<th align="right" colspan="2" style="width: 64px;">Shipped</th>
</tr>
</thead>
<#list record.item as tranline><tr>
<td colspan="12" style="width: 264px;"><span style="font-size:11px;"><span class="itemname">${tranline.item}</span></span><br /><span style="font-size:10px;">${tranline.description}</span></td>
<td colspan="4" style="width: 165px;"><span style="font-size:10px;">${tranline.custcol_nsts_csic_upc}</span></td>
<td colspan="3" style="width: 90px;"><span style="font-size:10px;">${tranline.options} <#if (tranline.custcol_nsts_csre_rnt_guid?has_content)> Rental <#else> </#if> </span></td>
<td colspan="3" style="width: 90px;"><span style="font-size:10px;">${tranline.custcol_retail}</span></td>
<td align="right" colspan="2" style="width: 89px;"><span style="font-size:10px;">${tranline.quantityremaining}</span></td>
<td align="right" colspan="2" style="width: 64px;"><span style="font-size:10px;">${tranline.quantity}</span></td>
</tr>
</#list></table>
<table align="right" border="1" cellpadding="1" cellspacing="1" style="width:35%;">
<tr>
<td style="width: 114px;">Quantity Total</td>
<td style="width: 97px;">${record.quantity.total}</td>
</tr>
<tr>
<td style="width: 114px;">Subtotal</td>
<td style="width: 97px;">${record.custbody_pack_slip_subtotal}</td>
</tr>
<tr>
<td style="width: 114px;">Shipping & Handling</td>
<td style="width: 97px;">${record.custbody_pack_slip_shiptotal}</td>
</tr>
<tr>
<td style="width: 114px;">Tax</td>
<td style="width: 97px;">${record.custbody_pack_slip_taxtotal}</td>
</tr>
<tr>
<td style="width: 114px;">Total</td>
<td style="width: 97px;">${record.custbody_pack_slip_trxtotal}</td>
</tr></table>
</#if>
</body>
</pdf>
Solution 1:[1]
You are not invoking the function or providing an eval argument (sort of). The functionName argument should just be the exported function name from the client script without parens. e.g.:
functionName: 'print1'
Also note that the client script doesn't have to be a ClientScript and that (IMO) your code is more portable if you use
formObj.clientScriptModulePath = './custom_print_CS.js'; // or whatever you name it and save it besid your User Event script code.
and your client script could look like:
/**
* @NApiVersion 2.x
*/
define([
'N/url',
],
function(url) {
function print1() {
var output = url.resolveScript({
scriptId: 698,
deploymentId: 1695,
returnExternalUrl: true
});
window.open(output);
}
return {
print1: print1
};
})
Solution 2:[2]
I know Suite Answer suggests this but there is an alternate solution that lets you skip the Client Script , by directly redirecting from the button to the Suitelet by directly putting the redirection on the button. The inside of your event script beforeLoad function would look like this
const href = url.resolveScript({
scriptId:698,
deploymentId: 1695,
returnExternalUrl: true
});
context.form.addButton({
id:'custpage_print1',
label: 'Print 1',
functionName: "(window.location='"+href +"')"
});
Don't forget to load the N/url module
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | bknights |
Solution 2 | bluehank |