feat: add AssetCategoryAttributeDef Prisma model + migration
Per-category dynamic attribute definitions (key/label/dataType/required/ sortOrder), FK to AssetCategory, composite unique (categoryId, key) so the same key is allowed across different categories but not duplicated within one. dataType is a new closed Prisma enum (AssetAttributeDataType), a dev-defined technical set distinct from AssetCategoryValueGroup.
This commit is contained in:
+52
-4
@@ -237,14 +237,62 @@ enum AssetCategoryValueGroup {
|
||||
// (e.g. "Laptop", "Cáp HDMI") freely; only `valueGroup` is a fixed 2-value
|
||||
// set.
|
||||
model AssetCategory {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
name String
|
||||
valueGroup AssetCategoryValueGroup @map("value_group")
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
name String
|
||||
valueGroup AssetCategoryValueGroup @map("value_group")
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
||||
attributeDefs AssetCategoryAttributeDef[]
|
||||
|
||||
@@map("asset_categories")
|
||||
}
|
||||
|
||||
// Fixed, closed set of 4 values — a DEV-defined technical set (which Zod
|
||||
// validator to build dynamically for AssetModel.specs at write time: string
|
||||
// -> z.string(), number -> z.number(), boolean -> z.boolean(), date ->
|
||||
// z.coerce.date()). Deliberately a different kind of "fixed set" than
|
||||
// AssetCategoryValueGroup above: that one is a closed business classification
|
||||
// IT admins never extend; this one is a closed set of *primitive types* the
|
||||
// app's dynamic-attribute machinery knows how to handle. Still modeled as a
|
||||
// native Postgres enum per the same codebase convention (RequestType /
|
||||
// AssetItemStatus / LicenseType / AssetCategoryValueGroup), not a String +
|
||||
// hand-rolled check.
|
||||
enum AssetAttributeDataType {
|
||||
string
|
||||
number
|
||||
boolean
|
||||
date
|
||||
}
|
||||
|
||||
// Per-category dynamic attribute definitions — see docs/
|
||||
// asset-license-management.md's "Cập nhật 2026-08-02 (3): thêm
|
||||
// AssetCategoryAttributeDef" section and the `AssetCategoryAttributeDef` row
|
||||
// in the "Data model (sơ bộ)" table. Defines the *shape* of attributes a
|
||||
// category cares about (e.g. category "Laptop" -> key `ram_gb`, label
|
||||
// "RAM (GB)", dataType number); the actual *values* live in
|
||||
// AssetModel.specs (JSONB), not built in this task. Each AssetCategory owns
|
||||
// its own independent set of attribute defs — deliberately NOT a shared
|
||||
// attribute library across categories (stakeholder explicitly rejected that
|
||||
// design to keep things simple at current scale), so the same `key` string
|
||||
// existing under two different categoryId rows is normal, not a collision —
|
||||
// only unique *within* a category, hence the composite unique below (not a
|
||||
// plain @unique on key alone). `sortOrder` is the display order on the
|
||||
// dynamic form, no uniqueness constraint of its own (ties/gaps are fine,
|
||||
// it's just a sort key).
|
||||
model AssetCategoryAttributeDef {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
categoryId String @map("category_id") @db.Uuid
|
||||
category AssetCategory @relation(fields: [categoryId], references: [id])
|
||||
key String
|
||||
label String
|
||||
dataType AssetAttributeDataType @map("data_type")
|
||||
required Boolean
|
||||
sortOrder Int @map("sort_order")
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
||||
|
||||
@@unique([categoryId, key])
|
||||
@@map("asset_category_attribute_defs")
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
Reference in New Issue
Block a user