Create clean release database baseline

This commit is contained in:
2026-07-31 14:07:26 +02:00
parent 5bee3cb103
commit 734a2bcfc4
129 changed files with 2121 additions and 30607 deletions
@@ -1,28 +0,0 @@
CREATE TABLE `consumers` (
`id` text PRIMARY KEY NOT NULL,
`project_id` text NOT NULL,
`distribution_board_id` text,
`name` text NOT NULL,
`category` text,
`quantity` integer NOT NULL,
`installed_power_per_unit_kw` real NOT NULL,
`demand_factor` real NOT NULL,
`voltage_v` real,
`phase_count` integer,
`power_factor` real,
`note` text,
FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`distribution_board_id`) REFERENCES `distribution_boards`(`id`) ON UPDATE no action ON DELETE set null
);
--> statement-breakpoint
CREATE TABLE `distribution_boards` (
`id` text PRIMARY KEY NOT NULL,
`project_id` text NOT NULL,
`name` text NOT NULL,
FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `projects` (
`id` text PRIMARY KEY NOT NULL,
`name` text NOT NULL
);
+300
View File
@@ -0,0 +1,300 @@
CREATE TABLE `circuit_device_rows` (
`id` text PRIMARY KEY NOT NULL,
`circuit_id` text NOT NULL,
`linked_project_device_id` text,
`sort_order` integer DEFAULT 0 NOT NULL,
`name` text NOT NULL,
`display_name` text NOT NULL,
`phase_type` text,
`connection_kind` text,
`cost_group` text,
`category` text,
`level` text,
`room_id` text,
`room_number_snapshot` text,
`room_name_snapshot` text,
`quantity` integer NOT NULL,
`power_per_unit` real NOT NULL,
`simultaneity_factor` real NOT NULL,
`cos_phi` real,
`remark` text,
`overridden_fields` text,
FOREIGN KEY (`circuit_id`) REFERENCES `circuits`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`linked_project_device_id`) REFERENCES `project_devices`(`id`) ON UPDATE no action ON DELETE set null,
FOREIGN KEY (`room_id`) REFERENCES `rooms`(`id`) ON UPDATE no action ON DELETE set null
);
--> statement-breakpoint
CREATE TABLE `circuit_list_equipment_identifiers` (
`owner_type` text NOT NULL,
`owner_id` text NOT NULL,
`circuit_list_id` text NOT NULL,
`equipment_identifier` text NOT NULL,
FOREIGN KEY (`circuit_list_id`) REFERENCES `circuit_lists`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `circuit_list_equipment_identifiers_owner_unique` ON `circuit_list_equipment_identifiers` (`owner_type`,`owner_id`);--> statement-breakpoint
CREATE UNIQUE INDEX `circuit_list_equipment_identifiers_list_identifier_unique` ON `circuit_list_equipment_identifiers` (`circuit_list_id`,`equipment_identifier`);--> statement-breakpoint
CREATE TABLE `circuit_lists` (
`id` text PRIMARY KEY NOT NULL,
`project_id` text NOT NULL,
`distribution_board_id` text NOT NULL,
`name` text NOT NULL,
FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`distribution_board_id`) REFERENCES `distribution_boards`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `circuit_lists_distribution_board_id_unique` ON `circuit_lists` (`distribution_board_id`);--> statement-breakpoint
CREATE TABLE `circuit_protection_devices` (
`circuit_id` text PRIMARY KEY NOT NULL,
`type` text NOT NULL,
`rated_current_a` real NOT NULL,
`fuse_utilization_category` text,
`trip_characteristic` text,
`rcd_type` text,
`rated_residual_current_ma` real,
FOREIGN KEY (`circuit_id`) REFERENCES `circuits`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `circuit_sections` (
`id` text PRIMARY KEY NOT NULL,
`circuit_list_id` text NOT NULL,
`key` text NOT NULL,
`display_name` text NOT NULL,
`prefix` text NOT NULL,
`sort_order` integer DEFAULT 0 NOT NULL,
`category` text,
`group_number` integer,
FOREIGN KEY (`circuit_list_id`) REFERENCES `circuit_lists`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `circuit_sections_list_key_unique` ON `circuit_sections` (`circuit_list_id`,`key`);--> statement-breakpoint
CREATE UNIQUE INDEX `circuit_sections_list_prefix_unique` ON `circuit_sections` (`circuit_list_id`,`prefix`);--> statement-breakpoint
CREATE UNIQUE INDEX `circuit_sections_list_category_group_unique` ON `circuit_sections` (`circuit_list_id`,`category`,`group_number`);--> statement-breakpoint
CREATE TABLE `circuits` (
`id` text PRIMARY KEY NOT NULL,
`circuit_list_id` text NOT NULL,
`section_id` text NOT NULL,
`equipment_identifier` text NOT NULL,
`display_name` text,
`sort_order` integer DEFAULT 0 NOT NULL,
`cable_type` text,
`cable_cross_section` text,
`cable_length` real,
`rcd_assignment` text,
`terminal_designation` text,
`voltage` real,
`control_requirement` text,
`status` text,
`is_reserve` integer DEFAULT 0 NOT NULL,
`remark` text,
FOREIGN KEY (`circuit_list_id`) REFERENCES `circuit_lists`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`section_id`) REFERENCES `circuit_sections`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `circuits_list_equipment_identifier_unique` ON `circuits` (`circuit_list_id`,`equipment_identifier`);--> statement-breakpoint
CREATE TABLE `distribution_board_component_protection_devices` (
`component_id` text PRIMARY KEY NOT NULL,
`type` text NOT NULL,
`rated_current_a` real NOT NULL,
`fuse_utilization_category` text,
`trip_characteristic` text,
`rcd_type` text,
`rated_residual_current_ma` real,
FOREIGN KEY (`component_id`) REFERENCES `distribution_board_components`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `distribution_board_components` (
`id` text PRIMARY KEY NOT NULL,
`circuit_list_id` text NOT NULL,
`section_id` text,
`equipment_identifier` text NOT NULL,
`name` text NOT NULL,
`role` text NOT NULL,
`placement` text NOT NULL,
`sort_order` integer DEFAULT 0 NOT NULL,
FOREIGN KEY (`circuit_list_id`) REFERENCES `circuit_lists`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`section_id`) REFERENCES `circuit_sections`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `distribution_board_components_list_identifier_unique` ON `distribution_board_components` (`circuit_list_id`,`equipment_identifier`);--> statement-breakpoint
CREATE UNIQUE INDEX `distribution_board_components_section_role_unique` ON `distribution_board_components` (`section_id`,`role`);--> statement-breakpoint
CREATE TABLE `distribution_boards` (
`id` text PRIMARY KEY NOT NULL,
`project_id` text NOT NULL,
`name` text NOT NULL,
`floor_id` text,
`supply_type` text,
`simultaneity_factor` real DEFAULT 1 NOT NULL,
FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`floor_id`) REFERENCES `floors`(`id`) ON UPDATE no action ON DELETE set null
);
--> statement-breakpoint
CREATE TABLE `floors` (
`id` text PRIMARY KEY NOT NULL,
`project_id` text NOT NULL,
`name` text NOT NULL,
`sort_order` integer DEFAULT 0 NOT NULL,
FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `global_devices` (
`id` text PRIMARY KEY NOT NULL,
`name` text NOT NULL,
`display_name` text NOT NULL,
`category` text,
`quantity` integer NOT NULL,
`installed_power_per_unit_kw` real NOT NULL,
`demand_factor` real NOT NULL,
`voltage_v` real,
`phase_count` integer,
`power_factor` real,
`note` text
);
--> statement-breakpoint
CREATE TABLE `project_change_sets` (
`id` text PRIMARY KEY NOT NULL,
`project_revision_id` text NOT NULL,
`command_type` text NOT NULL,
`payload_schema_version` integer NOT NULL,
`forward_payload_json` text NOT NULL,
`inverse_payload_json` text NOT NULL,
FOREIGN KEY (`project_revision_id`) REFERENCES `project_revisions`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `project_change_sets_revision_unique` ON `project_change_sets` (`project_revision_id`);--> statement-breakpoint
CREATE TABLE `project_devices` (
`id` text PRIMARY KEY NOT NULL,
`project_id` text NOT NULL,
`name` text NOT NULL,
`display_name` text NOT NULL,
`phase_type` text DEFAULT 'single_phase' NOT NULL,
`connection_kind` text,
`cost_group` text,
`category` text,
`quantity` integer NOT NULL,
`power_per_unit` real DEFAULT 0 NOT NULL,
`simultaneity_factor` real DEFAULT 1 NOT NULL,
`cos_phi` real,
`remark` text,
`voltage_v` real,
FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `project_history_stack_entries` (
`change_set_id` text PRIMARY KEY NOT NULL,
`project_id` text NOT NULL,
`stack` text NOT NULL,
`position` integer NOT NULL,
FOREIGN KEY (`change_set_id`) REFERENCES `project_change_sets`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE cascade,
CONSTRAINT "project_history_stack_entries_stack_check" CHECK("project_history_stack_entries"."stack" in ('undo', 'redo'))
);
--> statement-breakpoint
CREATE UNIQUE INDEX `project_history_stack_entries_position_unique` ON `project_history_stack_entries` (`project_id`,`stack`,`position`);--> statement-breakpoint
CREATE TABLE `project_revisions` (
`id` text PRIMARY KEY NOT NULL,
`project_id` text NOT NULL,
`revision_number` integer NOT NULL,
`created_at_iso` text NOT NULL,
`actor_id` text,
`source` text NOT NULL,
`description` text,
FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `project_revisions_project_number_unique` ON `project_revisions` (`project_id`,`revision_number`);--> statement-breakpoint
CREATE TABLE `project_snapshots` (
`id` text PRIMARY KEY NOT NULL,
`project_id` text NOT NULL,
`source_revision` integer NOT NULL,
`schema_version` integer NOT NULL,
`kind` text DEFAULT 'named' NOT NULL,
`name` text NOT NULL,
`description` text,
`payload_json` text NOT NULL,
`payload_sha256` text NOT NULL,
`created_at_iso` text NOT NULL,
`created_by_actor_id` text,
FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE INDEX `project_snapshots_project_created_idx` ON `project_snapshots` (`project_id`,`created_at_iso`);--> statement-breakpoint
CREATE INDEX `project_snapshots_project_kind_revision_idx` ON `project_snapshots` (`project_id`,`kind`,`source_revision`);--> statement-breakpoint
CREATE UNIQUE INDEX `project_snapshots_project_name_unique` ON `project_snapshots` (`project_id`,`name`);--> statement-breakpoint
CREATE TABLE `projects` (
`id` text PRIMARY KEY NOT NULL,
`name` text NOT NULL,
`internal_project_number` text,
`external_project_number` text,
`building_owner` text,
`description` text,
`single_phase_voltage_v` integer DEFAULT 230 NOT NULL,
`three_phase_voltage_v` integer DEFAULT 400 NOT NULL,
`enabled_distribution_board_supply_types` text DEFAULT '["AV","SV","EV","USV","MSR","SiBe"]' NOT NULL,
`current_revision` integer DEFAULT 0 NOT NULL
);
--> statement-breakpoint
CREATE TABLE `rooms` (
`id` text PRIMARY KEY NOT NULL,
`project_id` text NOT NULL,
`floor_id` text,
`room_number` text NOT NULL,
`room_name` text NOT NULL,
FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`floor_id`) REFERENCES `floors`(`id`) ON UPDATE no action ON DELETE set null
);
--> statement-breakpoint
CREATE UNIQUE INDEX `circuit_list_equipment_identifiers_normalized_unique`
ON `circuit_list_equipment_identifiers` (`circuit_list_id`, lower(trim(`equipment_identifier`)));
--> statement-breakpoint
CREATE UNIQUE INDEX `distribution_board_components_fixed_role_unique`
ON `distribution_board_components` (`circuit_list_id`, `role`)
WHERE `role` IN ('main_switch', 'surge_protective_device');
--> statement-breakpoint
CREATE TRIGGER `circuits_equipment_identifier_insert`
AFTER INSERT ON `circuits`
BEGIN
INSERT INTO `circuit_list_equipment_identifiers`
(`owner_type`, `owner_id`, `circuit_list_id`, `equipment_identifier`)
VALUES ('circuit', NEW.`id`, NEW.`circuit_list_id`, NEW.`equipment_identifier`);
END;
--> statement-breakpoint
CREATE TRIGGER `circuits_equipment_identifier_update`
AFTER UPDATE OF `circuit_list_id`, `equipment_identifier` ON `circuits`
BEGIN
UPDATE `circuit_list_equipment_identifiers`
SET `circuit_list_id` = NEW.`circuit_list_id`,
`equipment_identifier` = NEW.`equipment_identifier`
WHERE `owner_type` = 'circuit' AND `owner_id` = OLD.`id`;
END;
--> statement-breakpoint
CREATE TRIGGER `circuits_equipment_identifier_delete`
AFTER DELETE ON `circuits`
BEGIN
DELETE FROM `circuit_list_equipment_identifiers`
WHERE `owner_type` = 'circuit' AND `owner_id` = OLD.`id`;
END;
--> statement-breakpoint
CREATE TRIGGER `distribution_board_components_equipment_identifier_insert`
AFTER INSERT ON `distribution_board_components`
BEGIN
INSERT INTO `circuit_list_equipment_identifiers`
(`owner_type`, `owner_id`, `circuit_list_id`, `equipment_identifier`)
VALUES ('distribution_board_component', NEW.`id`, NEW.`circuit_list_id`, NEW.`equipment_identifier`);
END;
--> statement-breakpoint
CREATE TRIGGER `distribution_board_components_equipment_identifier_update`
AFTER UPDATE OF `circuit_list_id`, `equipment_identifier` ON `distribution_board_components`
BEGIN
UPDATE `circuit_list_equipment_identifiers`
SET `circuit_list_id` = NEW.`circuit_list_id`,
`equipment_identifier` = NEW.`equipment_identifier`
WHERE `owner_type` = 'distribution_board_component' AND `owner_id` = OLD.`id`;
END;
--> statement-breakpoint
CREATE TRIGGER `distribution_board_components_equipment_identifier_delete`
AFTER DELETE ON `distribution_board_components`
BEGIN
DELETE FROM `circuit_list_equipment_identifiers`
WHERE `owner_type` = 'distribution_board_component' AND `owner_id` = OLD.`id`;
END;
-12
View File
@@ -1,12 +0,0 @@
CREATE TABLE `global_devices` (
`id` text PRIMARY KEY NOT NULL,
`name` text NOT NULL,
`category` text,
`quantity` integer NOT NULL,
`installed_power_per_unit_kw` real NOT NULL,
`demand_factor` real NOT NULL,
`voltage_v` real,
`phase_count` integer,
`power_factor` real,
`note` text
);
@@ -1,3 +0,0 @@
ALTER TABLE `projects` ADD COLUMN `single_phase_voltage_v` integer NOT NULL DEFAULT 230;
--> statement-breakpoint
ALTER TABLE `projects` ADD COLUMN `three_phase_voltage_v` integer NOT NULL DEFAULT 400;
@@ -1,19 +0,0 @@
CREATE TABLE `floors` (
`id` text PRIMARY KEY NOT NULL,
`project_id` text NOT NULL,
`name` text NOT NULL,
`sort_order` integer NOT NULL DEFAULT 0,
FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `rooms` (
`id` text PRIMARY KEY NOT NULL,
`project_id` text NOT NULL,
`floor_id` text,
`room_number` text NOT NULL,
`room_name` text NOT NULL,
FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`floor_id`) REFERENCES `floors`(`id`) ON UPDATE no action ON DELETE set null
);
--> statement-breakpoint
ALTER TABLE `consumers` ADD COLUMN `room_id` text REFERENCES `rooms`(`id`) ON UPDATE no action ON DELETE set null;
@@ -1,44 +0,0 @@
CREATE TABLE `circuit_lists` (
`id` text PRIMARY KEY NOT NULL,
`project_id` text NOT NULL,
`distribution_board_id` text NOT NULL,
`name` text NOT NULL,
FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`distribution_board_id`) REFERENCES `distribution_boards`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `circuit_lists_distribution_board_id_unique` ON `circuit_lists` (`distribution_board_id`);
--> statement-breakpoint
INSERT INTO `circuit_lists` (`id`, `project_id`, `distribution_board_id`, `name`)
SELECT `id`, `project_id`, `id`, `name` || ' Stromkreisliste'
FROM `distribution_boards`;
--> statement-breakpoint
ALTER TABLE `consumers` ADD COLUMN `circuit_list_id` text REFERENCES `circuit_lists`(`id`) ON UPDATE no action ON DELETE set null;
--> statement-breakpoint
UPDATE `consumers` SET `circuit_list_id` = `distribution_board_id` WHERE `distribution_board_id` IS NOT NULL;
--> statement-breakpoint
ALTER TABLE `consumers` ADD COLUMN `circuit_number` text;
--> statement-breakpoint
ALTER TABLE `consumers` ADD COLUMN `description` text;
--> statement-breakpoint
UPDATE `consumers` SET `description` = `name` WHERE `description` IS NULL;
--> statement-breakpoint
ALTER TABLE `consumers` ADD COLUMN `device_type` text;
--> statement-breakpoint
ALTER TABLE `consumers` ADD COLUMN `phase_type` text;
--> statement-breakpoint
ALTER TABLE `consumers` ADD COLUMN `trade_or_cost_group` text;
--> statement-breakpoint
ALTER TABLE `consumers` ADD COLUMN `group_name` text;
--> statement-breakpoint
ALTER TABLE `consumers` ADD COLUMN `protection_type` text;
--> statement-breakpoint
ALTER TABLE `consumers` ADD COLUMN `protection_rated_current` real;
--> statement-breakpoint
ALTER TABLE `consumers` ADD COLUMN `protection_characteristic` text;
--> statement-breakpoint
ALTER TABLE `consumers` ADD COLUMN `cable_type` text;
--> statement-breakpoint
ALTER TABLE `consumers` ADD COLUMN `cable_cross_section` text;
--> statement-breakpoint
ALTER TABLE `consumers` ADD COLUMN `comment` text;
@@ -1,14 +0,0 @@
CREATE TABLE `project_devices` (
`id` text PRIMARY KEY NOT NULL,
`project_id` text NOT NULL,
`name` text NOT NULL,
`category` text,
`quantity` integer NOT NULL,
`installed_power_per_unit_kw` real NOT NULL,
`demand_factor` real NOT NULL,
`voltage_v` real,
`phase_count` integer,
`power_factor` real,
`note` text,
FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE cascade
);
@@ -1,7 +0,0 @@
ALTER TABLE `global_devices` ADD COLUMN `display_name` text;
--> statement-breakpoint
UPDATE `global_devices` SET `display_name` = `name` WHERE `display_name` IS NULL;
--> statement-breakpoint
ALTER TABLE `project_devices` ADD COLUMN `display_name` text;
--> statement-breakpoint
UPDATE `project_devices` SET `display_name` = `name` WHERE `display_name` IS NULL;
@@ -1,3 +0,0 @@
ALTER TABLE `consumers` ADD COLUMN `project_device_id` text REFERENCES `project_devices`(`id`) ON UPDATE no action ON DELETE set null;
--> statement-breakpoint
ALTER TABLE `consumers` ADD COLUMN `is_linked_to_device` integer NOT NULL DEFAULT 0;
@@ -1,99 +0,0 @@
CREATE TABLE `circuit_sections` (
`id` text PRIMARY KEY NOT NULL,
`circuit_list_id` text NOT NULL,
`key` text NOT NULL,
`display_name` text NOT NULL,
`prefix` text NOT NULL,
`sort_order` integer NOT NULL DEFAULT 0,
FOREIGN KEY (`circuit_list_id`) REFERENCES `circuit_lists`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `circuit_sections_list_key_unique` ON `circuit_sections` (`circuit_list_id`,`key`);
--> statement-breakpoint
CREATE UNIQUE INDEX `circuit_sections_list_prefix_unique` ON `circuit_sections` (`circuit_list_id`,`prefix`);
--> statement-breakpoint
CREATE TABLE `circuits` (
`id` text PRIMARY KEY NOT NULL,
`circuit_list_id` text NOT NULL,
`section_id` text NOT NULL,
`equipment_identifier` text NOT NULL,
`display_name` text,
`sort_order` integer NOT NULL DEFAULT 0,
`protection_type` text,
`protection_rated_current` real,
`protection_characteristic` text,
`cable_type` text,
`cable_cross_section` text,
`cable_length` real,
`rcd_assignment` text,
`terminal_designation` text,
`voltage` real,
`status` text,
`is_reserve` integer NOT NULL DEFAULT 0,
`remark` text,
FOREIGN KEY (`circuit_list_id`) REFERENCES `circuit_lists`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`section_id`) REFERENCES `circuit_sections`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `circuits_list_equipment_identifier_unique` ON `circuits` (`circuit_list_id`,`equipment_identifier`);
--> statement-breakpoint
CREATE TABLE `circuit_device_rows` (
`id` text PRIMARY KEY NOT NULL,
`circuit_id` text NOT NULL,
`linked_project_device_id` text,
`legacy_consumer_id` text,
`sort_order` integer NOT NULL DEFAULT 0,
`name` text NOT NULL,
`display_name` text NOT NULL,
`phase_type` text,
`connection_kind` text,
`cost_group` text,
`category` text,
`level` text,
`room_id` text,
`room_number_snapshot` text,
`room_name_snapshot` text,
`quantity` integer NOT NULL,
`power_per_unit` real NOT NULL,
`simultaneity_factor` real NOT NULL,
`cos_phi` real,
`remark` text,
`overridden_fields` text,
FOREIGN KEY (`circuit_id`) REFERENCES `circuits`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`linked_project_device_id`) REFERENCES `project_devices`(`id`) ON UPDATE no action ON DELETE set null,
FOREIGN KEY (`room_id`) REFERENCES `rooms`(`id`) ON UPDATE no action ON DELETE set null
);
--> statement-breakpoint
CREATE TABLE `legacy_consumer_circuit_migrations` (
`consumer_id` text PRIMARY KEY NOT NULL,
`circuit_id` text NOT NULL,
`circuit_device_row_id` text NOT NULL,
`circuit_list_id` text NOT NULL,
`created_at_iso` text NOT NULL,
FOREIGN KEY (`circuit_id`) REFERENCES `circuits`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`circuit_device_row_id`) REFERENCES `circuit_device_rows`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`circuit_list_id`) REFERENCES `circuit_lists`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `legacy_consumer_migration_reports` (
`id` text PRIMARY KEY NOT NULL,
`circuit_list_id` text NOT NULL,
`legacy_consumer_count` integer NOT NULL,
`created_circuit_count` integer NOT NULL,
`created_device_row_count` integer NOT NULL,
`duplicate_grouped_count` integer NOT NULL,
`generated_identifier_count` integer NOT NULL,
`unassigned_row_count` integer NOT NULL,
`warnings_json` text NOT NULL,
`generated_identifiers_json` text NOT NULL,
`duplicate_groups_json` text NOT NULL,
`created_at_iso` text NOT NULL,
FOREIGN KEY (`circuit_list_id`) REFERENCES `circuit_lists`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `legacy_consumer_migration_reports_list_unique` ON `legacy_consumer_migration_reports` (`circuit_list_id`);
@@ -1,24 +0,0 @@
ALTER TABLE `project_devices` ADD COLUMN `phase_type` text NOT NULL DEFAULT 'single_phase';
--> statement-breakpoint
UPDATE `project_devices`
SET `phase_type` = CASE WHEN `phase_count` = 3 THEN 'three_phase' ELSE 'single_phase' END;
--> statement-breakpoint
ALTER TABLE `project_devices` ADD COLUMN `connection_kind` text;
--> statement-breakpoint
ALTER TABLE `project_devices` ADD COLUMN `cost_group` text;
--> statement-breakpoint
ALTER TABLE `project_devices` ADD COLUMN `power_per_unit` real NOT NULL DEFAULT 0;
--> statement-breakpoint
UPDATE `project_devices` SET `power_per_unit` = `installed_power_per_unit_kw`;
--> statement-breakpoint
ALTER TABLE `project_devices` ADD COLUMN `simultaneity_factor` real NOT NULL DEFAULT 1;
--> statement-breakpoint
UPDATE `project_devices` SET `simultaneity_factor` = `demand_factor`;
--> statement-breakpoint
ALTER TABLE `project_devices` ADD COLUMN `cos_phi` real;
--> statement-breakpoint
UPDATE `project_devices` SET `cos_phi` = `power_factor`;
--> statement-breakpoint
ALTER TABLE `project_devices` ADD COLUMN `remark` text;
--> statement-breakpoint
UPDATE `project_devices` SET `remark` = `note`;
@@ -1 +0,0 @@
ALTER TABLE `circuits` ADD COLUMN `control_requirement` text;
@@ -1,9 +0,0 @@
ALTER TABLE `project_devices` DROP COLUMN `installed_power_per_unit_kw`;
--> statement-breakpoint
ALTER TABLE `project_devices` DROP COLUMN `demand_factor`;
--> statement-breakpoint
ALTER TABLE `project_devices` DROP COLUMN `phase_count`;
--> statement-breakpoint
ALTER TABLE `project_devices` DROP COLUMN `power_factor`;
--> statement-breakpoint
ALTER TABLE `project_devices` DROP COLUMN `note`;
@@ -1,24 +0,0 @@
CREATE TABLE `project_change_sets` (
`id` text PRIMARY KEY NOT NULL,
`project_revision_id` text NOT NULL,
`command_type` text NOT NULL,
`payload_schema_version` integer NOT NULL,
`forward_payload_json` text NOT NULL,
`inverse_payload_json` text NOT NULL,
FOREIGN KEY (`project_revision_id`) REFERENCES `project_revisions`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `project_change_sets_revision_unique` ON `project_change_sets` (`project_revision_id`);--> statement-breakpoint
CREATE TABLE `project_revisions` (
`id` text PRIMARY KEY NOT NULL,
`project_id` text NOT NULL,
`revision_number` integer NOT NULL,
`created_at_iso` text NOT NULL,
`actor_id` text,
`source` text NOT NULL,
`description` text,
FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `project_revisions_project_number_unique` ON `project_revisions` (`project_id`,`revision_number`);--> statement-breakpoint
ALTER TABLE `projects` ADD `current_revision` integer DEFAULT 0 NOT NULL;
@@ -1,11 +0,0 @@
CREATE TABLE `project_history_stack_entries` (
`change_set_id` text PRIMARY KEY NOT NULL,
`project_id` text NOT NULL,
`stack` text NOT NULL,
`position` integer NOT NULL,
FOREIGN KEY (`change_set_id`) REFERENCES `project_change_sets`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE cascade,
CONSTRAINT "project_history_stack_entries_stack_check" CHECK("project_history_stack_entries"."stack" in ('undo', 'redo'))
);
--> statement-breakpoint
CREATE UNIQUE INDEX `project_history_stack_entries_position_unique` ON `project_history_stack_entries` (`project_id`,`stack`,`position`);
@@ -1,16 +0,0 @@
CREATE TABLE `project_snapshots` (
`id` text PRIMARY KEY NOT NULL,
`project_id` text NOT NULL,
`source_revision` integer NOT NULL,
`schema_version` integer NOT NULL,
`name` text NOT NULL,
`description` text,
`payload_json` text NOT NULL,
`payload_sha256` text NOT NULL,
`created_at_iso` text NOT NULL,
`created_by_actor_id` text,
FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE INDEX `project_snapshots_project_created_idx` ON `project_snapshots` (`project_id`,`created_at_iso`);--> statement-breakpoint
CREATE UNIQUE INDEX `project_snapshots_project_name_unique` ON `project_snapshots` (`project_id`,`name`);
@@ -1,2 +0,0 @@
ALTER TABLE `project_snapshots` ADD `kind` text DEFAULT 'named' NOT NULL;--> statement-breakpoint
CREATE INDEX `project_snapshots_project_kind_revision_idx` ON `project_snapshots` (`project_id`,`kind`,`source_revision`);
@@ -1,4 +0,0 @@
ALTER TABLE `projects` ADD `internal_project_number` text;--> statement-breakpoint
ALTER TABLE `projects` ADD `external_project_number` text;--> statement-breakpoint
ALTER TABLE `projects` ADD `building_owner` text;--> statement-breakpoint
ALTER TABLE `projects` ADD `description` text;
@@ -1,2 +0,0 @@
ALTER TABLE `distribution_boards` ADD `floor_id` text REFERENCES floors(id);--> statement-breakpoint
ALTER TABLE `distribution_boards` ADD `supply_type` text;
-1
View File
@@ -1 +0,0 @@
ALTER TABLE `projects` ADD `enabled_distribution_board_supply_types` text DEFAULT '["AV","SV","EV","USV","MSR","SiBe"]' NOT NULL;
@@ -1,48 +0,0 @@
UPDATE `global_devices`
SET `voltage_v` = NULL;
--> statement-breakpoint
UPDATE `project_devices` AS `pd`
SET `voltage_v` = CASE
WHEN `pd`.`phase_type` = 'three_phase' THEN `p`.`three_phase_voltage_v`
ELSE `p`.`single_phase_voltage_v`
END
FROM `projects` AS `p`
WHERE `p`.`id` = `pd`.`project_id`;
--> statement-breakpoint
WITH `derived_circuit_voltages` AS (
SELECT
`c`.`id` AS `circuit_id`,
CASE
WHEN `cs`.`key` = 'three_phase' THEN `p`.`three_phase_voltage_v`
WHEN `cs`.`key` IN ('lighting', 'single_phase') THEN `p`.`single_phase_voltage_v`
WHEN EXISTS (
SELECT 1
FROM `circuit_device_rows` AS `three_phase_row`
WHERE `three_phase_row`.`circuit_id` = `c`.`id`
AND `three_phase_row`.`phase_type` = 'three_phase'
) AND NOT EXISTS (
SELECT 1
FROM `circuit_device_rows` AS `other_row`
WHERE `other_row`.`circuit_id` = `c`.`id`
AND `other_row`.`phase_type` = 'single_phase'
) THEN `p`.`three_phase_voltage_v`
ELSE `p`.`single_phase_voltage_v`
END AS `derived_voltage`
FROM `circuits` AS `c`
INNER JOIN `circuit_lists` AS `cl`
ON `cl`.`id` = `c`.`circuit_list_id`
INNER JOIN `projects` AS `p`
ON `p`.`id` = `cl`.`project_id`
INNER JOIN `circuit_sections` AS `cs`
ON `cs`.`id` = `c`.`section_id`
)
UPDATE `circuits`
SET `voltage` = (
SELECT `derived_voltage`
FROM `derived_circuit_voltages`
WHERE `circuit_id` = `circuits`.`id`
)
WHERE `id` IN (
SELECT `circuit_id`
FROM `derived_circuit_voltages`
);
@@ -1,85 +0,0 @@
UPDATE `project_devices`
SET `phase_type` = CASE
WHEN LOWER(REPLACE(REPLACE(TRIM(`phase_type`), '-', ''), '_', ''))
IN ('1ph', '1phase', '1phasig', 'einphasig', 'singlephase')
THEN 'single_phase'
WHEN LOWER(REPLACE(REPLACE(TRIM(`phase_type`), '-', ''), '_', ''))
IN ('3ph', '3phase', '3phasig', 'dreiphasig', 'threephase')
THEN 'three_phase'
ELSE `phase_type`
END
WHERE LOWER(REPLACE(REPLACE(TRIM(`phase_type`), '-', ''), '_', ''))
IN (
'1ph',
'1phase',
'1phasig',
'einphasig',
'singlephase',
'3ph',
'3phase',
'3phasig',
'dreiphasig',
'threephase'
);
--> statement-breakpoint
UPDATE `circuit_device_rows`
SET `phase_type` = CASE
WHEN LOWER(REPLACE(REPLACE(TRIM(`phase_type`), '-', ''), '_', ''))
IN ('1ph', '1phase', '1phasig', 'einphasig', 'singlephase')
THEN 'single_phase'
WHEN LOWER(REPLACE(REPLACE(TRIM(`phase_type`), '-', ''), '_', ''))
IN ('3ph', '3phase', '3phasig', 'dreiphasig', 'threephase')
THEN 'three_phase'
ELSE `phase_type`
END
WHERE LOWER(REPLACE(REPLACE(TRIM(`phase_type`), '-', ''), '_', ''))
IN (
'1ph',
'1phase',
'1phasig',
'einphasig',
'singlephase',
'3ph',
'3phase',
'3phasig',
'dreiphasig',
'threephase'
);
--> statement-breakpoint
WITH `derived_circuit_voltages` AS (
SELECT
`c`.`id` AS `circuit_id`,
CASE
WHEN `cs`.`key` = 'three_phase' THEN `p`.`three_phase_voltage_v`
WHEN `cs`.`key` IN ('lighting', 'single_phase') THEN `p`.`single_phase_voltage_v`
WHEN EXISTS (
SELECT 1
FROM `circuit_device_rows` AS `three_phase_row`
WHERE `three_phase_row`.`circuit_id` = `c`.`id`
AND `three_phase_row`.`phase_type` = 'three_phase'
) AND NOT EXISTS (
SELECT 1
FROM `circuit_device_rows` AS `single_phase_row`
WHERE `single_phase_row`.`circuit_id` = `c`.`id`
AND `single_phase_row`.`phase_type` = 'single_phase'
) THEN `p`.`three_phase_voltage_v`
ELSE `p`.`single_phase_voltage_v`
END AS `derived_voltage`
FROM `circuits` AS `c`
INNER JOIN `circuit_lists` AS `cl`
ON `cl`.`id` = `c`.`circuit_list_id`
INNER JOIN `projects` AS `p`
ON `p`.`id` = `cl`.`project_id`
INNER JOIN `circuit_sections` AS `cs`
ON `cs`.`id` = `c`.`section_id`
)
UPDATE `circuits`
SET `voltage` = (
SELECT `derived_voltage`
FROM `derived_circuit_voltages`
WHERE `circuit_id` = `circuits`.`id`
)
WHERE `id` IN (
SELECT `circuit_id`
FROM `derived_circuit_voltages`
);
@@ -1,75 +0,0 @@
UPDATE `circuit_device_rows`
SET `phase_type` = COALESCE(
(
SELECT `pd`.`phase_type`
FROM `project_devices` AS `pd`
WHERE `pd`.`id` = `circuit_device_rows`.`linked_project_device_id`
AND `pd`.`phase_type` IN ('single_phase', 'three_phase')
),
(
SELECT CASE
WHEN `cs`.`key` = 'three_phase' THEN 'three_phase'
WHEN `cs`.`key` IN ('lighting', 'single_phase') THEN 'single_phase'
ELSE NULL
END
FROM `circuits` AS `c`
INNER JOIN `circuit_sections` AS `cs`
ON `cs`.`id` = `c`.`section_id`
WHERE `c`.`id` = `circuit_device_rows`.`circuit_id`
)
)
WHERE `phase_type` IS NULL
AND (
EXISTS (
SELECT 1
FROM `project_devices` AS `pd`
WHERE `pd`.`id` = `circuit_device_rows`.`linked_project_device_id`
AND `pd`.`phase_type` IN ('single_phase', 'three_phase')
)
OR EXISTS (
SELECT 1
FROM `circuits` AS `c`
INNER JOIN `circuit_sections` AS `cs`
ON `cs`.`id` = `c`.`section_id`
WHERE `c`.`id` = `circuit_device_rows`.`circuit_id`
AND `cs`.`key` IN ('lighting', 'single_phase', 'three_phase')
)
);
--> statement-breakpoint
WITH `derived_circuit_voltages` AS (
SELECT
`c`.`id` AS `circuit_id`,
CASE
WHEN `cs`.`key` = 'three_phase' THEN `p`.`three_phase_voltage_v`
WHEN `cs`.`key` IN ('lighting', 'single_phase') THEN `p`.`single_phase_voltage_v`
WHEN EXISTS (
SELECT 1
FROM `circuit_device_rows` AS `three_phase_row`
WHERE `three_phase_row`.`circuit_id` = `c`.`id`
AND `three_phase_row`.`phase_type` = 'three_phase'
) AND NOT EXISTS (
SELECT 1
FROM `circuit_device_rows` AS `single_phase_row`
WHERE `single_phase_row`.`circuit_id` = `c`.`id`
AND `single_phase_row`.`phase_type` = 'single_phase'
) THEN `p`.`three_phase_voltage_v`
ELSE `p`.`single_phase_voltage_v`
END AS `derived_voltage`
FROM `circuits` AS `c`
INNER JOIN `circuit_lists` AS `cl`
ON `cl`.`id` = `c`.`circuit_list_id`
INNER JOIN `projects` AS `p`
ON `p`.`id` = `cl`.`project_id`
INNER JOIN `circuit_sections` AS `cs`
ON `cs`.`id` = `c`.`section_id`
)
UPDATE `circuits`
SET `voltage` = (
SELECT `derived_voltage`
FROM `derived_circuit_voltages`
WHERE `circuit_id` = `circuits`.`id`
)
WHERE `id` IN (
SELECT `circuit_id`
FROM `derived_circuit_voltages`
);
@@ -1,7 +0,0 @@
UPDATE `circuit_device_rows`
SET `phase_type` = 'single_phase'
WHERE `phase_type` IS NULL;
--> statement-breakpoint
UPDATE `global_devices`
SET `phase_count` = 1
WHERE `phase_count` IS NULL OR `phase_count` NOT IN (1, 3);
@@ -1 +0,0 @@
ALTER TABLE `distribution_boards` ADD `simultaneity_factor` real DEFAULT 1 NOT NULL;
@@ -1,150 +0,0 @@
CREATE TABLE `circuit_list_equipment_identifiers` (
`owner_type` text NOT NULL,
`owner_id` text NOT NULL,
`circuit_list_id` text NOT NULL,
`equipment_identifier` text NOT NULL,
FOREIGN KEY (`circuit_list_id`) REFERENCES `circuit_lists`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `circuit_list_equipment_identifiers_owner_unique` ON `circuit_list_equipment_identifiers` (`owner_type`,`owner_id`);--> statement-breakpoint
CREATE UNIQUE INDEX `circuit_list_equipment_identifiers_list_identifier_unique` ON `circuit_list_equipment_identifiers` (`circuit_list_id`,`equipment_identifier`);--> statement-breakpoint
CREATE TABLE `circuit_protection_devices` (
`circuit_id` text PRIMARY KEY NOT NULL,
`type` text NOT NULL,
`rated_current_a` real NOT NULL,
`fuse_utilization_category` text,
`trip_characteristic` text,
`rcd_type` text,
`rated_residual_current_ma` real,
FOREIGN KEY (`circuit_id`) REFERENCES `circuits`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `distribution_board_component_protection_devices` (
`component_id` text PRIMARY KEY NOT NULL,
`type` text NOT NULL,
`rated_current_a` real NOT NULL,
`fuse_utilization_category` text,
`trip_characteristic` text,
`rcd_type` text,
`rated_residual_current_ma` real,
FOREIGN KEY (`component_id`) REFERENCES `distribution_board_components`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `distribution_board_components` (
`id` text PRIMARY KEY NOT NULL,
`circuit_list_id` text NOT NULL,
`section_id` text,
`equipment_identifier` text NOT NULL,
`name` text NOT NULL,
`role` text NOT NULL,
`placement` text NOT NULL,
`sort_order` integer DEFAULT 0 NOT NULL,
FOREIGN KEY (`circuit_list_id`) REFERENCES `circuit_lists`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`section_id`) REFERENCES `circuit_sections`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `distribution_board_components_list_identifier_unique` ON `distribution_board_components` (`circuit_list_id`,`equipment_identifier`);--> statement-breakpoint
CREATE UNIQUE INDEX `distribution_board_components_section_role_unique` ON `distribution_board_components` (`section_id`,`role`);--> statement-breakpoint
ALTER TABLE `circuit_sections` ADD `category` text;--> statement-breakpoint
ALTER TABLE `circuit_sections` ADD `group_number` integer;--> statement-breakpoint
CREATE UNIQUE INDEX `circuit_sections_list_category_group_unique` ON `circuit_sections` (`circuit_list_id`,`category`,`group_number`);--> statement-breakpoint
UPDATE `circuit_sections`
SET
`category` = CASE `key`
WHEN 'lighting' THEN 'lighting'
WHEN 'single_phase' THEN 'single_phase'
WHEN 'three_phase' THEN 'three_phase'
END,
`group_number` = 1
WHERE `key` IN ('lighting', 'single_phase', 'three_phase');--> statement-breakpoint
INSERT INTO `circuit_list_equipment_identifiers` (
`owner_type`,
`owner_id`,
`circuit_list_id`,
`equipment_identifier`
)
SELECT
'circuit',
`id`,
`circuit_list_id`,
`equipment_identifier`
FROM `circuits`;--> statement-breakpoint
CREATE UNIQUE INDEX `circuit_list_equipment_identifiers_normalized_unique`
ON `circuit_list_equipment_identifiers` (
`circuit_list_id`,
lower(trim(`equipment_identifier`))
);--> statement-breakpoint
CREATE UNIQUE INDEX `distribution_board_components_fixed_role_unique`
ON `distribution_board_components` (`circuit_list_id`, `role`)
WHERE `role` IN ('main_switch', 'surge_protective_device');--> statement-breakpoint
CREATE TRIGGER `circuits_equipment_identifier_insert`
AFTER INSERT ON `circuits`
BEGIN
INSERT INTO `circuit_list_equipment_identifiers` (
`owner_type`,
`owner_id`,
`circuit_list_id`,
`equipment_identifier`
)
VALUES (
'circuit',
NEW.`id`,
NEW.`circuit_list_id`,
NEW.`equipment_identifier`
);
END;--> statement-breakpoint
CREATE TRIGGER `circuits_equipment_identifier_update`
AFTER UPDATE OF `circuit_list_id`, `equipment_identifier` ON `circuits`
BEGIN
UPDATE `circuit_list_equipment_identifiers`
SET
`circuit_list_id` = NEW.`circuit_list_id`,
`equipment_identifier` = NEW.`equipment_identifier`
WHERE
`owner_type` = 'circuit'
AND `owner_id` = OLD.`id`;
END;--> statement-breakpoint
CREATE TRIGGER `circuits_equipment_identifier_delete`
AFTER DELETE ON `circuits`
BEGIN
DELETE FROM `circuit_list_equipment_identifiers`
WHERE
`owner_type` = 'circuit'
AND `owner_id` = OLD.`id`;
END;--> statement-breakpoint
CREATE TRIGGER `distribution_board_components_equipment_identifier_insert`
AFTER INSERT ON `distribution_board_components`
BEGIN
INSERT INTO `circuit_list_equipment_identifiers` (
`owner_type`,
`owner_id`,
`circuit_list_id`,
`equipment_identifier`
)
VALUES (
'distribution_board_component',
NEW.`id`,
NEW.`circuit_list_id`,
NEW.`equipment_identifier`
);
END;--> statement-breakpoint
CREATE TRIGGER `distribution_board_components_equipment_identifier_update`
AFTER UPDATE OF `circuit_list_id`, `equipment_identifier`
ON `distribution_board_components`
BEGIN
UPDATE `circuit_list_equipment_identifiers`
SET
`circuit_list_id` = NEW.`circuit_list_id`,
`equipment_identifier` = NEW.`equipment_identifier`
WHERE
`owner_type` = 'distribution_board_component'
AND `owner_id` = OLD.`id`;
END;--> statement-breakpoint
CREATE TRIGGER `distribution_board_components_equipment_identifier_delete`
AFTER DELETE ON `distribution_board_components`
BEGIN
DELETE FROM `circuit_list_equipment_identifiers`
WHERE
`owner_type` = 'distribution_board_component'
AND `owner_id` = OLD.`id`;
END;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+2 -170
View File
@@ -5,176 +5,8 @@
{
"idx": 0,
"version": "6",
"when": 1777565414148,
"tag": "0000_bizarre_colossus",
"breakpoints": true
},
{
"idx": 1,
"version": "6",
"when": 1777577000000,
"tag": "0001_global_devices",
"breakpoints": true
},
{
"idx": 2,
"version": "6",
"when": 1777580000000,
"tag": "0002_project_voltage_defaults",
"breakpoints": true
},
{
"idx": 3,
"version": "6",
"when": 1777589000000,
"tag": "0003_project_floors_rooms",
"breakpoints": true
},
{
"idx": 4,
"version": "6",
"when": 1777594000000,
"tag": "0004_circuit_lists_and_entry_fields",
"breakpoints": true
},
{
"idx": 5,
"version": "6",
"when": 1777597000000,
"tag": "0005_project_devices",
"breakpoints": true
},
{
"idx": 6,
"version": "6",
"when": 1777652000000,
"tag": "0006_device_display_name",
"breakpoints": true
},
{
"idx": 7,
"version": "6",
"when": 1777680000000,
"tag": "0007_consumer_device_link",
"breakpoints": true
},
{
"idx": 8,
"version": "6",
"when": 1777800000000,
"tag": "0008_circuit_first_model",
"breakpoints": true
},
{
"idx": 9,
"version": "6",
"when": 1784743200000,
"tag": "0009_project_device_circuit_fields",
"breakpoints": true
},
{
"idx": 10,
"version": "6",
"when": 1784746800000,
"tag": "0010_circuit_control_requirement",
"breakpoints": true
},
{
"idx": 11,
"version": "6",
"when": 1784832541202,
"tag": "0011_project_device_canonical_fields",
"breakpoints": true
},
{
"idx": 12,
"version": "6",
"when": 1784833957929,
"tag": "0012_project_revision_foundation",
"breakpoints": true
},
{
"idx": 13,
"version": "6",
"when": 1784835703230,
"tag": "0013_project_history_stacks",
"breakpoints": true
},
{
"idx": 14,
"version": "6",
"when": 1785009799979,
"tag": "0014_project_snapshots",
"breakpoints": true
},
{
"idx": 15,
"version": "6",
"when": 1785014383032,
"tag": "0015_modern_madame_masque",
"breakpoints": true
},
{
"idx": 16,
"version": "6",
"when": 1785254079435,
"tag": "0016_dashing_darkstar",
"breakpoints": true
},
{
"idx": 17,
"version": "6",
"when": 1785307189539,
"tag": "0017_vengeful_romulus",
"breakpoints": true
},
{
"idx": 18,
"version": "6",
"when": 1785308458789,
"tag": "0018_fancy_argent",
"breakpoints": true
},
{
"idx": 19,
"version": "6",
"when": 1785310907349,
"tag": "0019_normalize_project_voltages",
"breakpoints": true
},
{
"idx": 20,
"version": "6",
"when": 1785314248007,
"tag": "0020_normalize_phase_types",
"breakpoints": true
},
{
"idx": 21,
"version": "6",
"when": 1785314558555,
"tag": "0021_fill_missing_phase_types",
"breakpoints": true
},
{
"idx": 22,
"version": "6",
"when": 1785314626476,
"tag": "0022_default_remaining_phase_types",
"breakpoints": true
},
{
"idx": 23,
"version": "6",
"when": 1785343645615,
"tag": "0023_cheerful_night_thrasher",
"breakpoints": true
},
{
"idx": 24,
"version": "6",
"when": 1785431250141,
"tag": "0024_damp_mister_sinister",
"when": 1785499119431,
"tag": "0000_whole_surge",
"breakpoints": true
}
]
@@ -382,9 +382,6 @@ export class CircuitDeviceRowMoveProjectCommandRepository
equipmentIdentifier: snapshot.equipmentIdentifier,
displayName: snapshot.displayName,
sortOrder: snapshot.sortOrder,
protectionType: snapshot.protectionType,
protectionRatedCurrent: snapshot.protectionRatedCurrent,
protectionCharacteristic: snapshot.protectionCharacteristic,
cableType: snapshot.cableType,
cableCrossSection: snapshot.cableCrossSection,
cableLength: snapshot.cableLength,
@@ -423,11 +420,6 @@ export class CircuitDeviceRowMoveProjectCommandRepository
snapshot.equipmentIdentifier ||
current.displayName !== snapshot.displayName ||
current.sortOrder !== snapshot.sortOrder ||
current.protectionType !== snapshot.protectionType ||
current.protectionRatedCurrent !==
snapshot.protectionRatedCurrent ||
current.protectionCharacteristic !==
snapshot.protectionCharacteristic ||
current.cableType !== snapshot.cableType ||
current.cableCrossSection !== snapshot.cableCrossSection ||
current.cableLength !== snapshot.cableLength ||
@@ -77,11 +77,6 @@ export class CircuitDeviceRowStructureProjectCommandRepository
source: ExecuteCircuitDeviceRowStructureCommandInput["source"],
row: CircuitDeviceRowSnapshot
) {
if (source === "user" && row.legacyConsumerId !== null) {
throw new Error(
"User commands cannot assign legacy consumer ids."
);
}
if (source === "user" && !isElectricalPhaseType(row.phaseType)) {
throw new Error("Device-row phase type is invalid.");
}
@@ -49,7 +49,6 @@ export function toCircuitDeviceRowSnapshot(
id: row.id,
circuitId: row.circuitId,
linkedProjectDeviceId: row.linkedProjectDeviceId,
legacyConsumerId: row.legacyConsumerId,
sortOrder: row.sortOrder,
name: row.name,
displayName: row.displayName,
@@ -44,7 +44,6 @@ export interface CircuitDeviceRowPatchInput {
export interface CircuitDeviceRowCreateInput extends CircuitDeviceRowUpdateInput {
circuitId: string;
linkedProjectDeviceId?: string;
legacyConsumerId?: string;
sortOrder: number;
}
@@ -110,7 +109,6 @@ export function toCircuitDeviceRowCreateValues(
return {
id,
circuitId: input.circuitId,
legacyConsumerId: input.legacyConsumerId ?? null,
sortOrder: input.sortOrder,
...toCircuitDeviceRowUpdateValues(input),
};
@@ -25,7 +25,6 @@ export class CircuitDeviceRowRepository {
id: circuitDeviceRows.id,
circuitId: circuitDeviceRows.circuitId,
linkedProjectDeviceId: circuitDeviceRows.linkedProjectDeviceId,
legacyConsumerId: circuitDeviceRows.legacyConsumerId,
sortOrder: circuitDeviceRows.sortOrder,
name: circuitDeviceRows.name,
displayName: circuitDeviceRows.displayName,
@@ -133,11 +133,6 @@ export class CircuitStructureProjectCommandRepository
throw new Error("Circuit device-row id already exists.");
}
for (const row of snapshot.deviceRows) {
if (source === "user" && row.legacyConsumerId !== null) {
throw new Error(
"User commands cannot assign legacy consumer ids."
);
}
if (source === "user" && !isElectricalPhaseType(row.phaseType)) {
throw new Error("Device-row phase type is invalid.");
}
@@ -158,9 +153,6 @@ export class CircuitStructureProjectCommandRepository
equipmentIdentifier: snapshot.equipmentIdentifier,
displayName: snapshot.displayName,
sortOrder: snapshot.sortOrder,
protectionType: snapshot.protectionType,
protectionRatedCurrent: snapshot.protectionRatedCurrent,
protectionCharacteristic: snapshot.protectionCharacteristic,
cableType: snapshot.cableType,
cableCrossSection: snapshot.cableCrossSection,
cableLength: snapshot.cableLength,
@@ -238,9 +230,6 @@ export class CircuitStructureProjectCommandRepository
equipmentIdentifier: circuit.equipmentIdentifier,
displayName: circuit.displayName,
sortOrder: circuit.sortOrder,
protectionType: circuit.protectionType,
protectionRatedCurrent: circuit.protectionRatedCurrent,
protectionCharacteristic: circuit.protectionCharacteristic,
cableType: circuit.cableType,
cableCrossSection: circuit.cableCrossSection,
cableLength: circuit.cableLength,
@@ -4,9 +4,6 @@ export interface CircuitCreatePersistenceInput {
equipmentIdentifier: string;
displayName?: string;
sortOrder: number;
protectionType?: string;
protectionRatedCurrent?: number;
protectionCharacteristic?: string;
cableType?: string;
cableCrossSection?: string;
cableLength?: number;
@@ -24,9 +21,6 @@ export interface CircuitPatchPersistenceInput {
equipmentIdentifier?: string;
displayName?: string | null;
sortOrder?: number;
protectionType?: string | null;
protectionRatedCurrent?: number | null;
protectionCharacteristic?: string | null;
cableType?: string | null;
cableCrossSection?: string | null;
cableLength?: number | null;
@@ -47,9 +41,6 @@ export function toCircuitCreateValues(id: string, input: CircuitCreatePersistenc
equipmentIdentifier: input.equipmentIdentifier,
displayName: input.displayName ?? null,
sortOrder: input.sortOrder,
protectionType: input.protectionType ?? null,
protectionRatedCurrent: input.protectionRatedCurrent ?? null,
protectionCharacteristic: input.protectionCharacteristic ?? null,
cableType: input.cableType ?? null,
cableCrossSection: input.cableCrossSection ?? null,
cableLength: input.cableLength ?? null,
@@ -74,13 +65,6 @@ export function toCircuitPatchValues(input: CircuitPatchPersistenceInput) {
}
if (has("displayName")) values.displayName = input.displayName ?? null;
if (input.sortOrder !== undefined) values.sortOrder = input.sortOrder;
if (has("protectionType")) values.protectionType = input.protectionType ?? null;
if (has("protectionRatedCurrent")) {
values.protectionRatedCurrent = input.protectionRatedCurrent ?? null;
}
if (has("protectionCharacteristic")) {
values.protectionCharacteristic = input.protectionCharacteristic ?? null;
}
if (has("cableType")) values.cableType = input.cableType ?? null;
if (has("cableCrossSection")) {
values.cableCrossSection = input.cableCrossSection ?? null;
@@ -12,7 +12,6 @@ import {
import {
assertDistributionBoardUpdateProjectCommand,
createDistributionBoardUpdateProjectCommand,
legacyDistributionBoardUpdateCommandSchemaVersion,
type DistributionBoardUpdateField,
type DistributionBoardUpdatePatch,
type DistributionBoardUpdateProjectCommand,
@@ -332,19 +331,10 @@ export class DistributionBoardStructureProjectCommandRepository
getDistributionBoardFieldValue(current, change.field),
])
) as DistributionBoardUpdatePatch;
const currentInverse = createDistributionBoardUpdateProjectCommand(
const inverse = createDistributionBoardUpdateProjectCommand(
current.id,
inversePatch
);
const inverse =
input.command.schemaVersion ===
legacyDistributionBoardUpdateCommandSchemaVersion
? ({
...currentInverse,
schemaVersion:
legacyDistributionBoardUpdateCommandSchemaVersion,
} as DistributionBoardUpdateProjectCommand)
: currentInverse;
const updated = database
.update(distributionBoards)
.set(patch)
@@ -1,142 +0,0 @@
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 } from "./circuit-device-row.persistence.js";
import { toCircuitCreateValues } from "./circuit.persistence.js";
export class LegacyConsumerMigrationRepository {
constructor(private readonly database: AppDatabase) {}
async listSourceConsumersByCircuitList(circuitListId: string) {
return this.database
.select()
.from(consumers)
.where(eq(consumers.circuitListId, circuitListId));
}
async listUnmigratedConsumers() {
return this.database
.select({
id: consumers.id,
projectId: consumers.projectId,
circuitListId: consumers.circuitListId,
})
.from(consumers)
.leftJoin(
legacyConsumerCircuitMigrations,
eq(legacyConsumerCircuitMigrations.consumerId, consumers.id)
)
.where(isNull(legacyConsumerCircuitMigrations.consumerId));
}
async listMigratedConsumerIds(circuitListId: string) {
const rows = await this.database
.select({ consumerId: legacyConsumerCircuitMigrations.consumerId })
.from(legacyConsumerCircuitMigrations)
.where(eq(legacyConsumerCircuitMigrations.circuitListId, circuitListId));
return rows.map((row) => row.consumerId);
}
persistCircuitListMigration(input: {
circuitListId: string;
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.");
}
const consumerIds = input.circuits.flatMap((entry) =>
entry.deviceRows.map((row) => row.legacyConsumerId)
);
if (new Set(consumerIds).size !== consumerIds.length) {
throw new Error("A legacy consumer may only be migrated once per operation.");
}
const preparedCircuits = input.circuits.map((entry) => {
const circuitId = crypto.randomUUID();
return {
circuitId,
circuitValues: toCircuitCreateValues(circuitId, entry.circuit),
rows: entry.deviceRows.map((row) => {
const rowId = crypto.randomUUID();
return {
consumerId: row.legacyConsumerId,
rowId,
rowValues: toCircuitDeviceRowCreateValues(rowId, {
...row,
circuitId,
}),
};
}),
};
});
const createdAtIso = new Date().toISOString();
this.database.transaction((tx) => {
if (consumerIds.length > 0) {
const existingMappings = tx
.select({ consumerId: legacyConsumerCircuitMigrations.consumerId })
.from(legacyConsumerCircuitMigrations)
.where(inArray(legacyConsumerCircuitMigrations.consumerId, consumerIds))
.all();
if (existingMappings.length > 0) {
throw new Error("A legacy consumer was migrated before this operation completed.");
}
}
for (const preparedCircuit of preparedCircuits) {
tx.insert(circuits).values(preparedCircuit.circuitValues).run();
for (const row of preparedCircuit.rows) {
tx.insert(circuitDeviceRows).values(row.rowValues).run();
tx
.insert(legacyConsumerCircuitMigrations)
.values({
consumerId: row.consumerId,
circuitId: preparedCircuit.circuitId,
circuitDeviceRowId: row.rowId,
circuitListId: input.circuitListId,
createdAtIso,
})
.run();
}
}
const reportValues = {
...input.report,
createdAtIso,
};
const existingReport = tx
.select({ id: legacyConsumerMigrationReports.id })
.from(legacyConsumerMigrationReports)
.where(eq(legacyConsumerMigrationReports.circuitListId, input.circuitListId))
.limit(1)
.all();
if (existingReport.length > 0) {
tx
.update(legacyConsumerMigrationReports)
.set(reportValues)
.where(eq(legacyConsumerMigrationReports.id, existingReport[0].id))
.run();
} else {
tx
.insert(legacyConsumerMigrationReports)
.values({
id: crypto.randomUUID(),
circuitListId: input.circuitListId,
...reportValues,
})
.run();
}
});
}
}
@@ -34,7 +34,6 @@ const circuitDeviceRowSnapshotFields = [
"id",
"circuitId",
"linkedProjectDeviceId",
"legacyConsumerId",
"sortOrder",
"name",
"displayName",
@@ -22,7 +22,6 @@ import type {
} from "../../domain/ports/project-location-structure-project-command.store.js";
import type { AppDatabase } from "../database-context.js";
import { circuitDeviceRows } from "../schema/circuit-device-rows.js";
import { consumers } from "../schema/consumers.js";
import { floors } from "../schema/floors.js";
import { distributionBoards } from "../schema/distribution-boards.js";
import { projects } from "../schema/projects.js";
@@ -216,13 +215,7 @@ export class ProjectLocationStructureProjectCommandRepository
.where(eq(circuitDeviceRows.roomId, expected.id))
.limit(1)
.get();
const referencedLegacyConsumer = database
.select({ id: consumers.id })
.from(consumers)
.where(eq(consumers.roomId, expected.id))
.limit(1)
.get();
if (referencedDeviceRow || referencedLegacyConsumer) {
if (referencedDeviceRow) {
throw new Error(
"A referenced project room cannot be removed by history."
);
@@ -2,9 +2,6 @@ import { and, eq, isNotNull } from "drizzle-orm";
import {
assertProjectSettingsUpdateProjectCommand,
createProjectSettingsUpdateProjectCommand,
legacyProjectSettingsUpdateCommandSchemaVersion,
normalizeProjectSettingsValues,
previousProjectSettingsUpdateCommandSchemaVersion,
type ProjectSettingsValues,
} from "../../domain/models/project-settings-project-command.model.js";
import type {
@@ -43,49 +40,22 @@ export class ProjectSettingsProjectCommandRepository
if (!current) {
throw new Error("Project not found.");
}
const target = normalizeProjectSettingsValues(input.command, current);
const target = input.command.payload;
if (projectSettingsEqual(current, target)) {
throw new Error("Project settings did not change.");
}
const inverse =
input.command.schemaVersion ===
legacyProjectSettingsUpdateCommandSchemaVersion
? {
schemaVersion: legacyProjectSettingsUpdateCommandSchemaVersion,
type: input.command.type,
payload: {
singlePhaseVoltageV: current.singlePhaseVoltageV,
threePhaseVoltageV: current.threePhaseVoltageV,
},
}
: input.command.schemaVersion ===
previousProjectSettingsUpdateCommandSchemaVersion
? {
schemaVersion:
previousProjectSettingsUpdateCommandSchemaVersion,
type: input.command.type,
payload: {
name: current.name,
internalProjectNumber: current.internalProjectNumber,
externalProjectNumber: current.externalProjectNumber,
buildingOwner: current.buildingOwner,
description: current.description,
singlePhaseVoltageV: current.singlePhaseVoltageV,
threePhaseVoltageV: current.threePhaseVoltageV,
},
}
: createProjectSettingsUpdateProjectCommand({
name: current.name,
internalProjectNumber: current.internalProjectNumber,
externalProjectNumber: current.externalProjectNumber,
buildingOwner: current.buildingOwner,
description: current.description,
singlePhaseVoltageV: current.singlePhaseVoltageV,
threePhaseVoltageV: current.threePhaseVoltageV,
enabledDistributionBoardSupplyTypes:
current.enabledDistributionBoardSupplyTypes,
});
const inverse = createProjectSettingsUpdateProjectCommand({
name: current.name,
internalProjectNumber: current.internalProjectNumber,
externalProjectNumber: current.externalProjectNumber,
buildingOwner: current.buildingOwner,
description: current.description,
singlePhaseVoltageV: current.singlePhaseVoltageV,
threePhaseVoltageV: current.threePhaseVoltageV,
enabledDistributionBoardSupplyTypes:
current.enabledDistributionBoardSupplyTypes,
});
assertDisabledSupplyTypesAreUnused(tx, input.projectId, target);
const updated = tx
.update(projects)
@@ -4,14 +4,8 @@ import { ProjectRevisionConflictError } from "../../domain/errors/project-revisi
import { ProjectSnapshotNameConflictError } from "../../domain/errors/project-snapshot-name-conflict.error.js";
import { createProjectStateRestoreCommand } from "../../domain/models/project-state-restore-command.model.js";
import {
distributionBoardProjectStateSnapshotSchemaVersion,
deserializeProjectStateSnapshot,
legacyProjectStateSnapshotSchemaVersion,
previousProjectStateSnapshotSchemaVersion,
projectStateSnapshotSchemaVersion,
simultaneityFactorProjectStateSnapshotSchemaVersion,
supplyTypesProjectStateSnapshotSchemaVersion,
voltageProjectStateSnapshotSchemaVersion,
} from "../../domain/models/project-state-snapshot.model.js";
import type {
CreateNamedProjectSnapshotInput,
@@ -144,19 +138,7 @@ export class ProjectSnapshotRepository implements ProjectSnapshotStore {
if (actualChecksum !== stored.payloadSha256) {
throw new Error("Project snapshot checksum verification failed.");
}
if (
stored.schemaVersion !== legacyProjectStateSnapshotSchemaVersion &&
stored.schemaVersion !== previousProjectStateSnapshotSchemaVersion &&
stored.schemaVersion !==
distributionBoardProjectStateSnapshotSchemaVersion &&
stored.schemaVersion !==
supplyTypesProjectStateSnapshotSchemaVersion &&
stored.schemaVersion !==
voltageProjectStateSnapshotSchemaVersion &&
stored.schemaVersion !==
simultaneityFactorProjectStateSnapshotSchemaVersion &&
stored.schemaVersion !== projectStateSnapshotSchemaVersion
) {
if (stored.schemaVersion !== projectStateSnapshotSchemaVersion) {
throw new Error("Project snapshot schema version is not supported.");
}
const targetState = deserializeProjectStateSnapshot(stored.payloadJson);
@@ -18,13 +18,10 @@ import { circuitLists } from "../schema/circuit-lists.js";
import { circuitProtectionDevices } from "../schema/circuit-protection-devices.js";
import { circuitSections } from "../schema/circuit-sections.js";
import { circuits } from "../schema/circuits.js";
import { consumers } from "../schema/consumers.js";
import { distributionBoardComponentProtectionDevices } from "../schema/distribution-board-component-protection-devices.js";
import { distributionBoardComponents } from "../schema/distribution-board-components.js";
import { distributionBoards } from "../schema/distribution-boards.js";
import { floors } from "../schema/floors.js";
import { legacyConsumerCircuitMigrations } from "../schema/legacy-consumer-circuit-migrations.js";
import { legacyConsumerMigrationReports } from "../schema/legacy-consumer-migration-report.js";
import { projectDevices } from "../schema/project-devices.js";
import { projectChangeSets } from "../schema/project-change-sets.js";
import { projects } from "../schema/projects.js";
@@ -186,10 +183,6 @@ function replaceProjectState(
database: AppDatabase,
state: ProjectStateSnapshot
) {
const upgradeOnlyData = captureUpgradeOnlyData(
database,
state.project.id
);
database
.delete(distributionBoards)
.where(eq(distributionBoards.projectId, state.project.id))
@@ -257,136 +250,6 @@ function replaceProjectState(
circuitDeviceRows,
state.circuits.flatMap((circuit) => circuit.deviceRows)
);
restoreCompatibleUpgradeOnlyData(database, state, upgradeOnlyData);
}
function captureUpgradeOnlyData(
database: AppDatabase,
projectId: string
) {
const consumerLinks = database
.select({
id: consumers.id,
distributionBoardId: consumers.distributionBoardId,
circuitListId: consumers.circuitListId,
projectDeviceId: consumers.projectDeviceId,
roomId: consumers.roomId,
})
.from(consumers)
.where(eq(consumers.projectId, projectId))
.all();
const migrationMappings = database
.select({
consumerId: legacyConsumerCircuitMigrations.consumerId,
circuitId: legacyConsumerCircuitMigrations.circuitId,
circuitDeviceRowId:
legacyConsumerCircuitMigrations.circuitDeviceRowId,
circuitListId: legacyConsumerCircuitMigrations.circuitListId,
createdAtIso: legacyConsumerCircuitMigrations.createdAtIso,
})
.from(legacyConsumerCircuitMigrations)
.innerJoin(
circuitLists,
eq(
circuitLists.id,
legacyConsumerCircuitMigrations.circuitListId
)
)
.where(eq(circuitLists.projectId, projectId))
.all();
const migrationReports = database
.select({
id: legacyConsumerMigrationReports.id,
circuitListId: legacyConsumerMigrationReports.circuitListId,
legacyConsumerCount:
legacyConsumerMigrationReports.legacyConsumerCount,
createdCircuitCount:
legacyConsumerMigrationReports.createdCircuitCount,
createdDeviceRowCount:
legacyConsumerMigrationReports.createdDeviceRowCount,
duplicateGroupedCount:
legacyConsumerMigrationReports.duplicateGroupedCount,
generatedIdentifierCount:
legacyConsumerMigrationReports.generatedIdentifierCount,
unassignedRowCount:
legacyConsumerMigrationReports.unassignedRowCount,
warningsJson: legacyConsumerMigrationReports.warningsJson,
generatedIdentifiersJson:
legacyConsumerMigrationReports.generatedIdentifiersJson,
duplicateGroupsJson:
legacyConsumerMigrationReports.duplicateGroupsJson,
createdAtIso: legacyConsumerMigrationReports.createdAtIso,
})
.from(legacyConsumerMigrationReports)
.innerJoin(
circuitLists,
eq(circuitLists.id, legacyConsumerMigrationReports.circuitListId)
)
.where(eq(circuitLists.projectId, projectId))
.all();
return { consumerLinks, migrationMappings, migrationReports };
}
function restoreCompatibleUpgradeOnlyData(
database: AppDatabase,
state: ProjectStateSnapshot,
data: ReturnType<typeof captureUpgradeOnlyData>
) {
const boardIds = new Set(
state.distributionBoards.map((entry) => entry.id)
);
const listIds = new Set(state.circuitLists.map((entry) => entry.id));
const deviceIds = new Set(
state.projectDevices.map((entry) => entry.id)
);
const roomIds = new Set(state.rooms.map((entry) => entry.id));
const circuitIds = new Set(state.circuits.map((entry) => entry.id));
const rowIds = new Set(
state.circuits.flatMap((circuit) =>
circuit.deviceRows.map((entry) => entry.id)
)
);
for (const link of data.consumerLinks) {
database
.update(consumers)
.set({
distributionBoardId:
link.distributionBoardId &&
boardIds.has(link.distributionBoardId)
? link.distributionBoardId
: null,
circuitListId:
link.circuitListId && listIds.has(link.circuitListId)
? link.circuitListId
: null,
projectDeviceId:
link.projectDeviceId && deviceIds.has(link.projectDeviceId)
? link.projectDeviceId
: null,
roomId:
link.roomId && roomIds.has(link.roomId) ? link.roomId : null,
})
.where(eq(consumers.id, link.id))
.run();
}
insertMany(
database,
legacyConsumerCircuitMigrations,
data.migrationMappings.filter(
(entry) =>
listIds.has(entry.circuitListId) &&
circuitIds.has(entry.circuitId) &&
rowIds.has(entry.circuitDeviceRowId)
)
);
insertMany(
database,
legacyConsumerMigrationReports,
data.migrationReports.filter((entry) =>
listIds.has(entry.circuitListId)
)
);
}
function insertMany<TTable extends Parameters<AppDatabase["insert"]>[0]>(
@@ -145,9 +145,6 @@ export function readProjectStateSnapshot(
equipmentIdentifier: circuits.equipmentIdentifier,
displayName: circuits.displayName,
sortOrder: circuits.sortOrder,
protectionType: circuits.protectionType,
protectionRatedCurrent: circuits.protectionRatedCurrent,
protectionCharacteristic: circuits.protectionCharacteristic,
cableType: circuits.cableType,
cableCrossSection: circuits.cableCrossSection,
cableLength: circuits.cableLength,
@@ -173,7 +170,6 @@ export function readProjectStateSnapshot(
id: circuitDeviceRows.id,
circuitId: circuitDeviceRows.circuitId,
linkedProjectDeviceId: circuitDeviceRows.linkedProjectDeviceId,
legacyConsumerId: circuitDeviceRows.legacyConsumerId,
sortOrder: circuitDeviceRows.sortOrder,
name: circuitDeviceRows.name,
displayName: circuitDeviceRows.displayName,
-1
View File
@@ -11,7 +11,6 @@ export const circuitDeviceRows = sqliteTable("circuit_device_rows", {
linkedProjectDeviceId: text("linked_project_device_id").references(() => projectDevices.id, {
onDelete: "set null",
}),
legacyConsumerId: text("legacy_consumer_id"),
sortOrder: integer("sort_order").notNull().default(0),
name: text("name").notNull(),
displayName: text("display_name").notNull(),
-3
View File
@@ -15,9 +15,6 @@ export const circuits = sqliteTable(
equipmentIdentifier: text("equipment_identifier").notNull(),
displayName: text("display_name"),
sortOrder: integer("sort_order").notNull().default(0),
protectionType: text("protection_type"),
protectionRatedCurrent: real("protection_rated_current"),
protectionCharacteristic: text("protection_characteristic"),
cableType: text("cable_type"),
cableCrossSection: text("cable_cross_section"),
cableLength: real("cable_length"),
-47
View File
@@ -1,47 +0,0 @@
import { integer, real, sqliteTable, text } from "drizzle-orm/sqlite-core";
import { circuitLists } from "./circuit-lists.js";
import { distributionBoards } from "./distribution-boards.js";
import { projectDevices } from "./project-devices.js";
import { projects } from "./projects.js";
import { rooms } from "./rooms.js";
export const consumers = sqliteTable("consumers", {
id: text("id").primaryKey(),
projectId: text("project_id")
.notNull()
.references(() => projects.id, { onDelete: "cascade" }),
distributionBoardId: text("distribution_board_id").references(() => distributionBoards.id, {
onDelete: "set null",
}),
circuitListId: text("circuit_list_id").references(() => circuitLists.id, {
onDelete: "set null",
}),
projectDeviceId: text("project_device_id").references(() => projectDevices.id, {
onDelete: "set null",
}),
isLinkedToDevice: integer("is_linked_to_device").notNull().default(0),
roomId: text("room_id").references(() => rooms.id, {
onDelete: "set null",
}),
circuitNumber: text("circuit_number"),
description: text("description"),
name: text("name").notNull(),
category: text("category"),
deviceType: text("device_type"),
phaseType: text("phase_type"),
tradeOrCostGroup: text("trade_or_cost_group"),
group: text("group_name"),
protectionType: text("protection_type"),
protectionRatedCurrent: real("protection_rated_current"),
protectionCharacteristic: text("protection_characteristic"),
cableType: text("cable_type"),
cableCrossSection: text("cable_cross_section"),
comment: text("comment"),
quantity: integer("quantity").notNull(),
installedPowerPerUnitKw: real("installed_power_per_unit_kw").notNull(),
demandFactor: real("demand_factor").notNull(),
voltageV: real("voltage_v"),
phaseCount: integer("phase_count"),
powerFactor: real("power_factor"),
note: text("note"),
});
@@ -1,19 +0,0 @@
import { sqliteTable, text } from "drizzle-orm/sqlite-core";
import { circuitDeviceRows } from "./circuit-device-rows.js";
import { circuitLists } from "./circuit-lists.js";
import { circuits } from "./circuits.js";
export const legacyConsumerCircuitMigrations = sqliteTable("legacy_consumer_circuit_migrations", {
consumerId: text("consumer_id").primaryKey(),
circuitId: text("circuit_id")
.notNull()
.references(() => circuits.id, { onDelete: "cascade" }),
circuitDeviceRowId: text("circuit_device_row_id")
.notNull()
.references(() => circuitDeviceRows.id, { onDelete: "cascade" }),
circuitListId: text("circuit_list_id")
.notNull()
.references(() => circuitLists.id, { onDelete: "cascade" }),
createdAtIso: text("created_at_iso").notNull(),
});
@@ -1,24 +0,0 @@
import { integer, sqliteTable, text, unique } from "drizzle-orm/sqlite-core";
import { circuitLists } from "./circuit-lists.js";
export const legacyConsumerMigrationReports = sqliteTable(
"legacy_consumer_migration_reports",
{
id: text("id").primaryKey(),
circuitListId: text("circuit_list_id")
.notNull()
.references(() => circuitLists.id, { onDelete: "cascade" }),
legacyConsumerCount: integer("legacy_consumer_count").notNull(),
createdCircuitCount: integer("created_circuit_count").notNull(),
createdDeviceRowCount: integer("created_device_row_count").notNull(),
duplicateGroupedCount: integer("duplicate_grouped_count").notNull(),
generatedIdentifierCount: integer("generated_identifier_count").notNull(),
unassignedRowCount: integer("unassigned_row_count").notNull(),
warningsJson: text("warnings_json").notNull(),
generatedIdentifiersJson: text("generated_identifiers_json").notNull(),
duplicateGroupsJson: text("duplicate_groups_json").notNull(),
createdAtIso: text("created_at_iso").notNull(),
},
(table) => [unique("legacy_consumer_migration_reports_list_unique").on(table.circuitListId)]
);
@@ -10,7 +10,6 @@ export interface CircuitDeviceRowSnapshot {
id: string;
circuitId: string;
linkedProjectDeviceId: string | null;
legacyConsumerId: string | null;
sortOrder: number;
name: string;
displayName: string;
@@ -101,7 +100,6 @@ export function assertCircuitDeviceRowInsertProjectCommand(
assertNonEmptyString(row.id, "row.id");
assertNonEmptyString(row.circuitId, "row.circuitId");
assertNullableString(row.linkedProjectDeviceId, "row.linkedProjectDeviceId");
assertNullableString(row.legacyConsumerId, "row.legacyConsumerId");
assertFiniteNumber(row.sortOrder, "row.sortOrder");
assertNonEmptyString(row.name, "row.name");
assertNonEmptyString(row.displayName, "row.displayName");
@@ -2,7 +2,6 @@ export interface CircuitDeviceRow {
id: string;
circuitId: string;
linkedProjectDeviceId?: string;
legacyConsumerId?: string;
sortOrder: number;
name: string;
displayName: string;
@@ -8,9 +8,6 @@ export interface CircuitUpdateValues {
equipmentIdentifier: string;
displayName: string | null;
sortOrder: number;
protectionType: string | null;
protectionRatedCurrent: number | null;
protectionCharacteristic: string | null;
cableType: string | null;
cableCrossSection: string | null;
cableLength: number | null;
@@ -118,7 +115,6 @@ function assertCircuitUpdateFieldValue(
return;
}
if (
field === "protectionRatedCurrent" ||
field === "cableLength" ||
field === "voltage"
) {
@@ -147,9 +143,6 @@ function isCircuitUpdateField(value: string): value is CircuitUpdateField {
"equipmentIdentifier",
"displayName",
"sortOrder",
"protectionType",
"protectionRatedCurrent",
"protectionCharacteristic",
"cableType",
"cableCrossSection",
"cableLength",
@@ -21,9 +21,6 @@ export interface CircuitSnapshot {
equipmentIdentifier: string;
displayName: string | null;
sortOrder: number;
protectionType: string | null;
protectionRatedCurrent: number | null;
protectionCharacteristic: string | null;
cableType: string | null;
cableCrossSection: string | null;
cableLength: number | null;
@@ -115,8 +112,6 @@ export function assertCircuitInsertProjectCommand(
assertNullableString(circuit.displayName, "circuit.displayName");
assertFiniteNumber(circuit.sortOrder, "circuit.sortOrder");
for (const field of [
"protectionType",
"protectionCharacteristic",
"cableType",
"cableCrossSection",
"rcdAssignment",
@@ -127,10 +122,6 @@ export function assertCircuitInsertProjectCommand(
] as const) {
assertNullableString(circuit[field], `circuit.${field}`);
}
assertNullableNonNegativeNumber(
circuit.protectionRatedCurrent,
"circuit.protectionRatedCurrent"
);
assertNullableNonNegativeNumber(
circuit.cableLength,
"circuit.cableLength"
-15
View File
@@ -1,7 +1,6 @@
export interface CircuitTreeDeviceRow {
id: string;
linkedProjectDeviceId?: string;
legacyConsumerId?: string;
sortOrder: number;
name: string;
displayName: string;
@@ -29,9 +28,6 @@ export interface CircuitTreeCircuit {
equipmentIdentifier: string;
displayName?: string;
sortOrder: number;
protectionType?: string;
protectionRatedCurrent?: number;
protectionCharacteristic?: string;
cableType?: string;
cableCrossSection?: string;
cableLength?: number;
@@ -73,17 +69,6 @@ export interface CircuitTreeResponse {
footerComponents: CircuitTreeComponent[];
}
export interface LegacyMigrationReport {
circuitListId: string;
legacyConsumerCount: number;
createdCircuitCount: number;
createdDeviceRowCount: number;
groupedDuplicateCircuitNumbers: Array<{ normalizedCircuitNumber: string; count: number }>;
generatedIdentifiers: string[];
unassignedRows: Array<{ consumerId: string; reason: string }>;
warnings: string[];
}
import type { CircuitGroupCategory } from "../../shared/constants/circuit-group.js";
import type {
DistributionBoardComponentPlacement,
-3
View File
@@ -5,9 +5,6 @@ export interface Circuit {
equipmentIdentifier: string;
displayName?: string;
sortOrder: number;
protectionType?: string;
protectionRatedCurrent?: number;
protectionCharacteristic?: string;
cableType?: string;
cableCrossSection?: string;
cableLength?: number;
@@ -6,8 +6,7 @@ import type { SerializedProjectCommand } from "./project-command.model.js";
export const distributionBoardUpdateCommandType =
"distribution-board.update" as const;
export const legacyDistributionBoardUpdateCommandSchemaVersion = 1 as const;
export const distributionBoardUpdateCommandSchemaVersion = 2 as const;
export const distributionBoardUpdateCommandSchemaVersion = 1 as const;
export interface DistributionBoardUpdateValues {
floorId: string | null;
@@ -33,27 +32,17 @@ export interface DistributionBoardUpdateCommandPayload {
changes: DistributionBoardUpdateFieldChange[];
}
interface CurrentDistributionBoardUpdateProjectCommand
export interface DistributionBoardUpdateProjectCommand
extends SerializedProjectCommand<DistributionBoardUpdateCommandPayload> {
schemaVersion: typeof distributionBoardUpdateCommandSchemaVersion;
type: typeof distributionBoardUpdateCommandType;
}
interface LegacyDistributionBoardUpdateProjectCommand
extends SerializedProjectCommand<DistributionBoardUpdateCommandPayload> {
schemaVersion: typeof legacyDistributionBoardUpdateCommandSchemaVersion;
type: typeof distributionBoardUpdateCommandType;
}
export type DistributionBoardUpdateProjectCommand =
| CurrentDistributionBoardUpdateProjectCommand
| LegacyDistributionBoardUpdateProjectCommand;
export function createDistributionBoardUpdateProjectCommand(
distributionBoardId: string,
patch: DistributionBoardUpdatePatch
): CurrentDistributionBoardUpdateProjectCommand {
const command: CurrentDistributionBoardUpdateProjectCommand = {
): DistributionBoardUpdateProjectCommand {
const command: DistributionBoardUpdateProjectCommand = {
schemaVersion: distributionBoardUpdateCommandSchemaVersion,
type: distributionBoardUpdateCommandType,
payload: {
@@ -72,9 +61,7 @@ export function assertDistributionBoardUpdateProjectCommand(
command: SerializedProjectCommand<unknown>
): asserts command is DistributionBoardUpdateProjectCommand {
if (
(command.schemaVersion !==
legacyDistributionBoardUpdateCommandSchemaVersion &&
command.schemaVersion !== distributionBoardUpdateCommandSchemaVersion) ||
command.schemaVersion !== distributionBoardUpdateCommandSchemaVersion ||
command.type !== distributionBoardUpdateCommandType ||
!isPlainObject(command.payload)
) {
@@ -97,9 +84,7 @@ export function assertDistributionBoardUpdateProjectCommand(
!isPlainObject(change) ||
(change.field !== "floorId" &&
change.field !== "supplyType" &&
(command.schemaVersion ===
legacyDistributionBoardUpdateCommandSchemaVersion ||
change.field !== "simultaneityFactor")) ||
change.field !== "simultaneityFactor") ||
seen.has(change.field)
) {
throw new Error(
@@ -18,36 +18,7 @@ export const distributionBoardInsertCommandType =
"distribution-board.insert" as const;
export const distributionBoardDeleteCommandType =
"distribution-board.delete" as const;
export const legacyDistributionBoardStructureCommandSchemaVersion = 1 as const;
export const previousDistributionBoardStructureCommandSchemaVersion = 2 as const;
export const distributionBoardStructureCommandSchemaVersion = 3 as const;
const legacyDefaultCircuitSectionDefinitions = [
{
key: "lighting",
displayName: "Lighting",
prefix: "-1F",
sortOrder: 10,
},
{
key: "single_phase",
displayName: "Single-phase circuits",
prefix: "-2F",
sortOrder: 20,
},
{
key: "three_phase",
displayName: "Three-phase circuits",
prefix: "-3F",
sortOrder: 30,
},
{
key: "unassigned",
displayName: "Unassigned",
prefix: "-UF",
sortOrder: 90,
},
] as const;
export const distributionBoardStructureCommandSchemaVersion = 1 as const;
export const defaultCircuitSectionDefinitions = [
{
@@ -76,31 +47,6 @@ export const defaultCircuitSectionDefinitions = [
},
] as const;
interface LegacyDistributionBoardStructureSnapshot {
distributionBoard: {
id: string;
projectId: string;
name: string;
};
circuitList: DistributionBoardStructureSnapshot["circuitList"];
sections: LegacyCircuitSectionSnapshot[];
}
interface PreviousDistributionBoardStructureSnapshot {
distributionBoard: DistributionBoardStructureSnapshot["distributionBoard"];
circuitList: DistributionBoardStructureSnapshot["circuitList"];
sections: LegacyCircuitSectionSnapshot[];
}
interface LegacyCircuitSectionSnapshot {
id: string;
circuitListId: string;
key: string;
displayName: string;
prefix: string;
sortOrder: number;
}
export interface DistributionBoardStructureSnapshot {
distributionBoard: {
id: string;
@@ -140,22 +86,15 @@ export interface DistributionBoardStructureSnapshot {
export interface NormalizedDistributionBoardStructureSnapshot {
distributionBoard: DistributionBoardStructureSnapshot["distributionBoard"];
circuitList: DistributionBoardStructureSnapshot["circuitList"];
sections: Array<
LegacyCircuitSectionSnapshot & {
category: CircuitGroupCategory | null;
groupNumber: number | null;
}
>;
sections: DistributionBoardStructureSnapshot["sections"];
components: DistributionBoardStructureSnapshot["components"];
}
interface DistributionBoardStructureCommandPayload<
TStructure = DistributionBoardStructureSnapshot,
> {
structure: TStructure;
interface DistributionBoardStructureCommandPayload {
structure: DistributionBoardStructureSnapshot;
}
interface CurrentDistributionBoardStructureProjectCommand
interface DistributionBoardStructureProjectCommandBase
extends SerializedProjectCommand<DistributionBoardStructureCommandPayload> {
schemaVersion: typeof distributionBoardStructureCommandSchemaVersion;
type:
@@ -163,41 +102,19 @@ interface CurrentDistributionBoardStructureProjectCommand
| typeof distributionBoardDeleteCommandType;
}
interface LegacyDistributionBoardStructureProjectCommand
extends SerializedProjectCommand<
DistributionBoardStructureCommandPayload<LegacyDistributionBoardStructureSnapshot>
> {
schemaVersion: typeof legacyDistributionBoardStructureCommandSchemaVersion;
type:
| typeof distributionBoardInsertCommandType
| typeof distributionBoardDeleteCommandType;
}
interface PreviousDistributionBoardStructureProjectCommand
extends SerializedProjectCommand<
DistributionBoardStructureCommandPayload<PreviousDistributionBoardStructureSnapshot>
> {
schemaVersion: typeof previousDistributionBoardStructureCommandSchemaVersion;
type:
| typeof distributionBoardInsertCommandType
| typeof distributionBoardDeleteCommandType;
}
export interface DistributionBoardInsertProjectCommand
extends CurrentDistributionBoardStructureProjectCommand {
extends DistributionBoardStructureProjectCommandBase {
type: typeof distributionBoardInsertCommandType;
}
export interface DistributionBoardDeleteProjectCommand
extends CurrentDistributionBoardStructureProjectCommand {
extends DistributionBoardStructureProjectCommandBase {
type: typeof distributionBoardDeleteCommandType;
}
export type DistributionBoardStructureProjectCommand =
| DistributionBoardInsertProjectCommand
| DistributionBoardDeleteProjectCommand
| PreviousDistributionBoardStructureProjectCommand
| LegacyDistributionBoardStructureProjectCommand;
| DistributionBoardDeleteProjectCommand;
export function createDistributionBoardStructureSnapshot(
projectId: string,
@@ -296,35 +213,6 @@ export function assertDistributionBoardDeleteProjectCommand(
export function normalizeDistributionBoardStructureProjectCommand(
command: DistributionBoardStructureProjectCommand
): NormalizedDistributionBoardStructureSnapshot {
if (
command.schemaVersion ===
legacyDistributionBoardStructureCommandSchemaVersion
) {
return {
distributionBoard: {
...command.payload.structure.distributionBoard,
floorId: null,
supplyType: null,
},
circuitList: command.payload.structure.circuitList,
sections: command.payload.structure.sections.map(
normalizeLegacySection
),
components: [],
};
}
if (
command.schemaVersion ===
previousDistributionBoardStructureCommandSchemaVersion
) {
return {
...command.payload.structure,
sections: command.payload.structure.sections.map(
normalizeLegacySection
),
components: [],
};
}
return command.payload.structure;
}
@@ -340,26 +228,10 @@ export function invertDistributionBoardStructureProjectCommand(
} as DistributionBoardStructureProjectCommand;
}
function normalizeLegacySection(
section: LegacyCircuitSectionSnapshot
): NormalizedDistributionBoardStructureSnapshot["sections"][number] {
const category =
section.key === "lighting" ||
section.key === "single_phase" ||
section.key === "three_phase"
? section.key
: null;
return {
...section,
category,
groupNumber: category === null ? null : 1,
};
}
export function assertDistributionBoardStructureSnapshot(
structure: unknown
): asserts structure is DistributionBoardStructureSnapshot {
assertDistributionBoardStructureSnapshotVersion(structure, "current");
assertDistributionBoardStructureSnapshotVersion(structure);
}
function assertDistributionBoardStructureProjectCommand(
@@ -375,63 +247,36 @@ function assertDistributionBoardStructureProjectCommand(
) {
throw new Error("Unsupported distribution-board structure command.");
}
if (
command.schemaVersion ===
legacyDistributionBoardStructureCommandSchemaVersion
) {
assertDistributionBoardStructureSnapshotVersion(
command.payload.structure,
"legacy"
);
return;
}
if (
command.schemaVersion ===
previousDistributionBoardStructureCommandSchemaVersion
) {
assertDistributionBoardStructureSnapshotVersion(
command.payload.structure,
"previous"
);
return;
}
if (
command.schemaVersion !==
distributionBoardStructureCommandSchemaVersion
) {
throw new Error("Unsupported distribution-board structure command.");
}
assertDistributionBoardStructureSnapshotVersion(
command.payload.structure,
"current"
);
assertDistributionBoardStructureSnapshotVersion(command.payload.structure);
}
function assertDistributionBoardStructureSnapshotVersion(
structure: unknown,
version: "legacy" | "previous" | "current"
structure: unknown
) {
const current = version === "current";
if (
!isPlainObject(structure) ||
Object.keys(structure).length !== (current ? 4 : 3)
Object.keys(structure).length !== 4
) {
throw new Error("Distribution-board structure is invalid.");
}
const { distributionBoard, circuitList, sections, components } =
structure;
const expectedSections = current
? defaultCircuitSectionDefinitions
: legacyDefaultCircuitSectionDefinitions;
const expectedSections = defaultCircuitSectionDefinitions;
if (
!isPlainObject(distributionBoard) ||
Object.keys(distributionBoard).length !==
(version === "legacy" ? 3 : 5) ||
Object.keys(distributionBoard).length !== 5 ||
!isPlainObject(circuitList) ||
Object.keys(circuitList).length !== 4 ||
!Array.isArray(sections) ||
sections.length !== expectedSections.length ||
(current && (!Array.isArray(components) || components.length !== 2))
!Array.isArray(components) ||
components.length !== 2
) {
throw new Error("Distribution-board structure is incomplete.");
}
@@ -441,10 +286,8 @@ function assertDistributionBoardStructureSnapshotVersion(
`distributionBoard.${field}`
);
}
if (version !== "legacy") {
assertNullableId(distributionBoard.floorId, "distributionBoard.floorId");
assertNullableSupplyType(distributionBoard.supplyType);
}
assertNullableId(distributionBoard.floorId, "distributionBoard.floorId");
assertNullableSupplyType(distributionBoard.supplyType);
for (const [field, value] of Object.entries(circuitList)) {
assertNonEmptyString(value, `circuitList.${field}`);
}
@@ -462,7 +305,7 @@ function assertDistributionBoardStructureSnapshotVersion(
const expected = expectedSections[index];
if (
!isPlainObject(section) ||
Object.keys(section).length !== (current ? 8 : 6)
Object.keys(section).length !== 8
) {
throw new Error("Default circuit section is invalid.");
}
@@ -477,10 +320,8 @@ function assertDistributionBoardStructureSnapshotVersion(
section.displayName !== expected.displayName ||
section.prefix !== expected.prefix ||
section.sortOrder !== expected.sortOrder ||
(current &&
(!("category" in expected) ||
section.category !== expected.category ||
section.groupNumber !== expected.groupNumber))
section.category !== expected.category ||
section.groupNumber !== expected.groupNumber
) {
throw new Error(
"Distribution-board structure has invalid default sections."
@@ -488,9 +329,7 @@ function assertDistributionBoardStructureSnapshotVersion(
}
}
if (current) {
assertDefaultComponents(components, circuitList.id);
}
assertDefaultComponents(components, circuitList.id);
}
function assertDefaultComponents(
@@ -156,7 +156,6 @@ export function cloneDistributionBoardSubtree(
...row,
id: createId(),
circuitId,
legacyConsumerId: null,
})),
protectionDevice:
circuit.protectionDevice === null ||
@@ -6,54 +6,29 @@ import {
export const projectSettingsUpdateCommandType =
"project.update-settings" as const;
export const legacyProjectSettingsUpdateCommandSchemaVersion = 1 as const;
export const previousProjectSettingsUpdateCommandSchemaVersion = 2 as const;
export const projectSettingsUpdateCommandSchemaVersion = 3 as const;
export const projectSettingsUpdateCommandSchemaVersion = 1 as const;
export interface LegacyProjectSettingsValues {
singlePhaseVoltageV: number;
threePhaseVoltageV: number;
}
export interface PreviousProjectSettingsValues extends LegacyProjectSettingsValues {
export interface ProjectSettingsValues {
name: string;
internalProjectNumber: string | null;
externalProjectNumber: string | null;
buildingOwner: string | null;
description: string | null;
}
export interface ProjectSettingsValues extends PreviousProjectSettingsValues {
singlePhaseVoltageV: number;
threePhaseVoltageV: number;
enabledDistributionBoardSupplyTypes: DistributionBoardSupplyType[];
}
export interface LegacyProjectSettingsUpdateProjectCommand
extends SerializedProjectCommand<LegacyProjectSettingsValues> {
schemaVersion: typeof legacyProjectSettingsUpdateCommandSchemaVersion;
type: typeof projectSettingsUpdateCommandType;
}
export interface CurrentProjectSettingsUpdateProjectCommand
export interface ProjectSettingsUpdateProjectCommand
extends SerializedProjectCommand<ProjectSettingsValues> {
schemaVersion: typeof projectSettingsUpdateCommandSchemaVersion;
type: typeof projectSettingsUpdateCommandType;
}
export interface PreviousProjectSettingsUpdateProjectCommand
extends SerializedProjectCommand<PreviousProjectSettingsValues> {
schemaVersion: typeof previousProjectSettingsUpdateCommandSchemaVersion;
type: typeof projectSettingsUpdateCommandType;
}
export type ProjectSettingsUpdateProjectCommand =
| LegacyProjectSettingsUpdateProjectCommand
| PreviousProjectSettingsUpdateProjectCommand
| CurrentProjectSettingsUpdateProjectCommand;
export function createProjectSettingsUpdateProjectCommand(
values: ProjectSettingsValues
): CurrentProjectSettingsUpdateProjectCommand {
const command: CurrentProjectSettingsUpdateProjectCommand = {
): ProjectSettingsUpdateProjectCommand {
const command: ProjectSettingsUpdateProjectCommand = {
schemaVersion: projectSettingsUpdateCommandSchemaVersion,
type: projectSettingsUpdateCommandType,
payload: { ...values },
@@ -67,11 +42,7 @@ export function assertProjectSettingsUpdateProjectCommand(
): asserts command is ProjectSettingsUpdateProjectCommand {
if (
command.type !== projectSettingsUpdateCommandType ||
(command.schemaVersion !==
legacyProjectSettingsUpdateCommandSchemaVersion &&
command.schemaVersion !==
previousProjectSettingsUpdateCommandSchemaVersion &&
command.schemaVersion !== projectSettingsUpdateCommandSchemaVersion)
command.schemaVersion !== projectSettingsUpdateCommandSchemaVersion
) {
throw new Error("Unsupported project settings update command.");
}
@@ -80,14 +51,6 @@ export function assertProjectSettingsUpdateProjectCommand(
"Project settings update command contains invalid values."
);
}
if (command.schemaVersion === legacyProjectSettingsUpdateCommandSchemaVersion) {
assertLegacyValues(command.payload);
return;
}
const expectedKeyCount =
command.schemaVersion === previousProjectSettingsUpdateCommandSchemaVersion
? 7
: 8;
if (
!isTrimmedString(command.payload.name, 1, 200) ||
!isNullableTrimmedString(command.payload.internalProjectNumber, 100) ||
@@ -96,14 +59,13 @@ export function assertProjectSettingsUpdateProjectCommand(
!isNullableTrimmedString(command.payload.description, 2000) ||
!isPositiveFiniteNumber(command.payload.singlePhaseVoltageV) ||
!isPositiveFiniteNumber(command.payload.threePhaseVoltageV) ||
Object.keys(command.payload).length !== expectedKeyCount
Object.keys(command.payload).length !== 8
) {
throw new Error(
"Project settings update command contains invalid values."
);
}
if (
command.schemaVersion === projectSettingsUpdateCommandSchemaVersion &&
!isValidSupplyTypes(
command.payload.enabledDistributionBoardSupplyTypes
)
@@ -114,28 +76,6 @@ export function assertProjectSettingsUpdateProjectCommand(
}
}
export function normalizeProjectSettingsValues(
command: ProjectSettingsUpdateProjectCommand,
current: ProjectSettingsValues
): ProjectSettingsValues {
if (command.schemaVersion === legacyProjectSettingsUpdateCommandSchemaVersion) {
return {
...current,
...command.payload,
};
}
if (
command.schemaVersion === previousProjectSettingsUpdateCommandSchemaVersion
) {
return {
...command.payload,
enabledDistributionBoardSupplyTypes:
current.enabledDistributionBoardSupplyTypes,
};
}
return command.payload;
}
function isValidSupplyTypes(
value: unknown
): value is DistributionBoardSupplyType[] {
@@ -153,20 +93,6 @@ function isValidSupplyTypes(
);
}
function assertLegacyValues(
payload: Record<string, unknown>
): asserts payload is Record<string, unknown> & LegacyProjectSettingsValues {
if (
!isPositiveFiniteNumber(payload.singlePhaseVoltageV) ||
!isPositiveFiniteNumber(payload.threePhaseVoltageV) ||
Object.keys(payload).length !== 2
) {
throw new Error(
"Project settings update command contains invalid voltages."
);
}
}
function isPositiveFiniteNumber(value: unknown): value is number {
return (
typeof value === "number" &&
+24 -304
View File
@@ -1,11 +1,7 @@
import { z } from "zod";
import {
defaultDistributionBoardSupplyTypes,
distributionBoardSupplyTypes,
} from "../../shared/constants/distribution-board.js";
import { distributionBoardSupplyTypes } from "../../shared/constants/distribution-board.js";
import {
circuitGroupCategories,
type CircuitGroupCategory,
} from "../../shared/constants/circuit-group.js";
import {
distributionBoardComponentPlacements,
@@ -21,61 +17,38 @@ import {
import {
resolveCircuitPhaseType,
resolveProjectVoltage,
normalizeKnownElectricalPhaseType,
} from "../services/project-voltage.service.js";
export const legacyProjectStateSnapshotSchemaVersion = 1 as const;
export const previousProjectStateSnapshotSchemaVersion = 2 as const;
export const distributionBoardProjectStateSnapshotSchemaVersion = 3 as const;
export const supplyTypesProjectStateSnapshotSchemaVersion = 4 as const;
export const voltageProjectStateSnapshotSchemaVersion = 5 as const;
export const simultaneityFactorProjectStateSnapshotSchemaVersion = 6 as const;
export const projectStateSnapshotSchemaVersion = 7 as const;
export const projectStateSnapshotSchemaVersion = 1 as const;
const idSchema = z.string().trim().min(1);
const nullableStringSchema = z.string().nullable();
const finiteNumberSchema = z.number().finite();
const legacyProjectSchema = z
const projectSchema = z
.object({
id: idSchema,
name: z.string().trim().min(1),
internalProjectNumber: z.string().trim().min(1).max(100).nullable(),
externalProjectNumber: z.string().trim().min(1).max(100).nullable(),
buildingOwner: z.string().trim().min(1).max(200).nullable(),
description: z.string().trim().min(1).max(2000).nullable(),
singlePhaseVoltageV: finiteNumberSchema.positive(),
threePhaseVoltageV: finiteNumberSchema.positive(),
enabledDistributionBoardSupplyTypes: z
.array(z.enum(distributionBoardSupplyTypes))
.min(1)
.refine((values) => new Set(values).size === values.length),
})
.strict();
const projectSchema = legacyProjectSchema.extend({
internalProjectNumber: z.string().trim().min(1).max(100).nullable(),
externalProjectNumber: z.string().trim().min(1).max(100).nullable(),
buildingOwner: z.string().trim().min(1).max(200).nullable(),
description: z.string().trim().min(1).max(2000).nullable(),
});
const currentProjectSchema = projectSchema.extend({
enabledDistributionBoardSupplyTypes: z
.array(z.enum(distributionBoardSupplyTypes))
.min(1)
.refine((values) => new Set(values).size === values.length),
});
const legacyDistributionBoardSchema = z
const distributionBoardSchema = z
.object({
id: idSchema,
projectId: idSchema,
name: z.string().trim().min(1),
})
.strict();
const distributionBoardSchema = legacyDistributionBoardSchema
.extend({
floorId: idSchema.nullable(),
supplyType: z.enum(distributionBoardSupplyTypes).nullable(),
})
.strict();
const currentDistributionBoardSchema = distributionBoardSchema
.extend({
simultaneityFactor: finiteNumberSchema.min(0).max(1),
})
.strict();
@@ -89,7 +62,7 @@ const circuitListSchema = z
})
.strict();
const legacyCircuitSectionSchema = z
const circuitSectionSchema = z
.object({
id: idSchema,
circuitListId: idSchema,
@@ -97,11 +70,6 @@ const legacyCircuitSectionSchema = z
displayName: z.string().trim().min(1),
prefix: z.string().trim().min(1),
sortOrder: finiteNumberSchema,
})
.strict();
const circuitSectionSchema = legacyCircuitSectionSchema
.extend({
category: z.enum(circuitGroupCategories).nullable(),
groupNumber: z.number().int().positive().nullable(),
})
@@ -121,11 +89,10 @@ const circuitDeviceRowSchema = z
id: idSchema,
circuitId: idSchema,
linkedProjectDeviceId: idSchema.nullable(),
legacyConsumerId: z.string().nullable(),
sortOrder: finiteNumberSchema,
name: z.string().trim().min(1),
displayName: z.string().trim().min(1),
phaseType: nullableStringSchema,
phaseType: z.enum(["single_phase", "three_phase"]).nullable(),
connectionKind: nullableStringSchema,
costGroup: nullableStringSchema,
category: nullableStringSchema,
@@ -150,9 +117,6 @@ const circuitSchema = z
equipmentIdentifier: z.string().trim().min(1),
displayName: nullableStringSchema,
sortOrder: finiteNumberSchema,
protectionType: nullableStringSchema,
protectionRatedCurrent: finiteNumberSchema.nonnegative().nullable(),
protectionCharacteristic: nullableStringSchema,
cableType: nullableStringSchema,
cableCrossSection: nullableStringSchema,
cableLength: finiteNumberSchema.nonnegative().nullable(),
@@ -280,18 +244,13 @@ const componentProtectionDeviceSchema = z
.strict()
.superRefine(validatePersistedProtectionDevice);
const legacyProjectStateSnapshotContents = {
const projectStateSnapshotContents = {
circuitLists: z.array(circuitListSchema),
circuitSections: z.array(legacyCircuitSectionSchema),
circuitSections: z.array(circuitSectionSchema),
circuits: z.array(circuitSchema),
projectDevices: z.array(projectDeviceSchema),
floors: z.array(floorSchema),
rooms: z.array(roomSchema),
};
const currentProjectStateSnapshotContents = {
...legacyProjectStateSnapshotContents,
circuitSections: z.array(circuitSectionSchema),
distributionBoardComponents: z.array(distributionBoardComponentSchema),
circuitProtectionDevices: z.array(circuitProtectionDeviceSchema),
distributionBoardComponentProtectionDevices: z.array(
@@ -299,64 +258,14 @@ const currentProjectStateSnapshotContents = {
),
};
const legacyProjectStateSnapshotSchema = z
export const projectStateSnapshotSchema = z
.object({
schemaVersion: z.literal(legacyProjectStateSnapshotSchemaVersion),
project: legacyProjectSchema,
distributionBoards: z.array(legacyDistributionBoardSchema),
...legacyProjectStateSnapshotContents,
})
.strict();
const previousProjectStateSnapshotSchema = z
.object({
schemaVersion: z.literal(previousProjectStateSnapshotSchemaVersion),
project: projectSchema,
distributionBoards: z.array(legacyDistributionBoardSchema),
...legacyProjectStateSnapshotContents,
})
.strict();
const distributionBoardProjectStateSnapshotSchema = z
.object({
schemaVersion: z.literal(
distributionBoardProjectStateSnapshotSchemaVersion
),
project: projectSchema,
distributionBoards: z.array(distributionBoardSchema),
...legacyProjectStateSnapshotContents,
})
.strict();
const supplyTypesProjectStateSnapshotSchema = z
.object({
schemaVersion: z.literal(supplyTypesProjectStateSnapshotSchemaVersion),
project: currentProjectSchema,
distributionBoards: z.array(distributionBoardSchema),
...legacyProjectStateSnapshotContents,
})
.strict();
const voltageProjectStateSnapshotSchema =
supplyTypesProjectStateSnapshotSchema.extend({
schemaVersion: z.literal(
voltageProjectStateSnapshotSchemaVersion
),
});
const simultaneityFactorProjectStateSnapshotSchema =
voltageProjectStateSnapshotSchema.extend({
schemaVersion: z.literal(
simultaneityFactorProjectStateSnapshotSchemaVersion
),
distributionBoards: z.array(currentDistributionBoardSchema),
});
export const projectStateSnapshotSchema =
simultaneityFactorProjectStateSnapshotSchema.extend({
schemaVersion: z.literal(projectStateSnapshotSchemaVersion),
...currentProjectStateSnapshotContents,
});
project: projectSchema,
distributionBoards: z.array(distributionBoardSchema),
...projectStateSnapshotContents,
})
.strict();
export type ProjectStateSnapshot = z.infer<
typeof projectStateSnapshotSchema
@@ -365,68 +274,7 @@ export type ProjectStateSnapshot = z.infer<
export function parseProjectStateSnapshot(
value: unknown
): ProjectStateSnapshot {
const version = isPlainObject(value) ? value.schemaVersion : undefined;
const parsedSnapshot =
version === legacyProjectStateSnapshotSchemaVersion
? upgradeSimultaneityFactorProjectStateSnapshot(
upgradeVoltageProjectStateSnapshot(
upgradeSupplyTypesProjectStateSnapshot(
upgradeDistributionBoardProjectStateSnapshot(
upgradePreviousProjectStateSnapshot(
upgradeLegacyProjectStateSnapshot(
legacyProjectStateSnapshotSchema.parse(value)
)
)
)
)
)
)
: version === previousProjectStateSnapshotSchemaVersion
? upgradeSimultaneityFactorProjectStateSnapshot(
upgradeVoltageProjectStateSnapshot(
upgradeSupplyTypesProjectStateSnapshot(
upgradeDistributionBoardProjectStateSnapshot(
upgradePreviousProjectStateSnapshot(
previousProjectStateSnapshotSchema.parse(value)
)
)
)
)
)
: version === distributionBoardProjectStateSnapshotSchemaVersion
? upgradeSimultaneityFactorProjectStateSnapshot(
upgradeVoltageProjectStateSnapshot(
upgradeSupplyTypesProjectStateSnapshot(
upgradeDistributionBoardProjectStateSnapshot(
distributionBoardProjectStateSnapshotSchema.parse(
value
)
)
)
)
)
: version === supplyTypesProjectStateSnapshotSchemaVersion
? upgradeSimultaneityFactorProjectStateSnapshot(
upgradeVoltageProjectStateSnapshot(
upgradeSupplyTypesProjectStateSnapshot(
supplyTypesProjectStateSnapshotSchema.parse(value)
)
)
)
: version === voltageProjectStateSnapshotSchemaVersion
? upgradeSimultaneityFactorProjectStateSnapshot(
upgradeVoltageProjectStateSnapshot(
voltageProjectStateSnapshotSchema.parse(value)
)
)
: version ===
simultaneityFactorProjectStateSnapshotSchemaVersion
? upgradeSimultaneityFactorProjectStateSnapshot(
simultaneityFactorProjectStateSnapshotSchema.parse(
value
)
)
: projectStateSnapshotSchema.parse(value);
const parsedSnapshot = projectStateSnapshotSchema.parse(value);
const snapshot = normalizeSnapshotPhaseTypes(parsedSnapshot);
assertProjectStateSnapshotRelations(snapshot);
return snapshot;
@@ -446,15 +294,13 @@ function normalizeSnapshotPhaseTypes(
circuits: snapshot.circuits.map((circuit) => {
const section = sectionById.get(circuit.sectionId);
const deviceRows = circuit.deviceRows.map((row) => {
const normalizedPhaseType =
normalizeKnownElectricalPhaseType(row.phaseType);
const linkedPhaseType = row.linkedProjectDeviceId
? projectDeviceById.get(row.linkedProjectDeviceId)?.phaseType
: undefined;
return {
...row,
phaseType:
normalizedPhaseType ??
row.phaseType ??
linkedPhaseType ??
(section?.key === "three_phase"
? "three_phase"
@@ -478,132 +324,6 @@ function normalizeSnapshotPhaseTypes(
};
}
function upgradeLegacyProjectStateSnapshot(
snapshot: z.infer<typeof legacyProjectStateSnapshotSchema>
): z.infer<typeof previousProjectStateSnapshotSchema> {
return {
...snapshot,
schemaVersion: previousProjectStateSnapshotSchemaVersion,
project: {
...snapshot.project,
internalProjectNumber: null,
externalProjectNumber: null,
buildingOwner: null,
description: null,
},
};
}
function upgradePreviousProjectStateSnapshot(
snapshot: z.infer<typeof previousProjectStateSnapshotSchema>
): z.infer<typeof distributionBoardProjectStateSnapshotSchema> {
return {
...snapshot,
schemaVersion: distributionBoardProjectStateSnapshotSchemaVersion,
distributionBoards: snapshot.distributionBoards.map((board) => ({
...board,
floorId: null,
supplyType: null,
})),
};
}
function upgradeDistributionBoardProjectStateSnapshot(
snapshot: z.infer<typeof distributionBoardProjectStateSnapshotSchema>
): z.infer<typeof supplyTypesProjectStateSnapshotSchema> {
return {
...snapshot,
schemaVersion: supplyTypesProjectStateSnapshotSchemaVersion,
project: {
...snapshot.project,
enabledDistributionBoardSupplyTypes: [
...defaultDistributionBoardSupplyTypes,
],
},
};
}
function upgradeSupplyTypesProjectStateSnapshot(
snapshot: z.infer<typeof supplyTypesProjectStateSnapshotSchema>
): z.infer<typeof voltageProjectStateSnapshotSchema> {
const settings = snapshot.project;
const sectionById = new Map(
snapshot.circuitSections.map((section) => [section.id, section])
);
return {
...snapshot,
schemaVersion: voltageProjectStateSnapshotSchemaVersion,
projectDevices: snapshot.projectDevices.map((device) => ({
...device,
voltageV: resolveProjectVoltage(device.phaseType, settings),
})),
circuits: snapshot.circuits.map((circuit) => {
const section = sectionById.get(circuit.sectionId);
if (!section) {
return circuit;
}
const phaseType = resolveCircuitPhaseType(
section.key,
circuit.deviceRows.map((row) => row.phaseType)
);
return {
...circuit,
voltage: resolveProjectVoltage(phaseType, settings),
};
}),
};
}
function upgradeVoltageProjectStateSnapshot(
snapshot: z.infer<typeof voltageProjectStateSnapshotSchema>
): z.infer<typeof simultaneityFactorProjectStateSnapshotSchema> {
return {
...snapshot,
schemaVersion: simultaneityFactorProjectStateSnapshotSchemaVersion,
distributionBoards: snapshot.distributionBoards.map((board) => ({
...board,
simultaneityFactor: 1,
})),
};
}
function upgradeSimultaneityFactorProjectStateSnapshot(
snapshot: z.infer<typeof simultaneityFactorProjectStateSnapshotSchema>
): ProjectStateSnapshot {
return {
...snapshot,
schemaVersion: projectStateSnapshotSchemaVersion,
circuitSections: snapshot.circuitSections.map((section) => {
const category = initialCategoryBySectionKey(section.key);
return {
...section,
category,
groupNumber: category === null ? null : 1,
};
}),
distributionBoardComponents: [],
circuitProtectionDevices: [],
distributionBoardComponentProtectionDevices: [],
};
}
function initialCategoryBySectionKey(
key: string
): CircuitGroupCategory | null {
if (
key === "lighting" ||
key === "single_phase" ||
key === "three_phase"
) {
return key;
}
return null;
}
function isPlainObject(value: unknown): value is Record<string, unknown> {
return value !== null && typeof value === "object" && !Array.isArray(value);
}
export function serializeProjectStateSnapshot(
snapshot: ProjectStateSnapshot
): string {
@@ -156,7 +156,6 @@ export function remapProjectState(
row.linkedProjectDeviceId === null
? null
: requiredId(deviceIds, row.linkedProjectDeviceId),
legacyConsumerId: null,
roomId:
row.roomId === null ? null : requiredId(roomIds, row.roomId),
})),
@@ -1,143 +0,0 @@
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,60 +0,0 @@
export interface LegacyConsumerForPlanning {
id: string;
circuitNumber: string | null;
category: string | null;
phaseType: string | null;
phaseCount: number | null;
}
// Accepts only normalized BMK-like legacy circuit numbers used for grouping.
export function normalizeCircuitNumber(value: string | null): string | null {
if (!value) {
return null;
}
const trimmed = value.trim().toUpperCase();
if (!trimmed) {
return null;
}
if (!/^-\d+F\d+$/.test(trimmed)) {
return null;
}
return trimmed;
}
// Best-effort fallback when no valid circuit number exists. Keeps migration deterministic
// by preferring explicit category/phase cues over random assignment.
export function inferSectionKeyFromLegacyInput(consumer: LegacyConsumerForPlanning): string | null {
const category = (consumer.category ?? "").toLowerCase();
if (category.includes("light") || category.includes("beleuchtung")) {
return "lighting";
}
if (consumer.phaseCount === 3) {
return "three_phase";
}
if (consumer.phaseCount === 1) {
return "single_phase";
}
const phaseType = (consumer.phaseType ?? "").toLowerCase();
if (phaseType.includes("three") || phaseType.includes("3")) {
return "three_phase";
}
if (phaseType.includes("single") || phaseType.includes("1")) {
return "single_phase";
}
return null;
}
// Prefix-based section inference for already normalized equipment identifiers.
export function inferSectionKeyFromEquipmentIdentifier(equipmentIdentifier: string): string | null {
if (equipmentIdentifier.startsWith("-1F")) {
return "lighting";
}
if (equipmentIdentifier.startsWith("-2F")) {
return "single_phase";
}
if (equipmentIdentifier.startsWith("-3F")) {
return "three_phase";
}
return null;
}
@@ -1,269 +0,0 @@
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";
function parseEquipmentSequence(
equipmentIdentifier: string,
prefix: string
): number | null {
if (!equipmentIdentifier.startsWith(prefix)) {
return null;
}
const suffix = equipmentIdentifier.slice(prefix.length);
if (!/^\d+$/.test(suffix)) {
return null;
}
return Number(suffix);
}
export class LegacyConsumerMigrationService {
constructor(
private readonly dependencies: LegacyConsumerMigrationDependencies
) {}
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.dependencies.circuitListReader.findById(
projectId,
circuitListId
);
if (!list) {
throw new Error("Circuit list not found in project.");
}
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.dependencies.circuitReader.listByCircuitList(circuitListId);
const usedEquipmentIdentifiers = new Set(
existingCircuits.map((circuit) => circuit.equipmentIdentifier.toUpperCase())
);
const legacyConsumers =
await this.dependencies.migrationStore.listSourceConsumersByCircuitList(
circuitListId
);
const rooms = await this.dependencies.roomReader.listByProject(projectId);
const roomById = new Map(rooms.map((room) => [room.id, room]));
const report: LegacyMigrationReport = {
circuitListId,
legacyConsumerCount: legacyConsumers.length,
createdCircuitCount: 0,
createdDeviceRowCount: 0,
groupedDuplicateCircuitNumbers: [],
generatedIdentifiers: [],
unassignedRows: [],
warnings: [],
};
// Idempotency guard: skip consumers already mapped in previous migration run.
const migratedConsumerIds = new Set(
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, LegacyConsumerSource[]>();
const withoutNormalizedCircuitNumber: LegacyConsumerSource[] = [];
for (const consumer of consumersToMigrate) {
const normalized = normalizeCircuitNumber(consumer.circuitNumber ?? null);
if (!normalized) {
withoutNormalizedCircuitNumber.push(consumer);
continue;
}
if (!byNormalizedCircuitNumber.has(normalized)) {
byNormalizedCircuitNumber.set(normalized, []);
}
byNormalizedCircuitNumber.get(normalized)!.push(consumer);
}
// Duplicate normalized circuit numbers are expected and represented as one circuit
// with multiple circuit_device_rows.
report.groupedDuplicateCircuitNumbers = [...byNormalizedCircuitNumber.entries()]
.filter(([, grouped]) => grouped.length > 1)
.map(([normalizedCircuitNumber, grouped]) => ({ normalizedCircuitNumber, count: grouped.length }));
const groups: Array<{
equipmentIdentifier: string | null;
consumers: LegacyConsumerSource[];
inferredSectionKey: string | null;
isGeneratedIdentifier: boolean;
}> = [];
// Stable normalized circuit numbers keep existing intent where available.
for (const [normalizedCircuitNumber, grouped] of byNormalizedCircuitNumber.entries()) {
groups.push({
equipmentIdentifier: normalizedCircuitNumber,
consumers: grouped,
inferredSectionKey: inferSectionKeyFromEquipmentIdentifier(normalizedCircuitNumber),
isGeneratedIdentifier: false,
});
}
// Missing/invalid circuit numbers are migrated as single-row groups with
// generated identifiers and best-effort section inference.
for (const consumer of withoutNormalizedCircuitNumber) {
groups.push({
equipmentIdentifier: null,
consumers: [consumer],
inferredSectionKey: inferSectionKeyFromLegacyInput(consumer),
isGeneratedIdentifier: true,
});
}
let nextSortOrder = existingCircuits.length ? Math.max(...existingCircuits.map((circuit) => circuit.sortOrder)) + 10 : 10;
const maxBySectionPrefix = new Map<string, number>();
for (const circuit of existingCircuits) {
const section = sections.find((entry) => entry.id === circuit.sectionId);
if (!section) {
continue;
}
const sequence = parseEquipmentSequence(circuit.equipmentIdentifier.toUpperCase(), section.prefix.toUpperCase());
if (sequence === null) {
continue;
}
const current = maxBySectionPrefix.get(section.prefix.toUpperCase()) ?? 0;
maxBySectionPrefix.set(section.prefix.toUpperCase(), Math.max(current, sequence));
}
const migrationCircuits: LegacyMigrationCircuitInput[] = [];
for (const group of groups) {
const representative = group.consumers[0];
let section = group.inferredSectionKey ? sectionByKey.get(group.inferredSectionKey) : null;
if (!section) {
section = unassignedSection;
}
let equipmentIdentifier = group.equipmentIdentifier;
if (!equipmentIdentifier || usedEquipmentIdentifiers.has(equipmentIdentifier.toUpperCase())) {
const prefix = section.prefix.toUpperCase();
const current = maxBySectionPrefix.get(prefix) ?? 0;
const generatedSequence = current + 1;
maxBySectionPrefix.set(prefix, generatedSequence);
equipmentIdentifier = `${section.prefix}${generatedSequence}`;
report.generatedIdentifiers.push(equipmentIdentifier);
}
if (!group.inferredSectionKey && group.isGeneratedIdentifier) {
for (const consumer of group.consumers) {
report.unassignedRows.push({
consumerId: consumer.id,
reason: "Missing or invalid circuit number and no section inferred from phase/category.",
});
}
}
usedEquipmentIdentifiers.add(equipmentIdentifier.toUpperCase());
const deviceRows: LegacyMigrationCircuitInput["deviceRows"] = [];
const circuitSortOrder = nextSortOrder;
nextSortOrder += 10;
let rowSortOrder = 10;
for (const consumer of group.consumers) {
const room = consumer.roomId ? roomById.get(consumer.roomId) : undefined;
let noteRemark = consumer.note?.trim() || consumer.comment?.trim() || undefined;
if (consumer.deviceType && consumer.deviceType.trim()) {
const legacyDeviceTypeNote = `Legacy deviceType: ${consumer.deviceType.trim()}`;
noteRemark = noteRemark ? `${noteRemark} | ${legacyDeviceTypeNote}` : legacyDeviceTypeNote;
}
deviceRows.push({
linkedProjectDeviceId: consumer.projectDeviceId ?? undefined,
legacyConsumerId: consumer.id,
sortOrder: rowSortOrder,
name: consumer.name,
displayName: consumer.description ?? consumer.name,
phaseType: consumer.phaseType ?? undefined,
connectionKind: undefined,
costGroup: consumer.tradeOrCostGroup ?? undefined,
category: consumer.category ?? undefined,
roomId: consumer.roomId ?? undefined,
roomNumberSnapshot: room?.roomNumber,
roomNameSnapshot: room?.roomName,
quantity: consumer.quantity,
powerPerUnit: consumer.installedPowerPerUnitKw,
simultaneityFactor: consumer.demandFactor,
cosPhi: consumer.powerFactor ?? undefined,
remark: noteRemark,
});
rowSortOrder += 10;
}
migrationCircuits.push({
circuit: {
circuitListId,
sectionId: section.id,
equipmentIdentifier,
displayName: representative.description ?? representative.name,
sortOrder: circuitSortOrder,
protectionType: representative.protectionType ?? undefined,
protectionRatedCurrent: representative.protectionRatedCurrent ?? undefined,
protectionCharacteristic: representative.protectionCharacteristic ?? undefined,
cableType: representative.cableType ?? undefined,
cableCrossSection: representative.cableCrossSection ?? undefined,
voltage: representative.voltageV ?? undefined,
remark: undefined,
},
deviceRows,
});
}
report.createdCircuitCount = migrationCircuits.length;
report.createdDeviceRowCount = migrationCircuits.reduce(
(count, entry) => count + entry.deviceRows.length,
0
);
if (consumersToMigrate.some((consumer) => Boolean(consumer.comment?.trim()))) {
report.warnings.push(
"Legacy comment field was mapped to circuit_device_rows.remark because circuit-level vs row-level intent is ambiguous."
);
}
for (const row of report.unassignedRows) {
report.warnings.push(`Consumer ${row.consumerId} was migrated into unassigned section.`);
}
this.dependencies.migrationStore.persistCircuitListMigration({
circuitListId,
circuits: migrationCircuits,
report: {
legacyConsumerCount: report.legacyConsumerCount,
createdCircuitCount: report.createdCircuitCount,
createdDeviceRowCount: report.createdDeviceRowCount,
duplicateGroupedCount: report.groupedDuplicateCircuitNumbers.length,
generatedIdentifierCount: report.generatedIdentifiers.length,
unassignedRowCount: report.unassignedRows.length,
warningsJson: JSON.stringify(report.warnings),
generatedIdentifiersJson: JSON.stringify(report.generatedIdentifiers),
duplicateGroupsJson: JSON.stringify(report.groupedDuplicateCircuitNumbers),
},
});
return report;
}
}
@@ -6,34 +6,6 @@ export function isElectricalPhaseType(
return value === "single_phase" || value === "three_phase";
}
export function normalizeKnownElectricalPhaseType(
value: string | null
): ElectricalPhaseType | null {
if (value === null) {
return null;
}
const normalized = value
.trim()
.toLocaleLowerCase("de-DE")
.replaceAll("-", "")
.replaceAll("_", "");
if (
["1ph", "1phase", "1phasig", "einphasig", "singlephase"].includes(
normalized
)
) {
return "single_phase";
}
if (
["3ph", "3phase", "3phasig", "dreiphasig", "threephase"].includes(
normalized
)
) {
return "three_phase";
}
throw new Error(`Unknown electrical phase type: ${value}`);
}
export interface ProjectVoltageSettings {
singlePhaseVoltageV: number;
threePhaseVoltageV: number;
@@ -30,10 +30,8 @@ import {
formatPhaseTypeLabel,
formatValue,
getCircuitSectionLabel,
getLegacyProjectColumnLayoutStorageKey,
getProjectColumnLayoutStorageKey,
isGridEditorControlTarget,
migrateLegacyColumnLayout,
normalizeColumnOrder,
parseStoredColumnLayout,
} from "../utils/circuit-grid-model";
@@ -191,9 +189,6 @@ type CircuitReorderDropIntent =
| { kind: "after-circuit"; sectionId: string; targetCircuitId: string; valid: boolean }
| { kind: "section-end"; sectionId: string; valid: boolean };
const LEGACY_COLUMN_LAYOUT_STORAGE_KEY =
"circuitTreeEditor.columnLayout.v1";
function normalizeUiError(err: unknown): string {
const message = err instanceof Error ? err.message : "Der Vorgang ist fehlgeschlagen.";
try {
@@ -379,25 +374,11 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
const currentLayout = parseStoredColumnLayout(
localStorage.getItem(getProjectColumnLayoutStorageKey(projectId))
);
const legacyLayout =
parseStoredColumnLayout(
localStorage.getItem(
getLegacyProjectColumnLayoutStorageKey(projectId)
)
) ??
parseStoredColumnLayout(
localStorage.getItem(LEGACY_COLUMN_LAYOUT_STORAGE_KEY)
);
const parsed =
currentLayout ??
(legacyLayout
? migrateLegacyColumnLayout(legacyLayout)
: null);
if (!parsed) {
if (!currentLayout) {
return;
}
setColumnOrder(parsed.order);
setVisibleColumnKeys(parsed.visible);
setColumnOrder(currentLayout.order);
setVisibleColumnKeys(currentLayout.visible);
} catch {
// ignore invalid local storage and use defaults
} finally {
@@ -114,11 +114,11 @@ function SectionRows(props: { section: CircuitTreeResponseDto["sections"][number
<td className="text-end">
{formatValue(circuit.circuitTotalPower, "circuitTotalPower")}
</td>
<td>{circuit.protectionType ?? "-"}</td>
<td>{circuit.protectionDevice?.type ?? "-"}</td>
<td className="text-end">
{formatValue(circuit.protectionRatedCurrent, "protectionRatedCurrent")}
{formatValue(circuit.protectionDevice?.ratedCurrentA, "protectionRatedCurrent")}
</td>
<td>{circuit.protectionCharacteristic ?? "-"}</td>
<td>{circuit.protectionDevice?.tripCharacteristic ?? circuit.protectionDevice?.fuseUtilizationCategory ?? "-"}</td>
<td>{circuit.cableType ?? "-"}</td>
<td>{circuit.cableCrossSection ?? "-"}</td>
<td className="text-end">{formatValue(circuit.cableLength, "cableLength")}</td>
@@ -147,11 +147,11 @@ function SectionRows(props: { section: CircuitTreeResponseDto["sections"][number
</td>
<td className="text-end">{formatValue(row.cosPhi, "cosPhi")}</td>
<td className="text-end">{formatValue(row.rowTotalPower, "rowTotalPower")}</td>
<td>{circuit.protectionType ?? "-"}</td>
<td>{circuit.protectionDevice?.type ?? "-"}</td>
<td className="text-end">
{formatValue(circuit.protectionRatedCurrent, "protectionRatedCurrent")}
{formatValue(circuit.protectionDevice?.ratedCurrentA, "protectionRatedCurrent")}
</td>
<td>{circuit.protectionCharacteristic ?? "-"}</td>
<td>{circuit.protectionDevice?.tripCharacteristic ?? circuit.protectionDevice?.fuseUtilizationCategory ?? "-"}</td>
<td>{circuit.cableType ?? "-"}</td>
<td>{circuit.cableCrossSection ?? "-"}</td>
<td className="text-end">{formatValue(circuit.cableLength, "cableLength")}</td>
@@ -169,11 +169,11 @@ function SectionRows(props: { section: CircuitTreeResponseDto["sections"][number
<td className="text-end">
{formatValue(circuit.circuitTotalPower, "circuitTotalPower")}
</td>
<td>{circuit.protectionType ?? "-"}</td>
<td>{circuit.protectionDevice?.type ?? "-"}</td>
<td className="text-end">
{formatValue(circuit.protectionRatedCurrent, "protectionRatedCurrent")}
{formatValue(circuit.protectionDevice?.ratedCurrentA, "protectionRatedCurrent")}
</td>
<td>{circuit.protectionCharacteristic ?? "-"}</td>
<td>{circuit.protectionDevice?.tripCharacteristic ?? circuit.protectionDevice?.fuseUtilizationCategory ?? "-"}</td>
<td>{circuit.cableType ?? "-"}</td>
<td>{circuit.cableCrossSection ?? "-"}</td>
<td className="text-end">{formatValue(circuit.cableLength, "cableLength")}</td>
-19
View File
@@ -267,7 +267,6 @@ export interface ProjectDeviceSyncCommandResultDto
export interface CircuitTreeDeviceRowDto {
id: string;
linkedProjectDeviceId?: string;
legacyConsumerId?: string;
sortOrder: number;
name: string;
displayName: string;
@@ -316,9 +315,6 @@ export interface CircuitTreeCircuitDto {
equipmentIdentifier: string;
displayName?: string;
sortOrder: number;
protectionType?: string;
protectionRatedCurrent?: number;
protectionCharacteristic?: string;
cableType?: string;
cableCrossSection?: string;
cableLength?: number;
@@ -347,17 +343,6 @@ export interface CircuitTreeSectionDto {
circuits: CircuitTreeCircuitDto[];
}
export interface CircuitTreeMigrationReportDto {
circuitListId: string;
legacyConsumerCount: number;
createdCircuitCount: number;
createdDeviceRowCount: number;
groupedDuplicateCircuitNumbers: Array<{ normalizedCircuitNumber: string; count: number }>;
generatedIdentifiers: string[];
unassignedRows: Array<{ consumerId: string; reason: string }>;
warnings: string[];
}
export interface CircuitTreeResponseDto {
circuitListId: string;
currentRevision: number;
@@ -369,7 +354,6 @@ export interface CircuitTreeResponseDto {
headerComponents: CircuitTreeComponentDto[];
sections: CircuitTreeSectionDto[];
footerComponents: CircuitTreeComponentDto[];
migrationReport?: CircuitTreeMigrationReportDto;
}
export interface CreateCircuitInputDto {
@@ -377,9 +361,6 @@ export interface CreateCircuitInputDto {
equipmentIdentifier: string;
displayName?: string;
sortOrder: number;
protectionType?: string;
protectionRatedCurrent?: number;
protectionCharacteristic?: string;
cableType?: string;
cableCrossSection?: string;
cableLength?: number;
+5 -48
View File
@@ -130,8 +130,6 @@ export const defaultVisibleColumnKeys = allColumns
const projectColumnLayoutStoragePrefix =
"circuitTreeEditor.columnLayout.v3";
const legacyProjectColumnLayoutStoragePrefix =
"circuitTreeEditor.columnLayout.v2";
export interface CircuitColumnLayout {
order: CellKey[];
@@ -164,12 +162,6 @@ export function getProjectColumnLayoutStorageKey(
return `${projectColumnLayoutStoragePrefix}.${projectId}`;
}
export function getLegacyProjectColumnLayoutStorageKey(
projectId: string
): string {
return `${legacyProjectColumnLayoutStoragePrefix}.${projectId}`;
}
export function normalizeColumnOrder(keys: CellKey[]): CellKey[] {
const unique = [...new Set(keys)];
const allKeys = allColumns.map((column) => column.key);
@@ -233,28 +225,6 @@ export function parseStoredColumnLayout(
}
}
export function migrateLegacyColumnLayout(
layout: CircuitColumnLayout
): CircuitColumnLayout {
const newlyVisibleKey: CellKey = "protectionSummary";
const order = layout.order.filter(
(key) => key !== newlyVisibleKey
) as CellKey[];
const totalPowerIndex = order.indexOf("rowTotalPower");
order.splice(
totalPowerIndex >= 0 ? totalPowerIndex + 1 : order.length,
0,
newlyVisibleKey
);
return {
order,
visible: layout.visible.includes(newlyVisibleKey)
? layout.visible
: [...layout.visible, newlyVisibleKey],
};
}
const deviceOnlyColumns = new Set<CellKey>([
"quantity",
"powerPerUnit",
@@ -422,11 +392,7 @@ export function buildCircuitEditPatch(
if (key === "protectionSummary" || key === "cableSummary") {
return { remark: draft.trim() || null };
}
if (
key === "protectionRatedCurrent" ||
key === "cableLength" ||
key === "voltage"
) {
if (key === "cableLength" || key === "voltage") {
return { [key]: parseNumeric(key, draft) ?? null };
}
if (key === "isReserve") {
@@ -530,23 +496,14 @@ export function getCircuitValue(circuit: CircuitTreeCircuitDto, key: CellKey): G
case "protectionType":
return protection
? protectionDeviceTypeLabels[protection.type]
: circuit.protectionType;
: undefined;
case "protectionRatedCurrent":
return protection?.ratedCurrentA ?? circuit.protectionRatedCurrent;
return protection?.ratedCurrentA;
case "protectionCharacteristic":
return protectionCharacteristic || circuit.protectionCharacteristic;
return protectionCharacteristic || undefined;
case "protectionSummary": {
if (!protection) {
const legacyCurrent =
circuit.protectionRatedCurrent !== undefined &&
circuit.protectionRatedCurrent !== null
? `${circuit.protectionRatedCurrent}A`
: "";
return [
circuit.protectionType,
legacyCurrent,
circuit.protectionCharacteristic,
].filter(Boolean).join(" ").trim() || undefined;
return undefined;
}
const currentValue =
protection.ratedCurrentA;
@@ -117,11 +117,6 @@ export function buildCircuitGroupSubtreeSnapshot(
equipmentIdentifier: circuit.equipmentIdentifier,
displayName: circuit.displayName ?? null,
sortOrder: circuit.sortOrder,
protectionType: circuit.protectionType ?? null,
protectionRatedCurrent:
circuit.protectionRatedCurrent ?? null,
protectionCharacteristic:
circuit.protectionCharacteristic ?? null,
cableType: circuit.cableType ?? null,
cableCrossSection: circuit.cableCrossSection ?? null,
cableLength: circuit.cableLength ?? null,
@@ -143,7 +138,6 @@ export function buildCircuitGroupSubtreeSnapshot(
circuitId: circuit.id,
linkedProjectDeviceId:
row.linkedProjectDeviceId ?? null,
legacyConsumerId: row.legacyConsumerId ?? null,
sortOrder: row.sortOrder,
name: row.name,
displayName: row.displayName,
@@ -22,7 +22,6 @@ export function buildCircuitDeviceRowInsertSnapshot(input: {
id,
circuitId,
linkedProjectDeviceId: values.linkedProjectDeviceId ?? null,
legacyConsumerId: null,
sortOrder: values.sortOrder ?? defaultSortOrder,
name: values.name,
displayName: values.displayName,
@@ -61,9 +60,6 @@ export function buildCircuitInsertSnapshot(input: {
equipmentIdentifier: values.equipmentIdentifier,
displayName: values.displayName ?? null,
sortOrder: values.sortOrder,
protectionType: values.protectionType ?? null,
protectionRatedCurrent: values.protectionRatedCurrent ?? null,
protectionCharacteristic: values.protectionCharacteristic ?? null,
cableType: values.cableType ?? null,
cableCrossSection: values.cableCrossSection ?? null,
cableLength: values.cableLength ?? null,
@@ -148,7 +148,6 @@ export async function getCircuitTree(req: Request, res: Response) {
const deviceRows = (rowsByCircuitId.get(circuit.id) ?? []).map((row) => ({
id: row.id,
linkedProjectDeviceId: row.linkedProjectDeviceId ?? undefined,
legacyConsumerId: row.legacyConsumerId ?? undefined,
sortOrder: row.sortOrder,
name: row.name,
displayName: row.displayName,
@@ -178,9 +177,6 @@ export async function getCircuitTree(req: Request, res: Response) {
equipmentIdentifier: circuit.equipmentIdentifier,
displayName: circuit.displayName ?? undefined,
sortOrder: circuit.sortOrder,
protectionType: circuit.protectionType ?? undefined,
protectionRatedCurrent: circuit.protectionRatedCurrent ?? undefined,
protectionCharacteristic: circuit.protectionCharacteristic ?? undefined,
cableType: circuit.cableType ?? undefined,
cableCrossSection: circuit.cableCrossSection ?? undefined,
cableLength: circuit.cableLength ?? undefined,