generator client { provider = "prisma-client" output = "../src/generated/prisma" } datasource db { provider = "postgresql" } enum CaseStatus { NEW CONTACTED DOCS_PENDING REVIEWING PRE_APPROVED REJECTED CLOSED CONVERTED_TO_HIS } // enum DocumentType { // PASSPORT // MEDICAL_RECORD // IMAGING // LAB_RESULT // OTHER // } enum InteractionType { PHONE WHATSAPP EMAIL SYSTEM } model Patient { id String @id @default(uuid()) pid String @unique @db.VarChar(10) // -------- Identity -------- firstName String @db.VarChar(100) lastName String @db.VarChar(100) birthDate DateTime? sex Sex? age Int? // -------- Nationality -------- nationality Countries? @relation(fields: [nationalityId], references: [id]) nationalityId Int? nationalityCode String? @db.Char(3) passportCode String? @db.VarChar(20) // -------- Contact -------- phone String? @db.VarChar(20) email String? @db.VarChar(255) preferredLanguage String? @db.VarChar(10) // -------- Address -------- address String? @db.VarChar(500) postalCode String? @db.VarChar(20) // -------- System -------- createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deletedAt DateTime? // -------- Relations -------- cases OnlineCase[] documents Document[] // -------- Indexes & Constraints -------- @@unique([nationalityCode]) // کد ملی @@unique([passportCode, nationalityId]) // پاسپورت @@unique([phone]) @@unique([email]) @@unique([firstName, lastName, birthDate]) @@index([pid]) } model Document { id String @id @default(uuid()) caseId String? patientId String? uploadedById String? type DocumentType filename String fileUrl String fileKey String mimeType String? size Int? checksum String? status DocStatus @default(NEW) is_deleted Boolean @default(false) createdAt DateTime @default(now()) case OnlineCase? @relation(fields: [caseId], references: [id]) patient Patient? @relation(fields: [patientId], references: [id]) uploadedBy Staff? @relation(fields: [uploadedById], references: [id]) } enum DocumentType { MEDICAL_IMAGE LAB_RESULT PRESCRIPTION PASSPORT NATIONAL_ID OTHER OTHER_FILE } enum DocStatus { NEW PENDING_SCAN SAFE INFECTED INVALID } enum Sex { male female other } model OnlineCase { id String @id @default(uuid()) patientId String trackingCode String @unique message String? specialty String? formData Json? status CaseStatus @default(NEW) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt patient Patient @relation(fields: [patientId], references: [id]) documents Document[] // interactions Interaction[] reviews Review[] statusHistory CaseStatusHistory[] } model Review { id String @id @default(uuid()) caseId String doctorId String? createdAt DateTime @default(now()) deletedAt DateTime? case OnlineCase @relation(fields: [caseId], references: [id]) doctor Staff? @relation(fields: [doctorId], references: [id]) translations ReviewTranslation[] } model ReviewTranslation { id Int @id @default(autoincrement()) reviewId String lang Language? @relation(fields: [lang_id], references: [id]) lang_id Int? note String? result String? // eligible, needs more docs, not eligible (می‌تونه ترجمه شود) review Review @relation(fields: [reviewId], references: [id]) @@unique([reviewId, lang_id]) // هر Review در هر زبان فقط یک ترجمه دارد } model CaseStatusHistory { id String @id @default(uuid()) caseId String from CaseStatus? to CaseStatus? changedBy String? createdAt DateTime @default(now()) case OnlineCase @relation(fields: [caseId], references: [id]) @@index([id, caseId]) } model UploadSession { id String @id @default(uuid()) uploadKey String @unique // path/key to store file on CDN (e.g. documents/{uuid}.pdf) nonce String @unique used Boolean @default(false) createdById String? // user id purpose String // "document" | "image" allowedTypes String? // JSON string array maxSize Int? status String @default("PENDING") createdAt DateTime @default(now()) expiresAt DateTime // indexes @@index([createdById]) @@index([status]) } enum UploadStatus { PENDING UPLOADING UPLOADED VERIFIED FAILED EXPIRED } enum StaffRoles { developer admin doctor coordinator } model Staff { id String @id @default(uuid()) username String @unique password String email String? @unique role StaffRoles is_verified Boolean @default(false) send_notif_with_email Boolean @default(false) resetPasswordToken String? resetPasswordExpires DateTime? status StaffStatus @default(ACTIVE) trustScore Int @default(100) strikes Int @default(0) restrictions Restriction[] violations Violation[] documents Document[] reviews Review[] profilePicture Image? @relation(fields: [profilePictureID],references: [id]) profilePictureID Int? translations StaffTranslation[] tokens RefreshToken[] } enum StaffStatus { ACTIVE WARNED RESTRICTED BANNED } model Violation { id String @id @default(uuid()) userId String type ViolationType severity Int reason String user Staff @relation(fields: [userId], references: [id]) createdAt DateTime @default(now()) } enum ViolationType { SPAM RATE_LIMIT CONTENT ABUSE } model Restriction { id String @id @default(uuid()) userId String type RestrictionType expiresAt DateTime? user Staff @relation(fields: [userId], references: [id]) } enum RestrictionType { TEMP SHADOW PERMANENT } model StaffTranslation { id Int @id @default(autoincrement()) staffId String lang Language? @relation(fields: [lang_id], references: [id]) lang_id Int? // فیلدهایی که نیاز به ترجمه دارند displayName String? position String? description String? staff Staff @relation(fields: [staffId], references: [id]) @@unique([staffId, lang_id]) // هر کارمند در هر زبان فقط یک ترجمه دارد } model Image { id Int @id @default(autoincrement()) fileKey String filename String? fileUrl String? mimeType String? size Int? usersProfile Users[] countriesCover Countries[] medicalPackagesThumbnails MedicalPackage[] transferPackageLocationImages TransferPackage[] @relation(name: "TransferPackageLocationImages") transferPackageGalleryImages TransferPackage[] @relation(name: "TransferPackageGalleryImages") staffProfilePictures Staff[] } model Users { id Int @id @default(autoincrement()) slug String @unique type UsersType @default(DOCTOR) displayInMainPage Boolean @default(false) phone String? email String? teamName String? medicalNumber String? expertise Expertise? @relation(fields: [expertiseId], references: [id]) expertiseId Int? image Image? @relation(fields: [imageId], references: [id]) imageId Int? createdAt DateTime @default(now()) translations UserTranslation[] transerTeamMembers TransferTeam[] @@index([id, slug]) } model UserTranslation { id Int @id @default(autoincrement()) userId Int lang Language? @relation(fields: [lang_id], references: [id]) lang_id Int? firstName String? lastName String? position String? bio String? excerpt String? user Users @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([userId, lang_id]) // هر کاربر در هر زبان فقط یک ترجمه دارد } model Expertise { id Int @id @default(autoincrement()) slug String @db.VarChar(100) users Users[] translations ExpertiseTranslation[] } model ExpertiseTranslation { id Int @id @default(autoincrement()) expertiseId Int lang Language? @relation(fields: [lang_id], references: [id]) lang_id Int? displayName String? level EducationLevel? expertise Expertise @relation(fields: [expertiseId], references: [id]) @@unique([expertiseId, lang_id]) // هر کاربر در هر زبان فقط یک ترجمه دارد } enum EducationLevel { GP SPECIALIST SUBSPECIALIST FELLOWSHIP } enum UsersType { DOCTOR TRANSFER_TEAM DEPARTMENT } model PanelConfig { id String @id @default(uuid()) key String @unique value String // مقدار خام (string) type ConfigType // نوع داده description String? updatedAt DateTime @updatedAt createdAt DateTime @default(now()) } model Default { id String @id @default("SITE_DEFAULTS") email String hospitalPhone String logoUrl String? mapAddress String instagramLink String? linkedinLink String? ipdNumber String? updatedAt DateTime @updatedAt translations DefaultTranslation[] } model DefaultTranslation { id Int @id @default(autoincrement()) default Default @relation(fields: [defaultId], references: [id]) defaultId String language Language @relation(fields: [languageId], references: [id]) languageId Int address String underLogoText String? aboutUsText String? patientsRights String? @@unique([defaultId, languageId]) } model Statics { id String @id @default(uuid()) key String @unique @db.VarChar(191) group String @db.VarChar(100) description String? @db.Text translations StaticsTranslation[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([group]) } model StaticsTranslation { id String @id @default(uuid()) languageId Int staticsKeyId String value String @db.Text language Language @relation(fields: [languageId], references: [id], onDelete: Cascade) staticsKey Statics @relation(fields: [staticsKeyId], references: [id], onDelete: Cascade) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([languageId, staticsKeyId]) @@index([languageId]) @@index([staticsKeyId]) } enum ConfigType { BOOLEAN NUMBER STRING JSON STRING_ARRAY } model Page { id Int @id @default(autoincrement()) slug String @unique createdAt DateTime @default(now()) pageBlocks PageBlock[] } model PageBlock { id Int @id @default(autoincrement()) pageId Int type String // hero, text, image, faq, etc sort Int page Page @relation(fields: [pageId], references: [id]) pageBlockTranslations PageBlockTranslation[] } model PageBlockTranslation { id Int @id @default(autoincrement()) blockId Int lang Language? @relation(fields: [lang_id], references: [id]) lang_id Int? field String // title, subtitle, description, button_text value String block PageBlock @relation(fields: [blockId], references: [id]) } model Language { id Int @id @default(autoincrement()) title String slug String @unique pageBlockTranslations PageBlockTranslation[] // translations Translation[] reviewTranslations ReviewTranslation[] staffTranslations StaffTranslation[] userTranslations UserTranslation[] expertiseTranslations ExpertiseTranslation[] defaultTranslations DefaultTranslation[] medicalPackagesTranslations MedicalPackagesTranslation[] transferPackageTranslations TransferPackageTranslations[] transferTeamTranslations TransferTeamTranslation[] staticsTranslations StaticsTranslation[] @@index([id, slug]) } model RefreshToken { id Int @id @default(autoincrement()) token String @unique staff Staff @relation(fields: [staffId], references: [id]) staffId String expiresAt DateTime createdAt DateTime @default(now()) } model Countries { id Int @id @default(autoincrement()) name String callCode String cover Image? @relation(fields: [coverId], references: [id]) coverId Int? createdAt DateTime @default(now()) patients Patient[] } enum AuditAction { CREATE UPDATE DELETE READ } model AuditLog { id Int @id @default(autoincrement()) actorId String? actorRole StaffRoles? action AuditAction entity String entityId Int? before Json? after Json? ip String? createdAt DateTime @default(now()) @@index([entity, entityId]) } model AccessLog { id Int @id @default(autoincrement()) userId String userRole StaffRoles resource String resourceId Int? reason String? ip String? createdAt DateTime @default(now()) } model DecisionLog { id Int @id @default(autoincrement()) decisionType String input Json output Json algorithmVersion String actorId String? createdAt DateTime @default(now()) } model TermsOfService { id String @id @default(uuid()) // شناسه یکتا title String // عنوان سند، مثلا "Terms of Service" content String // متن کامل TOS (Markdown یا HTML) version String @unique // نسخه سند، مثلا "v1.0.0" isActive Boolean @default(true) // آیا این نسخه فعال است؟ createdAt DateTime @default(now()) // زمان ایجاد نسخه updatedAt DateTime @updatedAt // زمان آخرین آپدیت /// روابط اختیاری، اگر بخواهید لاگ پذیرش کاربران را نگه دارید acceptances TosAcceptanceLog[] // ثبت کاربرانی که این نسخه را پذیرفته‌اند } model TosAcceptanceLog { id Int @id @default(autoincrement()) userId String // شناسه کاربر policyType String // نوع سند (TOS یا Privacy Policy) policyVersion String // نسخه سند پذیرفته شده acceptedAt DateTime @default(now()) // زمان پذیرش ip String? // آی‌پی کاربر برای ثبت لاگ tos TermsOfService? @relation(fields: [policyVersion], references: [version]) } model PrivacyPolicy { id Int @id @default(autoincrement()) // شناسه یکتا content String // متن کامل TOS (Markdown یا HTML) createdAt DateTime @default(now()) // زمان ایجاد نسخه updatedAt DateTime @updatedAt // زمان آخرین آپدیت } model MedicalPackage { id Int @id @default(autoincrement()) thumbnail Image? @relation(fields: [thumbnail_id], references: [id]) thumbnail_id Int? icon String? priority Int? price String parent MedicalPackage? @relation("MedicalPackageHierarchy", fields: [parent_id], references: [id], onDelete: SetNull) parent_id Int? // برای اشاره به دسته‌بندی والد children MedicalPackage[] @relation("MedicalPackageHierarchy") translations MedicalPackagesTranslation[] } model MedicalPackagesTranslation { id Int @id @default(autoincrement()) title String @db.VarChar(50) content String? lang Language? @relation(fields: [lang_id], references: [id]) lang_id Int? medicalPackage MedicalPackage? @relation(fields: [medicalPackageId], references: [id]) medicalPackageId Int? @@unique([medicalPackageId, lang_id]) // هر کاربر در هر زبان فقط یک ترجمه دارد } model TransferPackage { id Int @id @default(autoincrement()) location String price String locationImages Image[] @relation(name: "TransferPackageLocationImages") galleryImages Image[] @relation(name: "TransferPackageGalleryImages") transferTeam TransferTeam[] translations TransferPackageTranslations[] } model TransferPackageTranslations { id Int @id @default(autoincrement()) name String content String lang Language? @relation(fields: [lang_id], references: [id]) lang_id Int? transferPackage TransferPackage? @relation(fields: [transferPackageId], references: [id]) transferPackageId Int? @@unique([transferPackageId, lang_id]) // هر کاربر در هر زبان فقط یک ترجمه دارد } model TransferTeam { id Int @id @default(autoincrement()) members Users[] packages TransferPackage[] translations TransferTeamTranslation[] } model TransferTeamTranslation { id Int @id @default(autoincrement()) lang Language? @relation(fields: [lang_id], references: [id]) lang_id Int? transferTeam TransferTeam? @relation(fields: [transferTeamId], references: [id]) transferTeamId Int? name String @db.VarChar(50) introduction String duties String services String @@unique([transferTeamId, lang_id]) // هر کاربر در هر زبان فقط یک ترجمه دارد }