From 03d74d69c19be4b8b9604478dc91d5e54f2f6758 Mon Sep 17 00:00:00 2001 From: toanhoang Date: Mon, 24 Aug 2026 21:57:33 -0400 Subject: [PATCH] Rename AssetItemStatus 'assigned' to 'in_use' 'assigned' implied a specific person always holds the item, but createAssignment() only requires status=in_stock to hand something out -- the status value itself never guaranteed a matching Assignment row exists. ~158 of the 613 real-imported assets carry this status with no Assignment at all (shared/unowned equipment like meeting-room gear), so "assigned" was actively misleading for them. 'in_use' doesn't carry that implication. DB: `ALTER TYPE "AssetItemStatus" RENAME VALUE 'assigned' TO 'in_use'` (hand-written migration -- runs cleanly, no data migration needed, every existing row's status just reads under the new name). Renamed across: schema.prisma enum, src/lib/assets.ts (type union + IMPORT_STATUS_MAP, which keeps an `assigned` key mapped to `in_use` for backward-compat with pre-2026-08-25 exports/CSVs), src/lib/assignments.ts (the literal status value createAssignment() sets), src/app/assets/page.tsx + api/assets/ route.ts (filter dropdown + validStatuses), src/app/maintenance-records/ page.tsx (its own copy of the status list for the "close" targetStatus picker). maintenance-records.ts itself needed no logic change -- its closeableTargetStatusValues was already just ["in_stock", "retired"], "assigned" was never a valid close target, only mentioned in comments (updated for accuracy). Also fixes a real regression found while verifying this: tests/ assets.test.ts (a pre-existing, properly-reviewed test file, not written this session) broke 2 commits ago when GET /api/assets's signature and response shape changed for pagination -- it still called GET() with no Request and expected a bare array. tsc didn't catch this until now because this container has no dev bind mount (Dockerfile is COPY-based, `docker exec` only reflects the last build) -- see the ubi-dan-itam-docker-no-bind-mount memory note. Added a getRequest() helper and updated assertions to the {items, total} shape. Verified by actually rebuilding and running the full suite fresh (not just tsc/lint smoke checks): 369/369 tests pass, including assignments.test.ts and maintenance-records.test.ts, which this rename touches directly. tsc/lint clean against the rebuilt image. Migration applied to the real DB, data verified intact (542 in_use + 71 in_stock = 613, matches pre-rename counts exactly). --- .../migration.sql | 2 ++ prisma/schema.prisma | 11 +++++--- src/app/api/assets/route.ts | 2 +- src/app/assets/page.tsx | 6 ++--- src/app/maintenance-records/page.tsx | 2 +- src/lib/assets.ts | 11 ++++---- src/lib/assignments.ts | 10 +++---- src/lib/maintenance-records.ts | 10 +++---- tests/assets.test.ts | 26 +++++++++++++------ tests/assignments.test.ts | 22 ++++++++-------- tests/maintenance-records.test.ts | 6 ++--- 11 files changed, 63 insertions(+), 45 deletions(-) create mode 100644 prisma/migrations/20260825084412_rename_asset_item_status_in_use/migration.sql diff --git a/prisma/migrations/20260825084412_rename_asset_item_status_in_use/migration.sql b/prisma/migrations/20260825084412_rename_asset_item_status_in_use/migration.sql new file mode 100644 index 0000000..5b0a50b --- /dev/null +++ b/prisma/migrations/20260825084412_rename_asset_item_status_in_use/migration.sql @@ -0,0 +1,2 @@ +-- Hand-written (same deviation as other 2026-08-2x migrations -- see CLAUDE.md). +ALTER TYPE "AssetItemStatus" RENAME VALUE 'assigned' TO 'in_use'; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 8ae421d..8083fa2 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -90,10 +90,15 @@ model Employee { // 2026-08-25 (ALTER TYPE ... ADD VALUE, not a table -- see the // 2026-08-24/25 discussion in CLAUDE.md on why this stays an enum: the // values are read directly by business logic, e.g. createAssignment()'s -// `status !== "in_stock"` gate, not just display labels). +// `status !== "in_stock"` gate, not just display labels). `assigned` +// renamed to `in_use` same day (ALTER TYPE ... RENAME VALUE) -- "assigned" +// implied a specific person always holds it, but createAssignment() only +// requires in_stock -> the enum value itself, and ~158 real-imported items +// have this status with no Assignment row at all (shared/unowned +// equipment) -- "in_use" doesn't carry that false implication. enum AssetItemStatus { in_stock - assigned + in_use repair retired lost @@ -157,7 +162,7 @@ model AssetItem { // Nullable: real-world coverage is thin (the ITAM export's own location // data was nearly useless -- "Physical location" was the literal string // "DAN" for all 613 rows, no per-item detail; only "Stockroom" carried - // any signal, and only for the ~71 in_stock items -- an assigned item is + // any signal, and only for the ~71 in_stock items -- an in_use item is // with a person, not sitting in a stockroom bin, so it has no area here // by design, not by data gap). areaId String? @map("area_id") @db.Uuid diff --git a/src/app/api/assets/route.ts b/src/app/api/assets/route.ts index c7e5b7b..60daf8d 100644 --- a/src/app/api/assets/route.ts +++ b/src/app/api/assets/route.ts @@ -86,7 +86,7 @@ export async function GET(request: Request): Promise { const areaId = params.get("areaId") ?? undefined; const search = params.get("search") ?? undefined; - const validStatuses = ["in_stock", "assigned", "repair", "retired", "lost"]; + const validStatuses = ["in_stock", "in_use", "repair", "retired", "lost"]; const result = await listAssetItems({ page, pageSize, diff --git a/src/app/assets/page.tsx b/src/app/assets/page.tsx index cd72828..2b251ef 100644 --- a/src/app/assets/page.tsx +++ b/src/app/assets/page.tsx @@ -30,7 +30,7 @@ interface AssetItemRecord { model_id: string; purchase_date: string | null; warranty_end_date: string | null; - status: "in_stock" | "assigned" | "repair" | "retired" | "lost"; + status: "in_stock" | "in_use" | "repair" | "retired" | "lost"; cost: number | null; cost_currency: string | null; area_id: string | null; @@ -467,7 +467,7 @@ export default function AssetsPage() { All in_stock - assigned + in_use repair retired lost @@ -623,7 +623,7 @@ export default function AssetsPage() { ), purchase date (không bắt buộc -- cũng nhận order received). Tuỳ chọn: asset tag, warranty end date (bỏ trống thì tự tính từ warranty của model), status/state (nhận{" "} - In use → assigned, In stock → in_stock, bỏ trống thì mặc định + In use → in_use, In stock → in_stock, bỏ trống thì mặc định in_stock), cost, cost currency,{" "} po number (text tự do, khác purchase order id là UUID trong hệ thống), area (tên khu vực -- tự tạo mới nếu chưa có, cũng nhận{" "} diff --git a/src/app/maintenance-records/page.tsx b/src/app/maintenance-records/page.tsx index 2c79e05..e2b04e8 100644 --- a/src/app/maintenance-records/page.tsx +++ b/src/app/maintenance-records/page.tsx @@ -58,7 +58,7 @@ interface VendorRecord { // assetItemStatusValues constant (not re-derived from an API call, this is // a closed set already known client-side, same precedent as licenses // page's licenseType/perpetual|subscription