feat(P18B-1,P18B-2): 新增资产流转模型与受控接口
这个提交包含在:
@@ -0,0 +1,113 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "InventoryCheckScopeType" AS ENUM ('all', 'legal_entity', 'cost_center', 'location');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "InventoryCheckStatus" AS ENUM ('draft', 'counting', 'completed');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "InventoryCheckResult" AS ENUM ('matched', 'missing', 'extra', 'damaged');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "asset_assignments" (
|
||||
"id" UUID NOT NULL,
|
||||
"asset_id" UUID NOT NULL,
|
||||
"employee_id" UUID NOT NULL,
|
||||
"assigned_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"returned_at" TIMESTAMP(3),
|
||||
"note" TEXT,
|
||||
|
||||
CONSTRAINT "asset_assignments_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "asset_transfers" (
|
||||
"id" UUID NOT NULL,
|
||||
"asset_id" UUID NOT NULL,
|
||||
"from_employee_id" UUID,
|
||||
"to_employee_id" UUID,
|
||||
"from_location" TEXT,
|
||||
"to_location" TEXT,
|
||||
"transferred_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"note" TEXT,
|
||||
|
||||
CONSTRAINT "asset_transfers_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "inventory_checks" (
|
||||
"id" UUID NOT NULL,
|
||||
"check_no" TEXT NOT NULL,
|
||||
"scope_type" "InventoryCheckScopeType" NOT NULL,
|
||||
"scope_ref_id" VARCHAR(200),
|
||||
"status" "InventoryCheckStatus" NOT NULL DEFAULT 'draft',
|
||||
"started_at" TIMESTAMP(3),
|
||||
"completed_at" TIMESTAMP(3),
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"created_by" UUID,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
"updated_by" UUID,
|
||||
|
||||
CONSTRAINT "inventory_checks_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "inventory_check_items" (
|
||||
"id" UUID NOT NULL,
|
||||
"inventory_check_id" UUID NOT NULL,
|
||||
"asset_id" UUID NOT NULL,
|
||||
"result" "InventoryCheckResult" NOT NULL,
|
||||
"note" TEXT,
|
||||
|
||||
CONSTRAINT "inventory_check_items_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_asset_assignments_asset_open" ON "asset_assignments"("asset_id", "returned_at");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_asset_assignments_employee_open" ON "asset_assignments"("employee_id", "returned_at");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_asset_transfers_asset_time" ON "asset_transfers"("asset_id", "transferred_at");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_asset_transfers_from_employee" ON "asset_transfers"("from_employee_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_asset_transfers_to_employee" ON "asset_transfers"("to_employee_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "inventory_checks_check_no_key" ON "inventory_checks"("check_no");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_inventory_checks_status_created" ON "inventory_checks"("status", "created_at");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_inventory_checks_scope" ON "inventory_checks"("scope_type", "scope_ref_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_inventory_check_items_asset" ON "inventory_check_items"("asset_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "inventory_check_asset_ux" ON "inventory_check_items"("inventory_check_id", "asset_id");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "asset_assignments" ADD CONSTRAINT "asset_assignments_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "asset_cards"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "asset_assignments" ADD CONSTRAINT "asset_assignments_employee_id_fkey" FOREIGN KEY ("employee_id") REFERENCES "employees"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "asset_transfers" ADD CONSTRAINT "asset_transfers_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "asset_cards"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "asset_transfers" ADD CONSTRAINT "asset_transfers_from_employee_id_fkey" FOREIGN KEY ("from_employee_id") REFERENCES "employees"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "asset_transfers" ADD CONSTRAINT "asset_transfers_to_employee_id_fkey" FOREIGN KEY ("to_employee_id") REFERENCES "employees"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "inventory_check_items" ADD CONSTRAINT "inventory_check_items_inventory_check_id_fkey" FOREIGN KEY ("inventory_check_id") REFERENCES "inventory_checks"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "inventory_check_items" ADD CONSTRAINT "inventory_check_items_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "asset_cards"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
@@ -60,6 +60,26 @@ enum AssetCardStatus {
|
||||
archived
|
||||
}
|
||||
|
||||
enum InventoryCheckScopeType {
|
||||
all
|
||||
legal_entity
|
||||
cost_center
|
||||
location
|
||||
}
|
||||
|
||||
enum InventoryCheckStatus {
|
||||
draft
|
||||
counting
|
||||
completed
|
||||
}
|
||||
|
||||
enum InventoryCheckResult {
|
||||
matched
|
||||
missing
|
||||
extra
|
||||
damaged
|
||||
}
|
||||
|
||||
enum LeaveDaySession {
|
||||
am
|
||||
pm
|
||||
@@ -242,6 +262,9 @@ model Employee {
|
||||
purchase_attachments_uploaded PurchaseAttachment[] @relation("PurchaseAttachmentUploader")
|
||||
purchase_order_archives PurchaseOrderArchive[] @relation("PurchaseOrderArchiveArchivedBy")
|
||||
assigned_asset_cards AssetCard[] @relation("AssetCardAssignee")
|
||||
asset_assignments AssetAssignment[]
|
||||
asset_transfers_from AssetTransfer[] @relation("AssetTransferFromEmployee")
|
||||
asset_transfers_to AssetTransfer[] @relation("AssetTransferToEmployee")
|
||||
|
||||
owned_invoices InvoicePool[] @relation("InvoiceOwner")
|
||||
uploaded_invoices InvoicePool[] @relation("InvoiceUploader")
|
||||
@@ -715,6 +738,9 @@ model AssetCard {
|
||||
cost_center CostCenter @relation(fields: [cost_center_id], references: [id], onDelete: Restrict)
|
||||
purchase_request PurchaseRequest? @relation(fields: [purchase_request_id], references: [id], onDelete: SetNull)
|
||||
assignee Employee? @relation("AssetCardAssignee", fields: [assignee_employee_id], references: [id], onDelete: SetNull)
|
||||
assignments AssetAssignment[]
|
||||
transfers AssetTransfer[]
|
||||
inventory_items InventoryCheckItem[]
|
||||
|
||||
@@index([category_id], map: "idx_asset_cards_category")
|
||||
@@index([legal_entity_id], map: "idx_asset_cards_legal_entity")
|
||||
@@ -725,6 +751,78 @@ model AssetCard {
|
||||
@@map("asset_cards")
|
||||
}
|
||||
|
||||
model AssetAssignment {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
asset_id String @db.Uuid
|
||||
employee_id String @db.Uuid
|
||||
assigned_at DateTime @default(now())
|
||||
returned_at DateTime?
|
||||
note String?
|
||||
|
||||
asset AssetCard @relation(fields: [asset_id], references: [id], onDelete: Cascade)
|
||||
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Restrict)
|
||||
|
||||
@@index([asset_id, returned_at], map: "idx_asset_assignments_asset_open")
|
||||
@@index([employee_id, returned_at], map: "idx_asset_assignments_employee_open")
|
||||
@@map("asset_assignments")
|
||||
}
|
||||
|
||||
model AssetTransfer {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
asset_id String @db.Uuid
|
||||
from_employee_id String? @db.Uuid
|
||||
to_employee_id String? @db.Uuid
|
||||
from_location String?
|
||||
to_location String?
|
||||
transferred_at DateTime @default(now())
|
||||
note String?
|
||||
|
||||
asset AssetCard @relation(fields: [asset_id], references: [id], onDelete: Cascade)
|
||||
from_employee Employee? @relation("AssetTransferFromEmployee", fields: [from_employee_id], references: [id], onDelete: SetNull)
|
||||
to_employee Employee? @relation("AssetTransferToEmployee", fields: [to_employee_id], references: [id], onDelete: SetNull)
|
||||
|
||||
@@index([asset_id, transferred_at], map: "idx_asset_transfers_asset_time")
|
||||
@@index([from_employee_id], map: "idx_asset_transfers_from_employee")
|
||||
@@index([to_employee_id], map: "idx_asset_transfers_to_employee")
|
||||
@@map("asset_transfers")
|
||||
}
|
||||
|
||||
model InventoryCheck {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
check_no String @unique
|
||||
scope_type InventoryCheckScopeType
|
||||
scope_ref_id String? @db.VarChar(200)
|
||||
status InventoryCheckStatus @default(draft)
|
||||
started_at DateTime?
|
||||
completed_at DateTime?
|
||||
|
||||
created_at DateTime @default(now())
|
||||
created_by String? @db.Uuid
|
||||
updated_at DateTime @updatedAt
|
||||
updated_by String? @db.Uuid
|
||||
|
||||
items InventoryCheckItem[]
|
||||
|
||||
@@index([status, created_at], map: "idx_inventory_checks_status_created")
|
||||
@@index([scope_type, scope_ref_id], map: "idx_inventory_checks_scope")
|
||||
@@map("inventory_checks")
|
||||
}
|
||||
|
||||
model InventoryCheckItem {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
inventory_check_id String @db.Uuid
|
||||
asset_id String @db.Uuid
|
||||
result InventoryCheckResult
|
||||
note String?
|
||||
|
||||
inventory_check InventoryCheck @relation(fields: [inventory_check_id], references: [id], onDelete: Cascade)
|
||||
asset AssetCard @relation(fields: [asset_id], references: [id], onDelete: Restrict)
|
||||
|
||||
@@unique([inventory_check_id, asset_id], map: "inventory_check_asset_ux")
|
||||
@@index([asset_id], map: "idx_inventory_check_items_asset")
|
||||
@@map("inventory_check_items")
|
||||
}
|
||||
|
||||
model ReimbursementForm {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
form_number String? @unique
|
||||
|
||||
在新工单中引用
屏蔽一个用户