/** * Plans, cutting list, and packing routes */ import { generateAllPlans, generatePDFHtml } from '../services/planGenerator.js'; import { generateCuttingList, formatCuttingListForExport } from '../services/cuttingEngine.js'; import { generatePackingList } from '../services/packingEngine.js'; import { generateCncProgram } from '../services/cncGenerator.js'; import { validateConfig } from '../services/configValidation.js'; import { generateHardwareList } from '../services/hardwareListEngine.js'; import { generateMprZipBuffer, generateMprPieceDownload } from '../services/mprGenerator.js'; /** * POST /api/plans/generate * Generate all 2D plans for a configuration */ export async function generatePlansRoute(req, res) { try { const { config, modules = [], colors = {} } = req.body; if (!config) { return res.status(400).json({ error: 'Configuration requise' }); } const validation = validateConfig(config); if (validation.errors.length > 0) { return res.status(400).json({ error: 'Configuration invalide', details: validation.errors }); } const plans = generateAllPlans(config, modules, colors); res.json({ success: true, plans, warnings: validation.warnings }); } catch (error) { console.error('Plan generation error:', error); res.status(500).json({ error: error.message || 'Erreur de génération des plans' }); } } /** * POST /api/plans/cutting-list * Generate cutting list + packing list for a configuration */ export async function generateCuttingListRoute(req, res) { try { const { config, modules = [], colors = {} } = req.body; if (!config) { return res.status(400).json({ error: 'Configuration requise' }); } const validation = validateConfig(config); if (validation.errors.length > 0) { return res.status(400).json({ error: 'Configuration invalide', details: validation.errors }); } const cuttingList = generateCuttingList(config, colors, modules); const packing = generatePackingList(cuttingList); res.json({ success: true, cuttingList, packing, warnings: validation.warnings }); } catch (error) { console.error('Cutting list error:', error); res.status(500).json({ error: error.message || 'Erreur de génération de la liste de débitage' }); } } /** * POST /api/plans/export-text * Export cutting list as text file */ export async function exportCuttingListText(req, res) { try { const { config, modules = [], colors = {} } = req.body; if (!config) { return res.status(400).json({ error: 'Configuration requise' }); } const validation = validateConfig(config); if (validation.errors.length > 0) { return res.status(400).json({ error: 'Configuration invalide', details: validation.errors }); } const cuttingList = generateCuttingList(config, colors, modules); const textContent = formatCuttingListForExport(cuttingList); res.setHeader('Content-Type', 'text/plain; charset=utf-8'); res.setHeader('Content-Disposition', 'attachment; filename="liste-debitage.txt"'); res.send(textContent); } catch (error) { console.error('Export text error:', error); res.status(500).json({ error: error.message || 'Erreur d\'export' }); } } /** * POST /api/plans/export-pdf * Export plans, cutting list, and packing as HTML (for PDF conversion) */ export async function exportPDFHtml(req, res) { try { const { config, modules = [], colors = {} } = req.body; if (!config) { return res.status(400).json({ error: 'Configuration requise' }); } const validation = validateConfig(config); if (validation.errors.length > 0) { return res.status(400).json({ error: 'Configuration invalide', details: validation.errors }); } const cuttingList = generateCuttingList(config, colors, modules); const packing = generatePackingList(cuttingList); const html = generatePDFHtml(config, modules, cuttingList, packing); res.setHeader('Content-Type', 'text/html; charset=utf-8'); res.send(html); } catch (error) { console.error('Export PDF error:', error); res.status(500).json({ error: error.message || 'Erreur d\'export PDF' }); } } /** * POST /api/plans/export-cnc * Export CNC program as standard ISO G-code (.nc) */ export async function exportCncRoute(req, res) { try { const { config, modules = [], colors = {} } = req.body; if (!config) { return res.status(400).json({ error: 'Configuration requise' }); } const validation = validateConfig(config); if (validation.errors.length > 0) { return res.status(400).json({ error: 'Configuration invalide', details: validation.errors }); } const cnc = generateCncProgram(config, modules, colors); res.setHeader('Content-Type', cnc.mimeType); res.setHeader('Content-Disposition', `attachment; filename="${cnc.fileName}"`); res.send(cnc.content); } catch (error) { console.error('Export CNC error:', error); res.status(500).json({ error: error.message || 'Erreur d\'export CNC' }); } } /** * POST /api/plans/export-mpr * Archive ZIP avec un fichier .mpr Homag/Woodwop par panneau. */ export async function exportMprRoute(req, res) { try { const { config, modules = [], colors = {}, orderReference = '', stackListKm = '', mprMaterialArticle = '', mprGroupIndex = 1 } = req.body || {}; if (!config) { return res.status(400).json({ error: 'Configuration requise' }); } const validation = validateConfig(config); if (validation.errors.length > 0) { return res.status(400).json({ error: 'Configuration invalide', details: validation.errors }); } const gi = Number(mprGroupIndex); const groupIndex = Number.isFinite(gi) && gi >= 1 ? Math.floor(gi) : 1; const { buffer, fileName, mimeType } = await generateMprZipBuffer({ config, modules, colors, orderReference, stackListKm: typeof stackListKm === 'string' ? stackListKm : String(stackListKm || '01'), defaultMaterialArticle: typeof mprMaterialArticle === 'string' ? mprMaterialArticle : '', groupIndex }); const safeName = /^[\w\-_.]+$/.test(String(fileName)) ? fileName : 'apex-mpr.zip'; res.setHeader('Content-Type', mimeType); res.setHeader('Content-Disposition', `attachment; filename="${safeName}"`); res.send(Buffer.from(buffer)); } catch (error) { console.error('Export MPR error:', error); res.status(500).json({ error: error.message || 'Erreur d\'export MPR' }); } } /** * POST /api/plans/export-mpr-piece * Export MPR pour une ligne de la liste de débitage (fichier unique ou mini-ZIP si plusieurs exemplaires). */ export async function exportMprPieceRoute(req, res) { try { const { config, modules = [], colors = {}, pieceId, instanceIndex, orderReference = '', stackListKm = '', mprMaterialArticle = '', mprGroupIndex = 1 } = req.body || {}; if (!config) { return res.status(400).json({ error: 'Configuration requise' }); } if (!pieceId || typeof pieceId !== 'string') { return res.status(400).json({ error: 'pieceId requis' }); } const validation = validateConfig(config); if (validation.errors.length > 0) { return res.status(400).json({ error: 'Configuration invalide', details: validation.errors }); } const gi = Number(mprGroupIndex); const groupIndex = Number.isFinite(gi) && gi >= 1 ? Math.floor(gi) : 1; const { buffer, fileName, mimeType } = await generateMprPieceDownload({ config, modules, colors, pieceId, instanceIndex, orderReference, stackListKm: typeof stackListKm === 'string' ? stackListKm : String(stackListKm || '01'), defaultMaterialArticle: typeof mprMaterialArticle === 'string' ? mprMaterialArticle : '', groupIndex }); const safeName = /^[\w\-_.]+$/.test(String(fileName)) ? fileName : 'piece.mpr'; res.setHeader('Content-Type', mimeType); res.setHeader('Content-Disposition', `attachment; filename="${safeName}"`); res.send(Buffer.from(buffer)); } catch (error) { console.error('Export MPR piece error:', error); const status = /introuvable|requis|invalide/i.test(error.message || '') ? 400 : 500; res.status(status).json({ error: error.message || 'Erreur d\'export MPR pièce' }); } } /** * POST /api/plans/hardware-list * Generate bill of materials for hardware/quincaillerie with prices (best-effort). */ export async function generateHardwareListRoute(req, res) { try { const { config, modules = [], colors = {} } = req.body; if (!config) { return res.status(400).json({ error: 'Configuration requise' }); } const validation = validateConfig(config); if (validation.errors.length > 0) { return res.status(400).json({ error: 'Configuration invalide', details: validation.errors }); } const result = await generateHardwareList({ config, modules, colors }); res.json({ success: true, ...result, warnings: validation.warnings }); } catch (error) { console.error('Hardware list error:', error); res.status(500).json({ error: error.message || 'Erreur de génération de la liste de quincaillerie' }); } }