Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/prisma/schema/adminTelephony.prisma
T

356 lines
13 KiB
Plaintext

// VA-P28d: tenant-less telephony for HiveGrid's own admin sales workspace.
//
// Aircall credentials remain in AdminDataConnection. Every operator-owned row
// carries adminUserId so Postgres can enforce the platform-admin user context;
// worker writes use the reviewed worker branch of the same policies.
enum AdminCallDirection {
inbound
outbound
}
enum AdminCallStatus {
ringing
answered
completed
missed
failed
}
enum AdminCallMatchStatus {
unmatched
unique
ambiguous
confirmed
}
enum AdminCallParticipantKind {
admin
contact
lead
external
}
enum AdminCallRecordingStatus {
pending
processing
clean
rejected
expired
deleted
}
enum AdminDialerListStatus {
draft
active
paused
completed
}
enum AdminDialerEntryStatus {
pending
active
completed
skipped
blocked
}
enum AdminOutboundCallRequestStatus {
pending
processing
accepted
failed
cancelled
}
model AdminPhoneConnection {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
archivedAt DateTime?
adminUserId String
connectionId String @unique
providerCompanyId String?
providerUserId String?
providerUserEmail String?
defaultPhoneNumberId String?
webhookTokenHash String? @unique
webhookVerifiedAt DateTime?
lastEventAt DateTime?
adminUser AdminUser @relation(fields: [adminUserId], references: [id], onDelete: Restrict)
connection AdminDataConnection @relation(fields: [connectionId], references: [id], onDelete: Restrict)
defaultPhoneNumber AdminPhoneNumber? @relation("AdminDefaultPhoneNumber", fields: [defaultPhoneNumberId], references: [id], onDelete: SetNull)
phoneNumbers AdminPhoneNumber[] @relation("AdminPhoneConnectionNumbers")
calls AdminCallSession[]
outboundRequests AdminOutboundCallRequest[]
@@index([adminUserId, archivedAt], name: "admin_phone_connection_owner_archive_idx")
@@index([providerCompanyId, archivedAt], name: "admin_phone_connection_company_idx")
}
model AdminPhoneNumber {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
archivedAt DateTime?
adminUserId String
phoneConnectionId String
providerNumberId String
label String
e164 String
countryCode String?
selected Boolean @default(true)
phoneConnection AdminPhoneConnection @relation("AdminPhoneConnectionNumbers", fields: [phoneConnectionId], references: [id], onDelete: Cascade)
defaultFor AdminPhoneConnection[] @relation("AdminDefaultPhoneNumber")
@@unique([phoneConnectionId, providerNumberId], name: "admin_phone_number_provider_key")
@@index([adminUserId, selected, archivedAt], name: "admin_phone_number_owner_selected_idx")
@@index([e164, archivedAt], name: "admin_phone_number_e164_idx")
}
model AdminTelephonyPolicy {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
adminUserId String @unique
outboundEnabled Boolean @default(false)
recordingEnabled Boolean @default(false)
recordingConsentText String?
recordingConsentVersion String?
recordingConsentAt DateTime?
recordingRetentionDays Int @default(90)
dncEnforced Boolean @default(true)
dailyCallLimit Int?
workDayStartMinutes Int?
workDayEndMinutes Int?
workDays Int[] @default([])
timeZone String?
@@index([outboundEnabled, adminUserId], name: "admin_telephony_policy_outbound_idx")
@@index([recordingEnabled, adminUserId], name: "admin_telephony_policy_recording_idx")
}
model AdminCallDisposition {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
archivedAt DateTime?
code String @unique
label String
position Int @unique
active Boolean @default(true)
isSystem Boolean @default(true)
calls AdminCallSession[]
@@index([active, archivedAt, position], name: "admin_call_disposition_active_idx")
}
model AdminCallSession {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
adminUserId String
phoneConnectionId String
providerCallId String
direction AdminCallDirection
status AdminCallStatus
providerUserId String?
fromNumber String
toNumber String
externalNumber String
startedAt DateTime
answeredAt DateTime?
endedAt DateTime?
durationSeconds Int?
contactId String?
leadId String?
dealId String?
matchStatus AdminCallMatchStatus @default(unmatched)
matchConfidenceBasisPoints Int?
note String?
dispositionId String?
dispositionCodeSnapshot String?
dispositionLabelSnapshot String?
callbackTaskId String? @unique
recordingOffered Boolean @default(false)
recordingConsentConfirmedAt DateTime?
phoneConnection AdminPhoneConnection @relation(fields: [phoneConnectionId], references: [id], onDelete: Restrict)
contact AdminContact? @relation(fields: [contactId], references: [id], onDelete: SetNull)
lead AdminLead? @relation(fields: [leadId], references: [id], onDelete: SetNull)
deal AdminDeal? @relation(fields: [dealId], references: [id], onDelete: SetNull)
disposition AdminCallDisposition? @relation(fields: [dispositionId], references: [id], onDelete: SetNull)
callbackTask AdminTask? @relation(fields: [callbackTaskId], references: [id], onDelete: SetNull)
participants AdminCallParticipant[]
events AdminCallEvent[]
matchCandidates AdminCallMatchCandidate[]
recording AdminCallRecording?
dialerEntry AdminDialerEntry?
activities AdminActivity[]
@@unique([phoneConnectionId, providerCallId], name: "admin_call_provider_key")
@@index([adminUserId, startedAt(sort: Desc)], name: "admin_call_owner_started_idx")
@@index([contactId, startedAt(sort: Desc)], name: "admin_call_contact_started_idx")
@@index([leadId, startedAt(sort: Desc)], name: "admin_call_lead_started_idx")
@@index([dealId, startedAt(sort: Desc)], name: "admin_call_deal_started_idx")
@@index([matchStatus, startedAt(sort: Desc)], name: "admin_call_match_queue_idx")
@@index([status, startedAt], name: "admin_call_status_started_idx")
}
model AdminCallParticipant {
id String @id @default(cuid())
createdAt DateTime @default(now())
adminUserId String
callSessionId String
kind AdminCallParticipantKind
externalId String?
displayName String?
phone String
callSession AdminCallSession @relation(fields: [callSessionId], references: [id], onDelete: Cascade)
@@unique([callSessionId, kind, phone], name: "admin_call_participant_identity_key")
@@index([adminUserId, createdAt(sort: Desc)], name: "admin_call_participant_owner_idx")
}
model AdminCallEvent {
id String @id @default(cuid())
createdAt DateTime @default(now())
adminUserId String
callSessionId String
dedupeKey String @unique
providerEvent String
status AdminCallStatus
occurredAt DateTime
durationSeconds Int?
callSession AdminCallSession @relation(fields: [callSessionId], references: [id], onDelete: Cascade)
@@index([adminUserId, occurredAt(sort: Desc)], name: "admin_call_event_owner_time_idx")
@@index([callSessionId, occurredAt], name: "admin_call_event_session_time_idx")
}
model AdminCallMatchCandidate {
id String @id @default(cuid())
createdAt DateTime @default(now())
adminUserId String
callSessionId String
contactId String?
leadId String?
confidenceBasisPoints Int
matchReason String
callSession AdminCallSession @relation(fields: [callSessionId], references: [id], onDelete: Cascade)
contact AdminContact? @relation(fields: [contactId], references: [id], onDelete: Cascade)
lead AdminLead? @relation(fields: [leadId], references: [id], onDelete: Cascade)
@@unique([callSessionId, contactId], name: "admin_call_match_contact_key")
@@unique([callSessionId, leadId], name: "admin_call_match_lead_key")
@@index([adminUserId, createdAt(sort: Desc)], name: "admin_call_match_candidate_owner_idx")
}
model AdminCallRecording {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
adminUserId String
callSessionId String @unique
status AdminCallRecordingStatus @default(pending)
providerUrlWrappedDek Bytes?
providerUrlCiphertext String?
providerUrlKeyVersion Int?
storageBucket String?
storageKey String?
encryptedSizeBytes Int?
checksumSha256 String?
wrappedDek Bytes?
iv Bytes?
authTag Bytes?
processingAt DateTime?
attemptCount Int @default(0)
lastErrorCode String?
scannedAt DateTime?
retainedUntil DateTime
expiredAt DateTime?
deletedAt DateTime?
rejectionCode String?
callSession AdminCallSession @relation(fields: [callSessionId], references: [id], onDelete: Restrict)
@@index([adminUserId, createdAt(sort: Desc)], name: "admin_call_recording_owner_idx")
@@index([status, retainedUntil], name: "admin_call_recording_retention_idx")
}
model AdminDialerList {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
archivedAt DateTime?
adminUserId String
name String
status AdminDialerListStatus @default(draft)
currentPosition Int?
startedAt DateTime?
completedAt DateTime?
entries AdminDialerEntry[]
@@index([adminUserId, status, updatedAt(sort: Desc)], name: "admin_dialer_list_owner_status_idx")
}
model AdminDialerEntry {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
adminUserId String
dialerListId String
contactId String
position Int
status AdminDialerEntryStatus @default(pending)
callSessionId String? @unique
blockedReason String?
activatedAt DateTime?
completedAt DateTime?
dialerList AdminDialerList @relation(fields: [dialerListId], references: [id], onDelete: Cascade)
contact AdminContact @relation(fields: [contactId], references: [id], onDelete: Restrict)
callSession AdminCallSession? @relation(fields: [callSessionId], references: [id], onDelete: SetNull)
outboundRequests AdminOutboundCallRequest[]
@@unique([dialerListId, position], name: "admin_dialer_entry_position_key")
@@unique([dialerListId, contactId], name: "admin_dialer_entry_contact_key")
@@index([adminUserId, status, createdAt], name: "admin_dialer_entry_owner_status_idx")
}
model AdminOutboundCallRequest {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
adminUserId String
phoneConnectionId String
contactId String
dealId String?
dialerEntryId String?
idempotencyKey String @unique
destinationE164 String
status AdminOutboundCallRequestStatus @default(pending)
requestedAt DateTime @default(now())
processingAt DateTime?
acceptedAt DateTime?
failedAt DateTime?
attemptCount Int @default(0)
errorCode String?
phoneConnection AdminPhoneConnection @relation(fields: [phoneConnectionId], references: [id], onDelete: Restrict)
contact AdminContact @relation(fields: [contactId], references: [id], onDelete: Restrict)
deal AdminDeal? @relation(fields: [dealId], references: [id], onDelete: SetNull)
dialerEntry AdminDialerEntry? @relation(fields: [dialerEntryId], references: [id], onDelete: SetNull)
@@index([adminUserId, status, requestedAt], name: "admin_outbound_call_owner_status_idx")
@@index([status, requestedAt], name: "admin_outbound_call_worker_queue_idx")
@@index([contactId, requestedAt(sort: Desc)], name: "admin_outbound_call_contact_idx")
}