forked from jappel/leistungsbilanz-ts
56 lines
5.4 KiB
TypeScript
56 lines
5.4 KiB
TypeScript
import type { Request, Response } from "express";
|
|
import { createExternalObjectPlanningUpdateProjectCommand } from "../../domain/models/external-object-planning-project-command.model.js";
|
|
import type { ExternalModelObjectPlanningValues } from "../../external-model/domain/external-model-contracts.js";
|
|
import { distributionBoardRepository, externalModelStateRepository, projectDeviceRepository, projectRepository } from "../composition/application-repositories.js";
|
|
import { projectCommandService } from "../composition/project-command-stores.js";
|
|
import { respondWithProjectCommandError } from "./project-command.controller.js";
|
|
|
|
export async function listExternalObjects(req: Request, res: Response) {
|
|
const projectId = String(req.params.projectId ?? "");
|
|
const [project, boards, devices] = await Promise.all([projectRepository.findById(projectId), distributionBoardRepository.listByProject(projectId), projectDeviceRepository.listByProject(projectId)]);
|
|
if (!project) return res.status(404).json({ error: "Projekt nicht gefunden." });
|
|
const state = externalModelStateRepository.getByProject(projectId).state;
|
|
const mappings = new Map(state.roomMappings.map((mapping) => [mapping.id, mapping]));
|
|
return res.json({
|
|
currentRevision: project.currentRevision,
|
|
sourceName: state.source?.name ?? null,
|
|
distributionBoards: boards.map(({ id, name }) => ({ id, name })),
|
|
projectDevices: devices.map(({ id, name, displayName }) => ({ id, name, displayName })),
|
|
objects: state.objects.map((object) => {
|
|
const mapping = object.externalRoomMappingId ? mappings.get(object.externalRoomMappingId) : null;
|
|
return { id: object.id, worksharingId: object.worksharingId, ifcGuid: object.ifcGuid, deviceTypeId: object.deviceTypeId, familyAndType: object.acceptedSourceValues.familyAndType, selectionMarker: scalarText(object.acceptedSourceValues.selectionMarker), sourceCircuitIdentifier: scalarText(object.acceptedSourceValues.circuitCombined) || scalarText(object.acceptedSourceValues.circuitIdentifier), sourceRoomNumber: scalarText(object.acceptedSourceValues.roomNumber), sourceRoomName: scalarText(object.acceptedSourceValues.roomName), roomId: mapping?.roomId ?? null, distributionBoardId: object.distributionBoardId, linkedProjectDeviceId: object.linkedProjectDeviceId, circuitDeviceRowId: object.circuitDeviceRowId, presenceStatus: object.presenceStatus, planningValues: object.planningValues };
|
|
}),
|
|
});
|
|
}
|
|
|
|
export function updateExternalObjects(req: Request, res: Response) {
|
|
const projectId = String(req.params.projectId ?? "");
|
|
try {
|
|
const expectedRevision = req.body?.expectedRevision;
|
|
if (!Number.isSafeInteger(expectedRevision) || expectedRevision < 0) throw new Error("Ungültige Projektrevision.");
|
|
const objectIds = req.body?.objectIds;
|
|
const patch = req.body?.patch;
|
|
if (!Array.isArray(objectIds) || objectIds.length === 0 || new Set(objectIds).size !== objectIds.length || !isRecord(patch)) throw new Error("Objektauswahl oder Änderungen sind ungültig.");
|
|
const state = externalModelStateRepository.getByProject(projectId);
|
|
if (!state.projectExists) return res.status(404).json({ error: "Projekt nicht gefunden." });
|
|
const byId = new Map(state.state.objects.map((object) => [object.id, object]));
|
|
const transitions = objectIds.map((id: unknown) => {
|
|
if (typeof id !== "string") throw new Error("Ungültige Revit-Objekt-ID.");
|
|
const expected = byId.get(id);
|
|
if (!expected) throw new Error("Ein ausgewähltes Revit-Objekt wurde nicht gefunden.");
|
|
if (expected.circuitDeviceRowId !== null) throw new Error("Verknüpfte Revit-Objekte folgen ihrer Gerätezeile und können hier nicht geändert werden.");
|
|
const planningValues = { ...expected.planningValues };
|
|
const overriddenFields = new Set(expected.overriddenFields);
|
|
for (const field of planningFields) if (hasOwn(patch, field)) { planningValues[field] = patch[field] as never; overriddenFields.add(field); }
|
|
return { expected, target: { ...expected, planningValues, overriddenFields: [...overriddenFields], distributionBoardId: hasOwn(patch, "distributionBoardId") ? nullableId(patch.distributionBoardId) : expected.distributionBoardId, linkedProjectDeviceId: hasOwn(patch, "linkedProjectDeviceId") ? nullableId(patch.linkedProjectDeviceId) : expected.linkedProjectDeviceId } };
|
|
});
|
|
const result = projectCommandService.executeUser({ projectId, expectedRevision, description: `${transitions.length} Revit-Objekte bearbeitet`, command: createExternalObjectPlanningUpdateProjectCommand(transitions) });
|
|
return res.json(result);
|
|
} catch (error) { return respondWithProjectCommandError(error, res); }
|
|
}
|
|
|
|
const planningFields = ["displayName", "category", "connectionKind", "effectiveQuantity", "powerPerUnitW", "simultaneityFactor", "cosPhi", "costGroup", "remark"] as const satisfies readonly (keyof ExternalModelObjectPlanningValues)[];
|
|
function scalarText(value: string | number | boolean | null) { return value === null ? "" : String(value); }
|
|
function isRecord(value: unknown): value is Record<string, unknown> { return value !== null && typeof value === "object" && !Array.isArray(value); }
|
|
function hasOwn(value: object, key: PropertyKey) { return Object.prototype.hasOwnProperty.call(value, key); }
|
|
function nullableId(value: unknown) { if (value === null || value === "") return null; if (typeof value !== "string") throw new Error("Ungültige Zuordnungs-ID."); return value; }
|