Files
leistungsbilanz-ts/src/domain/models/circuit-device-row-structure-project-command.model.ts
T

187 lines
5.8 KiB
TypeScript

import type { SerializedProjectCommand } from "./project-command.model.js";
export const circuitDeviceRowInsertCommandType =
"circuit-device-row.insert" as const;
export const circuitDeviceRowDeleteCommandType =
"circuit-device-row.delete" as const;
export const circuitDeviceRowStructureCommandSchemaVersion = 1 as const;
export interface CircuitDeviceRowSnapshot {
id: string;
circuitId: string;
linkedProjectDeviceId: string | null;
legacyConsumerId: string | null;
sortOrder: number;
name: string;
displayName: string;
phaseType: string | null;
connectionKind: string | null;
costGroup: string | null;
category: string | null;
level: string | null;
roomId: string | null;
roomNumberSnapshot: string | null;
roomNameSnapshot: string | null;
quantity: number;
powerPerUnit: number;
simultaneityFactor: number;
cosPhi: number | null;
remark: string | null;
overriddenFields: string | null;
}
export interface CircuitDeviceRowInsertCommandPayload {
row: CircuitDeviceRowSnapshot;
}
export interface CircuitDeviceRowDeleteCommandPayload {
rowId: string;
expectedCircuitId: string;
}
export interface CircuitDeviceRowInsertProjectCommand
extends SerializedProjectCommand<CircuitDeviceRowInsertCommandPayload> {
schemaVersion: typeof circuitDeviceRowStructureCommandSchemaVersion;
type: typeof circuitDeviceRowInsertCommandType;
}
export interface CircuitDeviceRowDeleteProjectCommand
extends SerializedProjectCommand<CircuitDeviceRowDeleteCommandPayload> {
schemaVersion: typeof circuitDeviceRowStructureCommandSchemaVersion;
type: typeof circuitDeviceRowDeleteCommandType;
}
export type CircuitDeviceRowStructureProjectCommand =
| CircuitDeviceRowInsertProjectCommand
| CircuitDeviceRowDeleteProjectCommand;
export function createCircuitDeviceRowInsertProjectCommand(
row: CircuitDeviceRowSnapshot
): CircuitDeviceRowInsertProjectCommand {
const command: CircuitDeviceRowInsertProjectCommand = {
schemaVersion: circuitDeviceRowStructureCommandSchemaVersion,
type: circuitDeviceRowInsertCommandType,
payload: { row },
};
assertCircuitDeviceRowInsertProjectCommand(command);
return command;
}
export function createCircuitDeviceRowDeleteProjectCommand(
rowId: string,
expectedCircuitId: string
): CircuitDeviceRowDeleteProjectCommand {
const command: CircuitDeviceRowDeleteProjectCommand = {
schemaVersion: circuitDeviceRowStructureCommandSchemaVersion,
type: circuitDeviceRowDeleteCommandType,
payload: { rowId, expectedCircuitId },
};
assertCircuitDeviceRowDeleteProjectCommand(command);
return command;
}
export function assertCircuitDeviceRowInsertProjectCommand(
command: SerializedProjectCommand<unknown>
): asserts command is CircuitDeviceRowInsertProjectCommand {
if (
command.schemaVersion !==
circuitDeviceRowStructureCommandSchemaVersion ||
command.type !== circuitDeviceRowInsertCommandType
) {
throw new Error("Unsupported circuit device-row insert command.");
}
if (!isPlainObject(command.payload)) {
throw new Error("Circuit device-row insert payload must be an object.");
}
const row = command.payload.row;
if (!isPlainObject(row)) {
throw new Error("Circuit device-row insert command requires a row.");
}
assertNonEmptyString(row.id, "row.id");
assertNonEmptyString(row.circuitId, "row.circuitId");
assertNullableString(row.linkedProjectDeviceId, "row.linkedProjectDeviceId");
assertNullableString(row.legacyConsumerId, "row.legacyConsumerId");
assertFiniteNumber(row.sortOrder, "row.sortOrder");
assertNonEmptyString(row.name, "row.name");
assertNonEmptyString(row.displayName, "row.displayName");
for (const field of [
"phaseType",
"connectionKind",
"costGroup",
"category",
"level",
"roomId",
"roomNumberSnapshot",
"roomNameSnapshot",
"remark",
"overriddenFields",
] as const) {
assertNullableString(row[field], `row.${field}`);
}
assertNonNegativeNumber(row.quantity, "row.quantity");
assertNonNegativeNumber(row.powerPerUnit, "row.powerPerUnit");
assertNonNegativeNumber(
row.simultaneityFactor,
"row.simultaneityFactor"
);
if (row.cosPhi !== null) {
assertFiniteNumber(row.cosPhi, "row.cosPhi");
if (row.cosPhi <= 0) {
throw new Error("row.cosPhi must be positive or null.");
}
}
}
export function assertCircuitDeviceRowDeleteProjectCommand(
command: SerializedProjectCommand<unknown>
): asserts command is CircuitDeviceRowDeleteProjectCommand {
if (
command.schemaVersion !==
circuitDeviceRowStructureCommandSchemaVersion ||
command.type !== circuitDeviceRowDeleteCommandType
) {
throw new Error("Unsupported circuit device-row delete command.");
}
if (!isPlainObject(command.payload)) {
throw new Error("Circuit device-row delete payload must be an object.");
}
assertNonEmptyString(command.payload.rowId, "rowId");
assertNonEmptyString(
command.payload.expectedCircuitId,
"expectedCircuitId"
);
}
function assertNonEmptyString(value: unknown, field: string) {
if (typeof value !== "string" || !value.trim()) {
throw new Error(`${field} must be a non-empty string.`);
}
}
function assertNullableString(value: unknown, field: string) {
if (value !== null && typeof value !== "string") {
throw new Error(`${field} must be a string or null.`);
}
}
function assertFiniteNumber(
value: unknown,
field: string
): asserts value is number {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new Error(`${field} must be a finite number.`);
}
}
function assertNonNegativeNumber(value: unknown, field: string) {
assertFiniteNumber(value, field);
if (value < 0) {
throw new Error(`${field} must not be negative.`);
}
}
function isPlainObject(value: unknown): value is Record<string, unknown> {
return value !== null && typeof value === "object" && !Array.isArray(value);
}