generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } enum UserRole { owner admin sales support } enum DealStatus { open won lost } enum ActivityType { call meeting task note } enum ActivityStatus { open done canceled } enum CustomFieldEntity { person company deal } enum CustomFieldType { text number date boolean select } enum QuoteStatus { draft sent accepted rejected expired } enum InvoiceStatus { draft issued partially_paid paid canceled } enum PaymentMethod { cash transfer card check other } enum InstallmentStatus { pending paid canceled } enum TicketStatus { open in_progress waiting resolved closed } enum TicketPriority { low normal high urgent } enum AutomationTrigger { deal_won ticket_resolved } enum AutomationAction { create_activity create_notification add_loyalty_points } enum NoteAiSuggestionStatus { pending applied dismissed failed } enum MemorySourceType { note manual } model Organization { id String @id @default(cuid()) name String slug String @unique baseCurrency String @default("IRR") enabledCurrencies String[] @default(["IRR", "USD", "EUR"]) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt users User[] companies Company[] people Person[] pipelines Pipeline[] deals Deal[] activities Activity[] notes Note[] customFields CustomFieldDefinition[] products Product[] notifications Notification[] noteAttachments NoteAttachment[] leadForms LeadForm[] quotes Quote[] invoices Invoice[] salesGoals SalesGoal[] exchangeRates ExchangeRate[] tickets Ticket[] faqArticles FaqArticle[] portalOtps PortalOtp[] projectBoards ProjectBoard[] projectCards ProjectCard[] loyaltyTiers LoyaltyTier[] automationRules AutomationRule[] memoryItems MemoryItem[] noteAiSuggestions NoteAiSuggestion[] } model User { id String @id @default(cuid()) email String passwordHash String fullName String role UserRole @default(sales) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) ownedDeals Deal[] @relation("DealOwner") ownedQuotes Quote[] @relation("QuoteOwner") ownedInvoices Invoice[] @relation("InvoiceOwner") ownedPeople Person[] @relation("PersonOwner") ownedCompanies Company[] @relation("CompanyOwner") ownedProjectCards ProjectCard[] @relation("ProjectCardOwner") activities Activity[] @relation("ActivityAssignee") notes Note[] @relation("NoteAuthor") noteAttachments NoteAttachment[] @relation("NoteAttachmentUploader") notifications Notification[] salesGoals SalesGoal[] @relation("SalesGoalUser") assignedTickets Ticket[] @relation("TicketAssignee") reportedTickets Ticket[] @relation("TicketReporter") ticketMessages TicketMessage[] @relation("TicketMessageAuthor") confirmedMemories MemoryItem[] @relation("MemoryConfirmedBy") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([organizationId, email]) @@index([organizationId]) } model Company { id String @id @default(cuid()) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) name String nationalId String? phone String? email String? website String? address String? notes String? customFields Json @default("{}") people Person[] deals Deal[] activities Activity[] smartNotes Note[] quotes Quote[] invoices Invoice[] ownerId String? owner User? @relation("CompanyOwner", fields: [ownerId], references: [id], onDelete: SetNull) tickets Ticket[] projectCards ProjectCard[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([organizationId]) @@index([organizationId, name]) @@index([organizationId, email]) @@index([organizationId, phone]) @@index([ownerId]) } model Person { id String @id @default(cuid()) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) companyId String? company Company? @relation(fields: [companyId], references: [id], onDelete: SetNull) firstName String lastName String email String? phone String? mobile String? position String? notes String? customFields Json @default("{}") loyaltyPoints Int @default(0) loyaltyTierId String? loyaltyTier LoyaltyTier? @relation(fields: [loyaltyTierId], references: [id], onDelete: SetNull) deals Deal[] activities Activity[] smartNotes Note[] quotes Quote[] invoices Invoice[] ownerId String? owner User? @relation("PersonOwner", fields: [ownerId], references: [id], onDelete: SetNull) tickets Ticket[] projectCards ProjectCard[] memoryItems MemoryItem[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([organizationId]) @@index([organizationId, email]) @@index([organizationId, mobile]) @@index([organizationId, lastName, firstName]) @@index([organizationId, loyaltyPoints]) @@index([companyId]) @@index([ownerId]) @@index([loyaltyTierId]) } model Pipeline { id String @id @default(cuid()) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) name String isDefault Boolean @default(false) stages PipelineStage[] deals Deal[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([organizationId]) @@index([organizationId, isDefault]) } model PipelineStage { id String @id @default(cuid()) pipelineId String pipeline Pipeline @relation(fields: [pipelineId], references: [id], onDelete: Cascade) name String position Int isWon Boolean @default(false) isLost Boolean @default(false) deals Deal[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([pipelineId, position]) } model Deal { id String @id @default(cuid()) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) pipelineId String pipeline Pipeline @relation(fields: [pipelineId], references: [id], onDelete: Cascade) stageId String stage PipelineStage @relation(fields: [stageId], references: [id], onDelete: Restrict) title String amount Decimal? @db.Decimal(18, 2) currency String @default("IRR") status DealStatus @default(open) source String? lostReason String? notes String? tags String[] @default([]) isStale Boolean @default(false) isRotten Boolean @default(false) customFields Json @default("{}") expectedCloseAt DateTime? personId String? person Person? @relation(fields: [personId], references: [id], onDelete: SetNull) companyId String? company Company? @relation(fields: [companyId], references: [id], onDelete: SetNull) ownerId String? owner User? @relation("DealOwner", fields: [ownerId], references: [id], onDelete: SetNull) position Int @default(0) activities Activity[] smartNotes Note[] lineItems DealLineItem[] quotes Quote[] invoices Invoice[] tickets Ticket[] projectCards ProjectCard[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([organizationId]) @@index([pipelineId, stageId]) @@index([stageId, position]) @@index([personId]) @@index([companyId]) @@index([ownerId]) } model Activity { id String @id @default(cuid()) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) type ActivityType @default(task) status ActivityStatus @default(open) title String description String? dueAt DateTime? completedAt DateTime? personId String? person Person? @relation(fields: [personId], references: [id], onDelete: SetNull) companyId String? company Company? @relation(fields: [companyId], references: [id], onDelete: SetNull) dealId String? deal Deal? @relation(fields: [dealId], references: [id], onDelete: SetNull) assigneeId String? assignee User? @relation("ActivityAssignee", fields: [assigneeId], references: [id], onDelete: SetNull) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([organizationId, status, dueAt]) @@index([organizationId, assigneeId]) @@index([personId]) @@index([companyId]) @@index([dealId]) } model Note { id String @id @default(cuid()) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) title String? body String noteType String @default("general") pinned Boolean @default(false) color String? @default("default") tags String[] @default([]) reminderAt DateTime? personId String? person Person? @relation(fields: [personId], references: [id], onDelete: SetNull) companyId String? company Company? @relation(fields: [companyId], references: [id], onDelete: SetNull) dealId String? deal Deal? @relation(fields: [dealId], references: [id], onDelete: SetNull) authorId String? author User? @relation("NoteAuthor", fields: [authorId], references: [id], onDelete: SetNull) attachments NoteAttachment[] aiSuggestion NoteAiSuggestion? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([organizationId, pinned, updatedAt]) @@index([organizationId, noteType, updatedAt]) @@index([organizationId, reminderAt]) @@index([personId]) @@index([companyId]) @@index([dealId]) @@index([authorId]) } model NoteAttachment { id String @id @default(cuid()) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) noteId String note Note @relation(fields: [noteId], references: [id], onDelete: Cascade) fileName String mimeType String sizeBytes Int storageKey String uploadedById String? uploadedBy User? @relation("NoteAttachmentUploader", fields: [uploadedById], references: [id], onDelete: SetNull) createdAt DateTime @default(now()) @@index([noteId, createdAt]) @@index([organizationId]) @@index([uploadedById]) } model NoteAiSuggestion { id String @id @default(cuid()) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) noteId String @unique note Note @relation(fields: [noteId], references: [id], onDelete: Cascade) status NoteAiSuggestionStatus @default(pending) payload Json? errorMessage String? analyzedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([organizationId, status]) } model MemoryItem { id String @id @default(cuid()) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) personId String person Person @relation(fields: [personId], references: [id], onDelete: Cascade) content String sourceType MemorySourceType @default(manual) sourceId String? confirmedById String? confirmedBy User? @relation("MemoryConfirmedBy", fields: [confirmedById], references: [id], onDelete: SetNull) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([organizationId, personId, updatedAt]) @@index([personId]) @@index([confirmedById]) } model CustomFieldDefinition { id String @id @default(cuid()) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) entityType CustomFieldEntity key String label String fieldType CustomFieldType @default(text) options String[] @default([]) required Boolean @default(false) position Int @default(0) isActive Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([organizationId, entityType, key]) @@index([organizationId, entityType, position]) } model Product { id String @id @default(cuid()) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) name String sku String? description String? unitPrice Decimal? @db.Decimal(18, 2) currency String @default("IRR") isActive Boolean @default(true) lineItems DealLineItem[] quoteLineItems QuoteLineItem[] invoiceLineItems InvoiceLineItem[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([organizationId, name]) @@index([organizationId, isActive]) } model DealLineItem { id String @id @default(cuid()) dealId String deal Deal @relation(fields: [dealId], references: [id], onDelete: Cascade) productId String? product Product? @relation(fields: [productId], references: [id], onDelete: SetNull) name String quantity Decimal @default(1) @db.Decimal(18, 2) unitPrice Decimal @db.Decimal(18, 2) currency String @default("IRR") position Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([dealId, position]) @@index([productId]) } model Notification { id String @id @default(cuid()) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) type String title String body String? link String? entityType String? entityId String? readAt DateTime? createdAt DateTime @default(now()) @@index([userId, readAt, createdAt]) @@index([organizationId, createdAt]) @@index([userId, type, entityId]) } model LeadForm { id String @id @default(cuid()) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) name String publicToken String @unique @default(cuid()) isActive Boolean @default(true) createPerson Boolean @default(true) createDeal Boolean @default(true) pipelineId String? source String? thankYouMessage String? redirectUrl String? assigneeIds String[] @default([]) roundRobinIndex Int @default(0) fields LeadFormField[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([organizationId, updatedAt]) } model LeadFormField { id String @id @default(cuid()) formId String form LeadForm @relation(fields: [formId], references: [id], onDelete: Cascade) key String label String fieldType String @default("text") required Boolean @default(false) mapTo String position Int @default(0) @@unique([formId, key]) @@index([formId, position]) } model Quote { id String @id @default(cuid()) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) number String title String status QuoteStatus @default(draft) currency String @default("IRR") notes String? validUntil DateTime? personId String? person Person? @relation(fields: [personId], references: [id], onDelete: SetNull) companyId String? company Company? @relation(fields: [companyId], references: [id], onDelete: SetNull) dealId String? deal Deal? @relation(fields: [dealId], references: [id], onDelete: SetNull) ownerId String? owner User? @relation("QuoteOwner", fields: [ownerId], references: [id], onDelete: SetNull) lineItems QuoteLineItem[] invoices Invoice[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([organizationId, number]) @@index([organizationId, status, updatedAt]) @@index([personId]) @@index([companyId]) @@index([dealId]) @@index([ownerId]) } model QuoteLineItem { id String @id @default(cuid()) quoteId String quote Quote @relation(fields: [quoteId], references: [id], onDelete: Cascade) productId String? product Product? @relation(fields: [productId], references: [id], onDelete: SetNull) name String quantity Decimal @default(1) @db.Decimal(18, 2) unitPrice Decimal @db.Decimal(18, 2) currency String @default("IRR") position Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([quoteId, position]) @@index([productId]) } model Invoice { id String @id @default(cuid()) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) number String title String status InvoiceStatus @default(draft) currency String @default("IRR") notes String? issuedAt DateTime? dueDate DateTime? personId String? person Person? @relation(fields: [personId], references: [id], onDelete: SetNull) companyId String? company Company? @relation(fields: [companyId], references: [id], onDelete: SetNull) dealId String? deal Deal? @relation(fields: [dealId], references: [id], onDelete: SetNull) quoteId String? quote Quote? @relation(fields: [quoteId], references: [id], onDelete: SetNull) ownerId String? owner User? @relation("InvoiceOwner", fields: [ownerId], references: [id], onDelete: SetNull) lineItems InvoiceLineItem[] payments Payment[] installments InvoiceInstallment[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([organizationId, number]) @@index([organizationId, status, updatedAt]) @@index([personId]) @@index([companyId]) @@index([dealId]) @@index([quoteId]) @@index([ownerId]) } model InvoiceLineItem { id String @id @default(cuid()) invoiceId String invoice Invoice @relation(fields: [invoiceId], references: [id], onDelete: Cascade) productId String? product Product? @relation(fields: [productId], references: [id], onDelete: SetNull) name String quantity Decimal @default(1) @db.Decimal(18, 2) unitPrice Decimal @db.Decimal(18, 2) currency String @default("IRR") position Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([invoiceId, position]) @@index([productId]) } model Payment { id String @id @default(cuid()) invoiceId String invoice Invoice @relation(fields: [invoiceId], references: [id], onDelete: Cascade) amount Decimal @db.Decimal(18, 2) currency String @default("IRR") method PaymentMethod @default(transfer) paidAt DateTime @default(now()) notes String? installmentId String? @unique installment InvoiceInstallment? @relation(fields: [installmentId], references: [id], onDelete: SetNull) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([invoiceId, paidAt]) } model InvoiceInstallment { id String @id @default(cuid()) invoiceId String invoice Invoice @relation(fields: [invoiceId], references: [id], onDelete: Cascade) position Int @default(0) dueDate DateTime amount Decimal @db.Decimal(18, 2) currency String @default("IRR") status InstallmentStatus @default(pending) paidAt DateTime? payment Payment? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([invoiceId, position]) @@index([invoiceId, dueDate]) @@index([status, dueDate]) } model SalesGoal { id String @id @default(cuid()) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) /// null = هدف سازمانی / تیمی userId String? user User? @relation("SalesGoalUser", fields: [userId], references: [id], onDelete: Cascade) title String periodStart DateTime periodEnd DateTime targetAmount Decimal @db.Decimal(18, 2) currency String @default("IRR") notes String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([organizationId, periodStart, periodEnd]) @@index([userId]) } model ExchangeRate { id String @id @default(cuid()) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) /// ارز مبدأ؛ ۱ واحد این ارز = rateToBase واحد ارز پایه currency String rateToBase Decimal @db.Decimal(24, 6) updatedAt DateTime @updatedAt createdAt DateTime @default(now()) @@unique([organizationId, currency]) @@index([organizationId]) } model Ticket { id String @id @default(cuid()) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) number String subject String description String? status TicketStatus @default(open) priority TicketPriority @default(normal) personId String? person Person? @relation(fields: [personId], references: [id], onDelete: SetNull) companyId String? company Company? @relation(fields: [companyId], references: [id], onDelete: SetNull) dealId String? deal Deal? @relation(fields: [dealId], references: [id], onDelete: SetNull) assigneeId String? assignee User? @relation("TicketAssignee", fields: [assigneeId], references: [id], onDelete: SetNull) reporterId String? reporter User? @relation("TicketReporter", fields: [reporterId], references: [id], onDelete: SetNull) resolvedAt DateTime? /// مهلت پاسخ اول بر اساس اولویت slaDueAt DateTime? firstRespondedAt DateTime? csatScore Int? csatComment String? csatAt DateTime? messages TicketMessage[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([organizationId, number]) @@index([organizationId, status, updatedAt]) @@index([organizationId, slaDueAt]) @@index([assigneeId]) @@index([reporterId]) @@index([personId]) @@index([companyId]) @@index([dealId]) } model TicketMessage { id String @id @default(cuid()) ticketId String ticket Ticket @relation(fields: [ticketId], references: [id], onDelete: Cascade) authorId String? author User? @relation("TicketMessageAuthor", fields: [authorId], references: [id], onDelete: SetNull) body String isInternal Boolean @default(false) createdAt DateTime @default(now()) @@index([ticketId, createdAt]) @@index([authorId]) } model FaqArticle { id String @id @default(cuid()) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) question String answer String category String? isPublished Boolean @default(true) position Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([organizationId, isPublished, position]) } /// کد یک‌بارمصرف ورود پرتال مشتری (موبایل) model PortalOtp { id String @id @default(cuid()) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) mobile String code String expiresAt DateTime consumedAt DateTime? createdAt DateTime @default(now()) @@index([organizationId, mobile, createdAt]) @@index([expiresAt]) } /// برد کانبان برای کارهای داخلی / پس‌ازفروش (جدا از کاریز فروش) model ProjectBoard { id String @id @default(cuid()) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) name String isDefault Boolean @default(false) stages ProjectBoardStage[] cards ProjectCard[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([organizationId]) @@index([organizationId, isDefault]) } model ProjectBoardStage { id String @id @default(cuid()) boardId String board ProjectBoard @relation(fields: [boardId], references: [id], onDelete: Cascade) name String position Int cards ProjectCard[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([boardId, position]) } model ProjectCard { id String @id @default(cuid()) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) boardId String board ProjectBoard @relation(fields: [boardId], references: [id], onDelete: Cascade) stageId String stage ProjectBoardStage @relation(fields: [stageId], references: [id], onDelete: Restrict) title String notes String? position Int @default(0) personId String? person Person? @relation(fields: [personId], references: [id], onDelete: SetNull) companyId String? company Company? @relation(fields: [companyId], references: [id], onDelete: SetNull) dealId String? deal Deal? @relation(fields: [dealId], references: [id], onDelete: SetNull) ownerId String? owner User? @relation("ProjectCardOwner", fields: [ownerId], references: [id], onDelete: SetNull) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([organizationId]) @@index([boardId, stageId]) @@index([stageId, position]) @@index([personId]) @@index([companyId]) @@index([dealId]) @@index([ownerId]) } model LoyaltyTier { id String @id @default(cuid()) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) name String minPoints Int @default(0) position Int @default(0) color String? people Person[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([organizationId, position]) @@index([organizationId, minPoints]) } model AutomationRule { id String @id @default(cuid()) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) name String isActive Boolean @default(true) trigger AutomationTrigger action AutomationAction /// عنوان فعالیت/اعلان، assigneeId، notifyUserId، points، activityType config Json @default("{}") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([organizationId, isActive, trigger]) }