Datenmodell (Prisma, RLS): Supplier + SupplierAsset, SupplierAssessment, Contract, Nda, SupplierEvidence, ServiceResponsibility, Subcontractor, ManagementDecision, SupplierControl (globaler Katalog) + SupplierControlMaturity. - VDA-ISA-2027-Kap.-6-Mini-Katalog (Controls 6.1.1–6.1.3) mit Zielbild, Muss/Soll, Anforderungen für hoch/sehr hoch, Simplified Group Assessment, Ziel-Reifegrad 3 und Cross-Referenzen (ISO/NIST/BSI); im Seed befüllt - Lieferantenverzeichnis mit KPIs (gesamt, NIS2-relevant, ablaufend, Reviews fällig), Kritikalität, NIS2-Flag, Review-Fristen - Detail-Popup: Stammdaten (Sektor/Leistung/Datenkategorien/CIA), betroffene Assets, VDA-ISA-Reifegrade je Control (Ziel 3, farbige Balken), Nachweise (Angemessenheit + Ablauf), Assessments, Verträge (AV/DPA, Flow-down, Fristen), NDAs (Fristen), Shared-Responsibility- Matrix, Subunternehmer, Managemententscheidungen - Bearbeiten-Popup: Stammdaten (ein Speichern), Reifegrad je Control, Add/Delete für alle Kind-Entitäten, Löschen im ⋯-Menü - Regel 6.1.1: fehlt geprüfter Audit-/TISAX-Nachweis → Warnung, dass eine dokumentierte risikobasierte Managemententscheidung nötig ist - Server-Actions mit Zod/RBAC/Audit-Log; Seed mit Demo-Lieferant (TISAX-Label, AV/DPA, NDA, Assessment, RACI, Reifegrade) - Menüpunkt „Lieferanten" aktiv; Lieferant ↔ Asset verknüpft (Graph) Verifiziert: Register/Detail/Bearbeiten dunkel & vollständig, Reifegrad- Save (6.1.2→4 mit Audit), Managemententscheidungs-Logik. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
664 lines
20 KiB
Plaintext
664 lines
20 KiB
Plaintext
// ISMS-Tool — Prisma-Schema
|
||
// Fundament (Iteration 1): Mandanten, Nutzer, RBAC, Audit-Log.
|
||
// Fachliche Entitäten (Assets, BIA, Risiken, …) folgen je Iteration — siehe docs/SPEC.md §5.
|
||
//
|
||
// Multi-Tenant-Regel: Jede fachliche Tabelle trägt tenantId. Zugriff nur über den
|
||
// zentralen Tenant-Guard (src/server/db.ts); zusätzlich Postgres RLS (prisma/rls.sql).
|
||
|
||
generator client {
|
||
provider = "prisma-client-js"
|
||
previewFeatures = ["postgresqlExtensions"]
|
||
}
|
||
|
||
datasource db {
|
||
provider = "postgresql"
|
||
extensions = [pgvector(map: "vector")]
|
||
}
|
||
|
||
enum TenantStatus {
|
||
ACTIVE
|
||
SUSPENDED
|
||
}
|
||
|
||
enum UserStatus {
|
||
ACTIVE
|
||
INVITED
|
||
LOCKED
|
||
DEACTIVATED
|
||
}
|
||
|
||
model Tenant {
|
||
id String @id @default(cuid())
|
||
name String
|
||
slug String @unique
|
||
status TenantStatus @default(ACTIVE)
|
||
config Json @default("{}")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
users User[]
|
||
roles Role[]
|
||
auditLogs AuditLog[]
|
||
|
||
@@map("tenants")
|
||
}
|
||
|
||
model User {
|
||
id String @id @default(cuid())
|
||
tenantId String @map("tenant_id")
|
||
email String
|
||
passwordHash String @map("password_hash")
|
||
name String
|
||
status UserStatus @default(ACTIVE)
|
||
mfaSecret String? @map("mfa_secret")
|
||
// Plattform-Admin ist mandantenübergreifend (Betreiberrolle)
|
||
isPlatformAdmin Boolean @default(false) @map("is_platform_admin")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
tenant Tenant @relation(fields: [tenantId], references: [id])
|
||
userRoles UserRole[]
|
||
ownedAssets Asset[] @relation("assetOwner")
|
||
ownedProcesses Process[] @relation("processOwner")
|
||
ownedRisks Risk[] @relation("riskOwner")
|
||
ownedMeasures Measure[] @relation("measureOwner")
|
||
|
||
@@unique([tenantId, email])
|
||
@@index([tenantId])
|
||
@@map("users")
|
||
}
|
||
|
||
model Role {
|
||
id String @id @default(cuid())
|
||
tenantId String @map("tenant_id")
|
||
key String // z. B. tenant-admin, isb, auditor, owner, user
|
||
name String
|
||
|
||
tenant Tenant @relation(fields: [tenantId], references: [id])
|
||
userRoles UserRole[]
|
||
rolePermissions RolePermission[]
|
||
|
||
@@unique([tenantId, key])
|
||
@@index([tenantId])
|
||
@@map("roles")
|
||
}
|
||
|
||
model Permission {
|
||
id String @id @default(cuid())
|
||
key String @unique // z. B. asset:read, risk:write, incident:manage
|
||
|
||
rolePermissions RolePermission[]
|
||
|
||
@@map("permissions")
|
||
}
|
||
|
||
model UserRole {
|
||
userId String @map("user_id")
|
||
roleId String @map("role_id")
|
||
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
|
||
|
||
@@id([userId, roleId])
|
||
@@map("user_roles")
|
||
}
|
||
|
||
model RolePermission {
|
||
roleId String @map("role_id")
|
||
permissionId String @map("permission_id")
|
||
|
||
role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
|
||
permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade)
|
||
|
||
@@id([roleId, permissionId])
|
||
@@map("role_permissions")
|
||
}
|
||
|
||
// ── Assets & BIA (ein Modul, SPEC §4.1) ─────────────────────────────────────
|
||
|
||
enum AssetType {
|
||
INFORMATION
|
||
SYSTEM
|
||
APPLICATION
|
||
LOCATION
|
||
SUPPLIER
|
||
PERSON
|
||
DATA
|
||
}
|
||
|
||
enum AssetStatus {
|
||
ACTIVE
|
||
PLANNED
|
||
RETIRED
|
||
}
|
||
|
||
enum ProcessAssetRole {
|
||
PRIMARY
|
||
SECONDARY
|
||
}
|
||
|
||
enum ProcessCategory {
|
||
CORE // Kernprozess
|
||
MANAGEMENT // Managementprozess
|
||
SUPPORT // Unterstützender Prozess
|
||
}
|
||
|
||
model Asset {
|
||
id String @id @default(cuid())
|
||
tenantId String @map("tenant_id")
|
||
name String
|
||
description String?
|
||
type AssetType
|
||
status AssetStatus @default(ACTIVE)
|
||
ownerId String? @map("owner_id")
|
||
location String?
|
||
tags String[] @default([])
|
||
// Schutzbedarf 1–4 je Schutzziel (C/I/A)
|
||
confidentiality Int @default(1)
|
||
integrity Int @default(1)
|
||
availability Int @default(1)
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
createdBy String? @map("created_by")
|
||
|
||
owner User? @relation("assetOwner", fields: [ownerId], references: [id])
|
||
relationsFrom AssetRelation[] @relation("relationFrom")
|
||
relationsTo AssetRelation[] @relation("relationTo")
|
||
processAssets ProcessAsset[]
|
||
riskAssets RiskAsset[]
|
||
supplierLinks SupplierAsset[]
|
||
|
||
@@index([tenantId])
|
||
@@index([tenantId, type])
|
||
@@map("assets")
|
||
}
|
||
|
||
model AssetRelation {
|
||
id String @id @default(cuid())
|
||
tenantId String @map("tenant_id")
|
||
assetId String @map("asset_id")
|
||
relatedAssetId String @map("related_asset_id")
|
||
// z. B. depends_on, hosts, processes_data_of
|
||
type String @default("depends_on")
|
||
|
||
asset Asset @relation("relationFrom", fields: [assetId], references: [id], onDelete: Cascade)
|
||
relatedAsset Asset @relation("relationTo", fields: [relatedAssetId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([assetId, relatedAssetId, type])
|
||
@@index([tenantId])
|
||
@@map("asset_relations")
|
||
}
|
||
|
||
model Process {
|
||
id String @id @default(cuid())
|
||
tenantId String @map("tenant_id")
|
||
name String
|
||
description String?
|
||
category ProcessCategory @default(CORE)
|
||
ownerId String? @map("owner_id")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
createdBy String? @map("created_by")
|
||
|
||
owner User? @relation("processOwner", fields: [ownerId], references: [id])
|
||
processAssets ProcessAsset[]
|
||
bia BiaEntry?
|
||
risks Risk[]
|
||
|
||
@@index([tenantId])
|
||
@@map("processes")
|
||
}
|
||
|
||
model ProcessAsset {
|
||
id String @id @default(cuid())
|
||
tenantId String @map("tenant_id")
|
||
processId String @map("process_id")
|
||
assetId String @map("asset_id")
|
||
role ProcessAssetRole @default(SECONDARY)
|
||
|
||
process Process @relation(fields: [processId], references: [id], onDelete: Cascade)
|
||
asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([processId, assetId])
|
||
@@index([tenantId])
|
||
@@map("process_assets")
|
||
}
|
||
|
||
model BiaEntry {
|
||
id String @id @default(cuid())
|
||
tenantId String @map("tenant_id")
|
||
processId String @unique @map("process_id")
|
||
// Wiederanlaufparameter in Stunden (SPEC §4.1.2)
|
||
rtoHours Int? @map("rto_hours")
|
||
rpoHours Int? @map("rpo_hours")
|
||
mtdHours Int? @map("mtd_hours")
|
||
// Schadenshöhe 1–4 je Schutzziel
|
||
impactC Int @default(1) @map("impact_c")
|
||
impactI Int @default(1) @map("impact_i")
|
||
impactA Int @default(1) @map("impact_a")
|
||
// Kritikalität 1–4, abgeleitet (Max-Prinzip), redundant gespeichert für Listen/Filter
|
||
criticality Int @default(1)
|
||
notes String?
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
process Process @relation(fields: [processId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([tenantId])
|
||
@@map("bia_entries")
|
||
}
|
||
|
||
// ── Risikoanalyse (SPEC §4.2) ───────────────────────────────────────────────
|
||
|
||
enum RiskTreatment {
|
||
AVOID // Vermeiden
|
||
MITIGATE // Vermindern
|
||
TRANSFER // Übertragen
|
||
ACCEPT // Akzeptieren
|
||
}
|
||
|
||
enum RiskStatus {
|
||
OPEN
|
||
IN_TREATMENT
|
||
ACCEPTED
|
||
CLOSED
|
||
}
|
||
|
||
model Risk {
|
||
id String @id @default(cuid())
|
||
tenantId String @map("tenant_id")
|
||
// Laufende Nummer je Mandant → Anzeige als "R-001"
|
||
refNo Int @map("ref_no")
|
||
|
||
title String
|
||
description String?
|
||
threat String? // Bedrohung (Katalog folgt, SPEC §5)
|
||
vulnerability String? // Schwachstelle
|
||
|
||
// Brutto-Bewertung: Eintrittswahrscheinlichkeit × Auswirkung (je 1–5)
|
||
likelihood Int @default(1)
|
||
impact Int @default(1)
|
||
score Int @default(1) // redundant für Listen/Heatmap
|
||
|
||
// Rest-Risiko nach Maßnahmen (berechnet, dezimal wegen Teil-Minderungen)
|
||
residualLikelihood Float? @map("residual_likelihood")
|
||
residualImpact Float? @map("residual_impact")
|
||
residualScore Float? @map("residual_score")
|
||
|
||
treatment RiskTreatment @default(MITIGATE)
|
||
status RiskStatus @default(OPEN)
|
||
ownerId String? @map("owner_id")
|
||
processId String? @map("process_id")
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
createdBy String? @map("created_by")
|
||
|
||
owner User? @relation("riskOwner", fields: [ownerId], references: [id])
|
||
process Process? @relation(fields: [processId], references: [id])
|
||
riskAssets RiskAsset[]
|
||
riskMeasures RiskMeasure[]
|
||
|
||
@@unique([tenantId, refNo])
|
||
@@index([tenantId])
|
||
@@index([tenantId, status])
|
||
@@map("risks")
|
||
}
|
||
|
||
model RiskAsset {
|
||
id String @id @default(cuid())
|
||
tenantId String @map("tenant_id")
|
||
riskId String @map("risk_id")
|
||
assetId String @map("asset_id")
|
||
|
||
risk Risk @relation(fields: [riskId], references: [id], onDelete: Cascade)
|
||
asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([riskId, assetId])
|
||
@@index([tenantId])
|
||
@@map("risk_assets")
|
||
}
|
||
|
||
// ── Maßnahmen (SPEC §4.4) ───────────────────────────────────────────────────
|
||
|
||
enum MeasureStatus {
|
||
OPEN
|
||
IN_PROGRESS
|
||
DONE
|
||
}
|
||
|
||
enum MeasurePriority {
|
||
LOW
|
||
MEDIUM
|
||
HIGH
|
||
}
|
||
|
||
model Measure {
|
||
id String @id @default(cuid())
|
||
tenantId String @map("tenant_id")
|
||
refNo Int @map("ref_no") // Anzeige als "M-001"
|
||
|
||
title String
|
||
description String?
|
||
status MeasureStatus @default(OPEN)
|
||
priority MeasurePriority @default(MEDIUM)
|
||
ownerId String? @map("owner_id")
|
||
dueDate DateTime? @map("due_date")
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
createdBy String? @map("created_by")
|
||
|
||
owner User? @relation("measureOwner", fields: [ownerId], references: [id])
|
||
riskMeasures RiskMeasure[]
|
||
|
||
@@unique([tenantId, refNo])
|
||
@@index([tenantId])
|
||
@@index([tenantId, status])
|
||
@@map("measures")
|
||
}
|
||
|
||
// Verknüpfung Risiko ↔ Maßnahme inkl. erwarteter Risikominderung:
|
||
// Das Rest-Risiko wird aus likelihood/impact minus der Summe der
|
||
// Minderungen aller verknüpften Maßnahmen berechnet (siehe src/server/risk-calc.ts).
|
||
model RiskMeasure {
|
||
id String @id @default(cuid())
|
||
tenantId String @map("tenant_id")
|
||
riskId String @map("risk_id")
|
||
measureId String @map("measure_id")
|
||
|
||
// Minderung je Dimension (0,00–4,00) — mehrere Maßnahmen summieren sich,
|
||
// bis ein voller Punkt (oder mehr) erreicht ist
|
||
reductionLikelihood Float @default(0) @map("reduction_likelihood")
|
||
reductionImpact Float @default(0) @map("reduction_impact")
|
||
|
||
risk Risk @relation(fields: [riskId], references: [id], onDelete: Cascade)
|
||
measure Measure @relation(fields: [measureId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([riskId, measureId])
|
||
@@index([tenantId])
|
||
@@map("risk_measures")
|
||
}
|
||
|
||
// Globale Kataloge (SPEC §5: Threat/Vulnerability, mandantenübergreifend).
|
||
// Risiken speichern weiterhin Freitext — der Katalog liefert Vorschläge (datalist),
|
||
// eigene Formulierungen bleiben möglich.
|
||
model Threat {
|
||
id String @id @default(cuid())
|
||
name String @unique
|
||
|
||
@@map("threats")
|
||
}
|
||
|
||
model Vulnerability {
|
||
id String @id @default(cuid())
|
||
name String @unique
|
||
|
||
@@map("vulnerabilities")
|
||
}
|
||
|
||
// ── Lieferantenmanagement (SPEC §4.14, VDA-ISA 2027 Kap. 6, NIS2 Art. 21(2)(d)) ──
|
||
|
||
enum SupplierStatus {
|
||
ACTIVE
|
||
ONBOARDING
|
||
UNDER_REVIEW
|
||
OFFBOARDED
|
||
}
|
||
|
||
enum AssessmentType {
|
||
QUESTIONNAIRE
|
||
SELF_ASSESSMENT
|
||
AUDIT
|
||
}
|
||
|
||
enum AssessmentStatus {
|
||
SENT
|
||
RECEIVED
|
||
EVALUATED
|
||
OVERDUE
|
||
}
|
||
|
||
enum EvidenceKind {
|
||
CERTIFICATE
|
||
TISAX_LABEL
|
||
ATTESTATION
|
||
AUDIT_REPORT
|
||
SELF_ASSESSMENT
|
||
}
|
||
|
||
enum ResponsibleParty {
|
||
CLIENT
|
||
SUPPLIER
|
||
SHARED
|
||
}
|
||
|
||
model Supplier {
|
||
id String @id @default(cuid())
|
||
tenantId String @map("tenant_id")
|
||
refNo Int @map("ref_no") // Anzeige "L-001"
|
||
|
||
name String
|
||
sector String?
|
||
services String? // erbrachte Leistung / IT-Services
|
||
criticality Int @default(1) // 1–4
|
||
dataCategories String[] @default([]) @map("data_categories")
|
||
// Schutzbedarf der verarbeiteten Informationen (C/I/A 1–4)
|
||
confidentiality Int @default(1)
|
||
integrity Int @default(1)
|
||
availability Int @default(1)
|
||
nis2Relevant Boolean @default(false) @map("nis2_relevant") // Teil der Lieferkette
|
||
status SupplierStatus @default(ACTIVE)
|
||
contact String?
|
||
nextReview DateTime? @map("next_review")
|
||
notes String?
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
createdBy String? @map("created_by")
|
||
|
||
assessments SupplierAssessment[]
|
||
contracts Contract[]
|
||
ndas Nda[]
|
||
evidence SupplierEvidence[]
|
||
responsibilities ServiceResponsibility[]
|
||
subcontractors Subcontractor[]
|
||
decisions ManagementDecision[]
|
||
controlMaturity SupplierControlMaturity[]
|
||
assetLinks SupplierAsset[]
|
||
|
||
@@unique([tenantId, refNo])
|
||
@@index([tenantId])
|
||
@@map("suppliers")
|
||
}
|
||
|
||
// Lieferant ↔ Asset (welcher Dienstleister betrifft welche Assets) — speist den Graph
|
||
model SupplierAsset {
|
||
id String @id @default(cuid())
|
||
tenantId String @map("tenant_id")
|
||
supplierId String @map("supplier_id")
|
||
assetId String @map("asset_id")
|
||
|
||
supplier Supplier @relation(fields: [supplierId], references: [id], onDelete: Cascade)
|
||
asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([supplierId, assetId])
|
||
@@index([tenantId])
|
||
@@map("supplier_assets")
|
||
}
|
||
|
||
model SupplierAssessment {
|
||
id String @id @default(cuid())
|
||
tenantId String @map("tenant_id")
|
||
supplierId String @map("supplier_id")
|
||
type AssessmentType
|
||
status AssessmentStatus @default(SENT)
|
||
score Int? // 0–100 Scoring
|
||
date DateTime?
|
||
nextReview DateTime? @map("next_review")
|
||
result String?
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
|
||
supplier Supplier @relation(fields: [supplierId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([tenantId])
|
||
@@map("supplier_assessments")
|
||
}
|
||
|
||
model Contract {
|
||
id String @id @default(cuid())
|
||
tenantId String @map("tenant_id")
|
||
supplierId String @map("supplier_id")
|
||
type String @default("service") // service | av_dpa | nda | sla …
|
||
avDpa Boolean @default(false) @map("av_dpa") // AV/DPA (DSGVO Art. 28)
|
||
securityClauses Boolean @default(false) @map("security_clauses")
|
||
flowdown Boolean @default(false) // Weitergabe an Subunternehmer
|
||
customerTransparency Boolean @default(false) @map("customer_transparency")
|
||
validFrom DateTime? @map("valid_from")
|
||
validTo DateTime? @map("valid_to")
|
||
reference String?
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
|
||
supplier Supplier @relation(fields: [supplierId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([tenantId])
|
||
@@map("contracts")
|
||
}
|
||
|
||
model Nda {
|
||
id String @id @default(cuid())
|
||
tenantId String @map("tenant_id")
|
||
supplierId String @map("supplier_id")
|
||
parties String?
|
||
infoScope String? @map("info_scope")
|
||
subject String?
|
||
validFrom DateTime? @map("valid_from")
|
||
validTo DateTime? @map("valid_to")
|
||
obligations String?
|
||
extensionStatus String? @map("extension_status") // z. B. offen | verlängert | ausgelaufen
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
|
||
supplier Supplier @relation(fields: [supplierId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([tenantId])
|
||
@@map("ndas")
|
||
}
|
||
|
||
model SupplierEvidence {
|
||
id String @id @default(cuid())
|
||
tenantId String @map("tenant_id")
|
||
supplierId String @map("supplier_id")
|
||
kind EvidenceKind
|
||
name String?
|
||
protectsCia String? @map("protects_cia") // z. B. "C,I,A"
|
||
validTo DateTime? @map("valid_to")
|
||
adequacyChecked Boolean @default(false) @map("adequacy_checked")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
|
||
supplier Supplier @relation(fields: [supplierId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([tenantId])
|
||
@@map("supplier_evidence")
|
||
}
|
||
|
||
model ServiceResponsibility {
|
||
id String @id @default(cuid())
|
||
tenantId String @map("tenant_id")
|
||
supplierId String @map("supplier_id")
|
||
itService String @map("it_service")
|
||
requirement String
|
||
responsibleParty ResponsibleParty @default(SHARED) @map("responsible_party")
|
||
isaApplicability String? @map("isa_applicability")
|
||
evidence String?
|
||
integratedLocalControls String? @map("integrated_local_controls")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
|
||
supplier Supplier @relation(fields: [supplierId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([tenantId])
|
||
@@map("service_responsibilities")
|
||
}
|
||
|
||
model Subcontractor {
|
||
id String @id @default(cuid())
|
||
tenantId String @map("tenant_id")
|
||
supplierId String @map("supplier_id")
|
||
name String
|
||
flowdownObligation Boolean @default(false) @map("flowdown_obligation")
|
||
|
||
supplier Supplier @relation(fields: [supplierId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([tenantId])
|
||
@@map("subcontractors")
|
||
}
|
||
|
||
model ManagementDecision {
|
||
id String @id @default(cuid())
|
||
tenantId String @map("tenant_id")
|
||
supplierId String @map("supplier_id")
|
||
reasonNoAudit String @map("reason_no_audit")
|
||
decision String
|
||
decidedBy String? @map("decided_by")
|
||
date DateTime @default(now())
|
||
recordRef String? @map("record_ref")
|
||
|
||
supplier Supplier @relation(fields: [supplierId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([tenantId])
|
||
@@map("management_decisions")
|
||
}
|
||
|
||
// VDA-ISA 2027 Kapitel 6 — Supplier Relationships (globaler Katalog, per Import versioniert)
|
||
model SupplierControl {
|
||
id String @id @default(cuid())
|
||
ref String @unique // 6.1.1 / 6.1.2 / 6.1.3
|
||
title String
|
||
objective String
|
||
|
||
mustReq String? @map("must_req")
|
||
shouldReq String? @map("should_req")
|
||
highReq String? @map("high_req") // Additional for high protection
|
||
veryHighReq String? @map("very_high_req") // Additional for very high protection
|
||
simplifiedGroupAssessment Boolean @default(false) @map("simplified_group_assessment")
|
||
targetMaturity Int @default(3) @map("target_maturity")
|
||
references String? // ISO 27001, NIST CSF, BSI …
|
||
|
||
maturity SupplierControlMaturity[]
|
||
|
||
@@map("supplier_controls")
|
||
}
|
||
|
||
model SupplierControlMaturity {
|
||
id String @id @default(cuid())
|
||
tenantId String @map("tenant_id")
|
||
supplierId String @map("supplier_id")
|
||
controlId String @map("control_id")
|
||
maturity Int @default(0) // 0–5
|
||
notes String?
|
||
|
||
supplier Supplier @relation(fields: [supplierId], references: [id], onDelete: Cascade)
|
||
control SupplierControl @relation(fields: [controlId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([supplierId, controlId])
|
||
@@index([tenantId])
|
||
@@map("supplier_control_maturity")
|
||
}
|
||
|
||
model AuditLog {
|
||
id String @id @default(cuid())
|
||
tenantId String @map("tenant_id")
|
||
actorId String? @map("actor_id")
|
||
action String // create | update | delete | login | export | …
|
||
entity String // z. B. asset, risk, user
|
||
entityId String? @map("entity_id")
|
||
before Json?
|
||
after Json?
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
|
||
tenant Tenant @relation(fields: [tenantId], references: [id])
|
||
|
||
@@index([tenantId, entity, entityId])
|
||
@@index([tenantId, createdAt])
|
||
@@map("audit_logs")
|
||
}
|