Add component persistence foundation
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
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
@@ -169,6 +169,13 @@
|
||||
"when": 1785343645615,
|
||||
"tag": "0023_cheerful_night_thrasher",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 24,
|
||||
"version": "6",
|
||||
"when": 1785431250141,
|
||||
"tag": "0024_damp_mister_sinister",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import type {
|
||||
DistributionBoardStructureProjectCommandStore,
|
||||
ExecuteDistributionBoardStructureCommandInput,
|
||||
} from "../../domain/ports/distribution-board-structure-project-command.store.js";
|
||||
import type { CircuitGroupCategory } from "../../shared/constants/circuit-group.js";
|
||||
import type { AppDatabase } from "../database-context.js";
|
||||
import { circuitLists } from "../schema/circuit-lists.js";
|
||||
import { circuitSections } from "../schema/circuit-sections.js";
|
||||
@@ -168,7 +169,10 @@ export class DistributionBoardStructureProjectCommandRepository
|
||||
.values(structure.distributionBoard)
|
||||
.run();
|
||||
database.insert(circuitLists).values(structure.circuitList).run();
|
||||
database.insert(circuitSections).values(structure.sections).run();
|
||||
database
|
||||
.insert(circuitSections)
|
||||
.values(structure.sections.map(withInitialGroupIdentity))
|
||||
.run();
|
||||
return createDistributionBoardDeleteProjectCommand(structure);
|
||||
}
|
||||
|
||||
@@ -409,9 +413,48 @@ function structureMatches(
|
||||
left.sortOrder - right.sortOrder ||
|
||||
left.id.localeCompare(right.id)
|
||||
);
|
||||
return expectedSections.every((section, index) =>
|
||||
sameRecord(section, actual.sections[index])
|
||||
);
|
||||
return expectedSections.every((section, index) => {
|
||||
const actualSection = actual.sections[index];
|
||||
if (
|
||||
actualSection.category !== initialCategoryBySectionKey(section.key) ||
|
||||
actualSection.groupNumber !== initialGroupNumberBySectionKey(section.key)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
const {
|
||||
category: _category,
|
||||
groupNumber: _groupNumber,
|
||||
...comparableActualSection
|
||||
} = actualSection;
|
||||
return sameRecord(section, comparableActualSection);
|
||||
});
|
||||
}
|
||||
|
||||
function withInitialGroupIdentity(
|
||||
section: DistributionBoardStructureSnapshot["sections"][number]
|
||||
): typeof circuitSections.$inferInsert {
|
||||
return {
|
||||
...section,
|
||||
category: initialCategoryBySectionKey(section.key),
|
||||
groupNumber: initialGroupNumberBySectionKey(section.key),
|
||||
};
|
||||
}
|
||||
|
||||
function initialCategoryBySectionKey(
|
||||
key: string
|
||||
): CircuitGroupCategory | null {
|
||||
if (
|
||||
key === "lighting" ||
|
||||
key === "single_phase" ||
|
||||
key === "three_phase"
|
||||
) {
|
||||
return key;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function initialGroupNumberBySectionKey(key: string): number | null {
|
||||
return initialCategoryBySectionKey(key) === null ? null : 1;
|
||||
}
|
||||
|
||||
function sameRecord(
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { sqliteTable, text, unique } from "drizzle-orm/sqlite-core";
|
||||
import { circuitLists } from "./circuit-lists.js";
|
||||
|
||||
export const circuitListEquipmentIdentifiers = sqliteTable(
|
||||
"circuit_list_equipment_identifiers",
|
||||
{
|
||||
ownerType: text("owner_type")
|
||||
.$type<"circuit" | "distribution_board_component">()
|
||||
.notNull(),
|
||||
ownerId: text("owner_id").notNull(),
|
||||
circuitListId: text("circuit_list_id")
|
||||
.notNull()
|
||||
.references(() => circuitLists.id, { onDelete: "cascade" }),
|
||||
equipmentIdentifier: text("equipment_identifier").notNull(),
|
||||
},
|
||||
(table) => [
|
||||
unique("circuit_list_equipment_identifiers_owner_unique").on(
|
||||
table.ownerType,
|
||||
table.ownerId
|
||||
),
|
||||
unique("circuit_list_equipment_identifiers_list_identifier_unique").on(
|
||||
table.circuitListId,
|
||||
table.equipmentIdentifier
|
||||
),
|
||||
]
|
||||
);
|
||||
@@ -0,0 +1,27 @@
|
||||
import { real, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
||||
import type {
|
||||
BreakerTripCharacteristic,
|
||||
FuseUtilizationCategory,
|
||||
ProtectionDeviceType,
|
||||
RcdType,
|
||||
} from "../../shared/constants/protection-device.js";
|
||||
import { circuits } from "./circuits.js";
|
||||
|
||||
export const circuitProtectionDevices = sqliteTable(
|
||||
"circuit_protection_devices",
|
||||
{
|
||||
circuitId: text("circuit_id")
|
||||
.primaryKey()
|
||||
.references(() => circuits.id, { onDelete: "cascade" }),
|
||||
type: text("type").$type<ProtectionDeviceType>().notNull(),
|
||||
ratedCurrentA: real("rated_current_a").notNull(),
|
||||
fuseUtilizationCategory: text(
|
||||
"fuse_utilization_category"
|
||||
).$type<FuseUtilizationCategory>(),
|
||||
tripCharacteristic: text(
|
||||
"trip_characteristic"
|
||||
).$type<BreakerTripCharacteristic>(),
|
||||
rcdType: text("rcd_type").$type<RcdType>(),
|
||||
ratedResidualCurrentMa: real("rated_residual_current_ma"),
|
||||
}
|
||||
);
|
||||
@@ -1,5 +1,6 @@
|
||||
import { integer, sqliteTable, text, unique } from "drizzle-orm/sqlite-core";
|
||||
import { circuitLists } from "./circuit-lists.js";
|
||||
import type { CircuitGroupCategory } from "../../shared/constants/circuit-group.js";
|
||||
|
||||
export const circuitSections = sqliteTable(
|
||||
"circuit_sections",
|
||||
@@ -12,10 +13,17 @@ export const circuitSections = sqliteTable(
|
||||
displayName: text("display_name").notNull(),
|
||||
prefix: text("prefix").notNull(),
|
||||
sortOrder: integer("sort_order").notNull().default(0),
|
||||
category: text("category").$type<CircuitGroupCategory>(),
|
||||
groupNumber: integer("group_number"),
|
||||
},
|
||||
(table) => [
|
||||
unique("circuit_sections_list_key_unique").on(table.circuitListId, table.key),
|
||||
unique("circuit_sections_list_prefix_unique").on(table.circuitListId, table.prefix),
|
||||
unique("circuit_sections_list_category_group_unique").on(
|
||||
table.circuitListId,
|
||||
table.category,
|
||||
table.groupNumber
|
||||
),
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { real, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
||||
import type {
|
||||
BreakerTripCharacteristic,
|
||||
FuseUtilizationCategory,
|
||||
ProtectionDeviceType,
|
||||
RcdType,
|
||||
} from "../../shared/constants/protection-device.js";
|
||||
import { distributionBoardComponents } from "./distribution-board-components.js";
|
||||
|
||||
export const distributionBoardComponentProtectionDevices = sqliteTable(
|
||||
"distribution_board_component_protection_devices",
|
||||
{
|
||||
componentId: text("component_id")
|
||||
.primaryKey()
|
||||
.references(() => distributionBoardComponents.id, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
type: text("type").$type<ProtectionDeviceType>().notNull(),
|
||||
ratedCurrentA: real("rated_current_a").notNull(),
|
||||
fuseUtilizationCategory: text(
|
||||
"fuse_utilization_category"
|
||||
).$type<FuseUtilizationCategory>(),
|
||||
tripCharacteristic: text(
|
||||
"trip_characteristic"
|
||||
).$type<BreakerTripCharacteristic>(),
|
||||
rcdType: text("rcd_type").$type<RcdType>(),
|
||||
ratedResidualCurrentMa: real("rated_residual_current_ma"),
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,37 @@
|
||||
import { integer, sqliteTable, text, unique } from "drizzle-orm/sqlite-core";
|
||||
import type {
|
||||
DistributionBoardComponentPlacement,
|
||||
DistributionBoardComponentRole,
|
||||
} from "../../shared/constants/distribution-board-component.js";
|
||||
import { circuitLists } from "./circuit-lists.js";
|
||||
import { circuitSections } from "./circuit-sections.js";
|
||||
|
||||
export const distributionBoardComponents = sqliteTable(
|
||||
"distribution_board_components",
|
||||
{
|
||||
id: text("id").primaryKey(),
|
||||
circuitListId: text("circuit_list_id")
|
||||
.notNull()
|
||||
.references(() => circuitLists.id, { onDelete: "cascade" }),
|
||||
sectionId: text("section_id").references(() => circuitSections.id, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
equipmentIdentifier: text("equipment_identifier").notNull(),
|
||||
name: text("name").notNull(),
|
||||
role: text("role").$type<DistributionBoardComponentRole>().notNull(),
|
||||
placement: text("placement")
|
||||
.$type<DistributionBoardComponentPlacement>()
|
||||
.notNull(),
|
||||
sortOrder: integer("sort_order").notNull().default(0),
|
||||
},
|
||||
(table) => [
|
||||
unique("distribution_board_components_list_identifier_unique").on(
|
||||
table.circuitListId,
|
||||
table.equipmentIdentifier
|
||||
),
|
||||
unique("distribution_board_components_section_role_unique").on(
|
||||
table.sectionId,
|
||||
table.role
|
||||
),
|
||||
]
|
||||
);
|
||||
Reference in New Issue
Block a user