forked from jappel/leistungsbilanz-ts
In LPH 2/3 there are no circuits yet, but the office needs a power balance by DIN 276 cost group, as in the Excel template "Leistungsbilanz - KG400": each trade enters consumers by piece or by area (W/m²) with GLF and cos phi; the balance sums AV and SV per cost group, adds a reserve and derives the maximum current. The module is separate from the circuit structure: own tables (migration 0010), own folder src/estimate-balance and an import-free shared calculation used by API and UI. Writes go through project commands (estimate-balance.insert/.update/.delete with complete documents), so revision checks and Undo/Redo apply; it is not part of project snapshots or transfer yet. A comparison endpoint sums the device rows of all circuit lists by cost group (longest prefix) against the estimate. The UI adds a sidebar entry, a list of Stände (template KG 400, empty or copy) and a spreadsheet-like editor with autosave, AMEV picker for the GLF, totals, reserve, maximum current, comparison table and print layout. The tests reproduce the template example exactly (143.24 kW, 148.48 kVA, 157.56 kW / 163.33 kVA with 10 % reserve, 235.75 A). See docs/estimate-balance.md and issue #7. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
138 lines
5.2 KiB
TypeScript
138 lines
5.2 KiB
TypeScript
import type {
|
|
ProjectRevisionSourceDto,
|
|
ProjectRevisionSummaryDto,
|
|
ProjectSnapshotMetadataDto,
|
|
} from "../types";
|
|
|
|
const sourceLabels: Record<ProjectRevisionSourceDto, string> = {
|
|
user: "Bearbeitung",
|
|
undo: "Rückgängig",
|
|
redo: "Wiederholt",
|
|
restore: "Wiederherstellung",
|
|
migration: "Migration",
|
|
};
|
|
|
|
const commandTypeLabels: Record<string, string> = {
|
|
"circuit.update": "Stromkreis bearbeitet",
|
|
"circuit.insert": "Stromkreis angelegt",
|
|
"circuit.delete": "Stromkreis gelöscht",
|
|
"circuit-device-row.update": "Gerätezeile bearbeitet",
|
|
"circuit-device-row.insert": "Gerätezeile angelegt",
|
|
"circuit-device-row.delete": "Gerätezeile gelöscht",
|
|
"circuit-device-row.move": "Gerätezeile verschoben",
|
|
"circuit-device-row.move-with-new-circuit":
|
|
"Gerätezeile in neuen Stromkreis verschoben",
|
|
"circuit.reorder-section": "Stromkreise sortiert",
|
|
"circuit.reorder-sections": "Stromkreise bereichsübergreifend sortiert",
|
|
"circuit.renumber-section": "Bereich neu nummeriert",
|
|
"project-device.update": "Projektgerät bearbeitet",
|
|
"project-device.insert": "Projektgerät angelegt",
|
|
"project-device.delete": "Projektgerät gelöscht",
|
|
"project-device.sync-rows": "Projektgerät-Verknüpfungen geändert",
|
|
"project.update-settings": "Projekteinstellungen bearbeitet",
|
|
"distribution-board.insert": "Verteilung angelegt",
|
|
"distribution-board.update": "Verteilung bearbeitet",
|
|
"distribution-board.delete": "Verteilung entfernt",
|
|
"distribution-board.insert-subtree": "Verteilung vollständig eingefügt",
|
|
"distribution-board.delete-subtree": "Verteilung vollständig entfernt",
|
|
"distribution-board-component.insert": "Verteilergerät angelegt",
|
|
"distribution-board-component.delete": "Verteilergerät entfernt",
|
|
"distribution-board-component.update": "Verteilergerät bearbeitet",
|
|
"circuit-group.insert": "Stromkreisgruppe angelegt",
|
|
"circuit-group.delete": "Stromkreisgruppe entfernt",
|
|
"circuit-group.update": "Stromkreisgruppe bearbeitet",
|
|
"circuit-group.reorder": "Stromkreisgruppen sortiert",
|
|
"circuit-group.renumber": "Stromkreisgruppen neu nummeriert",
|
|
"circuit.move-group": "Stromkreis in andere Gruppe verschoben",
|
|
"circuit-group.delete-subtree": "Stromkreisgruppe vollständig entfernt",
|
|
"circuit-group.restore-subtree": "Stromkreisgruppe vollständig wiederhergestellt",
|
|
"project-building.insert": "Gebäude angelegt",
|
|
"project-building.update": "Gebäude bearbeitet",
|
|
"project-building.delete": "Gebäude entfernt",
|
|
"project-floor.insert": "Geschoss angelegt",
|
|
"project-floor.update": "Geschoss bearbeitet",
|
|
"project-floor.delete": "Geschoss entfernt",
|
|
"project-room.insert": "Raum angelegt",
|
|
"project-room.update": "Raum bearbeitet",
|
|
"project-room.delete": "Raum entfernt",
|
|
"project.restore-state": "Projektstand wiederhergestellt",
|
|
"circuit-protection.update": "Stromkreisschutz bearbeitet",
|
|
"external-csv-configuration.update": "Revit-CSV-Konfiguration bearbeitet",
|
|
"external-import.apply-initial": "Revit-Erstimport übernommen",
|
|
"external-object.assign-to-new-circuit":
|
|
"Externes Objekt in neuen Stromkreis übernommen",
|
|
"external-object.unassign-and-delete-created-circuit":
|
|
"Externes Objekt aus erzeugtem Stromkreis gelöst",
|
|
"external-object.assign-to-new-row":
|
|
"Externes Objekt in neue Gerätezeile übernommen",
|
|
"external-object.unassign-and-delete-created-row":
|
|
"Externes Objekt aus erzeugter Gerätezeile gelöst",
|
|
"external-object.update-row-assignment": "Externe Objektzuordnung geändert",
|
|
"estimate-balance.insert": "Grobe Leistungsbilanz angelegt",
|
|
"estimate-balance.update": "Grobe Leistungsbilanz bearbeitet",
|
|
"estimate-balance.delete": "Grobe Leistungsbilanz entfernt",
|
|
};
|
|
|
|
export function getProjectRevisionSourceLabel(
|
|
source: ProjectRevisionSourceDto
|
|
) {
|
|
return sourceLabels[source];
|
|
}
|
|
|
|
export function getProjectRevisionDescription(
|
|
revision: ProjectRevisionSummaryDto
|
|
) {
|
|
const description = revision.description?.trim();
|
|
if (
|
|
description === `Undo ${revision.commandType}` ||
|
|
description === `Redo ${revision.commandType}`
|
|
) {
|
|
return commandTypeLabels[revision.commandType] || revision.commandType;
|
|
}
|
|
return (
|
|
description ||
|
|
commandTypeLabels[revision.commandType] ||
|
|
revision.commandType
|
|
);
|
|
}
|
|
|
|
export function getProjectSnapshotKindLabel(
|
|
kind: ProjectSnapshotMetadataDto["kind"]
|
|
) {
|
|
return kind === "automatic" ? "Automatisch" : "Benannt";
|
|
}
|
|
|
|
export function getProjectSnapshotRevisionDescription(
|
|
snapshot: ProjectSnapshotMetadataDto
|
|
) {
|
|
if (snapshot.sourceRevision === 0) {
|
|
return "Projektstart";
|
|
}
|
|
if (
|
|
!snapshot.sourceRevisionMetadata ||
|
|
snapshot.sourceRevisionMetadata.revisionNumber !==
|
|
snapshot.sourceRevision
|
|
) {
|
|
return "Änderungsdetails nicht verfügbar";
|
|
}
|
|
return `${getProjectRevisionSourceLabel(
|
|
snapshot.sourceRevisionMetadata.source
|
|
)}: ${getProjectRevisionDescription(
|
|
snapshot.sourceRevisionMetadata
|
|
)}`;
|
|
}
|
|
|
|
export function mergeProjectRevisionPages(
|
|
current: ProjectRevisionSummaryDto[],
|
|
next: ProjectRevisionSummaryDto[]
|
|
) {
|
|
const byId = new Map(
|
|
[...current, ...next].map((revision) => [
|
|
revision.revisionId,
|
|
revision,
|
|
])
|
|
);
|
|
return [...byId.values()].sort(
|
|
(left, right) => right.revisionNumber - left.revisionNumber
|
|
);
|
|
}
|