forked from jappel/leistungsbilanz-ts
195 lines
5.8 KiB
TypeScript
195 lines
5.8 KiB
TypeScript
import { and, eq } from "drizzle-orm";
|
|
import {
|
|
assertCircuitDeviceRowDeleteProjectCommand,
|
|
assertCircuitDeviceRowInsertProjectCommand,
|
|
circuitDeviceRowDeleteCommandType,
|
|
circuitDeviceRowInsertCommandType,
|
|
createCircuitDeviceRowDeleteProjectCommand,
|
|
createCircuitDeviceRowInsertProjectCommand,
|
|
type CircuitDeviceRowSnapshot,
|
|
type CircuitDeviceRowStructureProjectCommand,
|
|
} from "../../domain/models/circuit-device-row-structure-project-command.model.js";
|
|
import type {
|
|
CircuitDeviceRowStructureProjectCommandStore,
|
|
ExecuteCircuitDeviceRowStructureCommandInput,
|
|
} from "../../domain/ports/circuit-device-row-structure-project-command.store.js";
|
|
import type { AppDatabase } from "../database-context.js";
|
|
import { isElectricalPhaseType } from "../../domain/services/project-voltage.service.js";
|
|
import { circuitDeviceRows } from "../schema/circuit-device-rows.js";
|
|
import { circuitLists } from "../schema/circuit-lists.js";
|
|
import { circuits } from "../schema/circuits.js";
|
|
import {
|
|
assertCircuitDeviceRowReferencesInProject,
|
|
toCircuitDeviceRowInsertValues,
|
|
toCircuitDeviceRowSnapshot,
|
|
} from "./circuit-device-row-structure.persistence.js";
|
|
import { executeProjectCommandTransaction } from "./project-command-transaction.persistence.js";
|
|
import { updateDerivedCircuitVoltage } from "./project-voltage.persistence.js";
|
|
|
|
export class CircuitDeviceRowStructureProjectCommandRepository
|
|
implements CircuitDeviceRowStructureProjectCommandStore
|
|
{
|
|
constructor(private readonly database: AppDatabase) {}
|
|
|
|
execute(input: ExecuteCircuitDeviceRowStructureCommandInput) {
|
|
return executeProjectCommandTransaction(
|
|
this.database,
|
|
input,
|
|
(tx) =>
|
|
this.applyCommand(
|
|
tx,
|
|
input.projectId,
|
|
input.source,
|
|
input.command
|
|
)
|
|
);
|
|
}
|
|
|
|
private applyCommand(
|
|
database: AppDatabase,
|
|
projectId: string,
|
|
source: ExecuteCircuitDeviceRowStructureCommandInput["source"],
|
|
command: CircuitDeviceRowStructureProjectCommand
|
|
): CircuitDeviceRowStructureProjectCommand {
|
|
if (command.type === circuitDeviceRowInsertCommandType) {
|
|
assertCircuitDeviceRowInsertProjectCommand(command);
|
|
return this.insert(
|
|
database,
|
|
projectId,
|
|
source,
|
|
command.payload.row
|
|
);
|
|
}
|
|
if (command.type === circuitDeviceRowDeleteCommandType) {
|
|
assertCircuitDeviceRowDeleteProjectCommand(command);
|
|
return this.delete(
|
|
database,
|
|
projectId,
|
|
command.payload.rowId,
|
|
command.payload.expectedCircuitId
|
|
);
|
|
}
|
|
throw new Error("Unsupported circuit device-row structure command.");
|
|
}
|
|
|
|
private insert(
|
|
database: AppDatabase,
|
|
projectId: string,
|
|
source: ExecuteCircuitDeviceRowStructureCommandInput["source"],
|
|
row: CircuitDeviceRowSnapshot
|
|
) {
|
|
if (source === "user" && !isElectricalPhaseType(row.phaseType)) {
|
|
throw new Error("Device-row phase type is invalid.");
|
|
}
|
|
this.assertCircuitInProject(database, projectId, row.circuitId);
|
|
assertCircuitDeviceRowReferencesInProject(database, projectId, row);
|
|
|
|
const existing = database
|
|
.select({ id: circuitDeviceRows.id })
|
|
.from(circuitDeviceRows)
|
|
.where(eq(circuitDeviceRows.id, row.id))
|
|
.get();
|
|
if (existing) {
|
|
throw new Error("Circuit device-row id already exists.");
|
|
}
|
|
|
|
database
|
|
.insert(circuitDeviceRows)
|
|
.values(toCircuitDeviceRowInsertValues(row))
|
|
.run();
|
|
const circuitUpdate = database
|
|
.update(circuits)
|
|
.set({ isReserve: 0 })
|
|
.where(eq(circuits.id, row.circuitId))
|
|
.run();
|
|
if (circuitUpdate.changes !== 1) {
|
|
throw new Error("Circuit changed before device-row insertion.");
|
|
}
|
|
updateDerivedCircuitVoltage(database, projectId, row.circuitId);
|
|
|
|
return createCircuitDeviceRowDeleteProjectCommand(
|
|
row.id,
|
|
row.circuitId
|
|
);
|
|
}
|
|
|
|
private delete(
|
|
database: AppDatabase,
|
|
projectId: string,
|
|
rowId: string,
|
|
expectedCircuitId: string
|
|
) {
|
|
const row = database
|
|
.select()
|
|
.from(circuitDeviceRows)
|
|
.where(eq(circuitDeviceRows.id, rowId))
|
|
.get();
|
|
if (!row || row.circuitId !== expectedCircuitId) {
|
|
throw new Error(
|
|
"Circuit device row changed before command execution."
|
|
);
|
|
}
|
|
this.assertCircuitInProject(database, projectId, row.circuitId);
|
|
|
|
const result = database
|
|
.delete(circuitDeviceRows)
|
|
.where(
|
|
and(
|
|
eq(circuitDeviceRows.id, rowId),
|
|
eq(circuitDeviceRows.circuitId, expectedCircuitId)
|
|
)
|
|
)
|
|
.run();
|
|
if (result.changes !== 1) {
|
|
throw new Error("Circuit device row could not be deleted.");
|
|
}
|
|
|
|
const remainingRow = database
|
|
.select({ id: circuitDeviceRows.id })
|
|
.from(circuitDeviceRows)
|
|
.where(eq(circuitDeviceRows.circuitId, expectedCircuitId))
|
|
.limit(1)
|
|
.get();
|
|
const circuitUpdate = database
|
|
.update(circuits)
|
|
.set({ isReserve: remainingRow ? 0 : 1 })
|
|
.where(eq(circuits.id, expectedCircuitId))
|
|
.run();
|
|
if (circuitUpdate.changes !== 1) {
|
|
throw new Error("Circuit changed before device-row deletion.");
|
|
}
|
|
updateDerivedCircuitVoltage(
|
|
database,
|
|
projectId,
|
|
expectedCircuitId
|
|
);
|
|
|
|
return createCircuitDeviceRowInsertProjectCommand(
|
|
toCircuitDeviceRowSnapshot(row)
|
|
);
|
|
}
|
|
|
|
private assertCircuitInProject(
|
|
database: AppDatabase,
|
|
projectId: string,
|
|
circuitId: string
|
|
) {
|
|
const circuit = database
|
|
.select({ id: circuits.id })
|
|
.from(circuits)
|
|
.innerJoin(
|
|
circuitLists,
|
|
eq(circuitLists.id, circuits.circuitListId)
|
|
)
|
|
.where(
|
|
and(
|
|
eq(circuits.id, circuitId),
|
|
eq(circuitLists.projectId, projectId)
|
|
)
|
|
)
|
|
.get();
|
|
if (!circuit) {
|
|
throw new Error("Circuit does not belong to project.");
|
|
}
|
|
}
|
|
}
|