488 lines
13 KiB
Plaintext
488 lines
13 KiB
Plaintext
// This is your Prisma schema file,
|
|
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
|
|
|
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
|
|
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
|
|
|
|
generator client {
|
|
provider = "prisma-client-js"
|
|
output = "../generated/prisma"
|
|
}
|
|
|
|
generator zod {
|
|
provider = "prisma-zod-generator"
|
|
output = "../shared/" // Zod schemas will be generated here inside `db/shared`
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
}
|
|
|
|
model User {
|
|
id Int @id @default(autoincrement())
|
|
username String @unique
|
|
password String
|
|
patients Patient[]
|
|
appointments Appointment[]
|
|
staff Staff[]
|
|
npiProviders NpiProvider[]
|
|
claims Claim[]
|
|
insuranceCredentials InsuranceCredential[]
|
|
updatedPayments Payment[] @relation("PaymentUpdatedBy")
|
|
backups DatabaseBackup[]
|
|
backupDestinations BackupDestination[]
|
|
notifications Notification[]
|
|
cloudFolders CloudFolder[]
|
|
cloudFiles CloudFile[]
|
|
communications Communication[]
|
|
}
|
|
|
|
model Patient {
|
|
id Int @id @default(autoincrement())
|
|
firstName String
|
|
lastName String
|
|
dateOfBirth DateTime @db.Date
|
|
gender String
|
|
phone String
|
|
email String?
|
|
address String?
|
|
city String?
|
|
zipCode String?
|
|
insuranceProvider String?
|
|
insuranceId String?
|
|
groupNumber String?
|
|
policyHolder String?
|
|
allergies String?
|
|
medicalConditions String?
|
|
status PatientStatus @default(UNKNOWN)
|
|
userId Int
|
|
createdAt DateTime @default(now())
|
|
user User @relation(fields: [userId], references: [id])
|
|
appointments Appointment[]
|
|
procedures AppointmentProcedure[]
|
|
claims Claim[]
|
|
groups PdfGroup[]
|
|
payment Payment[]
|
|
communications Communication[]
|
|
|
|
@@index([insuranceId])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
enum PatientStatus {
|
|
ACTIVE
|
|
INACTIVE
|
|
UNKNOWN
|
|
}
|
|
|
|
model Appointment {
|
|
id Int @id @default(autoincrement())
|
|
patientId Int
|
|
userId Int
|
|
staffId Int
|
|
title String
|
|
date DateTime @db.Date
|
|
startTime String // Store time as "hh:mm"
|
|
endTime String // Store time as "hh:mm"
|
|
type String // e.g., "checkup", "cleaning", "filling", etc.
|
|
notes String?
|
|
procedureCodeNotes String?
|
|
status String @default("scheduled") // "scheduled", "completed", "cancelled", "no-show"
|
|
createdAt DateTime @default(now())
|
|
|
|
eligibilityStatus PatientStatus @default(UNKNOWN)
|
|
|
|
patient Patient @relation(fields: [patientId], references: [id], onDelete: Cascade)
|
|
user User @relation(fields: [userId], references: [id])
|
|
staff Staff? @relation(fields: [staffId], references: [id])
|
|
procedures AppointmentProcedure[]
|
|
claims Claim[]
|
|
|
|
@@index([patientId])
|
|
@@index([date])
|
|
}
|
|
|
|
model Staff {
|
|
id Int @id @default(autoincrement())
|
|
userId Int
|
|
name String
|
|
email String?
|
|
role String // e.g., "Dentist", "Hygienist", "Assistant"
|
|
phone String?
|
|
createdAt DateTime @default(now())
|
|
displayOrder Int @default(0)
|
|
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
appointments Appointment[]
|
|
claims Claim[] @relation("ClaimStaff")
|
|
}
|
|
|
|
model NpiProvider {
|
|
id Int @id @default(autoincrement())
|
|
userId Int
|
|
npiNumber String
|
|
providerName String
|
|
createdAt DateTime @default(now())
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([userId, npiNumber])
|
|
@@index([userId])
|
|
}
|
|
|
|
enum ProcedureSource {
|
|
COMBO
|
|
MANUAL
|
|
}
|
|
|
|
model AppointmentProcedure {
|
|
id Int @id @default(autoincrement())
|
|
appointmentId Int
|
|
patientId Int
|
|
|
|
procedureCode String
|
|
procedureLabel String?
|
|
fee Decimal? @db.Decimal(10, 2)
|
|
|
|
category String?
|
|
|
|
toothNumber String?
|
|
toothSurface String?
|
|
oralCavityArea String?
|
|
|
|
source ProcedureSource @default(MANUAL)
|
|
comboKey String?
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
appointment Appointment @relation(fields: [appointmentId], references: [id], onDelete: Cascade)
|
|
patient Patient @relation(fields: [patientId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([appointmentId])
|
|
@@index([patientId])
|
|
}
|
|
|
|
model Claim {
|
|
id Int @id @default(autoincrement())
|
|
patientId Int
|
|
appointmentId Int
|
|
userId Int
|
|
staffId Int
|
|
patientName String
|
|
memberId String
|
|
dateOfBirth DateTime @db.Date
|
|
remarks String
|
|
missingTeethStatus MissingTeethStatus @default(No_missing)
|
|
missingTeeth Json? // { "T_14": "X", "T_G": "O", ... }
|
|
serviceDate DateTime
|
|
insuranceProvider String // e.g., "Delta MA"
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
status ClaimStatus @default(PENDING)
|
|
|
|
patient Patient @relation(fields: [patientId], references: [id], onDelete: Cascade)
|
|
appointment Appointment @relation(fields: [appointmentId], references: [id], onDelete: Cascade)
|
|
user User? @relation(fields: [userId], references: [id])
|
|
staff Staff? @relation("ClaimStaff", fields: [staffId], references: [id])
|
|
|
|
serviceLines ServiceLine[]
|
|
claimFiles ClaimFile[]
|
|
payment Payment?
|
|
}
|
|
|
|
enum ClaimStatus {
|
|
PENDING
|
|
APPROVED
|
|
CANCELLED
|
|
REVIEW
|
|
VOID
|
|
}
|
|
|
|
enum MissingTeethStatus {
|
|
No_missing
|
|
endentulous
|
|
Yes_missing
|
|
}
|
|
|
|
model ServiceLine {
|
|
id Int @id @default(autoincrement())
|
|
claimId Int?
|
|
paymentId Int?
|
|
procedureCode String
|
|
procedureDate DateTime @db.Date
|
|
oralCavityArea String?
|
|
toothNumber String?
|
|
toothSurface String?
|
|
totalBilled Decimal @db.Decimal(10, 2)
|
|
totalPaid Decimal @default(0.00) @db.Decimal(10, 2)
|
|
totalAdjusted Decimal @default(0.00) @db.Decimal(10, 2)
|
|
totalDue Decimal @default(0.00) @db.Decimal(10, 2)
|
|
status ServiceLineStatus @default(UNPAID)
|
|
|
|
claim Claim? @relation(fields: [claimId], references: [id], onDelete: Cascade)
|
|
payment Payment? @relation(fields: [paymentId], references: [id], onDelete: Cascade)
|
|
|
|
serviceLineTransactions ServiceLineTransaction[]
|
|
}
|
|
|
|
enum ServiceLineStatus {
|
|
PENDING
|
|
PARTIALLY_PAID
|
|
PAID
|
|
UNPAID
|
|
ADJUSTED
|
|
OVERPAID
|
|
DENIED
|
|
}
|
|
|
|
model ClaimFile {
|
|
id Int @id @default(autoincrement())
|
|
claimId Int
|
|
filename String
|
|
mimeType String
|
|
|
|
claim Claim @relation(fields: [claimId], references: [id], onDelete: Cascade)
|
|
}
|
|
|
|
model InsuranceCredential {
|
|
id Int @id @default(autoincrement())
|
|
userId Int
|
|
siteKey String
|
|
username String
|
|
password String
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([userId, siteKey])
|
|
@@index([userId])
|
|
}
|
|
|
|
model PdfGroup {
|
|
id Int @id @default(autoincrement())
|
|
title String
|
|
titleKey PdfTitleKey @default(OTHER)
|
|
createdAt DateTime @default(now())
|
|
patientId Int
|
|
patient Patient @relation(fields: [patientId], references: [id], onDelete: Cascade)
|
|
pdfs PdfFile[]
|
|
|
|
@@index([patientId])
|
|
@@index([titleKey])
|
|
}
|
|
|
|
model PdfFile {
|
|
id Int @id @default(autoincrement())
|
|
filename String
|
|
pdfData Bytes
|
|
uploadedAt DateTime @default(now())
|
|
groupId Int
|
|
group PdfGroup @relation(fields: [groupId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([groupId])
|
|
}
|
|
|
|
enum PdfTitleKey {
|
|
INSURANCE_CLAIM
|
|
INSURANCE_CLAIM_PREAUTH
|
|
ELIGIBILITY_STATUS
|
|
CLAIM_STATUS
|
|
OTHER
|
|
}
|
|
|
|
model Payment {
|
|
id Int @id @default(autoincrement())
|
|
claimId Int? @unique
|
|
patientId Int
|
|
userId Int
|
|
updatedById Int?
|
|
totalBilled Decimal @db.Decimal(10, 2)
|
|
totalPaid Decimal @default(0.00) @db.Decimal(10, 2)
|
|
totalAdjusted Decimal @default(0.00) @db.Decimal(10, 2)
|
|
totalDue Decimal @db.Decimal(10, 2)
|
|
status PaymentStatus @default(PENDING)
|
|
notes String?
|
|
icn String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
claim Claim? @relation(fields: [claimId], references: [id], onDelete: Cascade)
|
|
patient Patient @relation(fields: [patientId], references: [id], onDelete: Cascade)
|
|
updatedBy User? @relation("PaymentUpdatedBy", fields: [updatedById], references: [id])
|
|
serviceLineTransactions ServiceLineTransaction[]
|
|
serviceLines ServiceLine[]
|
|
|
|
@@index([claimId])
|
|
@@index([patientId])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model ServiceLineTransaction {
|
|
id Int @id @default(autoincrement())
|
|
paymentId Int
|
|
serviceLineId Int
|
|
transactionId String?
|
|
paidAmount Decimal @db.Decimal(10, 2)
|
|
adjustedAmount Decimal @default(0.00) @db.Decimal(10, 2)
|
|
method PaymentMethod
|
|
receivedDate DateTime
|
|
payerName String?
|
|
notes String?
|
|
createdAt DateTime @default(now())
|
|
|
|
payment Payment @relation(fields: [paymentId], references: [id], onDelete: Cascade)
|
|
serviceLine ServiceLine @relation(fields: [serviceLineId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([paymentId])
|
|
@@index([serviceLineId])
|
|
}
|
|
|
|
enum PaymentStatus {
|
|
PENDING
|
|
PARTIALLY_PAID
|
|
PAID
|
|
OVERPAID
|
|
DENIED
|
|
VOID
|
|
}
|
|
|
|
enum PaymentMethod {
|
|
EFT
|
|
CHECK
|
|
CASH
|
|
CARD
|
|
OTHER
|
|
}
|
|
|
|
// Database management page
|
|
model DatabaseBackup {
|
|
id Int @id @default(autoincrement())
|
|
userId Int
|
|
createdAt DateTime @default(now())
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([userId])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model BackupDestination {
|
|
id Int @id @default(autoincrement())
|
|
userId Int
|
|
path String
|
|
isActive Boolean @default(true)
|
|
createdAt DateTime @default(now())
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
}
|
|
|
|
model Notification {
|
|
id Int @id @default(autoincrement())
|
|
userId Int
|
|
type NotificationTypes
|
|
message String
|
|
createdAt DateTime @default(now())
|
|
read Boolean @default(false)
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([userId])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
enum NotificationTypes {
|
|
BACKUP
|
|
CLAIM
|
|
PAYMENT
|
|
ETC
|
|
}
|
|
|
|
model CloudFolder {
|
|
id Int @id @default(autoincrement())
|
|
userId Int
|
|
name String
|
|
parentId Int?
|
|
parent CloudFolder? @relation("FolderChildren", fields: [parentId], references: [id], onDelete: Cascade)
|
|
children CloudFolder[] @relation("FolderChildren")
|
|
user User @relation(fields: [userId], references: [id])
|
|
files CloudFile[]
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([userId, parentId, name]) // prevents sibling folder name duplicates
|
|
@@index([parentId])
|
|
}
|
|
|
|
model CloudFile {
|
|
id Int @id @default(autoincrement())
|
|
userId Int
|
|
name String
|
|
mimeType String?
|
|
fileSize BigInt @db.BigInt
|
|
folderId Int? // optional: null => root
|
|
isComplete Boolean @default(false) // upload completed?
|
|
totalChunks Int? // optional: expected number of chunks
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
user User @relation(fields: [userId], references: [id])
|
|
folder CloudFolder? @relation(fields: [folderId], references: [id], onDelete: SetNull)
|
|
|
|
chunks CloudFileChunk[]
|
|
|
|
@@index([folderId])
|
|
}
|
|
|
|
model CloudFileChunk {
|
|
id Int @id @default(autoincrement())
|
|
fileId Int
|
|
seq Int
|
|
data Bytes
|
|
createdAt DateTime @default(now())
|
|
|
|
file CloudFile @relation(fields: [fileId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([fileId, seq])
|
|
@@index([fileId, seq])
|
|
}
|
|
|
|
// patient-connection-
|
|
enum CommunicationChannel {
|
|
sms
|
|
voice
|
|
}
|
|
|
|
enum CommunicationDirection {
|
|
outbound
|
|
inbound
|
|
}
|
|
|
|
enum CommunicationStatus {
|
|
queued
|
|
sent
|
|
delivered
|
|
failed
|
|
completed
|
|
busy
|
|
no_answer
|
|
}
|
|
|
|
model Communication {
|
|
id Int @id @default(autoincrement())
|
|
patientId Int
|
|
userId Int?
|
|
|
|
channel CommunicationChannel
|
|
direction CommunicationDirection
|
|
status CommunicationStatus
|
|
|
|
body String?
|
|
callDuration Int?
|
|
twilioSid String?
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
// Relations
|
|
patient Patient @relation(fields: [patientId], references: [id])
|
|
user User? @relation(fields: [userId], references: [id])
|
|
|
|
@@map("communications")
|
|
}
|