All first todos completed
This commit is contained in:
@@ -3,11 +3,14 @@ import { CircuitListRepository } from "../../db/repositories/circuit-list.reposi
|
||||
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";
|
||||
@@ -16,6 +19,7 @@ const circuitListRepository = new CircuitListRepository();
|
||||
const consumerRepository = new ConsumerRepository();
|
||||
const distributionBoardRepository = new DistributionBoardRepository();
|
||||
const floorRepository = new FloorRepository();
|
||||
const projectDeviceRepository = new ProjectDeviceRepository();
|
||||
const projectRepository = new ProjectRepository();
|
||||
const roomRepository = new RoomRepository();
|
||||
const powerBalanceService = new PowerBalanceService();
|
||||
@@ -25,6 +29,8 @@ type ConsumerRow = {
|
||||
projectId: string;
|
||||
distributionBoardId: string | null;
|
||||
circuitListId: string | null;
|
||||
projectDeviceId: string | null;
|
||||
isLinkedToDevice: number;
|
||||
roomId: string | null;
|
||||
circuitNumber: string | null;
|
||||
description: string | null;
|
||||
@@ -66,6 +72,22 @@ async function validateRoomOwnership(projectId: string, roomId: string | undefin
|
||||
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;
|
||||
@@ -125,6 +147,8 @@ function buildConsumerFromRow(
|
||||
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,
|
||||
@@ -195,9 +219,18 @@ export async function createConsumer(req: Request, res: Response) {
|
||||
return res.status(400).json({ error: parsed.error.flatten() });
|
||||
}
|
||||
|
||||
const [hasValidDistributionBoard, hasValidRoom] = await Promise.all([
|
||||
validateDistributionBoardOwnership(parsed.data.projectId, parsed.data.distributionBoardId),
|
||||
validateRoomOwnership(parsed.data.projectId, parsed.data.roomId),
|
||||
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
|
||||
@@ -207,28 +240,34 @@ export async function createConsumer(req: Request, res: Response) {
|
||||
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: parsed.data.projectId,
|
||||
distributionBoardId: parsed.data.distributionBoardId,
|
||||
circuitListId: parsed.data.circuitListId,
|
||||
projectId: normalizedData.projectId,
|
||||
distributionBoardId: normalizedData.distributionBoardId,
|
||||
circuitListId: normalizedData.circuitListId,
|
||||
});
|
||||
if (!resolvedScope.ok) {
|
||||
return res.status(400).json({ error: resolvedScope.error });
|
||||
}
|
||||
|
||||
const payload = {
|
||||
...parsed.data,
|
||||
const payload = await applyDeviceLinkIfNeeded({
|
||||
...normalizedData,
|
||||
distributionBoardId: resolvedScope.distributionBoardId,
|
||||
circuitListId: resolvedScope.circuitListId,
|
||||
description: parsed.data.description ?? parsed.data.name,
|
||||
};
|
||||
description: normalizedData.description ?? normalizedData.name,
|
||||
});
|
||||
|
||||
const created = await consumerRepository.create(payload);
|
||||
const [project, floors, rooms] = await Promise.all([
|
||||
projectRepository.findById(parsed.data.projectId),
|
||||
floorRepository.listByProject(parsed.data.projectId),
|
||||
roomRepository.listByProject(parsed.data.projectId),
|
||||
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 }])
|
||||
@@ -269,9 +308,18 @@ export async function updateConsumer(req: Request, res: Response) {
|
||||
return res.status(400).json({ error: parsed.error.flatten() });
|
||||
}
|
||||
|
||||
const [hasValidDistributionBoard, hasValidRoom] = await Promise.all([
|
||||
validateDistributionBoardOwnership(parsed.data.projectId, parsed.data.distributionBoardId),
|
||||
validateRoomOwnership(parsed.data.projectId, parsed.data.roomId),
|
||||
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
|
||||
@@ -281,23 +329,31 @@ export async function updateConsumer(req: Request, res: Response) {
|
||||
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: parsed.data.projectId,
|
||||
distributionBoardId: parsed.data.distributionBoardId,
|
||||
circuitListId: parsed.data.circuitListId,
|
||||
projectId: normalizedData.projectId,
|
||||
distributionBoardId: normalizedData.distributionBoardId,
|
||||
circuitListId: normalizedData.circuitListId,
|
||||
});
|
||||
if (!resolvedScope.ok) {
|
||||
return res.status(400).json({ error: resolvedScope.error });
|
||||
}
|
||||
|
||||
await consumerRepository.update(consumerId, {
|
||||
...parsed.data,
|
||||
const payload = await applyDeviceLinkIfNeeded({
|
||||
...normalizedData,
|
||||
distributionBoardId: resolvedScope.distributionBoardId,
|
||||
circuitListId: resolvedScope.circuitListId,
|
||||
description: parsed.data.description ?? parsed.data.name,
|
||||
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" });
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import type { Request, Response } from "express";
|
||||
import { GlobalDeviceRepository } from "../../db/repositories/global-device.repository.js";
|
||||
import { ProjectDeviceRepository } from "../../db/repositories/project-device.repository.js";
|
||||
import {
|
||||
createGlobalDeviceSchema,
|
||||
updateGlobalDeviceSchema,
|
||||
} from "../../shared/validation/global-device.schemas.js";
|
||||
|
||||
const globalDeviceRepository = new GlobalDeviceRepository();
|
||||
const projectDeviceRepository = new ProjectDeviceRepository();
|
||||
|
||||
export async function listGlobalDevices(_req: Request, res: Response) {
|
||||
const rows = await globalDeviceRepository.list();
|
||||
@@ -50,3 +52,30 @@ export async function deleteGlobalDevice(req: Request, res: Response) {
|
||||
await globalDeviceRepository.delete(globalDeviceId);
|
||||
return res.status(204).send();
|
||||
}
|
||||
|
||||
export async function copyProjectDeviceToGlobal(req: Request, res: Response) {
|
||||
const { projectId, projectDeviceId } = req.params;
|
||||
if (typeof projectId !== "string" || typeof projectDeviceId !== "string") {
|
||||
return res.status(400).json({ error: "Invalid parameters" });
|
||||
}
|
||||
|
||||
const source = await projectDeviceRepository.findById(projectId, projectDeviceId);
|
||||
if (!source) {
|
||||
return res.status(404).json({ error: "Project device not found" });
|
||||
}
|
||||
|
||||
const created = await globalDeviceRepository.create({
|
||||
name: source.name,
|
||||
displayName: source.displayName,
|
||||
category: source.category ?? undefined,
|
||||
quantity: source.quantity,
|
||||
installedPowerPerUnitKw: source.installedPowerPerUnitKw,
|
||||
demandFactor: source.demandFactor,
|
||||
voltageV: source.voltageV ?? undefined,
|
||||
phaseCount: source.phaseCount === 1 || source.phaseCount === 3 ? source.phaseCount : undefined,
|
||||
powerFactor: source.powerFactor ?? undefined,
|
||||
note: source.note ?? undefined,
|
||||
});
|
||||
|
||||
return res.status(201).json(created);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import type { Request, Response } from "express";
|
||||
import { GlobalDeviceRepository } from "../../db/repositories/global-device.repository.js";
|
||||
import { ConsumerRepository } from "../../db/repositories/consumer.repository.js";
|
||||
import { ProjectDeviceRepository } from "../../db/repositories/project-device.repository.js";
|
||||
import {
|
||||
createProjectDeviceSchema,
|
||||
updateProjectDeviceSchema,
|
||||
} from "../../shared/validation/project-device.schemas.js";
|
||||
|
||||
const globalDeviceRepository = new GlobalDeviceRepository();
|
||||
const consumerRepository = new ConsumerRepository();
|
||||
const projectDeviceRepository = new ProjectDeviceRepository();
|
||||
|
||||
export async function listProjectDevicesByProject(req: Request, res: Response) {
|
||||
@@ -41,6 +45,16 @@ export async function updateProjectDevice(req: Request, res: Response) {
|
||||
}
|
||||
|
||||
await projectDeviceRepository.update(projectId, projectDeviceId, parsed.data);
|
||||
await consumerRepository.syncLinkedConsumersFromProjectDevice(projectId, projectDeviceId, {
|
||||
displayName: parsed.data.displayName,
|
||||
category: parsed.data.category,
|
||||
quantity: parsed.data.quantity,
|
||||
installedPowerPerUnitKw: parsed.data.installedPowerPerUnitKw,
|
||||
demandFactor: parsed.data.demandFactor,
|
||||
phaseCount: parsed.data.phaseCount,
|
||||
powerFactor: parsed.data.powerFactor,
|
||||
note: parsed.data.note,
|
||||
});
|
||||
const row = await projectDeviceRepository.findById(projectId, projectDeviceId);
|
||||
if (!row) {
|
||||
return res.status(404).json({ error: "Project device not found" });
|
||||
@@ -57,3 +71,30 @@ export async function deleteProjectDevice(req: Request, res: Response) {
|
||||
await projectDeviceRepository.delete(projectId, projectDeviceId);
|
||||
return res.status(204).send();
|
||||
}
|
||||
|
||||
export async function copyGlobalDeviceToProject(req: Request, res: Response) {
|
||||
const { projectId, globalDeviceId } = req.params;
|
||||
if (typeof projectId !== "string" || typeof globalDeviceId !== "string") {
|
||||
return res.status(400).json({ error: "Invalid parameters" });
|
||||
}
|
||||
|
||||
const source = await globalDeviceRepository.findById(globalDeviceId);
|
||||
if (!source) {
|
||||
return res.status(404).json({ error: "Global device not found" });
|
||||
}
|
||||
|
||||
const created = await projectDeviceRepository.create(projectId, {
|
||||
name: source.name,
|
||||
displayName: source.displayName,
|
||||
category: source.category ?? undefined,
|
||||
quantity: source.quantity,
|
||||
installedPowerPerUnitKw: source.installedPowerPerUnitKw,
|
||||
demandFactor: source.demandFactor,
|
||||
voltageV: source.voltageV ?? undefined,
|
||||
phaseCount: source.phaseCount === 1 || source.phaseCount === 3 ? source.phaseCount : undefined,
|
||||
powerFactor: source.powerFactor ?? undefined,
|
||||
note: source.note ?? undefined,
|
||||
});
|
||||
|
||||
return res.status(201).json(created);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Router } from "express";
|
||||
import {
|
||||
copyProjectDeviceToGlobal,
|
||||
createGlobalDevice,
|
||||
deleteGlobalDevice,
|
||||
listGlobalDevices,
|
||||
@@ -9,6 +10,7 @@ import {
|
||||
export const globalDeviceRouter = Router();
|
||||
|
||||
globalDeviceRouter.get("/", listGlobalDevices);
|
||||
globalDeviceRouter.post("/import-project/:projectId/:projectDeviceId", copyProjectDeviceToGlobal);
|
||||
globalDeviceRouter.post("/", createGlobalDevice);
|
||||
globalDeviceRouter.put("/:globalDeviceId", updateGlobalDevice);
|
||||
globalDeviceRouter.delete("/:globalDeviceId", deleteGlobalDevice);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Router } from "express";
|
||||
import {
|
||||
copyGlobalDeviceToProject,
|
||||
createProjectDevice,
|
||||
deleteProjectDevice,
|
||||
listProjectDevicesByProject,
|
||||
@@ -10,5 +11,6 @@ export const projectDeviceRouter = Router();
|
||||
|
||||
projectDeviceRouter.get("/projects/:projectId", listProjectDevicesByProject);
|
||||
projectDeviceRouter.post("/projects/:projectId", createProjectDevice);
|
||||
projectDeviceRouter.post("/projects/:projectId/import-global/:globalDeviceId", copyGlobalDeviceToProject);
|
||||
projectDeviceRouter.put("/projects/:projectId/:projectDeviceId", updateProjectDevice);
|
||||
projectDeviceRouter.delete("/projects/:projectId/:projectDeviceId", deleteProjectDevice);
|
||||
|
||||
Reference in New Issue
Block a user