import { BadRequestException, InternalServerErrorException, Logger, } from '@nestjs/common'; import * as oracledb from 'oracledb'; const PDFDocument = require('pdfkit'); const fs = require('fs'); const path = require('path'); const PDFMerger = require('pdf-merger-js').default; import * as fsPromises from 'fs/promises'; import { BadRequestException as BR } from 'src/exceptions/badRequest.exception'; import { existsSync, readFileSync } from 'fs'; import { PDFDocument as pdfl, TextAlignment, PDFTextField, PDFCheckBox, PDFRadioGroup, PDFDropdown, PDFOptionList } from 'pdf-lib'; import { PrintCarnetDTO, PrintGLDTO } from 'src/dto/property.dto'; const logger = new Logger('Helper'); export const handleError = (error: any, context: string = 'UnknownService'): never => { if (error instanceof BadRequestException || error instanceof BR) { logger.warn(`[${context}] ${error.message}`); throw error; } if (error.message?.includes('NJS-107')) { logger.warn(`[${context}] Invalid cursor encountered: ${error.message}`); throw new BadRequestException('Invalid database response.'); } if (error.message?.includes('ORA-')) { logger.error(`[${context}] Oracle error occurred: ${error.message}`); throw new InternalServerErrorException(error.message); } logger.error(`[${context}] Unexpected error:`, error.stack || error); throw new InternalServerErrorException(error.message || 'Internal error'); }; export const closeOracleDbConnection = async (connection: any, context: string = 'UnknownService'): Promise => { if (connection) { try { await connection.close(); } catch (closeErr) { logger.error(`[${context}] Failed to close DB connection`, closeErr.stack || closeErr); } } }; export const fetchCursor = async (cursor: oracledb.ResultSet, context: string = 'UnknownService'): Promise => { try { const rows = await cursor.getRows(); await cursor.close(); return rows; } catch (err) { logger.error(`[${context}] Failed to fetch from cursor`, err.stack || err); throw new InternalServerErrorException('Error reading data from database.'); } }; export const setEmptyStringsToNull = (obj: any): void => { Object.keys(obj).forEach((key) => { if (typeof obj[key] === 'object' && obj[key] !== null) { setEmptyStringsToNull(obj[key]); } else if (obj[key] === '') { obj[key] = null; } }); } export interface pdfGLData { itemNo: number; description: string; noOfPieces: number; weight: string; itemValue: number; countryOfOrigin: string } export interface pdfCarnetData { carnetNo: string; ISSUEDBY: string; ISSUEDATE: string; EXPDATE: string; AUTHREP: string; NOOFUSSETS: number; NOOFFOREIGNSETS: number; NOOFTRANSITSETS: number; PRINTGOODSTOBEEXPORTED: string; HOLDERDATATOPRINT: string; CONTINUATIONSHEETS: number; NEXTUSCFNO: number; NEXTFOREIGNCFNO: number; NEXTTRANSITCFNO: number; contPageNo: number; // for GL table only } function formatNumber(num) { if (isNaN(num)) return 'N/A'; return num.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); } function toUSDateTime(isoString) { const date = new Date(isoString); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); const year = date.getFullYear(); let hours = date.getHours(); const minutes = String(date.getMinutes()).padStart(2, '0'); const seconds = String(date.getSeconds()).padStart(2, '0'); const ampm = hours >= 12 ? 'PM' : 'AM'; hours = hours % 12 || 12; // return `${month}-${day}-${year}, ${hours}:${minutes}:${seconds} ${ampm}`; return `${year}-${month}-${day}`; } export function replaceSlashWithDash(input) { return input.replace(/\//g, '-'); } const PDF_LOCAL_PATH: string = "../../../public/carnet-pdf"; const PDF_SERVER_PATH: string = "../../public/carnet-pdf"; const PDF_URL = PDF_SERVER_PATH export const generateGreenCoverPDF = (carnetData: pdfCarnetData) => { return new Promise(async (resolve, reject) => { try { const templatePath = path.join(__dirname, PDF_URL, '1GreenCover.pdf'); const existingPdfBytes = fs.readFileSync(templatePath); const pdfDoc = await pdfl.load(existingPdfBytes); const form = pdfDoc.getForm(); const CURRENT_FONT = 11; const fields = form.getFields(); const fieldInfo = fields.map(f => { let type = 'unknown'; if (f instanceof PDFTextField) type = 'text'; else if (f instanceof PDFCheckBox) type = 'checkbox'; else if (f instanceof PDFRadioGroup) type = 'radio'; else if (f instanceof PDFDropdown) type = 'dropdown'; else if (f instanceof PDFOptionList) type = 'option-list'; return { name: f.getName(), type }; }); fieldInfo.forEach(x => { if (x.type === "text" && x.name === 'CarnetNo') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(carnetData.carnetNo); } else if (x.type === "text" && x.name === 'AuthBy') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(carnetData.AUTHREP); } else if (x.type === "text" && x.name === 'ContnSheetNo') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(carnetData.CONTINUATIONSHEETS + ""); } else if (x.type === "text" && x.name === 'GoodstobeExportedas') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(carnetData.PRINTGOODSTOBEEXPORTED); } else if (x.type === "text" && x.name === 'IssuedOn') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); const formattedDate = toUSDateTime(carnetData.ISSUEDATE) nameField.setText(formattedDate); } else if (x.type === "text" && x.name === 'IssuedBy') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(carnetData.ISSUEDBY); } else if (x.type === "text" && x.name === 'ValidUntil') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); const formattedDate = toUSDateTime(carnetData.EXPDATE) nameField.setText(formattedDate); } else if (x.type === "text" && x.name === 'HolderNameAddress') { const nameField = form.getTextField(x.name); nameField.enableMultiline(); // nameField.setFontSize(CURRENT_FONT); const multilineAddress = carnetData.HOLDERDATATOPRINT.replace(/~/g, "\n"); nameField.setText(multilineAddress); } }); form.flatten(); const filledPdfBytes = await pdfDoc.save(); const outputPath = path.join(__dirname, PDF_URL, `${replaceSlashWithDash(carnetData.carnetNo)}p1.pdf`); const writeStream = fs.createWriteStream(outputPath); writeStream.write(filledPdfBytes); writeStream.end(); writeStream.on('finish', () => { // console.log("PDF has been successfully created and saved."); resolve("p1 done"); // wait until file fully written }); writeStream.on('error', err => { console.error("Error writing PDF to file:", err); reject(err); }); } catch (err) { reject(err); } }); }; export const generateUSCounterFoilFront = (carnetData: pdfCarnetData, t1: string, t2: string, dn: number, pn: number): Promise => { return new Promise(async (resolve, reject) => { try { const templatePath = path.join(__dirname, PDF_URL, '3USCounterFoilFront.pdf'); const existingPdfBytes = fs.readFileSync(templatePath); const pdfDoc = await pdfl.load(existingPdfBytes); const form = pdfDoc.getForm(); const CURRENT_FONT = 11; const fields = form.getFields(); const fieldInfo = fields.map(f => { let type = 'unknown'; if (f instanceof PDFTextField) type = 'text'; else if (f instanceof PDFCheckBox) type = 'checkbox'; else if (f instanceof PDFRadioGroup) type = 'radio'; else if (f instanceof PDFDropdown) type = 'dropdown'; else if (f instanceof PDFOptionList) type = 'option-list'; return { name: f.getName(), type }; }); // console.log(fieldInfo); fieldInfo.forEach(x => { if (x.type === "text" && x.name === 'Document No1') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); if (t1 === "VALUE") { nameField.setText(dn + ""); dn++; } else { nameField.setText("VOID"); } } else if (x.type === "text" && x.name === 'CarnetNo') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(carnetData.carnetNo); } else if (x.type === "text" && x.name === 'Document No2') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); if (t2 === "VALUE") { nameField.setText(dn + ""); dn++; } else { nameField.setText("VOID"); } } else if (x.type === "text" && x.name === 'PrintVoidText1') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); if (t1 === "VOID") { nameField.setAlignment(TextAlignment.Center); nameField.setText(t1); } } else if (x.type === "text" && x.name === 'PrintVoidText2') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); if (t2 === "VOID") { nameField.setAlignment(TextAlignment.Center); nameField.setText(t2); } } }); form.flatten(); const filledPdfBytes = await pdfDoc.save(); const outputPath = path.join(__dirname, PDF_URL, `${replaceSlashWithDash(carnetData.carnetNo)}${pn}USFront.pdf`); const writeStream = fs.createWriteStream(outputPath); writeStream.write(filledPdfBytes); writeStream.end(); pn++; writeStream.on('finish', () => { // console.log(`${dn}USFront.pdf has been successfully created and saved.`); resolve(Number(dn)); // wait until file fully written }); writeStream.on('error', err => { console.error("Error writing PDF to file:", err); reject(err); }); } catch (err) { reject(err); } }); } export const generateUSCounterFoilBack = (carnetData: pdfCarnetData, t1: string, t2: string, dn: number, pn: number): Promise => { return new Promise(async (resolve, reject) => { try { const templatePath = path.join(__dirname, PDF_URL, '4USCounterFoilBack.pdf'); const existingPdfBytes = fs.readFileSync(templatePath); const pdfDoc = await pdfl.load(existingPdfBytes); const form = pdfDoc.getForm(); const CURRENT_FONT = 11; const fields = form.getFields(); const fieldInfo = fields.map(f => { let type = 'unknown'; if (f instanceof PDFTextField) type = 'text'; else if (f instanceof PDFCheckBox) type = 'checkbox'; else if (f instanceof PDFRadioGroup) type = 'radio'; else if (f instanceof PDFDropdown) type = 'dropdown'; else if (f instanceof PDFOptionList) type = 'option-list'; return { name: f.getName(), type }; }); fieldInfo.forEach(x => { if (x.type === "text" && x.name === 'Document No1') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); if (t1 === "VALUE") { nameField.setText(dn + ""); dn++; } else { nameField.setText("VOID"); } } else if (x.type === "text" && x.name === 'CarnetNo') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(carnetData.carnetNo); } else if (x.type === "text" && x.name === 'Document No2') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); if (t2 === "VALUE") { nameField.setText(dn + ""); dn++; } else { nameField.setText("VOID"); } } else if (x.type === "text" && x.name === 'PrintVoidText1') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); if (t1 === "VOID") { nameField.setAlignment(TextAlignment.Center); nameField.setText(t1); } } else if (x.type === "text" && x.name === 'PrintVoidText2') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); if (t2 === "VOID") { nameField.setAlignment(TextAlignment.Center); nameField.setText(t2); } } }); form.flatten(); const filledPdfBytes = await pdfDoc.save(); const outputPath = path.join(__dirname, PDF_URL, `${replaceSlashWithDash(carnetData.carnetNo)}${pn}USBack.pdf`); const writeStream = fs.createWriteStream(outputPath); writeStream.write(filledPdfBytes); writeStream.end(); pn++ writeStream.on('finish', () => { // console.log(`${pn}USBack.pdf` + " has been successfully created and saved."); resolve(Number(dn)); // wait until file fully written }); writeStream.on('error', err => { console.error("Error writing PDF to file:", err); reject(err); }); } catch (err) { reject(err); } }); } export const generateFNCounterFoilFront = (carnetData: pdfCarnetData, t1: string, t2: string, dn: number, pn: number): Promise => { return new Promise(async (resolve, reject) => { try { const templatePath = path.join(__dirname, PDF_URL, '6ForeignCounterfoilFront.pdf'); const existingPdfBytes = fs.readFileSync(templatePath); const pdfDoc = await pdfl.load(existingPdfBytes); const form = pdfDoc.getForm(); const CURRENT_FONT = 11; const fields = form.getFields(); const fieldInfo = fields.map(f => { let type = 'unknown'; if (f instanceof PDFTextField) type = 'text'; else if (f instanceof PDFCheckBox) type = 'checkbox'; else if (f instanceof PDFRadioGroup) type = 'radio'; else if (f instanceof PDFDropdown) type = 'dropdown'; else if (f instanceof PDFOptionList) type = 'option-list'; return { name: f.getName(), type }; }); // console.log(fieldInfo); fieldInfo.forEach(x => { if (x.type === "text" && x.name === 'DocumentNo1') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); if (t1 === "VALUE") { nameField.setText(dn + ""); dn++; } else { nameField.setText("VOID"); } } else if (x.type === "text" && x.name === 'CarnetNo') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(carnetData.carnetNo); } else if (x.type === "text" && x.name === 'DocumentNo2') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); if (t2 === "VALUE") { nameField.setText(dn + ""); dn++; } else { nameField.setText("VOID"); } } else if (x.type === "text" && x.name === 'PrintVoidText1') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); if (t1 === "VOID") { nameField.setAlignment(TextAlignment.Center); nameField.setText(t1); } } else if (x.type === "text" && x.name === 'PrintVoidText2') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); if (t2 === "VOID") { nameField.setAlignment(TextAlignment.Center); nameField.setText(t2); } } }); form.flatten(); const filledPdfBytes = await pdfDoc.save(); const outputPath = path.join(__dirname, PDF_URL, `${replaceSlashWithDash(carnetData.carnetNo)}${pn}FNFront.pdf`); const writeStream = fs.createWriteStream(outputPath); writeStream.write(filledPdfBytes); writeStream.end(); pn++; writeStream.on('finish', () => { // console.log(`${dn}FNFront.pdf has been successfully created and saved.`); resolve(Number(dn)); // wait until file fully written }); writeStream.on('error', err => { console.error("Error writing PDF to file:", err); reject(err); }); } catch (err) { reject(err); } }); } export const generateFNCounterFoilBack = (carnetData: pdfCarnetData, t1: string, t2: string, dn: number, pn: number): Promise => { return new Promise(async (resolve, reject) => { try { const templatePath = path.join(__dirname, PDF_URL, '7ForeignCounterFoilBack.pdf'); const existingPdfBytes = fs.readFileSync(templatePath); const pdfDoc = await pdfl.load(existingPdfBytes); const form = pdfDoc.getForm(); const CURRENT_FONT = 11; const fields = form.getFields(); const fieldInfo = fields.map(f => { let type = 'unknown'; if (f instanceof PDFTextField) type = 'text'; else if (f instanceof PDFCheckBox) type = 'checkbox'; else if (f instanceof PDFRadioGroup) type = 'radio'; else if (f instanceof PDFDropdown) type = 'dropdown'; else if (f instanceof PDFOptionList) type = 'option-list'; return { name: f.getName(), type }; }); fieldInfo.forEach(x => { if (x.type === "text" && x.name === 'DocumentNo1') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); if (t1 === "VALUE") { nameField.setText(dn + ""); dn++; } else { nameField.setText("VOID"); } } else if (x.type === "text" && x.name === 'CarnetNo') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(carnetData.carnetNo); } else if (x.type === "text" && x.name === 'DocumentNo2') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); if (t2 === "VALUE") { nameField.setText(dn + ""); dn++; } else { nameField.setText("VOID"); } } else if (x.type === "text" && x.name === 'PrintVoidText1') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); if (t1 === "VOID") { nameField.setAlignment(TextAlignment.Center); nameField.setText(t1); } } else if (x.type === "text" && x.name === 'PrintVoidText2') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); if (t2 === "VOID") { nameField.setAlignment(TextAlignment.Center); nameField.setText(t2); } } }); form.flatten(); const filledPdfBytes = await pdfDoc.save(); const outputPath = path.join(__dirname, PDF_URL, `${replaceSlashWithDash(carnetData.carnetNo)}${pn}FNBack.pdf`); const writeStream = fs.createWriteStream(outputPath); writeStream.write(filledPdfBytes); writeStream.end(); pn++ writeStream.on('finish', () => { // console.log(`${pn}FNBack.pdf` + " has been successfully created and saved."); resolve(Number(dn)); // wait until file fully written }); writeStream.on('error', err => { console.error("Error writing PDF to file:", err); reject(err); }); } catch (err) { reject(err); } }); } export const generateTSCounterFoilFront = (carnetData: pdfCarnetData, t1: string, t2: string, dn: number, pn: number): Promise => { return new Promise(async (resolve, reject) => { try { const templatePath = path.join(__dirname, PDF_URL, '8TransitCounterFoilFront.pdf'); const existingPdfBytes = fs.readFileSync(templatePath); const pdfDoc = await pdfl.load(existingPdfBytes); const form = pdfDoc.getForm(); const CURRENT_FONT = 11; const fields = form.getFields(); const fieldInfo = fields.map(f => { let type = 'unknown'; if (f instanceof PDFTextField) type = 'text'; else if (f instanceof PDFCheckBox) type = 'checkbox'; else if (f instanceof PDFRadioGroup) type = 'radio'; else if (f instanceof PDFDropdown) type = 'dropdown'; else if (f instanceof PDFOptionList) type = 'option-list'; return { name: f.getName(), type }; }); // console.log(fieldInfo); fieldInfo.forEach(x => { if (x.type === "text" && x.name === 'DocumentNo1') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); if (t1 === "VALUE") { nameField.setText(dn + ""); dn++; } else { nameField.setText("VOID"); } } else if (x.type === "text" && x.name === 'CarnetNo') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(carnetData.carnetNo); } else if (x.type === "text" && x.name === 'DocumentNo2') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); if (t2 === "VALUE") { nameField.setText(dn + ""); dn++; } else { nameField.setText("VOID"); } } else if (x.type === "text" && x.name === 'PrintVoidText1') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); if (t1 === "VOID") { nameField.setAlignment(TextAlignment.Center); nameField.setText(t1); } } else if (x.type === "text" && x.name === 'PrintVoidText2') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); if (t2 === "VOID") { nameField.setAlignment(TextAlignment.Center); nameField.setText(t2); } } }); form.flatten(); const filledPdfBytes = await pdfDoc.save(); const outputPath = path.join(__dirname, PDF_URL, `${replaceSlashWithDash(carnetData.carnetNo)}${pn}TSFront.pdf`); const writeStream = fs.createWriteStream(outputPath); writeStream.write(filledPdfBytes); writeStream.end(); pn++; writeStream.on('finish', () => { // console.log(`${dn}TSFront.pdf has been successfully created and saved.`); resolve(Number(dn)); // wait until file fully written }); writeStream.on('error', err => { console.error("Error writing PDF to file:", err); reject(err); }); } catch (err) { reject(err); } }); } export const generateTSCounterFoilBack = (carnetData: pdfCarnetData, t1: string, t2: string, dn: number, pn: number): Promise => { return new Promise(async (resolve, reject) => { try { const templatePath = path.join(__dirname, PDF_URL, '9TransitCounterfoilBack.pdf'); const existingPdfBytes = fs.readFileSync(templatePath); const pdfDoc = await pdfl.load(existingPdfBytes); const form = pdfDoc.getForm(); const CURRENT_FONT = 11; const fields = form.getFields(); const fieldInfo = fields.map(f => { let type = 'unknown'; if (f instanceof PDFTextField) type = 'text'; else if (f instanceof PDFCheckBox) type = 'checkbox'; else if (f instanceof PDFRadioGroup) type = 'radio'; else if (f instanceof PDFDropdown) type = 'dropdown'; else if (f instanceof PDFOptionList) type = 'option-list'; return { name: f.getName(), type }; }); fieldInfo.forEach(x => { if (x.type === "text" && x.name === 'DocumentNo1') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); if (t1 === "VALUE") { nameField.setText(dn + ""); dn++; } else { nameField.setText("VOID"); } } else if (x.type === "text" && x.name === 'CarnetNo') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(carnetData.carnetNo); } else if (x.type === "text" && x.name === 'DocumentNo2') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); if (t2 === "VALUE") { nameField.setText(dn + ""); dn++; } else { nameField.setText("VOID"); } } else if (x.type === "text" && x.name === 'PrintVoidText1') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); if (t1 === "VOID") { nameField.setAlignment(TextAlignment.Center); nameField.setText(t1); } } else if (x.type === "text" && x.name === 'PrintVoidText2') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); if (t1 === "VOID") { nameField.setAlignment(TextAlignment.Center); nameField.setText(t1); } } }); form.flatten(); const filledPdfBytes = await pdfDoc.save(); const outputPath = path.join(__dirname, PDF_URL, `${replaceSlashWithDash(carnetData.carnetNo)}${pn}TSBack.pdf`); const writeStream = fs.createWriteStream(outputPath); writeStream.write(filledPdfBytes); writeStream.end(); pn++ writeStream.on('finish', () => { // console.log(`${pn}TSBack.pdf` + " has been successfully created and saved."); resolve(Number(dn)); // wait until file fully written }); writeStream.on('error', err => { console.error("Error writing PDF to file:", err); reject(err); }); } catch (err) { reject(err); } }); } export const generateFNImportV = (carnetData: pdfCarnetData, dn: number, pn: number): Promise => { return new Promise(async (resolve, reject) => { try { const templatePath = path.join(__dirname, PDF_URL, '10ImportVoucher.pdf'); const existingPdfBytes = fs.readFileSync(templatePath); const pdfDoc = await pdfl.load(existingPdfBytes); const form = pdfDoc.getForm(); const CURRENT_FONT = 11; const fields = form.getFields(); const fieldInfo = fields.map(f => { let type = 'unknown'; if (f instanceof PDFTextField) type = 'text'; else if (f instanceof PDFCheckBox) type = 'checkbox'; else if (f instanceof PDFRadioGroup) type = 'radio'; else if (f instanceof PDFDropdown) type = 'dropdown'; else if (f instanceof PDFOptionList) type = 'option-list'; return { name: f.getName(), type }; }); // console.log(fieldInfo); fieldInfo.forEach(x => { if (x.type === "text" && x.name === 'Valid Until') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); const formattedDate = toUSDateTime(carnetData.EXPDATE) nameField.setText(formattedDate); } else if (x.type === "text" && x.name === 'CarnetNo') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(carnetData.carnetNo); } else if (x.type === "text" && x.name === 'HolderNameAddress') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); const multilineAddress = carnetData.HOLDERDATATOPRINT.replace(/~/g, "\n"); nameField.setText(multilineAddress); } else if (x.type === "text" && x.name === 'VoucherNo') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(dn + ""); dn++; } else if (x.type === "text" && x.name === 'AuthorizedBy') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(carnetData.AUTHREP); } else if (x.type === "text" && x.name === 'GoodsDescription') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(carnetData.PRINTGOODSTOBEEXPORTED); } else if (x.type === "text" && x.name === 'IssuedBy') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(carnetData.ISSUEDBY); } else if (x.type === "text" && x.name === 'Issued') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); const formattedDate = toUSDateTime(carnetData.ISSUEDATE) nameField.setText(formattedDate); } }); form.flatten(); const filledPdfBytes = await pdfDoc.save(); const outputPath = path.join(__dirname, PDF_URL, `${replaceSlashWithDash(carnetData.carnetNo)}${pn}FNImportV.pdf`); const writeStream = fs.createWriteStream(outputPath); writeStream.write(filledPdfBytes); writeStream.end(); pn++; writeStream.on('finish', () => { // console.log(`${dn}FNImportV.pdf has been successfully created and saved.`); resolve(Number(dn)); // wait until file fully written }); writeStream.on('error', err => { console.error("Error writing PDF to file:", err); reject(err); }); } catch (err) { reject(err); } }); } export const generateFNExportV = (carnetData: pdfCarnetData, dn: number, pn: number): Promise => { return new Promise(async (resolve, reject) => { try { const templatePath = path.join(__dirname, PDF_URL, '11ExportVoucher.pdf'); const existingPdfBytes = fs.readFileSync(templatePath); const pdfDoc = await pdfl.load(existingPdfBytes); const form = pdfDoc.getForm(); const CURRENT_FONT = 11; const fields = form.getFields(); const fieldInfo = fields.map(f => { let type = 'unknown'; if (f instanceof PDFTextField) type = 'text'; else if (f instanceof PDFCheckBox) type = 'checkbox'; else if (f instanceof PDFRadioGroup) type = 'radio'; else if (f instanceof PDFDropdown) type = 'dropdown'; else if (f instanceof PDFOptionList) type = 'option-list'; return { name: f.getName(), type }; }); // console.log(fieldInfo); fieldInfo.forEach(x => { if (x.type === "text" && x.name === 'Valid Until') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); const formattedDate = toUSDateTime(carnetData.EXPDATE) nameField.setText(formattedDate); } else if (x.type === "text" && x.name === 'CarnetNo') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(carnetData.carnetNo); } else if (x.type === "text" && x.name === 'HolderNameAddress') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); const multilineAddress = carnetData.HOLDERDATATOPRINT.replace(/~/g, "\n"); nameField.setText(multilineAddress); } else if (x.type === "text" && x.name === 'VoucherNo') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(dn + ""); dn++; } else if (x.type === "text" && x.name === 'AuthorizedBy') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(carnetData.AUTHREP); } else if (x.type === "text" && x.name === 'GoodsDescription') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(carnetData.PRINTGOODSTOBEEXPORTED); } else if (x.type === "text" && x.name === 'IssuedBy') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(carnetData.ISSUEDBY); } else if (x.type === "text" && x.name === 'Issued') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); const formattedDate = toUSDateTime(carnetData.ISSUEDATE) nameField.setText(formattedDate); } }); form.flatten(); const filledPdfBytes = await pdfDoc.save(); const outputPath = path.join(__dirname, PDF_URL, `${replaceSlashWithDash(carnetData.carnetNo)}${pn}FNExportV.pdf`); const writeStream = fs.createWriteStream(outputPath); writeStream.write(filledPdfBytes); writeStream.end(); pn++; writeStream.on('finish', () => { // console.log(`${dn}FNExportV.pdf has been successfully created and saved.`); resolve(Number(dn)); // wait until file fully written }); writeStream.on('error', err => { console.error("Error writing PDF to file:", err); reject(err); }); } catch (err) { reject(err); } }); } export const generateTNV = (carnetData: pdfCarnetData, dn: number, pn: number): Promise => { return new Promise(async (resolve, reject) => { try { const templatePath = path.join(__dirname, PDF_URL, '12TransitVoucher.pdf'); const existingPdfBytes = fs.readFileSync(templatePath); const pdfDoc = await pdfl.load(existingPdfBytes); const form = pdfDoc.getForm(); const CURRENT_FONT = 11; const fields = form.getFields(); const fieldInfo = fields.map(f => { let type = 'unknown'; if (f instanceof PDFTextField) type = 'text'; else if (f instanceof PDFCheckBox) type = 'checkbox'; else if (f instanceof PDFRadioGroup) type = 'radio'; else if (f instanceof PDFDropdown) type = 'dropdown'; else if (f instanceof PDFOptionList) type = 'option-list'; return { name: f.getName(), type }; }); // console.log(fieldInfo); fieldInfo.forEach(x => { if (x.type === "text" && x.name === 'Valid Until') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); const formattedDate = toUSDateTime(carnetData.EXPDATE) nameField.setText(formattedDate); } else if (x.type === "text" && x.name === 'CarnetNo') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(carnetData.carnetNo); } else if (x.type === "text" && x.name === 'HolderNameAddress') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); const multilineAddress = carnetData.HOLDERDATATOPRINT.replace(/~/g, "\n"); nameField.setText(multilineAddress); } else if (x.type === "text" && x.name === 'VoucherNo') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(dn + ""); dn++; } else if (x.type === "text" && x.name === 'AuthorizedBy') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(carnetData.AUTHREP); } else if (x.type === "text" && x.name === 'GoodsDescription') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(carnetData.PRINTGOODSTOBEEXPORTED); } else if (x.type === "text" && x.name === 'IssuedBy') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(carnetData.ISSUEDBY); } else if (x.type === "text" && x.name === 'Issued') { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); const formattedDate = toUSDateTime(carnetData.ISSUEDATE) nameField.setText(formattedDate); } }); form.flatten(); const filledPdfBytes = await pdfDoc.save(); const outputPath = path.join(__dirname, PDF_URL, `${replaceSlashWithDash(carnetData.carnetNo)}${pn}TNV.pdf`); const writeStream = fs.createWriteStream(outputPath); writeStream.write(filledPdfBytes); writeStream.end(); pn++; writeStream.on('finish', () => { // console.log(`${dn}TNV.pdf has been successfully created and saved.`); resolve(Number(dn)); // wait until file fully written }); writeStream.on('error', err => { console.error("Error writing PDF to file:", err); reject(err); }); } catch (err) { reject(err); } }); } function getPatternArray(n) { if (n < 1) { return []; } const numFours = Math.floor((n - 1) / 4); // Create an array with the leading 4s const result = Array(numFours).fill(4); // Calculate and add the final number const lastNumber = (n - 1) % 4 + 1; result.push(lastNumber); return result; } export function generateGL1(carnetData: pdfCarnetData): Promise { return new Promise(async (resolve, reject) => { try { const templatePath = path.join(__dirname, PDF_URL, '2GeneralListSeeattached.pdf'); const existingPdfBytes = fs.readFileSync(templatePath); const outputPath = path.join(__dirname, PDF_URL, `${replaceSlashWithDash(carnetData.carnetNo)}p2.pdf`); const writeStream = fs.createWriteStream(outputPath); writeStream.write(existingPdfBytes); writeStream.end(); writeStream.on('finish', () => { // console.log("PDF has been successfully created and saved."); resolve("p2 done"); // wait until file fully written }); writeStream.on('error', err => { console.error("Error writing PDF to file:", err); reject(err); }); } catch (err) { reject(err); } }); } export async function processGlDataInBatches( glDataArray: pdfGLData[], batchSize: number, carnetData: pdfCarnetData, // carnetData is a parameter here body: PrintGLDTO, batchCallback: (batch: pdfGLData[], batchNumber: number, startIndex: number, endIndex: number) => Promise ): Promise { if (!Array.isArray(glDataArray)) { throw new Error("Input 'glDataArray' must be an array."); } const totalLength: number = glDataArray.length; console.log(`✨ [Batch Processor] Starting batch processing for ${totalLength} items.`); console.log(`đŸ“Ļ [Batch Processor] Batch size set to ${batchSize} items.`); if (totalLength === 0) { console.log("â„šī¸ [Batch Processor] Array is empty. No batches to process."); return 0; } let batchNumber: number = 1; for (let i: number = 0; i < totalLength; i += batchSize) { const startIndex: number = i; const endIndex: number = Math.min(i + batchSize, totalLength); // console.log("item i : ", i); // Extract the current batch using slice, creating a new array for the batch const currentBatch: pdfGLData[] = glDataArray.slice(startIndex, endIndex); // Log the particular batch data here // console.log(`🔍 [Batch Processor] Details for Batch ${batchNumber} (Items ${startIndex} to ${endIndex - 1}):`, JSON.stringify(currentBatch, null, 2)); // Define pdfGeneration function *inside* the loop for each iteration // Modified to accept and log batchNumber for clearer output async function pdfGeneration(data: pdfCarnetData, currentBatchNumber: number) { // console.log(`📄 [PDF Generation] Calling PDF generation for Batch ${currentBatchNumber}. Carnet Data:`, JSON.stringify(data, null, 2)); // Added a small delay to ensure logs are flushed // await new Promise(resolve => setTimeout(resolve, 50)); // return; function transformGlDataForDisplay(glDataArray: any[]): any[] { if (!Array.isArray(glDataArray)) { console.error("Input for transformGlDataForDisplay must be an array."); return []; } const transformedData: any[] = []; glDataArray.forEach((item, index) => { const rowNumber = index + 1; // 1-based index for "RowX" transformedData.push( { name: `Item NoRow${rowNumber}`, value: item.itemNo, type: "text" }, { name: `DescriptionRow${rowNumber}`, value: item.description, type: "text" }, { name: `No of piecesRow${rowNumber}`, value: item.noOfPieces, type: "text" }, { name: `WeightRow${rowNumber}`, value: item.weight, type: "text" }, { name: `Item ValueRow${rowNumber}`, value: item.itemValue, type: "text" }, { name: `Origin CountryRow${rowNumber}`, value: item.countryOfOrigin, type: "text" } ); }); return transformedData; } function calculateTotalItemValue(glDataArray: any[]): number { if (!Array.isArray(glDataArray)) { console.error("Input for calculateTotalItemValue must be an array."); return 0; } let totalItemValue = 0; for (const item of glDataArray) { if (typeof item.itemValue === 'number') { totalItemValue += item.itemValue; } else { console.warn(`Non-numeric itemValue encountered for itemNo: ${item.itemNo}. Skipping value.`); } } let value = formatNumber(totalItemValue) // console.log(`total for batch ${batchNumber} : `, value); return value; } return new Promise(async (resolve, reject) => { try { const templatePath = path.join(__dirname, PDF_URL, '2GeneralList.pdf'); const existingPdfBytes = fs.readFileSync(templatePath); const pdfDoc = await pdfl.load(existingPdfBytes); const form = pdfDoc.getForm(); const CURRENT_FONT = 11; const fields = form.getFields(); const fieldInfo = fields.map(f => { let type = 'unknown'; if (f instanceof PDFTextField) type = 'text'; else if (f instanceof PDFCheckBox) type = 'checkbox'; else if (f instanceof PDFRadioGroup) type = 'radio'; else if (f instanceof PDFDropdown) type = 'dropdown'; else if (f instanceof PDFOptionList) type = 'option-list'; return { name: f.getName(), type }; }); // console.log(fieldInfo); const fdata = transformGlDataForDisplay(currentBatch); fdata.forEach(x => { // if (x.type === "text" && x.name === 'CarnetNo') { // const nameField = form.getTextField(x.name); // // nameField.setFontSize(CURRENT_FONT); // nameField.setText(carnetData.carnetNo); // } if (/^Item ValueRow\d+$/.test(x.name)) { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); let ThousandSeparatorValue = formatNumber(x.value); nameField.setAlignment(TextAlignment.Right); nameField.setText(ThousandSeparatorValue + ""); } else { const nameField = form.getTextField(x.name); // nameField.setFontSize(CURRENT_FONT); nameField.setText(x.value + ""); } }); const TotPageItemValue = form.getTextField('TotPageItemValue'); TotPageItemValue.setFontSize(CURRENT_FONT); let totalValue = calculateTotalItemValue(currentBatch); TotPageItemValue.setAlignment(TextAlignment.Right); TotPageItemValue.setText(totalValue + ""); const carnetNoField = form.getTextField('Carnet No'); carnetNoField.setFontSize(CURRENT_FONT); carnetNoField.setText(data.carnetNo + ""); const contnSheetNoField = form.getTextField('ContnSheet No'); contnSheetNoField.setFontSize(CURRENT_FONT); contnSheetNoField.setText((currentBatchNumber - 1) + ""); form.flatten(); const filledPdfBytes = await pdfDoc.save(); const outputPath = path.join(__dirname, PDF_URL, `${body.P_HEADERID}p2gl${currentBatchNumber}.pdf`); const writeStream = fs.createWriteStream(outputPath); writeStream.write(filledPdfBytes); writeStream.end(); writeStream.on('finish', () => { // console.log("PDF has been successfully created and saved."); resolve("p1 done"); // wait until file fully written }); writeStream.on('error', err => { console.error("Error writing PDF to file:", err); reject(err); }); } catch (err) { reject(err); } }); } // Call pdfGeneration for *each* batch, passing the carnetData and current batch number await pdfGeneration(carnetData, batchNumber); // Await the processing of the current batch to ensure sequential execution. await batchCallback(currentBatch, batchNumber, startIndex, endIndex); batchNumber++; // Increment batch number for the next iteration } console.log(`✅ [Batch Processor] Batch processing complete. Total batches processed: ${batchNumber - 1}`); return batchNumber } export async function generateGL(glData: pdfGLData[], carnetData: pdfCarnetData, body: PrintGLDTO): Promise { const BATCH_SIZE = 38; const totalLength = glData.length; console.log(`--- Initiating generateGL for ${totalLength} items ---`); // The inner function that logs the received array length // This is passed as the batchCallback to processGlDataInBatches const innerLogFunction = async (batch: pdfGLData[], batchNumber: number) => { console.log(`Received batch ${batchNumber}. Array length: ${batch.length}`); // Added a small delay to ensure logs are flushed await new Promise(resolve => setTimeout(resolve, 50)); }; // Call the existing processGlDataInBatches function and await its completion try { const loop = await processGlDataInBatches(glData, BATCH_SIZE, carnetData, body, innerLogFunction); console.log("--- generateGL function completed ---"); let fileArray: string[] = [] for (let i = 1; i < loop; i++) { fileArray.push(`${body.P_HEADERID}p2gl${i}.pdf`) } return { fileArray, batch: loop - 1 }; } catch (error) { console.error("Error in generateGL batch processing:", error); throw error; // Re-throw the error for the caller to handle } } export function generateMockGlData(count: number): any[] { const mockData: any[] = []; for (let i: number = 1; i <= count; i++) { mockData.push({ ITEMNO: i, ITEMDESCRIPTION: `Product Component ${i}`, NOOFPIECES: Math.floor(Math.random() * 20) + 1, WEIGHT: `${(Math.random() * 100).toFixed(2)} kg`, ITEMVALUE: parseFloat((Math.random() * 5000).toFixed(2)), GOODSORIGINCOUNTRY: ['USA', 'Canada', 'Germany', 'China', 'Japan'][ Math.floor(Math.random() * 5) ], }); } return mockData; } // console.log(generateMockGlData(40)) export async function usCounterFoil(carnetData: pdfCarnetData) { let documentNo = carnetData.NEXTUSCFNO; let fpdf = 1; const set = getPatternArray(carnetData.NOOFUSSETS) const generatedFiles: any = []; for (const s of set) { // console.log(" Current pattern value (s): ", s); // This is where you would use the 's' value to determine arguments // For now, let's keep the arguments constant as per your example const funFrontSecondArg = s > 1 ? "VALUE" : "VOID"; const funBackFirstArg = s > 2 ? "VALUE" : "VOID"; const funBackSecondArg = s > 3 ? "VALUE" : "VOID"; // Step 1: Await front PDF generation let inc1: number = await generateUSCounterFoilFront(carnetData, "VALUE", funFrontSecondArg, documentNo, fpdf); documentNo = inc1; generatedFiles.push(`${replaceSlashWithDash(carnetData.carnetNo)}${fpdf}USFront.pdf`); // Step 2: Await back PDF generation let inc2: number = await generateUSCounterFoilBack(carnetData, funBackFirstArg, funBackSecondArg, documentNo, fpdf); documentNo = inc2; generatedFiles.push(`${replaceSlashWithDash(carnetData.carnetNo)}${fpdf}USBack.pdf`); // Increment fpdf for the next loop fpdf++; } // console.log("All US Counter Foil PDFs have been completely generated."); // console.log(generatedFiles); // Return the list of generated filenames return generatedFiles; } export async function fnCounterFoil(carnetData: pdfCarnetData) { let documentNo = carnetData.NEXTFOREIGNCFNO; let fpdf = 1; const set = getPatternArray(carnetData.NOOFFOREIGNSETS) const generatedFiles: any = []; for (const s of set) { // This is where you would use the 's' value to determine arguments // For now, let's keep the arguments constant as per your example const funFrontSecondArg = s > 1 ? "VALUE" : "VOID"; const funBackFirstArg = s > 2 ? "VALUE" : "VOID"; const funBackSecondArg = s > 3 ? "VALUE" : "VOID"; // Step 1: Await front PDF generation let inc1: number = await generateFNCounterFoilFront(carnetData, "VALUE", funFrontSecondArg, documentNo, fpdf); documentNo = inc1; generatedFiles.push(`${replaceSlashWithDash(carnetData.carnetNo)}${fpdf}FNFront.pdf`); // Step 2: Await back PDF generation let inc2: number = await generateFNCounterFoilBack(carnetData, funBackFirstArg, funBackSecondArg, documentNo, fpdf); documentNo = inc2; generatedFiles.push(`${replaceSlashWithDash(carnetData.carnetNo)}${fpdf}FNBack.pdf`); // Increment fpdf for the next loop fpdf++; } // console.log("All US Counter Foil PDFs have been completely generated."); // console.log(generatedFiles); // Return the list of generated filenames return generatedFiles; } export async function tsCounterFoil(carnetData: pdfCarnetData) { let documentNo = carnetData.NEXTTRANSITCFNO; let fpdf = 1; const set = getPatternArray(carnetData.NOOFTRANSITSETS) const generatedFiles: any = []; for (const s of set) { // console.log(" Current pattern value (s): ", s); // This is where you would use the 's' value to determine arguments // For now, let's keep the arguments constant as per your example const funFrontSecondArg = s > 1 ? "VALUE" : "VOID"; const funBackFirstArg = s > 2 ? "VALUE" : "VOID"; const funBackSecondArg = s > 3 ? "VALUE" : "VOID"; // console.log("document no : ", s, " : ", documentNo); // Step 1: Await front PDF generation let inc1: number = await generateTSCounterFoilFront(carnetData, "VALUE", funFrontSecondArg, documentNo, fpdf); documentNo = inc1; generatedFiles.push(`${replaceSlashWithDash(carnetData.carnetNo)}${fpdf}TSFront.pdf`); // Step 2: Await back PDF generation let inc2: number = await generateTSCounterFoilBack(carnetData, funBackFirstArg, funBackSecondArg, documentNo, fpdf); documentNo = inc2; generatedFiles.push(`${replaceSlashWithDash(carnetData.carnetNo)}${fpdf}TSBack.pdf`); // Increment fpdf for the next loop fpdf++; } // console.log("All US Counter Foil PDFs have been completely generated."); // console.log(generatedFiles); // Return the list of generated filenames return generatedFiles; } export async function fnVochers(carnetData: pdfCarnetData, body: PrintCarnetDTO) { let importNo = 1; let exportNo = 1; let fpdf = 1; const set = (carnetData.NOOFFOREIGNSETS) const generatedFiles: any = []; for (let i = 1; i <= set; i++) { // Step 1: Await back PDF generation let inc1: number = await generateFNImportV(carnetData, importNo, fpdf); importNo = inc1; generatedFiles.push(`${replaceSlashWithDash(carnetData.carnetNo)}${fpdf}FNImportV.pdf`); if (body.P_PRINTGL === 'Y') { generatedFiles.push(`${replaceSlashWithDash(carnetData.carnetNo)}p2.pdf`); } // Step 2: Await back PDF generation let inc2: number = await generateFNExportV(carnetData, exportNo, fpdf); exportNo = inc2; generatedFiles.push(`${replaceSlashWithDash(carnetData.carnetNo)}${fpdf}FNExportV.pdf`); if (body.P_PRINTGL === 'Y') { generatedFiles.push(`${replaceSlashWithDash(carnetData.carnetNo)}p2.pdf`); } // Increment fpdf for the next loop fpdf++; } // console.log("All US Counter Foil PDFs have been completely generated."); // console.log(generatedFiles); // Return the list of generated filenames return generatedFiles; } export async function tnVochers(carnetData: pdfCarnetData, body: PrintCarnetDTO) { let tnNo = 1; let fpdf = 1; const set = (carnetData.NOOFTRANSITSETS) const generatedFiles: any = []; for (let i = 1; i <= set; i++) { // console.log(" Current pattern value (s): ", s); let inc1: number = await generateTNV(carnetData, tnNo, fpdf); tnNo = inc1; generatedFiles.push(`${replaceSlashWithDash(carnetData.carnetNo)}${fpdf}TNV.pdf`); if (body.P_PRINTGL === 'Y') { generatedFiles.push(`${replaceSlashWithDash(carnetData.carnetNo)}p2.pdf`); } fpdf++; } // console.log("All US Counter Foil PDFs have been completely generated."); // console.log(generatedFiles); return generatedFiles; } export const generateFinalPDF = async (inputFiles: string[], outputFile: string) => { const merger = new PDFMerger(); for (const fileName of inputFiles) { const filePath = path.join(__dirname, PDF_URL, fileName); // console.log(`Does the file exist? ${existsSync(filePath)}`); // console.log(`📄 Adding: ${filePath}`); try { const fileData = readFileSync(filePath); await merger.add(fileData); } catch (err) { console.error(`❌ Failed to add PDF: ${fileName}`, err); } } const outputPath = path.join(__dirname, PDF_URL, outputFile); try { await merger.save(outputPath); // console.log(`✅ Merged PDF saved to ${outputPath}`); } catch (err) { console.error(`❌ Failed to save merged PDF`, err); throw new Error("Failed to save merged PDF"); } }; export const deleteFilesAsync = async (relativeFilePaths: string[]): Promise => { for (const relativeFilePath of relativeFilePaths) { const filePath = path.join(__dirname, PDF_URL, relativeFilePath); if (fs.existsSync(filePath)) { try { await fsPromises.unlink(filePath); // console.log(`đŸ—‘ī¸ Deleted: ${filePath}`); } catch (err) { console.error(`❌ Error deleting file: ${filePath}`, err); } } else { console.warn(`âš ī¸ File not found: ${filePath}`); } } };