diff --git a/docs/circuit-list-editor-api.md b/docs/circuit-list-editor-api.md index 191fb7a..af2893f 100644 --- a/docs/circuit-list-editor-api.md +++ b/docs/circuit-list-editor-api.md @@ -148,14 +148,12 @@ Request sketch: } ``` -## Legacy Endpoints (Temporary) +## Removed Legacy Endpoints -- `GET /consumers/projects/:projectId` -- `POST /consumers` -- `PUT /consumers/:consumerId` -- `DELETE /consumers/:consumerId` - -These remain for migration/legacy views. New circuit-list interactions must use circuit-first endpoints above. +The former `/consumers` read/write endpoints were removed after every retained +consumer had a verified Circuit-First migration mapping. Database upgrade tooling +reads retained legacy rows directly; application features must use the Circuit-First +endpoints above. ## Linked Project Device Review diff --git a/docs/circuit-list-editor-known-limitations.md b/docs/circuit-list-editor-known-limitations.md index b698006..ec98515 100644 --- a/docs/circuit-list-editor-known-limitations.md +++ b/docs/circuit-list-editor-known-limitations.md @@ -10,4 +10,4 @@ - No full norm-compliant voltage-drop and protection-dimensioning calculation flow yet. - Sorting is view-only until users explicitly apply sorted order. - Cross-section circuit drag-reorder is intentionally blocked. -- The legacy consumer UI has been removed after complete migration; its server endpoints and retained database records are still pending final cleanup. +- Legacy consumer UI and server endpoints are removed; retained source rows and migration mappings remain available only for upgrade traceability. diff --git a/docs/circuit-list-editor-migration.md b/docs/circuit-list-editor-migration.md index a6a29a4..142ceda 100644 --- a/docs/circuit-list-editor-migration.md +++ b/docs/circuit-list-editor-migration.md @@ -78,6 +78,6 @@ Actions: Do not delete legacy consumers yet. -- legacy write endpoints remain temporarily available for the next server cutover step +- legacy read/write endpoints have been removed from the application server - migration trace tables reference old/new mapping - keeping legacy rows allows comparison and rollback validation during transition diff --git a/docs/spec/07-implementation-phases-todo.md b/docs/spec/07-implementation-phases-todo.md index e6e8335..6bb559a 100644 --- a/docs/spec/07-implementation-phases-todo.md +++ b/docs/spec/07-implementation-phases-todo.md @@ -342,6 +342,8 @@ Implemented foundation: - the legacy consumer migration receives its database dependency at the script entry point - legacy circuit, row, mapping and report writes have real late-failure rollback coverage - circuit and device-row persistence value mapping is separated from the general repositories +- the legacy consumer UI and application read/write endpoints are removed after verified data cutover +- retained legacy rows are accessible only through explicit database upgrade tooling ## Phase 12: Project Revisions and Persistent Undo / Redo diff --git a/package.json b/package.json index 7da87f1..16e3355 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,8 @@ "build:api": "tsc -p tsconfig.json", "build:web": "next build", "start": "node dist/server/index.js", - "test": "tsx --test tests/power-calculation.test.ts tests/consumer-linking.service.test.ts tests/consumer-schema-options.test.ts tests/project-device-schema.test.ts tests/project-device-placement.service.test.ts tests/project-device-sync.service.test.ts tests/legacy-consumer-migration-planner.test.ts tests/legacy-consumer-migration.repository.test.ts tests/circuit-numbering.service.test.ts tests/circuit-write.rules.test.ts tests/circuit-power-calculation.test.ts tests/circuit-tree.controller.test.ts tests/circuit-grid-insertion.test.ts tests/circuit-grid-safety.test.ts tests/circuit-grid-model.test.ts tests/circuit-grid-projection.test.ts tests/distribution-board.repository.test.ts tests/circuit-device-row-transaction.repository.test.ts tests/project-device-row-sync.repository.test.ts tests/circuit-section-transaction.repository.test.ts tests/database-backup.test.ts", - "test:watch": "tsx --watch --test tests/power-calculation.test.ts tests/consumer-linking.service.test.ts tests/consumer-schema-options.test.ts tests/project-device-schema.test.ts tests/project-device-placement.service.test.ts tests/project-device-sync.service.test.ts tests/legacy-consumer-migration-planner.test.ts tests/legacy-consumer-migration.repository.test.ts tests/circuit-numbering.service.test.ts tests/circuit-write.rules.test.ts tests/circuit-power-calculation.test.ts tests/circuit-tree.controller.test.ts tests/circuit-grid-insertion.test.ts tests/circuit-grid-safety.test.ts tests/circuit-grid-model.test.ts tests/circuit-grid-projection.test.ts tests/distribution-board.repository.test.ts tests/circuit-device-row-transaction.repository.test.ts tests/project-device-row-sync.repository.test.ts tests/circuit-section-transaction.repository.test.ts tests/database-backup.test.ts", + "test": "tsx --test tests/project-device-schema.test.ts tests/project-device-placement.service.test.ts tests/project-device-sync.service.test.ts tests/legacy-consumer-migration-planner.test.ts tests/legacy-consumer-migration.repository.test.ts tests/circuit-numbering.service.test.ts tests/circuit-write.rules.test.ts tests/circuit-power-calculation.test.ts tests/circuit-tree.controller.test.ts tests/circuit-grid-insertion.test.ts tests/circuit-grid-safety.test.ts tests/circuit-grid-model.test.ts tests/circuit-grid-projection.test.ts tests/distribution-board.repository.test.ts tests/circuit-device-row-transaction.repository.test.ts tests/project-device-row-sync.repository.test.ts tests/circuit-section-transaction.repository.test.ts tests/database-backup.test.ts", + "test:watch": "tsx --watch --test tests/project-device-schema.test.ts tests/project-device-placement.service.test.ts tests/project-device-sync.service.test.ts tests/legacy-consumer-migration-planner.test.ts tests/legacy-consumer-migration.repository.test.ts tests/circuit-numbering.service.test.ts tests/circuit-write.rules.test.ts tests/circuit-power-calculation.test.ts tests/circuit-tree.controller.test.ts tests/circuit-grid-insertion.test.ts tests/circuit-grid-safety.test.ts tests/circuit-grid-model.test.ts tests/circuit-grid-projection.test.ts tests/distribution-board.repository.test.ts tests/circuit-device-row-transaction.repository.test.ts tests/project-device-row-sync.repository.test.ts tests/circuit-section-transaction.repository.test.ts tests/database-backup.test.ts", "db:generate": "drizzle-kit generate", "db:migrate": "drizzle-kit migrate", "db:backup": "tsx scripts/db-backup.ts", diff --git a/src/db/repositories/consumer.repository.ts b/src/db/repositories/consumer.repository.ts deleted file mode 100644 index fc92e40..0000000 --- a/src/db/repositories/consumer.repository.ts +++ /dev/null @@ -1,147 +0,0 @@ -import crypto from "node:crypto"; -import { and, eq } from "drizzle-orm"; -import { db } from "../client.js"; -import { consumers } from "../schema/consumers.js"; -import type { - CreateConsumerInput, - UpdateConsumerInput, -} from "../../shared/validation/consumer.schemas.js"; - -export class ConsumerRepository { - async listByProject(projectId: string) { - return db.select().from(consumers).where(eq(consumers.projectId, projectId)); - } - - async listByCircuitList(circuitListId: string) { - return db.select().from(consumers).where(eq(consumers.circuitListId, circuitListId)); - } - - async create(input: CreateConsumerInput) { - const id = crypto.randomUUID(); - const normalizedName = input.name?.trim() || "Unbenannter Eintrag"; - const normalizedQuantity = input.quantity ?? 0; - const normalizedInstalledPowerPerUnitKw = input.installedPowerPerUnitKw ?? 0; - const normalizedDemandFactor = input.demandFactor ?? 1; - await db.insert(consumers).values({ - id, - projectId: input.projectId, - distributionBoardId: input.distributionBoardId ?? null, - circuitListId: input.circuitListId ?? null, - projectDeviceId: input.projectDeviceId ?? null, - isLinkedToDevice: input.isLinkedToDevice ? 1 : 0, - roomId: input.roomId ?? null, - circuitNumber: input.circuitNumber ?? null, - description: input.description ?? null, - name: normalizedName, - category: input.category ?? null, - deviceType: input.deviceType ?? null, - phaseType: input.phaseType ?? null, - tradeOrCostGroup: input.tradeOrCostGroup ?? null, - group: input.group ?? null, - protectionType: input.protectionType ?? null, - protectionRatedCurrent: input.protectionRatedCurrent ?? null, - protectionCharacteristic: input.protectionCharacteristic ?? null, - cableType: input.cableType ?? null, - cableCrossSection: input.cableCrossSection ?? null, - comment: input.comment ?? null, - quantity: normalizedQuantity, - installedPowerPerUnitKw: normalizedInstalledPowerPerUnitKw, - demandFactor: normalizedDemandFactor, - voltageV: input.voltageV ?? null, - phaseCount: input.phaseCount ?? null, - powerFactor: input.powerFactor ?? null, - note: input.note ?? null, - }); - return { - id, - ...input, - name: normalizedName, - quantity: normalizedQuantity, - installedPowerPerUnitKw: normalizedInstalledPowerPerUnitKw, - demandFactor: normalizedDemandFactor, - }; - } - - async update(consumerId: string, input: UpdateConsumerInput) { - const normalizedName = input.name?.trim() || "Unbenannter Eintrag"; - const normalizedQuantity = input.quantity ?? 0; - const normalizedInstalledPowerPerUnitKw = input.installedPowerPerUnitKw ?? 0; - const normalizedDemandFactor = input.demandFactor ?? 1; - await db - .update(consumers) - .set({ - projectId: input.projectId, - distributionBoardId: input.distributionBoardId ?? null, - circuitListId: input.circuitListId ?? null, - projectDeviceId: input.projectDeviceId ?? null, - isLinkedToDevice: input.isLinkedToDevice ? 1 : 0, - roomId: input.roomId ?? null, - circuitNumber: input.circuitNumber ?? null, - description: input.description ?? null, - name: normalizedName, - category: input.category ?? null, - deviceType: input.deviceType ?? null, - phaseType: input.phaseType ?? null, - tradeOrCostGroup: input.tradeOrCostGroup ?? null, - group: input.group ?? null, - protectionType: input.protectionType ?? null, - protectionRatedCurrent: input.protectionRatedCurrent ?? null, - protectionCharacteristic: input.protectionCharacteristic ?? null, - cableType: input.cableType ?? null, - cableCrossSection: input.cableCrossSection ?? null, - comment: input.comment ?? null, - quantity: normalizedQuantity, - installedPowerPerUnitKw: normalizedInstalledPowerPerUnitKw, - demandFactor: normalizedDemandFactor, - voltageV: input.voltageV ?? null, - phaseCount: input.phaseCount ?? null, - powerFactor: input.powerFactor ?? null, - note: input.note ?? null, - }) - .where(eq(consumers.id, consumerId)); - } - - async findById(consumerId: string) { - const [row] = await db.select().from(consumers).where(eq(consumers.id, consumerId)).limit(1); - return row ?? null; - } - - async delete(consumerId: string) { - await db.delete(consumers).where(eq(consumers.id, consumerId)); - } - - async syncLinkedConsumersFromProjectDevice( - projectId: string, - projectDeviceId: string, - data: { - displayName: string; - category?: string; - quantity: number; - installedPowerPerUnitKw: number; - demandFactor: number; - phaseCount?: 1 | 3; - powerFactor?: number; - note?: string; - } - ) { - await db - .update(consumers) - .set({ - name: data.displayName, - category: data.category ?? null, - quantity: data.quantity, - installedPowerPerUnitKw: data.installedPowerPerUnitKw, - demandFactor: data.demandFactor, - phaseCount: data.phaseCount ?? null, - powerFactor: data.powerFactor ?? null, - note: data.note ?? null, - }) - .where( - and( - eq(consumers.projectId, projectId), - eq(consumers.projectDeviceId, projectDeviceId), - eq(consumers.isLinkedToDevice, 1) - ) - ); - } -} diff --git a/src/db/repositories/legacy-consumer-migration.repository.ts b/src/db/repositories/legacy-consumer-migration.repository.ts index d557f3f..e0896c7 100644 --- a/src/db/repositories/legacy-consumer-migration.repository.ts +++ b/src/db/repositories/legacy-consumer-migration.repository.ts @@ -37,6 +37,13 @@ export interface LegacyMigrationReportPersistenceInput { export class LegacyConsumerMigrationRepository { constructor(private readonly database: AppDatabase) {} + async listSourceConsumersByCircuitList(circuitListId: string) { + return this.database + .select() + .from(consumers) + .where(eq(consumers.circuitListId, circuitListId)); + } + async listUnmigratedConsumers() { return this.database .select({ diff --git a/src/db/repositories/project.repository.ts b/src/db/repositories/project.repository.ts index a13f2bc..d5e6b25 100644 --- a/src/db/repositories/project.repository.ts +++ b/src/db/repositories/project.repository.ts @@ -5,7 +5,7 @@ import { projects } from "../schema/projects.js"; import type { CreateProjectInput, UpdateProjectSettingsInput, -} from "../../shared/validation/consumer.schemas.js"; +} from "../../shared/validation/project-structure.schemas.js"; export class ProjectRepository { async list() { diff --git a/src/db/repositories/room.repository.ts b/src/db/repositories/room.repository.ts index b71a189..ed0f104 100644 --- a/src/db/repositories/room.repository.ts +++ b/src/db/repositories/room.repository.ts @@ -2,7 +2,7 @@ import crypto from "node:crypto"; import { and, asc, eq } from "drizzle-orm"; import { db } from "../client.js"; import { rooms } from "../schema/rooms.js"; -import type { CreateRoomInput } from "../../shared/validation/consumer.schemas.js"; +import type { CreateRoomInput } from "../../shared/validation/project-structure.schemas.js"; export class RoomRepository { async listByProject(projectId: string) { diff --git a/src/db/schema/project-devices.ts b/src/db/schema/project-devices.ts index 97bdb78..5b400e0 100644 --- a/src/db/schema/project-devices.ts +++ b/src/db/schema/project-devices.ts @@ -17,7 +17,7 @@ export const projectDevices = sqliteTable("project_devices", { simultaneityFactor: real("simultaneity_factor").notNull().default(1), cosPhi: real("cos_phi"), remark: text("remark"), - // Legacy compatibility fields. Keep these synchronized until the Consumer editor is removed. + // Transitional mirror columns retained until a dedicated schema cleanup migration removes them. installedPowerPerUnitKw: real("installed_power_per_unit_kw").notNull(), demandFactor: real("demand_factor").notNull(), voltageV: real("voltage_v"), diff --git a/src/domain/calculations/power-calculation.ts b/src/domain/calculations/power-calculation.ts deleted file mode 100644 index b038912..0000000 --- a/src/domain/calculations/power-calculation.ts +++ /dev/null @@ -1,29 +0,0 @@ -export interface PowerInput { - quantity: number; - installedPowerPerUnitKw: number; - demandFactor: number; -} - -export interface CurrentInput { - demandPowerKw: number; - voltageV: number; - phaseCount: 1 | 3; - powerFactor: number; -} - -export function calculateInstalledPowerKw(input: PowerInput): number { - return input.quantity * input.installedPowerPerUnitKw; -} - -export function calculateDemandPowerKw(input: PowerInput): number { - return calculateInstalledPowerKw(input) * input.demandFactor; -} - -export function calculateCurrentA(input: CurrentInput): number { - const powerW = input.demandPowerKw * 1000; - if (input.phaseCount === 1) { - return powerW / (input.voltageV * input.powerFactor); - } - return powerW / (Math.sqrt(3) * input.voltageV * input.powerFactor); -} - diff --git a/src/domain/models/consumer.model.ts b/src/domain/models/consumer.model.ts deleted file mode 100644 index ee5b11a..0000000 --- a/src/domain/models/consumer.model.ts +++ /dev/null @@ -1,34 +0,0 @@ -export interface Consumer { - id: string; - projectId: string; - distributionBoardId?: string; - circuitListId?: string; - projectDeviceId?: string; - isLinkedToDevice?: boolean; - roomId?: string; - roomNumber?: string; - roomName?: string; - floorId?: string; - floorName?: string; - circuitNumber?: string; - description?: string; - name: string; - category?: string; - deviceType?: string; - phaseType?: string; - tradeOrCostGroup?: string; - group?: string; - protectionType?: string; - protectionRatedCurrent?: number; - protectionCharacteristic?: string; - cableType?: string; - cableCrossSection?: string; - comment?: string; - quantity: number; - installedPowerPerUnitKw: number; - demandFactor: number; - voltageV?: number; - phaseCount?: 1 | 3; - powerFactor?: number; - note?: string; -} diff --git a/src/domain/services/consumer-linking.service.ts b/src/domain/services/consumer-linking.service.ts deleted file mode 100644 index 7c93c8f..0000000 --- a/src/domain/services/consumer-linking.service.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { CreateConsumerInput } from "../../shared/validation/consumer.schemas.js"; - -type ProjectDeviceForLinking = { - displayName: string; - category: string | null; - quantity: number; - installedPowerPerUnitKw: number; - demandFactor: number; - phaseCount: number | null; - powerFactor: number | null; - note: string | null; -}; - -export function applyLinkedProjectDeviceValues( - input: CreateConsumerInput, - projectDevice: ProjectDeviceForLinking | null -): CreateConsumerInput { - if (!input.projectDeviceId || !input.isLinkedToDevice || !projectDevice) { - return input; - } - - return { - ...input, - name: projectDevice.displayName, - category: projectDevice.category ?? undefined, - quantity: projectDevice.quantity, - installedPowerPerUnitKw: projectDevice.installedPowerPerUnitKw, - demandFactor: projectDevice.demandFactor, - phaseCount: - projectDevice.phaseCount === 1 || projectDevice.phaseCount === 3 - ? (projectDevice.phaseCount as 1 | 3) - : undefined, - powerFactor: projectDevice.powerFactor ?? undefined, - note: projectDevice.note ?? undefined, - }; -} diff --git a/src/domain/services/legacy-consumer-migration.service.ts b/src/domain/services/legacy-consumer-migration.service.ts index 3c5bb84..747a390 100644 --- a/src/domain/services/legacy-consumer-migration.service.ts +++ b/src/domain/services/legacy-consumer-migration.service.ts @@ -1,7 +1,6 @@ import { CircuitRepository } from "../../db/repositories/circuit.repository.js"; import { CircuitListRepository } from "../../db/repositories/circuit-list.repository.js"; import { CircuitSectionRepository } from "../../db/repositories/circuit-section.repository.js"; -import { ConsumerRepository } from "../../db/repositories/consumer.repository.js"; import { LegacyConsumerMigrationRepository, type LegacyMigrationCircuitPersistenceInput, @@ -14,7 +13,11 @@ import { normalizeCircuitNumber, } from "./legacy-consumer-migration-planner.js"; -type LegacyConsumerRow = Awaited>[number]; +type LegacyConsumerRow = Awaited< + ReturnType< + LegacyConsumerMigrationRepository["listSourceConsumersByCircuitList"] + > +>[number]; function parseEquipmentSequence(equipmentIdentifier: string, prefix: string): number | null { if (!equipmentIdentifier.startsWith(prefix)) { @@ -32,7 +35,6 @@ export class LegacyConsumerMigrationService { private readonly circuitListRepository = new CircuitListRepository(); private readonly sectionRepository = new CircuitSectionRepository(); private readonly circuitRepository = new CircuitRepository(); - private readonly consumerRepository = new ConsumerRepository(); private readonly roomRepository = new RoomRepository(); constructor( @@ -60,7 +62,10 @@ export class LegacyConsumerMigrationService { existingCircuits.map((circuit) => circuit.equipmentIdentifier.toUpperCase()) ); - const legacyConsumers = await this.consumerRepository.listByCircuitList(circuitListId); + const legacyConsumers = + await this.migrationRepository.listSourceConsumersByCircuitList( + circuitListId + ); const rooms = await this.roomRepository.listByProject(projectId); const roomById = new Map(rooms.map((room) => [room.id, room])); diff --git a/src/domain/services/power-balance.service.ts b/src/domain/services/power-balance.service.ts deleted file mode 100644 index 1cdf9ca..0000000 --- a/src/domain/services/power-balance.service.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { - calculateCurrentA, - calculateDemandPowerKw, - calculateInstalledPowerKw, -} from "../calculations/power-calculation.js"; -import type { Consumer } from "../models/consumer.model.js"; - -export interface ConsumerWithCalculatedValues extends Consumer { - installedPowerKw: number; - demandPowerKw: number; - effectiveVoltageV?: number; - currentA?: number; -} - -export interface ProjectVoltageDefaults { - singlePhaseVoltageV: number; - threePhaseVoltageV: number; -} - -export class PowerBalanceService { - enrichConsumer( - consumer: Consumer, - projectVoltageDefaults?: ProjectVoltageDefaults - ): ConsumerWithCalculatedValues { - const installedPowerKw = calculateInstalledPowerKw({ - quantity: consumer.quantity, - installedPowerPerUnitKw: consumer.installedPowerPerUnitKw, - demandFactor: consumer.demandFactor, - }); - const demandPowerKw = calculateDemandPowerKw({ - quantity: consumer.quantity, - installedPowerPerUnitKw: consumer.installedPowerPerUnitKw, - demandFactor: consumer.demandFactor, - }); - - const effectiveVoltageV = - consumer.voltageV ?? - (consumer.phaseCount === 1 - ? projectVoltageDefaults?.singlePhaseVoltageV - : consumer.phaseCount === 3 - ? projectVoltageDefaults?.threePhaseVoltageV - : undefined); - - let currentA: number | undefined; - if (effectiveVoltageV && consumer.phaseCount && consumer.powerFactor) { - currentA = calculateCurrentA({ - demandPowerKw, - voltageV: effectiveVoltageV, - phaseCount: consumer.phaseCount, - powerFactor: consumer.powerFactor, - }); - } - - return { - ...consumer, - installedPowerKw, - demandPowerKw, - effectiveVoltageV, - currentA, - }; - } -} diff --git a/src/server/controllers/consumer.controller.ts b/src/server/controllers/consumer.controller.ts deleted file mode 100644 index dddb584..0000000 --- a/src/server/controllers/consumer.controller.ts +++ /dev/null @@ -1,394 +0,0 @@ -import type { Request, Response } from "express"; -import { db } from "../../db/client.js"; -import { CircuitListRepository } from "../../db/repositories/circuit-list.repository.js"; -import { ConsumerRepository } from "../../db/repositories/consumer.repository.js"; -import { DistributionBoardRepository } from "../../db/repositories/distribution-board.repository.js"; -import { FloorRepository } from "../../db/repositories/floor.repository.js"; -import { ProjectDeviceRepository } from "../../db/repositories/project-device.repository.js"; -import { ProjectRepository } from "../../db/repositories/project.repository.js"; -import { RoomRepository } from "../../db/repositories/room.repository.js"; -import type { Consumer } from "../../domain/models/consumer.model.js"; -import { applyLinkedProjectDeviceValues } from "../../domain/services/consumer-linking.service.js"; -import { PowerBalanceService } from "../../domain/services/power-balance.service.js"; -import { - type CreateConsumerInput, - createConsumerSchema, - updateConsumerSchema, -} from "../../shared/validation/consumer.schemas.js"; - -const circuitListRepository = new CircuitListRepository(); -const consumerRepository = new ConsumerRepository(); -const distributionBoardRepository = new DistributionBoardRepository(db); -const floorRepository = new FloorRepository(); -const projectDeviceRepository = new ProjectDeviceRepository(); -const projectRepository = new ProjectRepository(); -const roomRepository = new RoomRepository(); -const powerBalanceService = new PowerBalanceService(); - -type ConsumerRow = { - id: string; - projectId: string; - distributionBoardId: string | null; - circuitListId: string | null; - projectDeviceId: string | null; - isLinkedToDevice: number; - roomId: string | null; - circuitNumber: string | null; - description: string | null; - name: string; - category: string | null; - deviceType: string | null; - phaseType: string | null; - tradeOrCostGroup: string | null; - group: string | null; - protectionType: string | null; - protectionRatedCurrent: number | null; - protectionCharacteristic: string | null; - cableType: string | null; - cableCrossSection: string | null; - comment: string | null; - quantity: number; - installedPowerPerUnitKw: number; - demandFactor: number; - voltageV: number | null; - phaseCount: number | null; - powerFactor: number | null; - note: string | null; -}; - -async function validateDistributionBoardOwnership( - projectId: string, - distributionBoardId: string | undefined -) { - if (!distributionBoardId) { - return true; - } - return distributionBoardRepository.existsInProject(projectId, distributionBoardId); -} - -async function validateRoomOwnership(projectId: string, roomId: string | undefined) { - if (!roomId) { - return true; - } - return roomRepository.existsInProject(projectId, roomId); -} - -async function validateProjectDeviceOwnership(projectId: string, projectDeviceId: string | undefined) { - if (!projectDeviceId) { - return true; - } - const row = await projectDeviceRepository.findById(projectId, projectDeviceId); - return Boolean(row); -} - -async function applyDeviceLinkIfNeeded(input: CreateConsumerInput): Promise { - if (!input.projectDeviceId || !input.isLinkedToDevice) { - return input; - } - const device = await projectDeviceRepository.findById(input.projectId, input.projectDeviceId); - return applyLinkedProjectDeviceValues(input, device); -} - -async function resolveCircuitScope(input: { - projectId: string; - distributionBoardId?: string; - circuitListId?: string; -}) { - let distributionBoardId = input.distributionBoardId; - let circuitListId = input.circuitListId; - - if (distributionBoardId) { - const linkedList = await circuitListRepository.findByDistributionBoardId( - input.projectId, - distributionBoardId - ); - if (!linkedList) { - return { ok: false as const, error: "No circuit list found for the provided distribution board." }; - } - if (circuitListId && circuitListId !== linkedList.id) { - return { - ok: false as const, - error: "Circuit list does not match the provided distribution board.", - }; - } - circuitListId = linkedList.id; - } - - if (circuitListId) { - const list = await circuitListRepository.findById(input.projectId, circuitListId); - if (!list) { - return { ok: false as const, error: "Circuit list does not belong to the provided project." }; - } - if (distributionBoardId && distributionBoardId !== list.distributionBoardId) { - return { - ok: false as const, - error: "Circuit list does not match the provided distribution board.", - }; - } - distributionBoardId = list.distributionBoardId; - } - - return { - ok: true as const, - distributionBoardId, - circuitListId, - }; -} - -function buildConsumerFromRow( - row: ConsumerRow, - roomById: Map, - floorById: Map -): Consumer { - const room = row.roomId ? roomById.get(row.roomId) : undefined; - const floor = room?.floorId ? floorById.get(room.floorId) : undefined; - - return { - id: row.id, - projectId: row.projectId, - distributionBoardId: row.distributionBoardId ?? undefined, - circuitListId: row.circuitListId ?? undefined, - projectDeviceId: row.projectDeviceId ?? undefined, - isLinkedToDevice: Boolean(row.isLinkedToDevice), - roomId: row.roomId ?? undefined, - roomNumber: room?.roomNumber, - roomName: room?.roomName, - floorId: room?.floorId ?? undefined, - floorName: floor?.name, - circuitNumber: row.circuitNumber ?? undefined, - description: row.description ?? undefined, - name: row.name, - category: row.category ?? undefined, - deviceType: row.deviceType ?? undefined, - phaseType: row.phaseType ?? undefined, - tradeOrCostGroup: row.tradeOrCostGroup ?? undefined, - group: row.group ?? undefined, - protectionType: row.protectionType ?? undefined, - protectionRatedCurrent: row.protectionRatedCurrent ?? undefined, - protectionCharacteristic: row.protectionCharacteristic ?? undefined, - cableType: row.cableType ?? undefined, - cableCrossSection: row.cableCrossSection ?? undefined, - comment: row.comment ?? undefined, - quantity: row.quantity, - installedPowerPerUnitKw: row.installedPowerPerUnitKw, - demandFactor: row.demandFactor, - voltageV: row.voltageV ?? undefined, - phaseCount: row.phaseCount === 1 || row.phaseCount === 3 ? row.phaseCount : undefined, - powerFactor: row.powerFactor ?? undefined, - note: row.note ?? undefined, - }; -} - -export async function listConsumersByProject(req: Request, res: Response) { - const { projectId } = req.params; - if (typeof projectId !== "string") { - return res.status(400).json({ error: "Invalid projectId" }); - } - - const [rows, project, floors, rooms] = await Promise.all([ - consumerRepository.listByProject(projectId), - projectRepository.findById(projectId), - floorRepository.listByProject(projectId), - roomRepository.listByProject(projectId), - ]); - - const roomById = new Map( - rooms.map((room) => [room.id, { floorId: room.floorId, roomName: room.roomName, roomNumber: room.roomNumber }]) - ); - const floorById = new Map(floors.map((floor) => [floor.id, { name: floor.name }])); - - const projectVoltageDefaults = project - ? { - singlePhaseVoltageV: project.singlePhaseVoltageV, - threePhaseVoltageV: project.threePhaseVoltageV, - } - : undefined; - - const enriched = rows.map((row) => - powerBalanceService.enrichConsumer( - buildConsumerFromRow(row as ConsumerRow, roomById, floorById), - projectVoltageDefaults - ) - ); - - return res.json(enriched); -} - -export async function createConsumer(req: Request, res: Response) { - const parsed = createConsumerSchema.safeParse(req.body); - if (!parsed.success) { - return res.status(400).json({ error: parsed.error.flatten() }); - } - - const normalizedData: CreateConsumerInput = { - ...parsed.data, - name: parsed.data.name?.trim() || "Unbenannter Eintrag", - quantity: parsed.data.quantity ?? 0, - installedPowerPerUnitKw: parsed.data.installedPowerPerUnitKw ?? 0, - demandFactor: parsed.data.demandFactor ?? 1, - }; - - const [hasValidDistributionBoard, hasValidRoom, hasValidProjectDevice] = await Promise.all([ - validateDistributionBoardOwnership(normalizedData.projectId, normalizedData.distributionBoardId), - validateRoomOwnership(normalizedData.projectId, normalizedData.roomId), - validateProjectDeviceOwnership(normalizedData.projectId, normalizedData.projectDeviceId), - ]); - if (!hasValidDistributionBoard) { - return res - .status(400) - .json({ error: "Distribution board does not belong to the provided project." }); - } - if (!hasValidRoom) { - return res.status(400).json({ error: "Room does not belong to the provided project." }); - } - if (!hasValidProjectDevice) { - return res.status(400).json({ error: "Project device does not belong to the provided project." }); - } - if (normalizedData.isLinkedToDevice && !normalizedData.projectDeviceId) { - return res.status(400).json({ error: "Linked entries require a projectDeviceId." }); - } - - const resolvedScope = await resolveCircuitScope({ - projectId: normalizedData.projectId, - distributionBoardId: normalizedData.distributionBoardId, - circuitListId: normalizedData.circuitListId, - }); - if (!resolvedScope.ok) { - return res.status(400).json({ error: resolvedScope.error }); - } - - const payload = await applyDeviceLinkIfNeeded({ - ...normalizedData, - distributionBoardId: resolvedScope.distributionBoardId, - circuitListId: resolvedScope.circuitListId, - description: normalizedData.description ?? normalizedData.name, - }); - - const created = await consumerRepository.create(payload); - const [project, floors, rooms] = await Promise.all([ - projectRepository.findById(normalizedData.projectId), - floorRepository.listByProject(normalizedData.projectId), - roomRepository.listByProject(normalizedData.projectId), - ]); - const roomById = new Map( - rooms.map((room) => [room.id, { floorId: room.floorId, roomName: room.roomName, roomNumber: room.roomNumber }]) - ); - const floorById = new Map(floors.map((floor) => [floor.id, { name: floor.name }])); - - const enriched = powerBalanceService.enrichConsumer( - { - ...(created as Consumer), - description: created.description ?? created.name, - roomNumber: created.roomId ? roomById.get(created.roomId)?.roomNumber : undefined, - roomName: created.roomId ? roomById.get(created.roomId)?.roomName : undefined, - floorId: created.roomId ? roomById.get(created.roomId)?.floorId ?? undefined : undefined, - floorName: - created.roomId && roomById.get(created.roomId)?.floorId - ? floorById.get(roomById.get(created.roomId)!.floorId as string)?.name - : undefined, - }, - project - ? { - singlePhaseVoltageV: project.singlePhaseVoltageV, - threePhaseVoltageV: project.threePhaseVoltageV, - } - : undefined - ); - - return res.status(201).json(enriched); -} - -export async function updateConsumer(req: Request, res: Response) { - const { consumerId } = req.params; - if (typeof consumerId !== "string") { - return res.status(400).json({ error: "Invalid consumerId" }); - } - - const parsed = updateConsumerSchema.safeParse(req.body); - if (!parsed.success) { - return res.status(400).json({ error: parsed.error.flatten() }); - } - - const normalizedData: CreateConsumerInput = { - ...parsed.data, - name: parsed.data.name?.trim() || "Unbenannter Eintrag", - quantity: parsed.data.quantity ?? 0, - installedPowerPerUnitKw: parsed.data.installedPowerPerUnitKw ?? 0, - demandFactor: parsed.data.demandFactor ?? 1, - }; - - const [hasValidDistributionBoard, hasValidRoom, hasValidProjectDevice] = await Promise.all([ - validateDistributionBoardOwnership(normalizedData.projectId, normalizedData.distributionBoardId), - validateRoomOwnership(normalizedData.projectId, normalizedData.roomId), - validateProjectDeviceOwnership(normalizedData.projectId, normalizedData.projectDeviceId), - ]); - if (!hasValidDistributionBoard) { - return res - .status(400) - .json({ error: "Distribution board does not belong to the provided project." }); - } - if (!hasValidRoom) { - return res.status(400).json({ error: "Room does not belong to the provided project." }); - } - if (!hasValidProjectDevice) { - return res.status(400).json({ error: "Project device does not belong to the provided project." }); - } - if (normalizedData.isLinkedToDevice && !normalizedData.projectDeviceId) { - return res.status(400).json({ error: "Linked entries require a projectDeviceId." }); - } - - const resolvedScope = await resolveCircuitScope({ - projectId: normalizedData.projectId, - distributionBoardId: normalizedData.distributionBoardId, - circuitListId: normalizedData.circuitListId, - }); - if (!resolvedScope.ok) { - return res.status(400).json({ error: resolvedScope.error }); - } - - const payload = await applyDeviceLinkIfNeeded({ - ...normalizedData, - distributionBoardId: resolvedScope.distributionBoardId, - circuitListId: resolvedScope.circuitListId, - description: normalizedData.description ?? normalizedData.name, - }); - - await consumerRepository.update(consumerId, payload); - - const row = await consumerRepository.findById(consumerId); - if (!row) { - return res.status(404).json({ error: "Consumer not found" }); - } - - const [project, floors, rooms] = await Promise.all([ - projectRepository.findById(row.projectId), - floorRepository.listByProject(row.projectId), - roomRepository.listByProject(row.projectId), - ]); - const roomById = new Map( - rooms.map((room) => [room.id, { floorId: room.floorId, roomName: room.roomName, roomNumber: room.roomNumber }]) - ); - const floorById = new Map(floors.map((floor) => [floor.id, { name: floor.name }])); - - const enriched = powerBalanceService.enrichConsumer( - buildConsumerFromRow(row as ConsumerRow, roomById, floorById), - project - ? { - singlePhaseVoltageV: project.singlePhaseVoltageV, - threePhaseVoltageV: project.threePhaseVoltageV, - } - : undefined - ); - - return res.json(enriched); -} - -export async function deleteConsumer(req: Request, res: Response) { - const { consumerId } = req.params; - if (typeof consumerId !== "string") { - return res.status(400).json({ error: "Invalid consumerId" }); - } - - await consumerRepository.delete(consumerId); - return res.status(204).send(); -} diff --git a/src/server/controllers/distribution-board.controller.ts b/src/server/controllers/distribution-board.controller.ts index f2db10b..dcd76c8 100644 --- a/src/server/controllers/distribution-board.controller.ts +++ b/src/server/controllers/distribution-board.controller.ts @@ -1,7 +1,7 @@ import type { Request, Response } from "express"; import { db } from "../../db/client.js"; import { DistributionBoardRepository } from "../../db/repositories/distribution-board.repository.js"; -import { createDistributionBoardSchema } from "../../shared/validation/consumer.schemas.js"; +import { createDistributionBoardSchema } from "../../shared/validation/project-structure.schemas.js"; const distributionBoardRepository = new DistributionBoardRepository(db); diff --git a/src/server/controllers/floor.controller.ts b/src/server/controllers/floor.controller.ts index 232a068..fbda8b3 100644 --- a/src/server/controllers/floor.controller.ts +++ b/src/server/controllers/floor.controller.ts @@ -1,6 +1,6 @@ import type { Request, Response } from "express"; import { FloorRepository } from "../../db/repositories/floor.repository.js"; -import { createFloorSchema } from "../../shared/validation/consumer.schemas.js"; +import { createFloorSchema } from "../../shared/validation/project-structure.schemas.js"; const floorRepository = new FloorRepository(); diff --git a/src/server/controllers/project.controller.ts b/src/server/controllers/project.controller.ts index f9739a9..ca64f29 100644 --- a/src/server/controllers/project.controller.ts +++ b/src/server/controllers/project.controller.ts @@ -3,7 +3,7 @@ import { ProjectRepository } from "../../db/repositories/project.repository.js"; import { createProjectSchema, updateProjectSettingsSchema, -} from "../../shared/validation/consumer.schemas.js"; +} from "../../shared/validation/project-structure.schemas.js"; const projectRepository = new ProjectRepository(); diff --git a/src/server/controllers/room.controller.ts b/src/server/controllers/room.controller.ts index 463bb73..654eb77 100644 --- a/src/server/controllers/room.controller.ts +++ b/src/server/controllers/room.controller.ts @@ -1,7 +1,7 @@ import type { Request, Response } from "express"; import { FloorRepository } from "../../db/repositories/floor.repository.js"; import { RoomRepository } from "../../db/repositories/room.repository.js"; -import { createRoomSchema } from "../../shared/validation/consumer.schemas.js"; +import { createRoomSchema } from "../../shared/validation/project-structure.schemas.js"; const floorRepository = new FloorRepository(); const roomRepository = new RoomRepository(); diff --git a/src/server/index.ts b/src/server/index.ts index c61643a..00778b7 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -2,7 +2,6 @@ import express from "express"; import { circuitDeviceRowRouter } from "./routes/circuit-device-row.routes.js"; import { circuitRouter } from "./routes/circuit.routes.js"; import { circuitSectionRouter } from "./routes/circuit-section.routes.js"; -import { consumerRouter } from "./routes/consumer.routes.js"; import { globalDeviceRouter } from "./routes/global-device.routes.js"; import { projectDeviceRouter } from "./routes/project-device.routes.js"; import { projectRouter } from "./routes/project.routes.js"; @@ -21,7 +20,6 @@ app.use("/api/projects", projectRouter); app.use("/api", circuitRouter); app.use("/api", circuitDeviceRowRouter); app.use("/api", circuitSectionRouter); -app.use("/api/consumers", consumerRouter); app.use("/api/global-devices", globalDeviceRouter); app.use("/api/project-devices", projectDeviceRouter); diff --git a/src/server/routes/consumer.routes.ts b/src/server/routes/consumer.routes.ts deleted file mode 100644 index f1a5e0a..0000000 --- a/src/server/routes/consumer.routes.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Router } from "express"; -import { - createConsumer, - deleteConsumer, - listConsumersByProject, - updateConsumer, -} from "../controllers/consumer.controller.js"; - -export const consumerRouter = Router(); - -consumerRouter.get("/projects/:projectId", listConsumersByProject); -consumerRouter.post("/", createConsumer); -consumerRouter.put("/:consumerId", updateConsumer); -consumerRouter.delete("/:consumerId", deleteConsumer); diff --git a/src/shared/constants/consumer-option-lists.ts b/src/shared/constants/consumer-option-lists.ts deleted file mode 100644 index 6762209..0000000 --- a/src/shared/constants/consumer-option-lists.ts +++ /dev/null @@ -1,49 +0,0 @@ -export const deviceTypeOptions = [ - "Beleuchtung", - "Steckdose", - "Heizung", - "Kühlung", - "Lüftung", - "Antrieb", - "Sicherheit", - "IT", - "Sonstiges", -] as const; - -export const phaseTypeOptions = ["1-phasig", "3-phasig"] as const; - -export const tradeOrCostGroupOptions = [ - "KG 440 Starkstromanlagen", - "KG 450 Fernmelde- und informationstechnische Anlagen", - "KG 460 Förderanlagen", - "KG 470 Nutzungsspezifische Anlagen", - "KG 480 Gebäude- und Anlagenautomation", - "Sonstiges", -] as const; - -export const consumerGroupOptions = [ - "Allgemein", - "Notstrom", - "Sicherheitsstrom", - "USV", - "Technik", - "Reserve", -] as const; - -export const protectionTypeOptions = ["LS", "Schmelzsicherung", "Leistungsschalter", "FI/LS"] as const; - -export const protectionCharacteristicOptions = ["B", "C", "D", "K", "Z"] as const; - -export const cableTypeOptions = ["NYM-J", "NYY-J", "H07RN-F", "NHXMH-J", "Sonstiges"] as const; - -export const cableCrossSectionOptions = [ - "1,5 mm²", - "2,5 mm²", - "4 mm²", - "6 mm²", - "10 mm²", - "16 mm²", - "25 mm²", - "35 mm²", - "50 mm²", -] as const; diff --git a/src/shared/validation/consumer.schemas.ts b/src/shared/validation/consumer.schemas.ts deleted file mode 100644 index ab8b0d9..0000000 --- a/src/shared/validation/consumer.schemas.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { z } from "zod"; -import { - cableCrossSectionOptions, - cableTypeOptions, - consumerGroupOptions, - deviceTypeOptions, - phaseTypeOptions, - protectionCharacteristicOptions, - protectionTypeOptions, - tradeOrCostGroupOptions, -} from "../constants/consumer-option-lists.js"; - -export const createConsumerSchema = z.object({ - projectId: z.string().min(1), - distributionBoardId: z.string().min(1).optional(), - circuitListId: z.string().min(1).optional(), - projectDeviceId: z.string().min(1).optional(), - isLinkedToDevice: z.boolean().optional(), - roomId: z.string().min(1).optional(), - circuitNumber: z.string().optional(), - description: z.string().optional(), - name: z.string().optional(), - category: z.string().optional(), - deviceType: z.enum(deviceTypeOptions).optional(), - phaseType: z.enum(phaseTypeOptions).optional(), - tradeOrCostGroup: z.enum(tradeOrCostGroupOptions).optional(), - group: z.enum(consumerGroupOptions).optional(), - protectionType: z.enum(protectionTypeOptions).optional(), - protectionRatedCurrent: z.number().min(0).optional(), - protectionCharacteristic: z.enum(protectionCharacteristicOptions).optional(), - cableType: z.enum(cableTypeOptions).optional(), - cableCrossSection: z.enum(cableCrossSectionOptions).optional(), - comment: z.string().optional(), - quantity: z.number().min(0).optional(), - installedPowerPerUnitKw: z.number().min(0).optional(), - demandFactor: z.number().min(0).max(1).optional(), - voltageV: z.number().positive().optional(), - phaseCount: z.union([z.literal(1), z.literal(3)]).optional(), - powerFactor: z.number().min(0).max(1).optional(), - note: z.string().optional(), -}); - -export const updateConsumerSchema = createConsumerSchema; - -export const createProjectSchema = z.object({ - name: z.string().min(1), - singlePhaseVoltageV: z.number().positive().optional(), - threePhaseVoltageV: z.number().positive().optional(), -}); - -export const updateProjectSettingsSchema = z.object({ - singlePhaseVoltageV: z.number().positive(), - threePhaseVoltageV: z.number().positive(), -}); - -export const createDistributionBoardSchema = z.object({ - name: z.string().min(1), -}); - -export const createFloorSchema = z.object({ - name: z.string().min(1), -}); - -export const createRoomSchema = z.object({ - floorId: z.string().min(1).optional(), - roomNumber: z.string().min(1), - roomName: z.string().min(1), -}); - -export type CreateConsumerInput = z.infer; -export type CreateProjectInput = z.infer; -export type UpdateProjectSettingsInput = z.infer; -export type CreateDistributionBoardInput = z.infer; -export type UpdateConsumerInput = z.infer; -export type CreateFloorInput = z.infer; -export type CreateRoomInput = z.infer; diff --git a/src/shared/validation/project-structure.schemas.ts b/src/shared/validation/project-structure.schemas.ts new file mode 100644 index 0000000..a47e775 --- /dev/null +++ b/src/shared/validation/project-structure.schemas.ts @@ -0,0 +1,36 @@ +import { z } from "zod"; + +export const createProjectSchema = z.object({ + name: z.string().min(1), + singlePhaseVoltageV: z.number().positive().optional(), + threePhaseVoltageV: z.number().positive().optional(), +}); + +export const updateProjectSettingsSchema = z.object({ + singlePhaseVoltageV: z.number().positive(), + threePhaseVoltageV: z.number().positive(), +}); + +export const createDistributionBoardSchema = z.object({ + name: z.string().min(1), +}); + +export const createFloorSchema = z.object({ + name: z.string().min(1), +}); + +export const createRoomSchema = z.object({ + floorId: z.string().min(1).optional(), + roomNumber: z.string().min(1), + roomName: z.string().min(1), +}); + +export type CreateProjectInput = z.infer; +export type UpdateProjectSettingsInput = z.infer< + typeof updateProjectSettingsSchema +>; +export type CreateDistributionBoardInput = z.infer< + typeof createDistributionBoardSchema +>; +export type CreateFloorInput = z.infer; +export type CreateRoomInput = z.infer; diff --git a/tests/consumer-linking.service.test.ts b/tests/consumer-linking.service.test.ts deleted file mode 100644 index 37f8009..0000000 --- a/tests/consumer-linking.service.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -import assert from "node:assert/strict"; -import { describe, it } from "node:test"; -import { applyLinkedProjectDeviceValues } from "../src/domain/services/consumer-linking.service.js"; - -describe("consumer linking service", () => { - it("applies project-device values to linked consumers", () => { - const input = { - projectId: "p1", - projectDeviceId: "d1", - isLinkedToDevice: true, - name: "Alt", - quantity: 1, - installedPowerPerUnitKw: 0.2, - demandFactor: 0.8, - }; - - const result = applyLinkedProjectDeviceValues(input, { - displayName: "Kaffeemaschine", - category: "Küche", - quantity: 3, - installedPowerPerUnitKw: 1.5, - demandFactor: 0.6, - phaseCount: 3, - powerFactor: 0.9, - note: "aus Vorlage", - }); - - assert.equal(result.name, "Kaffeemaschine"); - assert.equal(result.category, "Küche"); - assert.equal(result.quantity, 3); - assert.equal(result.installedPowerPerUnitKw, 1.5); - assert.equal(result.demandFactor, 0.6); - assert.equal(result.phaseCount, 3); - assert.equal(result.powerFactor, 0.9); - assert.equal(result.note, "aus Vorlage"); - }); - - it("keeps values unchanged when entry is not linked", () => { - const input = { - projectId: "p1", - projectDeviceId: "d1", - isLinkedToDevice: false, - name: "Eigener Name", - quantity: 2, - installedPowerPerUnitKw: 0.5, - demandFactor: 1, - }; - - const result = applyLinkedProjectDeviceValues(input, { - displayName: "Vorlage", - category: "Test", - quantity: 9, - installedPowerPerUnitKw: 9, - demandFactor: 0.2, - phaseCount: 1, - powerFactor: 1, - note: null, - }); - - assert.equal(result.name, "Eigener Name"); - assert.equal(result.quantity, 2); - assert.equal(result.installedPowerPerUnitKw, 0.5); - assert.equal(result.demandFactor, 1); - }); -}); diff --git a/tests/consumer-schema-options.test.ts b/tests/consumer-schema-options.test.ts deleted file mode 100644 index 6ab808a..0000000 --- a/tests/consumer-schema-options.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import assert from "node:assert/strict"; -import { describe, it } from "node:test"; -import { createConsumerSchema } from "../src/shared/validation/consumer.schemas.js"; - -describe("consumer schema fixed option lists", () => { - it("accepts valid predefined domain values", () => { - const parsed = createConsumerSchema.safeParse({ - projectId: "p1", - name: "Test", - quantity: 1, - installedPowerPerUnitKw: 1, - demandFactor: 1, - deviceType: "Beleuchtung", - phaseType: "3-phasig", - tradeOrCostGroup: "KG 440 Starkstromanlagen", - group: "Technik", - protectionType: "LS", - protectionCharacteristic: "C", - cableType: "NYM-J", - cableCrossSection: "2,5 mm²", - }); - - assert.equal(parsed.success, true); - }); - - it("rejects unknown domain values", () => { - const parsed = createConsumerSchema.safeParse({ - projectId: "p1", - name: "Test", - quantity: 1, - installedPowerPerUnitKw: 1, - demandFactor: 1, - deviceType: "Irgendwas", - }); - - assert.equal(parsed.success, false); - }); -}); diff --git a/tests/legacy-consumer-migration.repository.test.ts b/tests/legacy-consumer-migration.repository.test.ts index 29248e5..b9ed99e 100644 --- a/tests/legacy-consumer-migration.repository.test.ts +++ b/tests/legacy-consumer-migration.repository.test.ts @@ -126,6 +126,12 @@ describe("legacy consumer migration repository", () => { .run(); const repository = new LegacyConsumerMigrationRepository(context.db); + assert.deepEqual( + (await repository.listSourceConsumersByCircuitList(circuitListId)).map( + (consumer) => consumer.id + ), + ["consumer-1"] + ); assert.deepEqual( (await repository.listUnmigratedConsumers()).sort((left, right) => left.id.localeCompare(right.id) diff --git a/tests/power-calculation.test.js b/tests/power-calculation.test.js deleted file mode 100644 index 0fb8d95..0000000 --- a/tests/power-calculation.test.js +++ /dev/null @@ -1,56 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const vitest_1 = require("vitest"); -const power_calculation_js_1 = require("../src/domain/calculations/power-calculation.js"); -(0, vitest_1.describe)("power calculation", () => { - (0, vitest_1.it)("calculates installed power", () => { - const result = (0, power_calculation_js_1.calculateInstalledPowerKw)({ - quantity: 4, - installedPowerPerUnitKw: 1.2, - demandFactor: 0.8, - }); - (0, vitest_1.expect)(result).toBeCloseTo(4.8); - }); - (0, vitest_1.it)("calculates demand power", () => { - const result = (0, power_calculation_js_1.calculateDemandPowerKw)({ - quantity: 4, - installedPowerPerUnitKw: 1.2, - demandFactor: 0.8, - }); - (0, vitest_1.expect)(result).toBeCloseTo(3.84); - }); - (0, vitest_1.it)("handles zero quantity", () => { - const result = (0, power_calculation_js_1.calculateInstalledPowerKw)({ - quantity: 0, - installedPowerPerUnitKw: 3, - demandFactor: 0.9, - }); - (0, vitest_1.expect)(result).toBe(0); - }); - (0, vitest_1.it)("handles zero demand factor", () => { - const result = (0, power_calculation_js_1.calculateDemandPowerKw)({ - quantity: 5, - installedPowerPerUnitKw: 2, - demandFactor: 0, - }); - (0, vitest_1.expect)(result).toBe(0); - }); - (0, vitest_1.it)("calculates single-phase current", () => { - const current = (0, power_calculation_js_1.calculateCurrentA)({ - demandPowerKw: 2.3, - voltageV: 230, - phaseCount: 1, - powerFactor: 0.95, - }); - (0, vitest_1.expect)(current).toBeCloseTo(10.53, 2); - }); - (0, vitest_1.it)("calculates three-phase current", () => { - const current = (0, power_calculation_js_1.calculateCurrentA)({ - demandPowerKw: 9.5, - voltageV: 400, - phaseCount: 3, - powerFactor: 0.9, - }); - (0, vitest_1.expect)(current).toBeCloseTo(15.23, 2); - }); -}); diff --git a/tests/power-calculation.test.ts b/tests/power-calculation.test.ts deleted file mode 100644 index d44391a..0000000 --- a/tests/power-calculation.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { describe, it } from "node:test"; -import assert from "node:assert/strict"; -import { - calculateCurrentA, - calculateDemandPowerKw, - calculateInstalledPowerKw, -} from "../src/domain/calculations/power-calculation.js"; - -describe("power calculation", () => { - it("calculates installed power", () => { - const result = calculateInstalledPowerKw({ - quantity: 4, - installedPowerPerUnitKw: 1.2, - demandFactor: 0.8, - }); - assert.ok(Math.abs(result - 4.8) < 0.00001); - }); - - it("calculates demand power", () => { - const result = calculateDemandPowerKw({ - quantity: 4, - installedPowerPerUnitKw: 1.2, - demandFactor: 0.8, - }); - assert.ok(Math.abs(result - 3.84) < 0.00001); - }); - - it("handles zero quantity", () => { - const result = calculateInstalledPowerKw({ - quantity: 0, - installedPowerPerUnitKw: 3, - demandFactor: 0.9, - }); - assert.equal(result, 0); - }); - - it("handles zero demand factor", () => { - const result = calculateDemandPowerKw({ - quantity: 5, - installedPowerPerUnitKw: 2, - demandFactor: 0, - }); - assert.equal(result, 0); - }); - - it("calculates single-phase current", () => { - const current = calculateCurrentA({ - demandPowerKw: 2.3, - voltageV: 230, - phaseCount: 1, - powerFactor: 0.95, - }); - assert.ok(Math.abs(current - 10.53) < 0.02); - }); - - it("calculates three-phase current", () => { - const current = calculateCurrentA({ - demandPowerKw: 9.5, - voltageV: 400, - phaseCount: 3, - powerFactor: 0.9, - }); - assert.ok(Math.abs(current - 15.23) < 0.02); - }); -});