feat: add PurchaseOrder Prisma model + migration
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "purchase_orders" (
|
||||
"id" UUID NOT NULL,
|
||||
"vendor_id" UUID NOT NULL,
|
||||
"order_date" TIMESTAMPTZ(6) NOT NULL,
|
||||
"expected_date" TIMESTAMPTZ(6) NOT NULL,
|
||||
"received_at" TIMESTAMPTZ(6),
|
||||
"received_by" UUID,
|
||||
"created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "purchase_orders_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "purchase_orders" ADD CONSTRAINT "purchase_orders_vendor_id_fkey" FOREIGN KEY ("vendor_id") REFERENCES "vendors"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "purchase_orders" ADD CONSTRAINT "purchase_orders_received_by_fkey" FOREIGN KEY ("received_by") REFERENCES "employees"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
+56
-21
@@ -51,14 +51,17 @@ model Request {
|
||||
// possible future normalization, not built yet. `managerId` is a nullable
|
||||
// self-relation: top-level employees (no manager) have `managerId = null`.
|
||||
model Employee {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
name String
|
||||
department String
|
||||
managerId String? @map("manager_id") @db.Uuid
|
||||
manager Employee? @relation("EmployeeManager", fields: [managerId], references: [id])
|
||||
reports Employee[] @relation("EmployeeManager")
|
||||
requests Request[]
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
name String
|
||||
department String
|
||||
managerId String? @map("manager_id") @db.Uuid
|
||||
manager Employee? @relation("EmployeeManager", fields: [managerId], references: [id])
|
||||
reports Employee[] @relation("EmployeeManager")
|
||||
requests Request[]
|
||||
// Back-relation for PurchaseOrder.receivedById — an Employee can be the
|
||||
// receiver on multiple POs over time.
|
||||
purchaseOrdersReceived PurchaseOrder[]
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
||||
|
||||
@@map("employees")
|
||||
}
|
||||
@@ -130,24 +133,56 @@ model License {
|
||||
// (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. `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.
|
||||
// License.vendorId retrofit (same doc section). `purchaseOrders` is the
|
||||
// back-relation from PurchaseOrder.vendorId (see "## PurchaseOrder (gộp
|
||||
// Receipt) (chốt 2026-08-02)"). 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
|
||||
name String
|
||||
contactEmail String @map("contact_email")
|
||||
contactPhone String @map("contact_phone")
|
||||
address String
|
||||
taxCode String? @map("tax_code")
|
||||
notes String?
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
||||
licenses License[]
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
name String
|
||||
contactEmail String @map("contact_email")
|
||||
contactPhone String @map("contact_phone")
|
||||
address String
|
||||
taxCode String? @map("tax_code")
|
||||
notes String?
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
||||
licenses License[]
|
||||
purchaseOrders PurchaseOrder[]
|
||||
|
||||
@@map("vendors")
|
||||
}
|
||||
|
||||
// Purchasing document — see docs/asset-license-management.md's "##
|
||||
// PurchaseOrder (gộp Receipt) (chốt 2026-08-02)". Deliberately absorbs what
|
||||
// would otherwise be a separate `Receipt` entity: receiving is just part of
|
||||
// this row's own state (`receivedAt`/`receivedBy`), not a separate table to
|
||||
// reconcile against. No `status` field — status is inferred from linked
|
||||
// `Request`s (Ordered -> Received -> Delivered), not stored here, to avoid
|
||||
// two sources of truth. No cost/price fields — stakeholder said explicitly
|
||||
// PurchaseOrder doesn't need to track money, only "which asset/license
|
||||
// belongs to which PO". Receiving is all-or-nothing per PO (no partial
|
||||
// receipt tracking) — a deliberate simplification, see doc for the
|
||||
// stakeholder-accepted tradeoff. `receivedAt`/`receivedBy` are both
|
||||
// nullable and must be co-null/co-set together (both-null = not yet
|
||||
// received) — enforced at the Zod layer (src/lib/purchase-orders.ts), not a
|
||||
// DB constraint, same approach as other cross-field invariants in this
|
||||
// codebase (e.g. Assignment's assetItemId/licenseId XOR, not built yet).
|
||||
// purchaseOrderId back-references on AssetItem/License/Request (traceability,
|
||||
// same doc section) are NOT part of this change — out of scope for this task.
|
||||
model PurchaseOrder {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
vendorId String @map("vendor_id") @db.Uuid
|
||||
vendor Vendor @relation(fields: [vendorId], references: [id])
|
||||
orderDate DateTime @map("order_date") @db.Timestamptz(6)
|
||||
expectedDate DateTime @map("expected_date") @db.Timestamptz(6)
|
||||
receivedAt DateTime? @map("received_at") @db.Timestamptz(6)
|
||||
receivedById String? @map("received_by") @db.Uuid
|
||||
receivedBy Employee? @relation(fields: [receivedById], references: [id])
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
||||
|
||||
@@map("purchase_orders")
|
||||
}
|
||||
|
||||
// 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