283 lines
9.1 KiB
TypeScript
283 lines
9.1 KiB
TypeScript
import crypto from "node:crypto";
|
|
import { and, eq, inArray } from "drizzle-orm";
|
|
import type {
|
|
CircuitDeviceRowTransactionStore,
|
|
CreateCircuitDeviceRowTransactionInput,
|
|
CreateCircuitWithDeviceRowsTransactionInput,
|
|
MoveCircuitDeviceRowsTransactionInput,
|
|
} from "../../domain/ports/circuit-device-row-transaction.store.js";
|
|
import type { AppDatabase } from "../database-context.js";
|
|
import { circuitDeviceRows } from "../schema/circuit-device-rows.js";
|
|
import { circuits } from "../schema/circuits.js";
|
|
import { toCircuitCreateValues } from "./circuit.persistence.js";
|
|
import {
|
|
toCircuitDeviceRowCreateValues,
|
|
} from "./circuit-device-row.persistence.js";
|
|
|
|
export class CircuitDeviceRowTransactionRepository
|
|
implements CircuitDeviceRowTransactionStore
|
|
{
|
|
constructor(private readonly database: AppDatabase) {}
|
|
|
|
createInCircuit(input: CreateCircuitDeviceRowTransactionInput) {
|
|
const id = crypto.randomUUID();
|
|
this.database.transaction((tx) => {
|
|
const [circuit] = tx
|
|
.select({ id: circuits.id })
|
|
.from(circuits)
|
|
.where(eq(circuits.id, input.circuitId))
|
|
.limit(1)
|
|
.all();
|
|
if (!circuit) {
|
|
throw new Error("Der Stromkreis ist ungültig.");
|
|
}
|
|
|
|
const existingRows = tx
|
|
.select({ sortOrder: circuitDeviceRows.sortOrder })
|
|
.from(circuitDeviceRows)
|
|
.where(eq(circuitDeviceRows.circuitId, input.circuitId))
|
|
.all();
|
|
const lastSortOrder = existingRows.reduce(
|
|
(highest, row) => Math.max(highest, row.sortOrder),
|
|
0
|
|
);
|
|
const sortOrder = input.sortOrder ?? lastSortOrder + 10;
|
|
|
|
tx
|
|
.insert(circuitDeviceRows)
|
|
.values(toCircuitDeviceRowCreateValues(id, { ...input, sortOrder }))
|
|
.run();
|
|
tx
|
|
.update(circuits)
|
|
.set({ isReserve: 0 })
|
|
.where(eq(circuits.id, input.circuitId))
|
|
.run();
|
|
});
|
|
return id;
|
|
}
|
|
|
|
createCircuitWithDeviceRows(input: CreateCircuitWithDeviceRowsTransactionInput) {
|
|
if (input.deviceRows.length === 0) {
|
|
throw new Error("Mindestens eine Gerätezeile ist erforderlich.");
|
|
}
|
|
|
|
const circuitId = crypto.randomUUID();
|
|
const rowIds = input.deviceRows.map(() => crypto.randomUUID());
|
|
this.database.transaction((tx) => {
|
|
tx
|
|
.insert(circuits)
|
|
.values(
|
|
toCircuitCreateValues(circuitId, {
|
|
...input.circuit,
|
|
isReserve: false,
|
|
})
|
|
)
|
|
.run();
|
|
|
|
for (let index = 0; index < input.deviceRows.length; index += 1) {
|
|
const row = input.deviceRows[index];
|
|
tx
|
|
.insert(circuitDeviceRows)
|
|
.values(
|
|
toCircuitDeviceRowCreateValues(rowIds[index], {
|
|
...row,
|
|
circuitId,
|
|
sortOrder: row.sortOrder ?? (index + 1) * 10,
|
|
})
|
|
)
|
|
.run();
|
|
}
|
|
});
|
|
|
|
return { circuitId, rowIds };
|
|
}
|
|
|
|
deleteFromCircuit(rowId: string, expectedCircuitId: string) {
|
|
this.database.transaction((tx) => {
|
|
const [row] = tx
|
|
.select({ id: circuitDeviceRows.id, circuitId: circuitDeviceRows.circuitId })
|
|
.from(circuitDeviceRows)
|
|
.where(eq(circuitDeviceRows.id, rowId))
|
|
.limit(1)
|
|
.all();
|
|
if (!row || row.circuitId !== expectedCircuitId) {
|
|
throw new Error("Die Gerätezeile wurde vor dem Löschen verändert.");
|
|
}
|
|
|
|
const result = tx
|
|
.delete(circuitDeviceRows)
|
|
.where(
|
|
and(
|
|
eq(circuitDeviceRows.id, rowId),
|
|
eq(circuitDeviceRows.circuitId, expectedCircuitId)
|
|
)
|
|
)
|
|
.run();
|
|
if (result.changes !== 1) {
|
|
throw new Error("Die Gerätezeile konnte nicht gelöscht werden.");
|
|
}
|
|
|
|
const remainingRows = tx
|
|
.select({ id: circuitDeviceRows.id })
|
|
.from(circuitDeviceRows)
|
|
.where(eq(circuitDeviceRows.circuitId, expectedCircuitId))
|
|
.all();
|
|
tx
|
|
.update(circuits)
|
|
.set({ isReserve: remainingRows.length === 0 ? 1 : 0 })
|
|
.where(eq(circuits.id, expectedCircuitId))
|
|
.run();
|
|
});
|
|
}
|
|
|
|
moveRows(input: MoveCircuitDeviceRowsTransactionInput) {
|
|
if (input.rows.length === 0) {
|
|
throw new Error("Keine Gerätezeilen zum Verschieben angegeben.");
|
|
}
|
|
if (Boolean(input.targetCircuitId) === Boolean(input.createTargetCircuit)) {
|
|
throw new Error("Für die Mehrfachverschiebung muss genau ein Ziel angegeben werden.");
|
|
}
|
|
|
|
const rowIds = input.rows.map((row) => row.id);
|
|
if (new Set(rowIds).size !== rowIds.length) {
|
|
throw new Error("Gerätezeilen dürfen nicht mehrfach ausgewählt sein.");
|
|
}
|
|
|
|
const createdCircuitId = input.createTargetCircuit ? crypto.randomUUID() : undefined;
|
|
const targetCircuitId = input.targetCircuitId ?? createdCircuitId!;
|
|
|
|
this.database.transaction((tx) => {
|
|
const persistedRows = tx
|
|
.select({ id: circuitDeviceRows.id, circuitId: circuitDeviceRows.circuitId })
|
|
.from(circuitDeviceRows)
|
|
.where(inArray(circuitDeviceRows.id, rowIds))
|
|
.all();
|
|
if (persistedRows.length !== input.rows.length) {
|
|
throw new Error("Eine oder mehrere Gerätezeilen sind ungültig.");
|
|
}
|
|
|
|
const persistedRowsById = new Map(persistedRows.map((row) => [row.id, row]));
|
|
for (const expectedRow of input.rows) {
|
|
if (
|
|
persistedRowsById.get(expectedRow.id)?.circuitId !==
|
|
expectedRow.expectedCircuitId
|
|
) {
|
|
throw new Error(
|
|
"Eine Gerätezeile wurde vor Abschluss der Verschiebung verändert."
|
|
);
|
|
}
|
|
}
|
|
|
|
let targetCircuit: { id: string; circuitListId: string };
|
|
if (input.createTargetCircuit) {
|
|
tx
|
|
.insert(circuits)
|
|
.values({
|
|
id: targetCircuitId,
|
|
circuitListId: input.createTargetCircuit.circuitListId,
|
|
sectionId: input.createTargetCircuit.sectionId,
|
|
equipmentIdentifier: input.createTargetCircuit.equipmentIdentifier,
|
|
displayName: input.createTargetCircuit.displayName,
|
|
sortOrder: input.createTargetCircuit.sortOrder,
|
|
isReserve: 0,
|
|
})
|
|
.run();
|
|
targetCircuit = {
|
|
id: targetCircuitId,
|
|
circuitListId: input.createTargetCircuit.circuitListId,
|
|
};
|
|
} else {
|
|
const [persistedTargetCircuit] = tx
|
|
.select({ id: circuits.id, circuitListId: circuits.circuitListId })
|
|
.from(circuits)
|
|
.where(eq(circuits.id, targetCircuitId))
|
|
.limit(1)
|
|
.all();
|
|
if (!persistedTargetCircuit) {
|
|
throw new Error("Der Zielstromkreis ist ungültig.");
|
|
}
|
|
targetCircuit = persistedTargetCircuit;
|
|
}
|
|
|
|
const sourceCircuitIds = [
|
|
...new Set(input.rows.map((row) => row.expectedCircuitId)),
|
|
];
|
|
const sourceCircuits = tx
|
|
.select({ id: circuits.id, circuitListId: circuits.circuitListId })
|
|
.from(circuits)
|
|
.where(inArray(circuits.id, sourceCircuitIds))
|
|
.all();
|
|
if (sourceCircuits.length !== sourceCircuitIds.length) {
|
|
throw new Error("Einer oder mehrere Quellstromkreise sind ungültig.");
|
|
}
|
|
if (
|
|
sourceCircuits.some(
|
|
(circuit) => circuit.circuitListId !== targetCircuit.circuitListId
|
|
)
|
|
) {
|
|
throw new Error(
|
|
"Alle verschobenen Zeilen müssen zur Stromkreisliste des Ziels gehören."
|
|
);
|
|
}
|
|
|
|
const movedRowIdSet = new Set(rowIds);
|
|
const retainedTargetRows = tx
|
|
.select({ id: circuitDeviceRows.id, sortOrder: circuitDeviceRows.sortOrder })
|
|
.from(circuitDeviceRows)
|
|
.where(eq(circuitDeviceRows.circuitId, targetCircuitId))
|
|
.all()
|
|
.filter((row) => !movedRowIdSet.has(row.id));
|
|
const lastTargetSortOrder = retainedTargetRows.reduce(
|
|
(highest, row) => Math.max(highest, row.sortOrder),
|
|
0
|
|
);
|
|
|
|
for (let index = 0; index < input.rows.length; index += 1) {
|
|
const row = input.rows[index];
|
|
const result = tx
|
|
.update(circuitDeviceRows)
|
|
.set({
|
|
circuitId: targetCircuitId,
|
|
sortOrder: lastTargetSortOrder + (index + 1) * 10,
|
|
})
|
|
.where(
|
|
and(
|
|
eq(circuitDeviceRows.id, row.id),
|
|
eq(circuitDeviceRows.circuitId, row.expectedCircuitId)
|
|
)
|
|
)
|
|
.run();
|
|
if (result.changes !== 1) {
|
|
throw new Error(
|
|
"Eine Gerätezeile wurde vor Abschluss der Verschiebung verändert."
|
|
);
|
|
}
|
|
}
|
|
|
|
for (const sourceCircuitId of sourceCircuitIds) {
|
|
const remainingRows = tx
|
|
.select({ id: circuitDeviceRows.id })
|
|
.from(circuitDeviceRows)
|
|
.where(eq(circuitDeviceRows.circuitId, sourceCircuitId))
|
|
.all();
|
|
tx
|
|
.update(circuits)
|
|
.set({ isReserve: remainingRows.length === 0 ? 1 : 0 })
|
|
.where(eq(circuits.id, sourceCircuitId))
|
|
.run();
|
|
}
|
|
tx
|
|
.update(circuits)
|
|
.set({ isReserve: 0 })
|
|
.where(eq(circuits.id, targetCircuitId))
|
|
.run();
|
|
});
|
|
|
|
return {
|
|
movedRowIds: rowIds,
|
|
targetCircuitId,
|
|
createdCircuitId,
|
|
};
|
|
}
|
|
}
|