diff --git a/docs/current-architecture.md b/docs/current-architecture.md index 60d9b3a..60cca86 100644 --- a/docs/current-architecture.md +++ b/docs/current-architecture.md @@ -184,6 +184,15 @@ Stromkreisleistungen in einer gemeinsamen Spalte `Gesamtsumme`: Gerätezeilen verwenden `rowTotalPower`, Stromkreis-Sammelzeilen `circuitTotalPower`. Zahlen werden ausschließlich für die Anzeige deutsch und begrenzt formatiert; gespeicherte Werte und Bearbeitungsentwürfe behalten ihre volle Genauigkeit. +Jeder Abschnitt liefert und zeigt seine aufsummierte Stromkreisleistung. Die +Stromkreisliste zeigt außerdem die ungefilterte Gesamtleistung des Verteilers, +den am Verteiler gespeicherten Gleichzeitigkeitsfaktor und die daraus +abgeleitete Gesamtleistung unter Berücksichtigung dieses Faktors. Sortierung +und Filter verändern diese fachlichen Summen nicht. +Reihenfolge und Sichtbarkeit der Grid-Spalten sind reine UI-Präferenzen. Sie +werden im Browser unter einem projektspezifischen Schlüssel gespeichert und +deshalb beim Wechsel zwischen Verteilern desselben Projekts wiederverwendet, +ohne Projektrevisionen oder fachliche Snapshots zu erzeugen. `circuit.reorder-section` speichert die erwartete und neue Sortierposition jedes Stromkreises eines vollständigen Abschnitts. Forward, Undo und Redo ändern ausschließlich `sortOrder`; Stromkreisblöcke, Gerätezeilen und BMKs @@ -271,12 +280,16 @@ Controller-Schreibweg ist entfernt. Verteilungen besitzen eine optionale Etagenreferenz sowie eine Netzart aus dem Projektkatalog. Anlage und nachträgliche Bearbeitung prüfen die Projektzugehörigkeit der Etage und die Freigabe der Netzart in den Projekteinstellungen. -`distribution-board.update` versioniert Etage und Netzart gemeinsam und stellt -beide Werte über dauerhaftes Undo/Redo wieder her. Snapshot-Schema 4 und der -portable Projekttransfer enthalten diese Felder; Schema 1/2 sowie gespeicherte -Version-1-Strukturcommands werden ohne erfundene Zuordnung hochgestuft. -Snapshot-Schema 3 wird mit allen sechs Netzarten als Projektauswahl -hochgestuft. +`distribution-board.update` versioniert Etage, Netzart und den +verteilerweiten Gleichzeitigkeitsfaktor gemeinsam und stellt alle Werte über +dauerhaftes Undo/Redo wieder her. Der Faktor liegt zwischen `0` und `1` und +ist für bestehende sowie neu angelegte Verteilungen standardmäßig `1`. +Snapshot-Schema 6 und der portable Projekttransfer enthalten diesen Wert; +Schema 5 und älter werden mit dem neutralen Faktor `1` hochgestuft. +Snapshot-Schema 4 enthält bereits Etage und Netzart; Schema 1/2 sowie +gespeicherte Version-1-Strukturcommands werden ohne erfundene Zuordnung +hochgestuft. Snapshot-Schema 3 wird mit allen sechs Netzarten als +Projektauswahl hochgestuft. `project-floor.insert` und `project-room.insert` versionieren die Anlage von Geschossen und Räumen mit stabilen UUIDs. Die vollständigen Datensätze bilden jeweils die persistierte Inverse für Undo/Redo. Ein Geschoss wird durch Undo nur diff --git a/docs/spec/08-current-product-backlog.md b/docs/spec/08-current-product-backlog.md index 83b1658..7e2f055 100644 --- a/docs/spec/08-current-product-backlog.md +++ b/docs/spec/08-current-product-backlog.md @@ -52,11 +52,24 @@ requirements and intended sequencing, not proof of implementation. stored values. Version-four and older imports remain supported and are normalized while being upgraded. +## Circuit List Power Summary + +- [x] Show the total power of every circuit section. +- [x] Show the unadjusted total power of the complete distribution board. +- [x] Store a distribution-board simultaneity factor between zero and one in + the distribution-board settings. +- [x] Show the distribution-board total after applying that factor. +- [x] Preserve the factor in project commands, snapshots and portable project + transfers. +- [x] Keep column visibility and order as a project-specific UI preference so + all distribution boards in the same project open with the same columns. + ## Recommended Sequence 1. [x] Distribution-board floor and supply fields. 2. [x] Device voltage derivation without overrides. 3. [x] Snapshot-to-revision descriptions and history presentation. +4. [x] Distribution-board power summary and project column layout. The Revit/CSV/IFCGUID round-trip remains a separate jointly planned phase and must not be inferred from these tasks. diff --git a/src/app/globals.css b/src/app/globals.css index e78dbaa..351a429 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -116,6 +116,39 @@ body { font-weight: 600; } +.distribution-power-summary { + display: grid; + grid-template-columns: repeat(3, minmax(12rem, 1fr)); + gap: 0.5rem; + padding: 0.55rem; + border: 1px solid #cbd5e1; + border-radius: 5px; + background: #f8fafc; +} + +.distribution-power-summary > div { + display: flex; + flex-direction: column; + gap: 0.15rem; +} + +.distribution-power-summary span { + color: #475569; + font-size: 0.74rem; + line-height: 1.2; +} + +.distribution-power-summary strong { + color: #172033; + font-size: 0.95rem; +} + +@media (max-width: 900px) { + .distribution-power-summary { + grid-template-columns: 1fr; + } +} + .column-settings-menu { position: absolute; z-index: 9; @@ -695,6 +728,18 @@ body { outline-offset: -2px; } +.tree-grid .section-title { + display: flex; + align-items: baseline; + gap: 0.75rem; +} + +.tree-grid .section-title span { + color: #475569; + font-size: 0.78rem; + font-weight: 500; +} + .tree-grid .cell-invalid { outline: 2px solid #c2410c; outline-offset: -2px; diff --git a/src/app/projects/[projectId]/page.tsx b/src/app/projects/[projectId]/page.tsx index 915b6fb..b574312 100644 --- a/src/app/projects/[projectId]/page.tsx +++ b/src/app/projects/[projectId]/page.tsx @@ -87,6 +87,10 @@ export default function ProjectDetailPage() { const [editingBoardFloorId, setEditingBoardFloorId] = useState(""); const [editingBoardSupplyType, setEditingBoardSupplyType] = useState("AV"); + const [ + editingBoardSimultaneityFactor, + setEditingBoardSimultaneityFactor, + ] = useState("1"); const [floorName, setFloorName] = useState(""); const [roomNumber, setRoomNumber] = useState(""); const [roomName, setRoomName] = useState(""); @@ -398,6 +402,9 @@ export default function ProjectDetailPage() { project?.enabledDistributionBoardSupplyTypes[0] ?? "AV" ); + setEditingBoardSimultaneityFactor( + String(board.simultaneityFactor) + ); } function openBoardCreator() { @@ -412,6 +419,17 @@ export default function ProjectDetailPage() { if (!projectId || !project || !editingBoard) { return; } + const simultaneityFactor = Number( + editingBoardSimultaneityFactor + ); + if ( + !editingBoardSimultaneityFactor.trim() || + !Number.isFinite(simultaneityFactor) || + simultaneityFactor < 0 || + simultaneityFactor > 1 + ) { + return; + } setIsSaving(true); setError(null); try { @@ -421,6 +439,7 @@ export default function ProjectDetailPage() { { floorId: editingBoardFloorId || null, supplyType: editingBoardSupplyType, + simultaneityFactor, }, project.currentRevision ); @@ -702,6 +721,7 @@ export default function ProjectDetailPage() { Verteilung Etage Netzart + GZF Aktionen @@ -723,6 +743,15 @@ export default function ProjectDetailPage() { ] : "Nicht festgelegt"} + + {board.simultaneityFactor.toLocaleString( + "de-DE", + { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + } + )} +
+
+ + + setEditingBoardSimultaneityFactor( + event.target.value + ) + } + required + step="0.01" + type="number" + value={editingBoardSimultaneityFactor} + /> +
+ Wird auf die Gesamtleistung des Verteilers angewendet. +
+
) : null} diff --git a/src/db/migrations/0023_cheerful_night_thrasher.sql b/src/db/migrations/0023_cheerful_night_thrasher.sql new file mode 100644 index 0000000..c68bb46 --- /dev/null +++ b/src/db/migrations/0023_cheerful_night_thrasher.sql @@ -0,0 +1 @@ +ALTER TABLE `distribution_boards` ADD `simultaneity_factor` real DEFAULT 1 NOT NULL; \ No newline at end of file diff --git a/src/db/migrations/meta/0023_snapshot.json b/src/db/migrations/meta/0023_snapshot.json new file mode 100644 index 0000000..4602820 --- /dev/null +++ b/src/db/migrations/meta/0023_snapshot.json @@ -0,0 +1,1893 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "f99355d0-b023-424b-870e-3ea8423497eb", + "prevId": "c9eabd70-45c9-43c3-ae63-f0685aeeb1f1", + "tables": { + "circuit_device_rows": { + "name": "circuit_device_rows", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "circuit_id": { + "name": "circuit_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "linked_project_device_id": { + "name": "linked_project_device_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "legacy_consumer_id": { + "name": "legacy_consumer_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "phase_type": { + "name": "phase_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "connection_kind": { + "name": "connection_kind", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cost_group": { + "name": "cost_group", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "level": { + "name": "level", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "room_id": { + "name": "room_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "room_number_snapshot": { + "name": "room_number_snapshot", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "room_name_snapshot": { + "name": "room_name_snapshot", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "power_per_unit": { + "name": "power_per_unit", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "simultaneity_factor": { + "name": "simultaneity_factor", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cos_phi": { + "name": "cos_phi", + "type": "real", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "remark": { + "name": "remark", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "overridden_fields": { + "name": "overridden_fields", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "circuit_device_rows_circuit_id_circuits_id_fk": { + "name": "circuit_device_rows_circuit_id_circuits_id_fk", + "tableFrom": "circuit_device_rows", + "tableTo": "circuits", + "columnsFrom": [ + "circuit_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "circuit_device_rows_linked_project_device_id_project_devices_id_fk": { + "name": "circuit_device_rows_linked_project_device_id_project_devices_id_fk", + "tableFrom": "circuit_device_rows", + "tableTo": "project_devices", + "columnsFrom": [ + "linked_project_device_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "circuit_device_rows_room_id_rooms_id_fk": { + "name": "circuit_device_rows_room_id_rooms_id_fk", + "tableFrom": "circuit_device_rows", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "circuit_lists": { + "name": "circuit_lists", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "distribution_board_id": { + "name": "distribution_board_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "circuit_lists_distribution_board_id_unique": { + "name": "circuit_lists_distribution_board_id_unique", + "columns": [ + "distribution_board_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "circuit_lists_project_id_projects_id_fk": { + "name": "circuit_lists_project_id_projects_id_fk", + "tableFrom": "circuit_lists", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "circuit_lists_distribution_board_id_distribution_boards_id_fk": { + "name": "circuit_lists_distribution_board_id_distribution_boards_id_fk", + "tableFrom": "circuit_lists", + "tableTo": "distribution_boards", + "columnsFrom": [ + "distribution_board_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "circuit_sections": { + "name": "circuit_sections", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "circuit_list_id": { + "name": "circuit_list_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": { + "circuit_sections_list_key_unique": { + "name": "circuit_sections_list_key_unique", + "columns": [ + "circuit_list_id", + "key" + ], + "isUnique": true + }, + "circuit_sections_list_prefix_unique": { + "name": "circuit_sections_list_prefix_unique", + "columns": [ + "circuit_list_id", + "prefix" + ], + "isUnique": true + } + }, + "foreignKeys": { + "circuit_sections_circuit_list_id_circuit_lists_id_fk": { + "name": "circuit_sections_circuit_list_id_circuit_lists_id_fk", + "tableFrom": "circuit_sections", + "tableTo": "circuit_lists", + "columnsFrom": [ + "circuit_list_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "circuits": { + "name": "circuits", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "circuit_list_id": { + "name": "circuit_list_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "section_id": { + "name": "section_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "equipment_identifier": { + "name": "equipment_identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "protection_type": { + "name": "protection_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "protection_rated_current": { + "name": "protection_rated_current", + "type": "real", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "protection_characteristic": { + "name": "protection_characteristic", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cable_type": { + "name": "cable_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cable_cross_section": { + "name": "cable_cross_section", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cable_length": { + "name": "cable_length", + "type": "real", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "rcd_assignment": { + "name": "rcd_assignment", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "terminal_designation": { + "name": "terminal_designation", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "voltage": { + "name": "voltage", + "type": "real", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "control_requirement": { + "name": "control_requirement", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_reserve": { + "name": "is_reserve", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "remark": { + "name": "remark", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "circuits_list_equipment_identifier_unique": { + "name": "circuits_list_equipment_identifier_unique", + "columns": [ + "circuit_list_id", + "equipment_identifier" + ], + "isUnique": true + } + }, + "foreignKeys": { + "circuits_circuit_list_id_circuit_lists_id_fk": { + "name": "circuits_circuit_list_id_circuit_lists_id_fk", + "tableFrom": "circuits", + "tableTo": "circuit_lists", + "columnsFrom": [ + "circuit_list_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "circuits_section_id_circuit_sections_id_fk": { + "name": "circuits_section_id_circuit_sections_id_fk", + "tableFrom": "circuits", + "tableTo": "circuit_sections", + "columnsFrom": [ + "section_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "consumers": { + "name": "consumers", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "distribution_board_id": { + "name": "distribution_board_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "circuit_list_id": { + "name": "circuit_list_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "project_device_id": { + "name": "project_device_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_linked_to_device": { + "name": "is_linked_to_device", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "room_id": { + "name": "room_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "circuit_number": { + "name": "circuit_number", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "device_type": { + "name": "device_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "phase_type": { + "name": "phase_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "trade_or_cost_group": { + "name": "trade_or_cost_group", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "protection_type": { + "name": "protection_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "protection_rated_current": { + "name": "protection_rated_current", + "type": "real", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "protection_characteristic": { + "name": "protection_characteristic", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cable_type": { + "name": "cable_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cable_cross_section": { + "name": "cable_cross_section", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "comment": { + "name": "comment", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "installed_power_per_unit_kw": { + "name": "installed_power_per_unit_kw", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "demand_factor": { + "name": "demand_factor", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "voltage_v": { + "name": "voltage_v", + "type": "real", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "phase_count": { + "name": "phase_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "power_factor": { + "name": "power_factor", + "type": "real", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "consumers_project_id_projects_id_fk": { + "name": "consumers_project_id_projects_id_fk", + "tableFrom": "consumers", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "consumers_distribution_board_id_distribution_boards_id_fk": { + "name": "consumers_distribution_board_id_distribution_boards_id_fk", + "tableFrom": "consumers", + "tableTo": "distribution_boards", + "columnsFrom": [ + "distribution_board_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "consumers_circuit_list_id_circuit_lists_id_fk": { + "name": "consumers_circuit_list_id_circuit_lists_id_fk", + "tableFrom": "consumers", + "tableTo": "circuit_lists", + "columnsFrom": [ + "circuit_list_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "consumers_project_device_id_project_devices_id_fk": { + "name": "consumers_project_device_id_project_devices_id_fk", + "tableFrom": "consumers", + "tableTo": "project_devices", + "columnsFrom": [ + "project_device_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "consumers_room_id_rooms_id_fk": { + "name": "consumers_room_id_rooms_id_fk", + "tableFrom": "consumers", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "distribution_boards": { + "name": "distribution_boards", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "floor_id": { + "name": "floor_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "supply_type": { + "name": "supply_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "simultaneity_factor": { + "name": "simultaneity_factor", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + } + }, + "indexes": {}, + "foreignKeys": { + "distribution_boards_project_id_projects_id_fk": { + "name": "distribution_boards_project_id_projects_id_fk", + "tableFrom": "distribution_boards", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "distribution_boards_floor_id_floors_id_fk": { + "name": "distribution_boards_floor_id_floors_id_fk", + "tableFrom": "distribution_boards", + "tableTo": "floors", + "columnsFrom": [ + "floor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "floors": { + "name": "floors", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "floors_project_id_projects_id_fk": { + "name": "floors_project_id_projects_id_fk", + "tableFrom": "floors", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "global_devices": { + "name": "global_devices", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "installed_power_per_unit_kw": { + "name": "installed_power_per_unit_kw", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "demand_factor": { + "name": "demand_factor", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "voltage_v": { + "name": "voltage_v", + "type": "real", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "phase_count": { + "name": "phase_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "power_factor": { + "name": "power_factor", + "type": "real", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "legacy_consumer_circuit_migrations": { + "name": "legacy_consumer_circuit_migrations", + "columns": { + "consumer_id": { + "name": "consumer_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "circuit_id": { + "name": "circuit_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "circuit_device_row_id": { + "name": "circuit_device_row_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "circuit_list_id": { + "name": "circuit_list_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at_iso": { + "name": "created_at_iso", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "legacy_consumer_circuit_migrations_circuit_id_circuits_id_fk": { + "name": "legacy_consumer_circuit_migrations_circuit_id_circuits_id_fk", + "tableFrom": "legacy_consumer_circuit_migrations", + "tableTo": "circuits", + "columnsFrom": [ + "circuit_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "legacy_consumer_circuit_migrations_circuit_device_row_id_circuit_device_rows_id_fk": { + "name": "legacy_consumer_circuit_migrations_circuit_device_row_id_circuit_device_rows_id_fk", + "tableFrom": "legacy_consumer_circuit_migrations", + "tableTo": "circuit_device_rows", + "columnsFrom": [ + "circuit_device_row_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "legacy_consumer_circuit_migrations_circuit_list_id_circuit_lists_id_fk": { + "name": "legacy_consumer_circuit_migrations_circuit_list_id_circuit_lists_id_fk", + "tableFrom": "legacy_consumer_circuit_migrations", + "tableTo": "circuit_lists", + "columnsFrom": [ + "circuit_list_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "legacy_consumer_migration_reports": { + "name": "legacy_consumer_migration_reports", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "circuit_list_id": { + "name": "circuit_list_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "legacy_consumer_count": { + "name": "legacy_consumer_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_circuit_count": { + "name": "created_circuit_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_device_row_count": { + "name": "created_device_row_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "duplicate_grouped_count": { + "name": "duplicate_grouped_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "generated_identifier_count": { + "name": "generated_identifier_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "unassigned_row_count": { + "name": "unassigned_row_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "warnings_json": { + "name": "warnings_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "generated_identifiers_json": { + "name": "generated_identifiers_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "duplicate_groups_json": { + "name": "duplicate_groups_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at_iso": { + "name": "created_at_iso", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "legacy_consumer_migration_reports_list_unique": { + "name": "legacy_consumer_migration_reports_list_unique", + "columns": [ + "circuit_list_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "legacy_consumer_migration_reports_circuit_list_id_circuit_lists_id_fk": { + "name": "legacy_consumer_migration_reports_circuit_list_id_circuit_lists_id_fk", + "tableFrom": "legacy_consumer_migration_reports", + "tableTo": "circuit_lists", + "columnsFrom": [ + "circuit_list_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "project_change_sets": { + "name": "project_change_sets", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_revision_id": { + "name": "project_revision_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "command_type": { + "name": "command_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "payload_schema_version": { + "name": "payload_schema_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "forward_payload_json": { + "name": "forward_payload_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inverse_payload_json": { + "name": "inverse_payload_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "project_change_sets_revision_unique": { + "name": "project_change_sets_revision_unique", + "columns": [ + "project_revision_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "project_change_sets_project_revision_id_project_revisions_id_fk": { + "name": "project_change_sets_project_revision_id_project_revisions_id_fk", + "tableFrom": "project_change_sets", + "tableTo": "project_revisions", + "columnsFrom": [ + "project_revision_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "project_devices": { + "name": "project_devices", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "phase_type": { + "name": "phase_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'single_phase'" + }, + "connection_kind": { + "name": "connection_kind", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cost_group": { + "name": "cost_group", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "power_per_unit": { + "name": "power_per_unit", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "simultaneity_factor": { + "name": "simultaneity_factor", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "cos_phi": { + "name": "cos_phi", + "type": "real", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "remark": { + "name": "remark", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "voltage_v": { + "name": "voltage_v", + "type": "real", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "project_devices_project_id_projects_id_fk": { + "name": "project_devices_project_id_projects_id_fk", + "tableFrom": "project_devices", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "project_history_stack_entries": { + "name": "project_history_stack_entries", + "columns": { + "change_set_id": { + "name": "change_set_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "stack": { + "name": "stack", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "position": { + "name": "position", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "project_history_stack_entries_position_unique": { + "name": "project_history_stack_entries_position_unique", + "columns": [ + "project_id", + "stack", + "position" + ], + "isUnique": true + } + }, + "foreignKeys": { + "project_history_stack_entries_change_set_id_project_change_sets_id_fk": { + "name": "project_history_stack_entries_change_set_id_project_change_sets_id_fk", + "tableFrom": "project_history_stack_entries", + "tableTo": "project_change_sets", + "columnsFrom": [ + "change_set_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "project_history_stack_entries_project_id_projects_id_fk": { + "name": "project_history_stack_entries_project_id_projects_id_fk", + "tableFrom": "project_history_stack_entries", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": { + "project_history_stack_entries_stack_check": { + "name": "project_history_stack_entries_stack_check", + "value": "\"project_history_stack_entries\".\"stack\" in ('undo', 'redo')" + } + } + }, + "project_revisions": { + "name": "project_revisions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "revision_number": { + "name": "revision_number", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at_iso": { + "name": "created_at_iso", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "project_revisions_project_number_unique": { + "name": "project_revisions_project_number_unique", + "columns": [ + "project_id", + "revision_number" + ], + "isUnique": true + } + }, + "foreignKeys": { + "project_revisions_project_id_projects_id_fk": { + "name": "project_revisions_project_id_projects_id_fk", + "tableFrom": "project_revisions", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "project_snapshots": { + "name": "project_snapshots", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_revision": { + "name": "source_revision", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "schema_version": { + "name": "schema_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'named'" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "payload_json": { + "name": "payload_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "payload_sha256": { + "name": "payload_sha256", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at_iso": { + "name": "created_at_iso", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "project_snapshots_project_created_idx": { + "name": "project_snapshots_project_created_idx", + "columns": [ + "project_id", + "created_at_iso" + ], + "isUnique": false + }, + "project_snapshots_project_kind_revision_idx": { + "name": "project_snapshots_project_kind_revision_idx", + "columns": [ + "project_id", + "kind", + "source_revision" + ], + "isUnique": false + }, + "project_snapshots_project_name_unique": { + "name": "project_snapshots_project_name_unique", + "columns": [ + "project_id", + "name" + ], + "isUnique": true + } + }, + "foreignKeys": { + "project_snapshots_project_id_projects_id_fk": { + "name": "project_snapshots_project_id_projects_id_fk", + "tableFrom": "project_snapshots", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "projects": { + "name": "projects", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "internal_project_number": { + "name": "internal_project_number", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "external_project_number": { + "name": "external_project_number", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "building_owner": { + "name": "building_owner", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "single_phase_voltage_v": { + "name": "single_phase_voltage_v", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 230 + }, + "three_phase_voltage_v": { + "name": "three_phase_voltage_v", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 400 + }, + "enabled_distribution_board_supply_types": { + "name": "enabled_distribution_board_supply_types", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[\"AV\",\"SV\",\"EV\",\"USV\",\"MSR\",\"SiBe\"]'" + }, + "current_revision": { + "name": "current_revision", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "rooms": { + "name": "rooms", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "floor_id": { + "name": "floor_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "room_number": { + "name": "room_number", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "room_name": { + "name": "room_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "rooms_project_id_projects_id_fk": { + "name": "rooms_project_id_projects_id_fk", + "tableFrom": "rooms", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "rooms_floor_id_floors_id_fk": { + "name": "rooms_floor_id_floors_id_fk", + "tableFrom": "rooms", + "tableTo": "floors", + "columnsFrom": [ + "floor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/src/db/migrations/meta/_journal.json b/src/db/migrations/meta/_journal.json index e1d46a0..6825610 100644 --- a/src/db/migrations/meta/_journal.json +++ b/src/db/migrations/meta/_journal.json @@ -162,6 +162,13 @@ "when": 1785314626476, "tag": "0022_default_remaining_phase_types", "breakpoints": true + }, + { + "idx": 23, + "version": "6", + "when": 1785343645615, + "tag": "0023_cheerful_night_thrasher", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/db/repositories/distribution-board-structure-project-command.repository.ts b/src/db/repositories/distribution-board-structure-project-command.repository.ts index 8ef61df..7aab73e 100644 --- a/src/db/repositories/distribution-board-structure-project-command.repository.ts +++ b/src/db/repositories/distribution-board-structure-project-command.repository.ts @@ -14,6 +14,7 @@ import { import { assertDistributionBoardUpdateProjectCommand, createDistributionBoardUpdateProjectCommand, + legacyDistributionBoardUpdateCommandSchemaVersion, type DistributionBoardUpdateField, type DistributionBoardUpdatePatch, type DistributionBoardUpdateProjectCommand, @@ -303,10 +304,19 @@ export class DistributionBoardStructureProjectCommandRepository getDistributionBoardFieldValue(current, change.field), ]) ) as DistributionBoardUpdatePatch; - const inverse = createDistributionBoardUpdateProjectCommand( + const currentInverse = createDistributionBoardUpdateProjectCommand( current.id, inversePatch ); + const inverse = + input.command.schemaVersion === + legacyDistributionBoardUpdateCommandSchemaVersion + ? ({ + ...currentInverse, + schemaVersion: + legacyDistributionBoardUpdateCommandSchemaVersion, + } as DistributionBoardUpdateProjectCommand) + : currentInverse; const updated = database .update(distributionBoards) .set(patch) @@ -379,8 +389,16 @@ function structureMatches( sections: Array; } ) { + const { + simultaneityFactor, + ...actualDistributionBoard + } = actual.board; if ( - !sameRecord(expected.distributionBoard, actual.board) || + simultaneityFactor !== 1 || + !sameRecord( + expected.distributionBoard, + actualDistributionBoard + ) || !sameRecord(expected.circuitList, actual.list) || expected.sections.length !== actual.sections.length ) { diff --git a/src/db/repositories/distribution-board.repository.ts b/src/db/repositories/distribution-board.repository.ts index aaa2cc7..d18bd4f 100644 --- a/src/db/repositories/distribution-board.repository.ts +++ b/src/db/repositories/distribution-board.repository.ts @@ -1,4 +1,4 @@ -import { eq } from "drizzle-orm"; +import { and, eq } from "drizzle-orm"; import type { AppDatabase } from "../database-context.js"; import { distributionBoards } from "../schema/distribution-boards.js"; @@ -12,4 +12,19 @@ export class DistributionBoardRepository { .where(eq(distributionBoards.projectId, projectId)); } + async findById(projectId: string, distributionBoardId: string) { + return ( + this.database + .select() + .from(distributionBoards) + .where( + and( + eq(distributionBoards.projectId, projectId), + eq(distributionBoards.id, distributionBoardId) + ) + ) + .get() ?? null + ); + } + } diff --git a/src/db/repositories/project-snapshot.repository.ts b/src/db/repositories/project-snapshot.repository.ts index 17b989d..c5051aa 100644 --- a/src/db/repositories/project-snapshot.repository.ts +++ b/src/db/repositories/project-snapshot.repository.ts @@ -10,6 +10,7 @@ import { previousProjectStateSnapshotSchemaVersion, projectStateSnapshotSchemaVersion, supplyTypesProjectStateSnapshotSchemaVersion, + voltageProjectStateSnapshotSchemaVersion, } from "../../domain/models/project-state-snapshot.model.js"; import type { CreateNamedProjectSnapshotInput, @@ -149,6 +150,8 @@ export class ProjectSnapshotRepository implements ProjectSnapshotStore { distributionBoardProjectStateSnapshotSchemaVersion && stored.schemaVersion !== supplyTypesProjectStateSnapshotSchemaVersion && + stored.schemaVersion !== + voltageProjectStateSnapshotSchemaVersion && stored.schemaVersion !== projectStateSnapshotSchemaVersion ) { throw new Error("Project snapshot schema version is not supported."); diff --git a/src/db/schema/distribution-boards.ts b/src/db/schema/distribution-boards.ts index f4895c4..06fdaf6 100644 --- a/src/db/schema/distribution-boards.ts +++ b/src/db/schema/distribution-boards.ts @@ -1,4 +1,4 @@ -import { sqliteTable, text } from "drizzle-orm/sqlite-core"; +import { real, sqliteTable, text } from "drizzle-orm/sqlite-core"; import { floors } from "./floors.js"; import { projects } from "./projects.js"; import type { DistributionBoardSupplyType } from "../../shared/constants/distribution-board.js"; @@ -13,5 +13,8 @@ export const distributionBoards = sqliteTable("distribution_boards", { onDelete: "set null", }), supplyType: text("supply_type").$type(), + simultaneityFactor: real("simultaneity_factor") + .notNull() + .default(1), }); diff --git a/src/domain/calculations/circuit-power-calculation.ts b/src/domain/calculations/circuit-power-calculation.ts index 0e00199..09d7f65 100644 --- a/src/domain/calculations/circuit-power-calculation.ts +++ b/src/domain/calculations/circuit-power-calculation.ts @@ -15,3 +15,37 @@ export function calculateCircuitTotalPower( ); } +export function calculateSectionTotalPower( + circuits: Array<{ circuitTotalPower: number }> +): number { + return circuits.reduce( + (sum, circuit) => sum + circuit.circuitTotalPower, + 0 + ); +} + +export function calculateDistributionBoardTotalPower( + sections: Array<{ sectionTotalPower: number }> +): number { + return sections.reduce( + (sum, section) => sum + section.sectionTotalPower, + 0 + ); +} + +export function applyDistributionBoardSimultaneityFactor( + totalPower: number, + simultaneityFactor: number +): number { + if ( + !Number.isFinite(simultaneityFactor) || + simultaneityFactor < 0 || + simultaneityFactor > 1 + ) { + throw new Error( + "Distribution-board simultaneity factor must be between zero and one." + ); + } + return totalPower * simultaneityFactor; +} + diff --git a/src/domain/models/circuit-tree.model.ts b/src/domain/models/circuit-tree.model.ts index d476c51..7e0449f 100644 --- a/src/domain/models/circuit-tree.model.ts +++ b/src/domain/models/circuit-tree.model.ts @@ -52,6 +52,7 @@ export interface CircuitTreeSectionBlock { displayName: string; prefix: string; sortOrder: number; + sectionTotalPower: number; circuits: CircuitTreeCircuit[]; } @@ -60,6 +61,9 @@ export interface CircuitTreeResponse { currentRevision: number; singlePhaseVoltageV: number; threePhaseVoltageV: number; + distributionBoardSimultaneityFactor: number; + distributionBoardTotalPower: number; + distributionBoardTotalPowerWithSimultaneityFactor: number; sections: CircuitTreeSectionBlock[]; } diff --git a/src/domain/models/distribution-board-project-command.model.ts b/src/domain/models/distribution-board-project-command.model.ts index a591488..8f6d4fc 100644 --- a/src/domain/models/distribution-board-project-command.model.ts +++ b/src/domain/models/distribution-board-project-command.model.ts @@ -6,11 +6,13 @@ import type { SerializedProjectCommand } from "./project-command.model.js"; export const distributionBoardUpdateCommandType = "distribution-board.update" as const; -export const distributionBoardUpdateCommandSchemaVersion = 1 as const; +export const legacyDistributionBoardUpdateCommandSchemaVersion = 1 as const; +export const distributionBoardUpdateCommandSchemaVersion = 2 as const; export interface DistributionBoardUpdateValues { floorId: string | null; supplyType: DistributionBoardSupplyType | null; + simultaneityFactor: number; } export type DistributionBoardUpdateField = @@ -31,17 +33,27 @@ export interface DistributionBoardUpdateCommandPayload { changes: DistributionBoardUpdateFieldChange[]; } -export interface DistributionBoardUpdateProjectCommand +interface CurrentDistributionBoardUpdateProjectCommand extends SerializedProjectCommand { schemaVersion: typeof distributionBoardUpdateCommandSchemaVersion; type: typeof distributionBoardUpdateCommandType; } +interface LegacyDistributionBoardUpdateProjectCommand + extends SerializedProjectCommand { + schemaVersion: typeof legacyDistributionBoardUpdateCommandSchemaVersion; + type: typeof distributionBoardUpdateCommandType; +} + +export type DistributionBoardUpdateProjectCommand = + | CurrentDistributionBoardUpdateProjectCommand + | LegacyDistributionBoardUpdateProjectCommand; + export function createDistributionBoardUpdateProjectCommand( distributionBoardId: string, patch: DistributionBoardUpdatePatch -): DistributionBoardUpdateProjectCommand { - const command: DistributionBoardUpdateProjectCommand = { +): CurrentDistributionBoardUpdateProjectCommand { + const command: CurrentDistributionBoardUpdateProjectCommand = { schemaVersion: distributionBoardUpdateCommandSchemaVersion, type: distributionBoardUpdateCommandType, payload: { @@ -60,7 +72,9 @@ export function assertDistributionBoardUpdateProjectCommand( command: SerializedProjectCommand ): asserts command is DistributionBoardUpdateProjectCommand { if ( - command.schemaVersion !== distributionBoardUpdateCommandSchemaVersion || + (command.schemaVersion !== + legacyDistributionBoardUpdateCommandSchemaVersion && + command.schemaVersion !== distributionBoardUpdateCommandSchemaVersion) || command.type !== distributionBoardUpdateCommandType || !isPlainObject(command.payload) ) { @@ -81,7 +95,11 @@ export function assertDistributionBoardUpdateProjectCommand( for (const change of changes) { if ( !isPlainObject(change) || - (change.field !== "floorId" && change.field !== "supplyType") || + (change.field !== "floorId" && + change.field !== "supplyType" && + (command.schemaVersion === + legacyDistributionBoardUpdateCommandSchemaVersion || + change.field !== "simultaneityFactor")) || seen.has(change.field) ) { throw new Error( @@ -95,13 +113,24 @@ export function assertDistributionBoardUpdateProjectCommand( ) { throw new Error("floorId must be a non-empty string or null."); } + } else if (change.field === "supplyType") { + if ( + change.value !== null && + !distributionBoardSupplyTypes.includes( + change.value as DistributionBoardSupplyType + ) + ) { + throw new Error("supplyType is invalid."); + } } else if ( - change.value !== null && - !distributionBoardSupplyTypes.includes( - change.value as DistributionBoardSupplyType - ) + typeof change.value !== "number" || + !Number.isFinite(change.value) || + change.value < 0 || + change.value > 1 ) { - throw new Error("supplyType is invalid."); + throw new Error( + "simultaneityFactor must be between zero and one." + ); } seen.add(change.field); } diff --git a/src/domain/models/project-state-snapshot.model.ts b/src/domain/models/project-state-snapshot.model.ts index 3bb6239..3a6bef3 100644 --- a/src/domain/models/project-state-snapshot.model.ts +++ b/src/domain/models/project-state-snapshot.model.ts @@ -13,7 +13,8 @@ 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 projectStateSnapshotSchemaVersion = 5 as const; +export const voltageProjectStateSnapshotSchemaVersion = 5 as const; +export const projectStateSnapshotSchemaVersion = 6 as const; const idSchema = z.string().trim().min(1); const nullableStringSchema = z.string().nullable(); @@ -57,6 +58,12 @@ const distributionBoardSchema = legacyDistributionBoardSchema }) .strict(); +const currentDistributionBoardSchema = distributionBoardSchema + .extend({ + simultaneityFactor: finiteNumberSchema.min(0).max(1), + }) + .strict(); + const circuitListSchema = z .object({ id: idSchema, @@ -213,9 +220,17 @@ const supplyTypesProjectStateSnapshotSchema = z }) .strict(); -export const projectStateSnapshotSchema = +const voltageProjectStateSnapshotSchema = supplyTypesProjectStateSnapshotSchema.extend({ + schemaVersion: z.literal( + voltageProjectStateSnapshotSchemaVersion + ), + }); + +export const projectStateSnapshotSchema = + voltageProjectStateSnapshotSchema.extend({ schemaVersion: z.literal(projectStateSnapshotSchemaVersion), + distributionBoards: z.array(currentDistributionBoardSchema), }); export type ProjectStateSnapshot = z.infer< @@ -228,34 +243,46 @@ export function parseProjectStateSnapshot( const version = isPlainObject(value) ? value.schemaVersion : undefined; const parsedSnapshot = version === legacyProjectStateSnapshotSchemaVersion - ? upgradeSupplyTypesProjectStateSnapshot( - upgradeDistributionBoardProjectStateSnapshot( - upgradePreviousProjectStateSnapshot( - upgradeLegacyProjectStateSnapshot( - legacyProjectStateSnapshotSchema.parse(value) + ? upgradeVoltageProjectStateSnapshot( + upgradeSupplyTypesProjectStateSnapshot( + upgradeDistributionBoardProjectStateSnapshot( + upgradePreviousProjectStateSnapshot( + upgradeLegacyProjectStateSnapshot( + legacyProjectStateSnapshotSchema.parse(value) + ) ) ) ) ) : version === previousProjectStateSnapshotSchemaVersion - ? upgradeSupplyTypesProjectStateSnapshot( - upgradeDistributionBoardProjectStateSnapshot( - upgradePreviousProjectStateSnapshot( - previousProjectStateSnapshotSchema.parse(value) + ? upgradeVoltageProjectStateSnapshot( + upgradeSupplyTypesProjectStateSnapshot( + upgradeDistributionBoardProjectStateSnapshot( + upgradePreviousProjectStateSnapshot( + previousProjectStateSnapshotSchema.parse(value) + ) ) ) ) : version === distributionBoardProjectStateSnapshotSchemaVersion - ? upgradeSupplyTypesProjectStateSnapshot( - upgradeDistributionBoardProjectStateSnapshot( - distributionBoardProjectStateSnapshotSchema.parse(value) + ? upgradeVoltageProjectStateSnapshot( + upgradeSupplyTypesProjectStateSnapshot( + upgradeDistributionBoardProjectStateSnapshot( + distributionBoardProjectStateSnapshotSchema.parse(value) + ) ) ) : version === supplyTypesProjectStateSnapshotSchemaVersion - ? upgradeSupplyTypesProjectStateSnapshot( - supplyTypesProjectStateSnapshotSchema.parse(value) + ? upgradeVoltageProjectStateSnapshot( + upgradeSupplyTypesProjectStateSnapshot( + supplyTypesProjectStateSnapshotSchema.parse(value) + ) ) - : projectStateSnapshotSchema.parse(value); + : version === voltageProjectStateSnapshotSchemaVersion + ? upgradeVoltageProjectStateSnapshot( + voltageProjectStateSnapshotSchema.parse(value) + ) + : projectStateSnapshotSchema.parse(value); const snapshot = normalizeSnapshotPhaseTypes(parsedSnapshot); assertProjectStateSnapshotRelations(snapshot); return snapshot; @@ -354,14 +381,14 @@ function upgradeDistributionBoardProjectStateSnapshot( function upgradeSupplyTypesProjectStateSnapshot( snapshot: z.infer -): ProjectStateSnapshot { +): z.infer { const settings = snapshot.project; const sectionById = new Map( snapshot.circuitSections.map((section) => [section.id, section]) ); return { ...snapshot, - schemaVersion: projectStateSnapshotSchemaVersion, + schemaVersion: voltageProjectStateSnapshotSchemaVersion, projectDevices: snapshot.projectDevices.map((device) => ({ ...device, voltageV: resolveProjectVoltage(device.phaseType, settings), @@ -383,6 +410,19 @@ function upgradeSupplyTypesProjectStateSnapshot( }; } +function upgradeVoltageProjectStateSnapshot( + snapshot: z.infer +): ProjectStateSnapshot { + return { + ...snapshot, + schemaVersion: projectStateSnapshotSchemaVersion, + distributionBoards: snapshot.distributionBoards.map((board) => ({ + ...board, + simultaneityFactor: 1, + })), + }; +} + function isPlainObject(value: unknown): value is Record { return value !== null && typeof value === "object" && !Array.isArray(value); } diff --git a/src/frontend/components/circuit-tree-editor.tsx b/src/frontend/components/circuit-tree-editor.tsx index dea6ddd..469e4b2 100644 --- a/src/frontend/components/circuit-tree-editor.tsx +++ b/src/frontend/components/circuit-tree-editor.tsx @@ -27,7 +27,11 @@ import { deviceFieldKeys, formatPhaseTypeLabel, formatValue, + getCircuitSectionLabel, + getProjectColumnLayoutStorageKey, isGridEditorControlTarget, + normalizeColumnOrder, + parseStoredColumnLayout, } from "../utils/circuit-grid-model"; import { buildCircuitDeviceRowInsertSnapshot, @@ -131,7 +135,8 @@ type CircuitReorderDropIntent = | { kind: "after-circuit"; sectionId: string; targetCircuitId: string; valid: boolean } | { kind: "section-end"; sectionId: string; valid: boolean }; -const COLUMN_LAYOUT_STORAGE_KEY = "circuitTreeEditor.columnLayout.v1"; +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."; @@ -222,6 +227,10 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str const [filterValueSearch, setFilterValueSearch] = useState(""); const [visibleColumnKeys, setVisibleColumnKeys] = useState(defaultVisibleColumnKeys); const [columnOrder, setColumnOrder] = useState(allColumns.map((column) => column.key)); + const [ + loadedColumnLayoutProjectId, + setLoadedColumnLayoutProjectId, + ] = useState(null); const [isColumnMenuOpen, setIsColumnMenuOpen] = useState(false); const [columnSearch, setColumnSearch] = useState(""); const [draggingColumnKey, setDraggingColumnKey] = useState(null); @@ -267,13 +276,6 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str } } - function normalizeColumnOrder(keys: CellKey[]) { - const unique = [...new Set(keys)]; - const allKeys = allColumns.map((column) => column.key); - const merged = [...unique, ...allKeys.filter((key) => !unique.includes(key))]; - return ["equipmentIdentifier" as CellKey, ...merged.filter((key) => key !== "equipmentIdentifier")]; - } - // Initial/identity-change tree load for current route context. useEffect(() => { void loadTree({ showLoading: true }); @@ -292,32 +294,48 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str }, [projectId]); useEffect(() => { + setLoadedColumnLayoutProjectId(null); + setColumnOrder(allColumns.map((column) => column.key)); + setVisibleColumnKeys(defaultVisibleColumnKeys); try { - const raw = localStorage.getItem(COLUMN_LAYOUT_STORAGE_KEY); - if (!raw) { + const parsed = parseStoredColumnLayout( + localStorage.getItem( + getProjectColumnLayoutStorageKey(projectId) + ) ?? + localStorage.getItem( + LEGACY_COLUMN_LAYOUT_STORAGE_KEY + ) + ); + if (!parsed) { return; } - const parsed = JSON.parse(raw) as { order?: string[]; visible?: string[] }; - const validKeys = new Set(allColumns.map((column) => column.key)); - const parsedOrder = (parsed.order ?? []).filter((key): key is CellKey => validKeys.has(key as CellKey)); - const parsedVisible = (parsed.visible ?? []).filter((key): key is CellKey => validKeys.has(key as CellKey)); - if (!parsedOrder.length || !parsedVisible.length || !parsedVisible.includes("equipmentIdentifier")) { - return; - } - setColumnOrder(normalizeColumnOrder(parsedOrder)); - setVisibleColumnKeys(parsedVisible); + setColumnOrder(parsed.order); + setVisibleColumnKeys(parsed.visible); } catch { // ignore invalid local storage and use defaults + } finally { + setLoadedColumnLayoutProjectId(projectId); } - }, []); + }, [projectId]); useEffect(() => { + if (loadedColumnLayoutProjectId !== projectId) { + return; + } const payload = { order: columnOrder, visible: visibleColumnKeys, }; - localStorage.setItem(COLUMN_LAYOUT_STORAGE_KEY, JSON.stringify(payload)); - }, [columnOrder, visibleColumnKeys]); + localStorage.setItem( + getProjectColumnLayoutStorageKey(projectId), + JSON.stringify(payload) + ); + }, [ + columnOrder, + loadedColumnLayoutProjectId, + projectId, + visibleColumnKeys, + ]); useEffect(() => { const visible = new Set(visibleColumnKeys); @@ -474,6 +492,51 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str ); } + function renderDistributionPowerSummary() { + if (!data) { + return null; + } + return ( +
+
+ Gesamtleistung Verteiler + + {formatValue( + data.distributionBoardTotalPower, + "rowTotalPower" + )}{" "} + kW + +
+
+ Verteilerweiter Gleichzeitigkeitsfaktor + + {formatValue( + data.distributionBoardSimultaneityFactor, + "simultaneityFactor" + )} + +
+
+ + Gesamtleistung Verteiler unter Berücksichtigung des + Gleichzeitigkeitsfaktors + + + {formatValue( + data.distributionBoardTotalPowerWithSimultaneityFactor, + "rowTotalPower" + )}{" "} + kW + +
+
+ ); + } + function closeColumnSettingsMenu() { setIsColumnMenuOpen(false); setColumnSearch(""); @@ -2490,6 +2553,7 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str {renderColumnSettingsMenu("col-empty")} {renderActiveViewSummary()} + {renderDistributionPowerSummary()} {error ? (
{error} @@ -2598,6 +2662,7 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str {renderColumnSettingsMenu("col")}
{renderActiveViewSummary()} + {renderDistributionPowerSummary()} {hasActiveSortOrFilter ? (
Sortierung und Filter verändern nur die Ansicht. Die Neunummerierung ist währenddessen deaktiviert.
) : null} @@ -2701,14 +2766,18 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str const valid = !selectedProjectDevice || isProjectDevicePlacementValid(selectedProjectDevice, section); return ( ); })} {selectedProjectDevice && suggestedSection ? ( -

Vorgeschlagener Bereich: {suggestedSection.displayName}

+

+ Vorgeschlagener Bereich:{" "} + {getCircuitSectionLabel(suggestedSection)} +

) : null}