Files
ubi-dan-itam/prisma/schema.prisma
T
toanhoang 4c69bcf2c1 feat: add Employee Prisma model + migration
Employee directory entity per docs/asset-license-management.md's data
model table (id, name, department, manager). department stays a plain
String for now (not a separate Department entity). managerId is a
nullable self-relation (EmployeeManager) - top-level employees have
managerId = null.
2026-07-31 14:53:57 +07:00

76 lines
3.1 KiB
Plaintext

// Prisma schema — source of truth for the DB schema going forward,
// replacing the hand-written `CREATE TABLE IF NOT EXISTS` migration that
// used to live in src/lib/db.ts. Only the Request entity (Draft state only)
// exists so far — see ../processes/asset-license-management.md for the full
// data model / state machine this will grow into.
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
// Fixed, closed set of 2 values tied to the domain (asset vs license
// request) — a native Postgres enum is the idiomatic Prisma mapping here.
// This replaces the old `type TEXT NOT NULL CHECK (type IN ('asset',
// 'license'))` column with `type "RequestType" NOT NULL` backed by a
// `CREATE TYPE "RequestType" AS ENUM ('asset', 'license')`. Same validation
// guarantee (only these two values are ever storable), different mechanism.
// See feature-implementer report for the enum-vs-TEXT+CHECK trade-off.
enum RequestType {
asset
license
}
model Request {
id String @id @default(uuid()) @db.Uuid
employeeId String @map("employee_id")
type RequestType
reason String
// Only the 'Draft' state is implemented so far. Deliberately kept as a
// plain string (not an enum) until more states exist — see
// src/lib/requests.ts's RequestStatus type.
status String @default("Draft")
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
@@map("requests")
}
// Employee directory — see docs/asset-license-management.md's "Data model
// (sơ bộ)" table (id, tên, phòng ban, manager). `department` is deliberately
// a plain String for now, not a separate Department entity — that's a
// possible future normalization, not built yet. `managerId` is a nullable
// self-relation: top-level employees (no manager) have `managerId = null`.
model Employee {
id String @id @default(uuid()) @db.Uuid
name String
department String
managerId String? @map("manager_id") @db.Uuid
manager Employee? @relation("EmployeeManager", fields: [managerId], references: [id])
reports Employee[] @relation("EmployeeManager")
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
@@map("employees")
}
// Minimal internal auth accounts — NOT company SSO (deliberately ruled out,
// see CLAUDE.md / auth decision notes). A handful of trusted IT staff
// accounts, created by hand via scripts/create-user.ts, no self-registration.
// Auth.js v5 Credentials provider + JWT session strategy is used, so this is
// intentionally just a plain user table — no `Account`/`Session`/
// `VerificationToken` models, those exist for OAuth account linking / DB
// session strategy, neither of which applies here. No `role` field either:
// permission model is flat for now (any logged-in user can do anything).
model User {
id String @id @default(uuid()) @db.Uuid
email String @unique
passwordHash String @map("password_hash")
name String
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
@@map("users")
}