Hello everyone, I have created a custom Suitelet w...
# suitescript
v
Hello everyone, I have created a custom Suitelet where users can select a Sales Order and submit it to generate a PDF using an advanced PDF template. However, when executing the script, I encounter an “Invalid XML” error. If there are two Sales Orders, I want both in a single PDF, with the first Sales Order on the first page and the second Sales Order on the second page
Copy code
/**
 * @NApiVersion 2.1
 * @NScriptType Suitelet
 */
define(['N/render', 'N/record', 'N/file', 'N/log'], function (render, record, file, log) {
    function onRequest(context) {
        if (context.request.method === 'GET') {
            var orderIds = JSON.parse(context.request.parameters.orders || '[]');
            if (!orderIds.length) {
                context.response.write('No orders selected.');
                return;
            }
            log.debug('Order IDs', orderIds);
            var xmlContent = '<?xml version="1.0″ encoding="UTF-8″?>\n<pdfset>';
            orderIds.forEach(function (soId) {
                try {
                    var salesOrder = record.load({ type: record.Type.SALES_ORDER, id: soId });
                    var singleRenderer = render.create();
                    singleRenderer.setTemplateById(102); // Update with your actual PDF template ID
                    singleRenderer.addRecord('record', salesOrder);
                    var pdfBody = singleRenderer.renderAsString();
                    pdfBody = cleanXml(pdfBody);
                    xmlContent += `<pdf>${pdfBody}</pdf>\n`;
                } catch (e) {
                    log.error('Error loading Sales Order or rendering PDF', e);
                }
            });
            xmlContent += `</pdfset>`;
 log.debug('Final XML Content', xmlContent);
            try {
                // Ensure the XML content is valid before converting to PDF
                if (!isValidXml(xmlContent)) {
                    context.response.write('Error: Invalid XML structure. Check logs for details.');
                    log.error('Invalid XML structure', xmlContent);
                    return;
                }
                var finalPdf = render.xmlToPdf({ xmlString: xmlContent });
                saveAndServePdf(finalPdf, context);
            } catch (e) {
                log.error('Error rendering final PDF', e);
                context.response.write('Error generating PDF: ' + e.message);
            }
        }
    }
    function cleanXml(xmlString) {
        return xmlString
            .replace(/<\?xml.*?\?>/g, ") // Remove XML declaration inside content
            .replace(/<!DOCTYPE[^>]*>/g, ") // Remove DOCTYPE declaration
            .replace(/[\u0000-\u001F\u007F-\u009F]/g, ") // Remove control characters
            .replace(/&(?!amp;|lt;|gt;|quot;|apos;)/g, '&') // Escape unencoded ampersands
            .replace(/>/g, '>') // Escape greater than symbol
            .replace(/</g, '<'); // Escape less than symbol
    }
    function isValidXml(xmlString) {
        try {
            var parser = new DOMParser();
            var xmlDoc = parser.parseFromString(xmlString, 'application/xml');
            return !xmlDoc.querySelector('parsererror'); // Returns true if XML is valid
        } catch (e) {
            return false;
        }
    }
    function saveAndServePdf(finalPdf, context) {
        var mergedPdfFile = file.create({
            name: 'Merged_SalesOrders.pdf',
            fileType: file.Type.PDF,
            contents: finalPdf.getContents(),
            folder: 2552, // Update with your actual folder ID
            isOnline: true
        });
        var mergedFileId = mergedPdfFile.save();
        var fileRecord = file.load({ id: mergedFileId });
        context.response.write(`<script>window.open('${fileRecord.url}', '_blank');</script>`);
    }
    return { onRequest: onRequest };
});
m
It's going to be much more maintainable to create the data as an object or JSON and then feed it into a separate XML template using TemplateRenderer.addCustomDataSource. If you want them all part of the same page then take the
<pdf>
tags out of that XML document. Then you render those as strings, concatenate them together and then you can render them as a PDF using
Copy code
render.xmlToPdf({
            xmlString: `<?xml version="1.0"?><!DOCTYPE pdf PUBLIC "-//big.faceless.org//report" "report-1.1.dtd"><pdf>${pdf_contents}</pdf>`
        });
That should be much easier to read rather than trying to do a bunch of xml in code.
e
Yes to the
TemplateRenderer
approach. For combining PDFs, you can alternatively take advantage of BFO's
pdfset
tag
v
Thanks its working