+176
-6
@@ -1,8 +1,13 @@
|
||||
/**
|
||||
* Plans and cutting list routes
|
||||
* 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
|
||||
@@ -15,9 +20,13 @@ export async function generatePlansRoute(req, res) {
|
||||
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 });
|
||||
res.json({ success: true, plans, warnings: validation.warnings });
|
||||
|
||||
} catch (error) {
|
||||
console.error('Plan generation error:', error);
|
||||
@@ -27,7 +36,7 @@ export async function generatePlansRoute(req, res) {
|
||||
|
||||
/**
|
||||
* POST /api/plans/cutting-list
|
||||
* Generate cutting list for a configuration
|
||||
* Generate cutting list + packing list for a configuration
|
||||
*/
|
||||
export async function generateCuttingListRoute(req, res) {
|
||||
try {
|
||||
@@ -36,9 +45,15 @@ export async function generateCuttingListRoute(req, res) {
|
||||
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);
|
||||
res.json({ success: true, cuttingList });
|
||||
const packing = generatePackingList(cuttingList);
|
||||
|
||||
res.json({ success: true, cuttingList, packing, warnings: validation.warnings });
|
||||
|
||||
} catch (error) {
|
||||
console.error('Cutting list error:', error);
|
||||
@@ -57,6 +72,10 @@ export async function exportCuttingListText(req, res) {
|
||||
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);
|
||||
@@ -73,7 +92,7 @@ export async function exportCuttingListText(req, res) {
|
||||
|
||||
/**
|
||||
* POST /api/plans/export-pdf
|
||||
* Export plans and cutting list as HTML (for PDF conversion)
|
||||
* Export plans, cutting list, and packing as HTML (for PDF conversion)
|
||||
*/
|
||||
export async function exportPDFHtml(req, res) {
|
||||
try {
|
||||
@@ -82,9 +101,14 @@ export async function exportPDFHtml(req, res) {
|
||||
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 html = generatePDFHtml(config, modules, cuttingList);
|
||||
const packing = generatePackingList(cuttingList);
|
||||
const html = generatePDFHtml(config, modules, cuttingList, packing);
|
||||
|
||||
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
||||
res.send(html);
|
||||
@@ -94,3 +118,149 @@ export async function exportPDFHtml(req, res) {
|
||||
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' });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user