import type { Request, Response } from "express"; import { calculateCircuitTotalPower, calculateRowTotalPower, } from "../../domain/calculations/circuit-power-calculation.js"; import type { CircuitTreeResponse } from "../../domain/models/circuit-tree.model.js"; import { circuitDeviceRowRepository, circuitListRepository, circuitRepository, circuitSectionRepository, projectRepository, } from "../composition/application-repositories.js"; export function isMissingCircuitTreeSchemaError(error: unknown): boolean { if (!(error instanceof Error)) { return false; } return ( error.message.includes("no such table: circuit_sections") || error.message.includes("no such table: circuits") || error.message.includes("no such table: circuit_device_rows") ); } export async function getCircuitTree(req: Request, res: Response) { const { projectId, circuitListId } = req.params; if (typeof projectId !== "string" || typeof circuitListId !== "string") { return res.status(400).json({ error: "Invalid parameters" }); } const list = await circuitListRepository.findById(projectId, circuitListId); if (!list) { return res.status(404).json({ error: "Circuit list not found" }); } const project = await projectRepository.findById(projectId); if (!project) { return res.status(404).json({ error: "Project not found" }); } try { const sections = await circuitSectionRepository.listByCircuitList(circuitListId); const circuits = await circuitRepository.listByCircuitList(circuitListId); const rows = await circuitDeviceRowRepository.listByCircuitList(circuits.map((entry) => entry.id)); const sectionById = new Map(sections.map((section) => [section.id, section])); const rowsByCircuitId = new Map(); for (const row of rows) { if (!rowsByCircuitId.has(row.circuitId)) { rowsByCircuitId.set(row.circuitId, []); } rowsByCircuitId.get(row.circuitId)!.push(row); } const tree: CircuitTreeResponse = { circuitListId, currentRevision: project.currentRevision, sections: sections.map((section) => ({ id: section.id, key: section.key, displayName: section.displayName, prefix: section.prefix, sortOrder: section.sortOrder, circuits: [], })), }; const sectionBlocks = new Map(tree.sections.map((section) => [section.id, section])); for (const circuit of circuits) { const section = sectionById.get(circuit.sectionId); if (!section || section.circuitListId !== circuit.circuitListId) { continue; } const deviceRows = (rowsByCircuitId.get(circuit.id) ?? []).map((row) => ({ id: row.id, linkedProjectDeviceId: row.linkedProjectDeviceId ?? undefined, legacyConsumerId: row.legacyConsumerId ?? undefined, sortOrder: row.sortOrder, name: row.name, displayName: row.displayName, phaseType: row.phaseType ?? undefined, connectionKind: row.connectionKind ?? undefined, costGroup: row.costGroup ?? undefined, category: row.category ?? undefined, level: row.level ?? undefined, roomId: row.roomId ?? undefined, roomNumberSnapshot: row.roomNumberSnapshot ?? undefined, roomNameSnapshot: row.roomNameSnapshot ?? undefined, quantity: row.quantity, powerPerUnit: row.powerPerUnit, simultaneityFactor: row.simultaneityFactor, cosPhi: row.cosPhi ?? undefined, remark: row.remark ?? undefined, overriddenFields: row.overriddenFields ?? undefined, rowTotalPower: calculateRowTotalPower(row.quantity, row.powerPerUnit, row.simultaneityFactor), })); const circuitTotalPower = calculateCircuitTotalPower(deviceRows); sectionBlocks.get(section.id)?.circuits.push({ id: circuit.id, circuitListId: circuit.circuitListId, sectionId: circuit.sectionId, equipmentIdentifier: circuit.equipmentIdentifier, displayName: circuit.displayName ?? undefined, sortOrder: circuit.sortOrder, protectionType: circuit.protectionType ?? undefined, protectionRatedCurrent: circuit.protectionRatedCurrent ?? undefined, protectionCharacteristic: circuit.protectionCharacteristic ?? undefined, cableType: circuit.cableType ?? undefined, cableCrossSection: circuit.cableCrossSection ?? undefined, cableLength: circuit.cableLength ?? undefined, rcdAssignment: circuit.rcdAssignment ?? undefined, terminalDesignation: circuit.terminalDesignation ?? undefined, voltage: circuit.voltage ?? undefined, controlRequirement: circuit.controlRequirement ?? undefined, status: circuit.status ?? undefined, isReserve: Boolean(circuit.isReserve), remark: circuit.remark ?? undefined, circuitTotalPower, deviceRows, }); } return res.json(tree); } catch (error) { if (isMissingCircuitTreeSchemaError(error)) { return res.json({ circuitListId, currentRevision: project.currentRevision, sections: [], warning: "Circuit-first tables are not available yet. Run database migrations (including 0008_circuit_first_model).", }); } throw error; } }