Datenmodell fuer Spieler, Guthaben-Ledger und CCs
Guthaben werden als Kontobuch gefuehrt: jede Bewegung ist eine Zeile, der Stand eines Topfes ist deren Summe. Zusammengehoerige Zeilen teilen sich eine groupId, damit ein CC-Paket als Ganzes storniert werden kann; ein unique Constraint auf reversalOfId verhindert doppelte Stornos. CCs bilden ueber lineageId und predecessorId eine Redesign-Kette. Der Grund einer Deaktivierung entscheidet spaeter ueber die Reaktivierungskosten.
This commit is contained in:
@@ -0,0 +1,286 @@
|
|||||||
|
-- CreateSchema
|
||||||
|
CREATE SCHEMA IF NOT EXISTS "public";
|
||||||
|
|
||||||
|
-- CreateEnum
|
||||||
|
CREATE TYPE "Role" AS ENUM ('ADMIN', 'EDITOR', 'VIEWER');
|
||||||
|
|
||||||
|
-- CreateEnum
|
||||||
|
CREATE TYPE "CreditKind" AS ENUM ('CC', 'REDESIGN_PAID', 'REDESIGN_KULANZ');
|
||||||
|
|
||||||
|
-- CreateEnum
|
||||||
|
CREATE TYPE "CreditSource" AS ENUM ('CC_PACKAGE', 'REDESIGN_PACKAGE', 'KULANZ', 'ADJUSTMENT', 'CONSUME', 'REVERSAL');
|
||||||
|
|
||||||
|
-- CreateEnum
|
||||||
|
CREATE TYPE "CharacterStatus" AS ENUM ('ACTIVE', 'INACTIVE');
|
||||||
|
|
||||||
|
-- CreateEnum
|
||||||
|
CREATE TYPE "InactiveReason" AS ENUM ('PLAYER_INACTIVITY', 'REWORKED', 'OTHER');
|
||||||
|
|
||||||
|
-- CreateEnum
|
||||||
|
CREATE TYPE "ProductTarget" AS ENUM ('CC_PACKAGE', 'REDESIGN_PACKAGE', 'IGNORE');
|
||||||
|
|
||||||
|
-- CreateEnum
|
||||||
|
CREATE TYPE "ImportStatus" AS ENUM ('PENDING', 'COMMITTED', 'DISCARDED');
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "User" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"username" TEXT NOT NULL,
|
||||||
|
"passwordHash" TEXT NOT NULL,
|
||||||
|
"role" "Role" NOT NULL DEFAULT 'EDITOR',
|
||||||
|
"totpSecret" TEXT,
|
||||||
|
"totpEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"mustChangePassword" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"lastLoginAt" TIMESTAMP(3),
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Session" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"tokenHash" TEXT NOT NULL,
|
||||||
|
"userId" INTEGER NOT NULL,
|
||||||
|
"expiresAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"lastSeenAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"ip" TEXT,
|
||||||
|
"userAgent" TEXT,
|
||||||
|
|
||||||
|
CONSTRAINT "Session_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Player" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"displayName" TEXT NOT NULL,
|
||||||
|
"steamId" TEXT,
|
||||||
|
"forumName" TEXT,
|
||||||
|
"notes" TEXT,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "Player_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "CreditLedger" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"playerId" INTEGER NOT NULL,
|
||||||
|
"kind" "CreditKind" NOT NULL,
|
||||||
|
"amount" INTEGER NOT NULL,
|
||||||
|
"source" "CreditSource" NOT NULL,
|
||||||
|
"groupId" TEXT NOT NULL,
|
||||||
|
"characterId" INTEGER,
|
||||||
|
"importId" INTEGER,
|
||||||
|
"note" TEXT,
|
||||||
|
"reversalOfId" INTEGER,
|
||||||
|
"createdById" INTEGER,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
CONSTRAINT "CreditLedger_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Character" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"playerId" INTEGER NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"loreText" TEXT,
|
||||||
|
"status" "CharacterStatus" NOT NULL DEFAULT 'ACTIVE',
|
||||||
|
"inactiveReason" "InactiveReason",
|
||||||
|
"lineageId" TEXT NOT NULL,
|
||||||
|
"predecessorId" INTEGER,
|
||||||
|
"createdById" INTEGER,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "Character_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Import" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"filename" TEXT NOT NULL,
|
||||||
|
"status" "ImportStatus" NOT NULL DEFAULT 'PENDING',
|
||||||
|
"rowCount" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"bookedCount" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"mapping" JSONB,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"committedAt" TIMESTAMP(3),
|
||||||
|
"importedById" INTEGER,
|
||||||
|
|
||||||
|
CONSTRAINT "Import_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "ImportRow" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"importId" INTEGER NOT NULL,
|
||||||
|
"lineNo" INTEGER NOT NULL,
|
||||||
|
"externalId" TEXT,
|
||||||
|
"playerKey" TEXT,
|
||||||
|
"playerName" TEXT,
|
||||||
|
"productName" TEXT,
|
||||||
|
"purchasedAt" TIMESTAMP(3),
|
||||||
|
"target" "ProductTarget",
|
||||||
|
"resolvedPlayerId" INTEGER,
|
||||||
|
"plan" TEXT NOT NULL,
|
||||||
|
"raw" JSONB NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "ImportRow_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "ImportedOrder" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"externalId" TEXT NOT NULL,
|
||||||
|
"importId" INTEGER NOT NULL,
|
||||||
|
"playerId" INTEGER NOT NULL,
|
||||||
|
"target" "ProductTarget" NOT NULL,
|
||||||
|
"groupId" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
CONSTRAINT "ImportedOrder_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "ProductMapping" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"matchPattern" TEXT NOT NULL,
|
||||||
|
"target" "ProductTarget" NOT NULL,
|
||||||
|
"priority" INTEGER NOT NULL DEFAULT 100,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"note" TEXT,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "ProductMapping_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "AuditLog" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"actorUserId" INTEGER,
|
||||||
|
"actorName" TEXT,
|
||||||
|
"action" TEXT NOT NULL,
|
||||||
|
"targetType" TEXT NOT NULL,
|
||||||
|
"targetId" TEXT,
|
||||||
|
"details" JSONB,
|
||||||
|
"ip" TEXT,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
CONSTRAINT "AuditLog_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "User_username_key" ON "User"("username");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "Session_tokenHash_key" ON "Session"("tokenHash");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "Session_userId_idx" ON "Session"("userId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "Session_expiresAt_idx" ON "Session"("expiresAt");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "Player_steamId_key" ON "Player"("steamId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "Player_displayName_idx" ON "Player"("displayName");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "CreditLedger_reversalOfId_key" ON "CreditLedger"("reversalOfId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "CreditLedger_playerId_idx" ON "CreditLedger"("playerId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "CreditLedger_playerId_kind_idx" ON "CreditLedger"("playerId", "kind");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "CreditLedger_groupId_idx" ON "CreditLedger"("groupId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "CreditLedger_createdAt_idx" ON "CreditLedger"("createdAt");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "Character_playerId_idx" ON "Character"("playerId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "Character_playerId_status_idx" ON "Character"("playerId", "status");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "Character_lineageId_idx" ON "Character"("lineageId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "Import_status_idx" ON "Import"("status");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "ImportRow_importId_idx" ON "ImportRow"("importId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "ImportedOrder_externalId_key" ON "ImportedOrder"("externalId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "ImportedOrder_playerId_idx" ON "ImportedOrder"("playerId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "ProductMapping_priority_idx" ON "ProductMapping"("priority");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "AuditLog_createdAt_idx" ON "AuditLog"("createdAt");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "AuditLog_targetType_targetId_idx" ON "AuditLog"("targetType", "targetId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "AuditLog_actorUserId_idx" ON "AuditLog"("actorUserId");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "CreditLedger" ADD CONSTRAINT "CreditLedger_playerId_fkey" FOREIGN KEY ("playerId") REFERENCES "Player"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "CreditLedger" ADD CONSTRAINT "CreditLedger_characterId_fkey" FOREIGN KEY ("characterId") REFERENCES "Character"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "CreditLedger" ADD CONSTRAINT "CreditLedger_importId_fkey" FOREIGN KEY ("importId") REFERENCES "Import"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "CreditLedger" ADD CONSTRAINT "CreditLedger_reversalOfId_fkey" FOREIGN KEY ("reversalOfId") REFERENCES "CreditLedger"("id") ON DELETE NO ACTION ON UPDATE NO ACTION;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "CreditLedger" ADD CONSTRAINT "CreditLedger_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Character" ADD CONSTRAINT "Character_playerId_fkey" FOREIGN KEY ("playerId") REFERENCES "Player"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Character" ADD CONSTRAINT "Character_predecessorId_fkey" FOREIGN KEY ("predecessorId") REFERENCES "Character"("id") ON DELETE NO ACTION ON UPDATE NO ACTION;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Character" ADD CONSTRAINT "Character_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Import" ADD CONSTRAINT "Import_importedById_fkey" FOREIGN KEY ("importedById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "ImportRow" ADD CONSTRAINT "ImportRow_importId_fkey" FOREIGN KEY ("importId") REFERENCES "Import"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "ImportedOrder" ADD CONSTRAINT "ImportedOrder_importId_fkey" FOREIGN KEY ("importId") REFERENCES "Import"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "ImportedOrder" ADD CONSTRAINT "ImportedOrder_playerId_fkey" FOREIGN KEY ("playerId") REFERENCES "Player"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "AuditLog" ADD CONSTRAINT "AuditLog_actorUserId_fkey" FOREIGN KEY ("actorUserId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
@@ -0,0 +1,259 @@
|
|||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
binaryTargets = ["native", "debian-openssl-3.0.x"]
|
||||||
|
}
|
||||||
|
|
||||||
|
datasource db {
|
||||||
|
provider = "postgresql"
|
||||||
|
url = env("DATABASE_URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Role {
|
||||||
|
ADMIN
|
||||||
|
EDITOR
|
||||||
|
VIEWER
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CreditKind {
|
||||||
|
CC
|
||||||
|
REDESIGN_PAID
|
||||||
|
REDESIGN_KULANZ
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CreditSource {
|
||||||
|
CC_PACKAGE
|
||||||
|
REDESIGN_PACKAGE
|
||||||
|
KULANZ
|
||||||
|
ADJUSTMENT
|
||||||
|
CONSUME
|
||||||
|
REVERSAL
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CharacterStatus {
|
||||||
|
ACTIVE
|
||||||
|
INACTIVE
|
||||||
|
}
|
||||||
|
|
||||||
|
enum InactiveReason {
|
||||||
|
PLAYER_INACTIVITY
|
||||||
|
REWORKED
|
||||||
|
OTHER
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ProductTarget {
|
||||||
|
CC_PACKAGE
|
||||||
|
REDESIGN_PACKAGE
|
||||||
|
IGNORE
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ImportStatus {
|
||||||
|
PENDING
|
||||||
|
COMMITTED
|
||||||
|
DISCARDED
|
||||||
|
}
|
||||||
|
|
||||||
|
model User {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
username String @unique
|
||||||
|
passwordHash String
|
||||||
|
role Role @default(EDITOR)
|
||||||
|
totpSecret String?
|
||||||
|
totpEnabled Boolean @default(false)
|
||||||
|
isActive Boolean @default(true)
|
||||||
|
mustChangePassword Boolean @default(false)
|
||||||
|
lastLoginAt DateTime?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
sessions Session[]
|
||||||
|
auditEntries AuditLog[]
|
||||||
|
ledgerEntries CreditLedger[] @relation("LedgerCreatedBy")
|
||||||
|
charactersCreated Character[] @relation("CharacterCreatedBy")
|
||||||
|
imports Import[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model Session {
|
||||||
|
id String @id @default(uuid())
|
||||||
|
tokenHash String @unique
|
||||||
|
userId Int
|
||||||
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
expiresAt DateTime
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
lastSeenAt DateTime @default(now())
|
||||||
|
ip String?
|
||||||
|
userAgent String?
|
||||||
|
|
||||||
|
@@index([userId])
|
||||||
|
@@index([expiresAt])
|
||||||
|
}
|
||||||
|
|
||||||
|
model Player {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
displayName String
|
||||||
|
steamId String? @unique
|
||||||
|
forumName String?
|
||||||
|
notes String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
ledger CreditLedger[]
|
||||||
|
characters Character[]
|
||||||
|
orders ImportedOrder[]
|
||||||
|
|
||||||
|
@@index([displayName])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Kontobuch: jede Guthaben-Bewegung ist eine Zeile (+1 Zugang, -1 Verbrauch).
|
||||||
|
/// Der offene Stand eines Topfes ist die Summe der amounts je kind.
|
||||||
|
model CreditLedger {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
player Player @relation(fields: [playerId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
playerId Int
|
||||||
|
kind CreditKind
|
||||||
|
amount Int
|
||||||
|
source CreditSource
|
||||||
|
|
||||||
|
/// Klammert zusammengehoerige Zeilen: ein CC-Paket schreibt zwei Zeilen
|
||||||
|
/// mit derselben groupId, damit ein Storno beide zurueckholt.
|
||||||
|
groupId String @default(uuid())
|
||||||
|
|
||||||
|
characterId Int?
|
||||||
|
character Character? @relation(fields: [characterId], references: [id], onDelete: SetNull)
|
||||||
|
|
||||||
|
importId Int?
|
||||||
|
import Import? @relation(fields: [importId], references: [id], onDelete: SetNull)
|
||||||
|
|
||||||
|
note String?
|
||||||
|
|
||||||
|
/// Storno erfolgt als Gegenbuchung, nicht als Loeschung. Das @unique
|
||||||
|
/// verhindert, dass eine Zeile zweimal storniert wird.
|
||||||
|
reversalOfId Int? @unique
|
||||||
|
reversalOf CreditLedger? @relation("LedgerReversal", fields: [reversalOfId], references: [id], onDelete: NoAction, onUpdate: NoAction)
|
||||||
|
reversedBy CreditLedger? @relation("LedgerReversal")
|
||||||
|
|
||||||
|
createdById Int?
|
||||||
|
createdBy User? @relation("LedgerCreatedBy", fields: [createdById], references: [id], onDelete: SetNull)
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
|
||||||
|
@@index([playerId])
|
||||||
|
@@index([playerId, kind])
|
||||||
|
@@index([groupId])
|
||||||
|
@@index([createdAt])
|
||||||
|
}
|
||||||
|
|
||||||
|
model Character {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
playerId Int
|
||||||
|
player Player @relation(fields: [playerId], references: [id], onDelete: Cascade)
|
||||||
|
name String
|
||||||
|
loreText String?
|
||||||
|
status CharacterStatus @default(ACTIVE)
|
||||||
|
inactiveReason InactiveReason?
|
||||||
|
|
||||||
|
/// Alle CCs einer Redesign-Kette teilen sich die lineageId.
|
||||||
|
lineageId String @default(uuid())
|
||||||
|
predecessorId Int?
|
||||||
|
predecessor Character? @relation("CharacterLineage", fields: [predecessorId], references: [id], onDelete: NoAction, onUpdate: NoAction)
|
||||||
|
successors Character[] @relation("CharacterLineage")
|
||||||
|
|
||||||
|
ledgerEntries CreditLedger[]
|
||||||
|
|
||||||
|
createdById Int?
|
||||||
|
createdBy User? @relation("CharacterCreatedBy", fields: [createdById], references: [id], onDelete: SetNull)
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
@@index([playerId])
|
||||||
|
@@index([playerId, status])
|
||||||
|
@@index([lineageId])
|
||||||
|
}
|
||||||
|
|
||||||
|
model Import {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
filename String
|
||||||
|
status ImportStatus @default(PENDING)
|
||||||
|
rowCount Int @default(0)
|
||||||
|
bookedCount Int @default(0)
|
||||||
|
mapping Json?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
committedAt DateTime?
|
||||||
|
|
||||||
|
importedById Int?
|
||||||
|
importedBy User? @relation(fields: [importedById], references: [id], onDelete: SetNull)
|
||||||
|
|
||||||
|
rows ImportRow[]
|
||||||
|
orders ImportedOrder[]
|
||||||
|
ledgerEntries CreditLedger[]
|
||||||
|
|
||||||
|
@@index([status])
|
||||||
|
}
|
||||||
|
|
||||||
|
model ImportRow {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
importId Int
|
||||||
|
import Import @relation(fields: [importId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
lineNo Int
|
||||||
|
externalId String?
|
||||||
|
playerKey String?
|
||||||
|
playerName String?
|
||||||
|
productName String?
|
||||||
|
purchasedAt DateTime?
|
||||||
|
|
||||||
|
target ProductTarget?
|
||||||
|
resolvedPlayerId Int?
|
||||||
|
|
||||||
|
/// Was beim Buchen passieren wuerde: BOOK, CREATE_PLAYER_AND_BOOK,
|
||||||
|
/// SKIP_DUPLICATE, SKIP_IGNORED, SKIP_UNMAPPED, SKIP_NO_PLAYER
|
||||||
|
plan String
|
||||||
|
raw Json
|
||||||
|
|
||||||
|
@@index([importId])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dedup-Anker: eine bereits gebuchte Bestell-ID wird nie ein zweites Mal gebucht.
|
||||||
|
model ImportedOrder {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
externalId String @unique
|
||||||
|
importId Int
|
||||||
|
import Import @relation(fields: [importId], references: [id], onDelete: Cascade)
|
||||||
|
playerId Int
|
||||||
|
player Player @relation(fields: [playerId], references: [id], onDelete: Cascade)
|
||||||
|
target ProductTarget
|
||||||
|
groupId String
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
|
||||||
|
@@index([playerId])
|
||||||
|
}
|
||||||
|
|
||||||
|
model ProductMapping {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
matchPattern String
|
||||||
|
target ProductTarget
|
||||||
|
priority Int @default(100)
|
||||||
|
isActive Boolean @default(true)
|
||||||
|
note String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
@@index([priority])
|
||||||
|
}
|
||||||
|
|
||||||
|
model AuditLog {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
actorUserId Int?
|
||||||
|
actor User? @relation(fields: [actorUserId], references: [id], onDelete: SetNull)
|
||||||
|
/// Name wird mitgeschrieben, damit der Eintrag lesbar bleibt, wenn der Account weg ist.
|
||||||
|
actorName String?
|
||||||
|
action String
|
||||||
|
targetType String
|
||||||
|
targetId String?
|
||||||
|
details Json?
|
||||||
|
ip String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
|
||||||
|
@@index([createdAt])
|
||||||
|
@@index([targetType, targetId])
|
||||||
|
@@index([actorUserId])
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user