Hello everyone, I have created a custom Suitelet w...
# advancedpdf
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 };
});