forked from jappel/leistungsbilanz-ts
Remove legacy consumer API
This commit is contained in:
parent
04c299e3f2
commit
3fbf3ac622
30 changed files with 76 additions and 1089 deletions
|
|
@ -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<CreateConsumerInput> {
|
||||
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<string, { floorId: string | null; roomName: string; roomNumber: string }>,
|
||||
floorById: Map<string, { name: string }>
|
||||
): 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();
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
Loading…
Add table
Add a link
Reference in a new issue