GAP WP3.0: Generisches, editierbares Register-Datenmodell + Editor

Verwaltete Register (GAP-Report §5.2) sind nicht mehr nur benannte Dokumente,
sondern editierbare Register mit Pflichtspalten und Cross-Links.

- Schema: ManagedRegister (code, title, columns JSON, reviewCycle, responsible,
  supplierLink/assetLink) + RegisterRow (values JSON, supplierRef, assetRef).
  Migration + RLS; in TENANT_MODELS.
- import-managed.ts: 7 generische Register (REG-PROJECTS, -SENS-ROLES, -AUDIT-PLAN,
  -EXT-SERVICES, -SW-WHITELIST, -CRIT-SERVICES, -NET) mit definierten Pflichtspalten
  + Cross-Link-Flags; legt Register-Dokument (Bibliothek/Link) UND ManagedRegister-
  Definition idempotent an (RegisterRow bleibt bei Re-Import erhalten).
- actions/register.ts: addRegisterRow/updateRegisterRow/deleteRegisterRow
  (Modul policies, policy:write; Werte gegen die Pflichtspalten, optional Lieferant/Asset).
- components/generic-register.tsx: editierbare Zeilen-Tabelle + Cross-Link-Selects
  (Lieferant = Asset SUPPLIER/IT_SERVICE, Asset = alle); in /policies/[code] eingebunden.

Browser-verifiziert: REG-EXT-SERVICES rendert 7 Spalten + Lieferant/Asset-Selects;
Eintrag anlegen persistiert alle Werte. tsc + lint + build + Guard-Check grün.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-23 11:07:08 +02:00
co-authored by Claude Opus 4.8
parent b9784e55f4
commit bd56a478fc
8 changed files with 326 additions and 8 deletions
+41
View File
@@ -1029,3 +1029,44 @@ model HandbookTopic {
@@index([tenantId])
@@map("handbook_topics")
}
// Generisches verwaltetes Register (GAP-Report WP3.0): benannte Register mit
// Register-ID (REG-…), definierten Pflichtspalten, Review-Turnus und optionalen
// Cross-Links an Lieferanten (R13/VA-10) und Asset-Inventar (R02/VA-08).
model ManagedRegister {
id String @id @default(cuid())
tenantId String @map("tenant_id")
code String // REG-PROJECTS, REG-SW-WHITELIST, …
title String
description String?
columns Json @default("[]") // [{ key, label }] Pflichtspalten
reviewCycle String? @map("review_cycle")
responsible String?
supplierLink Boolean @default(false) @map("supplier_link") // Cross-Link Lieferant (Asset)
assetLink Boolean @default(false) @map("asset_link") // Cross-Link Asset
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
rows RegisterRow[]
@@unique([tenantId, code])
@@index([tenantId])
@@map("managed_registers")
}
model RegisterRow {
id String @id @default(cuid())
tenantId String @map("tenant_id")
registerId String @map("register_id")
values Json @default("{}") // { spaltenKey: wert }
supplierRef String? @map("supplier_ref") // Asset-ID (Lieferant)
assetRef String? @map("asset_ref") // Asset-ID
orderIdx Int @default(0) @map("order_idx")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
register ManagedRegister @relation(fields: [registerId], references: [id], onDelete: Cascade)
@@index([tenantId, registerId])
@@map("register_rows")
}