Files
apex/server/routes/plans.js
T
lejayandCursor 6c0d7e47b8 v2.0
Co-authored-by: Cursor <[email protected]>
2026-05-28 07:20:38 +02:00

267 lines
9.1 KiB
JavaScript

/**
* 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' });
}
}