528 lines
35 KiB
Markdown
528 lines
35 KiB
Markdown
# R0 – Audit der Linear-Excel-Anbindung
|
||
|
||
Stand: 29.09.2026. Geprüfter Code: `cf022b2`.
|
||
|
||
**Status: R0 abgeschlossen und mit Korrekturen für R1 freigegeben.** Dieser Bericht
|
||
ist ein Umsetzungsvorschlag, kein Nachweis einer bereits implementierten
|
||
Excel-Anbindung. Der neue Nutzerauftrag ersetzt die CSV-/IfcGUID-Vorgaben und
|
||
die alte Phasenliste 14.0–14.6. R1 bereinigt die bestehenden Dokumente; R0
|
||
ändert weder Laufzeitcode noch Datenbank noch Oberfläche.
|
||
|
||
Die 13 Freigabekorrekturen vom 29.09.2026 sind in den
|
||
[verbindlichen Anforderungen](revit-excel-integration-requirements.md) und im
|
||
[R0–R10-Plan](revit-excel-phase-14-plan.md) eingearbeitet. Bei Abweichungen
|
||
gelten diese beiden R1-Dokumente.
|
||
|
||
## 1. Geprüfter Bestand und Keep/Change/Remove
|
||
|
||
Gelesene Grundlagen: `AGENTS.md`, `docs/current-architecture.md`,
|
||
`docs/project-history-and-external-model-architecture.md`,
|
||
`docs/spec/revit-csv-integration-requirements.md` und
|
||
`docs/spec/revit-csv-phase-14-audit-and-plan.md`. Der alte Plan widerspricht
|
||
sich bereits intern: Sein Status nennt die Erstübernahme noch nicht vorhanden,
|
||
während Implementierung und Tests sie nachweisen. Alte Erledigt-Markierungen
|
||
werden deshalb nicht übernommen.
|
||
|
||
| Bereich / Codebeleg | Entscheidung | Begründung und Änderung |
|
||
| --- | --- | --- |
|
||
| `src/db/repositories/project-command-transaction.persistence.ts` | Keep | Eine Grenze für Fachänderung, Inverse, Revision und History; auch automatische Snapshots bleiben darin. |
|
||
| `src/db/repositories/external-model-state.repository.ts`, `src/db/schema/external-*.ts` | Change | Quelle, Batches, Raum-Mappings und Objekte wiederverwenden; neue Identität, Konfiguration und typisierte Matrix. Schlanke Objekt-Reads von großen Batch-Blobs trennen. |
|
||
| `src/domain/models/external-initial-import-project-command.model.ts`, zugehöriger Store | Change | Vollständiger erwarteter/Zielzustand und Row-freier Import bleiben; Worksharing-ID, Gerätearten und atomare Regelübernahme ergänzen. |
|
||
| `src/external-model/application/external-initial-import-plan.ts`, `external-initial-import-target.ts` | Change | Raumvorschläge, Kataloge, vollständige Entscheidungen und stabile UUIDs behalten. Objektentscheidungen und Gerätearten ergänzen. |
|
||
| `src/db/repositories/external-object-assignment.persistence.ts` und drei Zuordnungs-Stores | Change | Mengeninvariante und exakte Inversen behalten; Geräteart, effektiven Verteiler, Anwesenheit und Übernahme der Row-Werte beim Lösen ergänzen. |
|
||
| `CircuitDeviceRow.manualQuantity`, bestehende Mengenprüfungen | Keep | `quantity = manualQuantity + Summe(externer effektiver Mengen)`; Mehrfachsteckdosen bleiben unteilbar. |
|
||
| `src/external-model/application/external-circuit-list-objects.ts` | Change | Listen-/Projektprüfung behalten; vollständige Planungswerte, Geräteart, effektiver Verteiler, Status und Row-Anzahlen liefern. |
|
||
| `src/domain/models/project-state-snapshot.model.ts`, Snapshot-Persistenz und Transfer | Change | Schema 10; historische CSV-Zustände ausdrücklich ablehnen; alle neuen internen Referenzen remappen. |
|
||
| `src/external-model/csv/*`, CSV-Preview, CSV-Validierungen und Controller | Remove/replace | XLSX ersetzt CSV vollständig; kein zweiter aktiver Transportpfad. |
|
||
| `src/frontend/components/revit-csv-modal.tsx`, Einbindung auf Projektseite | Remove/replace | Eigene Revit-Seite; Einstellungen nur im Modal, Import in eigenem Wizard. |
|
||
| `src/frontend/components/Sidebar.tsx`, `src/frontend/utils/api.ts`, `src/frontend/types.ts` | Change | Route `revit`, neue DTOs und Endpunkte, CSV-Imports entfernen. |
|
||
| `src/frontend/components/circuit-tree-editor.tsx`, Grid-Drop-/Projektionsmodule | Change | Separater externer Drag-Payload; Row-, Circuit- und Freiplatz-Ziele bewusst unterscheiden. Bestehende Grid-Bedienung behalten. |
|
||
| Raum-/ProjectDevice-Formulare und Commands | Keep | Sichtbar vorbelegte, ausdrücklich bestätigte Anlage als eigener Undo-Schritt. |
|
||
| `scripts/verify-revit-reference-csv.ts`, Transporttests | Replace | Pfadargument für lokalen XLSX-Prüfer; ausschließlich synthetische Workbooks in Tests. |
|
||
|
||
### Konkrete Lücken gegenüber dem Auftrag
|
||
|
||
- `external-model-contracts.ts` importiert heute CSV-Verträge. Die neutralen
|
||
Konfigurations-/Matrixverträge gehören künftig in `external-model/domain`;
|
||
nur `external-model/xlsx` darf ExcelJS kennen.
|
||
- `ifc_guid` ist derzeit NOT NULL und je Quelle eindeutig; Matching,
|
||
Snapshots, DTOs und Commands setzen diese Identität voraus.
|
||
- `internalDeviceType` ist derzeit ein freier String in Planungswerten.
|
||
Es gibt keinen Geräteart-Katalog mit stabilen IDs und Standardwerten.
|
||
- Der Erstimport setzt den ProjectDevice-Link, verwendet im Target aber
|
||
weiterhin `suggestedPlanningValues`. Die neue Priorität ProjectDevice vor
|
||
Geräteart muss ausdrücklich implementiert werden; ein Link allein genügt
|
||
nicht. Revit-Leistung wird künftig nicht automatisch zum Planungswert.
|
||
- Der Import-Target setzt `distributionBoardId = null`; die Raumvorgabe liegt
|
||
separat im Mapping. Projektion und Zuordnungsprüfung vergleichen heute
|
||
direkt das Objektfeld. Eine zentrale Auflösung des effektiven Verteilers
|
||
fehlt und ist vor der Editor-Nutzung erforderlich.
|
||
- Die bisherige Row-Zuordnung darf laut Validator am Objekt ausschließlich
|
||
`circuitDeviceRowId` ändern; der Store schreibt auch nur dieses Feld.
|
||
Lösen mit aktuellen Row-Planungswerten benötigt eine neue Command-Version.
|
||
- Die Kompatibilitätsprüfung vergleicht Raum, Kategorie, Auswahlkenner und
|
||
Planungswerte, aber keine stabile Geräteart. Unterschiedliche Gerätearten
|
||
dürfen nicht versehentlich in dieselbe Row aggregiert werden.
|
||
- Rows speichern `powerPerUnit` in **kW**, externe Planung
|
||
`powerPerUnitW` in **W**. Eine zentrale bidirektionale Konvertierung ist
|
||
nötig; Exportleistung darf nicht um Faktor 1000 falsch sein.
|
||
- Externe FKs auf Row, ProjectDevice, Raum-Mapping und Verteiler verwenden
|
||
teilweise `ON DELETE SET NULL`. Bestehende allgemeine Löschpfade sichern
|
||
externe Objektübergänge nicht durchgängig in ihren Inversen. Auch
|
||
Unterbaum-/History-Löschungen müssen abgesichert werden.
|
||
- Ein Editor-Drawer, projektweite Objekttabelle, Exportnachweis und
|
||
ausführbarer Folgeimport fehlen. `follow_up` als vorhandener Enumwert ist
|
||
kein implementierter Folgeimport.
|
||
- Konfigurationsversionen steigen heute nur bei normalen Updates gegenüber
|
||
dem aktuellen Wert; Undo setzt alte Versionen zurück. Für eine tatsächlich
|
||
monotone Generation muss diese Semantik korrigiert werden.
|
||
- XLSX-Parsing ist asynchron. Das heutige erneute synchrone CSV-Parsing
|
||
innerhalb der SQLite-Transaktion darf nicht einfach durch `await` ersetzt
|
||
werden.
|
||
|
||
## 2. Prüfung der lokalen Musterdatei
|
||
|
||
`docs/spec/kbp-Stromkreis.xlsx` wurde ausschließlich lesend auf ZIP-/OOXML-
|
||
Ebene untersucht; keine Inhalte wurden hochgeladen oder in Fixtures kopiert.
|
||
Die Datei ist unversioniert und darf nicht committed werden.
|
||
|
||
| Befund | Ergebnis |
|
||
| --- | --- |
|
||
| Dateigröße / entpackte ZIP-Einträge | 56.765 / 337.740 Bytes |
|
||
| Arbeitsblätter / Blattnamenlänge | 1 / 31 Zeichen |
|
||
| Zellbereich | A1:O490 |
|
||
| Kopfzeile / Leerzeile / Objekte / Abschluss | 1 / 2 / 3–489 / 490 |
|
||
| Objektidentitäten | 487 syntaktisch passende Worksharing-IDs, keine Duplikate |
|
||
| Kopfzeilenkommentare | 14, zu den 14 Spalten nach der ID-Spalte |
|
||
| Fehlende Raumangaben | 67 Objekte ohne Raumnummer und Raumname |
|
||
| IfcGUID-Duplikate | Keine bei exaktem, case-sensitivem Vergleich |
|
||
| Formeln | Keine; synthetische Tests dafür erforderlich |
|
||
| Spaltendefinitionen | 16, obwohl nur 15 Datenspalten vorhanden sind |
|
||
|
||
Das beobachtete Profil aus dem Auftrag wird damit bestätigt. Zusätzliche
|
||
Spaltenformatdefinitionen dürfen beim Export nicht auf die gemappten Spalten
|
||
gekürzt werden. Das Muster enthält die neuen getrennten Stromkreisfelder und
|
||
die Bezeichnung noch nicht. Fehlende Rückgabespalten werden angezeigt; sie
|
||
dürfen nicht ohne Linear-Parameterbindung erfunden werden.
|
||
|
||
Die frühere Angabe von zwei IfcGUID-Duplikatgruppen war falsch: PowerShells
|
||
standardmäßiges Gruppieren verglich ohne Beachtung der Groß-/Kleinschreibung.
|
||
Ein erneuter ordinaler, case-sensitiver Vergleich ergab keine Duplikate. Die
|
||
Summenzeile hat nur in B490 einen Wert; `Familie und Typ` ist leer.
|
||
|
||
Nicht nachgewiesen: erfolgreicher Rückimport in Linear, dessen genaue
|
||
Auswertung der Kommentar-/VML-Metadaten und der ExcelJS-Roundtrip. R2 endet
|
||
deshalb mit einer aus der Musterdatei erzeugten Rückgabedatei und einem
|
||
verbindlichen Linear-Test durch den Nutzer vor R3.
|
||
|
||
## 3. Bibliothek, Adapter und Uploadgrenzen
|
||
|
||
**Vorschlag: genau eine neue direkte Abhängigkeit `exceljs`.** Seine
|
||
öffentliche API unterstützt XLSX, Zellnotizen, Formate, Spaltenbreiten und
|
||
typisierte Werte. Das passt zum beobachteten klassischen Kommentarformat.
|
||
Version und transitive Abhängigkeiten werden in R2 festgeschrieben und
|
||
geprüft. Die veröffentlichte Version ist laut Paketregister 4.4.0; eine
|
||
aktuelle Releasepflege ist damit nicht bewiesen. Dies ist ein ausdrückliches
|
||
Wartungsrisiko, kein als erfüllt abgehakter Bibliotheksnachweis.
|
||
|
||
Quellen, am 29.09.2026 geprüft:
|
||
[ExcelJS-Projekt](https://github.com/exceljs/exceljs),
|
||
[offizielle Paketdokumentation](https://www.npmjs.com/package/exceljs).
|
||
Keine Bibliothek wird in R0 installiert. R2 muss vor Integration Kommentar-
|
||
Roundtrip, Node-24-Verträglichkeit und Abhängigkeitsrisiken prüfen; bei einem
|
||
Blocker wird die Bibliotheksentscheidung neu vorgelegt.
|
||
|
||
Vorgeschlagene Grenzen, zentral konfiguriert und synthetisch getestet:
|
||
|
||
- ausschließlich `.xlsx`, kein `.xls` oder `.xlsm`;
|
||
- maximal 10 MiB Upload;
|
||
- genau ein Arbeitsblatt und keine Formelausführung;
|
||
- feste Zeilen-, Spalten- und Zellgrenzen nach dem Einlesen.
|
||
|
||
Es gibt keinen eigenen ZIP-Prüfer und keinen Parser-Worker.
|
||
|
||
Neutrale Matrix: Blattname, Kopfzeilen-/Startpositionen, Spaltenmetadaten
|
||
(Index, Name, Kommentartext, erkannter Parametertyp/Einheit), geordnete
|
||
Zeilen mit Originalzeilennummer und Klasse sowie Zellen als diskriminierte
|
||
Union `string | number | boolean | empty`. Formeltext und Cache-Status sind
|
||
optionale Transportmetadaten; fachlich zählt nur der gecachte skalare Wert.
|
||
Fehlender Cache oder Fehlerwert erzeugt einen Hinweis bzw. bei Pflichtfeldern
|
||
einen Blocker. Originalbytes bleiben unverändert gespeichert.
|
||
|
||
Eine Zeile ohne Worksharing-ID ist nur verdächtig, wenn `Familie und Typ`
|
||
gefüllt ist. Sie erzeugt einen Hinweis und wird übersprungen, ohne zu
|
||
blockieren. Andere ID-lose Zeilen, auch die Summenzeile mit Inhalt in B,
|
||
werden still übersprungen. Nicht passende nichtleere IDs blockieren.
|
||
Worksharing-ID und IfcGUID bleiben als Quelltext erhalten und werden exakt,
|
||
ordinal und case-sensitiv verglichen; ungültige äußere Leerzeichen werden
|
||
gemeldet statt still korrigiert. IfcGUID wird optional gespeichert, doppelte
|
||
Werte sind nur Hinweise.
|
||
Numerische Projektion rundet Leistung auf 0,01 W, nicht die gespeicherte
|
||
Quellzelle. Mengen aus `socketCount` müssen positive ganze Zahlen sein.
|
||
|
||
Rückgabe lädt eine Kopie der Originalbytes, bewahrt den Bereich oberhalb der
|
||
ersten Datenzeile samt Kommentaren, Formatierung und Spaltenbreiten und
|
||
ersetzt den Datenbereich durch gewählte Objektzeilen in Originalreihenfolge.
|
||
Alle Spalten bleiben enthalten; Summenzeilen entfallen. Unveränderte Zellen
|
||
behalten ihre importierten Werte und Typen. Formeln in Objektzeilen werden
|
||
als gecachte Werte ausgegeben, damit Zeilenauswahl keine falschen relativen
|
||
Bezüge erzeugt; dies wird vor Export als Hinweis ausgewiesen. Keine Formeln
|
||
werden berechnet. Roundtrip bedeutet strukturelle/semantische Gleichheit,
|
||
nicht identische ZIP-Bytes.
|
||
|
||
## 4. Konfigurationsschema
|
||
|
||
Neuer neutraler Vertrag `ExternalModelConfiguration`, Schema-Version 1:
|
||
|
||
```ts
|
||
{
|
||
schemaVersion: 1,
|
||
layout: {
|
||
headerRow: 1, firstDataRow: 3, firstDataColumn: 1
|
||
},
|
||
circuitMode: "combined" | "separate",
|
||
columns: {
|
||
worksharingId: string, familyAndType: string,
|
||
ifcGuid: string | null, roomNumber: string | null,
|
||
roomName: string | null, selectionMarker: string | null,
|
||
socketCount: string | null, sourceRemark: string | null,
|
||
circuitCombined: string | null, boardName: string | null,
|
||
circuitIdentifier: string | null, designation: string | null,
|
||
electricalPowerW: string | null, voltageV: string | null,
|
||
currentA: string | null
|
||
},
|
||
deviceTypes: [{
|
||
id: UUID, name: string,
|
||
category: "lighting" | "single_phase" | "three_phase",
|
||
defaults: {
|
||
powerPerUnitW: number | null, simultaneityFactor: number | null,
|
||
cosPhi: number | null, costGroup: string | null
|
||
}
|
||
}],
|
||
familyTypeRules: [{
|
||
id: UUID, exactFamilyAndType: string, deviceTypeId: UUID,
|
||
quantityRule: "one" | "socketCount"
|
||
}]
|
||
}
|
||
```
|
||
|
||
Zeilen/Spalten sind intern 1-basiert; UI stellt Spalten als A, B, … dar.
|
||
Es gibt nur `columns.worksharingId` und kein separates `layout.idColumn`.
|
||
Jede gemappte Kopfzeile muss eindeutig auflösbar sein;
|
||
doppelte Ausgabebelegungen werden abgelehnt. Freie Eingabe bleibt zulässig,
|
||
aber ein nicht existierender Name erzeugt vor Import/Export einen konkreten
|
||
Fehler. Erkennung und Vorbelegung nutzen die exakten Standardnamen aus dem
|
||
Auftrag. Unbekannte Spalten bleiben ohne Mapping erhalten.
|
||
|
||
Geräteart-IDs sind interne UUIDs, keine Namen als Relationsschlüssel. Regeln
|
||
sind exakt und je Familien-/Typtext eindeutig; jede importierte Familie muss
|
||
eine Geräteart haben. Gerätearten und Regeln werden als ein versioniertes
|
||
Konfigurationsaggregat gespeichert. Keine zusätzliche Gerätearttabelle ist
|
||
für diesen Umfang nötig; Validatoren erzwingen referenzielle Integrität.
|
||
Referenzierte Gerätearten können nicht kommentarlos entfernt werden.
|
||
|
||
Startwerte: je Feld ProjectDevice vor Geräteart vor leer. Anzeigename:
|
||
Auswahlkenner, sonst Geräteartname, sonst Familie/Typ. Quellleistung bleibt
|
||
Quelle; eine Übernahme geschieht nur als sichtbare Planungsentscheidung.
|
||
Fehlender GZF wird sichtbar mit `1` vorbelegt, ohne zusätzliche Bestätigung.
|
||
Geräteart-
|
||
Änderungen schreiben keine vorhandenen Objekt-Overrides oder Rows um.
|
||
|
||
## 5. Konkretes Persistenzschema
|
||
|
||
Bestehende UUID-/Projekt-FKs und Eigentumsprüfungen werden beibehalten.
|
||
`JSON` bezeichnet validierten JSON-Text in SQLite, nicht ExcelJS-Objekte.
|
||
|
||
| Tabelle | Zielspalten / Constraints |
|
||
| --- | --- |
|
||
| `external_model_configurations` (ersetzt CSV-Tabelle) | `id TEXT PK`, `project_id TEXT NOT NULL UNIQUE FK projects`, `configuration JSON NOT NULL`. Enthält obiges vollständiges Aggregat; keine eigene Versionszählung. |
|
||
| `external_model_sources` | `id`, `project_id UNIQUE`, `name`, `source_type = revit_xlsx`. Genau eine implizite Quelle je Projekt. |
|
||
| `external_import_batches` | Letzter Batch vollständig mit IDs, Importart, Zeit, Dateiname, SHA-256, Objektzahl, `applied_project_revision`, `configuration_snapshot JSON`, `original_bytes BLOB`, `document JSON`; ältere Batches nur als Metadaten Datum, Dateiname, SHA-256 und Objektzahl. |
|
||
| `external_room_mappings` | Vorhandene Felder einschließlich normalisiertem Quellraumschlüssel, `room_id`, `default_distribution_board_id`; UNIQUE `(source_id, normalized_source_room_key)` bleibt. |
|
||
| `external_model_objects` | Bestehende interne IDs/Links; neu `worksharing_id TEXT NOT NULL`, UNIQUE `(source_id, worksharing_id)`; `ifc_guid TEXT NULL`, ohne Unique; `device_type_id TEXT NOT NULL` als validierter Verweis in das Konfigurationsaggregat. |
|
||
| Objektzustand | `accepted_source_values JSON` mit rohen typisierten Werten für Raum, Familie/Typ, Marker, Menge, Bemerkung und alle Rückgabefelder; `planning_values JSON` mit Anzeigename, Kategorie, Anschlussart, effektiver Menge, Einzelleistung W, nullable GZF/cosPhi/Kostengruppe/Bemerkung; `overridden_fields JSON`; bisherige Batch-/Raum-/Board-/ProjectDevice-/Row-Verweise und `presence_status` bleiben. |
|
||
| `external_export_batches` (neu) | `id TEXT PK`, `project_id FK`, `project_revision INTEGER`, `import_batch_id TEXT`, `exported_at_iso TEXT`, `file_name TEXT`, `sha256 TEXT`, `configuration_snapshot/hash`, `scope TEXT`, `object_count INTEGER`. Append-only Audit-Store. |
|
||
|
||
`import_batch_id` des Exportnachweises ist absichtlich eine historische UUID
|
||
ohne löschenden FK: Undo eines Imports oder Snapshot-Restore darf den Nachweis
|
||
nicht entfernen. Exportnachweise liegen außerhalb rücksetzbarer Fachsnapshots
|
||
und werden beim Projektduplikat nicht als dort erfolgte Exporte ausgegeben.
|
||
Sie bleiben beim Originalprojekt erhalten; diese Abgrenzung wird in R1
|
||
dokumentiert. Nur der letzte Importbatch gehört mit Bytes und Matrix zu
|
||
Snapshot/Transfer; ältere Batches werden dort nur als Metadaten erhalten.
|
||
|
||
Indices: bisherige Projekt-/Presence-, Board- und Row-Indices behalten;
|
||
zusätzlich `(project_id, device_type_id)`, `external_room_mapping_id`,
|
||
`linked_project_device_id`, Export `(project_id, exported_at_iso)`.
|
||
Cross-Project-Links und Geräteart-Verweise prüft jeder Command in der
|
||
Transaktion; bloße Existenz eines UUID-FK genügt nicht.
|
||
|
||
Effektiver Verteiler: ein gemeinsamer Resolver verwendet bei verknüpften
|
||
Objekten die Row/Circuit-Verteilung, sonst die Objektabweichung, sonst die
|
||
Raumvorgabe. `distributionBoardId = null` bedeutet Raumvorgabe verwenden;
|
||
bei fehlender Vorgabe ist das Objekt ohne Verteiler. Eine Änderung des
|
||
Raum-Standardverteilers betrifft nur unzugeordnete Objekte; verknüpfte Objekte
|
||
folgen immer der Row-Verteilung und blockieren diese Änderung nicht.
|
||
|
||
Planungswerte bleiben von Quellwerten getrennt. Nach Row-Zuordnung sind
|
||
Objekt-Planungswerte eingefrorene Startwerte; Anzeige, Abweichung und Export
|
||
verwenden die Row. Beim Lösen werden aktuelle Row-Werte einschließlich
|
||
Einheitenkonvertierung in die Objektplanung übernommen und als explizite
|
||
Overrides markiert. Effektive Objektmenge wird nicht durch die Row-Gesamtmenge
|
||
ersetzt. Die Row und ihr manueller Anteil bleiben erhalten.
|
||
|
||
## 6. Commands und Transaktionsgrenze
|
||
|
||
Alle öffentlichen Fach-Writes verlangen `expectedRevision`, Anmeldung und
|
||
`X-Leistungsbilanz-Request: 1`. `expectedRevision` gehört wie bisher zum
|
||
Ausführungsumschlag. Jede Transition enthält vollständige erwartete/Ziel-
|
||
Snapshots der betroffenen Datensätze, keine ungesicherten Teilpatches.
|
||
|
||
| Command | Payload-Skizze und Gegenrichtung |
|
||
| --- | --- |
|
||
| `external-model-configuration.update` v1 | `{ expected, target }` einschließlich Layout, Mapping, Gerätearten und Regeln. Inverse tauscht den vollständigen Fachinhalt; keine eigene Konfigurationsversion. |
|
||
| `external-import.apply-initial` v2 | `{ expectedConfiguration, targetConfiguration, expected, target }`. Quelle, Batch, Mapping, Objekte und neue Regeln zusammen; Inverse stellt leeren externen und vorherigen Konfigurationsinhalt wieder her. Keine Rows. |
|
||
| `external-import.apply-follow-up` v1 | `{ configuration: transition, batch: transition, roomMappings: transitions, objects: transitions, rows: quantityTransitions, decisions }`; Server erzeugt deterministischen Diff und alle UUIDs. Inverse tauscht alle betroffenen Zustände. |
|
||
| `external-model-object.update-planning` v1 | `{ objects: transitions }`; nur ungebundene Objekte; geänderte Felder in Overrides. Einzel-/Sammelbearbeitung identischer Regeln. |
|
||
| `external-model-object.update-distribution-board` v1 | `{ objects: transitions }`; explizite Objektabweichung, Projekt-/Row-Konsistenz. |
|
||
| `external-model-room-mapping.update` v1 | `{ mappings: transitions }`; interner Raum/Standardverteiler; betroffene Objekt-/Row-Konsistenz mitprüfen. |
|
||
| `external-model-object.update-project-device-link` v1 | `{ objects: transitions, confirmedPlanningFields }`; keine Vorlagenänderung; neue Startwerte nur für ausdrücklich bestätigte Felder. Gebundene Objekte werden über ihre Row bearbeitet. |
|
||
| `external-object.update-row-assignment` v2 | `{ rows: transitions, objects: transitions, confirmedConflictObjectIds }`; Linkwechsel, materialisierte Mengen, beim Lösen aktuelle Row-Planungswerte; vollständige Inverse. |
|
||
| `external-object.assign-to-new-row` v2 | Bestehender vollständiger Row-/Objekt-/Reserveplan mit neuen Objektverträgen; history-only Inverse entfernt nur exakt unveränderte Erzeugnisse. |
|
||
| `external-object.assign-to-new-circuit` v2 | Bestehender Circuit-/Protection-/Row-/Objektplan; BMK einmal planen und festschreiben, gleiche UUIDs bei Redo. |
|
||
| `external-model-object.delete-missing` v1 | `{ objects: transitionsToNull, rows: quantityTransitions }`; ausschließlich ausdrücklich bestätigte weiterhin fehlende Objekte, kein Row-/Circuit-Löschen; Inverse stellt IDs, Links und Mengen wieder her. |
|
||
|
||
Bestehende `project-device.insert`, Room-/Floor-Commands bleiben eigenständige
|
||
Undo-Schritte. Familien-/Geräteartentscheidungen im Wizard sind lokale
|
||
Entwürfe und werden erst mit dem Import atomar persistiert. Nach separater
|
||
Kataloganlage aktualisiert der Wizard die Projektrevision und plant neu,
|
||
ohne lokale Entscheidungen zu verlieren.
|
||
|
||
Die Konfiguration hat keine eigene Versionszählung. Vorschau und Apply binden
|
||
sich an `expectedRevision`; weil auch Undo/Redo neue, strikt steigende
|
||
Projektrevisionen erzeugen, wird ein veralteter Plan eindeutig ungültig.
|
||
Importbatches speichern eine vollständige Konfigurationskopie. Prüfen:
|
||
Änderung → Undo → anderer Zweig darf niemals denselben Plan wieder gültig
|
||
machen.
|
||
|
||
XLSX wird vor der synchronen SQLite-Transaktion in einer serverinternen
|
||
Vorbereitung geparst. Apply überträgt Bytes erneut; Hash, Matrix und
|
||
Entscheidungen werden erneut validiert. Im Store werden Revision,
|
||
Konfigurationsinhalt, erwartete Objekte, Links und vollständiger
|
||
vorbereiteter Plan atomar geprüft. Vorbereitete Daten sind keine vom Client
|
||
behauptete Validierung: Generic-Command-Requests dürfen Importcommands nicht
|
||
an dieser serverseitigen Vorbereitung vorbeischleusen. Undo/Redo lesen und
|
||
parsen keine Excel-Datei; sie wenden ausschließlich die gespeicherten
|
||
Vorher-/Nachher-Zustände an. Keine asynchronen Callbacks in
|
||
`better-sqlite3`-Transaktionen.
|
||
|
||
Row-/Circuit-/Gruppen-Löschcommands erfassen verknüpfte Objekte vollständig.
|
||
Nach der Warnung `X Revit-Objekte werden wieder unzugeordnet` lösen sie die
|
||
Links im selben Command; ihre Inverse stellt sie exakt wieder her. Sperren
|
||
bleiben nur für das Löschen referenzierter Verteiler und Räume und gelten auch
|
||
in Unterbaum- und History-only-Pfaden.
|
||
|
||
## 7. Migration, Snapshot 10, History und Transfer
|
||
|
||
Neue additive Migration nach `0013_users` (voraussichtlich `0014`, Nummer
|
||
beim Beginn von R3 erneut prüfen). Bestehende SQL-/Metadateien unverändert.
|
||
|
||
1. Vor R3-Anwendung API-Schreibzugriffe stoppen und `npm run db:backup`
|
||
ausführen; verifiziertes Backup samt Wiederherstellungsmöglichkeit sichern.
|
||
R0 verändert keine DB und benötigt daher kein Migrationsbackup.
|
||
2. Vorhandene externe CSV-Entwicklungsdaten dürfen gemäß Auftrag verworfen
|
||
werden. Zuerst je verknüpfter Row `quantity = manual_quantity` setzen;
|
||
sonst bliebe nach Entfernen der externen Objekte eine Phantomlast.
|
||
Rows, Circuits, manuelle Anteile, UUIDs und BMKs bleiben bestehen.
|
||
3. Externe Objekte → Mappings → Batches → Quellen → CSV-Konfiguration in
|
||
FK-sicherer Reihenfolge entfernen. Tabellen gezielt neu aufbauen, wo
|
||
SQLite NOT-NULL-/Unique-Änderungen dies erfordern. Neue Zieltabellen und
|
||
Indices erstellen. Keine leeren Worksharing-IDs als Backfill erfinden.
|
||
4. Generiertes SQL und Drizzle-Metadaten prüfen; saubere und bestehende
|
||
Test-DBs migrieren; FK-/Integritätsprüfung und Mengenabgleich ausführen.
|
||
Der Reset ist eine dokumentierte einmalige Entwicklungsmigration,
|
||
kein stiller Laufzeit-Import.
|
||
5. Alte Revisionsdaten nicht umschreiben, keine CSV-History heimlich
|
||
überspringen. An der Ausführungsgrenze alte CSV-Konfigurationscommands,
|
||
Initial-/Zuordnungscommands v1 einschließlich ihrer History-Inversen und
|
||
Restores mit CSV-Inhalt vor jedem Write ablehnen.
|
||
|
||
Fehlermeldung für History:
|
||
„Dieser Verlaufsschritt verwendet die frühere CSV-/IfcGUID-Anbindung und
|
||
kann mit der Excel-Anbindung nicht ausgeführt werden.“
|
||
Der History-Status sollte den blockierten obersten Schritt samt Grund zeigen;
|
||
keine automatisch geleerten Stacks und keine Änderung bei Ablehnung. Frühere
|
||
allgemeine Commands können nach dem Reset zusätzlich an ihren exakten
|
||
Vorbedingungen scheitern; das bleibt eine sichere, sichtbare Ablehnung.
|
||
|
||
Snapshot v10 enthält aktive Excel-Konfiguration, Gerätearten/Regeln, Quelle,
|
||
den letzten Batch mit Originalbytes/Matrix, ältere Batches nur als Metadaten
|
||
sowie Raum-Mappings, Objekte und Links.
|
||
Upgrade v1–9: bestehende nicht-externe Upgrader erhalten. Nur wenn
|
||
`externalCsvConfiguration` fehlt/null ist **und** der externe Zustand leer
|
||
ist, verlustfrei auf v10 heben. Auch eine gespeicherte CSV-Konfiguration
|
||
allein zählt als nicht leerer CSV-Zustand und wird nicht still verworfen.
|
||
Snapshot-Checksumme am gespeicherten Original vor Upgrade prüfen.
|
||
|
||
Fehlermeldung:
|
||
„Dieser Projektstand enthält Daten der früheren CSV-/IfcGUID-Anbindung.
|
||
Eine automatische Übernahme in die Excel-Anbindung ist nicht möglich.
|
||
Bitte verwenden Sie einen Projektstand ohne CSV-Daten.“
|
||
|
||
Der aktuelle portable Transfer ist bereits **Format 2** (Grobbilanzen),
|
||
nicht Format 1 wie im alten Audit. Envelope 2 und dessen Format-1-Lesepfad
|
||
bleiben; enthaltenes Projektzustandsschema wird 10. Duplizieren remappt
|
||
Projekt-, Konfigurations-, Geräteart-, Regel-, Quellen-, Batch-, Mapping-,
|
||
Objekt-UUIDs und alle internen Links, auch in Konfigurationssnapshots der
|
||
Batches. Worksharing-IDs, optionale IfcGUIDs und Originalbytes bleiben
|
||
unverändert. Grobbilanzen und globale Office-/Auth-Daten behalten ihre
|
||
bisherige Abgrenzung. Exportnachweise werden nicht als neue Projektexporte
|
||
dupliziert.
|
||
|
||
## 8. Read-Modelle, Oberfläche und Rückgabe
|
||
|
||
Vorgeschlagene API-Basis `/api/projects/:projectId/revit`:
|
||
`configuration` (GET/PUT), `profile/preview` (POST, zustandslos),
|
||
`initial-import/plan|apply`, `follow-up-import/plan|apply` (POST),
|
||
`status` und `objects` (GET), `return-workbook` (POST mit Export-Audit).
|
||
Der bestehende listenbezogene External-Objects-GET bleibt erhalten.
|
||
Objektmutationen verwenden den vorhandenen Project-Command-Endpunkt mit
|
||
neuen typisierten Verträgen. CSV-Endpunkte werden entfernt, keine Aliasse.
|
||
|
||
Export verwendet `expectedRevision` für einen konsistent gelesenen Stand;
|
||
die Datei-Erzeugung erfolgt außerhalb des kurzen Read-Snapshots. Danach
|
||
separater append-only Audit-Write, ohne Projektrevision oder Undo-Schritt.
|
||
Bei Auditfehler kein Erfolg/download; ein Eintrag belegt Dateierzeugung,
|
||
nicht einen tatsächlich abgeschlossenen Browserdownload.
|
||
|
||
Die Revit-Seite lädt einmal eine kompakte Objektliste. Filterung und Sortierung
|
||
laufen im Client; die Tabelle wird virtualisiert und verwendet keine Server-
|
||
Pagination. Filter-/Sortierzustand, UUID-Auswahl und Sammelaktionen sind
|
||
unabhängig vom DOM. „Alle gefilterten“ wird vor einer Mutation zu einer
|
||
expliziten ID-Auswahl an der aktuellen Revision aufgelöst. Große Auswahlmengen
|
||
werden nicht in getrennte Undo-Schritte aufgeteilt. Kein vollständiges
|
||
Batch-Blob im DTO.
|
||
|
||
Einstellungen verwenden Musterdatei-Erkennung mit editierbaren Positionen,
|
||
Feld-Dropdowns/freier Eingabe und Kommentar-Typ-/Einheitenanzeige. Regeln
|
||
entstehen im Wizard und sind auf der Seite einklappbar nachbearbeitbar.
|
||
Fehlende Raumangaben bleiben zulässig, Board-Zuordnung ist auch ohne Raum
|
||
möglich. ProjectDevice-Zuordnung bleibt optional.
|
||
|
||
Rechter Editor-Drawer: nur vorhandene, ungebundene konfliktfreie Objekte des
|
||
effektiven Verteilers. Gruppierung nach internem Raum, Auswahlkenner,
|
||
Geräteart-ID, Kategorie und identischen Planungswerten; unterschiedliche
|
||
ProjectDevice-Links bleiben ebenfalls getrennt, da eine Row nur einen Link
|
||
haben kann. Effektive Einzelmengen sind kein Gleichheitsfeld: Einfach- und
|
||
Mehrfachsteckdosen sollen gemeinsam gruppierbar sein. Anzeige enthält
|
||
Mengensumme und Objektanzahl. Fachlich heterogene Auswahl darf nicht
|
||
willkürlich auf eine einzelne Row zusammenfallen; Konfliktanzeige verlangt
|
||
eine kompatible Auswahl. Row-Indikator öffnet Objekte und explizites Lösen.
|
||
|
||
Eine Funktion `getRevitBoardName(board)` liefert ausschließlich `board.name`.
|
||
Gemeinsam: Name + unverändertes `equipmentIdentifier`, ohne Separator.
|
||
Getrennt: Name und BMK in eigene Parameter. Ohne Circuit bleiben beide
|
||
Stromkreiswerte leer, auch wenn ein Planungsverteiler bekannt ist.
|
||
|
||
Exportprojektion und Abweichungsanzeige teilen dieselbe reine Funktion:
|
||
Row-Anzeigename/-Leistung bei Link, sonst Objektplanung; Objektleistung in W
|
||
= effektive Menge × Einzelleistung (Row-kW vorher × 1000), ohne GZF.
|
||
Spannung vom Circuit, sonst Projektstandard passend zur Kategorie.
|
||
Strom aus Objektleistung, U, cosPhi und Phasenart; Drehstrom mit Faktor √3,
|
||
auf 0,01 A. Fehlende/ungültige Werte ergeben leer plus Hinweis. Nullleistung
|
||
bleibt bei gültigem Nenner numerisch 0. Kein still angenommenes cosPhi.
|
||
Abweichungsvergleich normalisiert Zahlpräzision, Leere und skalare Typen
|
||
gegen den **zuletzt importierten**, nicht den zuletzt exportierten Stand.
|
||
|
||
Umfang: alle im letzten Batch vorhandenen Objekte / abweichende / explizite
|
||
Auswahl. Fehlende Objekte haben keine aktuelle Originalzeile und werden
|
||
nicht künstlich wieder eingefügt. Alle Spalten bleiben, nicht ausgegebene
|
||
Parameter behalten importierte Werte. Fehlende optionale Spalten für
|
||
Bezeichnung, Leistung, Spannung und Strom werden mit Hinweis übersprungen.
|
||
Nur fehlende Stromkreisspalten des gewählten Modus blockieren. Dateiname gemäß
|
||
Auftrag mit bereinigtem Projektnamen und Tagesdatum.
|
||
|
||
Folgeimport matcht ausschließlich Worksharing-ID. Ein neuer Batch speichert
|
||
immer den vollständigen letzten Quellstand. Unveränderte Objekte werden
|
||
nicht allein zum Fortschreiben von `lastSeenImportBatchId` neu geschrieben;
|
||
aktuelle Sichtbarkeit/Quellwerte kommen aus Batch/Matrix. Akzeptierte
|
||
Planungsgrundlage und letzte importierte Beobachtung sind getrennt.
|
||
Raum-/Familien-/Marker-/Mengenänderungen brauchen Entscheidungen. Bei
|
||
unvereinbarer Row-Zuordnung blockiert die Übernahme bis zum ausdrücklichen
|
||
Lösen/Umordnen, statt Rows automatisch zu erzeugen. Übernommene Mengen
|
||
ändern alle betroffenen Row-Summen im selben Importcommand. Fehlend setzt
|
||
nur den Status, erhält vorerst Link und Menge. Rückgabefeld-Abweichungen
|
||
sind Hinweise, keine Übernahmeentscheidungen. Ein völlig unveränderter
|
||
Folgeimport erzeugt einen nachvollziehbaren Batch, aber keine unnötigen
|
||
Objekt-/Row-Updates.
|
||
|
||
## 9. Pakete, Verifikation und Risiken
|
||
|
||
R0 liefert nur diesen Bericht und einen eigenen Commit. R1–R10 starten erst
|
||
nach Freigabe; keine Fortschreibung der alten 14.0–14.6-Liste.
|
||
|
||
| Paket | Arbeit und entscheidende Abnahme |
|
||
| --- | --- |
|
||
| R1 | Neue Excel-Anforderungen und R0–R10-Plan; alte Dokumente archivieren/auf Verweise kürzen; AGENTS, Architektur, History-Doku, Phase-14-Abschnitt, LLM-Kontext und alle Links nachziehen. Referenzdatei gezielt ignorieren. |
|
||
| R2 | XLSX-Adapter und lokaler Referenzprüfer mit Pfadargument. Synthetischer Roundtrip für Kommentare, Blattname, Spaltenbreiten, Typen, Header-/Startpositionen, unbekannte Spalten, Auswahl und Summenausschluss. Aus der lokalen Musterdatei entsteht eine Rückgabedatei mit geänderten Werten. Danach Nutzerabnahme in Linear; R3 bleibt bis zum Erfolg gesperrt. Bei Fehlschlag vor R3 OOXML-Direktänderung als Alternative vorlegen. Keine DB-Änderung. |
|
||
| R3 | Neue Konfiguration/Identität, additive Migration, Snapshot 10, Transfer und History-Ablehnungen; Backend-CSV entfernen. Parser-Vorbereitung von synchroner Transaktion trennen. |
|
||
| R4 | Revit-Seite, Navigation, Einstellungen; Projektseitenmodal und dessen CSV-Frontend-Verträge entfernen. |
|
||
| R5 | Erstimport mit atomaren Regeln, Geräten, Raum-/Board- und Objektentscheidungen; ProjectDevice-Vorbelegung nach Priorität; ein Import-Undo-Schritt. |
|
||
| R6 | Kompakte Objekt-Reads, clientseitige Filter/Sortierung, Virtualisierung, Sammel-Commands und Overrides. |
|
||
| R7 | Gruppierung, Drag-and-drop, Row-Indikatoren und Lösen inklusive aktueller Row-Werte; drei Drop-Ziele, atomare Objektlösung beim Row-/Circuit-/Gruppen-Löschen und Reload/Undo/Redo. |
|
||
| R8 | Drei Exportumfänge, gemeinsame Abweichungsprojektion, typisierte Rückgabe, append-only Nachweis und Linear-Strukturabnahme. |
|
||
| R9 | Kontrollierter Folgeimport, fehlende Objekte, explizites Löschen, mengenwirksame Entscheidungen und atomare Inverse. |
|
||
| R10 | Vollständige synthetische Ende-zu-Ende-Kette einschließlich Neustart, Snapshot/Restore und Duplikat; mehrere Tausend Objekte; Doku auf ausgelieferten Stand. |
|
||
|
||
Die temporäre Grenze zwischen R3 und R4 wird ausdrücklich behandelt:
|
||
Buildfähige, nicht aktive Kompatibilitätstypen für das alte Modal dürfen bis
|
||
R4 bestehen, aber kein aktiver CSV-Backendpfad. Veraltete Projektaktion bis
|
||
zur neuen Seite deaktivieren/entfernen, keinen funktionslosen Upload anbieten.
|
||
Die Dokumentation darf Zwischenstände nicht als fertigen Workflow ausweisen.
|
||
|
||
Pflicht je R1–R10: fokussierte Tests, `npm test`, `npm run build:api`,
|
||
`npm run build:web`, `npm run typecheck:scripts`,
|
||
`npx tsc --noEmit -p tsconfig.next.json`, je ein knapper imperativer Commit.
|
||
R0: 31 vorhandene Tests aus sechs Dateien erfolgreich ausgeführt:
|
||
`external-model-foundation`, `external-initial-import-plan`,
|
||
`external-initial-import-project-command.repository`,
|
||
`external-object-row-assignment-project-command.repository`,
|
||
`external-circuit-list-objects`,
|
||
`external-csv-configuration-project-command.repository` (je `.test.ts`).
|
||
Diese decken vorhandene Commit-/Rollback-, Undo-/Redo-, Snapshot-/Transfer-
|
||
Grundlagen ab, nicht die neue Excel-Funktion. Der erste Start scheiterte an
|
||
Sandbox-`spawn EPERM`; derselbe Lauf außerhalb der Sandbox bestand.
|
||
Vollständige Builds/Tests sind für diesen reinen R0-Bericht nicht ausgeführt.
|
||
|
||
Zusätzlich erforderliche neue Prüfungen:
|
||
|
||
- Migration mit externen Row-Anteilen; keine Phantomlast und unveränderte BMKs.
|
||
- v1–9 ohne CSV verlustfrei, mit CSV klare Ablehnung; alte Command-Inversen
|
||
und alte Restore-Payloads ebenfalls ohne Teilwrites ablehnen.
|
||
- Config-Änderung/Undo/Neuzweig invalidiert alte Pläne über `expectedRevision`; Apply mit anderem
|
||
Dateiinhalt, fehlenden Entscheidungen oder fremden UUIDs rollt vollständig
|
||
zurück. Später Historyfehler rollt auch Regeln, Links und Mengen zurück.
|
||
- Gebundene Row ändern → Export → Lösen → Undo/Redo behält fachliche Werte.
|
||
- Mehrfachsteckdosen: unterschiedliche effektive Mengen, identische
|
||
Einzelwerte, richtige Gruppierung und objektweise Rückgabeleistung.
|
||
- Row-/Circuit-/Gruppen-Löschen löst externe Links im Command und stellt sie
|
||
per Undo wieder her. Raum-/Verteilerlöschen mit Referenzen blockiert.
|
||
- Export-Audit scheitert: keine fachliche Revision; SHA-256 entspricht exakt
|
||
den gelieferten Bytes. Snapshot/Restore entfernt keine Exportnachweise.
|
||
- 5.000/10.000 synthetische Objekte: Parserzeit, Peak-Speicher, Payloadgröße,
|
||
Command-/Snapshot-Dauer und UI-Reaktionszeit messen; nur sichtbare Zeilen im
|
||
DOM, stabile Auswahl über virtuelle Bereiche.
|
||
|
||
Wesentliche Restrisiken: Linear-Kommentarbindung ist nur anhand des
|
||
Exports beobachtet; ExcelJS-Releasepflege und Roundtrip sind in R2 zu
|
||
belegen. Große Originaldateien vervielfachen sich in History/Snapshots;
|
||
Limits und Messungen müssen reale Speichergrößen berücksichtigen. Die
|
||
CSV-Entwicklungsbereinigung ist absichtlich verlustbehaftet, aber auf den
|
||
externen Zustand begrenzt und nur nach verifiziertem Backup zulässig.
|
||
Fehlende Zielparameter im Muster verlangen einen neuen vollständigen
|
||
Linear-Export, keine geratenen GUID-Kommentare.
|
||
|
||
**Freigabepunkt:** R0 wurde am 29.09.2026 mit den in R1 dokumentierten
|
||
Korrekturen freigegeben. R2 besitzt einen neuen verbindlichen Freigabepunkt:
|
||
Vor Beginn von R3 muss der Nutzer die in R2 erzeugte Rückgabedatei erfolgreich
|
||
in Linear getestet haben.
|