142 lines
6.1 KiB
Plaintext
142 lines
6.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
|
|
// Was a free-text String with no referential integrity — retrofitted to a
|
|
// real FK once Employee records existed to point to (see Employee model
|
|
// above). No production data existed at the time of this change (`requests`
|
|
// table was empty), so this was a plain column-type migration, not a
|
|
// backfill.
|
|
employeeId String @map("employee_id") @db.Uuid
|
|
employee Employee @relation(fields: [employeeId], references: [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")
|
|
requests Request[]
|
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
|
|
|
@@map("employees")
|
|
}
|
|
|
|
// Fixed, closed set of 4 values tied to the domain (see docs/
|
|
// asset-license-management.md's "Data model (sơ bộ)" table) — native
|
|
// Postgres enum, same rationale as RequestType above.
|
|
enum AssetItemStatus {
|
|
in_stock
|
|
assigned
|
|
repair
|
|
retired
|
|
}
|
|
|
|
// Hardware inventory — see docs/asset-license-management.md's "Data model
|
|
// (sơ bộ)" table (serial number, model, ngày mua, bảo hành, trạng thái).
|
|
// No Vendor/Assignment relation yet — those entities don't exist yet
|
|
// (see "Việc còn dở" in CLAUDE.md), so this is a standalone entity for now,
|
|
// same starting shape as Employee before Request grew an FK to it.
|
|
// warrantyEndDate is nullable: not every asset has known warranty terms at
|
|
// intake time (smallest sensible assumption — doesn't block creation).
|
|
model AssetItem {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
serialNumber String @unique @map("serial_number")
|
|
model String
|
|
purchaseDate DateTime @map("purchase_date") @db.Date
|
|
warrantyEndDate DateTime? @map("warranty_end_date") @db.Date
|
|
status AssetItemStatus @default(in_stock)
|
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
|
|
|
@@map("asset_items")
|
|
}
|
|
|
|
// Fixed, closed set of 2 values tied to the domain (see docs/
|
|
// asset-license-management.md's "Data model (sơ bộ)" table) — native
|
|
// Postgres enum, same rationale as RequestType/AssetItemStatus above.
|
|
enum LicenseType {
|
|
perpetual
|
|
subscription
|
|
}
|
|
|
|
// Software license inventory — see docs/asset-license-management.md's "Data
|
|
// model (sơ bộ)" table (tên, vendor, tổng seat, seat đã dùng, ngày hết hạn,
|
|
// loại). `vendor` is deliberately a plain String for now, not a separate
|
|
// Vendor entity — no Vendor entity exists yet, same "plain string for now"
|
|
// precedent as Employee.department. `seatsUsed` is a stored counter
|
|
// (defaults to 0 at creation), not a computed value — it will only ever be
|
|
// set to 0 here since Assignment (the thing that would consume seats)
|
|
// doesn't exist yet. `expiryDate` is nullable: perpetual licenses never
|
|
// expire, only subscription licenses usually have one.
|
|
model License {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
name String
|
|
vendor String
|
|
seatsTotal Int @map("seats_total")
|
|
seatsUsed Int @default(0) @map("seats_used")
|
|
expiryDate DateTime? @map("expiry_date") @db.Date
|
|
licenseType LicenseType @map("license_type")
|
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
|
|
|
@@map("licenses")
|
|
}
|
|
|
|
// 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")
|
|
}
|