import PDFDocument from 'pdfkit'; function addSectionTitle(doc, title) { doc.moveDown(0.5); doc.fontSize(14).font('Helvetica-Bold').text(title); doc.moveDown(0.3); doc.fontSize(10).font('Helvetica'); } function ensurePageSpace(doc, neededHeight = 80) { if (doc.y + neededHeight > doc.page.height - doc.page.margins.bottom) { doc.addPage(); } } function keyValue(doc, key, value) { doc.font('Helvetica-Bold').text(`${key}: `, { continued: true }); doc.font('Helvetica').text(String(value ?? '-')); } function tableRow(doc, cols, widths, opts = {}) { const startX = doc.x; const y = doc.y; const lineHeight = opts.lineHeight || 14; let x = startX; for (let i = 0; i < cols.length; i++) { const text = cols[i] == null ? '-' : String(cols[i]); doc.text(text, x, y, { width: widths[i], lineBreak: false }); x += widths[i]; } doc.y = y + lineHeight; } export function generateOrderDossierPdf({ orderId, createdAt, customerName, config, cuttingList, packing }) { return new Promise((resolve, reject) => { const doc = new PDFDocument({ size: 'A4', margin: 40 }); const chunks = []; doc.on('data', (chunk) => chunks.push(chunk)); doc.on('end', () => resolve(Buffer.concat(chunks))); doc.on('error', reject); doc.fontSize(18).font('Helvetica-Bold').text(`Dossier technique commande #${orderId}`); doc.fontSize(10).font('Helvetica').text(`Généré le ${new Date().toLocaleString('fr-CH')}`); if (createdAt) doc.text(`Commande du ${new Date(createdAt).toLocaleString('fr-CH')}`); if (customerName) doc.text(`Client: ${customerName}`); addSectionTitle(doc, 'Configuration'); keyValue(doc, 'Dimensions', `${config.widthMm} x ${config.heightMm} x ${config.depthMm} mm`); keyValue(doc, 'Installation', config.installationType === 'niche' ? 'Encastree' : 'Libre'); keyValue(doc, 'Portes', config.doorType === 'sliding' ? 'Coulissantes' : 'Battantes'); keyValue(doc, 'Modules', config.moduleCount || 0); addSectionTitle(doc, 'Liste de decoupage'); const cutWidths = [170, 130, 40, 130]; tableRow(doc, ['Piece', 'L x H x Ep.', 'Qte', 'Finition'], cutWidths, { lineHeight: 16 }); doc.moveTo(doc.x, doc.y).lineTo(doc.page.width - doc.page.margins.right, doc.y).stroke(); doc.moveDown(0.2); for (const piece of (cuttingList?.pieces || [])) { ensurePageSpace(doc, 18); tableRow( doc, [ piece.description, `${piece.widthMm}x${piece.heightMm}x${piece.thicknessMm}`, piece.quantity, piece.finishLabel || '-' ], cutWidths ); } addSectionTitle(doc, 'Colisage'); const packWidths = [50, 170, 70, 70]; tableRow(doc, ['Colis', 'Niveau', 'Poids', 'Pieces'], packWidths, { lineHeight: 16 }); doc.moveTo(doc.x, doc.y).lineTo(doc.page.width - doc.page.margins.right, doc.y).stroke(); doc.moveDown(0.2); for (const box of (packing?.boxes || [])) { ensurePageSpace(doc, 18); tableRow( doc, [ box.id, box.level || '-', `${Number(box.totalWeightKg || 0).toFixed(1)} kg`, box.pieces?.length || 0 ], packWidths ); } doc.end(); }); }