forked from jappel/leistungsbilanz-ts
535 lines
21 KiB
TypeScript
535 lines
21 KiB
TypeScript
import { CircuitDeviceRowRepository } from "../../db/repositories/circuit-device-row.repository.js";
|
|
import { CircuitListRepository } from "../../db/repositories/circuit-list.repository.js";
|
|
import { CircuitRepository } from "../../db/repositories/circuit.repository.js";
|
|
import { CircuitSectionRepository } from "../../db/repositories/circuit-section.repository.js";
|
|
import { ProjectDeviceRepository } from "../../db/repositories/project-device.repository.js";
|
|
import type {
|
|
CreateCircuitDeviceRowInput,
|
|
CreateCircuitInput,
|
|
CreateCircuitWithDeviceRowsInput,
|
|
MoveCircuitDeviceRowInput,
|
|
MoveCircuitDeviceRowsBulkInput,
|
|
ReorderSectionCircuitsInput,
|
|
UpdateSectionEquipmentIdentifiersInput,
|
|
UpdateCircuitDeviceRowInput,
|
|
UpdateCircuitInput,
|
|
} from "../../shared/validation/circuit.schemas.js";
|
|
import { CircuitNumberingService } from "./circuit-numbering.service.js";
|
|
import { projectDeviceSyncFields } from "../../shared/constants/project-device-sync-fields.js";
|
|
import {
|
|
parseOverriddenFields,
|
|
serializeOverriddenFields,
|
|
} from "./project-device-sync.service.js";
|
|
|
|
export class CircuitWriteService {
|
|
private readonly circuitRepository: CircuitRepository;
|
|
private readonly circuitSectionRepository: CircuitSectionRepository;
|
|
private readonly circuitListRepository: CircuitListRepository;
|
|
private readonly deviceRowRepository: CircuitDeviceRowRepository;
|
|
private readonly projectDeviceRepository: ProjectDeviceRepository;
|
|
private readonly numberingService: CircuitNumberingService;
|
|
|
|
constructor(deps?: {
|
|
circuitRepository?: CircuitRepository;
|
|
circuitSectionRepository?: CircuitSectionRepository;
|
|
circuitListRepository?: CircuitListRepository;
|
|
deviceRowRepository?: CircuitDeviceRowRepository;
|
|
projectDeviceRepository?: ProjectDeviceRepository;
|
|
numberingService?: CircuitNumberingService;
|
|
}) {
|
|
this.circuitRepository = deps?.circuitRepository ?? new CircuitRepository();
|
|
this.circuitSectionRepository = deps?.circuitSectionRepository ?? new CircuitSectionRepository();
|
|
this.circuitListRepository = deps?.circuitListRepository ?? new CircuitListRepository();
|
|
this.deviceRowRepository = deps?.deviceRowRepository ?? new CircuitDeviceRowRepository();
|
|
this.projectDeviceRepository = deps?.projectDeviceRepository ?? new ProjectDeviceRepository();
|
|
this.numberingService = deps?.numberingService ?? new CircuitNumberingService();
|
|
}
|
|
|
|
// Ensures writes never connect a section to the wrong circuit list.
|
|
private async assertSectionInList(sectionId: string, circuitListId: string) {
|
|
const section = await this.circuitSectionRepository.findById(sectionId);
|
|
if (!section) {
|
|
throw new Error("Invalid section id.");
|
|
}
|
|
if (section.circuitListId !== circuitListId) {
|
|
throw new Error("Section does not belong to circuit list.");
|
|
}
|
|
return section;
|
|
}
|
|
|
|
// Enforces BMK uniqueness inside one circuit list.
|
|
private async assertUniqueEquipmentIdentifier(
|
|
circuitListId: string,
|
|
equipmentIdentifier: string,
|
|
excludeCircuitId?: string
|
|
) {
|
|
const exists = await this.circuitRepository.existsByEquipmentIdentifier(
|
|
circuitListId,
|
|
equipmentIdentifier,
|
|
excludeCircuitId
|
|
);
|
|
if (exists) {
|
|
throw new Error("Duplicate equipmentIdentifier in circuit list.");
|
|
}
|
|
}
|
|
|
|
// Validates linked project-device id against owning project of the circuit list.
|
|
private async assertValidLinkedProjectDeviceForProject(
|
|
projectId: string,
|
|
linkedProjectDeviceId?: string
|
|
) {
|
|
if (!linkedProjectDeviceId) {
|
|
return;
|
|
}
|
|
const device = await this.projectDeviceRepository.findById(projectId, linkedProjectDeviceId);
|
|
if (!device) {
|
|
throw new Error("Invalid linked project device id.");
|
|
}
|
|
}
|
|
|
|
private async assertValidLinkedProjectDevice(circuitId: string, linkedProjectDeviceId?: string) {
|
|
if (!linkedProjectDeviceId) {
|
|
return;
|
|
}
|
|
const circuit = await this.circuitRepository.findById(circuitId);
|
|
if (!circuit) {
|
|
throw new Error("Invalid circuit id.");
|
|
}
|
|
const list = await this.circuitListRepository.findByIdByListIdOnly(circuit.circuitListId);
|
|
if (!list) {
|
|
throw new Error("Circuit list not found.");
|
|
}
|
|
await this.assertValidLinkedProjectDeviceForProject(list.projectId, linkedProjectDeviceId);
|
|
}
|
|
|
|
async createCircuit(projectId: string, circuitListId: string, input: CreateCircuitInput) {
|
|
const list = await this.circuitListRepository.findById(projectId, circuitListId);
|
|
if (!list) {
|
|
throw new Error("Circuit list not found in project.");
|
|
}
|
|
await this.assertSectionInList(input.sectionId, circuitListId);
|
|
await this.assertUniqueEquipmentIdentifier(circuitListId, input.equipmentIdentifier);
|
|
const id = await this.circuitRepository.create({
|
|
circuitListId,
|
|
sectionId: input.sectionId,
|
|
equipmentIdentifier: input.equipmentIdentifier,
|
|
displayName: input.displayName,
|
|
sortOrder: input.sortOrder,
|
|
protectionType: input.protectionType,
|
|
protectionRatedCurrent: input.protectionRatedCurrent,
|
|
protectionCharacteristic: input.protectionCharacteristic,
|
|
cableType: input.cableType,
|
|
cableCrossSection: input.cableCrossSection,
|
|
cableLength: input.cableLength,
|
|
rcdAssignment: input.rcdAssignment,
|
|
terminalDesignation: input.terminalDesignation,
|
|
voltage: input.voltage,
|
|
controlRequirement: input.controlRequirement,
|
|
status: input.status,
|
|
isReserve: input.isReserve ?? true,
|
|
remark: input.remark,
|
|
});
|
|
return this.circuitRepository.findById(id);
|
|
}
|
|
|
|
async createCircuitWithDeviceRows(
|
|
projectId: string,
|
|
circuitListId: string,
|
|
input: CreateCircuitWithDeviceRowsInput
|
|
) {
|
|
const list = await this.circuitListRepository.findById(projectId, circuitListId);
|
|
if (!list) {
|
|
throw new Error("Circuit list not found in project.");
|
|
}
|
|
await this.assertSectionInList(input.circuit.sectionId, circuitListId);
|
|
await this.assertUniqueEquipmentIdentifier(
|
|
circuitListId,
|
|
input.circuit.equipmentIdentifier
|
|
);
|
|
for (const row of input.deviceRows) {
|
|
await this.assertValidLinkedProjectDeviceForProject(
|
|
list.projectId,
|
|
row.linkedProjectDeviceId
|
|
);
|
|
}
|
|
|
|
const created = this.deviceRowRepository.createCircuitWithDeviceRowsTransactional({
|
|
circuit: {
|
|
circuitListId,
|
|
...input.circuit,
|
|
isReserve: false,
|
|
},
|
|
deviceRows: input.deviceRows,
|
|
});
|
|
const circuit = await this.circuitRepository.findById(created.circuitId);
|
|
const deviceRows = await Promise.all(
|
|
created.rowIds.map((rowId) => this.deviceRowRepository.findById(rowId))
|
|
);
|
|
if (!circuit || deviceRows.some((row) => !row)) {
|
|
throw new Error("Der angelegte Stromkreis konnte nicht geladen werden.");
|
|
}
|
|
return {
|
|
circuit,
|
|
deviceRows,
|
|
};
|
|
}
|
|
|
|
async updateCircuit(circuitId: string, input: UpdateCircuitInput) {
|
|
const current = await this.circuitRepository.findById(circuitId);
|
|
if (!current) {
|
|
throw new Error("Invalid circuit id.");
|
|
}
|
|
|
|
const sectionId = input.sectionId ?? current.sectionId;
|
|
const equipmentIdentifier = input.equipmentIdentifier ?? current.equipmentIdentifier;
|
|
const sortOrder = input.sortOrder ?? current.sortOrder;
|
|
await this.assertSectionInList(sectionId, current.circuitListId);
|
|
await this.assertUniqueEquipmentIdentifier(current.circuitListId, equipmentIdentifier, circuitId);
|
|
|
|
await this.circuitRepository.update(circuitId, {
|
|
sectionId,
|
|
equipmentIdentifier,
|
|
displayName: input.displayName ?? current.displayName ?? undefined,
|
|
sortOrder,
|
|
protectionType: input.protectionType ?? current.protectionType ?? undefined,
|
|
protectionRatedCurrent: input.protectionRatedCurrent ?? current.protectionRatedCurrent ?? undefined,
|
|
protectionCharacteristic:
|
|
input.protectionCharacteristic ?? current.protectionCharacteristic ?? undefined,
|
|
cableType: input.cableType ?? current.cableType ?? undefined,
|
|
cableCrossSection: input.cableCrossSection ?? current.cableCrossSection ?? undefined,
|
|
cableLength: input.cableLength ?? current.cableLength ?? undefined,
|
|
rcdAssignment: input.rcdAssignment ?? current.rcdAssignment ?? undefined,
|
|
terminalDesignation: input.terminalDesignation ?? current.terminalDesignation ?? undefined,
|
|
voltage: input.voltage ?? current.voltage ?? undefined,
|
|
controlRequirement: input.controlRequirement ?? current.controlRequirement ?? undefined,
|
|
status: input.status ?? current.status ?? undefined,
|
|
isReserve: input.isReserve ?? Boolean(current.isReserve),
|
|
remark: input.remark ?? current.remark ?? undefined,
|
|
});
|
|
return this.circuitRepository.findById(circuitId);
|
|
}
|
|
|
|
async deleteCircuit(circuitId: string) {
|
|
const current = await this.circuitRepository.findById(circuitId);
|
|
if (!current) {
|
|
throw new Error("Invalid circuit id.");
|
|
}
|
|
await this.circuitRepository.delete(circuitId);
|
|
}
|
|
|
|
async createDeviceRow(circuitId: string, input: CreateCircuitDeviceRowInput) {
|
|
const circuit = await this.circuitRepository.findById(circuitId);
|
|
if (!circuit) {
|
|
throw new Error("Invalid circuit id.");
|
|
}
|
|
await this.assertValidLinkedProjectDevice(circuitId, input.linkedProjectDeviceId);
|
|
|
|
const rowId = this.deviceRowRepository.createInCircuitTransactional({
|
|
circuitId,
|
|
linkedProjectDeviceId: input.linkedProjectDeviceId,
|
|
sortOrder: input.sortOrder,
|
|
name: input.name,
|
|
displayName: input.displayName,
|
|
phaseType: input.phaseType,
|
|
connectionKind: input.connectionKind,
|
|
costGroup: input.costGroup,
|
|
category: input.category,
|
|
level: input.level,
|
|
roomId: input.roomId,
|
|
roomNumberSnapshot: input.roomNumberSnapshot,
|
|
roomNameSnapshot: input.roomNameSnapshot,
|
|
quantity: input.quantity,
|
|
powerPerUnit: input.powerPerUnit,
|
|
simultaneityFactor: input.simultaneityFactor,
|
|
cosPhi: input.cosPhi,
|
|
remark: input.remark,
|
|
overriddenFields: input.overriddenFields,
|
|
});
|
|
|
|
return this.deviceRowRepository.findById(rowId);
|
|
}
|
|
|
|
async updateDeviceRow(rowId: string, input: UpdateCircuitDeviceRowInput) {
|
|
const current = await this.deviceRowRepository.findById(rowId);
|
|
if (!current) {
|
|
throw new Error("Invalid device row id.");
|
|
}
|
|
await this.assertValidLinkedProjectDevice(current.circuitId, input.linkedProjectDeviceId);
|
|
let overriddenFields = input.overriddenFields ?? current.overriddenFields ?? undefined;
|
|
if (current.linkedProjectDeviceId && input.overriddenFields === undefined) {
|
|
const overrides = new Set(parseOverriddenFields(current.overriddenFields));
|
|
const inputValues = input as Record<string, unknown>;
|
|
const currentValues = current as unknown as Record<string, unknown>;
|
|
for (const field of projectDeviceSyncFields) {
|
|
if (
|
|
Object.prototype.hasOwnProperty.call(input, field) &&
|
|
(inputValues[field] ?? null) !== (currentValues[field] ?? null)
|
|
) {
|
|
overrides.add(field);
|
|
}
|
|
}
|
|
overriddenFields = serializeOverriddenFields(overrides);
|
|
}
|
|
await this.deviceRowRepository.update(rowId, {
|
|
linkedProjectDeviceId: input.linkedProjectDeviceId ?? current.linkedProjectDeviceId ?? undefined,
|
|
name: input.name ?? current.name,
|
|
displayName: input.displayName ?? current.displayName,
|
|
phaseType: input.phaseType ?? current.phaseType ?? undefined,
|
|
connectionKind: input.connectionKind ?? current.connectionKind ?? undefined,
|
|
costGroup: input.costGroup ?? current.costGroup ?? undefined,
|
|
category: input.category ?? current.category ?? undefined,
|
|
level: input.level ?? current.level ?? undefined,
|
|
roomId: input.roomId ?? current.roomId ?? undefined,
|
|
roomNumberSnapshot: input.roomNumberSnapshot ?? current.roomNumberSnapshot ?? undefined,
|
|
roomNameSnapshot: input.roomNameSnapshot ?? current.roomNameSnapshot ?? undefined,
|
|
quantity: input.quantity ?? current.quantity,
|
|
powerPerUnit: input.powerPerUnit ?? current.powerPerUnit,
|
|
simultaneityFactor: input.simultaneityFactor ?? current.simultaneityFactor,
|
|
cosPhi: input.cosPhi ?? current.cosPhi ?? undefined,
|
|
remark: input.remark ?? current.remark ?? undefined,
|
|
overriddenFields,
|
|
});
|
|
return this.deviceRowRepository.findById(rowId);
|
|
}
|
|
|
|
async deleteDeviceRow(rowId: string) {
|
|
const current = await this.deviceRowRepository.findById(rowId);
|
|
if (!current) {
|
|
throw new Error("Invalid device row id.");
|
|
}
|
|
const circuit = await this.circuitRepository.findById(current.circuitId);
|
|
if (!circuit) {
|
|
throw new Error("Invalid circuit id.");
|
|
}
|
|
this.deviceRowRepository.deleteFromCircuitTransactional(rowId, circuit.id);
|
|
}
|
|
|
|
async moveDeviceRow(rowId: string, input: MoveCircuitDeviceRowInput) {
|
|
const row = await this.deviceRowRepository.findById(rowId);
|
|
if (!row) {
|
|
throw new Error("Invalid device row id.");
|
|
}
|
|
|
|
const sourceCircuit = await this.circuitRepository.findById(row.circuitId);
|
|
if (!sourceCircuit) {
|
|
throw new Error("Invalid circuit id.");
|
|
}
|
|
|
|
let targetCircuit = input.targetCircuitId
|
|
? await this.circuitRepository.findById(input.targetCircuitId)
|
|
: null;
|
|
if (input.targetCircuitId && !targetCircuit) {
|
|
throw new Error("Invalid target circuit id.");
|
|
}
|
|
|
|
let createTargetCircuit:
|
|
| {
|
|
circuitListId: string;
|
|
sectionId: string;
|
|
equipmentIdentifier: string;
|
|
displayName: string;
|
|
sortOrder: number;
|
|
}
|
|
| undefined;
|
|
|
|
// Placeholder-target move prepares a new circuit explicitly. Its creation and
|
|
// the row assignment are committed together without renumbering other circuits.
|
|
if (!targetCircuit) {
|
|
if (!input.targetSectionId || !input.createNewCircuit) {
|
|
throw new Error("Invalid move target.");
|
|
}
|
|
const section = await this.assertSectionInList(input.targetSectionId, sourceCircuit.circuitListId);
|
|
const nextIdentifier = await this.numberingService.getNextIdentifier(section.id);
|
|
const sectionCircuits = await this.circuitRepository.listBySection(section.id);
|
|
const nextSortOrder =
|
|
sectionCircuits.length > 0 ? Math.max(...sectionCircuits.map((circuit) => circuit.sortOrder)) + 10 : 10;
|
|
createTargetCircuit = {
|
|
circuitListId: sourceCircuit.circuitListId,
|
|
sectionId: section.id,
|
|
equipmentIdentifier: nextIdentifier,
|
|
displayName: "Neuer Stromkreis",
|
|
sortOrder: nextSortOrder,
|
|
};
|
|
}
|
|
|
|
if (targetCircuit && targetCircuit.circuitListId !== sourceCircuit.circuitListId) {
|
|
throw new Error("Target circuit does not belong to same circuit list.");
|
|
}
|
|
if (targetCircuit?.id === sourceCircuit.id) {
|
|
return this.deviceRowRepository.findById(rowId);
|
|
}
|
|
|
|
this.deviceRowRepository.moveRowsTransactional({
|
|
rows: [{ id: row.id, expectedCircuitId: row.circuitId }],
|
|
targetCircuitId: targetCircuit?.id,
|
|
createTargetCircuit,
|
|
});
|
|
|
|
return this.deviceRowRepository.findById(rowId);
|
|
}
|
|
|
|
async moveDeviceRowsBulk(input: MoveCircuitDeviceRowsBulkInput) {
|
|
// Bulk move keeps input order and resolves all source circuits first so undo can
|
|
// restore per-source assignment deterministically.
|
|
const uniqueRowIds = [...new Set(input.rowIds)];
|
|
if (uniqueRowIds.length === 0) {
|
|
throw new Error("No device rows provided.");
|
|
}
|
|
|
|
const rows = [];
|
|
for (const rowId of uniqueRowIds) {
|
|
const row = await this.deviceRowRepository.findById(rowId);
|
|
if (!row) {
|
|
throw new Error("Invalid device row id.");
|
|
}
|
|
rows.push(row);
|
|
}
|
|
|
|
const sourceCircuits = new Map<string, Awaited<ReturnType<CircuitRepository["findById"]>>>();
|
|
for (const row of rows) {
|
|
if (!sourceCircuits.has(row.circuitId)) {
|
|
const circuit = await this.circuitRepository.findById(row.circuitId);
|
|
if (!circuit) {
|
|
throw new Error("Invalid circuit id.");
|
|
}
|
|
sourceCircuits.set(row.circuitId, circuit);
|
|
}
|
|
}
|
|
|
|
const referenceSourceCircuit = sourceCircuits.get(rows[0].circuitId)!;
|
|
let targetCircuit = input.targetCircuitId
|
|
? await this.circuitRepository.findById(input.targetCircuitId)
|
|
: null;
|
|
if (input.targetCircuitId && !targetCircuit) {
|
|
throw new Error("Invalid target circuit id.");
|
|
}
|
|
|
|
// Bulk placeholder move prepares exactly one new circuit as common target.
|
|
// Its actual creation happens together with the row moves in one transaction.
|
|
let createTargetCircuit:
|
|
| {
|
|
circuitListId: string;
|
|
sectionId: string;
|
|
equipmentIdentifier: string;
|
|
displayName: string;
|
|
sortOrder: number;
|
|
}
|
|
| undefined;
|
|
if (!targetCircuit) {
|
|
if (!input.targetSectionId || !input.createNewCircuit) {
|
|
throw new Error("Invalid move target.");
|
|
}
|
|
const section = await this.assertSectionInList(input.targetSectionId, referenceSourceCircuit.circuitListId);
|
|
const nextIdentifier = await this.numberingService.getNextIdentifier(section.id);
|
|
const sectionCircuits = await this.circuitRepository.listBySection(section.id);
|
|
const nextSortOrder =
|
|
sectionCircuits.length > 0 ? Math.max(...sectionCircuits.map((circuit) => circuit.sortOrder)) + 10 : 10;
|
|
createTargetCircuit = {
|
|
circuitListId: referenceSourceCircuit.circuitListId,
|
|
sectionId: section.id,
|
|
equipmentIdentifier: nextIdentifier,
|
|
displayName: "Neuer Stromkreis",
|
|
sortOrder: nextSortOrder,
|
|
};
|
|
}
|
|
|
|
const targetCircuitListId = targetCircuit?.circuitListId ?? createTargetCircuit!.circuitListId;
|
|
for (const sourceCircuit of sourceCircuits.values()) {
|
|
if (sourceCircuit.circuitListId !== targetCircuitListId) {
|
|
throw new Error("All moved rows must belong to same circuit list as target.");
|
|
}
|
|
}
|
|
|
|
return this.deviceRowRepository.moveRowsTransactional({
|
|
rows: rows.map((row) => ({ id: row.id, expectedCircuitId: row.circuitId })),
|
|
targetCircuitId: targetCircuit?.id,
|
|
createTargetCircuit,
|
|
});
|
|
}
|
|
|
|
async getNextIdentifier(sectionId: string) {
|
|
return this.numberingService.getNextIdentifier(sectionId);
|
|
}
|
|
|
|
async renumberSection(sectionId: string) {
|
|
// Explicit renumber operation for one section only.
|
|
// Never renumbers other sections and never runs implicitly during move/sort operations.
|
|
const section = await this.circuitSectionRepository.findById(sectionId);
|
|
if (!section) {
|
|
throw new Error("Invalid section id.");
|
|
}
|
|
const sectionCircuits = await this.circuitRepository.listBySection(sectionId);
|
|
const otherCircuits = (await this.circuitRepository.listByCircuitList(section.circuitListId)).filter(
|
|
(circuit) => circuit.sectionId !== sectionId
|
|
);
|
|
const otherIdentifiers = new Set(otherCircuits.map((circuit) => circuit.equipmentIdentifier));
|
|
|
|
const finalAssignments: Array<{ id: string; equipmentIdentifier: string }> = [];
|
|
let index = 1;
|
|
for (const circuit of sectionCircuits) {
|
|
let candidate = `${section.prefix}${index}`;
|
|
while (otherIdentifiers.has(candidate)) {
|
|
index += 1;
|
|
candidate = `${section.prefix}${index}`;
|
|
}
|
|
finalAssignments.push({ id: circuit.id, equipmentIdentifier: candidate });
|
|
index += 1;
|
|
}
|
|
// Uses safe two-phase identifier update to avoid UNIQUE collisions during swaps.
|
|
await this.circuitRepository.updateEquipmentIdentifiersSafely(
|
|
section.circuitListId,
|
|
finalAssignments,
|
|
sectionId
|
|
);
|
|
|
|
return this.circuitRepository.listBySection(sectionId);
|
|
}
|
|
|
|
async updateSectionEquipmentIdentifiers(
|
|
sectionId: string,
|
|
input: UpdateSectionEquipmentIdentifiersInput
|
|
) {
|
|
const section = await this.circuitSectionRepository.findById(sectionId);
|
|
if (!section) {
|
|
throw new Error("Invalid section id.");
|
|
}
|
|
const sectionCircuits = await this.circuitRepository.listBySection(sectionId);
|
|
const sectionIds = new Set(sectionCircuits.map((circuit) => circuit.id));
|
|
if (input.identifiers.length !== sectionCircuits.length) {
|
|
throw new Error("identifiers must include all circuits in the section.");
|
|
}
|
|
for (const entry of input.identifiers) {
|
|
if (!sectionIds.has(entry.circuitId)) {
|
|
throw new Error("Circuit id does not belong to section.");
|
|
}
|
|
}
|
|
|
|
await this.circuitRepository.updateEquipmentIdentifiersSafely(
|
|
section.circuitListId,
|
|
input.identifiers.map((entry) => ({ id: entry.circuitId, equipmentIdentifier: entry.equipmentIdentifier })),
|
|
sectionId
|
|
);
|
|
return this.circuitRepository.listBySection(sectionId);
|
|
}
|
|
|
|
async reorderCircuitsInSection(sectionId: string, input: ReorderSectionCircuitsInput) {
|
|
// Reorder updates sortOrder only. BMKs remain unchanged; users may renumber explicitly later.
|
|
const section = await this.circuitSectionRepository.findById(sectionId);
|
|
if (!section) {
|
|
throw new Error("Invalid section id.");
|
|
}
|
|
const sectionCircuits = await this.circuitRepository.listBySection(sectionId);
|
|
const sectionIds = new Set(sectionCircuits.map((circuit) => circuit.id));
|
|
if (sectionCircuits.length !== input.orderedCircuitIds.length) {
|
|
throw new Error("orderedCircuitIds must include all circuits of the section.");
|
|
}
|
|
for (const circuitId of input.orderedCircuitIds) {
|
|
if (!sectionIds.has(circuitId)) {
|
|
throw new Error("Circuit id does not belong to section.");
|
|
}
|
|
}
|
|
|
|
this.circuitRepository.updateSortOrdersSafely(sectionId, input.orderedCircuitIds);
|
|
return this.circuitRepository.listBySection(sectionId);
|
|
}
|
|
}
|