fix: retrofit License.vendor to vendorId FK + createLicense error handling
License.vendor (free-text string) is now vendorId, a required FK to
Vendor, per docs/asset-license-management.md's "## Vendor (chốt
2026-08-02)" spec. Mirrors the Request.employeeId string->FK precedent:
Zod validates UUID format only, Prisma's own FK constraint (P2003) is
caught at insert time and translated into a clean 400 instead of a raw
500. No License data existed yet, so this is a pure column-type
migration, no backfill needed.
Also converts createLicense() to the {ok,value}|{ok,error} result
pattern used elsewhere (createRequest, createAssetItem) instead of
returning a bare LicenseRecord promise - closes the gap flagged in
CLAUDE.md now that License has its first FK-violation error case to
handle.
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `vendor` on the `licenses` table. All the data in the column will be lost.
|
||||
- Added the required column `vendor_id` to the `licenses` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "licenses" DROP COLUMN "vendor",
|
||||
ADD COLUMN "vendor_id" UUID NOT NULL;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "licenses" ADD CONSTRAINT "licenses_vendor_id_fkey" FOREIGN KEY ("vendor_id") REFERENCES "vendors"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
+22
-18
@@ -102,17 +102,20 @@ enum LicenseType {
|
||||
|
||||
// Software license inventory — see docs/asset-license-management.md's "Data
|
||||
// model (sơ bộ)" table (tên, vendor, tổng seat, seat đã dùng, ngày hết hạn,
|
||||
// loại). `vendor` is deliberately a plain String for now, not a separate
|
||||
// Vendor entity — no Vendor entity exists yet, same "plain string for now"
|
||||
// precedent as Employee.department. `seatsUsed` is a stored counter
|
||||
// (defaults to 0 at creation), not a computed value — it will only ever be
|
||||
// set to 0 here since Assignment (the thing that would consume seats)
|
||||
// doesn't exist yet. `expiryDate` is nullable: perpetual licenses never
|
||||
// expire, only subscription licenses usually have one.
|
||||
// loại). `vendorId` was a free-text `vendor` String until the "## Vendor
|
||||
// (chốt 2026-08-02)" retrofit — same precedent as Request.employeeId
|
||||
// (string -> FK once Employee existed). No production `License` data
|
||||
// existed at the time of this change, so this was a plain column-type
|
||||
// migration, not a backfill. `seatsUsed` is a stored counter (defaults to 0
|
||||
// at creation), not a computed value — it will only ever be set to 0 here
|
||||
// since Assignment (the thing that would consume seats) doesn't exist yet.
|
||||
// `expiryDate` is nullable: perpetual licenses never expire, only
|
||||
// subscription licenses usually have one.
|
||||
model License {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
name String
|
||||
vendor String
|
||||
vendorId String @map("vendor_id") @db.Uuid
|
||||
vendor Vendor @relation(fields: [vendorId], references: [id])
|
||||
seatsTotal Int @map("seats_total")
|
||||
seatsUsed Int @default(0) @map("seats_used")
|
||||
expiryDate DateTime? @map("expiry_date") @db.Date
|
||||
@@ -126,20 +129,21 @@ model License {
|
||||
// (chốt 2026-08-02)". Deliberately independent of AssetModel.manufacturer
|
||||
// (OEM, e.g. "Dell") — Vendor is the reseller/distributor actually bought
|
||||
// from (e.g. "FPT Trading"), a different concept that isn't always the same
|
||||
// company. No FKs on this entity yet: it's the starting point for later
|
||||
// FKs from PurchaseOrder/License (License.vendor string -> License.vendorId
|
||||
// FK retrofit is noted in the doc as a separate follow-up, not done here).
|
||||
// taxCode/notes are nullable — not every vendor record has a known VN tax
|
||||
// code or extra notes at creation time.
|
||||
// company. `licenses` is the back-relation from the License.vendor ->
|
||||
// License.vendorId retrofit (same doc section) — PurchaseOrder is a future
|
||||
// FK from this entity, not built yet. taxCode/notes are nullable — not
|
||||
// every vendor record has a known VN tax code or extra notes at creation
|
||||
// time.
|
||||
model Vendor {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
name String
|
||||
contactEmail String @map("contact_email")
|
||||
contactPhone String @map("contact_phone")
|
||||
contactEmail String @map("contact_email")
|
||||
contactPhone String @map("contact_phone")
|
||||
address String
|
||||
taxCode String? @map("tax_code")
|
||||
taxCode String? @map("tax_code")
|
||||
notes String?
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
||||
licenses License[]
|
||||
|
||||
@@map("vendors")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user