2026-08-29 21:06:30 +03:30
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-02 21:29:00 +03:30
|
|
|
enum ActivityPriority {
|
|
|
|
|
low
|
|
|
|
|
medium
|
|
|
|
|
high
|
|
|
|
|
very_low
|
|
|
|
|
very_high
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-03 14:20:55 +03:30
|
|
|
enum PersonMood {
|
|
|
|
|
newcomer
|
|
|
|
|
happy
|
|
|
|
|
unhappy
|
|
|
|
|
special
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-29 21:06:30 +03:30
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-30 09:42:07 +03:30
|
|
|
enum AutomationTrigger {
|
2026-09-09 21:43:17 +03:30
|
|
|
person_created
|
|
|
|
|
lead_submitted
|
|
|
|
|
deal_created
|
|
|
|
|
deal_stage_changed
|
2026-08-30 09:42:07 +03:30
|
|
|
deal_won
|
2026-09-09 21:43:17 +03:30
|
|
|
deal_lost
|
|
|
|
|
ticket_created
|
2026-08-30 09:42:07 +03:30
|
|
|
ticket_resolved
|
2026-09-09 21:43:17 +03:30
|
|
|
invoice_issued
|
|
|
|
|
invoice_paid
|
|
|
|
|
activity_done
|
2026-10-01 15:59:18 +03:30
|
|
|
sms_link_clicked
|
2026-08-30 09:42:07 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum AutomationAction {
|
|
|
|
|
create_activity
|
|
|
|
|
create_notification
|
|
|
|
|
add_loyalty_points
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-01 14:29:34 +03:30
|
|
|
enum NoteAiSuggestionStatus {
|
|
|
|
|
pending
|
|
|
|
|
applied
|
|
|
|
|
dismissed
|
|
|
|
|
failed
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum MemorySourceType {
|
|
|
|
|
note
|
|
|
|
|
manual
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-29 21:06:30 +03:30
|
|
|
model Organization {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
|
|
|
|
name String
|
|
|
|
|
slug String @unique
|
2026-09-03 16:45:59 +03:30
|
|
|
/// زمینه فعالیت سازمان — فهرست همتراز صنایع دیدار
|
2026-10-10 21:28:49 +03:30
|
|
|
businessField String?
|
|
|
|
|
baseCurrency String @default("IRR")
|
|
|
|
|
enabledCurrencies String[] @default(["IRR", "USD", "EUR"])
|
2026-09-20 11:24:14 +03:30
|
|
|
/// ماژولهای فعال منو برای این محیط
|
2026-10-10 21:28:49 +03:30
|
|
|
enabledModules String[] @default([])
|
2026-10-17 16:27:29 +03:30
|
|
|
/// تاریخ قطع بایگانی اطلاعات قبل (شروع روز انتخابشده)؛ null = بدون فیلتر
|
2026-10-15 10:48:30 +03:30
|
|
|
priorArchiveEnabledAt DateTime?
|
2026-10-10 21:28:49 +03:30
|
|
|
isActive Boolean @default(true)
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
users User[]
|
|
|
|
|
memberships Membership[]
|
|
|
|
|
orgRoles OrgRole[]
|
|
|
|
|
teams Team[]
|
|
|
|
|
companies Company[]
|
2026-10-21 17:16:31 +03:30
|
|
|
customerGroups CustomerGroup[]
|
2026-10-10 21:28:49 +03:30
|
|
|
people Person[]
|
2026-10-19 13:34:55 +03:30
|
|
|
attendanceEvents AttendanceEvent[]
|
2026-10-18 21:04:26 +03:30
|
|
|
discAssessments DiscAssessment[]
|
|
|
|
|
salesCopilotSessions SalesCopilotSession[]
|
2026-10-10 21:28:49 +03:30
|
|
|
pipelines Pipeline[]
|
|
|
|
|
deals Deal[]
|
|
|
|
|
activities Activity[]
|
|
|
|
|
notes Note[]
|
|
|
|
|
noteRevisions NoteRevision[]
|
|
|
|
|
customFields CustomFieldDefinition[]
|
|
|
|
|
products Product[]
|
|
|
|
|
notifications Notification[]
|
|
|
|
|
noteAttachments NoteAttachment[]
|
2026-10-27 21:30:45 +03:30
|
|
|
activityImages ActivityImage[]
|
2026-10-10 21:28:49 +03:30
|
|
|
leadForms LeadForm[]
|
|
|
|
|
quotes Quote[]
|
|
|
|
|
invoices Invoice[]
|
|
|
|
|
salesGoals SalesGoal[]
|
|
|
|
|
exchangeRates ExchangeRate[]
|
|
|
|
|
tickets Ticket[]
|
|
|
|
|
faqArticles FaqArticle[]
|
|
|
|
|
portalOtps PortalOtp[]
|
|
|
|
|
projectBoards ProjectBoard[]
|
|
|
|
|
projectCards ProjectCard[]
|
|
|
|
|
loyaltyTiers LoyaltyTier[]
|
|
|
|
|
loyaltySettings LoyaltySettings?
|
|
|
|
|
loyaltyLedgers LoyaltyLedger[]
|
|
|
|
|
loyaltyRewards LoyaltyReward[]
|
|
|
|
|
loyaltyRedemptions LoyaltyRedemption[]
|
|
|
|
|
automationRules AutomationRule[]
|
|
|
|
|
messageAutomationRules MessageAutomationRule[]
|
|
|
|
|
messageAutomationJobs MessageAutomationJob[]
|
|
|
|
|
memoryItems MemoryItem[]
|
|
|
|
|
noteAiSuggestions NoteAiSuggestion[]
|
|
|
|
|
accountingConnections AccountingConnection[]
|
|
|
|
|
accountingSyncJobs AccountingSyncJob[]
|
2026-10-22 18:20:46 +03:30
|
|
|
accountingAsnadLines AccountingAsnadLine[]
|
2026-10-24 12:34:02 +03:30
|
|
|
tahesabInvoiceSettings TahesabInvoiceSettings?
|
2026-10-10 21:28:49 +03:30
|
|
|
warehouses Warehouse[]
|
|
|
|
|
stockMovements StockMovement[]
|
|
|
|
|
partyOpenings PartyOpeningBalance[]
|
|
|
|
|
commissionEntries CommissionEntry[]
|
|
|
|
|
cheques Cheque[]
|
|
|
|
|
auditLogs AuditLog[]
|
|
|
|
|
savedViews SavedView[]
|
|
|
|
|
apiKeys OrganizationApiKey[]
|
|
|
|
|
webhookEndpoints WebhookEndpoint[]
|
|
|
|
|
smsCampaigns SmsCampaign[]
|
2026-10-17 21:23:07 +03:30
|
|
|
outboundSmsLogs OutboundSmsLog[]
|
2026-10-19 15:55:48 +03:30
|
|
|
smsAccount SmsAccount?
|
2026-10-18 18:49:49 +03:30
|
|
|
financialAccounts FinancialAccount[]
|
|
|
|
|
financialCategories FinancialCategory[]
|
|
|
|
|
financialDocuments FinancialDocument[]
|
|
|
|
|
financialMovements FinancialMovement[]
|
|
|
|
|
financialReceivables FinancialReceivable[]
|
|
|
|
|
financialPayrolls FinancialPayroll[]
|
|
|
|
|
financialRecurringItems FinancialRecurringItem[]
|
|
|
|
|
financialReconciliations FinancialReconciliation[]
|
2026-10-18 19:33:09 +03:30
|
|
|
financialAuditLogs FinancialAuditLog[]
|
|
|
|
|
financialAttachments FinancialAttachment[]
|
2026-10-10 21:28:49 +03:30
|
|
|
wooCommerceConnections WooCommerceConnection[]
|
|
|
|
|
montifyConnections MontifyConnection[]
|
|
|
|
|
voipConnections VoipConnection[]
|
|
|
|
|
reservationServices ReservationService[]
|
|
|
|
|
reservationAppointments ReservationAppointment[]
|
|
|
|
|
leads Lead[]
|
|
|
|
|
leadStages LeadStage[]
|
2026-10-20 17:23:27 +03:30
|
|
|
activityBoards ActivityBoard[]
|
2026-10-10 21:28:49 +03:30
|
|
|
activityStages ActivityStage[]
|
|
|
|
|
activityTypeDefs ActivityTypeDef[]
|
|
|
|
|
leadEvents LeadEvent[]
|
|
|
|
|
leadImportJobs LeadImportJob[]
|
|
|
|
|
conversationSettings ConversationSettings?
|
|
|
|
|
conversationRooms ConversationRoom[]
|
|
|
|
|
conversationHiddenMessages ConversationHiddenMessage[]
|
|
|
|
|
conversationPinnedMessages ConversationPinnedMessage[]
|
|
|
|
|
conversationRoomUserPins ConversationRoomUserPin[]
|
|
|
|
|
conversationRoomMembers ConversationRoomMember[]
|
|
|
|
|
conversationRoomUserMutes ConversationRoomUserMute[]
|
2026-09-30 14:58:44 +03:30
|
|
|
conversationMessageReactions ConversationMessageReaction[]
|
2026-10-10 21:28:49 +03:30
|
|
|
trackingLinks TrackingLink[]
|
|
|
|
|
trackingEvents TrackingEvent[]
|
|
|
|
|
routineCategories RoutineCategory[]
|
|
|
|
|
routines Routine[]
|
|
|
|
|
routineTasks RoutineTask[]
|
|
|
|
|
routineTaskEvents RoutineTaskEvent[]
|
|
|
|
|
routineUserSettings RoutineUserSettings[]
|
|
|
|
|
gamificationSettings GamificationSettings?
|
|
|
|
|
gamificationRules GamificationRule[]
|
|
|
|
|
gamificationTransactions GamificationTransaction[]
|
|
|
|
|
gamificationWallets GamificationWallet[]
|
|
|
|
|
gamificationRewards GamificationReward[]
|
|
|
|
|
gamificationRedemptions GamificationRedemption[]
|
|
|
|
|
gamificationBadges GamificationBadge[]
|
|
|
|
|
gamificationUserBadges GamificationUserBadge[]
|
|
|
|
|
gamificationBonuses GamificationBonus[]
|
2026-10-13 22:23:09 +03:30
|
|
|
/// month | two_months | three_months | six_months | year | never
|
|
|
|
|
deletionRetention String @default("never")
|
|
|
|
|
deletionArchives DeletionArchive[]
|
2026-08-29 21:06:30 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model User {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
|
|
|
|
email String
|
2026-09-05 00:47:33 +03:30
|
|
|
/// موبایل نرمالشده (مثلاً 9121234567) — برای ورود با موبایل
|
2026-10-10 21:28:49 +03:30
|
|
|
mobile String?
|
|
|
|
|
passwordHash String
|
|
|
|
|
fullName String
|
|
|
|
|
role UserRole @default(sales)
|
2026-09-15 14:10:52 +03:30
|
|
|
/// مسیر فایل آواتار در فضای آپلود
|
2026-10-10 21:28:49 +03:30
|
|
|
avatarStorageKey String?
|
|
|
|
|
isActive Boolean @default(true)
|
|
|
|
|
lastLoginAt DateTime?
|
|
|
|
|
orgRoleId String?
|
|
|
|
|
orgRole OrgRole? @relation(fields: [orgRoleId], references: [id], onDelete: SetNull)
|
|
|
|
|
emailVerifiedAt DateTime?
|
|
|
|
|
mobileVerifiedAt DateTime?
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
ownedDeals Deal[] @relation("DealOwner")
|
|
|
|
|
assignedDeals Deal[] @relation("DealAssignees")
|
|
|
|
|
createdDeals Deal[] @relation("DealCreator")
|
|
|
|
|
ownedQuotes Quote[] @relation("QuoteOwner")
|
|
|
|
|
ownedInvoices Invoice[] @relation("InvoiceOwner")
|
|
|
|
|
ownedPeople Person[] @relation("PersonOwner")
|
|
|
|
|
ownedCompanies Company[] @relation("CompanyOwner")
|
|
|
|
|
ownedProjectCards ProjectCard[] @relation("ProjectCardOwner")
|
|
|
|
|
activities Activity[] @relation("ActivityAssignee")
|
|
|
|
|
assignedActivities Activity[] @relation("ActivityAssignees")
|
|
|
|
|
createdActivities Activity[] @relation("ActivityCreator")
|
|
|
|
|
notes Note[] @relation("NoteAuthor")
|
|
|
|
|
noteRevisions NoteRevision[] @relation("NoteRevisionEditor")
|
|
|
|
|
noteAttachments NoteAttachment[] @relation("NoteAttachmentUploader")
|
2026-10-27 21:30:45 +03:30
|
|
|
activityImages ActivityImage[] @relation("ActivityImageUploader")
|
2026-10-10 21:28:49 +03:30
|
|
|
notifications Notification[]
|
|
|
|
|
salesGoals SalesGoal[] @relation("SalesGoalUser")
|
|
|
|
|
commissionPercent Decimal @default(0) @db.Decimal(8, 2)
|
|
|
|
|
commissionEntries CommissionEntry[]
|
|
|
|
|
receivedCheques Cheque[] @relation("ChequeOwner")
|
|
|
|
|
assignedTickets Ticket[] @relation("TicketAssignee")
|
|
|
|
|
reportedTickets Ticket[] @relation("TicketReporter")
|
|
|
|
|
ticketMessages TicketMessage[] @relation("TicketMessageAuthor")
|
|
|
|
|
confirmedMemories MemoryItem[] @relation("MemoryConfirmedBy")
|
|
|
|
|
memberships Membership[]
|
|
|
|
|
billingAccount BillingAccount?
|
|
|
|
|
auditLogs AuditLog[] @relation("AuditLogActor")
|
|
|
|
|
savedViews SavedView[] @relation("SavedViewOwner")
|
|
|
|
|
tablePreferences UserTablePreference[]
|
|
|
|
|
assignedProjectCards ProjectCard[] @relation("ProjectCardAssignees")
|
|
|
|
|
teamMemberships TeamMember[]
|
2026-09-27 09:38:17 +03:30
|
|
|
reservationAssignments ReservationAppointment[] @relation("ReservationAssignee")
|
2026-10-10 21:28:49 +03:30
|
|
|
assignedLeads Lead[] @relation("LeadAssignee")
|
|
|
|
|
createdLeads Lead[] @relation("LeadCreator")
|
|
|
|
|
assignedRoutines Routine[] @relation("RoutineAssignee")
|
|
|
|
|
createdRoutines Routine[] @relation("RoutineCreator")
|
|
|
|
|
routineTasks RoutineTask[] @relation("RoutineTaskAssignee")
|
|
|
|
|
createdRoutineTasks RoutineTask[] @relation("RoutineTaskCreator")
|
|
|
|
|
routineTaskEvents RoutineTaskEvent[]
|
|
|
|
|
routineSettings RoutineUserSettings[]
|
2026-10-19 13:34:55 +03:30
|
|
|
attendanceEvents AttendanceEvent[]
|
2026-10-13 22:23:09 +03:30
|
|
|
deletionArchives DeletionArchive[] @relation("DeletionActor")
|
2026-10-18 21:04:26 +03:30
|
|
|
discAssessments DiscAssessment[] @relation("DiscAssessor")
|
|
|
|
|
salesCopilotSessions SalesCopilotSession[] @relation("CopilotAuthor")
|
2026-10-10 21:28:49 +03:30
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-08-29 21:06:30 +03:30
|
|
|
|
|
|
|
|
@@unique([organizationId, email])
|
2026-09-05 00:47:33 +03:30
|
|
|
@@unique([mobile])
|
2026-08-29 21:06:30 +03:30
|
|
|
@@index([organizationId])
|
2026-09-05 00:47:33 +03:30
|
|
|
@@index([email])
|
2026-09-15 17:33:05 +03:30
|
|
|
@@index([orgRoleId])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model OrgRole {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
name String
|
|
|
|
|
description String @default("")
|
|
|
|
|
color String @default("teal")
|
|
|
|
|
permissions String[] @default([])
|
2026-10-22 11:53:26 +03:30
|
|
|
/// عمق داده بهازای بخش، مثلاً {"customers":{"mode":"rules","rules":[{"owners":["self"]}]}}
|
|
|
|
|
dataAccessPolicy Json @default("{}")
|
2026-09-15 17:33:05 +03:30
|
|
|
/// owner | admin | sales | support — نقشهای سیستمی قابل حذف نیستند
|
|
|
|
|
systemKey String?
|
2026-10-03 13:16:13 +03:30
|
|
|
/// نقش والد برای سلسلهمراتب زیرنقشها
|
|
|
|
|
parentId String?
|
|
|
|
|
parent OrgRole? @relation("OrgRoleTree", fields: [parentId], references: [id], onDelete: Restrict)
|
|
|
|
|
children OrgRole[] @relation("OrgRoleTree")
|
2026-09-15 17:33:05 +03:30
|
|
|
users User[]
|
2026-09-15 21:37:58 +03:30
|
|
|
memberships Membership[]
|
2026-09-15 17:33:05 +03:30
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@unique([organizationId, name])
|
|
|
|
|
@@index([organizationId])
|
2026-10-03 13:16:13 +03:30
|
|
|
@@index([parentId])
|
|
|
|
|
@@index([organizationId, parentId])
|
2026-08-29 21:06:30 +03:30
|
|
|
}
|
|
|
|
|
|
2026-09-15 21:37:58 +03:30
|
|
|
model Membership {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
|
|
|
|
userId String
|
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
role UserRole @default(sales)
|
|
|
|
|
orgRoleId String?
|
|
|
|
|
orgRole OrgRole? @relation(fields: [orgRoleId], references: [id], onDelete: SetNull)
|
|
|
|
|
isActive Boolean @default(true)
|
|
|
|
|
commissionPercent Decimal @default(0) @db.Decimal(8, 2)
|
2026-09-26 17:15:09 +03:30
|
|
|
/// own | team | org — سطح دید دادهها برای نقشهای غیرمالک/مدیر
|
2026-10-10 21:28:49 +03:30
|
|
|
dataVisibility String @default("own")
|
2026-10-06 15:04:46 +03:30
|
|
|
/// سطح دید تفکیکی هر بخش، مثلاً {"contacts":"org","deals":"own"}؛ بخشهای بدون مقدار = dataVisibility
|
2026-10-10 21:28:49 +03:30
|
|
|
dataVisibilityScopes Json @default("{}")
|
2026-10-22 11:53:26 +03:30
|
|
|
/// Override عمق داده نسبت به نقش؛ کلید غایب = ارث از OrgRole.dataAccessPolicy
|
|
|
|
|
dataAccessOverride Json @default("{}")
|
2026-10-15 10:48:30 +03:30
|
|
|
/// سوئیچ «بایگانی اطلاعات قبل» برای این عضو؛ فقط وقتی ماژول محیط روشن است اثر دارد
|
|
|
|
|
priorArchiveEnabled Boolean @default(false)
|
|
|
|
|
/// current | archive
|
|
|
|
|
priorArchiveLens String @default("current")
|
2026-10-19 13:34:55 +03:30
|
|
|
/// ساعت خروج روزانه به وقت تهران، مثل 18:00؛ خالی یعنی بدون محدودیت
|
|
|
|
|
logoutTime String?
|
2026-10-21 14:20:15 +03:30
|
|
|
/// بعد از ساعت خروج تا پایان روز (تهران) اجازهٔ ورود دوباره به این محیط نیست
|
|
|
|
|
blockLoginAfterLogout Boolean @default(false)
|
2026-10-22 15:43:24 +03:30
|
|
|
/// ورود به این محیط فقط با OTP؛ رمز عبور برای این عضویت کار نمیکند
|
|
|
|
|
loginOtpOnly Boolean @default(false)
|
2026-10-10 21:28:49 +03:30
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-09-15 21:37:58 +03:30
|
|
|
|
|
|
|
|
@@unique([userId, organizationId])
|
|
|
|
|
@@index([organizationId, role])
|
|
|
|
|
@@index([userId])
|
|
|
|
|
@@index([orgRoleId])
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-26 17:15:09 +03:30
|
|
|
/// گروه کاری داخل سازمان برای دیدپذیری مشترک
|
|
|
|
|
model Team {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
name String
|
|
|
|
|
description String @default("")
|
|
|
|
|
members TeamMember[]
|
2026-10-06 19:54:38 +03:30
|
|
|
routines Routine[]
|
2026-09-26 17:15:09 +03:30
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@unique([organizationId, name])
|
|
|
|
|
@@index([organizationId])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model TeamMember {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
teamId String
|
|
|
|
|
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
|
|
|
|
|
userId String
|
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
|
/// member | manager
|
|
|
|
|
role String @default("member")
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@unique([teamId, userId])
|
|
|
|
|
@@index([userId])
|
|
|
|
|
@@index([teamId])
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-19 21:54:21 +03:30
|
|
|
/// حساب سوپرادمین پلتفرم — جدا از User سازمانی
|
|
|
|
|
model PlatformAdmin {
|
|
|
|
|
id String @id @default(cuid())
|
2026-09-19 22:42:51 +03:30
|
|
|
/// ورود اصلی با موبایل نرمالشده (مثلاً 0912…)
|
|
|
|
|
mobile String? @unique
|
|
|
|
|
email String? @unique
|
2026-09-19 21:54:21 +03:30
|
|
|
passwordHash String
|
|
|
|
|
fullName String
|
|
|
|
|
isActive Boolean @default(true)
|
|
|
|
|
lastLoginAt DateTime?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-20 11:24:14 +03:30
|
|
|
/// اشتراک SaaS روی مالک حساب (نه روی هر عضویت)
|
|
|
|
|
model BillingAccount {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
ownerUserId String @unique
|
|
|
|
|
owner User @relation(fields: [ownerUserId], references: [id], onDelete: Cascade)
|
2026-10-06 19:54:38 +03:30
|
|
|
/// trial | monthly | quarterly | semiannual | yearly | lifetime | custom
|
2026-09-20 11:24:14 +03:30
|
|
|
planCode String @default("trial")
|
|
|
|
|
/// active | expired | suspended
|
|
|
|
|
status String @default("active")
|
|
|
|
|
startsAt DateTime @default(now())
|
|
|
|
|
expiresAt DateTime
|
|
|
|
|
maxOrganizations Int @default(1)
|
|
|
|
|
/// رزرو برای درگاه/لندینگ بعدی
|
|
|
|
|
externalPaymentRef String?
|
|
|
|
|
notes String?
|
2026-10-27 22:27:05 +03:30
|
|
|
/// پیامک پایان اشتراک برای مدیران پنل؛ خالی یعنی هنوز اطلاع داده نشده
|
|
|
|
|
expiryNotifiedAt DateTime?
|
2026-09-20 11:24:14 +03:30
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@index([status])
|
|
|
|
|
@@index([expiresAt])
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-29 21:06:30 +03:30
|
|
|
model Company {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
name String
|
2026-10-06 12:25:20 +03:30
|
|
|
/// کد داخلی شرکت (مثل «کد شرکت» در دیدار)
|
2026-10-10 21:28:49 +03:30
|
|
|
code String?
|
2026-10-06 12:25:20 +03:30
|
|
|
/// منبع ورود: manual | didar | tahesab | excel | other
|
2026-10-10 21:28:49 +03:30
|
|
|
importSource String @default("manual")
|
2026-10-06 12:25:20 +03:30
|
|
|
/// شناسه در مبدأ (مثلاً کد دیدار شرکت)
|
2026-10-10 21:28:49 +03:30
|
|
|
sourceRef String?
|
|
|
|
|
nationalId String?
|
|
|
|
|
economicCode String?
|
2026-10-06 12:25:20 +03:30
|
|
|
registrationNumber String?
|
2026-10-10 21:28:49 +03:30
|
|
|
phone String?
|
|
|
|
|
otherPhones String?
|
|
|
|
|
fax String?
|
|
|
|
|
email String?
|
|
|
|
|
otherEmails String?
|
|
|
|
|
website String?
|
|
|
|
|
province String?
|
|
|
|
|
city String?
|
|
|
|
|
postalCode String?
|
|
|
|
|
address String?
|
2026-10-21 17:16:31 +03:30
|
|
|
/// قدیمی؛ از نسخهٔ گروهبندی، گروهها در CompanyGroupLink هستند
|
2026-10-10 21:28:49 +03:30
|
|
|
groupName String?
|
|
|
|
|
notes String?
|
|
|
|
|
registeredAt DateTime?
|
|
|
|
|
tags String[] @default([])
|
|
|
|
|
customFields Json @default("{}")
|
2026-10-21 17:16:31 +03:30
|
|
|
groupLinks CompanyGroupLink[]
|
2026-10-10 21:28:49 +03:30
|
|
|
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[]
|
|
|
|
|
routineTasks RoutineTask[]
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-08-29 21:06:30 +03:30
|
|
|
|
|
|
|
|
@@index([organizationId])
|
|
|
|
|
@@index([organizationId, name])
|
|
|
|
|
@@index([organizationId, email])
|
|
|
|
|
@@index([organizationId, phone])
|
2026-10-06 12:25:20 +03:30
|
|
|
@@index([organizationId, sourceRef])
|
|
|
|
|
@@index([organizationId, code])
|
2026-08-29 21:06:30 +03:30
|
|
|
@@index([ownerId])
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-21 17:16:31 +03:30
|
|
|
/// گروه مشتری (مشترک بین اشخاص و شرکتها؛ هر مشتری چند گروه)
|
|
|
|
|
model CustomerGroup {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
name String
|
|
|
|
|
color String @default("teal")
|
|
|
|
|
position Int @default(0)
|
|
|
|
|
/// گروه متناظر در تهحساب (GID) روی همان اتصال
|
|
|
|
|
tahesabConnectionId String?
|
|
|
|
|
tahesabGid String?
|
2026-10-26 11:05:34 +03:30
|
|
|
/// گروه اصلی؛ فقط یک سطح (اصلی / فرعی)
|
|
|
|
|
parentId String?
|
|
|
|
|
parent CustomerGroup? @relation("CustomerGroupTree", fields: [parentId], references: [id], onDelete: SetNull)
|
|
|
|
|
children CustomerGroup[] @relation("CustomerGroupTree")
|
2026-10-21 17:16:31 +03:30
|
|
|
people PersonGroupLink[]
|
|
|
|
|
companies CompanyGroupLink[]
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@unique([organizationId, name])
|
|
|
|
|
@@index([organizationId, position])
|
|
|
|
|
@@index([tahesabConnectionId, tahesabGid])
|
2026-10-26 11:05:34 +03:30
|
|
|
@@index([parentId])
|
2026-10-21 17:16:31 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model PersonGroupLink {
|
|
|
|
|
personId String
|
|
|
|
|
person Person @relation(fields: [personId], references: [id], onDelete: Cascade)
|
|
|
|
|
groupId String
|
|
|
|
|
group CustomerGroup @relation(fields: [groupId], references: [id], onDelete: Cascade)
|
|
|
|
|
/// manual | tahesab — گروهِ تهحسابی با همگامسازی جایگزین میشود؛ دستی دست نمیخورد
|
|
|
|
|
source String @default("manual")
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@id([personId, groupId])
|
|
|
|
|
@@index([groupId])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model CompanyGroupLink {
|
|
|
|
|
companyId String
|
|
|
|
|
company Company @relation(fields: [companyId], references: [id], onDelete: Cascade)
|
|
|
|
|
groupId String
|
|
|
|
|
group CustomerGroup @relation(fields: [groupId], references: [id], onDelete: Cascade)
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@id([companyId, groupId])
|
|
|
|
|
@@index([groupId])
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-29 21:06:30 +03:30
|
|
|
model Person {
|
2026-10-10 21:28:49 +03:30
|
|
|
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
|
2026-10-04 12:01:21 +03:30
|
|
|
/// نام قبل از همگامسازی مبدأ (مثلاً تهحساب)
|
2026-10-10 21:28:49 +03:30
|
|
|
previousFirstName String?
|
|
|
|
|
previousLastName String?
|
2026-10-04 12:01:21 +03:30
|
|
|
/// منبع ورود: manual | tahesab | didar | excel | phone | lead_form | other
|
2026-10-10 21:28:49 +03:30
|
|
|
importSource String @default("manual")
|
2026-10-04 12:01:21 +03:30
|
|
|
/// شناسه در مبدأ (کد دیدار، کد مشتری تهحساب، …)
|
2026-10-10 21:28:49 +03:30
|
|
|
sourceRef String?
|
2026-10-05 20:54:23 +03:30
|
|
|
/// منبع و کد قبلی وقتی شخص از منبع دیگری دوباره وارد شده (مثلاً دیدار قبل از تهحساب)
|
2026-10-10 21:28:49 +03:30
|
|
|
previousImportSource String?
|
|
|
|
|
previousSourceRef String?
|
|
|
|
|
email String?
|
|
|
|
|
phone String?
|
|
|
|
|
mobile String?
|
|
|
|
|
position String?
|
2026-09-23 14:41:16 +03:30
|
|
|
/// آدرس وبسایت
|
2026-10-10 21:28:49 +03:30
|
|
|
website String?
|
2026-10-03 12:29:11 +03:30
|
|
|
/// پیج اینستاگرام
|
2026-10-10 21:28:49 +03:30
|
|
|
instagram String?
|
2026-10-13 18:04:53 +03:30
|
|
|
/// تاریخ تولد
|
|
|
|
|
birthDate DateTime?
|
2026-09-23 14:41:16 +03:30
|
|
|
/// نام معرف
|
2026-10-10 21:28:49 +03:30
|
|
|
referrer String?
|
2026-10-03 12:29:11 +03:30
|
|
|
/// لیست آدرسها (JSON array of strings)
|
2026-10-10 21:28:49 +03:30
|
|
|
addresses Json @default("[]")
|
|
|
|
|
notes String?
|
|
|
|
|
mood PersonMood @default(newcomer)
|
|
|
|
|
tags String[] @default([])
|
|
|
|
|
customFields Json @default("{}")
|
2026-10-21 17:16:31 +03:30
|
|
|
groupLinks PersonGroupLink[]
|
2026-10-10 21:28:49 +03:30
|
|
|
loyaltyPoints Int @default(0)
|
|
|
|
|
loyaltyTierId String?
|
|
|
|
|
loyaltyTier LoyaltyTier? @relation(fields: [loyaltyTierId], references: [id], onDelete: SetNull)
|
|
|
|
|
loyaltyLedgers LoyaltyLedger[]
|
|
|
|
|
loyaltyRedemptions LoyaltyRedemption[]
|
|
|
|
|
deals Deal[]
|
|
|
|
|
activities Activity[]
|
|
|
|
|
smartNotes Note[]
|
|
|
|
|
quotes Quote[]
|
|
|
|
|
invoices Invoice[]
|
2026-09-27 09:38:17 +03:30
|
|
|
reservationAppointments ReservationAppointment[]
|
2026-10-10 21:28:49 +03:30
|
|
|
ownerId String?
|
|
|
|
|
owner User? @relation("PersonOwner", fields: [ownerId], references: [id], onDelete: SetNull)
|
|
|
|
|
tickets Ticket[]
|
|
|
|
|
projectCards ProjectCard[]
|
|
|
|
|
memoryItems MemoryItem[]
|
|
|
|
|
accountingLinks PersonAccountingLink[]
|
|
|
|
|
accountingDocuments AccountingDocumentLog[]
|
2026-10-22 18:20:46 +03:30
|
|
|
asnadLines AccountingAsnadLine[]
|
2026-10-10 21:28:49 +03:30
|
|
|
openingBalances PartyOpeningBalance[]
|
|
|
|
|
cheques Cheque[]
|
|
|
|
|
trackingLinks TrackingLink[]
|
|
|
|
|
trackingEvents TrackingEvent[]
|
|
|
|
|
routineTasks RoutineTask[]
|
2026-10-17 21:23:07 +03:30
|
|
|
outboundSmsLogs OutboundSmsLog[]
|
2026-10-18 18:49:49 +03:30
|
|
|
financialDocuments FinancialDocument[]
|
2026-10-18 21:04:26 +03:30
|
|
|
discLabel String?
|
|
|
|
|
discReliable Boolean @default(false)
|
|
|
|
|
discAssessedAt DateTime?
|
|
|
|
|
discAssessments DiscAssessment[]
|
|
|
|
|
salesCopilotSessions SalesCopilotSession[]
|
2026-10-18 18:49:49 +03:30
|
|
|
financialReceivables FinancialReceivable[]
|
2026-10-10 21:28:49 +03:30
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-08-29 21:06:30 +03:30
|
|
|
|
|
|
|
|
@@index([organizationId])
|
|
|
|
|
@@index([organizationId, email])
|
|
|
|
|
@@index([organizationId, mobile])
|
|
|
|
|
@@index([organizationId, lastName, firstName])
|
2026-10-04 12:01:21 +03:30
|
|
|
@@index([organizationId, importSource])
|
|
|
|
|
@@index([organizationId, sourceRef])
|
2026-10-05 20:54:23 +03:30
|
|
|
@@index([organizationId, previousSourceRef])
|
2026-08-30 09:42:07 +03:30
|
|
|
@@index([organizationId, loyaltyPoints])
|
2026-08-29 21:06:30 +03:30
|
|
|
@@index([companyId])
|
|
|
|
|
@@index([ownerId])
|
2026-08-30 09:42:07 +03:30
|
|
|
@@index([loyaltyTierId])
|
2026-10-18 21:04:26 +03:30
|
|
|
@@index([organizationId, discLabel])
|
2026-08-29 21:06:30 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 {
|
2026-10-10 21:28:49 +03:30
|
|
|
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
|
2026-10-06 16:36:04 +03:30
|
|
|
/// کد دستی معامله
|
2026-10-10 21:28:49 +03:30
|
|
|
code String?
|
2026-10-06 16:36:04 +03:30
|
|
|
/// منبع ورود: manual | didar | excel
|
2026-10-10 21:28:49 +03:30
|
|
|
importSource String @default("manual")
|
2026-10-17 16:27:29 +03:30
|
|
|
/// اگر پر باشد، رکورد از بایگانی به کار جاری منتقل شده (فقط برچسب؛ بقیه فیلدها دستنخورده)
|
|
|
|
|
promotedFromPriorAt DateTime?
|
2026-10-06 16:36:04 +03:30
|
|
|
/// شناسه در مبدأ (کد دیدار معامله)
|
2026-10-10 21:28:49 +03:30
|
|
|
sourceRef String?
|
|
|
|
|
amount Decimal? @db.Decimal(18, 2)
|
|
|
|
|
currency String @default("IRR")
|
|
|
|
|
status DealStatus @default(open)
|
2026-10-06 16:36:04 +03:30
|
|
|
/// احتمال موفقیت (۰ تا ۱۰۰)
|
2026-10-10 21:28:49 +03:30
|
|
|
probability Int?
|
2026-10-06 16:36:04 +03:30
|
|
|
/// شیوه آشنایی
|
2026-10-10 21:28:49 +03:30
|
|
|
source String?
|
2026-10-06 16:36:04 +03:30
|
|
|
/// شیوه آشنایی دلخواه
|
2026-10-10 21:28:49 +03:30
|
|
|
sourceDetail String?
|
|
|
|
|
referrer String?
|
|
|
|
|
lostReason String?
|
|
|
|
|
lostNote String?
|
2026-10-06 16:36:04 +03:30
|
|
|
/// تاریخ انجام (موفق/ناموفق شدن)
|
2026-10-10 21:28:49 +03:30
|
|
|
closedAt DateTime?
|
|
|
|
|
notes String?
|
|
|
|
|
tags String[] @default([])
|
|
|
|
|
isStale Boolean @default(false)
|
|
|
|
|
isRotten Boolean @default(false)
|
|
|
|
|
customFields Json @default("{}")
|
2026-08-29 21:06:30 +03:30
|
|
|
expectedCloseAt DateTime?
|
2026-10-10 21:28:49 +03:30
|
|
|
personId String?
|
|
|
|
|
person Person? @relation(fields: [personId], references: [id], onDelete: SetNull)
|
2026-10-18 21:04:26 +03:30
|
|
|
salesCopilotSessions SalesCopilotSession[]
|
2026-10-10 21:28:49 +03:30
|
|
|
companyId String?
|
|
|
|
|
company Company? @relation(fields: [companyId], references: [id], onDelete: SetNull)
|
|
|
|
|
ownerId String?
|
|
|
|
|
owner User? @relation("DealOwner", fields: [ownerId], references: [id], onDelete: SetNull)
|
|
|
|
|
createdById String?
|
|
|
|
|
createdBy User? @relation("DealCreator", fields: [createdById], references: [id], onDelete: SetNull)
|
|
|
|
|
assignees User[] @relation("DealAssignees")
|
|
|
|
|
position Int @default(0)
|
|
|
|
|
activities Activity[]
|
|
|
|
|
smartNotes Note[]
|
|
|
|
|
lineItems DealLineItem[]
|
|
|
|
|
quotes Quote[]
|
|
|
|
|
invoices Invoice[]
|
|
|
|
|
tickets Ticket[]
|
|
|
|
|
projectCards ProjectCard[]
|
|
|
|
|
routineTasks RoutineTask[]
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-08-29 21:06:30 +03:30
|
|
|
|
|
|
|
|
@@index([organizationId])
|
|
|
|
|
@@index([pipelineId, stageId])
|
|
|
|
|
@@index([stageId, position])
|
|
|
|
|
@@index([personId])
|
|
|
|
|
@@index([companyId])
|
|
|
|
|
@@index([ownerId])
|
2026-10-06 16:36:04 +03:30
|
|
|
@@index([organizationId, sourceRef])
|
2026-10-17 16:27:29 +03:30
|
|
|
@@index([organizationId, createdAt])
|
2026-08-29 21:06:30 +03:30
|
|
|
}
|
|
|
|
|
|
2026-10-07 11:18:53 +03:30
|
|
|
/// مراحل/ستونهای فعالیت (تب و کانبان) — kind وضعیت فعالیت داخل این مرحله را تعیین میکند
|
2026-10-20 17:23:27 +03:30
|
|
|
/// گروه فعالیت (مثل مسیر فروش برای معاملات): هر گروه ستونهای خودش را دارد
|
|
|
|
|
model ActivityBoard {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
name String
|
|
|
|
|
isDefault Boolean @default(false)
|
|
|
|
|
position Int @default(0)
|
|
|
|
|
stages ActivityStage[]
|
|
|
|
|
activities Activity[]
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@unique([organizationId, name])
|
|
|
|
|
@@index([organizationId, position])
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-07 11:18:53 +03:30
|
|
|
model ActivityStage {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
2026-10-20 17:23:27 +03:30
|
|
|
boardId String
|
|
|
|
|
board ActivityBoard @relation(fields: [boardId], references: [id], onDelete: Cascade)
|
2026-10-07 11:18:53 +03:30
|
|
|
name String
|
|
|
|
|
position Int @default(0)
|
|
|
|
|
color String @default("green")
|
|
|
|
|
kind ActivityStatus @default(open)
|
|
|
|
|
activities Activity[]
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
2026-10-20 17:23:27 +03:30
|
|
|
@@unique([boardId, name])
|
2026-10-07 11:18:53 +03:30
|
|
|
@@index([organizationId, position])
|
2026-10-20 17:23:27 +03:30
|
|
|
@@index([boardId, position])
|
2026-10-07 11:18:53 +03:30
|
|
|
}
|
|
|
|
|
|
2026-10-07 12:29:23 +03:30
|
|
|
/// نوع فعالیت قابل تعریف توسط سازمان با فیلدهای اختصاصی
|
|
|
|
|
model ActivityTypeDef {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
name String
|
|
|
|
|
/// نوع پایه — مبنای گزارشها، یادآورها و سازگاری
|
|
|
|
|
baseType ActivityType @default(task)
|
|
|
|
|
icon String @default("check")
|
|
|
|
|
color String @default("green")
|
|
|
|
|
/// تعریف فیلدها: [{ key, label, type, options?, required?, placeholder? }]
|
|
|
|
|
fields Json @default("[]")
|
|
|
|
|
position Int @default(0)
|
|
|
|
|
isActive Boolean @default(true)
|
|
|
|
|
/// نوع اصلیِ هر baseType — حذفشدنی نیست
|
|
|
|
|
isSystem Boolean @default(false)
|
|
|
|
|
activities Activity[]
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@unique([organizationId, name])
|
|
|
|
|
@@index([organizationId, position])
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-29 21:06:30 +03:30
|
|
|
model Activity {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
type ActivityType @default(task)
|
|
|
|
|
status ActivityStatus @default(open)
|
|
|
|
|
priority ActivityPriority @default(medium)
|
|
|
|
|
title String
|
2026-10-15 10:48:30 +03:30
|
|
|
/// manual | didar — سابقهٔ منتقلشده از اطلاعات قبل
|
|
|
|
|
importSource String @default("manual")
|
2026-10-17 16:27:29 +03:30
|
|
|
/// اگر پر باشد، رکورد از بایگانی به کار جاری منتقل شده (فقط برچسب؛ بقیه فیلدها دستنخورده)
|
|
|
|
|
promotedFromPriorAt DateTime?
|
2026-10-10 21:28:49 +03:30
|
|
|
description String?
|
|
|
|
|
dueAt DateTime?
|
|
|
|
|
durationMinutes Int?
|
|
|
|
|
reminderAt DateTime?
|
2026-09-05 02:04:39 +03:30
|
|
|
/// اعلان داخل پنل هنگام رسیدن یادآور
|
2026-10-10 21:28:49 +03:30
|
|
|
reminderNotifyApp Boolean @default(true)
|
2026-09-05 02:04:39 +03:30
|
|
|
/// پیامک به موبایل مسئول هنگام رسیدن یادآور
|
2026-10-10 21:28:49 +03:30
|
|
|
reminderNotifySms Boolean @default(false)
|
2026-10-06 13:49:03 +03:30
|
|
|
/// زمان ارسال یادآور؛ با تغییر reminderAt پاک میشود
|
2026-10-10 21:28:49 +03:30
|
|
|
reminderSentAt DateTime?
|
|
|
|
|
completedAt DateTime?
|
|
|
|
|
tags String[] @default([])
|
|
|
|
|
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)
|
2026-10-03 13:16:13 +03:30
|
|
|
/// مسئولان مشترک (علاوه بر assigneeId بهعنوان مسئول اصلی)
|
2026-10-10 21:28:49 +03:30
|
|
|
assignees User[] @relation("ActivityAssignees")
|
|
|
|
|
createdById String?
|
|
|
|
|
createdBy User? @relation("ActivityCreator", fields: [createdById], references: [id], onDelete: SetNull)
|
|
|
|
|
notes Note[]
|
|
|
|
|
routineTasks RoutineTask[]
|
2026-10-20 17:23:27 +03:30
|
|
|
/// گروه فعالیت؛ null = گروه پیشفرض محیط
|
|
|
|
|
boardId String?
|
|
|
|
|
board ActivityBoard? @relation(fields: [boardId], references: [id], onDelete: SetNull)
|
2026-10-07 11:18:53 +03:30
|
|
|
/// مرحله (ستون کانبان)؛ null یا مرحلهٔ ناهمنوع با status → اولین مرحلهٔ همنوع
|
2026-10-10 21:28:49 +03:30
|
|
|
stageId String?
|
|
|
|
|
stage ActivityStage? @relation(fields: [stageId], references: [id], onDelete: SetNull)
|
2026-10-07 12:29:23 +03:30
|
|
|
/// نوع سفارشی؛ null → نوع اصلیِ همان type
|
2026-10-10 21:28:49 +03:30
|
|
|
typeDefId String?
|
|
|
|
|
typeDef ActivityTypeDef? @relation(fields: [typeDefId], references: [id], onDelete: SetNull)
|
2026-10-07 12:29:23 +03:30
|
|
|
/// مقادیر فیلدهای اختصاصی نوع (کلید فیلد → مقدار)
|
2026-10-10 21:28:49 +03:30
|
|
|
fields Json?
|
2026-10-07 12:29:23 +03:30
|
|
|
/// کاربران شرکتکننده؛ فعالیت در لیست آنها هم دیده میشود
|
2026-10-10 21:28:49 +03:30
|
|
|
participantIds String[] @default([])
|
2026-10-11 14:12:36 +03:30
|
|
|
/// لید مرتبط (مثلاً فعالیت ساختهشده از ارسال فرم)
|
|
|
|
|
leadId String?
|
|
|
|
|
lead Lead? @relation(fields: [leadId], references: [id], onDelete: SetNull)
|
2026-10-27 21:30:45 +03:30
|
|
|
images ActivityImage[]
|
2026-10-10 21:28:49 +03:30
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-08-29 21:06:30 +03:30
|
|
|
|
2026-10-07 11:18:53 +03:30
|
|
|
@@index([stageId])
|
2026-10-20 17:23:27 +03:30
|
|
|
@@index([boardId])
|
2026-10-07 12:29:23 +03:30
|
|
|
@@index([typeDefId])
|
2026-10-11 14:12:36 +03:30
|
|
|
@@index([leadId])
|
2026-08-29 21:06:30 +03:30
|
|
|
@@index([organizationId, status, dueAt])
|
2026-10-15 10:48:30 +03:30
|
|
|
@@index([organizationId, importSource])
|
2026-10-17 16:27:29 +03:30
|
|
|
@@index([organizationId, createdAt])
|
2026-08-29 21:06:30 +03:30
|
|
|
@@index([organizationId, assigneeId])
|
2026-09-02 21:29:00 +03:30
|
|
|
@@index([organizationId, createdById])
|
2026-10-06 13:49:03 +03:30
|
|
|
@@index([reminderAt, reminderSentAt])
|
2026-08-29 21:06:30 +03:30
|
|
|
@@index([personId])
|
|
|
|
|
@@index([companyId])
|
|
|
|
|
@@index([dealId])
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-27 21:30:45 +03:30
|
|
|
/// عکس فشردهشدهٔ یادداشت فعالیت (حداکثر ۲۰۰ کیلوبایت)
|
|
|
|
|
model ActivityImage {
|
|
|
|
|
id String @id
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
activityId String
|
|
|
|
|
activity Activity @relation(fields: [activityId], references: [id], onDelete: Cascade)
|
|
|
|
|
fileName String
|
|
|
|
|
mimeType String
|
|
|
|
|
sizeBytes Int
|
|
|
|
|
storageKey String
|
|
|
|
|
uploadedById String?
|
|
|
|
|
uploadedBy User? @relation("ActivityImageUploader", fields: [uploadedById], references: [id], onDelete: SetNull)
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@index([activityId, createdAt])
|
|
|
|
|
@@index([organizationId])
|
|
|
|
|
@@index([uploadedById])
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-29 21:06:30 +03:30
|
|
|
model Note {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
2026-08-29 21:06:30 +03:30
|
|
|
organizationId String
|
2026-10-10 21:28:49 +03:30
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
2026-08-29 21:06:30 +03:30
|
|
|
title String?
|
|
|
|
|
body String
|
2026-10-10 21:28:49 +03:30
|
|
|
noteType String @default("general")
|
|
|
|
|
pinned Boolean @default(false)
|
|
|
|
|
color String? @default("default")
|
|
|
|
|
tags String[] @default([])
|
2026-08-29 21:06:30 +03:30
|
|
|
reminderAt DateTime?
|
2026-09-05 19:13:01 +03:30
|
|
|
/// [{ id: string, text: string, done: boolean }]
|
|
|
|
|
checklist Json?
|
|
|
|
|
locationText String?
|
2026-10-10 21:28:49 +03:30
|
|
|
analyzeEnabled Boolean @default(true)
|
2026-08-29 21:06:30 +03:30
|
|
|
personId String?
|
2026-10-10 21:28:49 +03:30
|
|
|
person Person? @relation(fields: [personId], references: [id], onDelete: SetNull)
|
2026-08-29 21:06:30 +03:30
|
|
|
companyId String?
|
2026-10-10 21:28:49 +03:30
|
|
|
company Company? @relation(fields: [companyId], references: [id], onDelete: SetNull)
|
2026-08-29 21:06:30 +03:30
|
|
|
dealId String?
|
2026-10-10 21:28:49 +03:30
|
|
|
deal Deal? @relation(fields: [dealId], references: [id], onDelete: SetNull)
|
2026-09-05 19:13:01 +03:30
|
|
|
activityId String?
|
2026-10-10 21:28:49 +03:30
|
|
|
activity Activity? @relation(fields: [activityId], references: [id], onDelete: SetNull)
|
2026-08-29 21:06:30 +03:30
|
|
|
authorId String?
|
2026-10-10 21:28:49 +03:30
|
|
|
author User? @relation("NoteAuthor", fields: [authorId], references: [id], onDelete: SetNull)
|
2026-09-27 13:19:08 +03:30
|
|
|
/// team = قابلمشاهده برای تیم/سازمان در محدوده پرونده | personal = فقط نویسنده
|
2026-10-10 21:28:49 +03:30
|
|
|
visibility String @default("team")
|
2026-08-29 21:06:30 +03:30
|
|
|
attachments NoteAttachment[]
|
2026-09-01 14:29:34 +03:30
|
|
|
aiSuggestion NoteAiSuggestion?
|
2026-09-27 13:19:08 +03:30
|
|
|
revisions NoteRevision[]
|
2026-10-10 21:28:49 +03:30
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-08-29 21:06:30 +03:30
|
|
|
|
|
|
|
|
@@index([organizationId, pinned, updatedAt])
|
|
|
|
|
@@index([organizationId, noteType, updatedAt])
|
|
|
|
|
@@index([organizationId, reminderAt])
|
2026-09-27 13:19:08 +03:30
|
|
|
@@index([organizationId, visibility])
|
2026-08-29 21:06:30 +03:30
|
|
|
@@index([personId])
|
|
|
|
|
@@index([companyId])
|
|
|
|
|
@@index([dealId])
|
2026-09-05 19:13:01 +03:30
|
|
|
@@index([activityId])
|
2026-08-29 21:06:30 +03:30
|
|
|
@@index([authorId])
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-27 13:19:08 +03:30
|
|
|
/// اسنپشات متن قبل از هر ویرایش معنادار یادداشت
|
|
|
|
|
model NoteRevision {
|
|
|
|
|
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)
|
|
|
|
|
editorId String?
|
|
|
|
|
editor User? @relation("NoteRevisionEditor", fields: [editorId], references: [id], onDelete: SetNull)
|
|
|
|
|
title String?
|
|
|
|
|
body String
|
|
|
|
|
noteType String
|
|
|
|
|
visibility String @default("team")
|
|
|
|
|
tags String[] @default([])
|
|
|
|
|
checklist Json?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@index([noteId, createdAt])
|
|
|
|
|
@@index([organizationId])
|
|
|
|
|
@@index([editorId])
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-29 21:06:30 +03:30
|
|
|
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])
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-01 14:29:34 +03:30
|
|
|
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])
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-29 21:06:30 +03:30
|
|
|
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 {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
2026-09-05 16:26:09 +03:30
|
|
|
/// product | service
|
2026-10-10 21:28:49 +03:30
|
|
|
kind String @default("product")
|
|
|
|
|
name String
|
|
|
|
|
sku String?
|
|
|
|
|
description String?
|
|
|
|
|
category String?
|
|
|
|
|
unit String? @default("عدد")
|
|
|
|
|
stockQty Int?
|
|
|
|
|
brand String?
|
|
|
|
|
model String?
|
|
|
|
|
country String?
|
|
|
|
|
tags String[] @default([])
|
|
|
|
|
unitPrice Decimal? @db.Decimal(18, 2)
|
|
|
|
|
currency String @default("IRR")
|
|
|
|
|
isActive Boolean @default(true)
|
|
|
|
|
lineItems DealLineItem[]
|
|
|
|
|
quoteLineItems QuoteLineItem[]
|
|
|
|
|
invoiceLineItems InvoiceLineItem[]
|
|
|
|
|
stockMovements StockMovement[]
|
|
|
|
|
accountingLinks ProductAccountingLink[]
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-08-29 21:06:30 +03:30
|
|
|
|
|
|
|
|
@@index([organizationId, name])
|
|
|
|
|
@@index([organizationId, isActive])
|
2026-09-05 16:26:09 +03:30
|
|
|
@@index([organizationId, category])
|
2026-08-29 21:06:30 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
2026-08-29 21:06:30 +03:30
|
|
|
organizationId String
|
2026-10-10 21:28:49 +03:30
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
2026-08-29 21:06:30 +03:30
|
|
|
name String
|
2026-09-06 13:50:05 +03:30
|
|
|
description String?
|
2026-10-10 21:28:49 +03:30
|
|
|
publicToken String @unique @default(cuid())
|
|
|
|
|
isActive Boolean @default(true)
|
2026-09-28 13:39:44 +03:30
|
|
|
/// ثبت بهعنوان Lead در CRM (مسیر اصلی انتشار فرم)
|
2026-10-10 21:28:49 +03:30
|
|
|
createLead Boolean @default(true)
|
|
|
|
|
createPerson Boolean @default(false)
|
|
|
|
|
createDeal Boolean @default(false)
|
2026-08-29 21:06:30 +03:30
|
|
|
pipelineId String?
|
2026-09-28 13:39:44 +03:30
|
|
|
/// مرحله اولیه لید (اختیاری؛ وگرنه اولین مرحله)
|
|
|
|
|
leadStageId String?
|
|
|
|
|
/// skip | update | activity
|
2026-10-10 21:28:49 +03:30
|
|
|
duplicateMode String @default("update")
|
2026-08-29 21:06:30 +03:30
|
|
|
source String?
|
|
|
|
|
thankYouMessage String?
|
|
|
|
|
redirectUrl String?
|
2026-10-10 21:28:49 +03:30
|
|
|
assigneeIds String[] @default([])
|
|
|
|
|
roundRobinIndex Int @default(0)
|
|
|
|
|
submitLabel String @default("ارسال درخواست")
|
|
|
|
|
submitColor String @default("#14b8a6")
|
2026-09-06 13:50:05 +03:30
|
|
|
coverStorageKey String?
|
2026-10-10 21:28:49 +03:30
|
|
|
viewCount Int @default(0)
|
|
|
|
|
startCount Int @default(0)
|
|
|
|
|
submitCount Int @default(0)
|
|
|
|
|
leadCreateCount Int @default(0)
|
|
|
|
|
duplicateCount Int @default(0)
|
2026-08-29 21:06:30 +03:30
|
|
|
fields LeadFormField[]
|
2026-09-06 13:50:05 +03:30
|
|
|
submissions LeadFormSubmission[]
|
2026-10-10 21:28:49 +03:30
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-08-29 21:06:30 +03:30
|
|
|
|
|
|
|
|
@@index([organizationId, updatedAt])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model LeadFormField {
|
2026-09-06 13:50:05 +03:30
|
|
|
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)
|
|
|
|
|
placeholder String?
|
|
|
|
|
options Json?
|
2026-08-29 21:06:30 +03:30
|
|
|
|
|
|
|
|
@@unique([formId, key])
|
|
|
|
|
@@index([formId, position])
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-06 13:50:05 +03:30
|
|
|
model LeadFormSubmission {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
formId String
|
|
|
|
|
form LeadForm @relation(fields: [formId], references: [id], onDelete: Cascade)
|
|
|
|
|
values Json
|
|
|
|
|
personId String?
|
2026-09-28 13:39:44 +03:30
|
|
|
leadId String?
|
|
|
|
|
/// created | updated | duplicate_skipped | error
|
|
|
|
|
status String @default("created")
|
|
|
|
|
utm Json?
|
|
|
|
|
meta Json?
|
2026-09-06 13:50:05 +03:30
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@index([formId, createdAt])
|
2026-09-28 13:39:44 +03:30
|
|
|
@@index([leadId])
|
2026-09-06 13:50:05 +03:30
|
|
|
}
|
|
|
|
|
|
2026-08-29 21:06:30 +03:30
|
|
|
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?
|
2026-09-05 17:53:50 +03:30
|
|
|
discountAmount Decimal @default(0) @db.Decimal(18, 2)
|
|
|
|
|
taxPercent Decimal @default(0) @db.Decimal(8, 2)
|
|
|
|
|
issuedAt DateTime?
|
2026-08-29 21:06:30 +03:30
|
|
|
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 {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
number String
|
|
|
|
|
title String
|
|
|
|
|
status InvoiceStatus @default(draft)
|
|
|
|
|
kind String @default("invoice") // invoice | credit
|
|
|
|
|
currency String @default("IRR")
|
|
|
|
|
notes String?
|
|
|
|
|
discountAmount Decimal @default(0) @db.Decimal(18, 2)
|
|
|
|
|
taxPercent Decimal @default(0) @db.Decimal(8, 2)
|
|
|
|
|
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[]
|
|
|
|
|
stockMovements StockMovement[]
|
|
|
|
|
commissionPercent Decimal? @db.Decimal(8, 2)
|
|
|
|
|
commissionFixedAmount Decimal? @db.Decimal(18, 2)
|
|
|
|
|
commissionEntry CommissionEntry?
|
|
|
|
|
cheques Cheque[]
|
|
|
|
|
routineTasks RoutineTask[]
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-08-29 21:06:30 +03:30
|
|
|
|
|
|
|
|
@@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 {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
2026-08-29 21:06:30 +03:30
|
|
|
invoiceId String
|
2026-10-10 21:28:49 +03:30
|
|
|
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())
|
2026-08-29 21:06:30 +03:30
|
|
|
notes String?
|
2026-10-18 19:33:09 +03:30
|
|
|
/// اگر پر باشد، این پرداخت یکبار در صندوق نشسته است
|
|
|
|
|
financialAccountId String?
|
2026-10-10 21:28:49 +03:30
|
|
|
installmentId String? @unique
|
2026-08-29 21:06:30 +03:30
|
|
|
installment InvoiceInstallment? @relation(fields: [installmentId], references: [id], onDelete: SetNull)
|
2026-09-11 01:09:59 +03:30
|
|
|
cheque Cheque?
|
2026-10-10 21:28:49 +03:30
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-08-29 21:06:30 +03:30
|
|
|
|
|
|
|
|
@@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
|
2026-09-05 18:38:11 +03:30
|
|
|
/// sales|customer|revenue|deal|activity|quality
|
|
|
|
|
type String @default("sales")
|
2026-08-29 21:06:30 +03:30
|
|
|
periodStart DateTime
|
|
|
|
|
periodEnd DateTime
|
|
|
|
|
targetAmount Decimal @db.Decimal(18, 2)
|
2026-09-05 18:38:11 +03:30
|
|
|
forecastAmount Decimal? @db.Decimal(18, 2)
|
|
|
|
|
/// 0-100
|
|
|
|
|
probability Decimal? @db.Decimal(5, 2)
|
2026-08-29 21:06:30 +03:30
|
|
|
currency String @default("IRR")
|
|
|
|
|
notes String?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, periodStart, periodEnd])
|
|
|
|
|
@@index([userId])
|
2026-09-05 18:38:11 +03:30
|
|
|
@@index([organizationId, type])
|
2026-08-29 21:06:30 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 {
|
2026-10-10 21:28:49 +03:30
|
|
|
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?
|
2026-08-29 21:06:30 +03:30
|
|
|
/// مهلت پاسخ اول بر اساس اولویت
|
2026-10-10 21:28:49 +03:30
|
|
|
slaDueAt DateTime?
|
2026-08-29 21:06:30 +03:30
|
|
|
firstRespondedAt DateTime?
|
2026-10-10 21:28:49 +03:30
|
|
|
csatScore Int?
|
|
|
|
|
csatComment String?
|
|
|
|
|
csatAt DateTime?
|
|
|
|
|
messages TicketMessage[]
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-08-29 21:06:30 +03:30
|
|
|
|
|
|
|
|
@@unique([organizationId, number])
|
|
|
|
|
@@index([organizationId, status, updatedAt])
|
|
|
|
|
@@index([organizationId, slaDueAt])
|
|
|
|
|
@@index([assigneeId])
|
|
|
|
|
@@index([reporterId])
|
|
|
|
|
@@index([personId])
|
|
|
|
|
@@index([companyId])
|
|
|
|
|
@@index([dealId])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model TicketMessage {
|
2026-10-10 21:28:49 +03:30
|
|
|
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())
|
2026-08-29 21:06:30 +03:30
|
|
|
|
|
|
|
|
@@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])
|
|
|
|
|
}
|
2026-08-30 09:42:07 +03:30
|
|
|
|
2026-10-06 20:51:50 +03:30
|
|
|
/// برد کانبان برای کارهای داخلی / پسازفروش (جدا از مسیر فروش)
|
2026-08-30 09:42:07 +03:30
|
|
|
model ProjectBoard {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
2026-08-30 09:42:07 +03:30
|
|
|
organizationId String
|
2026-10-10 21:28:49 +03:30
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
2026-08-30 09:42:07 +03:30
|
|
|
name String
|
2026-10-10 21:28:49 +03:30
|
|
|
isDefault Boolean @default(false)
|
2026-08-30 09:42:07 +03:30
|
|
|
stages ProjectBoardStage[]
|
|
|
|
|
cards ProjectCard[]
|
2026-10-10 21:28:49 +03:30
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-08-30 09:42:07 +03:30
|
|
|
|
|
|
|
|
@@index([organizationId])
|
|
|
|
|
@@index([organizationId, isDefault])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model ProjectBoardStage {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
2026-08-30 09:42:07 +03:30
|
|
|
boardId String
|
2026-10-10 21:28:49 +03:30
|
|
|
board ProjectBoard @relation(fields: [boardId], references: [id], onDelete: Cascade)
|
2026-08-30 09:42:07 +03:30
|
|
|
name String
|
|
|
|
|
position Int
|
|
|
|
|
cards ProjectCard[]
|
2026-10-10 21:28:49 +03:30
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-08-30 09:42:07 +03:30
|
|
|
|
|
|
|
|
@@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?
|
2026-09-26 14:59:48 +03:30
|
|
|
tags String[] @default([])
|
2026-08-30 09:42:07 +03:30
|
|
|
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)
|
2026-09-26 14:59:48 +03:30
|
|
|
assignees User[] @relation("ProjectCardAssignees")
|
2026-08-30 09:42:07 +03:30
|
|
|
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])
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-06 13:50:05 +03:30
|
|
|
model LoyaltySettings {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String @unique
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
enabled Boolean @default(true)
|
|
|
|
|
welcomePoints Int @default(0)
|
|
|
|
|
notifyOnAdjust Boolean @default(false)
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model LoyaltyLedger {
|
|
|
|
|
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)
|
|
|
|
|
delta Int
|
|
|
|
|
reason String?
|
|
|
|
|
source String @default("manual")
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, createdAt])
|
|
|
|
|
@@index([personId, createdAt])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model LoyaltyReward {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
name String
|
|
|
|
|
description String?
|
|
|
|
|
pointsCost Int
|
|
|
|
|
isActive Boolean @default(true)
|
|
|
|
|
position Int @default(0)
|
|
|
|
|
color String?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
redemptions LoyaltyRedemption[]
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, position])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model LoyaltyRedemption {
|
|
|
|
|
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)
|
|
|
|
|
rewardId String
|
|
|
|
|
reward LoyaltyReward @relation(fields: [rewardId], references: [id], onDelete: Restrict)
|
|
|
|
|
pointsSpent Int
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, createdAt])
|
|
|
|
|
@@index([personId])
|
|
|
|
|
@@index([rewardId])
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-30 09:42:07 +03:30
|
|
|
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("{}")
|
2026-10-11 14:12:36 +03:30
|
|
|
cursors AutomationCursor[]
|
2026-08-30 09:42:07 +03:30
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, isActive, trigger])
|
|
|
|
|
}
|
2026-09-04 22:00:23 +03:30
|
|
|
|
2026-10-11 14:12:36 +03:30
|
|
|
/// شمارندهٔ تقسیم نوبتی برای هر گام عمل در یک قانون اتوماسیون
|
|
|
|
|
model AutomationCursor {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
ruleId String
|
|
|
|
|
rule AutomationRule @relation(fields: [ruleId], references: [id], onDelete: Cascade)
|
|
|
|
|
stepId String
|
|
|
|
|
counter Int @default(0)
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@unique([ruleId, stepId])
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-13 19:11:47 +03:30
|
|
|
enum MessageAutomationTrigger {
|
|
|
|
|
person_created
|
|
|
|
|
lead_submitted
|
|
|
|
|
deal_created
|
|
|
|
|
deal_stage_changed
|
|
|
|
|
deal_won
|
|
|
|
|
deal_lost
|
|
|
|
|
ticket_created
|
|
|
|
|
ticket_resolved
|
|
|
|
|
invoice_issued
|
|
|
|
|
invoice_paid
|
|
|
|
|
activity_created
|
|
|
|
|
activity_done
|
2026-10-01 15:59:18 +03:30
|
|
|
sms_link_clicked
|
2026-09-13 19:11:47 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum MessageAutomationDelayUnit {
|
|
|
|
|
minutes
|
|
|
|
|
hours
|
|
|
|
|
days
|
2026-09-13 21:22:41 +03:30
|
|
|
weeks
|
|
|
|
|
months
|
2026-09-13 19:11:47 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum MessageAutomationChannel {
|
|
|
|
|
app
|
|
|
|
|
sms
|
|
|
|
|
both
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum MessageAutomationTarget {
|
|
|
|
|
staff
|
|
|
|
|
person
|
|
|
|
|
both
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum MessageAutomationJobStatus {
|
|
|
|
|
pending
|
|
|
|
|
sent
|
|
|
|
|
cancelled
|
|
|
|
|
failed
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model MessageAutomationRule {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
name String
|
|
|
|
|
isActive Boolean @default(true)
|
|
|
|
|
trigger MessageAutomationTrigger
|
|
|
|
|
startDelayValue Int @default(0)
|
|
|
|
|
startDelayUnit MessageAutomationDelayUnit @default(minutes)
|
|
|
|
|
repeatCount Int @default(1)
|
|
|
|
|
intervalValue Int @default(1)
|
|
|
|
|
intervalUnit MessageAutomationDelayUnit @default(days)
|
|
|
|
|
notifyTarget MessageAutomationTarget @default(person)
|
|
|
|
|
channel MessageAutomationChannel @default(sms)
|
|
|
|
|
title String
|
2026-09-14 16:49:43 +03:30
|
|
|
description String?
|
2026-09-13 19:11:47 +03:30
|
|
|
body String
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
jobs MessageAutomationJob[]
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, isActive, trigger])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model MessageAutomationJob {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
ruleId String
|
|
|
|
|
rule MessageAutomationRule @relation(fields: [ruleId], references: [id], onDelete: Cascade)
|
|
|
|
|
trigger MessageAutomationTrigger
|
|
|
|
|
entityType String
|
|
|
|
|
entityId String
|
|
|
|
|
entityTitle String
|
|
|
|
|
personId String?
|
|
|
|
|
ownerId String?
|
|
|
|
|
assigneeId String?
|
|
|
|
|
attemptIndex Int
|
|
|
|
|
sendAt DateTime
|
|
|
|
|
status MessageAutomationJobStatus @default(pending)
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
sentAt DateTime?
|
|
|
|
|
|
|
|
|
|
@@unique([ruleId, entityId, attemptIndex])
|
|
|
|
|
@@index([status, sendAt])
|
|
|
|
|
@@index([organizationId, entityType, entityId, status])
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-05 00:47:33 +03:30
|
|
|
/// چالش OTP برای ثبتنام / ورود پنل
|
|
|
|
|
model AuthChallenge {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
/// register | login
|
|
|
|
|
kind String
|
|
|
|
|
/// برای login: email | mobile
|
|
|
|
|
channel String?
|
|
|
|
|
email String?
|
|
|
|
|
mobile String?
|
|
|
|
|
passwordHash String?
|
|
|
|
|
/// دادهٔ موقت ثبتنام (سازمان، نام، زمینه، کد معرف، …)
|
|
|
|
|
payload Json @default("{}")
|
|
|
|
|
emailCode String?
|
|
|
|
|
mobileCode String?
|
|
|
|
|
emailVerified Boolean @default(false)
|
|
|
|
|
mobileVerified Boolean @default(false)
|
|
|
|
|
expiresAt DateTime
|
|
|
|
|
consumedAt DateTime?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@index([kind, createdAt])
|
|
|
|
|
@@index([expiresAt])
|
|
|
|
|
@@index([email])
|
|
|
|
|
@@index([mobile])
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-04 22:00:23 +03:30
|
|
|
/// اتصال به نرمافزار حسابداری خارجی (تهحساب) — چند اتصال/DB بهازای هر سازمان
|
|
|
|
|
model AccountingConnection {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
2026-09-04 22:00:23 +03:30
|
|
|
/// فعلاً فقط tahesab
|
2026-10-10 21:28:49 +03:30
|
|
|
provider String @default("tahesab")
|
|
|
|
|
label String
|
|
|
|
|
baseUrl String
|
|
|
|
|
dbName String
|
2026-09-04 22:00:23 +03:30
|
|
|
/// AES-256-GCM ciphertext (base64)
|
2026-10-10 21:28:49 +03:30
|
|
|
tokenCiphertext String
|
|
|
|
|
tokenIv String
|
|
|
|
|
tokenTag String
|
2026-09-04 22:00:23 +03:30
|
|
|
/// چهار کاراکتر آخر توکن برای نمایش ماسکشده
|
2026-10-10 21:28:49 +03:30
|
|
|
tokenLast4 String @default("")
|
|
|
|
|
enabled Boolean @default(true)
|
|
|
|
|
isDefault Boolean @default(false)
|
2026-09-04 22:00:23 +03:30
|
|
|
/// اگر true باشد verify SSL نادیده گرفته میشود (فقط برای نصبهای داخلی با گواهی خودامضا)
|
2026-10-10 21:28:49 +03:30
|
|
|
allowInsecureTls Boolean @default(false)
|
|
|
|
|
lastHealthAt DateTime?
|
|
|
|
|
lastHealthOk Boolean?
|
|
|
|
|
lastError String?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
personLinks PersonAccountingLink[]
|
|
|
|
|
productLinks ProductAccountingLink[]
|
|
|
|
|
documentLogs AccountingDocumentLog[]
|
|
|
|
|
balanceCaches AccountingBalanceCache[]
|
2026-10-22 18:20:46 +03:30
|
|
|
asnadLines AccountingAsnadLine[]
|
2026-09-04 22:00:23 +03:30
|
|
|
|
|
|
|
|
@@unique([organizationId, baseUrl, dbName])
|
|
|
|
|
@@index([organizationId, enabled])
|
|
|
|
|
@@index([organizationId, isDefault])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model PersonAccountingLink {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
|
|
|
|
personId String
|
|
|
|
|
person Person @relation(fields: [personId], references: [id], onDelete: Cascade)
|
|
|
|
|
connectionId String
|
|
|
|
|
connection AccountingConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
|
2026-09-04 22:00:23 +03:30
|
|
|
/// کد مشتری در تهحساب (Moshtari_Code)
|
2026-10-10 21:28:49 +03:30
|
|
|
moshtariCode Int
|
2026-09-04 22:00:23 +03:30
|
|
|
/// manual | mobile | name | created
|
2026-10-10 21:28:49 +03:30
|
|
|
matchMethod String
|
|
|
|
|
linkedAt DateTime @default(now())
|
|
|
|
|
lastBalanceSyncedAt DateTime?
|
|
|
|
|
balanceCache AccountingBalanceCache?
|
2026-10-22 18:20:46 +03:30
|
|
|
asnadLines AccountingAsnadLine[]
|
2026-10-10 21:28:49 +03:30
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-09-04 22:00:23 +03:30
|
|
|
|
|
|
|
|
@@unique([personId, connectionId])
|
|
|
|
|
@@unique([connectionId, moshtariCode])
|
|
|
|
|
@@index([personId])
|
|
|
|
|
@@index([connectionId])
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-21 21:25:34 +03:30
|
|
|
/// لینک محصول CRM ↔ نام کارساخته (یا معادل) در تهحساب
|
|
|
|
|
model ProductAccountingLink {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
productId String
|
|
|
|
|
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
|
|
|
|
|
connectionId String
|
|
|
|
|
connection AccountingConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
|
|
|
|
|
/// نام کارساخته در تهحساب (DoListNameKarsakhte.Name)
|
|
|
|
|
externalName String
|
|
|
|
|
ayar Int?
|
|
|
|
|
vaznSabet Float?
|
|
|
|
|
/// import | manual
|
|
|
|
|
matchMethod String @default("import")
|
|
|
|
|
linkedAt DateTime @default(now())
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@unique([productId, connectionId])
|
|
|
|
|
@@unique([connectionId, externalName])
|
|
|
|
|
@@index([productId])
|
|
|
|
|
@@index([connectionId])
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-04 22:00:23 +03:30
|
|
|
model AccountingDocumentLog {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
|
|
|
|
connectionId String
|
|
|
|
|
connection AccountingConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
|
|
|
|
|
personId String?
|
|
|
|
|
person Person? @relation(fields: [personId], references: [id], onDelete: SetNull)
|
2026-09-04 22:00:23 +03:30
|
|
|
/// مثلاً DoNewSanadBuySaleGOLD
|
2026-10-10 21:28:49 +03:30
|
|
|
docType String
|
2026-09-04 22:00:23 +03:30
|
|
|
/// کد یکتای ≥۲۰ کاراکتر — idempotency
|
2026-10-10 21:28:49 +03:30
|
|
|
factorCode String
|
|
|
|
|
requestPayload Json
|
|
|
|
|
responsePayload Json?
|
2026-09-04 22:00:23 +03:30
|
|
|
/// pending | success | failed
|
2026-10-10 21:28:49 +03:30
|
|
|
status String @default("pending")
|
|
|
|
|
tahesabOk String?
|
|
|
|
|
tahesabFactorId Int?
|
|
|
|
|
errorMessage String?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-09-04 22:00:23 +03:30
|
|
|
|
|
|
|
|
@@unique([connectionId, factorCode])
|
|
|
|
|
@@index([connectionId, status])
|
|
|
|
|
@@index([personId])
|
|
|
|
|
@@index([docType])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model AccountingBalanceCache {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
|
|
|
|
linkId String @unique
|
|
|
|
|
link PersonAccountingLink @relation(fields: [linkId], references: [id], onDelete: Cascade)
|
|
|
|
|
connectionId String
|
|
|
|
|
connection AccountingConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
|
|
|
|
|
goldBalance Float?
|
|
|
|
|
rialBalance Float?
|
2026-09-04 22:00:23 +03:30
|
|
|
currencyBalance Float?
|
|
|
|
|
/// مانده همارز طلا (MandeyeKolBeTALA) در صورت وجود
|
2026-10-10 21:28:49 +03:30
|
|
|
totalGoldEquiv Float?
|
2026-09-04 22:00:23 +03:30
|
|
|
/// جزئیات گروهبندیشده برای UI
|
2026-10-10 21:28:49 +03:30
|
|
|
mandehDetail Json @default("[]")
|
|
|
|
|
rawResponse Json?
|
|
|
|
|
fetchedAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-09-04 22:00:23 +03:30
|
|
|
|
|
|
|
|
@@index([connectionId])
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-22 18:20:46 +03:30
|
|
|
/// اسنپشات ردیفهای DoListAsnad برای گزارش فروش گروهی
|
|
|
|
|
model AccountingAsnadLine {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
connectionId String
|
|
|
|
|
connection AccountingConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
|
|
|
|
|
personId String?
|
|
|
|
|
person Person? @relation(fields: [personId], references: [id], onDelete: SetNull)
|
|
|
|
|
linkId String?
|
|
|
|
|
link PersonAccountingLink? @relation(fields: [linkId], references: [id], onDelete: SetNull)
|
|
|
|
|
moshtariCode Int
|
|
|
|
|
/// کلید پایدار برای upsert: ID تهحساب یا f:factor|r:radif|t:tarikh|n:no
|
|
|
|
|
lineKey String
|
|
|
|
|
tahesabId Int?
|
|
|
|
|
factorCode String?
|
|
|
|
|
shFactor Int?
|
|
|
|
|
radif Int?
|
|
|
|
|
/// شمسی YYYY-MM-DD
|
|
|
|
|
tarikh String
|
|
|
|
|
noRaw String @default("")
|
|
|
|
|
/// sale | return | purchase | other
|
|
|
|
|
kind String @default("other")
|
|
|
|
|
jensFelez Int?
|
|
|
|
|
noKar String?
|
|
|
|
|
vazn Float?
|
|
|
|
|
tabdilVazn Float?
|
|
|
|
|
mali Float?
|
|
|
|
|
count Float?
|
|
|
|
|
ayar Float?
|
2026-10-26 11:05:34 +03:30
|
|
|
/// درصد اجرت سند تهحساب؛ بعد از همگامسازی پر میشود
|
|
|
|
|
darsad Float?
|
|
|
|
|
darsadTalayee Float?
|
|
|
|
|
maliatDarsad Float?
|
2026-10-22 18:20:46 +03:30
|
|
|
mazaneh Float?
|
|
|
|
|
fi Float?
|
|
|
|
|
sharh1 String?
|
|
|
|
|
syncedAt DateTime @default(now())
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@unique([connectionId, lineKey])
|
|
|
|
|
@@index([organizationId, connectionId, tarikh])
|
|
|
|
|
@@index([organizationId, kind])
|
|
|
|
|
@@index([personId])
|
|
|
|
|
@@index([linkId])
|
|
|
|
|
@@index([moshtariCode])
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-04 22:00:23 +03:30
|
|
|
model AccountingSyncJob {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
connectionId String?
|
|
|
|
|
/// search | import | match
|
|
|
|
|
kind String
|
|
|
|
|
status String @default("pending")
|
|
|
|
|
request Json @default("{}")
|
|
|
|
|
result Json?
|
|
|
|
|
errorMessage String?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, createdAt])
|
|
|
|
|
}
|
2026-09-10 22:24:16 +03:30
|
|
|
|
2026-10-24 12:34:02 +03:30
|
|
|
/// تنظیمات ظاهر فاکتور PDF اسناد تهحساب (جدا از فاکتور CRM)
|
|
|
|
|
model TahesabInvoiceSettings {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String @unique
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
/// data URL تصویر لوگو (مثلاً data:image/png;base64,...)
|
|
|
|
|
logoDataUrl String? @db.Text
|
|
|
|
|
companyName String @default("")
|
|
|
|
|
companySubtitle String @default("")
|
|
|
|
|
address String @default("")
|
|
|
|
|
phone String @default("")
|
|
|
|
|
headerText String @default("")
|
|
|
|
|
footerText String @default("")
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-10 22:24:16 +03:30
|
|
|
/// انبار داخلی — وقتی تهحساب وصل نیست
|
|
|
|
|
model Warehouse {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
name String
|
|
|
|
|
isDefault Boolean @default(true)
|
|
|
|
|
movements StockMovement[]
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, isDefault])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// گردش کالا؛ مقدار مثبت ورود، منفی خروج
|
|
|
|
|
model StockMovement {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
warehouseId String
|
|
|
|
|
warehouse Warehouse @relation(fields: [warehouseId], references: [id], onDelete: Cascade)
|
|
|
|
|
productId String
|
|
|
|
|
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
|
|
|
|
|
/// opening | purchase | sale | credit | adjustment
|
|
|
|
|
type String
|
|
|
|
|
quantity Decimal @db.Decimal(18, 2)
|
|
|
|
|
note String?
|
|
|
|
|
invoiceId String?
|
|
|
|
|
invoice Invoice? @relation(fields: [invoiceId], references: [id], onDelete: Cascade)
|
|
|
|
|
occurredAt DateTime @default(now())
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, productId, occurredAt])
|
|
|
|
|
@@index([invoiceId])
|
|
|
|
|
@@index([warehouseId])
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-11 01:09:59 +03:30
|
|
|
/// پورسانت کارشناس بعد از صدور فاکتور
|
|
|
|
|
model CommissionEntry {
|
|
|
|
|
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)
|
|
|
|
|
invoiceId String @unique
|
|
|
|
|
invoice Invoice @relation(fields: [invoiceId], references: [id], onDelete: Cascade)
|
|
|
|
|
percent Decimal @db.Decimal(8, 2)
|
|
|
|
|
amount Decimal @db.Decimal(18, 2)
|
|
|
|
|
currency String @default("IRR")
|
|
|
|
|
/// posted | void
|
|
|
|
|
status String @default("posted")
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, userId, createdAt])
|
|
|
|
|
@@index([status])
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-11 11:34:49 +03:30
|
|
|
/// چک دریافتی؛ مستقل یا وصل به پرداخت فاکتور
|
2026-09-11 01:09:59 +03:30
|
|
|
model Cheque {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
paymentId String? @unique
|
|
|
|
|
payment Payment? @relation(fields: [paymentId], references: [id], onDelete: Cascade)
|
|
|
|
|
invoiceId String?
|
|
|
|
|
invoice Invoice? @relation(fields: [invoiceId], references: [id], onDelete: Cascade)
|
|
|
|
|
personId String?
|
|
|
|
|
person Person? @relation(fields: [personId], references: [id], onDelete: SetNull)
|
|
|
|
|
ownerId String?
|
|
|
|
|
owner User? @relation("ChequeOwner", fields: [ownerId], references: [id], onDelete: SetNull)
|
|
|
|
|
amount Decimal @db.Decimal(18, 2)
|
|
|
|
|
currency String @default("IRR")
|
|
|
|
|
number String
|
|
|
|
|
bank String
|
|
|
|
|
dueDate DateTime
|
2026-09-11 01:09:59 +03:30
|
|
|
/// pending | cleared | bounced
|
2026-10-10 21:28:49 +03:30
|
|
|
status String @default("pending")
|
2026-09-11 11:34:49 +03:30
|
|
|
/// in = دریافتی، out = پرداختی
|
2026-10-10 21:28:49 +03:30
|
|
|
direction String @default("in")
|
2026-09-11 11:34:49 +03:30
|
|
|
reminderNotifiedAt DateTime?
|
2026-10-18 19:33:09 +03:30
|
|
|
/// حساب صندوق برای لحظهٔ وصول؛ تا قبل از وصول موجودی را عوض نمیکند
|
|
|
|
|
financialAccountId String?
|
2026-10-10 21:28:49 +03:30
|
|
|
notes String?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-09-11 01:09:59 +03:30
|
|
|
|
|
|
|
|
@@index([organizationId, status, dueDate])
|
2026-09-11 11:34:49 +03:30
|
|
|
@@index([organizationId, direction, dueDate])
|
2026-09-11 01:09:59 +03:30
|
|
|
@@index([invoiceId])
|
2026-09-11 11:34:49 +03:30
|
|
|
@@index([personId])
|
|
|
|
|
@@index([ownerId])
|
2026-09-11 01:09:59 +03:30
|
|
|
}
|
|
|
|
|
|
2026-09-10 22:24:16 +03:30
|
|
|
/// مانده اول دوره طرفحساب (مثبت = بدهکار ما)
|
|
|
|
|
model PartyOpeningBalance {
|
|
|
|
|
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)
|
|
|
|
|
amount Decimal @db.Decimal(18, 2)
|
|
|
|
|
currency String @default("IRR")
|
|
|
|
|
note String?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@unique([organizationId, personId])
|
|
|
|
|
@@index([personId])
|
|
|
|
|
}
|
2026-09-26 14:59:48 +03:30
|
|
|
|
|
|
|
|
/// لاگ فعالیت کاربران روی منابع سازمان
|
|
|
|
|
model AuditLog {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
actorUserId String?
|
|
|
|
|
actor User? @relation("AuditLogActor", fields: [actorUserId], references: [id], onDelete: SetNull)
|
|
|
|
|
action String
|
|
|
|
|
method String
|
|
|
|
|
path String
|
|
|
|
|
entityType String?
|
|
|
|
|
entityId String?
|
|
|
|
|
summary String
|
|
|
|
|
meta Json?
|
|
|
|
|
ip String?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, createdAt])
|
|
|
|
|
@@index([organizationId, actorUserId, createdAt])
|
|
|
|
|
@@index([organizationId, entityType, createdAt])
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-13 22:23:09 +03:30
|
|
|
/// نسخهٔ قابل بازگشتِ رکوردهای حذفشده
|
|
|
|
|
model DeletionArchive {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
/// نام مدل پریزما
|
|
|
|
|
model String
|
|
|
|
|
entityId String
|
|
|
|
|
label String
|
|
|
|
|
snapshot Json
|
|
|
|
|
deletedById String?
|
|
|
|
|
deletedBy User? @relation("DeletionActor", fields: [deletedById], references: [id], onDelete: SetNull)
|
|
|
|
|
deletedAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, deletedAt])
|
|
|
|
|
@@index([organizationId, model])
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-06 20:51:50 +03:30
|
|
|
/// فیلترهای ذخیرهشده برای لیستها و مسیرهای فروش
|
2026-10-05 11:56:49 +03:30
|
|
|
/// ستونهای مخفی هر جدول برای هر کاربر (بهازای هر سازمان)
|
|
|
|
|
model UserTablePreference {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
userId String
|
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
|
organizationId String
|
|
|
|
|
tableKey String
|
|
|
|
|
/// ستون جدیدی که بعداً اضافه شود بهصورت پیشفرض نمایش داده میشود
|
|
|
|
|
hiddenColumns String[] @default([])
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@unique([userId, organizationId, tableKey])
|
|
|
|
|
@@index([userId, organizationId])
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-26 14:59:48 +03:30
|
|
|
model SavedView {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
ownerUserId String?
|
|
|
|
|
owner User? @relation("SavedViewOwner", fields: [ownerUserId], references: [id], onDelete: SetNull)
|
|
|
|
|
name String
|
2026-09-27 11:11:33 +03:30
|
|
|
/// deals | people | companies | activities | project_cards | reports
|
2026-09-26 14:59:48 +03:30
|
|
|
scope String
|
|
|
|
|
filters Json @default("{}")
|
|
|
|
|
isShared Boolean @default(false)
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, scope])
|
|
|
|
|
@@index([organizationId, ownerUserId])
|
|
|
|
|
}
|
2026-09-26 16:12:29 +03:30
|
|
|
|
|
|
|
|
/// کلید API عمومی برای یکپارچهسازی خارجی
|
|
|
|
|
model OrganizationApiKey {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
name String
|
|
|
|
|
/// پیشوند قابلنمایش (مثلاً mk_ab12)
|
|
|
|
|
prefix String
|
|
|
|
|
/// SHA-256 هش کلید کامل
|
|
|
|
|
keyHash String
|
|
|
|
|
lastUsedAt DateTime?
|
|
|
|
|
revokedAt DateTime?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@unique([keyHash])
|
|
|
|
|
@@index([organizationId, revokedAt])
|
|
|
|
|
@@index([prefix])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// وبهوک خروجی سازمان
|
|
|
|
|
model WebhookEndpoint {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
2026-09-26 16:12:29 +03:30
|
|
|
organizationId String
|
2026-10-10 21:28:49 +03:30
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
2026-09-26 16:12:29 +03:30
|
|
|
name String
|
|
|
|
|
url String
|
|
|
|
|
secret String
|
2026-10-10 21:28:49 +03:30
|
|
|
events String[] @default([])
|
|
|
|
|
isActive Boolean @default(true)
|
2026-09-26 16:12:29 +03:30
|
|
|
deliveries WebhookDelivery[]
|
2026-10-10 21:28:49 +03:30
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-09-26 16:12:29 +03:30
|
|
|
|
|
|
|
|
@@index([organizationId, isActive])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model WebhookDelivery {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
endpointId String
|
|
|
|
|
endpoint WebhookEndpoint @relation(fields: [endpointId], references: [id], onDelete: Cascade)
|
|
|
|
|
event String
|
|
|
|
|
payload Json
|
|
|
|
|
status String @default("pending")
|
|
|
|
|
statusCode Int?
|
|
|
|
|
attempts Int @default(0)
|
|
|
|
|
lastError String?
|
|
|
|
|
nextAttemptAt DateTime @default(now())
|
|
|
|
|
deliveredAt DateTime?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, createdAt])
|
|
|
|
|
@@index([status, nextAttemptAt])
|
|
|
|
|
@@index([endpointId, createdAt])
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-18 18:49:49 +03:30
|
|
|
/// دفتر مالی داخلی: حساب صندوق/بانک/تنخواه
|
|
|
|
|
model FinancialAccount {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
name String
|
|
|
|
|
/// bank | cash | petty
|
|
|
|
|
type String
|
|
|
|
|
bankName String?
|
|
|
|
|
accountRef String?
|
|
|
|
|
responsibleName String?
|
|
|
|
|
openingBalance Decimal @default(0) @db.Decimal(18, 2)
|
|
|
|
|
openingDate DateTime
|
|
|
|
|
notes String?
|
|
|
|
|
isActive Boolean @default(true)
|
|
|
|
|
currency String @default("IRR")
|
|
|
|
|
createdById String?
|
|
|
|
|
movements FinancialMovement[]
|
|
|
|
|
documents FinancialDocument[] @relation("DocumentAccount")
|
|
|
|
|
reconciliations FinancialReconciliation[]
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, isActive])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model FinancialCategory {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
name String
|
|
|
|
|
/// income | expense
|
|
|
|
|
kind String
|
|
|
|
|
isActive Boolean @default(true)
|
|
|
|
|
documents FinancialDocument[]
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@unique([organizationId, kind, name])
|
|
|
|
|
@@index([organizationId, kind])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// درآمد یا هزینهٔ ثبتشده (تعهدی). موجودی حساب را خودش عوض نمیکند.
|
|
|
|
|
model FinancialDocument {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
/// income | expense
|
|
|
|
|
kind String
|
|
|
|
|
title String
|
|
|
|
|
amount Decimal @db.Decimal(18, 2)
|
|
|
|
|
bookedAt DateTime
|
|
|
|
|
dueAt DateTime?
|
|
|
|
|
categoryId String?
|
|
|
|
|
category FinancialCategory? @relation(fields: [categoryId], references: [id], onDelete: SetNull)
|
|
|
|
|
personId String?
|
|
|
|
|
person Person? @relation(fields: [personId], references: [id], onDelete: SetNull)
|
|
|
|
|
partyName String?
|
|
|
|
|
accountId String?
|
|
|
|
|
account FinancialAccount? @relation("DocumentAccount", fields: [accountId], references: [id], onDelete: SetNull)
|
|
|
|
|
/// cash | card_to_card | pos | transfer | other
|
|
|
|
|
method String?
|
|
|
|
|
/// open | partial | settled | void
|
|
|
|
|
status String @default("open")
|
|
|
|
|
reference String?
|
|
|
|
|
notes String?
|
|
|
|
|
createdById String?
|
|
|
|
|
voidedAt DateTime?
|
|
|
|
|
voidReason String?
|
|
|
|
|
movements FinancialMovement[]
|
2026-10-18 19:33:09 +03:30
|
|
|
attachments FinancialAttachment[]
|
2026-10-18 18:49:49 +03:30
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, kind, bookedAt])
|
|
|
|
|
@@index([organizationId, status])
|
|
|
|
|
@@index([personId])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// گردش قطعی حساب. افتتاحیه، دریافت، پرداخت، انتقال.
|
|
|
|
|
model FinancialMovement {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
accountId String
|
|
|
|
|
account FinancialAccount @relation(fields: [accountId], references: [id], onDelete: Cascade)
|
|
|
|
|
documentId String?
|
|
|
|
|
document FinancialDocument? @relation(fields: [documentId], references: [id], onDelete: SetNull)
|
|
|
|
|
receivableId String?
|
|
|
|
|
receivable FinancialReceivable? @relation(fields: [receivableId], references: [id], onDelete: SetNull)
|
|
|
|
|
installmentId String?
|
|
|
|
|
installment FinancialInstallment? @relation(fields: [installmentId], references: [id], onDelete: SetNull)
|
|
|
|
|
payrollId String?
|
|
|
|
|
payroll FinancialPayroll? @relation(fields: [payrollId], references: [id], onDelete: SetNull)
|
2026-10-18 19:33:09 +03:30
|
|
|
paymentId String?
|
|
|
|
|
chequeId String?
|
2026-10-18 18:49:49 +03:30
|
|
|
/// in | out
|
|
|
|
|
direction String
|
|
|
|
|
amount Decimal @db.Decimal(18, 2)
|
|
|
|
|
/// opening | receipt | payment | transfer_in | transfer_out
|
|
|
|
|
kind String
|
|
|
|
|
transferGroupId String?
|
|
|
|
|
occurredAt DateTime
|
|
|
|
|
memo String?
|
|
|
|
|
reference String?
|
|
|
|
|
createdById String?
|
|
|
|
|
voidedAt DateTime?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, accountId, occurredAt])
|
|
|
|
|
@@index([transferGroupId])
|
|
|
|
|
@@index([documentId])
|
|
|
|
|
@@index([receivableId])
|
|
|
|
|
@@index([installmentId])
|
|
|
|
|
@@index([payrollId])
|
2026-10-18 19:33:09 +03:30
|
|
|
@@index([paymentId])
|
|
|
|
|
@@index([chequeId])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model FinancialAuditLog {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
entityType String
|
|
|
|
|
entityId String
|
|
|
|
|
action String
|
|
|
|
|
before Json?
|
|
|
|
|
after Json?
|
|
|
|
|
actorUserId String?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, entityType, entityId, createdAt])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model FinancialAttachment {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
documentId String
|
|
|
|
|
document FinancialDocument @relation(fields: [documentId], references: [id], onDelete: Cascade)
|
|
|
|
|
fileName String
|
|
|
|
|
mimeType String
|
|
|
|
|
storageKey String
|
|
|
|
|
createdById String?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@index([documentId])
|
2026-10-18 18:49:49 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model FinancialPayroll {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
userId String
|
|
|
|
|
period String
|
|
|
|
|
baseAmount Decimal @db.Decimal(18, 2)
|
|
|
|
|
benefits Decimal @default(0) @db.Decimal(18, 2)
|
|
|
|
|
bonus Decimal @default(0) @db.Decimal(18, 2)
|
|
|
|
|
deductions Decimal @default(0) @db.Decimal(18, 2)
|
|
|
|
|
payable Decimal @db.Decimal(18, 2)
|
|
|
|
|
dueDate DateTime
|
|
|
|
|
notes String?
|
|
|
|
|
/// open | partial | settled | void
|
|
|
|
|
status String @default("open")
|
|
|
|
|
createdById String?
|
|
|
|
|
voidedAt DateTime?
|
|
|
|
|
movements FinancialMovement[]
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@unique([organizationId, userId, period])
|
|
|
|
|
@@index([organizationId, status])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model FinancialRecurringItem {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
title String
|
|
|
|
|
/// income | expense
|
|
|
|
|
kind String
|
|
|
|
|
amount Decimal @db.Decimal(18, 2)
|
|
|
|
|
/// weekly | monthly | yearly
|
|
|
|
|
interval String
|
|
|
|
|
startDate DateTime
|
|
|
|
|
endDate DateTime?
|
|
|
|
|
nextDue DateTime
|
|
|
|
|
accountId String?
|
|
|
|
|
categoryId String?
|
|
|
|
|
isActive Boolean @default(true)
|
|
|
|
|
createdById String?
|
|
|
|
|
occurrences FinancialRecurringOccurrence[]
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, isActive, nextDue])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model FinancialRecurringOccurrence {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
itemId String
|
|
|
|
|
item FinancialRecurringItem @relation(fields: [itemId], references: [id], onDelete: Cascade)
|
|
|
|
|
periodKey String
|
|
|
|
|
dueDate DateTime
|
|
|
|
|
documentId String?
|
|
|
|
|
/// pending | confirmed
|
|
|
|
|
status String @default("pending")
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@unique([itemId, periodKey])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model FinancialReconciliation {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
accountId String
|
|
|
|
|
account FinancialAccount @relation(fields: [accountId], references: [id], onDelete: Cascade)
|
|
|
|
|
reconciledAt DateTime
|
|
|
|
|
statedBalance Decimal @db.Decimal(18, 2)
|
|
|
|
|
systemBalance Decimal @db.Decimal(18, 2)
|
|
|
|
|
difference Decimal @db.Decimal(18, 2)
|
|
|
|
|
notes String?
|
|
|
|
|
createdById String?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, accountId, reconciledAt])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// قرارداد یا طلب اقساطی دفتر داخلی
|
|
|
|
|
model FinancialReceivable {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
title String
|
|
|
|
|
personId String?
|
|
|
|
|
person Person? @relation(fields: [personId], references: [id], onDelete: SetNull)
|
|
|
|
|
partyName String?
|
|
|
|
|
totalAmount Decimal @db.Decimal(18, 2)
|
|
|
|
|
notes String?
|
|
|
|
|
/// open | settled | void
|
|
|
|
|
status String @default("open")
|
|
|
|
|
createdById String?
|
|
|
|
|
voidedAt DateTime?
|
|
|
|
|
installments FinancialInstallment[]
|
|
|
|
|
movements FinancialMovement[]
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, status])
|
|
|
|
|
@@index([personId])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model FinancialInstallment {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
receivableId String
|
|
|
|
|
receivable FinancialReceivable @relation(fields: [receivableId], references: [id], onDelete: Cascade)
|
|
|
|
|
position Int
|
|
|
|
|
dueDate DateTime
|
|
|
|
|
amount Decimal @db.Decimal(18, 2)
|
|
|
|
|
reminderNotifiedAt DateTime?
|
|
|
|
|
movements FinancialMovement[]
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@unique([receivableId, position])
|
|
|
|
|
@@index([dueDate])
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-19 15:55:48 +03:30
|
|
|
/// کیفپول و تنظیمات پیامک هر محیط (الگوی همکار / سوپرادمین آبشُده)
|
|
|
|
|
model SmsAccount {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String @unique
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
balanceRials Int @default(0)
|
|
|
|
|
pricePerPartRials Int @default(2500)
|
|
|
|
|
isBlocked Boolean @default(false)
|
|
|
|
|
/// اگر ست باشد: وقتی موجودی < این مقدار، ارسال قطع میشود (میتواند منفی باشد). null = فقط کلید دستی
|
|
|
|
|
autoBlockBelowRials Int?
|
|
|
|
|
alertCriticalRials Int @default(10000)
|
|
|
|
|
alertWarningRials Int @default(100000)
|
|
|
|
|
/// platform = سامانهٔ ما | own = پنل اختصاصی مشتری (بدون کسر/لاگ کیف)
|
|
|
|
|
panelMode String @default("platform")
|
|
|
|
|
/// وقتی panelMode=own: 0098 | asanak | trez
|
|
|
|
|
ownPanelType String @default("0098")
|
|
|
|
|
ownPanelUsername String?
|
|
|
|
|
ownPanelPassword String?
|
|
|
|
|
ownPanelFrom String?
|
|
|
|
|
chargeCard String?
|
|
|
|
|
chargeIban String?
|
|
|
|
|
chargeHolder String?
|
|
|
|
|
chargeInstruction String?
|
|
|
|
|
alertDaysSent Int @default(0)
|
|
|
|
|
lastAlertDate DateTime?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
ledgers SmsLedger[]
|
|
|
|
|
sendLogs SmsBillingSendLog[]
|
|
|
|
|
|
|
|
|
|
@@index([isBlocked])
|
|
|
|
|
@@index([balanceRials])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model SmsLedger {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
smsAccountId String
|
|
|
|
|
smsAccount SmsAccount @relation(fields: [smsAccountId], references: [id], onDelete: Cascade)
|
|
|
|
|
/// charge | debit | alert_debit
|
|
|
|
|
type String
|
|
|
|
|
amountRials Int
|
|
|
|
|
balanceAfterRials Int
|
|
|
|
|
parts Int?
|
|
|
|
|
pricePerPartRials Int?
|
|
|
|
|
note String?
|
|
|
|
|
smsSendLogId String?
|
|
|
|
|
createdById String?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@index([smsAccountId, createdAt])
|
|
|
|
|
@@index([type])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// لاگ ارسال صورتحسابشده (همهٔ مسیرها وقتی panelMode=platform)
|
|
|
|
|
model SmsBillingSendLog {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
smsAccountId String
|
|
|
|
|
smsAccount SmsAccount @relation(fields: [smsAccountId], references: [id], onDelete: Cascade)
|
|
|
|
|
receiver String
|
|
|
|
|
body String
|
|
|
|
|
parts Int
|
|
|
|
|
pricePerPartRials Int
|
|
|
|
|
totalAmountRials Int
|
|
|
|
|
provider String?
|
|
|
|
|
source String?
|
|
|
|
|
isAlert Boolean @default(false)
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@index([smsAccountId, createdAt])
|
|
|
|
|
@@index([source])
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-17 21:23:07 +03:30
|
|
|
/// لاگ یکپارچهٔ پیامک خروجی (دستی / کمپین / اتوماسیون)
|
|
|
|
|
model OutboundSmsLog {
|
|
|
|
|
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: SetNull)
|
|
|
|
|
leadId String?
|
|
|
|
|
lead Lead? @relation(fields: [leadId], references: [id], onDelete: SetNull)
|
|
|
|
|
mobile String
|
|
|
|
|
body String
|
|
|
|
|
/// manual | campaign | automation
|
|
|
|
|
source String
|
|
|
|
|
sourceId String?
|
|
|
|
|
/// sent | failed
|
|
|
|
|
status String
|
|
|
|
|
error String?
|
|
|
|
|
actorUserId String?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, personId, createdAt])
|
|
|
|
|
@@index([organizationId, leadId, createdAt])
|
|
|
|
|
@@index([organizationId, createdAt])
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-26 16:12:29 +03:30
|
|
|
/// کمپین پیامک گروهی
|
|
|
|
|
model SmsCampaign {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
2026-09-26 16:12:29 +03:30
|
|
|
organizationId String
|
2026-10-10 21:28:49 +03:30
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
2026-09-26 16:12:29 +03:30
|
|
|
name String
|
|
|
|
|
body String
|
|
|
|
|
/// draft | queued | sending | done | cancelled
|
2026-10-10 21:28:49 +03:30
|
|
|
status String @default("draft")
|
2026-09-26 16:12:29 +03:30
|
|
|
scheduledAt DateTime?
|
2026-10-10 21:28:49 +03:30
|
|
|
totalCount Int @default(0)
|
|
|
|
|
sentCount Int @default(0)
|
|
|
|
|
failedCount Int @default(0)
|
|
|
|
|
skippedCount Int @default(0)
|
2026-09-26 16:12:29 +03:30
|
|
|
createdById String?
|
|
|
|
|
recipients SmsCampaignRecipient[]
|
2026-10-10 21:28:49 +03:30
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-09-26 16:12:29 +03:30
|
|
|
|
|
|
|
|
@@index([organizationId, status, scheduledAt])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model SmsCampaignRecipient {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
|
|
|
|
campaignId String
|
|
|
|
|
campaign SmsCampaign @relation(fields: [campaignId], references: [id], onDelete: Cascade)
|
|
|
|
|
personId String?
|
2026-10-01 15:59:18 +03:30
|
|
|
/// برای پشتیبانی آینده از Lead (فعلاً کمپین روی Person است)
|
2026-10-10 21:28:49 +03:30
|
|
|
leadId String?
|
|
|
|
|
mobile String?
|
2026-09-26 16:12:29 +03:30
|
|
|
/// pending | sent | failed | skipped
|
2026-10-10 21:28:49 +03:30
|
|
|
status String @default("pending")
|
|
|
|
|
error String?
|
|
|
|
|
sentAt DateTime?
|
|
|
|
|
createdAt DateTime @default(now())
|
2026-10-01 15:59:18 +03:30
|
|
|
trackingLinks TrackingLink[]
|
2026-09-26 16:12:29 +03:30
|
|
|
|
|
|
|
|
@@index([campaignId, status])
|
2026-10-01 15:59:18 +03:30
|
|
|
@@index([personId])
|
|
|
|
|
@@index([leadId])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// لینک ردیابی یکتا — کانالخنثی (SMS / Email / …)
|
|
|
|
|
model TrackingLink {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
2026-10-01 15:59:18 +03:30
|
|
|
organizationId String
|
2026-10-10 21:28:49 +03:30
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
2026-10-01 15:59:18 +03:30
|
|
|
/// توکن امن و غیرقابلحدس (بدون PII)
|
2026-10-10 21:28:49 +03:30
|
|
|
token String @unique
|
2026-10-01 15:59:18 +03:30
|
|
|
originalUrl String
|
|
|
|
|
/// sms | email | whatsapp | notification | other
|
2026-10-10 21:28:49 +03:30
|
|
|
channel String @default("sms")
|
2026-10-01 15:59:18 +03:30
|
|
|
/// sms_campaign | …
|
|
|
|
|
sourceType String
|
|
|
|
|
sourceId String
|
|
|
|
|
/// sms_campaign_recipient | …
|
|
|
|
|
recipientType String?
|
|
|
|
|
recipientId String?
|
|
|
|
|
smsRecipientId String?
|
|
|
|
|
smsRecipient SmsCampaignRecipient? @relation(fields: [smsRecipientId], references: [id], onDelete: SetNull)
|
|
|
|
|
personId String?
|
2026-10-10 21:28:49 +03:30
|
|
|
person Person? @relation(fields: [personId], references: [id], onDelete: SetNull)
|
2026-10-01 15:59:18 +03:30
|
|
|
leadId String?
|
2026-10-10 21:28:49 +03:30
|
|
|
lead Lead? @relation(fields: [leadId], references: [id], onDelete: SetNull)
|
|
|
|
|
clickCount Int @default(0)
|
2026-10-01 15:59:18 +03:30
|
|
|
firstClickedAt DateTime?
|
|
|
|
|
lastClickedAt DateTime?
|
2026-10-10 21:28:49 +03:30
|
|
|
createdAt DateTime @default(now())
|
2026-10-01 15:59:18 +03:30
|
|
|
events TrackingEvent[]
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, sourceType, sourceId])
|
|
|
|
|
@@index([organizationId, personId])
|
|
|
|
|
@@index([organizationId, leadId])
|
|
|
|
|
@@index([smsRecipientId])
|
|
|
|
|
@@index([token])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// رویداد رفتاری عمومی — آمادهٔ Automation
|
|
|
|
|
model TrackingEvent {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
trackingLinkId String?
|
|
|
|
|
trackingLink TrackingLink? @relation(fields: [trackingLinkId], references: [id], onDelete: SetNull)
|
|
|
|
|
/// مثلاً sms_link_clicked | sms_delivered | sms_failed | …
|
|
|
|
|
eventType String
|
|
|
|
|
channel String @default("sms")
|
|
|
|
|
sourceType String?
|
|
|
|
|
sourceId String?
|
|
|
|
|
recipientType String?
|
|
|
|
|
recipientId String?
|
|
|
|
|
personId String?
|
|
|
|
|
person Person? @relation(fields: [personId], references: [id], onDelete: SetNull)
|
|
|
|
|
leadId String?
|
|
|
|
|
lead Lead? @relation(fields: [leadId], references: [id], onDelete: SetNull)
|
|
|
|
|
occurredAt DateTime @default(now())
|
|
|
|
|
/// ua / referrer / ipHash (اختیاری، بدون PII خام)
|
|
|
|
|
metadata Json?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, eventType, occurredAt])
|
|
|
|
|
@@index([organizationId, sourceType, sourceId, eventType])
|
|
|
|
|
@@index([organizationId, personId, eventType])
|
|
|
|
|
@@index([organizationId, leadId, eventType])
|
|
|
|
|
@@index([trackingLinkId, occurredAt])
|
2026-09-26 16:12:29 +03:30
|
|
|
}
|
2026-09-26 17:59:16 +03:30
|
|
|
|
|
|
|
|
/// اتصال فروشگاه ووکامرس
|
|
|
|
|
model WooCommerceConnection {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
label String
|
|
|
|
|
storeUrl String
|
|
|
|
|
consumerKeyCiphertext String
|
|
|
|
|
consumerKeyIv String
|
|
|
|
|
consumerKeyTag String
|
|
|
|
|
consumerKeyLast4 String @default("")
|
2026-09-26 17:59:16 +03:30
|
|
|
consumerSecretCiphertext String
|
2026-10-10 21:28:49 +03:30
|
|
|
consumerSecretIv String
|
|
|
|
|
consumerSecretTag String
|
|
|
|
|
webhookSecretCiphertext String?
|
|
|
|
|
webhookSecretIv String?
|
|
|
|
|
webhookSecretTag String?
|
|
|
|
|
enabled Boolean @default(true)
|
|
|
|
|
lastSyncAt DateTime?
|
|
|
|
|
lastHealthAt DateTime?
|
|
|
|
|
lastHealthOk Boolean?
|
|
|
|
|
lastError String?
|
|
|
|
|
orders WooCommerceOrderLink[]
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-09-26 17:59:16 +03:30
|
|
|
|
|
|
|
|
@@index([organizationId, enabled])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model WooCommerceOrderLink {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
2026-09-26 17:59:16 +03:30
|
|
|
organizationId String
|
|
|
|
|
connectionId String
|
|
|
|
|
connection WooCommerceConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
|
|
|
|
|
wooOrderId String
|
|
|
|
|
wooOrderNumber String?
|
|
|
|
|
invoiceId String
|
2026-10-10 21:28:49 +03:30
|
|
|
status String @default("")
|
|
|
|
|
total Decimal @default(0) @db.Decimal(18, 2)
|
|
|
|
|
currency String @default("IRR")
|
2026-09-26 17:59:16 +03:30
|
|
|
rawPayload Json?
|
2026-10-10 21:28:49 +03:30
|
|
|
syncedAt DateTime @default(now())
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-09-26 17:59:16 +03:30
|
|
|
|
|
|
|
|
@@unique([connectionId, wooOrderId])
|
|
|
|
|
@@index([organizationId, syncedAt])
|
|
|
|
|
@@index([invoiceId])
|
|
|
|
|
}
|
2026-09-27 08:47:11 +03:30
|
|
|
|
2026-10-10 15:59:12 +03:30
|
|
|
/// اتصال فروشگاه Montify (هر فروشگاه: Base URL + توکن اختصاصی)
|
|
|
|
|
model MontifyConnection {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
2026-10-10 15:59:12 +03:30
|
|
|
organizationId String
|
2026-10-10 21:28:49 +03:30
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
2026-10-10 15:59:12 +03:30
|
|
|
label String
|
|
|
|
|
baseUrl String
|
2026-10-10 21:28:49 +03:30
|
|
|
storeId String @default("")
|
2026-10-10 15:59:12 +03:30
|
|
|
tokenCiphertext String
|
|
|
|
|
tokenIv String
|
|
|
|
|
tokenTag String
|
2026-10-10 21:28:49 +03:30
|
|
|
tokenLast4 String @default("")
|
|
|
|
|
enabled Boolean @default(true)
|
2026-10-10 15:59:12 +03:30
|
|
|
webhookRemoteId String?
|
|
|
|
|
webhookSecretCiphertext String?
|
|
|
|
|
webhookSecretIv String?
|
|
|
|
|
webhookSecretTag String?
|
|
|
|
|
/// بیشترین updated_at مشتریان دریافتشده (برای updated_after)
|
|
|
|
|
customersCursor DateTime?
|
|
|
|
|
/// idle | running | ok | error
|
2026-10-10 21:28:49 +03:30
|
|
|
syncStatus String @default("idle")
|
2026-10-10 15:59:12 +03:30
|
|
|
syncStartedAt DateTime?
|
|
|
|
|
lastSyncAt DateTime?
|
|
|
|
|
lastSyncResult Json?
|
|
|
|
|
lastHealthAt DateTime?
|
|
|
|
|
lastHealthOk Boolean?
|
|
|
|
|
lastError String?
|
|
|
|
|
lastWebhookAt DateTime?
|
|
|
|
|
links MontifyLink[]
|
|
|
|
|
orders MontifyOrder[]
|
|
|
|
|
transactions MontifyTransaction[]
|
|
|
|
|
deliveries MontifyWebhookDelivery[]
|
2026-10-10 21:28:49 +03:30
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-10-10 15:59:12 +03:30
|
|
|
|
|
|
|
|
@@index([organizationId, enabled])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// پیوند مشتری فروشگاه با شخص CRM (entityType = customer، entityId = Person.id)
|
|
|
|
|
model MontifyLink {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
2026-10-10 15:59:12 +03:30
|
|
|
organizationId String
|
|
|
|
|
connectionId String
|
|
|
|
|
connection MontifyConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
|
|
|
|
|
entityType String
|
|
|
|
|
externalId String
|
|
|
|
|
entityId String
|
|
|
|
|
externalUpdatedAt DateTime?
|
|
|
|
|
/// آخرین زمان دریافت سفارشهای این مشتری (updated_after سفارشها)
|
|
|
|
|
ordersSyncedAt DateTime?
|
|
|
|
|
/// پروفایل فروشگاهی مشتری: wallet_balance، gold_wallet_balance، summary، engagement، …
|
|
|
|
|
data Json?
|
2026-10-10 21:28:49 +03:30
|
|
|
syncedAt DateTime @default(now())
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-10-10 15:59:12 +03:30
|
|
|
|
|
|
|
|
@@unique([connectionId, entityType, externalId])
|
|
|
|
|
@@index([organizationId, entityType, entityId])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// سفارش فروشگاه (فقطخواندنی، نگاشت مستند: code → order_reference، sts → order_status)
|
|
|
|
|
model MontifyOrder {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
2026-10-10 15:59:12 +03:30
|
|
|
organizationId String
|
|
|
|
|
connectionId String
|
|
|
|
|
connection MontifyConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
|
|
|
|
|
externalId String
|
|
|
|
|
customerExternalId String?
|
|
|
|
|
personId String?
|
|
|
|
|
orderReference String?
|
|
|
|
|
orderStatus String?
|
|
|
|
|
paymentStatus String?
|
|
|
|
|
paymentType String?
|
2026-10-10 21:28:49 +03:30
|
|
|
totalPrice Decimal? @db.Decimal(20, 2)
|
|
|
|
|
discountTotalPrice Decimal? @db.Decimal(20, 2)
|
|
|
|
|
totalDiscounts Decimal? @db.Decimal(20, 2)
|
|
|
|
|
totalTax Decimal? @db.Decimal(20, 2)
|
|
|
|
|
totalQty Decimal? @db.Decimal(14, 3)
|
|
|
|
|
profit Decimal? @db.Decimal(20, 2)
|
|
|
|
|
percentProfit Decimal? @db.Decimal(10, 3)
|
|
|
|
|
sumGold Decimal? @db.Decimal(14, 3)
|
|
|
|
|
sumSilver Decimal? @db.Decimal(14, 3)
|
2026-10-10 15:59:12 +03:30
|
|
|
address Json?
|
|
|
|
|
note String?
|
|
|
|
|
cancelReason String?
|
|
|
|
|
cancelledAt DateTime?
|
|
|
|
|
closedAt DateTime?
|
|
|
|
|
codeDiscount String?
|
2026-10-10 21:28:49 +03:30
|
|
|
items Json @default("[]")
|
2026-10-10 15:59:12 +03:30
|
|
|
externalCreatedAt DateTime?
|
|
|
|
|
externalUpdatedAt DateTime?
|
|
|
|
|
deletedAt DateTime?
|
|
|
|
|
raw Json?
|
2026-10-10 21:28:49 +03:30
|
|
|
syncedAt DateTime @default(now())
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-10-10 15:59:12 +03:30
|
|
|
|
|
|
|
|
@@unique([connectionId, externalId])
|
|
|
|
|
@@index([organizationId, personId])
|
|
|
|
|
@@index([organizationId, externalCreatedAt])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// تراکنش مالی مشتری در فروشگاه (واریز/برداشت ریالی یا گرم طلا)
|
|
|
|
|
model MontifyTransaction {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
2026-10-10 15:59:12 +03:30
|
|
|
organizationId String
|
|
|
|
|
connectionId String
|
|
|
|
|
connection MontifyConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
|
|
|
|
|
externalId String
|
|
|
|
|
customerExternalId String?
|
|
|
|
|
personId String?
|
|
|
|
|
type String?
|
2026-10-10 21:28:49 +03:30
|
|
|
amount Decimal? @db.Decimal(24, 4)
|
2026-10-10 15:59:12 +03:30
|
|
|
currency String?
|
|
|
|
|
status String?
|
|
|
|
|
referenceCode String?
|
|
|
|
|
externalCreatedAt DateTime?
|
|
|
|
|
raw Json?
|
2026-10-10 21:28:49 +03:30
|
|
|
syncedAt DateTime @default(now())
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-10-10 15:59:12 +03:30
|
|
|
|
|
|
|
|
@@unique([connectionId, externalId])
|
|
|
|
|
@@index([organizationId, personId])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model MontifyWebhookDelivery {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
2026-10-10 15:59:12 +03:30
|
|
|
connectionId String
|
|
|
|
|
connection MontifyConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
|
|
|
|
|
deliveryId String
|
|
|
|
|
event String
|
|
|
|
|
/// received | processed | failed
|
2026-10-10 21:28:49 +03:30
|
|
|
status String @default("received")
|
2026-10-10 15:59:12 +03:30
|
|
|
error String?
|
2026-10-10 21:28:49 +03:30
|
|
|
createdAt DateTime @default(now())
|
2026-10-10 15:59:12 +03:30
|
|
|
|
|
|
|
|
@@unique([connectionId, deliveryId])
|
|
|
|
|
@@index([connectionId, createdAt])
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-27 08:47:11 +03:30
|
|
|
/// اتصال تلفن سازمانی / سانترال (وبهوک عمومی)
|
|
|
|
|
model VoipConnection {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
label String
|
|
|
|
|
enabled Boolean @default(true)
|
|
|
|
|
webhookSecretCiphertext String?
|
|
|
|
|
webhookSecretIv String?
|
|
|
|
|
webhookSecretTag String?
|
2026-09-27 08:47:11 +03:30
|
|
|
/// قالب URL شروع تماس؛ جای {number} شماره مینشیند
|
2026-10-10 21:28:49 +03:30
|
|
|
clickToCallUrl String?
|
|
|
|
|
lastEventAt DateTime?
|
|
|
|
|
lastError String?
|
|
|
|
|
calls VoipCallLog[]
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-09-27 08:47:11 +03:30
|
|
|
|
|
|
|
|
@@index([organizationId, enabled])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model VoipCallLog {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
connectionId String
|
|
|
|
|
connection VoipConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
|
|
|
|
|
externalCallId String
|
2026-09-27 08:47:11 +03:30
|
|
|
/// inbound | outbound
|
2026-10-10 21:28:49 +03:30
|
|
|
direction String @default("inbound")
|
2026-09-27 08:47:11 +03:30
|
|
|
/// ringing | answered | missed | completed | failed
|
2026-10-10 21:28:49 +03:30
|
|
|
status String @default("completed")
|
|
|
|
|
fromNumber String?
|
|
|
|
|
toNumber String?
|
|
|
|
|
personId String?
|
|
|
|
|
activityId String?
|
|
|
|
|
durationSec Int?
|
|
|
|
|
startedAt DateTime @default(now())
|
|
|
|
|
endedAt DateTime?
|
|
|
|
|
rawPayload Json?
|
2026-09-27 15:06:41 +03:30
|
|
|
/// لینک فایل ضبط از سانترال
|
2026-10-10 21:28:49 +03:30
|
|
|
recordingUrl String?
|
2026-09-27 15:06:41 +03:30
|
|
|
/// متن خام (از وبهوک یا STT)
|
2026-10-10 21:28:49 +03:30
|
|
|
transcript String?
|
2026-09-27 15:06:41 +03:30
|
|
|
/// خلاصه فارسی تولیدشده
|
2026-10-10 21:28:49 +03:30
|
|
|
summary String?
|
2026-09-27 15:06:41 +03:30
|
|
|
/// none | pending | processing | done | failed | skipped
|
2026-10-10 21:28:49 +03:30
|
|
|
summaryStatus String @default("none")
|
|
|
|
|
summaryNoteId String?
|
|
|
|
|
summaryError String?
|
|
|
|
|
summaryAttempts Int @default(0)
|
2026-09-27 15:06:41 +03:30
|
|
|
summaryNextAttemptAt DateTime?
|
2026-10-10 21:28:49 +03:30
|
|
|
syncedAt DateTime @default(now())
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-09-27 08:47:11 +03:30
|
|
|
|
|
|
|
|
@@unique([connectionId, externalCallId])
|
|
|
|
|
@@index([organizationId, syncedAt])
|
|
|
|
|
@@index([personId])
|
|
|
|
|
@@index([activityId])
|
2026-09-27 15:06:41 +03:30
|
|
|
@@index([summaryStatus, summaryNextAttemptAt])
|
2026-09-27 08:47:11 +03:30
|
|
|
}
|
2026-09-27 09:38:17 +03:30
|
|
|
|
|
|
|
|
/// خدمت قابل رزرو (مثلاً مشاوره ۳۰ دقیقهای)
|
|
|
|
|
model ReservationService {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
name String
|
|
|
|
|
description String @default("")
|
|
|
|
|
durationMinutes Int @default(30)
|
2026-09-27 09:38:17 +03:30
|
|
|
/// روزهای کار ۰=یکشنبه … ۶=شنبه
|
2026-10-10 21:28:49 +03:30
|
|
|
workDays Int[] @default([0, 1, 2, 3, 4, 5, 6])
|
|
|
|
|
dayStartHour Int @default(9)
|
|
|
|
|
dayEndHour Int @default(17)
|
|
|
|
|
isActive Boolean @default(true)
|
|
|
|
|
appointments ReservationAppointment[]
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-09-27 09:38:17 +03:30
|
|
|
|
|
|
|
|
@@index([organizationId, isActive])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model ReservationAppointment {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
2026-09-27 09:38:17 +03:30
|
|
|
organizationId String
|
2026-10-10 21:28:49 +03:30
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
2026-09-27 09:38:17 +03:30
|
|
|
serviceId String
|
|
|
|
|
service ReservationService @relation(fields: [serviceId], references: [id], onDelete: Restrict)
|
|
|
|
|
personId String
|
2026-10-10 21:28:49 +03:30
|
|
|
person Person @relation(fields: [personId], references: [id], onDelete: Cascade)
|
2026-09-27 09:38:17 +03:30
|
|
|
assigneeId String?
|
2026-10-10 21:28:49 +03:30
|
|
|
assignee User? @relation("ReservationAssignee", fields: [assigneeId], references: [id], onDelete: SetNull)
|
2026-09-27 09:38:17 +03:30
|
|
|
startsAt DateTime
|
|
|
|
|
endsAt DateTime
|
|
|
|
|
/// pending | confirmed | canceled | done
|
2026-10-10 21:28:49 +03:30
|
|
|
status String @default("pending")
|
2026-09-27 09:38:17 +03:30
|
|
|
notes String?
|
2026-10-10 21:28:49 +03:30
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-09-27 09:38:17 +03:30
|
|
|
|
|
|
|
|
@@index([organizationId, startsAt])
|
|
|
|
|
@@index([organizationId, status])
|
|
|
|
|
@@index([serviceId, startsAt])
|
|
|
|
|
@@index([personId])
|
|
|
|
|
@@index([assigneeId, startsAt])
|
|
|
|
|
}
|
2026-09-27 17:05:11 +03:30
|
|
|
|
|
|
|
|
/// مرحله/وضعیت لید — هم لیست وضعیت و هم ستون Kanban
|
|
|
|
|
model LeadStage {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
name String
|
|
|
|
|
position Int @default(0)
|
|
|
|
|
color String @default("teal")
|
|
|
|
|
/// نمایش در تخته Kanban
|
|
|
|
|
showOnBoard Boolean @default(true)
|
|
|
|
|
isConverted Boolean @default(false)
|
|
|
|
|
isLost Boolean @default(false)
|
|
|
|
|
isUnqualified Boolean @default(false)
|
|
|
|
|
leads Lead[]
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@unique([organizationId, name])
|
2026-10-10 21:28:49 +03:30
|
|
|
@@index([organizationId, position])
|
2026-09-27 17:05:11 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// مخاطب بالقوه — جدا از Person/مشتری
|
|
|
|
|
model Lead {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
2026-09-27 17:05:11 +03:30
|
|
|
/// نام فروشگاه / شخص / عنوان نمایشی
|
2026-10-10 21:28:49 +03:30
|
|
|
title String
|
2026-09-27 17:05:11 +03:30
|
|
|
/// نام مالک یا مسئول
|
2026-10-10 21:28:49 +03:30
|
|
|
contactName String?
|
|
|
|
|
mobile String
|
|
|
|
|
phone String?
|
|
|
|
|
city String?
|
|
|
|
|
region String?
|
2026-10-03 12:29:11 +03:30
|
|
|
/// پیج اینستاگرام
|
2026-10-10 21:28:49 +03:30
|
|
|
instagram String?
|
2026-10-13 18:04:53 +03:30
|
|
|
/// تاریخ تولد
|
|
|
|
|
birthDate DateTime?
|
2026-10-03 12:29:11 +03:30
|
|
|
/// لیست آدرسها (JSON array of strings)
|
2026-10-10 21:28:49 +03:30
|
|
|
addresses Json @default("[]")
|
2026-09-27 17:05:11 +03:30
|
|
|
/// صنف
|
2026-10-10 21:28:49 +03:30
|
|
|
trade String?
|
|
|
|
|
source String?
|
|
|
|
|
notes String?
|
2026-09-27 21:17:42 +03:30
|
|
|
/// لوگوی فروشگاه / شرکت
|
2026-10-10 21:28:49 +03:30
|
|
|
logoStorageKey String?
|
|
|
|
|
estimatedValue Decimal? @db.Decimal(18, 2)
|
|
|
|
|
stageId String
|
|
|
|
|
stage LeadStage @relation(fields: [stageId], references: [id], onDelete: Restrict)
|
|
|
|
|
position Int @default(0)
|
|
|
|
|
assigneeId String?
|
|
|
|
|
assignee User? @relation("LeadAssignee", fields: [assigneeId], references: [id], onDelete: SetNull)
|
|
|
|
|
createdById String?
|
|
|
|
|
createdBy User? @relation("LeadCreator", fields: [createdById], references: [id], onDelete: SetNull)
|
|
|
|
|
lastContactAt DateTime?
|
|
|
|
|
nextFollowUpAt DateTime?
|
2026-09-27 17:05:11 +03:30
|
|
|
/// شخص مشتری پس از تبدیل
|
|
|
|
|
convertedPersonId String?
|
2026-10-10 21:28:49 +03:30
|
|
|
convertedAt DateTime?
|
|
|
|
|
archivedAt DateTime?
|
|
|
|
|
events LeadEvent[]
|
2026-10-18 21:04:26 +03:30
|
|
|
salesCopilotSessions SalesCopilotSession[]
|
2026-10-10 21:28:49 +03:30
|
|
|
trackingLinks TrackingLink[]
|
|
|
|
|
trackingEvents TrackingEvent[]
|
|
|
|
|
routineTasks RoutineTask[]
|
2026-10-11 14:12:36 +03:30
|
|
|
activities Activity[]
|
2026-10-17 21:23:07 +03:30
|
|
|
outboundSmsLogs OutboundSmsLog[]
|
2026-10-10 21:28:49 +03:30
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-09-27 17:05:11 +03:30
|
|
|
|
|
|
|
|
@@unique([organizationId, mobile])
|
|
|
|
|
@@index([organizationId, stageId, position])
|
|
|
|
|
@@index([organizationId, assigneeId])
|
|
|
|
|
@@index([organizationId, nextFollowUpAt])
|
|
|
|
|
@@index([organizationId, lastContactAt])
|
|
|
|
|
@@index([organizationId, archivedAt])
|
|
|
|
|
@@index([organizationId, createdAt])
|
|
|
|
|
@@index([organizationId, city])
|
|
|
|
|
@@index([organizationId, trade])
|
|
|
|
|
@@index([organizationId, source])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model LeadEvent {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
leadId String
|
|
|
|
|
lead Lead @relation(fields: [leadId], references: [id], onDelete: Cascade)
|
|
|
|
|
/// created | assigned | stage_changed | call | note | task | follow_up | converted | archived | import
|
|
|
|
|
type String
|
|
|
|
|
title String
|
|
|
|
|
body String?
|
|
|
|
|
meta Json?
|
|
|
|
|
actorId String?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@index([leadId, createdAt])
|
|
|
|
|
@@index([organizationId, createdAt])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// ایمپورت پسزمینه فایل اکسل لید
|
|
|
|
|
model LeadImportJob {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
createdById String?
|
|
|
|
|
fileName String?
|
|
|
|
|
/// pending | processing | done | failed
|
|
|
|
|
status String @default("pending")
|
|
|
|
|
totalCount Int @default(0)
|
|
|
|
|
processedCount Int @default(0)
|
|
|
|
|
successCount Int @default(0)
|
|
|
|
|
duplicateCount Int @default(0)
|
|
|
|
|
errorCount Int @default(0)
|
|
|
|
|
/// ردیفهای خام [{...}]
|
|
|
|
|
rows Json @default("[]")
|
|
|
|
|
/// خلاصه خطاها
|
|
|
|
|
errorSamples Json @default("[]")
|
|
|
|
|
startedAt DateTime?
|
|
|
|
|
finishedAt DateTime?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, status, createdAt])
|
|
|
|
|
@@index([status, createdAt])
|
|
|
|
|
}
|
2026-09-29 10:34:01 +03:30
|
|
|
|
|
|
|
|
/// تنظیمات اتصال به سرویس گفتگوی خارجی (هر محیط CRM)
|
|
|
|
|
model ConversationSettings {
|
|
|
|
|
organizationId String @id
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
chatHost String
|
|
|
|
|
apiKeyCipher String
|
|
|
|
|
apiKeyIv String
|
|
|
|
|
apiKeyTag String
|
|
|
|
|
uploadUrl String @default("")
|
|
|
|
|
downloadUrl String @default("")
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// نگاشت roomهای سرویس چت (DM dedup و گروههای ساختهشده از CRM)
|
|
|
|
|
model ConversationRoom {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
externalRoomId String
|
|
|
|
|
kind String
|
|
|
|
|
name String?
|
|
|
|
|
dmUserMinId String?
|
|
|
|
|
dmUserMaxId String?
|
|
|
|
|
createdByUserId String?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@unique([organizationId, externalRoomId])
|
|
|
|
|
@@unique([organizationId, dmUserMinId, dmUserMaxId])
|
|
|
|
|
@@index([organizationId, kind])
|
|
|
|
|
}
|
2026-09-29 21:46:41 +03:30
|
|
|
|
2026-09-30 09:19:36 +03:30
|
|
|
/// پیامهای حذفشده در CRM (سرویس چت خارجی حذف پایدار ندارد)
|
|
|
|
|
/// scope=all → برای همه؛ scope=self → فقط برای hiddenByUserId
|
2026-09-29 21:46:41 +03:30
|
|
|
model ConversationHiddenMessage {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
externalRoomId String
|
|
|
|
|
messageId String
|
|
|
|
|
hiddenByUserId String
|
2026-09-30 09:19:36 +03:30
|
|
|
/// all = حذف برای همه | self = حذف فقط برای کاربر
|
|
|
|
|
scope String @default("all")
|
2026-09-29 21:46:41 +03:30
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
2026-09-30 09:19:36 +03:30
|
|
|
@@unique([organizationId, externalRoomId, messageId, scope, hiddenByUserId])
|
2026-09-29 21:46:41 +03:30
|
|
|
@@index([organizationId, externalRoomId])
|
|
|
|
|
}
|
2026-09-30 10:40:29 +03:30
|
|
|
|
|
|
|
|
/// یک پیام سنجاقشده در هر اتاق (برای همه اعضا)
|
|
|
|
|
model ConversationPinnedMessage {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
externalRoomId String
|
|
|
|
|
messageId String
|
|
|
|
|
preview String @default("")
|
|
|
|
|
pinnedByUserId String
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-09-30 10:40:29 +03:30
|
|
|
|
|
|
|
|
@@unique([organizationId, externalRoomId])
|
|
|
|
|
@@index([organizationId, externalRoomId])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// سنجاق گفتگو در لیست — فقط برای همان کاربر
|
|
|
|
|
model ConversationRoomUserPin {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
userId String
|
|
|
|
|
externalRoomId String
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@unique([organizationId, userId, externalRoomId])
|
|
|
|
|
@@index([organizationId, userId])
|
|
|
|
|
}
|
2026-09-30 11:53:04 +03:30
|
|
|
|
|
|
|
|
/// اعضای گروه/کانال در CRM (نقش ادمین برای ارسال در کانال و مدیریت اعضا)
|
|
|
|
|
model ConversationRoomMember {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
externalRoomId String
|
|
|
|
|
userId String
|
|
|
|
|
/// admin | member
|
|
|
|
|
role String @default("member")
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@unique([organizationId, externalRoomId, userId])
|
|
|
|
|
@@index([organizationId, externalRoomId])
|
|
|
|
|
}
|
2026-09-30 14:58:44 +03:30
|
|
|
|
|
|
|
|
/// بیصدا کردن گفتگو برای کاربر — با استثنای منشن
|
|
|
|
|
model ConversationRoomUserMute {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
userId String
|
|
|
|
|
externalRoomId String
|
|
|
|
|
/// اگر true باشد منشن همچنان اعلان/خواندهنشده میسازد
|
|
|
|
|
allowMentions Boolean @default(true)
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@unique([organizationId, userId, externalRoomId])
|
|
|
|
|
@@index([organizationId, userId])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// واکنش ایموجی روی پیام
|
|
|
|
|
model ConversationMessageReaction {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
externalRoomId String
|
|
|
|
|
messageId String
|
|
|
|
|
emoji String
|
|
|
|
|
userId String
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@unique([organizationId, externalRoomId, messageId, userId, emoji])
|
|
|
|
|
@@index([organizationId, externalRoomId])
|
|
|
|
|
}
|
2026-10-06 19:54:38 +03:30
|
|
|
|
|
|
|
|
enum RoutineTaskStatus {
|
|
|
|
|
todo
|
|
|
|
|
in_progress
|
|
|
|
|
completed
|
|
|
|
|
overdue
|
|
|
|
|
skipped
|
|
|
|
|
cancelled
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum RoutineRecurrence {
|
|
|
|
|
daily
|
|
|
|
|
weekdays
|
|
|
|
|
weekly
|
|
|
|
|
biweekly
|
|
|
|
|
monthly
|
|
|
|
|
every_n_days
|
|
|
|
|
specific_dates
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum RoutineTaskSource {
|
|
|
|
|
routine
|
|
|
|
|
manual
|
|
|
|
|
follow_up
|
|
|
|
|
lead
|
|
|
|
|
customer
|
|
|
|
|
deal
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// دستهبندی کارهای روتین (فروش، پیگیری، لید، …)
|
|
|
|
|
model RoutineCategory {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
name String
|
|
|
|
|
color String @default("#14b8a6")
|
|
|
|
|
sortOrder Int @default(0)
|
|
|
|
|
routines Routine[]
|
|
|
|
|
tasks RoutineTask[]
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@unique([organizationId, name])
|
|
|
|
|
@@index([organizationId, sortOrder])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// الگوی کار تکرارشونده؛ هر روز Task مستقل (RoutineTask) تولید میکند
|
|
|
|
|
model Routine {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
title String
|
|
|
|
|
description String?
|
|
|
|
|
assigneeId String?
|
|
|
|
|
assignee User? @relation("RoutineAssignee", fields: [assigneeId], references: [id], onDelete: SetNull)
|
|
|
|
|
/// اگر ست باشد برای هر عضو تیم یک Task جدا ساخته میشود
|
|
|
|
|
teamId String?
|
|
|
|
|
team Team? @relation(fields: [teamId], references: [id], onDelete: SetNull)
|
|
|
|
|
categoryId String?
|
|
|
|
|
category RoutineCategory? @relation(fields: [categoryId], references: [id], onDelete: SetNull)
|
|
|
|
|
priority ActivityPriority @default(medium)
|
|
|
|
|
/// HH:mm به وقت تهران
|
|
|
|
|
timeOfDay String @default("09:00")
|
|
|
|
|
durationMinutes Int?
|
|
|
|
|
startDate DateTime @db.Date
|
|
|
|
|
endDate DateTime? @db.Date
|
|
|
|
|
recurrence RoutineRecurrence @default(daily)
|
|
|
|
|
/// ۰ = شنبه … ۶ = جمعه
|
|
|
|
|
weekdays Int[] @default([])
|
|
|
|
|
intervalDays Int?
|
|
|
|
|
monthDay Int?
|
|
|
|
|
specificDates DateTime[] @db.Date
|
|
|
|
|
isMandatory Boolean @default(true)
|
|
|
|
|
allowSkip Boolean @default(false)
|
|
|
|
|
skipReasonRequired Boolean @default(true)
|
|
|
|
|
reportRequired Boolean @default(false)
|
|
|
|
|
allowAfterDeadline Boolean @default(true)
|
|
|
|
|
carryOverOverdue Boolean @default(false)
|
|
|
|
|
/// none | optional | lead | customer
|
|
|
|
|
entityLink String @default("none")
|
|
|
|
|
reminderMinutesBefore Int?
|
|
|
|
|
isActive Boolean @default(true)
|
|
|
|
|
createdById String?
|
|
|
|
|
createdBy User? @relation("RoutineCreator", fields: [createdById], references: [id], onDelete: SetNull)
|
|
|
|
|
deletedAt DateTime?
|
|
|
|
|
tasks RoutineTask[]
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, isActive, deletedAt])
|
|
|
|
|
@@index([assigneeId])
|
|
|
|
|
@@index([teamId])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// نمونه روزانه یک کار (از روتین، دستی، پیگیری، لید یا مشتری)
|
|
|
|
|
model RoutineTask {
|
2026-10-10 21:28:49 +03:30
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
routineId String?
|
|
|
|
|
routine Routine? @relation(fields: [routineId], references: [id], onDelete: SetNull)
|
|
|
|
|
assigneeId String
|
|
|
|
|
assignee User @relation("RoutineTaskAssignee", fields: [assigneeId], references: [id], onDelete: Cascade)
|
|
|
|
|
title String
|
|
|
|
|
description String?
|
|
|
|
|
categoryId String?
|
|
|
|
|
category RoutineCategory? @relation(fields: [categoryId], references: [id], onDelete: SetNull)
|
|
|
|
|
priority ActivityPriority @default(medium)
|
|
|
|
|
status RoutineTaskStatus @default(todo)
|
2026-10-06 19:54:38 +03:30
|
|
|
/// روز برنامه به وقت تهران
|
2026-10-10 21:28:49 +03:30
|
|
|
scheduledFor DateTime @db.Date
|
|
|
|
|
dueAt DateTime?
|
|
|
|
|
startedAt DateTime?
|
|
|
|
|
completedAt DateTime?
|
|
|
|
|
resultNote String?
|
|
|
|
|
skipReason String?
|
|
|
|
|
source RoutineTaskSource @default(manual)
|
|
|
|
|
isMandatory Boolean @default(false)
|
|
|
|
|
leadId String?
|
|
|
|
|
lead Lead? @relation(fields: [leadId], references: [id], onDelete: SetNull)
|
|
|
|
|
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)
|
|
|
|
|
invoiceId String?
|
|
|
|
|
invoice Invoice? @relation(fields: [invoiceId], references: [id], onDelete: SetNull)
|
|
|
|
|
activityId String?
|
|
|
|
|
activity Activity? @relation(fields: [activityId], references: [id], onDelete: SetNull)
|
|
|
|
|
reminderAt DateTime?
|
|
|
|
|
reminderSentAt DateTime?
|
2026-10-06 19:54:38 +03:30
|
|
|
overdueNotifiedAt DateTime?
|
2026-10-10 21:28:49 +03:30
|
|
|
createdById String?
|
|
|
|
|
createdBy User? @relation("RoutineTaskCreator", fields: [createdById], references: [id], onDelete: SetNull)
|
|
|
|
|
events RoutineTaskEvent[]
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
2026-10-06 19:54:38 +03:30
|
|
|
|
|
|
|
|
@@unique([routineId, assigneeId, scheduledFor])
|
|
|
|
|
@@index([organizationId, assigneeId, scheduledFor])
|
|
|
|
|
@@index([organizationId, status, dueAt])
|
|
|
|
|
@@index([reminderAt, reminderSentAt])
|
|
|
|
|
@@index([leadId])
|
|
|
|
|
@@index([personId])
|
|
|
|
|
@@index([companyId])
|
|
|
|
|
@@index([dealId])
|
|
|
|
|
@@index([activityId])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// تاریخچه تغییرات هر Task
|
|
|
|
|
model RoutineTaskEvent {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
taskId String
|
|
|
|
|
task RoutineTask @relation(fields: [taskId], references: [id], onDelete: Cascade)
|
|
|
|
|
actorId String?
|
|
|
|
|
actor User? @relation(fields: [actorId], references: [id], onDelete: SetNull)
|
|
|
|
|
/// created | status_changed | rescheduled | note | reassigned | updated
|
|
|
|
|
type String
|
|
|
|
|
fromStatus RoutineTaskStatus?
|
|
|
|
|
toStatus RoutineTaskStatus?
|
|
|
|
|
note String?
|
|
|
|
|
meta Json @default("{}")
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@index([taskId, createdAt])
|
|
|
|
|
@@index([organizationId, createdAt])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// تنظیمات اعلان کارهای روتین هر کاربر
|
|
|
|
|
model RoutineUserSettings {
|
|
|
|
|
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)
|
|
|
|
|
remindBeforeMinutes Int? @default(30)
|
|
|
|
|
endOfDaySummary Boolean @default(true)
|
|
|
|
|
overdueAlert Boolean @default(true)
|
|
|
|
|
summaryHour Int @default(16)
|
|
|
|
|
lastSummaryOn DateTime? @db.Date
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@unique([organizationId, userId])
|
|
|
|
|
}
|
2026-10-10 21:28:49 +03:30
|
|
|
|
|
|
|
|
/// ───────── باشگاه امتیازات کارکنان (جدا از باشگاه مشتریان) ─────────
|
|
|
|
|
|
|
|
|
|
model GamificationSettings {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String @unique
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
enabled Boolean @default(true)
|
|
|
|
|
/// [{ name, minScore }]
|
|
|
|
|
levels Json @default("[]")
|
|
|
|
|
monthlyGoal Int @default(1500)
|
|
|
|
|
showFullLeaderboard Boolean @default(true)
|
|
|
|
|
/// شروع استفاده — مبنای گزارش ROI
|
|
|
|
|
enabledAt DateTime @default(now())
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model GamificationRule {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
name String
|
|
|
|
|
description String?
|
|
|
|
|
event String
|
|
|
|
|
score Int @default(0)
|
|
|
|
|
coins Int @default(0)
|
|
|
|
|
/// [{ field, op, value }]
|
|
|
|
|
conditions Json @default("[]")
|
|
|
|
|
isActive Boolean @default(true)
|
|
|
|
|
maxPerDay Int?
|
|
|
|
|
maxCoinsPerDay Int?
|
|
|
|
|
maxPerUser Int?
|
|
|
|
|
cooldownMinutes Int?
|
|
|
|
|
oncePerEntity Boolean @default(false)
|
|
|
|
|
firstTimeOnly Boolean @default(false)
|
|
|
|
|
requiresApproval Boolean @default(false)
|
|
|
|
|
roles String[] @default([])
|
|
|
|
|
teamIds String[] @default([])
|
|
|
|
|
position Int @default(0)
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, event, isActive])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// دفتر کل امتیاز و سکه — هر تغییر کیف پول یک ردیف دارد
|
|
|
|
|
model GamificationTransaction {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
userId String
|
|
|
|
|
/// rule | bonus | achievement | adjust | redeem | refund
|
|
|
|
|
kind String
|
|
|
|
|
event String?
|
|
|
|
|
ruleId String?
|
|
|
|
|
bonusId String?
|
|
|
|
|
badgeId String?
|
|
|
|
|
score Int @default(0)
|
|
|
|
|
coins Int @default(0)
|
|
|
|
|
/// approved | pending | rejected
|
|
|
|
|
status String @default("approved")
|
|
|
|
|
title String
|
|
|
|
|
entityType String?
|
|
|
|
|
entityId String?
|
|
|
|
|
entityTitle String?
|
|
|
|
|
/// کلید یکتا برای جلوگیری از امتیاز تکراری
|
|
|
|
|
idempotencyKey String?
|
|
|
|
|
reason String?
|
|
|
|
|
createdById String?
|
|
|
|
|
decidedById String?
|
|
|
|
|
decidedAt DateTime?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@unique([organizationId, idempotencyKey])
|
|
|
|
|
@@index([organizationId, createdAt])
|
|
|
|
|
@@index([organizationId, userId, createdAt])
|
|
|
|
|
@@index([organizationId, ruleId, userId, createdAt])
|
|
|
|
|
@@index([organizationId, status])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// موجودی تجمیعی هر کاربر — برای خواندن سریع
|
|
|
|
|
model GamificationWallet {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
userId String
|
|
|
|
|
score Int @default(0)
|
|
|
|
|
coins Int @default(0)
|
|
|
|
|
coinsEarned Int @default(0)
|
|
|
|
|
bestMonthScore Int @default(0)
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@unique([organizationId, userId])
|
|
|
|
|
@@index([organizationId, score])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model GamificationReward {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
name String
|
|
|
|
|
description String?
|
|
|
|
|
cost Int
|
|
|
|
|
icon String @default("gift")
|
|
|
|
|
color String @default("#14b8a6")
|
|
|
|
|
stock Int?
|
|
|
|
|
isActive Boolean @default(true)
|
|
|
|
|
requiresApproval Boolean @default(true)
|
|
|
|
|
position Int @default(0)
|
|
|
|
|
redemptions GamificationRedemption[]
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, isActive])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model GamificationRedemption {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
userId String
|
|
|
|
|
rewardId String
|
|
|
|
|
reward GamificationReward @relation(fields: [rewardId], references: [id], onDelete: Cascade)
|
|
|
|
|
cost Int
|
|
|
|
|
/// pending | approved | rejected | delivered
|
|
|
|
|
status String @default("pending")
|
|
|
|
|
note String?
|
|
|
|
|
decisionNote String?
|
|
|
|
|
decidedById String?
|
|
|
|
|
decidedAt DateTime?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, status])
|
|
|
|
|
@@index([organizationId, userId])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model GamificationBadge {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
name String
|
|
|
|
|
description String?
|
|
|
|
|
icon String @default("medal")
|
|
|
|
|
color String @default("#f59e0b")
|
|
|
|
|
/// event_count | score_total | routine_streak | single_deal_amount
|
|
|
|
|
metric String
|
|
|
|
|
event String?
|
|
|
|
|
threshold Int
|
|
|
|
|
coins Int @default(0)
|
|
|
|
|
isActive Boolean @default(true)
|
|
|
|
|
position Int @default(0)
|
|
|
|
|
earned GamificationUserBadge[]
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, isActive])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model GamificationUserBadge {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
badgeId String
|
|
|
|
|
badge GamificationBadge @relation(fields: [badgeId], references: [id], onDelete: Cascade)
|
|
|
|
|
userId String
|
|
|
|
|
earnedAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@unique([badgeId, userId])
|
|
|
|
|
@@index([organizationId, userId])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model GamificationBonus {
|
|
|
|
|
id String @id @default(cuid())
|
|
|
|
|
organizationId String
|
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
|
name String
|
|
|
|
|
description String?
|
|
|
|
|
/// event_count_period | routine_streak | top_of_month
|
|
|
|
|
kind String
|
|
|
|
|
event String?
|
|
|
|
|
threshold Int @default(1)
|
|
|
|
|
/// day | week | month
|
|
|
|
|
period String @default("month")
|
|
|
|
|
score Int @default(0)
|
|
|
|
|
coins Int @default(0)
|
|
|
|
|
isActive Boolean @default(true)
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, isActive])
|
|
|
|
|
}
|
2026-10-18 21:04:26 +03:30
|
|
|
|
|
|
|
|
/// ارزیابی رفتاری DISC؛ هر بار یک رکورد تازه است و تاریخچه پاک نمیشود.
|
|
|
|
|
model DiscAssessment {
|
|
|
|
|
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)
|
|
|
|
|
assessorId String
|
|
|
|
|
assessor User @relation("DiscAssessor", fields: [assessorId], references: [id], onDelete: Restrict)
|
|
|
|
|
answers Json
|
|
|
|
|
scoreD Int
|
|
|
|
|
scoreI Int
|
|
|
|
|
scoreS Int
|
|
|
|
|
scoreC Int
|
|
|
|
|
percentD Int
|
|
|
|
|
percentI Int
|
|
|
|
|
percentS Int
|
|
|
|
|
percentC Int
|
|
|
|
|
validCount Int
|
|
|
|
|
primaryTypes String[] @default([])
|
|
|
|
|
secondaryTypes String[] @default([])
|
|
|
|
|
label String
|
|
|
|
|
secondaryLabel String?
|
|
|
|
|
reliable Boolean
|
|
|
|
|
confidence String
|
|
|
|
|
note String?
|
|
|
|
|
status String @default("confirmed")
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, personId, createdAt])
|
|
|
|
|
@@index([personId])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// نتیجهٔ تأییدشدهٔ دستیار فروش. پیشنهاد خام بدون تأیید کارشناس اینجا ذخیره نمیشود.
|
|
|
|
|
model SalesCopilotSession {
|
|
|
|
|
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)
|
|
|
|
|
leadId String?
|
|
|
|
|
lead Lead? @relation(fields: [leadId], references: [id], onDelete: SetNull)
|
|
|
|
|
dealId String?
|
|
|
|
|
deal Deal? @relation(fields: [dealId], references: [id], onDelete: SetNull)
|
|
|
|
|
userId String
|
|
|
|
|
user User @relation("CopilotAuthor", fields: [userId], references: [id], onDelete: Restrict)
|
|
|
|
|
topic String
|
|
|
|
|
goal String
|
|
|
|
|
customerStatement String
|
|
|
|
|
behaviorNotes String?
|
|
|
|
|
analysis Json
|
|
|
|
|
confirmedResponse String?
|
|
|
|
|
nextAction String?
|
|
|
|
|
followUpAt DateTime?
|
|
|
|
|
callOutcome String?
|
|
|
|
|
agentNote String?
|
|
|
|
|
status String
|
|
|
|
|
messageDraft String?
|
|
|
|
|
activityId String?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
updatedAt DateTime @updatedAt
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, personId, createdAt])
|
|
|
|
|
@@index([organizationId, leadId])
|
|
|
|
|
@@index([dealId])
|
|
|
|
|
}
|
2026-10-19 13:34:55 +03:30
|
|
|
|
|
|
|
|
/// ورود و خروج کارمندان به سامانه
|
|
|
|
|
model AttendanceEvent {
|
|
|
|
|
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)
|
|
|
|
|
/// login | logout | forced_logout
|
|
|
|
|
kind String
|
|
|
|
|
at DateTime @default(now())
|
|
|
|
|
/// ورود بعد از ساعت خروج تعیینشده
|
|
|
|
|
afterHours Boolean @default(false)
|
|
|
|
|
/// شروع نشست؛ برای اینکه خروج اجباری هر نشست فقط یک بار ثبت شود
|
|
|
|
|
sessionStartedAt DateTime?
|
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
|
|
|
|
|
|
@@index([organizationId, at])
|
|
|
|
|
@@index([organizationId, userId, at])
|
|
|
|
|
@@unique([organizationId, userId, kind, sessionStartedAt])
|
|
|
|
|
}
|