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:
@@ -0,0 +1,22 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "AssetAttributeDataType" AS ENUM ('string', 'number', 'boolean', 'date');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "asset_category_attribute_defs" (
|
||||
"id" UUID NOT NULL,
|
||||
"category_id" UUID NOT NULL,
|
||||
"key" TEXT NOT NULL,
|
||||
"label" TEXT NOT NULL,
|
||||
"data_type" "AssetAttributeDataType" NOT NULL,
|
||||
"required" BOOLEAN NOT NULL,
|
||||
"sort_order" INTEGER NOT NULL,
|
||||
"created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "asset_category_attribute_defs_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "asset_category_attribute_defs_category_id_key_key" ON "asset_category_attribute_defs"("category_id", "key");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "asset_category_attribute_defs" ADD CONSTRAINT "asset_category_attribute_defs_category_id_fkey" FOREIGN KEY ("category_id") REFERENCES "asset_categories"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
@@ -241,10 +241,58 @@ model AssetCategory {
|
||||
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