Decouple legacy migration service

This commit is contained in:
2026-07-26 12:11:05 +02:00
parent c1be85e408
commit 859fa4a42a
9 changed files with 335 additions and 71 deletions
+3
View File
@@ -30,6 +30,9 @@ It must support circuits, device rows, project devices, drag-and-drop restructur
directly; do not reintroduce a standalone runtime revision repository.
- Runtime domain services receive narrow reader/store dependencies explicitly;
concrete SQLite repositories are instantiated only under `src/server/composition`.
- The upgrade-only legacy migration service follows the same dependency rule;
its concrete repositories are instantiated only in
`scripts/db-migrate-legacy-consumers.ts`.
- General Circuit, CircuitDeviceRow, CircuitList and DistributionBoard
repositories expose only active reads. Runtime writes belong in typed command
repositories; direct integration fixtures belong under `tests/support`.
+3
View File
@@ -249,6 +249,9 @@ Kopieren in ein Projekt erzeugt ein eigenständiges Projektgerät.
- `npm run db:backup` erzeugt ein konsistentes und verifiziertes Online-Backup.
- Angewendete Migrationen werden niemals nachträglich verändert.
- `db:migrate:legacy-consumers` ist Upgrade-Werkzeug, kein Anwendungspfad.
Der fachliche Migrationsdienst kennt nur schmale Reader-/Store-Ports unter
`src/domain/ports`; konkrete SQLite-Repositories werden ausschließlich im
CLI-Skript zusammengesetzt.
- Allgemeine Circuit-, Gerätezeilen-, CircuitList- und DistributionBoard-
Repositories stellen im Anwendungspfad nur noch benötigte Leseabfragen bereit.
Fachliche Schreibvorgänge liegen in den typisierten Command-Repositories;
+2 -1
View File
@@ -340,7 +340,8 @@ Implemented foundation:
- circuit-device-row transaction tests cover both successful commits and forced SQLite rollbacks
- project-device row synchronization has real multi-row SQLite commit and rollback coverage
- BMK swaps and section reorder have real SQLite commit and rollback coverage
- the legacy consumer migration receives its database dependency at the script entry point
- the legacy consumer migration service depends only on narrow domain ports;
all concrete repositories are composed at the script entry point
- legacy circuit, row, mapping and report writes have real late-failure rollback coverage
- circuit and device-row persistence value mapping is separated from the general repositories
- obsolete direct Circuit, CircuitDeviceRow, CircuitList, DistributionBoard and
+2 -2
View File
@@ -14,8 +14,8 @@
"build:api": "tsc -p tsconfig.json",
"build:web": "next build",
"start": "node dist/server/index.js",
"test": "tsx --test tests/project-device-schema.test.ts tests/project-device-schema-migration.test.ts tests/project-device-placement.service.test.ts tests/project-device-sync.service.test.ts tests/legacy-consumer-migration-planner.test.ts tests/legacy-consumer-migration.repository.test.ts tests/circuit-numbering.service.test.ts tests/circuit-power-calculation.test.ts tests/circuit-tree.controller.test.ts tests/circuit-editor-history.test.ts tests/circuit-grid-insertion.test.ts tests/circuit-grid-safety.test.ts tests/circuit-grid-model.test.ts tests/circuit-structure-command.test.ts tests/circuit-device-row-move-command.test.ts tests/circuit-section-reorder-command.test.ts tests/circuit-section-renumber-command.test.ts tests/circuit-grid-projection.test.ts tests/distribution-board-structure-project-command.repository.test.ts tests/project-location-structure-project-command.repository.test.ts tests/project-device-project-command.repository.test.ts tests/project-device-structure-project-command.repository.test.ts tests/project-device-row-sync-project-command.repository.test.ts tests/circuit-section-reorder-project-command.repository.test.ts tests/circuit-section-renumber-project-command.repository.test.ts tests/project-command.model.test.ts tests/project-revision.persistence.test.ts tests/circuit-project-command.repository.test.ts tests/circuit-structure-project-command.repository.test.ts tests/circuit-device-row-project-command.repository.test.ts tests/circuit-device-row-structure-project-command.repository.test.ts tests/circuit-device-row-move-project-command.repository.test.ts tests/project-history.repository.test.ts tests/project-history-timeline.test.ts tests/project-version-history.test.ts tests/project-settings-project-command.repository.test.ts tests/project-state-snapshot.test.ts tests/project-snapshot-policy.test.ts tests/project-snapshot.repository.test.ts tests/project-state-restore-command.repository.test.ts tests/project-command.service.test.ts tests/database-backup.test.ts",
"test:watch": "tsx --watch --test tests/project-device-schema.test.ts tests/project-device-schema-migration.test.ts tests/project-device-placement.service.test.ts tests/project-device-sync.service.test.ts tests/legacy-consumer-migration-planner.test.ts tests/legacy-consumer-migration.repository.test.ts tests/circuit-numbering.service.test.ts tests/circuit-power-calculation.test.ts tests/circuit-tree.controller.test.ts tests/circuit-editor-history.test.ts tests/circuit-grid-insertion.test.ts tests/circuit-grid-safety.test.ts tests/circuit-grid-model.test.ts tests/circuit-structure-command.test.ts tests/circuit-device-row-move-command.test.ts tests/circuit-section-reorder-command.test.ts tests/circuit-section-renumber-command.test.ts tests/circuit-grid-projection.test.ts tests/distribution-board-structure-project-command.repository.test.ts tests/project-location-structure-project-command.repository.test.ts tests/project-device-project-command.repository.test.ts tests/project-device-structure-project-command.repository.test.ts tests/project-device-row-sync-project-command.repository.test.ts tests/circuit-section-reorder-project-command.repository.test.ts tests/circuit-section-renumber-project-command.repository.test.ts tests/project-command.model.test.ts tests/project-revision.persistence.test.ts tests/circuit-project-command.repository.test.ts tests/circuit-structure-project-command.repository.test.ts tests/circuit-device-row-project-command.repository.test.ts tests/circuit-device-row-structure-project-command.repository.test.ts tests/circuit-device-row-move-project-command.repository.test.ts tests/project-history.repository.test.ts tests/project-history-timeline.test.ts tests/project-version-history.test.ts tests/project-settings-project-command.repository.test.ts tests/project-state-snapshot.test.ts tests/project-snapshot-policy.test.ts tests/project-snapshot.repository.test.ts tests/project-state-restore-command.repository.test.ts tests/project-command.service.test.ts tests/database-backup.test.ts",
"test": "tsx --test tests/project-device-schema.test.ts tests/project-device-schema-migration.test.ts tests/project-device-placement.service.test.ts tests/project-device-sync.service.test.ts tests/legacy-consumer-migration-planner.test.ts tests/legacy-consumer-migration.service.test.ts tests/legacy-consumer-migration.repository.test.ts tests/circuit-numbering.service.test.ts tests/circuit-power-calculation.test.ts tests/circuit-tree.controller.test.ts tests/circuit-editor-history.test.ts tests/circuit-grid-insertion.test.ts tests/circuit-grid-safety.test.ts tests/circuit-grid-model.test.ts tests/circuit-structure-command.test.ts tests/circuit-device-row-move-command.test.ts tests/circuit-section-reorder-command.test.ts tests/circuit-section-renumber-command.test.ts tests/circuit-grid-projection.test.ts tests/distribution-board-structure-project-command.repository.test.ts tests/project-location-structure-project-command.repository.test.ts tests/project-device-project-command.repository.test.ts tests/project-device-structure-project-command.repository.test.ts tests/project-device-row-sync-project-command.repository.test.ts tests/circuit-section-reorder-project-command.repository.test.ts tests/circuit-section-renumber-project-command.repository.test.ts tests/project-command.model.test.ts tests/project-revision.persistence.test.ts tests/circuit-project-command.repository.test.ts tests/circuit-structure-project-command.repository.test.ts tests/circuit-device-row-project-command.repository.test.ts tests/circuit-device-row-structure-project-command.repository.test.ts tests/circuit-device-row-move-project-command.repository.test.ts tests/project-history.repository.test.ts tests/project-history-timeline.test.ts tests/project-version-history.test.ts tests/project-settings-project-command.repository.test.ts tests/project-state-snapshot.test.ts tests/project-snapshot-policy.test.ts tests/project-snapshot.repository.test.ts tests/project-state-restore-command.repository.test.ts tests/project-command.service.test.ts tests/database-backup.test.ts",
"test:watch": "tsx --watch --test tests/project-device-schema.test.ts tests/project-device-schema-migration.test.ts tests/project-device-placement.service.test.ts tests/project-device-sync.service.test.ts tests/legacy-consumer-migration-planner.test.ts tests/legacy-consumer-migration.service.test.ts tests/legacy-consumer-migration.repository.test.ts tests/circuit-numbering.service.test.ts tests/circuit-power-calculation.test.ts tests/circuit-tree.controller.test.ts tests/circuit-editor-history.test.ts tests/circuit-grid-insertion.test.ts tests/circuit-grid-safety.test.ts tests/circuit-grid-model.test.ts tests/circuit-structure-command.test.ts tests/circuit-device-row-move-command.test.ts tests/circuit-section-reorder-command.test.ts tests/circuit-section-renumber-command.test.ts tests/circuit-grid-projection.test.ts tests/distribution-board-structure-project-command.repository.test.ts tests/project-location-structure-project-command.repository.test.ts tests/project-device-project-command.repository.test.ts tests/project-device-structure-project-command.repository.test.ts tests/project-device-row-sync-project-command.repository.test.ts tests/circuit-section-reorder-project-command.repository.test.ts tests/circuit-section-renumber-project-command.repository.test.ts tests/project-command.model.test.ts tests/project-revision.persistence.test.ts tests/circuit-project-command.repository.test.ts tests/circuit-structure-project-command.repository.test.ts tests/circuit-device-row-project-command.repository.test.ts tests/circuit-device-row-structure-project-command.repository.test.ts tests/circuit-device-row-move-project-command.repository.test.ts tests/project-history.repository.test.ts tests/project-history-timeline.test.ts tests/project-version-history.test.ts tests/project-settings-project-command.repository.test.ts tests/project-state-snapshot.test.ts tests/project-snapshot-policy.test.ts tests/project-snapshot.repository.test.ts tests/project-state-restore-command.repository.test.ts tests/project-command.service.test.ts tests/database-backup.test.ts",
"db:generate": "drizzle-kit generate",
"db:migrate": "drizzle-kit migrate",
"db:backup": "tsx scripts/db-backup.ts",
+10 -3
View File
@@ -1,15 +1,22 @@
import { db } from "../src/db/client.js";
import { CircuitRepository } from "../src/db/repositories/circuit.repository.js";
import { CircuitListRepository } from "../src/db/repositories/circuit-list.repository.js";
import { CircuitSectionRepository } from "../src/db/repositories/circuit-section.repository.js";
import { LegacyConsumerMigrationRepository } from "../src/db/repositories/legacy-consumer-migration.repository.js";
import { ProjectRepository } from "../src/db/repositories/project.repository.js";
import { RoomRepository } from "../src/db/repositories/room.repository.js";
import { LegacyConsumerMigrationService } from "../src/domain/services/legacy-consumer-migration.service.js";
const projectRepository = new ProjectRepository();
const circuitListRepository = new CircuitListRepository();
const migrationRepository = new LegacyConsumerMigrationRepository(db);
const migrationService = new LegacyConsumerMigrationService(
migrationRepository
);
const migrationService = new LegacyConsumerMigrationService({
circuitListReader: circuitListRepository,
sectionStore: new CircuitSectionRepository(),
circuitReader: new CircuitRepository(),
roomReader: new RoomRepository(),
migrationStore: migrationRepository,
});
async function run() {
const projects = await projectRepository.list();
@@ -1,38 +1,17 @@
import crypto from "node:crypto";
import { eq, inArray, isNull } from "drizzle-orm";
import type {
LegacyMigrationCircuitInput,
LegacyMigrationReportInput,
} from "../../domain/ports/legacy-consumer-migration.store.js";
import type { AppDatabase } from "../database-context.js";
import { circuitDeviceRows } from "../schema/circuit-device-rows.js";
import { circuits } from "../schema/circuits.js";
import { consumers } from "../schema/consumers.js";
import { legacyConsumerCircuitMigrations } from "../schema/legacy-consumer-circuit-migrations.js";
import { legacyConsumerMigrationReports } from "../schema/legacy-consumer-migration-report.js";
import {
toCircuitDeviceRowCreateValues,
type CircuitDeviceRowCreateInput,
} from "./circuit-device-row.persistence.js";
import {
toCircuitCreateValues,
type CircuitCreatePersistenceInput,
} from "./circuit.persistence.js";
export interface LegacyMigrationCircuitPersistenceInput {
circuit: CircuitCreatePersistenceInput;
deviceRows: Array<
Omit<CircuitDeviceRowCreateInput, "circuitId"> & { legacyConsumerId: string }
>;
}
export interface LegacyMigrationReportPersistenceInput {
legacyConsumerCount: number;
createdCircuitCount: number;
createdDeviceRowCount: number;
duplicateGroupedCount: number;
generatedIdentifierCount: number;
unassignedRowCount: number;
warningsJson: string;
generatedIdentifiersJson: string;
duplicateGroupsJson: string;
}
import { toCircuitDeviceRowCreateValues } from "./circuit-device-row.persistence.js";
import { toCircuitCreateValues } from "./circuit.persistence.js";
export class LegacyConsumerMigrationRepository {
constructor(private readonly database: AppDatabase) {}
@@ -69,8 +48,8 @@ export class LegacyConsumerMigrationRepository {
persistCircuitListMigration(input: {
circuitListId: string;
circuits: LegacyMigrationCircuitPersistenceInput[];
report: LegacyMigrationReportPersistenceInput;
circuits: LegacyMigrationCircuitInput[];
report: LegacyMigrationReportInput;
}) {
if (input.circuits.some((entry) => entry.circuit.circuitListId !== input.circuitListId)) {
throw new Error("All migrated circuits must belong to the target circuit list.");
@@ -0,0 +1,143 @@
export interface LegacyConsumerMigrationCircuitListReader {
findById(
projectId: string,
circuitListId: string
): Promise<{ id: string } | null>;
}
export interface LegacyConsumerMigrationSection {
id: string;
key: string;
prefix: string;
}
export interface LegacyConsumerMigrationSectionStore {
createDefaults(circuitListId: string): Promise<void>;
listByCircuitList(
circuitListId: string
): Promise<LegacyConsumerMigrationSection[]>;
}
export interface LegacyConsumerMigrationCircuitReader {
listByCircuitList(circuitListId: string): Promise<
Array<{
sectionId: string;
equipmentIdentifier: string;
sortOrder: number;
}>
>;
}
export interface LegacyConsumerMigrationRoomReader {
listByProject(projectId: string): Promise<
Array<{
id: string;
roomNumber: string;
roomName: string;
}>
>;
}
export interface LegacyConsumerSource {
id: string;
projectDeviceId: string | null;
roomId: string | null;
circuitNumber: string | null;
description: string | null;
name: string;
category: string | null;
deviceType: string | null;
phaseType: string | null;
tradeOrCostGroup: string | null;
protectionType: string | null;
protectionRatedCurrent: number | null;
protectionCharacteristic: string | null;
cableType: string | null;
cableCrossSection: string | null;
comment: string | null;
quantity: number;
installedPowerPerUnitKw: number;
demandFactor: number;
voltageV: number | null;
phaseCount: number | null;
powerFactor: number | null;
note: string | null;
}
export interface LegacyMigrationDeviceRowInput {
linkedProjectDeviceId?: string;
legacyConsumerId: string;
sortOrder: number;
name: string;
displayName: string;
phaseType?: string;
connectionKind?: string;
costGroup?: string;
category?: string;
level?: string;
roomId?: string;
roomNumberSnapshot?: string;
roomNameSnapshot?: string;
quantity: number;
powerPerUnit: number;
simultaneityFactor: number;
cosPhi?: number;
remark?: string;
overriddenFields?: string;
}
export interface LegacyMigrationCircuitInput {
circuit: {
circuitListId: string;
sectionId: string;
equipmentIdentifier: string;
displayName?: string;
sortOrder: number;
protectionType?: string;
protectionRatedCurrent?: number;
protectionCharacteristic?: string;
cableType?: string;
cableCrossSection?: string;
cableLength?: number;
voltage?: number;
controlRequirement?: string;
remark?: string;
rcdAssignment?: string;
terminalDesignation?: string;
status?: string;
isReserve?: boolean;
};
deviceRows: LegacyMigrationDeviceRowInput[];
}
export interface LegacyMigrationReportInput {
legacyConsumerCount: number;
createdCircuitCount: number;
createdDeviceRowCount: number;
duplicateGroupedCount: number;
generatedIdentifierCount: number;
unassignedRowCount: number;
warningsJson: string;
generatedIdentifiersJson: string;
duplicateGroupsJson: string;
}
export interface LegacyConsumerMigrationStore {
listSourceConsumersByCircuitList(
circuitListId: string
): Promise<LegacyConsumerSource[]>;
listMigratedConsumerIds(circuitListId: string): Promise<string[]>;
persistCircuitListMigration(input: {
circuitListId: string;
circuits: LegacyMigrationCircuitInput[];
report: LegacyMigrationReportInput;
}): void;
}
export interface LegacyConsumerMigrationDependencies {
circuitListReader: LegacyConsumerMigrationCircuitListReader;
sectionStore: LegacyConsumerMigrationSectionStore;
circuitReader: LegacyConsumerMigrationCircuitReader;
roomReader: LegacyConsumerMigrationRoomReader;
migrationStore: LegacyConsumerMigrationStore;
}
@@ -1,25 +1,19 @@
import { CircuitRepository } from "../../db/repositories/circuit.repository.js";
import { CircuitListRepository } from "../../db/repositories/circuit-list.repository.js";
import { CircuitSectionRepository } from "../../db/repositories/circuit-section.repository.js";
import {
LegacyConsumerMigrationRepository,
type LegacyMigrationCircuitPersistenceInput,
} from "../../db/repositories/legacy-consumer-migration.repository.js";
import { RoomRepository } from "../../db/repositories/room.repository.js";
import type { LegacyMigrationReport } from "../models/circuit-tree.model.js";
import type {
LegacyConsumerMigrationDependencies,
LegacyConsumerSource,
LegacyMigrationCircuitInput,
} from "../ports/legacy-consumer-migration.store.js";
import {
inferSectionKeyFromEquipmentIdentifier,
inferSectionKeyFromLegacyInput,
normalizeCircuitNumber,
} from "./legacy-consumer-migration-planner.js";
type LegacyConsumerRow = Awaited<
ReturnType<
LegacyConsumerMigrationRepository["listSourceConsumersByCircuitList"]
>
>[number];
function parseEquipmentSequence(equipmentIdentifier: string, prefix: string): number | null {
function parseEquipmentSequence(
equipmentIdentifier: string,
prefix: string
): number | null {
if (!equipmentIdentifier.startsWith(prefix)) {
return null;
}
@@ -30,43 +24,45 @@ function parseEquipmentSequence(equipmentIdentifier: string, prefix: string): nu
return Number(suffix);
}
export class LegacyConsumerMigrationService {
private readonly circuitListRepository = new CircuitListRepository();
private readonly sectionRepository = new CircuitSectionRepository();
private readonly circuitRepository = new CircuitRepository();
private readonly roomRepository = new RoomRepository();
constructor(
private readonly migrationRepository: LegacyConsumerMigrationRepository
private readonly dependencies: LegacyConsumerMigrationDependencies
) {}
async migrateCircuitList(projectId: string, circuitListId: string): Promise<LegacyMigrationReport> {
async migrateCircuitList(
projectId: string,
circuitListId: string
): Promise<LegacyMigrationReport> {
// Migration is additive: legacy consumers are preserved, and circuit-first entities are created
// with mapping records so transition remains auditable and reversible.
const list = await this.circuitListRepository.findById(projectId, circuitListId);
const list = await this.dependencies.circuitListReader.findById(
projectId,
circuitListId
);
if (!list) {
throw new Error("Circuit list not found in project.");
}
await this.sectionRepository.createDefaults(circuitListId);
const sections = await this.sectionRepository.listByCircuitList(circuitListId);
await this.dependencies.sectionStore.createDefaults(circuitListId);
const sections =
await this.dependencies.sectionStore.listByCircuitList(circuitListId);
const sectionByKey = new Map(sections.map((section) => [section.key, section]));
const unassignedSection = sectionByKey.get("unassigned");
if (!unassignedSection) {
throw new Error("Unassigned section is required.");
}
const existingCircuits = await this.circuitRepository.listByCircuitList(circuitListId);
const existingCircuits =
await this.dependencies.circuitReader.listByCircuitList(circuitListId);
const usedEquipmentIdentifiers = new Set(
existingCircuits.map((circuit) => circuit.equipmentIdentifier.toUpperCase())
);
const legacyConsumers =
await this.migrationRepository.listSourceConsumersByCircuitList(
await this.dependencies.migrationStore.listSourceConsumersByCircuitList(
circuitListId
);
const rooms = await this.roomRepository.listByProject(projectId);
const rooms = await this.dependencies.roomReader.listByProject(projectId);
const roomById = new Map(rooms.map((room) => [room.id, room]));
const report: LegacyMigrationReport = {
@@ -82,14 +78,16 @@ export class LegacyConsumerMigrationService {
// Idempotency guard: skip consumers already mapped in previous migration run.
const migratedConsumerIds = new Set(
await this.migrationRepository.listMigratedConsumerIds(circuitListId)
await this.dependencies.migrationStore.listMigratedConsumerIds(
circuitListId
)
);
const consumersToMigrate = legacyConsumers.filter((consumer) => !migratedConsumerIds.has(consumer.id));
// Legacy rows are grouped by normalized circuit number so duplicates become
// multiple device rows within one circuit instead of duplicate circuits.
const byNormalizedCircuitNumber = new Map<string, LegacyConsumerRow[]>();
const withoutNormalizedCircuitNumber: LegacyConsumerRow[] = [];
const byNormalizedCircuitNumber = new Map<string, LegacyConsumerSource[]>();
const withoutNormalizedCircuitNumber: LegacyConsumerSource[] = [];
for (const consumer of consumersToMigrate) {
const normalized = normalizeCircuitNumber(consumer.circuitNumber ?? null);
if (!normalized) {
@@ -110,7 +108,7 @@ export class LegacyConsumerMigrationService {
const groups: Array<{
equipmentIdentifier: string | null;
consumers: LegacyConsumerRow[];
consumers: LegacyConsumerSource[];
inferredSectionKey: string | null;
isGeneratedIdentifier: boolean;
}> = [];
@@ -152,7 +150,7 @@ export class LegacyConsumerMigrationService {
maxBySectionPrefix.set(section.prefix.toUpperCase(), Math.max(current, sequence));
}
const migrationCircuits: LegacyMigrationCircuitPersistenceInput[] = [];
const migrationCircuits: LegacyMigrationCircuitInput[] = [];
for (const group of groups) {
const representative = group.consumers[0];
let section = group.inferredSectionKey ? sectionByKey.get(group.inferredSectionKey) : null;
@@ -181,7 +179,7 @@ export class LegacyConsumerMigrationService {
usedEquipmentIdentifiers.add(equipmentIdentifier.toUpperCase());
const deviceRows: LegacyMigrationCircuitPersistenceInput["deviceRows"] = [];
const deviceRows: LegacyMigrationCircuitInput["deviceRows"] = [];
const circuitSortOrder = nextSortOrder;
nextSortOrder += 10;
@@ -250,7 +248,7 @@ export class LegacyConsumerMigrationService {
report.warnings.push(`Consumer ${row.consumerId} was migrated into unassigned section.`);
}
this.migrationRepository.persistCircuitListMigration({
this.dependencies.migrationStore.persistCircuitListMigration({
circuitListId,
circuits: migrationCircuits,
report: {
@@ -0,0 +1,130 @@
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import type {
LegacyConsumerMigrationDependencies,
LegacyMigrationCircuitInput,
LegacyMigrationReportInput,
} from "../src/domain/ports/legacy-consumer-migration.store.js";
import { LegacyConsumerMigrationService } from "../src/domain/services/legacy-consumer-migration.service.js";
describe("legacy consumer migration service", () => {
it("plans the migration through injected readers and persists one complete batch", async () => {
let defaultsCreatedFor: string | null = null;
let persisted:
| {
circuitListId: string;
circuits: LegacyMigrationCircuitInput[];
report: LegacyMigrationReportInput;
}
| undefined;
const dependencies: LegacyConsumerMigrationDependencies = {
circuitListReader: {
async findById(projectId, circuitListId) {
assert.equal(projectId, "project-1");
assert.equal(circuitListId, "list-1");
return { id: circuitListId };
},
},
sectionStore: {
async createDefaults(circuitListId) {
defaultsCreatedFor = circuitListId;
},
async listByCircuitList() {
return [
{ id: "section-1", key: "single_phase", prefix: "-2F" },
{ id: "section-2", key: "unassigned", prefix: "-XF" },
];
},
},
circuitReader: {
async listByCircuitList() {
return [
{
sectionId: "section-1",
equipmentIdentifier: "-2F2",
sortOrder: 10,
},
];
},
},
roomReader: {
async listByProject() {
return [
{ id: "room-1", roomNumber: "1.01", roomName: "Besprechung" },
];
},
},
migrationStore: {
async listSourceConsumersByCircuitList() {
return [
{
id: "consumer-1",
projectDeviceId: null,
roomId: "room-1",
circuitNumber: null,
description: "Steckdosen Besprechung",
name: "Steckdose",
category: null,
deviceType: null,
phaseType: null,
tradeOrCostGroup: "KG 440",
protectionType: null,
protectionRatedCurrent: null,
protectionCharacteristic: null,
cableType: null,
cableCrossSection: null,
comment: null,
quantity: 2,
installedPowerPerUnitKw: 0.2,
demandFactor: 0.8,
voltageV: 230,
phaseCount: 1,
powerFactor: 0.95,
note: "Bestand",
},
];
},
async listMigratedConsumerIds() {
return [];
},
persistCircuitListMigration(input) {
persisted = input;
},
},
};
const report = await new LegacyConsumerMigrationService(
dependencies
).migrateCircuitList("project-1", "list-1");
assert.equal(defaultsCreatedFor, "list-1");
assert.deepEqual(report.generatedIdentifiers, ["-2F3"]);
assert.equal(report.createdCircuitCount, 1);
assert.equal(report.createdDeviceRowCount, 1);
assert.equal(persisted?.circuitListId, "list-1");
assert.equal(persisted?.circuits[0].circuit.sectionId, "section-1");
assert.equal(
persisted?.circuits[0].circuit.equipmentIdentifier,
"-2F3"
);
assert.deepEqual(persisted?.circuits[0].deviceRows[0], {
linkedProjectDeviceId: undefined,
legacyConsumerId: "consumer-1",
sortOrder: 10,
name: "Steckdose",
displayName: "Steckdosen Besprechung",
phaseType: undefined,
connectionKind: undefined,
costGroup: "KG 440",
category: undefined,
roomId: "room-1",
roomNumberSnapshot: "1.01",
roomNameSnapshot: "Besprechung",
quantity: 2,
powerPerUnit: 0.2,
simultaneityFactor: 0.8,
cosPhi: 0.95,
remark: "Bestand",
});
});
});