feat: add MaintenanceRecord Prisma model + migration
New entity tracking repair/warranty/preventive maintenance jobs for an AssetItem. Reuses Vendor (nullable) for the repair location, no cost field (mirrors PurchaseOrder). "At most 1 open record per AssetItem" invariant left to the application layer, mirroring Assignment.
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "MaintenanceType" AS ENUM ('repair', 'warranty', 'preventive');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "maintenance_records" (
|
||||
"id" UUID NOT NULL,
|
||||
"asset_item_id" UUID NOT NULL,
|
||||
"type" "MaintenanceType" NOT NULL,
|
||||
"vendor_id" UUID,
|
||||
"issue_description" TEXT NOT NULL,
|
||||
"resolution_notes" TEXT,
|
||||
"start_date" DATE NOT NULL,
|
||||
"end_date" DATE,
|
||||
"created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "maintenance_records_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "maintenance_records" ADD CONSTRAINT "maintenance_records_asset_item_id_fkey" FOREIGN KEY ("asset_item_id") REFERENCES "asset_items"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "maintenance_records" ADD CONSTRAINT "maintenance_records_vendor_id_fkey" FOREIGN KEY ("vendor_id") REFERENCES "vendors"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
@@ -125,6 +125,11 @@ model AssetItem {
|
||||
// multiple Assignment rows over its lifetime (historical + at most one
|
||||
// active, see Assignment model's comment), not built until this task.
|
||||
assignments Assignment[]
|
||||
// Back-relation for MaintenanceRecord.assetItemId — an AssetItem can have
|
||||
// multiple MaintenanceRecord rows over its lifetime (historical + at most
|
||||
// one open, see MaintenanceRecord model's comment), not built until this
|
||||
// task.
|
||||
maintenanceRecords MaintenanceRecord[]
|
||||
|
||||
@@map("asset_items")
|
||||
}
|
||||
@@ -194,6 +199,10 @@ model Vendor {
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
||||
licenses License[]
|
||||
purchaseOrders PurchaseOrder[]
|
||||
// Back-relation for MaintenanceRecord.vendorId — a Vendor can perform
|
||||
// multiple repairs/warranty jobs over time, same reuse rationale as
|
||||
// PurchaseOrder.vendorId (see MaintenanceRecord model's comment).
|
||||
maintenanceRecords MaintenanceRecord[]
|
||||
|
||||
@@map("vendors")
|
||||
}
|
||||
@@ -417,3 +426,50 @@ model User {
|
||||
|
||||
@@map("users")
|
||||
}
|
||||
|
||||
// Fixed, closed set of 3 values tied to the domain (see docs/
|
||||
// asset-license-management.md's "## MaintenanceRecord (spec BA + stakeholder,
|
||||
// chốt 2026-08-04)" section) — native Postgres enum, same rationale as
|
||||
// RequestType/AssetItemStatus/LicenseType/AssetCategoryValueGroup/
|
||||
// AssetAttributeDataType above.
|
||||
enum MaintenanceType {
|
||||
repair
|
||||
warranty
|
||||
preventive
|
||||
}
|
||||
|
||||
// Records a repair/warranty/preventive-maintenance job for a specific
|
||||
// AssetItem — see docs/asset-license-management.md's "## MaintenanceRecord
|
||||
// (spec BA + stakeholder, chốt 2026-08-04)" section. Only applies to
|
||||
// AssetItem (hardware) — License has no physical-repair concept, so unlike
|
||||
// Assignment there's no XOR here: assetItemId is the single, always-required
|
||||
// target. `vendorId` reuses the existing Vendor entity (same "external
|
||||
// counterparty" concept as PurchaseOrder.vendorId) and is nullable — null
|
||||
// means an internal repair with no outside vendor involved. No `cost` field,
|
||||
// per the same precedent as PurchaseOrder (stakeholder explicitly said no
|
||||
// money tracking there). No separate `status` field either — "open" vs
|
||||
// "done" is derived from `endDate` (null = still being worked on, set = done),
|
||||
// same precedent as PurchaseOrder.receivedAt not duplicating state. `endDate
|
||||
// >= startDate` and "at most one OPEN (endDate IS NULL) MaintenanceRecord per
|
||||
// AssetItem" are both business invariants enforced at the application layer
|
||||
// (src/lib/maintenance-records.ts, not built in this task) — NOT DB
|
||||
// constraints, per the same convention as Assignment's XOR / "at most one
|
||||
// active" invariant (see Assignment model's comment above for the mirrored
|
||||
// trade-off). No FK to User for "who logged this" — no existing precedent in
|
||||
// this codebase ties a business entity to User (User is login-only), see
|
||||
// doc for this explicit call.
|
||||
model MaintenanceRecord {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
assetItemId String @map("asset_item_id") @db.Uuid
|
||||
assetItem AssetItem @relation(fields: [assetItemId], references: [id])
|
||||
type MaintenanceType
|
||||
vendorId String? @map("vendor_id") @db.Uuid
|
||||
vendor Vendor? @relation(fields: [vendorId], references: [id])
|
||||
issueDescription String @map("issue_description")
|
||||
resolutionNotes String? @map("resolution_notes")
|
||||
startDate DateTime @map("start_date") @db.Date
|
||||
endDate DateTime? @map("end_date") @db.Date
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
||||
|
||||
@@map("maintenance_records")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user