Files
web-cad/frontend/src/services/bomService.ts
T

113 lines
3.6 KiB
TypeScript
Raw Normal View History

/**
* Task CAD-9 Stueckliste (BOM) fuer Traversen-Systeme.
*
* buildBOM gruppiert Traversen + Eck-Traversen nach Typ + Mass
* (Laenge x Breite), zaehlt Stueckzahlen und summiert die
* Gesamt-Traversemeter (Boxcorner zaehlt BEIDE Arme).
* exportBOMCsv: Semikolon-separierte CSV (Excel-DE-freundlich).
*/
import type { CADElement } from '../types/cad.types';
export interface BOMRow {
pos: number;
type: 'Traverse' | 'Eck-Traverse';
/** Laenge in cm (Traverse: length; Eck-Traverse: armLength). */
length: number;
/** Breite in cm (trussWidth). */
width: number;
count: number;
/** Gesamtlaenge dieser Position in cm (count * length, Ecken * 2). */
totalCm: number;
/** Bezeichnung, z.B. "Traverse 200 x 29". */
label: string;
}
export interface BOMResult {
rows: BOMRow[];
/** Gesamte Traversenlaenge in Metern (Ecken zaehlen beide Arme). */
totalLengthM: number;
}
interface GroupKey {
type: 'Traverse' | 'Eck-Traverse';
length: number;
width: number;
}
/**
* Baut die Stueckliste aus allen Zeichnungselementen.
* Nur truss- und boxcorner-Elemente fliessen ein; alles andere
* wird still ignoriert.
*/
export function buildBOM(elements: CADElement[]): BOMResult {
const groups = new Map<string, { key: GroupKey; count: number }>();
for (const el of elements) {
if (String(el.type) === 'truss' && el.properties.truss === true) {
const length = (el.properties.length as number | undefined) ?? el.width ?? 0;
const width = (el.properties.trussWidth as number | undefined) ?? el.height ?? 29;
const key: GroupKey = { type: 'Traverse', length, width };
const id = `T|${length}|${width}`;
const g = groups.get(id) ?? { key, count: 0 };
g.count++;
groups.set(id, g);
} else if (String(el.type) === 'boxcorner' && el.properties.boxcorner === true) {
const length = (el.properties.armLength as number | undefined) ?? 50;
const width = (el.properties.trussWidth as number | undefined) ?? 29;
const key: GroupKey = { type: 'Eck-Traverse', length, width };
const id = `C|${length}|${width}`;
const g = groups.get(id) ?? { key, count: 0 };
g.count++;
groups.set(id, g);
}
}
// Sortierung: Traversen vor Ecken, innerhalb Typ nach Laenge absteigend
const entries = Array.from(groups.values());
entries.sort((a, b) => {
if (a.key.type !== b.key.type) {
return a.key.type === 'Traverse' ? -1 : 1;
}
return b.key.length - a.key.length;
});
const rows: BOMRow[] = entries.map((g, i) => {
// Ecken haben 2 Arme: Gesamtlaenge = count * length * 2
const armFactor = g.key.type === 'Eck-Traverse' ? 2 : 1;
const totalCm = g.count * g.key.length * armFactor;
return {
pos: i + 1,
type: g.key.type,
length: g.key.length,
width: g.key.width,
count: g.count,
totalCm,
label: `${g.key.type} ${g.key.length} \u00d7 ${g.key.width}`,
};
});
const totalCmAll = rows.reduce((s, r) => s + r.totalCm, 0);
return {
rows,
totalLengthM: totalCmAll / 100,
};
}
/**
* Exportiert die Stueckliste als Semikolon-CSV (Excel-DE oeffnet
* das direkt mehrspaltig). Spalten: Pos;Typ;Laenge_cm;Breite_cm;
* Anzahl;Gesamt_cm;Bezeichnung + Summenzeile am Ende.
*/
export function exportBOMCsv(bom: BOMResult): string {
const lines: string[] = ['Pos;Typ;Laenge_cm;Breite_cm;Anzahl;Gesamt_cm;Bezeichnung'];
for (const r of bom.rows) {
lines.push(
`${r.pos};${r.type};${r.length};${r.width};${r.count};${r.totalCm};${r.label}`,
);
}
if (bom.rows.length > 0) {
lines.push(`;;;SUMME;;;${bom.totalLengthM.toFixed(2)} m`);
}
return lines.join('\n');
}