generator client { provider = "prisma-client" output = "../server/generated/prisma" } datasource db { provider = "postgresql" } enum TeamRole { OWNER MEMBER } enum MessageDirection { IN OUT } enum MessageChannel { TELEGRAM WHATSAPP INSTAGRAM PHONE EMAIL INTERNAL } enum ContactMessageKind { MESSAGE CALL } enum ChatRole { USER ASSISTANT SYSTEM } enum OmniMessageStatus { PENDING SENT FAILED DELIVERED READ } enum FeedCardDecision { PENDING ACCEPTED REJECTED } enum WorkspaceDocumentType { Regulation Playbook Policy Template } enum ClientTimelineContentType { CALENDAR_EVENT DOCUMENT RECOMMENDATION } model Team { id String @id @default(cuid()) name String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt members TeamMember[] contacts Contact[] calendarEvents CalendarEvent[] deals Deal[] aiConversations AiConversation[] aiMessages AiMessage[] omniThreads OmniThread[] omniMessages OmniMessage[] omniIdentities OmniContactIdentity[] telegramBusinessConnections TelegramBusinessConnection[] feedCards FeedCard[] contactPins ContactPin[] documents WorkspaceDocument[] clientTimelineEntries ClientTimelineEntry[] contactInboxes ContactInbox[] contactInboxPreferences ContactInboxPreference[] contactThreadReads ContactThreadRead[] } model User { id String @id @default(cuid()) phone String @unique passwordHash String email String? @unique name String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt memberships TeamMember[] aiConversations AiConversation[] @relation("ConversationCreator") aiMessages AiMessage[] @relation("ChatAuthor") contactInboxPreferences ContactInboxPreference[] contactThreadReads ContactThreadRead[] } model TeamMember { id String @id @default(cuid()) teamId String userId String role TeamRole @default(MEMBER) createdAt DateTime @default(now()) team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([teamId, userId]) @@index([userId]) } model Contact { id String @id @default(cuid()) teamId String name String avatarUrl String? email String? phone String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) note ContactNote? messages ContactMessage[] events CalendarEvent[] deals Deal[] feedCards FeedCard[] pins ContactPin[] omniThreads OmniThread[] omniMessages OmniMessage[] omniIdentities OmniContactIdentity[] contactInboxes ContactInbox[] clientTimelineEntries ClientTimelineEntry[] contactThreadReads ContactThreadRead[] @@index([teamId, updatedAt]) } model ContactThreadRead { id String @id @default(cuid()) teamId String userId String contactId String readAt DateTime @default(now()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade) @@unique([userId, contactId]) @@index([teamId, userId]) } model ContactNote { id String @id @default(cuid()) contactId String @unique content String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade) } model ContactMessage { id String @id @default(cuid()) contactId String contactInboxId String? kind ContactMessageKind @default(MESSAGE) direction MessageDirection channel MessageChannel content String audioUrl String? durationSec Int? waveformJson Json? transcriptJson Json? occurredAt DateTime @default(now()) createdAt DateTime @default(now()) contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade) contactInbox ContactInbox? @relation(fields: [contactInboxId], references: [id], onDelete: SetNull) @@index([contactId, occurredAt]) @@index([contactInboxId, occurredAt]) } model ContactInbox { id String @id @default(cuid()) teamId String contactId String channel MessageChannel sourceExternalId String title String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade) messages ContactMessage[] preferences ContactInboxPreference[] @@unique([teamId, channel, sourceExternalId]) @@index([contactId, updatedAt]) @@index([teamId, updatedAt]) } model ContactInboxPreference { id String @id @default(cuid()) teamId String userId String contactInboxId String isHidden Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) contactInbox ContactInbox @relation(fields: [contactInboxId], references: [id], onDelete: Cascade) @@unique([userId, contactInboxId]) @@index([teamId, userId, isHidden]) } model OmniContactIdentity { id String @id @default(cuid()) teamId String contactId String channel MessageChannel externalId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade) @@unique([teamId, channel, externalId]) @@index([contactId]) @@index([teamId, updatedAt]) } model OmniThread { id String @id @default(cuid()) teamId String contactId String channel MessageChannel externalChatId String businessConnectionId String? title String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade) messages OmniMessage[] @@unique([teamId, channel, externalChatId, businessConnectionId]) @@index([teamId, updatedAt]) @@index([contactId, updatedAt]) } model OmniMessage { id String @id @default(cuid()) teamId String contactId String threadId String direction MessageDirection channel MessageChannel status OmniMessageStatus @default(PENDING) text String providerMessageId String? providerUpdateId String? rawJson Json? occurredAt DateTime @default(now()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade) thread OmniThread @relation(fields: [threadId], references: [id], onDelete: Cascade) @@unique([threadId, providerMessageId]) @@index([teamId, occurredAt]) @@index([threadId, occurredAt]) } model TelegramBusinessConnection { id String @id @default(cuid()) teamId String businessConnectionId String isEnabled Boolean? canReply Boolean? rawJson Json? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) @@unique([teamId, businessConnectionId]) @@index([teamId, updatedAt]) } model CalendarEvent { id String @id @default(cuid()) teamId String contactId String? title String startsAt DateTime endsAt DateTime? note String? isArchived Boolean @default(false) archiveNote String? archivedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) contact Contact? @relation(fields: [contactId], references: [id], onDelete: SetNull) @@index([startsAt]) @@index([contactId, startsAt]) @@index([teamId, startsAt]) } model Deal { id String @id @default(cuid()) teamId String contactId String title String stage String amount Int? nextStep String? summary String? currentStepId String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade) steps DealStep[] @@index([teamId, updatedAt]) @@index([contactId, updatedAt]) @@index([currentStepId]) } model DealStep { id String @id @default(cuid()) dealId String title String description String? status String @default("todo") dueAt DateTime? order Int @default(0) completedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deal Deal @relation(fields: [dealId], references: [id], onDelete: Cascade) @@index([dealId, order]) @@index([status, dueAt]) } model AiConversation { id String @id @default(cuid()) teamId String createdByUserId String title String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) createdByUser User @relation("ConversationCreator", fields: [createdByUserId], references: [id], onDelete: Cascade) messages AiMessage[] @@index([teamId, updatedAt]) @@index([createdByUserId]) @@map("ChatConversation") } model AiMessage { id String @id @default(cuid()) teamId String conversationId String authorUserId String? role ChatRole text String planJson Json? createdAt DateTime @default(now()) team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) conversation AiConversation @relation(fields: [conversationId], references: [id], onDelete: Cascade) authorUser User? @relation("ChatAuthor", fields: [authorUserId], references: [id], onDelete: SetNull) @@index([createdAt]) @@index([teamId, createdAt]) @@index([conversationId, createdAt]) @@map("ChatMessage") } model FeedCard { id String @id @default(cuid()) teamId String contactId String? happenedAt DateTime text String proposalJson Json decision FeedCardDecision @default(PENDING) decisionNote String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) contact Contact? @relation(fields: [contactId], references: [id], onDelete: SetNull) @@index([teamId, happenedAt]) @@index([contactId, happenedAt]) } model ContactPin { id String @id @default(cuid()) teamId String contactId String text String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade) @@index([teamId, updatedAt]) @@index([contactId, updatedAt]) } model WorkspaceDocument { id String @id @default(cuid()) teamId String title String type WorkspaceDocumentType owner String scope String summary String body String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) @@index([teamId, updatedAt]) } model ClientTimelineEntry { id String @id @default(cuid()) teamId String contactId String contentType ClientTimelineContentType contentId String datetime DateTime @default(now()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade) @@unique([teamId, contentType, contentId]) @@index([teamId, contactId, datetime]) @@index([contactId, datetime]) }