define(['N/record', 'N/render', 'N/search'],
(record, render, search) => {
"use strict";
const TEMPLATE_MAP = {
salesorder: 'CUSTTMPL_JJ_SO_HIDE_LINE_ITEM_STBUK154',
estimate: 'CUSTTMPL_JJ_QUOTE_HIDE_LINE_ITEM_STBUK154',
invoice: 'CUSTTMPL_JJ_INV_HIDE_LINE_ITEM_STBUK154',
itemfulfillment: 'CUSTTMPL_JJ_IF_HIDE_LINE_ITEM_STBUK154'
};
/**
* Searches and maps SO lines to fulfillment lines using orderline references.
* @param {number} createdFromId - Sales Order internal ID
* @param {Array<Object>} orderLineRefs - Array of { line: number, shipped: number }
* @returns {Array<Object>} - Enriched line-level data
*/
function getMatchedSalesOrderLines(createdFromId, orderLineRefs) {
try {
const matchedLines = [];
const lineMap = {};
const soSearch = search.create({
type: search.Type.SALES_ORDER,
filters: [['internalid', 'is', createdFromId]],
columns: [
'line',
'item',
'item.name',
'quantity',
'custcol_partno',
'custcol_labeldoornumber',
'custcol_ezdoorheight',
'custcol_doorwidth',
'custcol_ezfirerated',
'custcol_ezhingehand',
'custcol_ezhingeprep',
'custcol_ezstriketype',
'custcol_ezwallthickness'
]
});
soSearch.run().each(result => {
const lineNum = parseInt(result.getValue({ name: 'line' }));
lineMap[lineNum] = {
custcol_partno: result.getValue('custcol_partno'),
custcol_labeldoornumber: result.getValue('custcol_labeldoornumber'),
description: result.getText('item') || '',
custcol_ezwallthickness: result.getValue('custcol_ezwallthickness'),
custcol_doorwidth: result.getValue('custcol_doorwidth'),
custcol_ezdoorheight: result.getValue('custcol_ezdoorheight'),
custcol_ezstriketype: result.getValue('custcol_ezstriketype'),
custcol_ezhingehand: result.getValue('custcol_ezhingehand'),
custcol_ezfirerated: result.getValue('custcol_ezfirerated'),
custcol_ezhingeprep: result.getValue('custcol_ezhingeprep'),
quantityordered: result.getValue('quantity')
};
return true;
});
for (const ref of orderLineRefs) {
const soLine = lineMap[ref.line];
if (soLine) {
matchedLines.push({
...soLine,
quantityshipped: ref.shipped
});
}
}
return matchedLines;
} catch (e) {
log.error("Error@getMatchedSalesOrderLines", e);
return [];
}
}
const onRequest = (scriptContext) => {
try {
if (scriptContext.request.method === 'GET') {
const reqParams = scriptContext.request.parameters;
const transRecId = reqParams.transRecId;
const transRecType = reqParams.transRecType;
const mainRecord = record.load({ type: transRecType, id: transRecId });
const templateId = TEMPLATE_MAP[transRecType];
if (!templateId) throw new Error(`No template configured for transaction type: ${transRecType}`);
const renderTemp = render.create();
renderTemp.setTemplateByScriptId({ scriptId: templateId });
if (transRecType === 'itemfulfillment') {
const createdFromId = mainRecord.getValue({ fieldId: 'createdfrom' });
const fulfillmentLineCount = mainRecord.getLineCount({ sublistId: 'item' });
const orderLineRefs = [];
for (let i = 0; i < fulfillmentLineCount; i++) {
const line = mainRecord.getSublistValue({ sublistId: 'item', fieldId: 'orderline', line: i });
const shipped = mainRecord.getSublistValue({ sublistId: 'item', fieldId: 'quantity', line: i });
if (line != null) {
orderLineRefs.push({ line: parseInt(line), shipped });
}
}
if (orderLineRefs.length && createdFromId) {
const enrichedData = getMatchedSalesOrderLines(createdFromId, orderLineRefs);
renderTemp.addCustomDataSource({
format: render.DataSource.OBJECT,
alias: 'salesorder',
data: { item: enrichedData }
});
}
}
renderTemp.addRecord({
templateName: 'record',
record: mainRecord
});
const transPdf = renderTemp.renderAsPdf();
scriptContext.response.writeFile(transPdf, true);
}
} catch (err) {
log.error("error @ onRequest", err);
scriptContext.response.write(`Error: ${err.message}`);
}
};
return { onRequest };
});
This suitelet script runs the search with transaction id and gets the necessary fields and uses these result in the pdf