forked from jappel/leistungsbilanz-ts
59 lines
6.4 KiB
TypeScript
59 lines
6.4 KiB
TypeScript
import { and, eq, inArray } from "drizzle-orm";
|
|
import { assertCircuitDeviceRowQuantity } from "../../domain/calculations/circuit-device-row-quantity.js";
|
|
import { assertExternalObjectDeleteMissingProjectCommand, invertExternalObjectDeleteMissingProjectCommand } from "../../domain/models/external-object-delete-missing-project-command.model.js";
|
|
import type { ExternalObjectDeleteMissingProjectCommandStore } from "../../domain/ports/external-object-delete-missing-project-command.store.js";
|
|
import type { AppDatabase } from "../database-context.js";
|
|
import { circuitDeviceRows } from "../schema/circuit-device-rows.js";
|
|
import { circuitLists } from "../schema/circuit-lists.js";
|
|
import { circuits } from "../schema/circuits.js";
|
|
import { externalModelObjects } from "../schema/external-model-objects.js";
|
|
import { externalModelSources } from "../schema/external-model-sources.js";
|
|
import { externalImportBatches } from "../schema/external-import-batches.js";
|
|
import { externalRoomMappings } from "../schema/external-room-mappings.js";
|
|
import { distributionBoards } from "../schema/distribution-boards.js";
|
|
import { projectDevices } from "../schema/project-devices.js";
|
|
import { toCircuitDeviceRowSnapshot } from "./circuit-device-row-structure.persistence.js";
|
|
import { snapshotsEqual, toExternalObjectSnapshot } from "./external-object-assignment.persistence.js";
|
|
import { executeProjectCommandTransaction } from "./project-command-transaction.persistence.js";
|
|
|
|
export class ExternalObjectDeleteMissingProjectCommandRepository implements ExternalObjectDeleteMissingProjectCommandStore {
|
|
constructor(private readonly database: AppDatabase) {}
|
|
execute(input: Parameters<ExternalObjectDeleteMissingProjectCommandStore["execute"]>[0]) {
|
|
assertExternalObjectDeleteMissingProjectCommand(input.command);
|
|
return executeProjectCommandTransaction(this.database, input, (tx) => {
|
|
const rowById = new Map(input.command.payload.rows.map((transition) => [transition.expected.id, transition]));
|
|
const rowIds = [...rowById.keys()];
|
|
const rowContexts = rowIds.length ? tx.select({ row: circuitDeviceRows, projectId: circuitLists.projectId }).from(circuitDeviceRows).innerJoin(circuits, eq(circuits.id, circuitDeviceRows.circuitId)).innerJoin(circuitLists, eq(circuitLists.id, circuits.circuitListId)).where(inArray(circuitDeviceRows.id, rowIds)).all() : [];
|
|
if (rowContexts.length !== rowIds.length || rowContexts.some((entry) => entry.projectId !== input.projectId || !snapshotsEqual(toCircuitDeviceRowSnapshot(entry.row), rowById.get(entry.row.id)!.expected))) throw new Error("Eine Gerätezeile wurde zwischenzeitlich geändert.");
|
|
const currentObjects = tx.select().from(externalModelObjects).where(eq(externalModelObjects.projectId, input.projectId)).all().map(toExternalObjectSnapshot);
|
|
const currentById = new Map(currentObjects.map((object) => [object.id, object]));
|
|
for (const transition of input.command.payload.objects) {
|
|
const current = currentById.get((transition.expected ?? transition.target)!.id) ?? null;
|
|
if (transition.expected === null ? current !== null : current === null || !snapshotsEqual(current, transition.expected)) throw new Error("Ein fehlendes Revit-Objekt wurde zwischenzeitlich geändert.");
|
|
}
|
|
const sourceIds = ids(tx.select({ id: externalModelSources.id }).from(externalModelSources).where(eq(externalModelSources.projectId, input.projectId)).all());
|
|
const batchIds = ids(tx.select({ id: externalImportBatches.id }).from(externalImportBatches).where(eq(externalImportBatches.projectId, input.projectId)).all());
|
|
const mappingIds = ids(tx.select({ id: externalRoomMappings.id }).from(externalRoomMappings).where(eq(externalRoomMappings.projectId, input.projectId)).all());
|
|
const boardIds = ids(tx.select({ id: distributionBoards.id }).from(distributionBoards).where(eq(distributionBoards.projectId, input.projectId)).all());
|
|
const deviceIds = ids(tx.select({ id: projectDevices.id }).from(projectDevices).where(eq(projectDevices.projectId, input.projectId)).all());
|
|
for (const { target } of input.command.payload.objects) if (target && (target.projectId !== input.projectId || !sourceIds.has(target.sourceId) || !batchIds.has(target.lastSeenImportBatchId) || !batchIds.has(target.lastAcceptedImportBatchId) || (target.externalRoomMappingId !== null && !mappingIds.has(target.externalRoomMappingId)) || (target.distributionBoardId !== null && !boardIds.has(target.distributionBoardId)) || (target.linkedProjectDeviceId !== null && !deviceIds.has(target.linkedProjectDeviceId)))) throw new Error("Ein wiederhergestelltes Revit-Objekt verweist auf ein anderes Projekt.");
|
|
const targetById = new Map(currentObjects.map((object) => [object.id, object]));
|
|
for (const transition of input.command.payload.objects) { const id = (transition.expected ?? transition.target)!.id; if (transition.target) targetById.set(id, transition.target); else targetById.delete(id); }
|
|
for (const transition of input.command.payload.rows) assertCircuitDeviceRowQuantity({ quantity: transition.target.quantity, manualQuantity: transition.target.manualQuantity ?? transition.target.quantity, externalObjects: [...targetById.values()].filter((object) => object.circuitDeviceRowId === transition.target.id).map((object) => ({ effectiveQuantity: object.planningValues.effectiveQuantity })) });
|
|
for (const transition of input.command.payload.rows) {
|
|
const result = tx.update(circuitDeviceRows).set({ quantity: transition.target.quantity }).where(and(eq(circuitDeviceRows.id, transition.expected.id), eq(circuitDeviceRows.quantity, transition.expected.quantity))).run();
|
|
if (result.changes !== 1) throw new Error("Eine Gerätezeile wurde während des Löschens geändert.");
|
|
}
|
|
for (const transition of input.command.payload.objects) {
|
|
const object = transition.expected ?? transition.target!;
|
|
if (transition.target === null) {
|
|
const result = tx.delete(externalModelObjects).where(and(eq(externalModelObjects.id, object.id), eq(externalModelObjects.projectId, input.projectId))).run();
|
|
if (result.changes !== 1) throw new Error("Ein fehlendes Revit-Objekt konnte nicht gelöscht werden.");
|
|
} else tx.insert(externalModelObjects).values(transition.target).run();
|
|
}
|
|
return invertExternalObjectDeleteMissingProjectCommand(input.command);
|
|
});
|
|
}
|
|
}
|
|
|
|
function ids(entries: Array<{ id: string }>) { return new Set(entries.map((entry) => entry.id)); }
|