generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } enum Role { ADMIN USER } enum SourceType { SQUARE_INVOICE SQUARE_PAYMENT } enum CandidateStatus { PENDING APPROVED REJECTED } model User { id String @id @default(uuid()) email String @unique passwordHash String name String role Role @default(USER) active Boolean @default(true) createdAt DateTime @default(now()) createdById String? createdBy User? @relation("UserCreatedBy", fields: [createdById], references: [id]) createdUsers User[] @relation("UserCreatedBy") approvals CalendarCandidate[] @relation("ApprovedBy") } model CalendarCandidate { id String @id @default(uuid()) sourceType SourceType squareId String squareEnvironment String status CandidateStatus @default(PENDING) // Square's own status (invoice: DRAFT/UNPAID/PAID/etc, payment: COMPLETED/etc) — // independent of our review `status` above, kept for filtering. squareStatus String? title String description String? // When Square created the invoice/payment — distinct from `startAt` (the // event's own date/time). Square's own invoice list sorts by this. squareCreatedAt DateTime? startAt DateTime endAt DateTime allDay Boolean @default(false) location String? customerName String? customerEmail String? amountCents Int? currency String? @default("USD") rawPayload Json fetchedAt DateTime @default(now()) updatedAt DateTime @updatedAt approvedById String? approvedBy User? @relation("ApprovedBy", fields: [approvedById], references: [id]) approvedAt DateTime? rejectedAt DateTime? caldavEventUid String? @unique caldavEventUrl String? caldavEtag String? // Strike (teardown/pickup) as its own, separate calendar entry — often a // different day than the main event, so it gets its own CalDAV event too. hasStrike Boolean @default(false) strikeStartAt DateTime? strikeEndAt DateTime? strikeCaldavEventUid String? @unique strikeCaldavEventUrl String? strikeCaldavEtag String? @@unique([sourceType, squareId, squareEnvironment], name: "square_source_unique") @@index([status]) } model SyncRun { id String @id @default(uuid()) startedAt DateTime @default(now()) finishedAt DateTime? trigger String invoicesFetched Int @default(0) paymentsFetched Int @default(0) candidatesCreated Int @default(0) error String? }