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
|
name String
|
||||||
valueGroup AssetCategoryValueGroup @map("value_group")
|
valueGroup AssetCategoryValueGroup @map("value_group")
|
||||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
||||||
|
attributeDefs AssetCategoryAttributeDef[]
|
||||||
|
|
||||||
@@map("asset_categories")
|
@@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,
|
// Minimal internal auth accounts — NOT company SSO (deliberately ruled out,
|
||||||
// see CLAUDE.md / auth decision notes). A handful of trusted IT staff
|
// see CLAUDE.md / auth decision notes). A handful of trusted IT staff
|
||||||
// accounts, created by hand via scripts/create-user.ts, no self-registration.
|
// accounts, created by hand via scripts/create-user.ts, no self-registration.
|
||||||
|
|||||||
Reference in New Issue
Block a user