feat: routines module; super admin subscription badge, 7-day trial and lifetime plan

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-06 19:54:38 +03:30
co-authored by Cursor
parent 3de2d0fa67
commit b3784818fc
58 changed files with 13018 additions and 18 deletions
+207 -1
View File
@@ -218,6 +218,11 @@ model Organization {
conversationMessageReactions ConversationMessageReaction[]
trackingLinks TrackingLink[]
trackingEvents TrackingEvent[]
routineCategories RoutineCategory[]
routines Routine[]
routineTasks RoutineTask[]
routineTaskEvents RoutineTaskEvent[]
routineUserSettings RoutineUserSettings[]
}
model User {
@@ -271,6 +276,12 @@ model User {
reservationAssignments ReservationAppointment[] @relation("ReservationAssignee")
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[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -338,6 +349,7 @@ model Team {
name String
description String @default("")
members TeamMember[]
routines Routine[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -379,7 +391,7 @@ model BillingAccount {
id String @id @default(cuid())
ownerUserId String @unique
owner User @relation(fields: [ownerUserId], references: [id], onDelete: Cascade)
/// trial | monthly | quarterly | semiannual | yearly | custom
/// trial | monthly | quarterly | semiannual | yearly | lifetime | custom
planCode String @default("trial")
/// active | expired | suspended
status String @default("active")
@@ -435,6 +447,7 @@ model Company {
owner User? @relation("CompanyOwner", fields: [ownerId], references: [id], onDelete: SetNull)
tickets Ticket[]
projectCards ProjectCard[]
routineTasks RoutineTask[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -503,6 +516,7 @@ model Person {
cheques Cheque[]
trackingLinks TrackingLink[]
trackingEvents TrackingEvent[]
routineTasks RoutineTask[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -601,6 +615,7 @@ model Deal {
invoices Invoice[]
tickets Ticket[]
projectCards ProjectCard[]
routineTasks RoutineTask[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -646,6 +661,7 @@ model Activity {
createdById String?
createdBy User? @relation("ActivityCreator", fields: [createdById], references: [id], onDelete: SetNull)
notes Note[]
routineTasks RoutineTask[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -1023,6 +1039,7 @@ model Invoice {
commissionFixedAmount Decimal? @db.Decimal(18, 2)
commissionEntry CommissionEntry?
cheques Cheque[]
routineTasks RoutineTask[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -2176,6 +2193,7 @@ model Lead {
events LeadEvent[]
trackingLinks TrackingLink[]
trackingEvents TrackingEvent[]
routineTasks RoutineTask[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -2361,3 +2379,191 @@ model ConversationMessageReaction {
@@unique([organizationId, externalRoomId, messageId, userId, emoji])
@@index([organizationId, externalRoomId])
}
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 {
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)
/// روز برنامه به وقت تهران
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?
overdueNotifiedAt DateTime?
createdById String?
createdBy User? @relation("RoutineTaskCreator", fields: [createdById], references: [id], onDelete: SetNull)
events RoutineTaskEvent[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@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])
}