I have created a print button to print an advanced PDF from a sales order, so the client can get a commercial invoice generated. I have done this for other clients with success but I am running into an issue where the standard templates will print a pdf from the button but the customized ones will not. Where am I going wrong in the code here:
Suitelet:
function onRequest(context) {
var custom_id = context.request.parameters.custom_id;
var tempid = context.request.parameters.tempid;
log.debug("id", custom_id);
log.debug("id", context.request.parameters);
var pdfFileName = "Commercial Invoice";
var renderer = render.create();
var content = renderer.addRecord({
templateName: 'record',
record: record.load({
type: record.Type.SALES_ORDER,
id: custom_id
})
});
renderer.setTemplateByScriptId(tempid);
var pdfFile = renderer.renderAsPdf();
log.debug ("pdf", pdfFile)
context.response.writeFile(renderer.renderAsPdf(),true);
}
return {
onRequest: onRequest
}
});
Client Script:
function onButtonClick() {
var suiteletUrl = url.resolveScript({
scriptId: 'customscript_commercialinv_so_sl',
deploymentId: 'customdeploy_commercialinv_so_sl',
returnExternalUrl: true,
params: {
custom_id: currentRecord.get().id, tempid: "CUSTTMPL_109_4753601_SB1_795"
},
});
window.open(suiteletUrl,"_blank");
}
exports.onButtonClick = onButtonClick;
exports.pageInit = pageInit;
return exports;
});
My user script looks solid. I am getting this error when I direct the Client script to a custom template as referenced above:
{"type":"error.SuiteScriptError","name":"MISSING_PDF_PARAMETERS","message":"Missing parameters required to generate PDF","stack":["renderAsPdf(N/render)","onRequest(/SuiteScripts/MegWebb/cust_SOprinttemplate_SL.js:28)"],"cause":{"type":"internal error","code":"MISSING_PDF_PARAMETERS","details":"Missing parameters required to generate PDF","userEvent":null,"stackTrace":["renderAsPdf(N/render)","onRequest(/SuiteScripts/MegWebb/cust_SOprinttemplate_SL.js:28)"],"notifyOff":false},"id":"","notifyOff":false,"userFacing":false}
Any help would be much appreciated!
Related
I am trying to use search.create in RESTlet to check whether a contact exist in my netsuite account or not. This is my code
define(['N/record','N/search'],
function(record,search) {
function dosearch(contact_mail){
var result;
try{
result = search.create({
type : record.Type.CONTACT,
filters:['email','IS',contact_mail],
columns: ['email']
}).run().getRange({
start: 0,
end: 1
});
log.debug(result);
} catch(e){
result = e;
}
return result;
}
function doGet(requestParams) {
var contact_mail = requestParams.email;
log.debug(contact_mail);
var result = dosearch(contact_mail);
return result;
}
And I am accesing this RESTlet from my system using this node js code.
async function myGet(){
var para = {id: 'my_id' , email: 'email'};
var accountSettings = {
//'Content-Type': 'application/json',
//params: para,
accountId: account_id,
tokenKey: token_key,
tokenSecret: token_secret,
consumerKey: consumer_key,
consumerSecret: consumer_secret };
var urlSettings = {
url: link
}
var myInvoices = nsrestlet.createLink(accountSettings, urlSettings)
try{
var res = await myInvoices.get(para);
console.log(res);
} catch(e){
console.log(e);
}
}
Is there something wrong with the code? I tried same search code with user event script and it worked there but its not working in RESTlet. Can someone help me with this.
Silently failing a search is an indicator that the role you are using with your RESTlet doesn't have access to contacts. I generally see this with transactions I'd expect contacts work similarly because under the covers a contact is a form of entity.
I tried implementing netsuite pagination in suitescript 2.0, the prev button is working but instead of 20 search data it's return like whole. Next button is not working always throwing error INVALID_PAGE_RANGE
Here is the suitelet code
var loadSearch = search.load({
id: 'customsearch_inv123'
});
var i = 0;
var pagedData = loadSearch.runPaged({
pageSize: 20
});
pagedData.pageRanges.forEach(function (pageRange) {
var lastPageRange = pagedData.pageRanges[pagedData.pageRanges.length - 1];
var Page = pagedData.fetch({
index: lastPageRange.index
});
if (context.request.parameters.next) {
Page = Page.next();
}
if (context.request.parameters.prev) {
Page =Page.prev();
}
Page.data.forEach(function (result) {
var number = result.getValue({
name: 'inventorynumber'
});
if (number) {
updateSublist.setSublistValue({
id: 'custpage_number',
line: i,
value: number
});
}
i++;
});
});
// submit button
form.addSubmitButton({
label: 'Submit'
});
form.addButton({
id: '_next',
label: 'Next',
functionName: 'next'
});
form.addButton({
id: '_prev',
label: 'Prev',
functionName: 'prev'
});
form.clientScriptModulePath = 'SuiteScripts/client.js';
In client script i wrote the function next and prev, and redirected to the same page with next or prev parameters and based on that i called page.next and page.prev.
If you have implemented this please help me.
here is client code.
next: function () {
window.location.href = 'example.com' + '&next=t';
},
prev: function () {
window.location.href = 'example.com' + '&prev=t';
}
You should pass current/requested pageNumber from client script and then use it to fetch data for that specific page like below
var pagedData = loadSearch.runPaged({
pageSize: 20
});
requestedPageNumber = 2
var page = pagedData.fetch({ index: requestedPageNumber });
page.data.forEach(function (result) {
// result is the searchResult object
// YOUR CODE GOES HERE
var number = result.getValue({
name: 'inventorynumber'
});
})
What you did wrong was you were starting from the last index and then trying to fetch unexisting array index in case where user clicks next.
Note: previous button should be disabled/hidden where there are no pages so as to not run into the same error and the same could be done for the next button too.
So, basically I am trying to get the XML template for the PDF and I plan to eventually add some additional XML in the code after getting the template working in this manner. However, when I attempt to pass a data source object to the PDF it is not working. Does anyone know the cause of this issue, and what I am doing incorrectly here?
XML template (stripped out everything but the variable in the table for testing):
<?xml version="1.0"?><!DOCTYPE pdf PUBLIC "-//big.faceless.org//report" "report-1.1.dtd">
<pdf>
<!--removed lengthy head to make code more readable-->
<body footer="nlfooter" footer-height="20pt" padding="0.5in 0.5in 0.5in 0.5in" size="Letter">
<table class="body" style="width: 100%; margin-top: 10px;">
<tr>
<td>${jsonObj.terms}</td>
</tr></table>
</body>
</pdf>
Script:
/**
* #NApiVersion 2.x
* #NScriptType UserEventScript
* #NModuleScope SameAccount
*/
define(['N/error','N/render','N/file','N/record','N/log'],
/**
* #param {error} error
*/
function(error, render, file, record, log) {
function beforeSubmit(context) {
log.debug('After submitting invoice, create advanced PDF detail layout', context);
var isInvoice = context.newRecord.type == 'invoice';
// Create advanced PDF
if (isInvoice){
log.audit('Creating invoice');
renderRecordToPdfWithTemplate(context.newRecord);
}
else{
error.create({
name: 'ERROR_RECEIVED',
message: 'Cannot create advanced PDF from this record type'
});
log.audit(error.name,error.message);
}
}
return {
beforeSubmit: beforeSubmit
};
function renderRecordToPdfWithTemplate(context) {
var jsonObj = {
terms: "test terms"
};
var templateId = '7959'; // ID of the XML
var templateFile = file.load({id: templateId});
var renderer = render.create();
renderer.templateContent = templateFile.getContents();
/*
renderer.addRecord({
type: record.Type.INVOICE,
record: context
});
*/
renderer.addCustomDataSource({
format: render.DataSource.OBJECT,
alias: "jsonObj",
data: jsonObj
});
log.debug('Rendering as PDF');
var renderXmlAsString = renderer.renderAsString();
log.debug('Added record to PDF', context);
var invoicePdf = render.xmlToPdf({
xmlString: renderXmlAsString
});
invoicePdf.name = 'Testpdf2.pdf';
invoicePdf.folder = -15;
try{
var fileId = invoicePdf.save();
log.debug('Saved PDF to file '+fileId);
}
catch(e){
alert('Error saving file');
log.debug('Error saving file');
debugger;
}
}
});
You don't need the renderer.renderAsString(); since you're already loading the XML from the file cabinet.
function renderRecordToPdfWithTemplate(context) {
var jsonObj = {
terms: "test terms"
};
var templateId = '7959'; // ID of the XML
var templateFile = file.load({id: templateId});
var renderer = render.create();
renderer.templateContent = templateFile.getContents();
renderer.addCustomDataSource({
format: render.DataSource.OBJECT,
alias: "jsonObj",
data: jsonObj
});
log.debug('Rendering as PDF');
var invoicePdf = renderer.renderAsPdf();
invoicePdf.name = 'Testpdf2.pdf';
invoicePdf.folder = -15;
try{
var fileId = invoicePdf.save();
log.debug('Saved PDF to file '+fileId);
}
catch(e){
alert('Error saving file');
log.debug('Error saving file');
debugger;
}
}
I add a button.when I click the button ,it will execut this function.
function onRequest(context) {
log.debug('exportTest');
var stringInput = 'Hello World\nHello World';
var base64EncodedString = encode.convert({
string : stringInput,
inputEncoding : encode.Encoding.UTF_8,
outputEncoding : encode.Encoding.BASE_64
});
var fileUrl = file.create({
name : 'test.txt',
fileType : file.Type.PLAINTEXT,
contents : base64EncodedString
});
log.debug('fileUrl',fileUrl);
context.response.writeFile({
file : fileUrl
});
}
I want to get a file called 'test.txt',but it return a String.
enter image description here
It appears to be returning exactly what you're asking for - that is, 'Hello World\nHello World' encoded as a base64 string. To display the original text you would need to decode again, or for this example you could just skip the encoding as it's only text anyway.
I know how to do it now.
1.add a button
function beforeLoad(scriptContext) {
if(scriptContext.type == scriptContext.UserEventType.VIEW){
var form = scriptContext.form;
form.addButton({
id: "custpage_export_test",
label: "Export Test",
functionName: 'exportExcel'
});
form.clientScriptModulePath = './export_test.js';
}
}
2.the export_test.js
function exportExcel(context) {
var suiteletURL = url.resolveScript({
scriptId:'customscript_export_excel',
deploymentId:'customdeploy_export_excel',
params:context
})
var downloadLink = document.createElement('a');
downloadLink.href = suiteletURL;
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
}
3.the export_excel.js
function onRequest(context) {
var response = context.response;
log.debug('exportTest');
var stringInput = 'Hello World\nHello World';
var excelFile = file.create({
name: 'test.txt',
fileType: file.Type.PLAINTEXT,
contents: stringInput
});
response.writeFile(excelFile);
}
var formData = new FormData();
formData.append("name", "John");
formData.append("age", "31");
for (var value of formData.values()) {
log.debug(value);
}
but when i want to log form values using formData api. It's giving below error.
ReferenceError: "FormData" is not defined.
FormData is a client side API managed under XMHttpRequest
UserEvent scripts are server side scripts with no browser based APIs available at all.
So you could use FormData in a client script to send info to a Suitelet or RESTlet but it's not present in a UserEvent script.
If you want to create a form in a Suitelet using SS2.0 you can use the following as a sample:
/**
*#NApiVersion 2.x
*#NScriptType Suitelet
*/
define(["N/log", "N/redirect", "N/runtime", "N/ui/serverWidget", "N/url", "./kotnRECBCFilters"],
function (log, redirect, runtime, ui, url, kotnRECBCFilters_1) {
function showPropertiesForm(context) {
var form = ui.createForm({
title: 'Property Trust Ledger'
});
var req = context.request;
var fromLoc = form.addField({
id: 'custpage_loc',
type: ui.FieldType.SELECT,
label: 'For Property',
source: 'location'
});
fromLoc.updateLayoutType({ layoutType: ui.FieldLayoutType.NORMAL });
fromLoc.updateBreakType({ breakType: ui.FieldBreakType.STARTCOL });
if (req.parameters.custpage_loc) {
fromLoc.defaultValue = req.parameters.custpage_loc;
}
var notAfterDate = form.addField({
id: 'custpage_not_after',
type: ui.FieldType.DATE,
label: 'On or Before'
});
if (req.parameters.custpage_not_after) {
notAfterDate.defaultValue = req.parameters.custpage_not_after;
}
form.addSubmitButton({
label: 'Get Detail'
});
//... bunch of stuff removed
context.response.writePage(form);
}
function onRequest(context) {
if (context.request.method === 'POST') {
var currentScript = runtime.getCurrentScript();
var params = {};
for (var k in context.request.parameters) {
if (k.indexOf('custpage_') == 0 && k.indexOf('custpage_transactions') == -1) {
if ((/^custpage_.*_display$/).test(k))
continue;
params[k] = context.request.parameters[k];
}
}
redirect.toSuitelet({
scriptId: currentScript.id,
deploymentId: currentScript.deploymentId,
parameters: params
});
return;
}
showPropertiesForm(context);
}
exports.onRequest = onRequest;
});