Co-authored-by: Cursor <[email protected]>
This commit is contained in:
lejay
2026-05-28 07:20:38 +02:00
co-authored by Cursor
parent 6923c319a1
commit 6c0d7e47b8
81 changed files with 21780 additions and 2986 deletions
+176 -6
View File
@@ -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' });
}
}