This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "RemoteCommand" DROP CONSTRAINT "RemoteCommand_deviceId_fkey";
|
||||
|
||||
-- DropIndex
|
||||
DROP INDEX "Device_venueId_key";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "Venue" ADD COLUMN "tabsSeededAt" TIMESTAMP(3);
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "RemoteCommand" ALTER COLUMN "deviceId" DROP NOT NULL;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "TabEvent" ADD COLUMN "lineId" TEXT,
|
||||
ADD COLUMN "productId" TEXT,
|
||||
ADD COLUMN "stockSetTo" INTEGER,
|
||||
ALTER COLUMN "tabId" DROP NOT NULL,
|
||||
ALTER COLUMN "customerName" DROP NOT NULL;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "Product" ADD COLUMN "countedAt" TIMESTAMP(3);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Tab" (
|
||||
"venueId" TEXT NOT NULL,
|
||||
"id" TEXT NOT NULL,
|
||||
"customerName" TEXT NOT NULL,
|
||||
"openedAt" TIMESTAMP(3) NOT NULL,
|
||||
"version" INTEGER NOT NULL DEFAULT 0,
|
||||
"lines" JSONB NOT NULL,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Tab_pkey" PRIMARY KEY ("venueId","id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Settlement" (
|
||||
"id" TEXT NOT NULL,
|
||||
"sequence" SERIAL NOT NULL,
|
||||
"venueId" TEXT NOT NULL,
|
||||
"tabId" TEXT NOT NULL,
|
||||
"customerName" TEXT NOT NULL,
|
||||
"settledAt" TIMESTAMP(3) NOT NULL,
|
||||
"paymentMethod" TEXT NOT NULL,
|
||||
"amountInCents" INTEGER NOT NULL,
|
||||
"lines" JSONB NOT NULL,
|
||||
"deviceId" TEXT,
|
||||
"requestedBy" TEXT,
|
||||
"commandId" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "Settlement_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Settlement_sequence_key" ON "Settlement"("sequence");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Settlement_venueId_sequence_idx" ON "Settlement"("venueId", "sequence");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Settlement_venueId_settledAt_idx" ON "Settlement"("venueId", "settledAt");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "RemoteCommand" ADD CONSTRAINT "RemoteCommand_deviceId_fkey" FOREIGN KEY ("deviceId") REFERENCES "Device"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Tab" ADD CONSTRAINT "Tab_venueId_fkey" FOREIGN KEY ("venueId") REFERENCES "Venue"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Settlement" ADD CONSTRAINT "Settlement_venueId_fkey" FOREIGN KEY ("venueId") REFERENCES "Venue"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Settlement" ADD CONSTRAINT "Settlement_deviceId_fkey" FOREIGN KEY ("deviceId") REFERENCES "Device"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
@@ -24,11 +24,17 @@ model Venue {
|
||||
tabEvents TabEvent[]
|
||||
products Product[]
|
||||
stockAdjustments StockAdjustment[]
|
||||
tabs Tab[]
|
||||
settlements Settlement[]
|
||||
// Set on the first sync after the server started holding the tabs: the tabs were then taken over from the
|
||||
// terminal's own open tabs, once
|
||||
tabsSeededAt DateTime?
|
||||
}
|
||||
|
||||
model Device {
|
||||
id String @id @default(uuid())
|
||||
venueId String @unique
|
||||
// A venue can have several terminals
|
||||
venueId String
|
||||
name String
|
||||
tokenHash String @unique
|
||||
tokenPrefix String
|
||||
@@ -42,6 +48,7 @@ model Device {
|
||||
tabOwnerships TabOwnership[]
|
||||
remoteCommands RemoteCommand[]
|
||||
tabEvents TabEvent[]
|
||||
settlements Settlement[]
|
||||
|
||||
@@index([venueId, revokedAt])
|
||||
}
|
||||
@@ -105,10 +112,13 @@ model TabOwnership {
|
||||
@@id([venueId, tabId])
|
||||
}
|
||||
|
||||
// A request from the dashboard. close_tab used to be executed by the owning terminal; the server now settles
|
||||
// the tab itself and records the command as completed right away, for the history.
|
||||
model RemoteCommand {
|
||||
id String @id @default(uuid())
|
||||
venueId String
|
||||
deviceId String
|
||||
// Null for a command the server carried out itself
|
||||
deviceId String?
|
||||
targetTabId String
|
||||
customerName String?
|
||||
amountInCents Int?
|
||||
@@ -122,8 +132,8 @@ model RemoteCommand {
|
||||
createdAt DateTime @default(now())
|
||||
expiresAt DateTime
|
||||
|
||||
venue Venue @relation(fields: [venueId], references: [id])
|
||||
device Device @relation(fields: [deviceId], references: [id])
|
||||
venue Venue @relation(fields: [venueId], references: [id])
|
||||
device Device? @relation(fields: [deviceId], references: [id])
|
||||
|
||||
@@index([deviceId, status, createdAt])
|
||||
@@index([venueId, status, createdAt])
|
||||
@@ -188,16 +198,19 @@ model TabReminder {
|
||||
@@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.
|
||||
// What happened on a tab, as reported by the terminal. The id is generated by the terminal so a re-sent event is
|
||||
// stored, and applied to the tab state and stock, only once.
|
||||
model TabEvent {
|
||||
id String @id
|
||||
venueId String
|
||||
deviceId String
|
||||
tabId String
|
||||
customerName String
|
||||
// line_added | line_removed | settled
|
||||
// Null for stock_set, which is not about a tab
|
||||
tabId String?
|
||||
customerName String?
|
||||
// tab_opened | tab_renamed | tab_deleted | line_added | line_removed | settled | stock_set
|
||||
type String
|
||||
lineId String?
|
||||
productId String?
|
||||
productName String?
|
||||
unitPriceInCents Int?
|
||||
// Signed: positive for line_added, negative for line_removed
|
||||
@@ -207,6 +220,8 @@ model TabEvent {
|
||||
paymentMethod String?
|
||||
commandId String?
|
||||
items Json?
|
||||
// stock_set: the counted stock, null to stop tracking
|
||||
stockSetTo Int?
|
||||
occurredAt DateTime
|
||||
receivedAt DateTime @default(now())
|
||||
|
||||
@@ -217,8 +232,9 @@ model TabEvent {
|
||||
@@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.
|
||||
// The venue's products as reported by its terminals, with the stock. The server owns the stock: it follows the
|
||||
// terminals' line events and stock counts and the dashboard's StockAdjustments. A product is never removed because
|
||||
// one terminal doesn't report it.
|
||||
model Product {
|
||||
venueId String
|
||||
// Generated by the terminal
|
||||
@@ -226,8 +242,10 @@ model Product {
|
||||
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.
|
||||
// Null when stock is not tracked for this product. Can be negative: a sale is never blocked.
|
||||
stock Int?
|
||||
// When the stock was last counted; line events that happened before it are already in the count
|
||||
countedAt DateTime?
|
||||
// Dashboard-only setting, never sent to or overwritten by the terminal
|
||||
lowStockThreshold Int?
|
||||
reportedAt DateTime
|
||||
@@ -237,8 +255,8 @@ model Product {
|
||||
@@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.
|
||||
// A stock change made on the dashboard. The server applies it right away. Older ones may still have been applied
|
||||
// by a terminal, which reported the outcome.
|
||||
model StockAdjustment {
|
||||
id String @id @default(uuid())
|
||||
venueId String
|
||||
@@ -252,7 +270,7 @@ model StockAdjustment {
|
||||
requestedById String
|
||||
// pending | applied | rejected | cancelled
|
||||
status String @default("pending")
|
||||
// The terminal's stock right after applying it
|
||||
// The stock right after applying it
|
||||
stockAfter Int?
|
||||
result String?
|
||||
createdAt DateTime @default(now())
|
||||
@@ -263,3 +281,49 @@ model StockAdjustment {
|
||||
@@index([venueId, status, createdAt])
|
||||
@@index([venueId, productId, createdAt])
|
||||
}
|
||||
|
||||
// An open tab of the venue: the source of truth the terminals replace their own tabs with on every sync. Tabs are
|
||||
// never closed, only emptied by a settlement; tab_deleted removes one.
|
||||
model Tab {
|
||||
venueId String
|
||||
// Generated by the terminal that opened it
|
||||
id String
|
||||
customerName String
|
||||
openedAt DateTime
|
||||
// Bumped by every change, so the dashboard can refuse to settle a tab that changed since it was shown
|
||||
version Int @default(0)
|
||||
// [{ id, productId, productName, unitPriceInCents, units: [{ id, addedAt, removedAt }] }]
|
||||
lines Json
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
venue Venue @relation(fields: [venueId], references: [id])
|
||||
|
||||
@@id([venueId, id])
|
||||
}
|
||||
|
||||
// A settlement made on a terminal (id = the settled event's id) or on the dashboard. Terminals read them as a feed
|
||||
// after their cursor, which is the sequence number.
|
||||
model Settlement {
|
||||
id String @id
|
||||
sequence Int @unique @default(autoincrement())
|
||||
venueId String
|
||||
tabId String
|
||||
customerName String
|
||||
settledAt DateTime
|
||||
// cash | payconiq | payment_pending | other
|
||||
paymentMethod String
|
||||
amountInCents Int
|
||||
// The settled lines, in the shape of Tab.lines with each line's settled and removed units
|
||||
lines Json
|
||||
// Who settled it: a terminal, or a dashboard user
|
||||
deviceId String?
|
||||
requestedBy String?
|
||||
commandId String?
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
venue Venue @relation(fields: [venueId], references: [id])
|
||||
device Device? @relation(fields: [deviceId], references: [id])
|
||||
|
||||
@@index([venueId, sequence])
|
||||
@@index([venueId, settledAt])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user