Files
kooltab-backoffice/apps/api/prisma/schema.prisma
T
brammie15 1e4438ec5a
CI / build-and-test (push) Canceled after 0s
feat: add stuff
2026-10-20 14:45:22 +02:00

266 lines
7.2 KiB
Plaintext

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model Venue {
id String @id @default(uuid())
name String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
devices Device[]
clientUsers ClientUser[]
tabOwnerships TabOwnership[]
remoteCommands RemoteCommand[]
auditLogs AuditLog[]
regCodes RegistrationCode[]
plannedTabs PlannedTab[]
tabReminders TabReminder[]
tabEvents TabEvent[]
products Product[]
stockAdjustments StockAdjustment[]
}
model Device {
id String @id @default(uuid())
venueId String @unique
name String
tokenHash String @unique
tokenPrefix String
tokenExpiresAt DateTime
lastSeenAt DateTime @default(now())
revokedAt DateTime?
createdAt DateTime @default(now())
venue Venue @relation(fields: [venueId], references: [id])
tabSnapshots OpenTabSnapshot[]
tabOwnerships TabOwnership[]
remoteCommands RemoteCommand[]
tabEvents TabEvent[]
@@index([venueId, revokedAt])
}
model ClientUser {
id String @id @default(uuid())
venueId String
email String @unique
passwordHash String
name String
role String @default("viewer")
createdAt DateTime @default(now())
venue Venue @relation(fields: [venueId], references: [id])
@@index([venueId])
}
model RegistrationCode {
id String @id @default(uuid())
venueId String
code String @unique
expiresAt DateTime
usedAt DateTime?
createdAt DateTime @default(now())
venue Venue @relation(fields: [venueId], references: [id])
@@index([venueId])
}
model OpenTabSnapshot {
id String @id @default(uuid())
deviceId String
tabId String
customerName String
version Int
totalInCents Int
itemCount Int
// The tab's lines as reported by the terminal: [{ productName, quantity, unitPriceInCents }].
// Null for terminals running an app version that doesn't report them.
items Json?
reportedAt DateTime
device Device @relation(fields: [deviceId], references: [id])
@@unique([deviceId, tabId])
@@index([deviceId])
@@index([tabId, reportedAt])
}
model TabOwnership {
tabId String
venueId String
deviceId String
lastReportedAt DateTime
venue Venue @relation(fields: [venueId], references: [id])
device Device @relation(fields: [deviceId], references: [id])
@@id([venueId, tabId])
}
model RemoteCommand {
id String @id @default(uuid())
venueId String
deviceId String
targetTabId String
customerName String?
amountInCents Int?
type String @default("close_tab")
expectedVersion Int
paymentMethod String
requestedBy String
status String @default("pending")
result String?
acknowledgedAt DateTime?
createdAt DateTime @default(now())
expiresAt DateTime
venue Venue @relation(fields: [venueId], references: [id])
device Device @relation(fields: [deviceId], references: [id])
@@index([deviceId, status, createdAt])
@@index([venueId, status, createdAt])
}
model AuditLog {
id String @id @default(uuid())
venueId String
actorType String
actorId String
action String
details String?
createdAt DateTime @default(now())
venue Venue @relation(fields: [venueId], references: [id])
@@index([venueId, createdAt])
}
model RefreshToken {
id String @id @default(uuid())
userId String
tokenHash String @unique
expiresAt DateTime
revokedAt DateTime?
createdAt DateTime @default(now())
@@index([userId])
}
model PlannedTab {
id String @id @default(uuid())
venueId String
customerName String
// Lowercased, whitespace-collapsed name: customers are matched on this everywhere
customerNameKey String
email String?
position Int @default(0)
createdAt DateTime @default(now())
// Set once the terminal reports an open tab with this name; it is then no longer sent to devices
deliveredAt DateTime?
venue Venue @relation(fields: [venueId], references: [id])
@@unique([venueId, customerNameKey])
@@index([venueId])
}
model TabReminder {
id String @id @default(uuid())
venueId String
tabId String
customerName String
email String
amountInCents Int
sentAt DateTime @default(now())
status String @default("sent")
venue Venue @relation(fields: [venueId], references: [id])
@@index([venueId, sentAt])
@@index([venueId, tabId])
}
// What happened on a tab, as reported by the terminal: lines added or removed, and settlements.
// The id is generated by the terminal so a re-sent event is stored only once.
model TabEvent {
id String @id
venueId String
deviceId String
tabId String
customerName String
// line_added | line_removed | settled
type String
productName String?
unitPriceInCents Int?
// Signed: positive for line_added, negative for line_removed
quantityDelta Int?
// settled only
amountInCents Int?
paymentMethod String?
commandId String?
items Json?
occurredAt DateTime
receivedAt DateTime @default(now())
venue Venue @relation(fields: [venueId], references: [id])
device Device @relation(fields: [deviceId], references: [id])
@@index([venueId, occurredAt])
@@index([venueId, type, occurredAt])
}
// The terminal's product catalog with its stock, as last reported by the terminal. The terminal owns the
// stock: it counts sales down offline, so the dashboard only changes it through StockAdjustments.
model Product {
venueId String
// Generated by the terminal
productId String
name String
category String?
priceInCents Int
// Null when the terminal doesn't track stock for this product. Can be negative: a sale is never blocked.
stock Int?
// Dashboard-only setting, never sent to or overwritten by the terminal
lowStockThreshold Int?
reportedAt DateTime
venue Venue @relation(fields: [venueId], references: [id])
@@id([venueId, productId])
}
// A stock change requested from the dashboard. It is sent to the terminal on every sync until the terminal
// reports it applied or rejected.
model StockAdjustment {
id String @id @default(uuid())
venueId String
productId String
productName String
// add (signed: a delivery, or breakage when negative) | set (a stock count)
type String
quantity Int
note String?
requestedBy String
requestedById String
// pending | applied | rejected | cancelled
status String @default("pending")
// The terminal's stock right after applying it
stockAfter Int?
result String?
createdAt DateTime @default(now())
processedAt DateTime?
venue Venue @relation(fields: [venueId], references: [id])
@@index([venueId, status, createdAt])
@@index([venueId, productId, createdAt])
}