The Client Script used for generating the PDF:
/**
* @NApiVersion 2.1
* @NScriptType ClientScript
* @NModuleScope SameAccount
*/
/***********************************************************************************************************
* Chesapeake Coffee Roasters-USA-NS/SCA
* CCRUN-8 Custom Print Button for Bank Deposit Summary with Advanced PDF/HTML Template
*****************************************************************************************
* Author : Jobin & Jismi
* Date Created : 15-October-2024
* Description : This Client script is for PDF customization of Bank deposit
*
* REVISION HISTORY
*
* Version 1.0 : 15-October-2024 : Created the initial build by JJ0325
*
************************************************************************************/
define(['N/record', 'N/currentRecord','N/url'],
/**
* @param{record} record
* @param{currentRecord} currentRecord
*/
function (record, currentRecord,url) {
/**
* Function to be executed after page is initialized.
*
* @param {Object} scriptContext
* @param {Record} scriptContext.currentRecord - Current form record
* @param {string} scriptContext.mode - The mode in which the record is being accessed (create, copy, or edit)
*
* @since 2015.2
*/
function pageInit(scriptContext) {
try {
return true;
} catch (e) {
console.error('error in Pageinit', e);
}
}
/**
* To generate the Print
*/
function printDeposit() {
try {
let recId = currentRecord.get().id;
let recType = currentRecord.get().type;
console.log("internalId of the Deposit record is ", recId);
let currenturl = url.resolveScript({
scriptId: "customscript_jj_sl_btn_pdf_depo_ccrun_8",
deploymentId: "customdeploy_jj_sl_btn_pdf_depo_ccrun_8",
params:
{
"recId": recId, "recType": recType
},
returnExternalUrl: false
})
window.open(currenturl)
}
catch (e) {
console.error({ title: "error@customerPaymentReceipt", details: e });
}
}
return {
pageInit: pageInit,
printDeposit: printDeposit
};
});
The Suitelet used for generating the PDF:
/**
* @NApiVersion 2.1
* @NScriptType Suitelet
*/
/***********************************************************************************************************
* Chesapeake Coffee Roasters-USA-NS/SCA
* CCRUN-8 Custom Print Button for Bank Deposit Summary with Advanced PDF/HTML Template
*****************************************************************************************
* Author : Jobin & Jismi
* Date Created : 15-October-2024
* Description : This suitelet script is for PDF customization of Bank deposit
*
* REVISION HISTORY
*
* Version 1.0: 19-October-2024 : Created the initial build by JJ0325
*
************************************************************************************/
define(['N/record', 'N/render', 'N/file', 'N/format'],
/**
* @param{record} record
*/
(record, render, file, format1) => {
/**
* Defines the Suitelet script trigger point.
* @param {Object} scriptContext
* @param {ServerRequest} scriptContext.request - Incoming request
* @param {ServerResponse} scriptContext.response - Suitelet response
* @since 2015.2
*/
const onRequest = (scriptContext) => {
try {
if (scriptContext.request.method === 'GET') {
let depRecID = scriptContext.request.parameters.recId
let depositType = scriptContext.request.parameters.recType;
// log.debug('Deposit record Id', depRecID, 'type is', depositType);
let currentDeposit = record.load({
type: depositType,
id: depRecID,
isDynamic: true,
});
// log.debug('Loaded record', currentDeposit);
let lineCount = currentDeposit.getLineCount({ sublistId: 'payment' });
let lineCountOther = currentDeposit.getLineCount({ sublistId: 'other' });
let lineCountCash = currentDeposit.getLineCount({ sublistId: 'cashback' });
if (lineCount > 0 || lineCountOther > 0 || lineCountCash > 0) {
let generatedContents = generateContents(depRecID, depositType);
log.debug('the lines are', generatedContents.lines);
if (!generatedContents.lines[0]) {
log.debug("lines Doesn't exists");
scriptContext.response.write('Invalid request method');
} else {
let generatedPdf = generatePrint(118, depRecID, generatedContents); // 118 is the internal ID of the PDF Template.
scriptContext.response.writeFile({ file: generatedPdf, isInline: true });
}
} else {
scriptContext.response.write('Invalid request method');
}
}
} catch (e) {
log.error({ title: "error@onRequest", details: e });
}
}
/**
* Generate contents by loading the deposit record and its sublist data.
* @param {number} internalId - The internal ID of the deposit record.
* @param {string} depositType - The record type of the deposit.
* @returns {Array} - Array of objects containing deposit details and line data.
*/
function generateContents(internalId, depositType) {
try {
let currentDeposit = record.load({
type: depositType,
id: internalId,
isDynamic: true,
});
log.debug('Loaded record', currentDeposit);
let lineCountPayment = currentDeposit.getLineCount({ sublistId: 'payment' });
let lineCountOther = currentDeposit.getLineCount({ sublistId: 'other' });
// Create an object to hold the deposit header information and an array for line items
let contents = {
trandate: escapeSpecialChar(currentDeposit.getText({ fieldId: 'trandate' })),
account: escapeSpecialChar(currentDeposit.getText({ fieldId: 'account' })),
totalAmount: formatNumberIndianStyle(currentDeposit.getValue({ fieldId: 'total' })),
tranid: escapeSpecialChar(currentDeposit.getValue({ fieldId: 'transactionnumber' })),
lines: [], // Array to hold line items
};
for (let i = 0; i < lineCountPayment; i++) {
// for generating contets from 'Payments' sublist
let deposit = currentDeposit.getSublistValue({
sublistId: 'payment',
fieldId: "deposit",
line: i
})
log.debug('deposit value', deposit)
if (deposit == 'T' || deposit == true) {
let lineDetails = {
customer: escapeSpecialChar(currentDeposit.getSublistText({
sublistId: 'payment',
fieldId: "entity",
line: i
})),
amount: formatNumberIndianStyle(currentDeposit.getSublistValue({
sublistId: 'payment',
fieldId: "paymentamount",
line: i
})),
method: escapeSpecialChar(currentDeposit.getSublistText({
sublistId: 'payment',
fieldId: "paymentmethod",
line: i
})),
memo: escapeSpecialChar(currentDeposit.getSublistValue({
sublistId: 'payment',
fieldId: "memo",
line: i
})),
checkNo: currentDeposit.getSublistValue({
sublistId: 'payment',
fieldId: "refnum",
line: i
})
};
contents.lines.push(lineDetails);
}
}
log.debug('Content generated is', contents);
// for generating contets from 'others' sublist
for (let i = 0; i < lineCountOther; i++) {
let lineDetailsOther = {
customer: escapeSpecialChar(currentDeposit.getSublistText({
sublistId: 'other',
fieldId: "entity",
line: i
})),
amount: formatNumberIndianStyle(currentDeposit.getSublistValue({
sublistId: 'other',
fieldId: "amount",
line: i
})),
method: escapeSpecialChar(currentDeposit.getSublistText({
sublistId: 'other',
fieldId: "paymentmethod",
line: i
})),
memo: escapeSpecialChar(currentDeposit.getSublistValue({
sublistId: 'other',
fieldId: "memo",
line: i
})),
checkNo: currentDeposit.getSublistValue({
sublistId: 'other',
fieldId: "refnum",
line: i
})
};
contents.lines.push(lineDetailsOther);
}
return contents;
} catch (e) {
log.error('Error in generateContents', e);
}
}
/**
* Generates and renders the PDF with the provided template and content.
* @param {number} tempId - The internal ID of the PDF template file.
* @param {number} cpRecID - The internal ID of the deposit record.
* @param {Object} generateContents - The content object generated from the record.
* @returns {file.File} - The generated PDF file.
*/
function generatePrint(tempId, cpRecID, generateContents) {
try {
let renderer = render.create();
let templateFile = file.load({ id: tempId });
log.debug('Loaded template', templateFile);
renderer.setTemplateById(tempId);
// Add the entire contents array as a custom data source
renderer.addCustomDataSource({
format: render.DataSource.OBJECT,
alias: "contents",
data: { contents: generateContents }
});
let pdfFile = renderer.renderAsPdf();
log.debug('Generated PDF Output', pdfFile);
return pdfFile;
} catch (e) {
log.error('Error in generatePrint function', e);
}
}
/**
* Escapes special characters to avoid breaking the template rendering.
* @param {string} textvalue - The string to escape special characters.
* @returns {string} - The escaped string.
*/
function escapeSpecialChar(textvalue) {
try {
if (textvalue) {
textvalue = textvalue.replace(/&/g, '&');
textvalue = textvalue.replace(/</g, '<');
textvalue = textvalue.replace(/>/g, '>');
textvalue = textvalue.replace(/"/g, '"');
textvalue = textvalue.replace(/'/g, ''');
textvalue = textvalue.replace(/u00A0/g, ' '); // Non-breaking space
textvalue = textvalue.replace(/$/g, ""); // Remove dollar sign
}
return textvalue;
} catch (e) {
log.error('error in escape special Char ', e);
return null;
}
}
/**
* For Formating the number for amounts
* @param {*} value
* @returns
*/
function formatNumberIndianStyle(value) {
try {
if (value !== null && value !== undefined) {
let formattedValue = format1.format({
value: value,
type: format1.Type.CURRENCY // This will format it with commas and two decimals
});
// If you need a specific custom logic for Indian formatting, you can apply it here
formattedValue = formattedValue.replace(/(d)(?=(dd)+db)/g, "$1,");
return formattedValue;
}
return value;
} catch (e) {
log.debug('error in formating', e)
}
}
return { onRequest };
}
);
Template Code:
<?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="footer">
<table class="footer" style="width: 100%;"><tr>
<td align="center" font-size="11px">Page <pagenumber/> </td>
</tr></table>
</macro>
</macrolist>
<style>
* {
<#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>
}
{
font-family: TimesNewRoman, serif;
font-size: 11x;
}
table {
font-size: 11px;
table-layout: fixed;
}
td {
padding: 8px 3px 6px;
}
span.title {
font-size: 16px;
}
span.number {
font-size: 11px;
}
table.itemtable th {
padding-bottom: 12px;
padding-top: 8px;
}
table.footer td {
padding: 0;
font-size: 8px;
}
td.empty {
border: none;
background-color: #e3e3e3;
}
th {
font-weight: bold;
vertical-align: middle;
padding: 5px 6px 3px;
background-color: #e3e3e3;
color: #333333;
}
tr.totalrow {
background-color: #e3e3e3;
line-height: 200%;
}
td p { align:left }
</style>
</head>
<body footer="footer" footer-height="0.25in" padding="0.5in 0.5in 0.5in 0.5in" size="Letter">
<table class="header" style="width: 100%; height: 23.5833px;"><tr style="height: 23.5833px;">
<td style="height: 23.5833px;" colspan="7"> </td>
<td style="height: 23.5833px;" colspan="4" align="center"><span class="title">Deposit Summary </span></td>
<td style="height: 23.5833px;" colspan="4" align="right" font-size="11px">${.now?string('MM/dd/yyyy')} ${.now?string("hh:mm a")}</td>
</tr></table>
<table class="header" style="width: 100%;"><tr style="height: 22px;">
<td style="height: 22px;" align="center" font-size="11px"><span class="number"> CBRC LLC</span></td>
</tr>
<tr>
<td align="center">Summary of Deposits to ${contents.contents.account} on ${contents.contents.trandate}</td>
</tr></table>
<table class="itemtable" table-layout= "fixed" style="width: 100%; margin-top: 10px;">
<thead font-size="11px">
<tr border-bottom="2px solid" >
<th colspan="2" align="left">&nsbp;<strong>Check No</strong></th>
<th colspan="2" align="left">&nsbp;<strong>Pmt Method</strong></th>
<th colspan="4" align="left">&nsbp;<strong>Received From</strong></th>
<th colspan="4" align="left"><strong>Memo</strong></th>
<th colspan="2" align="right"><strong>Amount</strong></th>
</tr>
</thead>
<#list contents.contents.lines as item><tr>
<td font-size="11px" colspan="2" align="left">${item.checkNo}</td>
<td font-size="11px" colspan="2" align="left">${item.method}</td>
<td font-size="11px" colspan="4" align="left">${item.customer}</td>
<td font-size="11px" colspan="4" align="left">${item.memo}</td>
<td font-size="11px" colspan="2" align="right">${item.amount}</td>
</tr>
</#list>
<tr>
<td> </td>
</tr></table>
<table style="height: 50px;" width="100%"><tr class="totalrow" style="height: 48px;">
<td style="height: 50px;" colspan="10" v-align="bottom" align="left" font-size="11px"><strong>Deposit Total</strong></td>
<td style="height: 50px;" colspan="2"> </td>
<td style="height: 50px;" colspan="2" v-align="bottom" align="right" font-size="11px"><b>${contents.contents.totalAmount}</b></td>
</tr></table>
</body>
</pdf>