文件
hyb-oa/server/prisma/schema.prisma
T

2645 行
97 KiB
Plaintext

generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
}
enum RoleCode {
employee
manager
ceo
finance
finance_mgr
administration
hr
admin
}
enum EmployeeStatus {
active
archived
}
enum DelegationGrantScope {
reimbursement_submit
all
}
enum ReimbursementStatus {
draft
reviewing
approved
paid
rejected
cancelled
}
enum LeaveRequestStatus {
draft
reviewing
approved
rejected
cancelled
}
enum PurchaseRequestStatus {
draft
reviewing
approved
rejected
cancelled
}
enum TravelRequestStatus {
draft
reviewing
approved
rejected
}
enum TravelTripType {
domestic
international
}
enum TravelSegmentRole {
outbound
return
connection
}
enum TravelTransportType {
flight
train
car
bus
other
}
enum TravelReconciliationBatchStatus {
draft
reconciled
}
enum TravelReconciliationItemKind {
order
orphan_transaction
}
enum TravelReconciliationMatchStatus {
matched
amount_mismatch
currency_mismatch
missing_transaction
duplicate_charge
orphan_transaction
reversed_after_payment
}
enum TravelReconciliationResolutionStatus {
pending
confirmed
}
enum AssetCardStatus {
in_stock
in_use
under_repair
retired
archived
}
enum InventoryCheckScopeType {
all
legal_entity
cost_center
location
}
enum InventoryCheckStatus {
draft
counting
completed
}
enum InventoryCheckResult {
matched
missing
extra
damaged
}
enum LeaveDaySession {
am
pm
}
enum LeaveLedgerEntryType {
grant
reserve
consume
release
adjust
}
enum InvoicePoolStatus {
pending_ocr
ocr_failed
available
bound
archived
}
enum InvoiceArchivedReason {
used_complete
unused_timeout
manual
}
enum OcrProvider {
tencent
}
enum OcrType {
vat
train
flight
taxi
general
}
enum DocumentResetPeriod {
none
daily
monthly
yearly
}
enum DocumentCategoryEntityType {
reimbursement
leave
purchase_request
travel_request
travel_reconciliation_batch
asset_card
general
}
enum HolidayCalendarDayType {
holiday
workday
adjusted_workday
}
enum WorkflowInstanceStatus {
pending
approved
rejected
cancelled
terminated
}
enum WorkflowTaskStatus {
pending
completed
rejected
cancelled
skipped
}
enum WorkflowTaskOutcome {
approved
rejected
returned
start
transfer
skipped
}
enum ApprovalTransferStatus {
active
cancelled
}
enum ExpenseCategory {
travel
rd
production
office
business
assets
other
}
enum BudgetControlMode {
warn
block
manual_review
}
enum RiskFlagStatus {
open
resolved
dismissed
}
enum PaymentBatchStatus {
draft
reviewed
executed
failed
cancelled
}
enum PaymentBatchEntityType {
reimbursement
travel_order
}
enum BudgetControlRuleEntityType {
all
reimbursement
purchase_request
travel_request
}
enum WorkspaceProfile {
small_team_internal
}
enum WorkspaceOrgMode {
single_company
}
enum WorkspacePrimaryEntryMode {
password
wecom
}
enum WorkspaceNotificationStrategy {
in_app
}
enum WorkspaceDevicePriority {
desktop
}
enum EmploymentLifecycleStatus {
onboarding
probation
active
offboarding
inactive
}
enum EmployeeLifecycleEventType {
onboard
regularize
transfer
offboard
}
enum EmployeeChecklistScene {
onboarding
offboarding
transfer
}
enum EmployeeChecklistTaskType {
account_setup
asset_issue
policy_ack
asset_return
access_revoke
expense_clearance
handover_note
}
enum EmployeeChecklistTaskStatus {
pending
completed
skipped
}
enum EmployeePresenceStatus {
office
remote
out
travel
leave
}
enum EmployeePresenceTimeRange {
full_day
morning
afternoon
}
enum EmployeePresenceSource {
manual
leave_sync
admin_override
}
enum EmployeeContractType {
labor
nda
non_compete
internship
}
enum EmployeeTrainingType {
internal
external
online
}
enum EmployeeTrainingStatus {
planned
in_progress
completed
cancelled
}
enum WeeklyReportStatus {
draft
submitted
}
enum TodoPriority {
low
normal
high
}
enum TodoStatus {
todo
doing
done
cancelled
}
enum TodoSource {
manual
weekly_report
}
enum ProjectStatus {
draft
active
paused
completed
archived
}
enum ProjectRole {
owner
manager
member
observer
}
enum ProjectMilestoneStatus {
pending
in_progress
done
delayed
cancelled
}
enum ProjectTaskStatus {
todo
doing
blocked
done
cancelled
}
enum ProjectTaskPriority {
low
normal
high
urgent
}
enum ProjectRiskLevel {
low
medium
high
}
enum ProjectDeliveryRecordType {
kickoff
onsite
stage_acceptance
final_acceptance
handover
}
enum ProjectCommunicationChannel {
wechat
phone
meeting
wecom
email
onsite
other
}
enum ProjectClosureChecklistStatus {
pending
done
waived
}
enum ConfidentialityLevel {
public
internal
confidential
restricted
}
enum DocumentStorageType {
oa_link
nas
internal_share
offline_archive
}
model Department {
id String @id @default(uuid()) @db.Uuid
name String
parent_id String? @db.Uuid
archived_at DateTime?
archived_by String? @db.Uuid
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
parent Department? @relation("DepartmentToParent", fields: [parent_id], references: [id], onDelete: Restrict)
children Department[] @relation("DepartmentToParent")
employees Employee[]
reimbursement_forms ReimbursementForm[]
leave_requests LeaveRequest[]
purchase_requests PurchaseRequest[]
travel_requests TravelRequest[]
workflow_instances WorkflowInstance[]
budget_pools BudgetPool[]
travel_orders TravelOrder[]
card_authorizations CardAuthorization[]
card_transactions CardTransaction[]
@@index([parent_id])
@@map("departments")
}
model Employee {
id String @id @default(uuid()) @db.Uuid
employee_no String @unique
name String
phone String @unique
email String @unique
department_id String @db.Uuid
legal_entity_id String? @db.Uuid
title String
direct_manager_id String? @db.Uuid
hire_date DateTime @db.Date
status EmployeeStatus @default(active)
employment_status EmploymentLifecycleStatus @default(active)
probation_end_date DateTime? @db.Date
last_working_date DateTime? @db.Date
work_location String?
emergency_contact_name String?
emergency_contact_phone String?
notes String?
avatar_path String?
bank_account_encrypted String
bank_name String
id_number_encrypted String?
password_hash String
token_version Int @default(0)
must_change_password Boolean @default(false)
login_failed_count Int @default(0)
login_failed_first_at DateTime?
login_locked_until DateTime?
archived_at DateTime?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
department Department @relation(fields: [department_id], references: [id], onDelete: Restrict)
legal_entity LegalEntity? @relation(fields: [legal_entity_id], references: [id], onDelete: SetNull)
direct_manager Employee? @relation("EmployeeToManager", fields: [direct_manager_id], references: [id], onDelete: Restrict)
subordinates Employee[] @relation("EmployeeToManager")
roles EmployeeRole[]
delegation_grants_as_principal DelegationGrant[] @relation("DelegationPrincipal")
delegation_grants_as_delegate DelegationGrant[] @relation("DelegationDelegate")
auth_sessions AuthSession[]
audit_logs AuditLog[] @relation("AuditActor")
notifications Notification[]
submitted_reimbursements ReimbursementForm[] @relation("ReimbursementSubmitter")
owned_reimbursements ReimbursementForm[] @relation("ReimbursementOwner")
paid_reimbursements ReimbursementForm[] @relation("ReimbursementPaidBy")
leave_requests LeaveRequest[]
leave_ledgers LeaveLedger[]
owned_cost_centers CostCenter[] @relation("CostCenterOwner")
purchase_requests_applicant PurchaseRequest[] @relation("PurchaseRequestApplicant")
travel_requests_applicant TravelRequest[] @relation("TravelRequestApplicant")
travel_requests_submitted TravelRequest[] @relation("TravelRequestSubmitter")
purchase_requests_submitted PurchaseRequest[] @relation("PurchaseRequestSubmitter")
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")
overridden_invoices InvoicePool[] @relation("InvoiceOverrideBy")
requested_invoice_verifications InvoiceVerificationRecord[] @relation("InvoiceVerificationRequester")
travel_orders TravelOrder[] @relation("TravelOrderEmployee")
paid_travel_orders TravelOrder[] @relation("TravelOrderPaidBy")
confirmed_travel_reconciliation_batches TravelReconciliationBatch[] @relation("TravelReconciliationBatchConfirmedBy")
card_authorizations CardAuthorization[]
card_transactions CardTransaction[] @relation("CardTransactionEmployee")
freeze_suggested_card_transactions CardTransaction[] @relation("CardTransactionFreezeSuggestedBy")
confirmed_travel_reconciliation_items TravelReconciliationItem[] @relation("TravelReconciliationItemConfirmedBy")
workflow_instances_started WorkflowInstance[] @relation("WorkflowInstanceSubmitter")
workflow_history_actions WorkflowHistory[] @relation("WorkflowHistoryActor")
workflow_tasks_assigned WorkflowTask[] @relation("WorkflowTaskAssignee")
workflow_tasks_completed WorkflowTask[] @relation("WorkflowTaskCompleter")
approval_transfers_from ApprovalTransfer[] @relation("TransferFrom")
approval_transfers_to ApprovalTransfer[] @relation("TransferTo")
password_reset_codes PasswordResetCode[]
lifecycle_events EmployeeLifecycleEvent[]
checklist_tasks EmployeeChecklistTask[]
presence_records EmployeePresence[]
weekly_reports WeeklyReport[]
todo_items_assigned TodoItem[] @relation("TodoAssignee")
todo_items_created TodoItem[] @relation("TodoCreator")
wecom_identity_binding WecomIdentityBinding?
owned_projects Project[] @relation("ProjectOwner")
managed_projects Project[] @relation("ProjectManager")
project_memberships ProjectMember[]
owned_project_milestones ProjectMilestone[] @relation("ProjectMilestoneOwner")
assigned_project_tasks ProjectTask[] @relation("ProjectTaskAssignee")
created_project_tasks ProjectTask[] @relation("ProjectTaskCreator")
owned_project_risks ProjectRisk[] @relation("ProjectRiskOwner")
owned_project_delivery_records ProjectDeliveryRecord[] @relation("ProjectDeliveryRecordOwner")
created_project_communication_logs ProjectCommunicationLog[] @relation("ProjectCommunicationLogCreator")
completed_project_closure_checklists ProjectClosureChecklistItem[] @relation("ProjectClosureChecklistItemCompleter")
owned_contract_registries ContractRegistry[] @relation("ContractRegistryOwner")
owned_confidential_document_refs ConfidentialDocumentRef[] @relation("ConfidentialDocumentRefOwner")
contracts EmployeeContract[]
trainings EmployeeTraining[]
certificates EmployeeCertificate[]
@@index([department_id])
@@index([legal_entity_id], map: "idx_employees_legal_entity")
@@index([direct_manager_id])
@@map("employees")
}
model PasswordResetCode {
id String @id @default(uuid()) @db.Uuid
employee_id String @db.Uuid
code_hash String
expires_at DateTime
used_at DateTime?
created_at DateTime @default(now())
created_ip String?
created_user_agent String?
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade)
@@index([employee_id, created_at])
@@index([expires_at])
@@map("password_reset_codes")
}
model EmployeeRole {
employee_id String @db.Uuid
role_code RoleCode
created_at DateTime @default(now())
created_by String? @db.Uuid
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade)
@@id([employee_id, role_code])
@@map("employee_roles")
}
model DelegationGrant {
id String @id @default(uuid()) @db.Uuid
principal_employee_id String @db.Uuid
delegate_employee_id String @db.Uuid
scope DelegationGrantScope
valid_from DateTime @default(now())
valid_to DateTime?
revoked_at DateTime?
created_at DateTime @default(now())
created_by String? @db.Uuid
principal_employee Employee @relation("DelegationPrincipal", fields: [principal_employee_id], references: [id], onDelete: Restrict)
delegate_employee Employee @relation("DelegationDelegate", fields: [delegate_employee_id], references: [id], onDelete: Restrict)
@@index([principal_employee_id])
@@index([delegate_employee_id])
@@map("delegation_grants")
}
model AuthSession {
id String @id @default(uuid()) @db.Uuid
employee_id String @db.Uuid
refresh_token_hash String
device_fingerprint String
ip_address String
user_agent String
created_at DateTime @default(now())
last_seen_at DateTime @default(now())
expires_at DateTime
revoked_at DateTime?
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade)
@@index([employee_id])
@@index([expires_at])
@@index([revoked_at])
@@map("auth_sessions")
}
model AuditLog {
id String @id @default(uuid()) @db.Uuid
request_id String
actor_id String @db.Uuid
action String
target_type String
target_id String? @db.Uuid
ip_address String?
user_agent String?
extra Json
created_at DateTime @default(now())
actor Employee @relation("AuditActor", fields: [actor_id], references: [id], onDelete: Restrict)
@@index([request_id])
@@index([actor_id, created_at])
@@index([created_at, actor_id, action], map: "idx_audit_logs_query")
@@index([target_type, created_at], map: "idx_audit_logs_target_type_created_at")
@@map("audit_logs")
}
model Notification {
id String @id @default(uuid()) @db.Uuid
recipient_employee_id String @db.Uuid
type String @db.VarChar(64)
title String @db.VarChar(200)
body String?
target_type String? @db.VarChar(64)
target_id String? @db.Uuid
read_at DateTime?
created_at DateTime @default(now())
recipient Employee @relation(fields: [recipient_employee_id], references: [id], onDelete: Cascade)
@@index([recipient_employee_id, created_at])
@@index([recipient_employee_id, read_at, created_at], map: "idx_notifications_unread_query")
@@map("notifications")
}
model ExpenseType {
id String @id @default(uuid()) @db.Uuid
code String @unique
name String
description String?
category ExpenseCategory
sort_order Int
account_code_default String?
is_system_default Boolean @default(false)
enabled Boolean @default(true)
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
reimbursement_items ReimbursementItem[]
@@index([enabled, sort_order])
@@map("expense_types")
}
model DocumentSequence {
document_type String @id
prefix String
date_format String
separator String
number_length Int @default(4)
reset_period DocumentResetPeriod
current_number Int @default(0)
last_reset_date DateTime?
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
@@map("document_sequences")
}
model LegalEntity {
id String @id @default(uuid()) @db.Uuid
code String @unique
name String
short_name String?
currency_code String @default("CNY") @db.VarChar(8)
tax_no String?
default_holiday_calendar_id String? @db.Uuid
enabled Boolean @default(true)
archived_at DateTime?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
default_holiday_calendar HolidayCalendar? @relation("LegalEntityDefaultHolidayCalendar", fields: [default_holiday_calendar_id], references: [id], onDelete: SetNull)
cost_centers CostCenter[]
employees Employee[]
holiday_calendars HolidayCalendar[] @relation("HolidayCalendarLegalEntity")
suppliers Supplier[]
purchase_requests PurchaseRequest[]
travel_requests TravelRequest[]
travel_reconciliation_batches TravelReconciliationBatch[]
card_authorizations CardAuthorization[]
asset_cards AssetCard[]
budget_pools BudgetPool[] @relation("BudgetPoolLegalEntity")
@@index([enabled, archived_at], map: "idx_legal_entities_status")
@@index([default_holiday_calendar_id], map: "idx_legal_entities_default_calendar")
@@map("legal_entities")
}
model CostCenter {
id String @id @default(uuid()) @db.Uuid
legal_entity_id String @db.Uuid
code String
name String
parent_id String? @db.Uuid
owner_employee_id String? @db.Uuid
enabled Boolean @default(true)
archived_at DateTime?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
legal_entity LegalEntity @relation(fields: [legal_entity_id], references: [id], onDelete: Restrict)
parent CostCenter? @relation("CostCenterToParent", fields: [parent_id], references: [id], onDelete: Restrict)
children CostCenter[] @relation("CostCenterToParent")
owner_employee Employee? @relation("CostCenterOwner", fields: [owner_employee_id], references: [id], onDelete: SetNull)
budget_pools BudgetPool[] @relation("BudgetPoolCostCenter")
purchase_requests PurchaseRequest[]
travel_requests TravelRequest[]
travel_reconciliation_batches TravelReconciliationBatch[]
card_authorizations CardAuthorization[]
asset_cards AssetCard[]
@@unique([legal_entity_id, code], map: "legal_entity_cost_center_code_ux")
@@index([legal_entity_id, enabled, archived_at], map: "idx_cost_centers_legal_entity_status")
@@index([parent_id], map: "idx_cost_centers_parent")
@@index([owner_employee_id], map: "idx_cost_centers_owner")
@@map("cost_centers")
}
model DocumentCategory {
id String @id @default(uuid()) @db.Uuid
code String @unique
name String
entity_type DocumentCategoryEntityType
sequence_prefix String
number_length Int @default(4)
reset_period DocumentResetPeriod
default_workflow_key String?
enabled Boolean @default(true)
archived_at DateTime?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
purchase_requests PurchaseRequest[]
asset_categories AssetCategory[]
@@index([entity_type, enabled, archived_at], map: "idx_document_categories_entity_status")
@@map("document_categories")
}
model HolidayCalendar {
id String @id @default(uuid()) @db.Uuid
code String @unique
name String
timezone String @default("Asia/Shanghai") @db.VarChar(64)
legal_entity_id String? @db.Uuid
enabled Boolean @default(true)
archived_at DateTime?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
legal_entity LegalEntity? @relation("HolidayCalendarLegalEntity", fields: [legal_entity_id], references: [id], onDelete: SetNull)
days HolidayCalendarDay[]
default_for_legal_entities LegalEntity[] @relation("LegalEntityDefaultHolidayCalendar")
@@index([legal_entity_id, enabled, archived_at], map: "idx_holiday_calendars_legal_entity_status")
@@map("holiday_calendars")
}
model HolidayCalendarDay {
id String @id @default(uuid()) @db.Uuid
calendar_id String @db.Uuid
date DateTime @db.Date
day_type HolidayCalendarDayType
name String
note String?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
calendar HolidayCalendar @relation(fields: [calendar_id], references: [id], onDelete: Cascade)
@@unique([calendar_id, date], map: "holiday_calendar_day_ux")
@@index([calendar_id, date], map: "idx_holiday_calendar_days_calendar_date")
@@map("holiday_calendar_days")
}
model Supplier {
id String @id @default(uuid()) @db.Uuid
code String @unique
name String
short_name String?
tax_no String?
contact_name String?
contact_phone String?
payment_terms String?
legal_entity_id String? @db.Uuid
enabled Boolean @default(true)
archived_at DateTime?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
legal_entity LegalEntity? @relation(fields: [legal_entity_id], references: [id], onDelete: SetNull)
purchase_requests PurchaseRequest[]
@@index([legal_entity_id, enabled, archived_at], map: "idx_suppliers_legal_entity_status")
@@index([name], map: "idx_suppliers_name")
@@map("suppliers")
}
model PurchaseRequest {
id String @id @default(uuid()) @db.Uuid
request_no String? @unique
applicant_id String @db.Uuid
submitter_id String? @db.Uuid
department_id String @db.Uuid
legal_entity_id String? @db.Uuid
cost_center_id String? @db.Uuid
supplier_id String? @db.Uuid
document_category_id String? @db.Uuid
budget_pool_id String? @db.Uuid
title String @default("")
reason String?
currency_code String @default("CNY") @db.VarChar(8)
total_amount Decimal @default(0) @db.Decimal(14, 2)
expected_delivery_date DateTime? @db.Date
status PurchaseRequestStatus @default(draft)
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
applicant Employee @relation("PurchaseRequestApplicant", fields: [applicant_id], references: [id], onDelete: Restrict)
submitter Employee? @relation("PurchaseRequestSubmitter", fields: [submitter_id], references: [id], onDelete: SetNull)
department Department @relation(fields: [department_id], references: [id], onDelete: Restrict)
legal_entity LegalEntity? @relation(fields: [legal_entity_id], references: [id], onDelete: SetNull)
cost_center CostCenter? @relation(fields: [cost_center_id], references: [id], onDelete: SetNull)
supplier Supplier? @relation(fields: [supplier_id], references: [id], onDelete: SetNull)
document_category DocumentCategory? @relation(fields: [document_category_id], references: [id], onDelete: SetNull)
budget_pool BudgetPool? @relation(fields: [budget_pool_id], references: [id], onDelete: SetNull)
items PurchaseRequestItem[]
attachments PurchaseAttachment[]
order_archive PurchaseOrderArchive?
asset_cards AssetCard[]
workflow_instances WorkflowInstance[]
@@index([applicant_id, status, updated_at], map: "idx_purchase_requests_applicant_status_updated")
@@index([submitter_id], map: "idx_purchase_requests_submitter")
@@index([department_id], map: "idx_purchase_requests_department")
@@index([legal_entity_id], map: "idx_purchase_requests_legal_entity")
@@index([cost_center_id], map: "idx_purchase_requests_cost_center")
@@index([supplier_id], map: "idx_purchase_requests_supplier")
@@index([document_category_id], map: "idx_purchase_requests_document_category")
@@index([budget_pool_id], map: "idx_purchase_requests_budget_pool")
@@index([status], map: "idx_purchase_requests_status")
@@map("purchase_requests")
}
model PurchaseRequestItem {
id String @id @default(uuid()) @db.Uuid
purchase_request_id String @db.Uuid
description String
specification String?
quantity Decimal @db.Decimal(14, 2)
unit_price Decimal @db.Decimal(14, 2)
line_amount Decimal @db.Decimal(14, 2)
is_asset_candidate Boolean @default(false)
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
purchase_request PurchaseRequest @relation(fields: [purchase_request_id], references: [id], onDelete: Cascade)
@@index([purchase_request_id], map: "idx_purchase_request_items_request")
@@map("purchase_request_items")
}
model PurchaseAttachment {
id String @id @default(uuid()) @db.Uuid
purchase_request_id String @db.Uuid
storage_path String
original_name String
mime_type String
file_size Int
uploaded_by String @db.Uuid
created_at DateTime @default(now())
purchase_request PurchaseRequest @relation(fields: [purchase_request_id], references: [id], onDelete: Cascade)
uploader Employee @relation("PurchaseAttachmentUploader", fields: [uploaded_by], references: [id], onDelete: Restrict)
@@index([purchase_request_id, created_at], map: "idx_purchase_attachments_request_created")
@@index([uploaded_by], map: "idx_purchase_attachments_uploader")
@@map("purchase_attachments")
}
model PurchaseOrderArchive {
id String @id @default(uuid()) @db.Uuid
purchase_request_id String @unique @db.Uuid
po_no String
contract_no String?
archived_files Json?
archived_at DateTime @default(now())
archived_by String @db.Uuid
purchase_request PurchaseRequest @relation(fields: [purchase_request_id], references: [id], onDelete: Cascade)
archived_by_employee Employee @relation("PurchaseOrderArchiveArchivedBy", fields: [archived_by], references: [id], onDelete: Restrict)
@@index([archived_by], map: "idx_purchase_order_archives_archived_by")
@@index([archived_at], map: "idx_purchase_order_archives_archived_at")
@@map("purchase_order_archives")
}
model AssetCategory {
id String @id @default(uuid()) @db.Uuid
code String @unique
name String
document_category_id String? @db.Uuid
enabled Boolean @default(true)
archived_at DateTime?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
document_category DocumentCategory? @relation(fields: [document_category_id], references: [id], onDelete: SetNull)
asset_cards AssetCard[]
@@index([enabled, archived_at], map: "idx_asset_categories_status")
@@index([document_category_id], map: "idx_asset_categories_document_category")
@@map("asset_categories")
}
model AssetCard {
id String @id @default(uuid()) @db.Uuid
asset_no String @unique
name String
category_id String @db.Uuid
legal_entity_id String @db.Uuid
cost_center_id String @db.Uuid
purchase_request_id String? @db.Uuid
serial_no String?
location String?
status AssetCardStatus @default(in_stock)
assignee_employee_id String? @db.Uuid
activated_at DateTime? @db.Date
retired_at DateTime? @db.Date
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
category AssetCategory @relation(fields: [category_id], references: [id], onDelete: Restrict)
legal_entity LegalEntity @relation(fields: [legal_entity_id], references: [id], onDelete: Restrict)
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")
@@index([cost_center_id], map: "idx_asset_cards_cost_center")
@@index([purchase_request_id], map: "idx_asset_cards_purchase_request")
@@index([assignee_employee_id], map: "idx_asset_cards_assignee")
@@index([status], map: "idx_asset_cards_status")
@@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 @default(now()) @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
submitter_id String @db.Uuid
owner_id String @db.Uuid
department_id String @db.Uuid
reimbursement_date DateTime @db.Date
total_amount Decimal @default(0) @db.Decimal(14, 2)
status ReimbursementStatus @default(draft)
description String?
payment_voucher_no String? @db.VarChar(50)
payment_date DateTime? @db.Date
payment_channel String? @db.VarChar(20)
paid_at DateTime?
paid_by String? @db.Uuid
project_id String? @db.Uuid
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
submitter Employee @relation("ReimbursementSubmitter", fields: [submitter_id], references: [id], onDelete: Restrict)
owner Employee @relation("ReimbursementOwner", fields: [owner_id], references: [id], onDelete: Restrict)
paid_by_employee Employee? @relation("ReimbursementPaidBy", fields: [paid_by], references: [id], onDelete: SetNull)
department Department @relation(fields: [department_id], references: [id], onDelete: Restrict)
project Project? @relation(fields: [project_id], references: [id], onDelete: SetNull)
items ReimbursementItem[]
invoice_pool_items InvoicePool[]
workflow_instances WorkflowInstance[]
approval_transfers ApprovalTransfer[]
budget_ledger_entries BudgetLedgerEntry[]
travel_orders TravelOrder[]
@@index([owner_id])
@@index([submitter_id])
@@index([department_id])
@@index([paid_by])
@@index([project_id], map: "idx_reimbursement_forms_project")
@@index([status])
@@index([reimbursement_date])
@@index([owner_id, status, updated_at], map: "idx_reimbursement_owner_status_updated_at")
@@map("reimbursement_forms")
}
model ReimbursementItem {
id String @id @default(uuid()) @db.Uuid
reimbursement_id String @db.Uuid
expense_type_id String @db.Uuid
expense_date DateTime @db.Date
amount Decimal @db.Decimal(14, 2)
description String
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
reimbursement_form ReimbursementForm @relation(fields: [reimbursement_id], references: [id], onDelete: Cascade)
expense_type ExpenseType @relation(fields: [expense_type_id], references: [id], onDelete: Restrict)
invoice_pool_items InvoicePool[]
@@index([reimbursement_id])
@@index([expense_type_id])
@@index([expense_date])
@@map("reimbursement_items")
}
model LeaveType {
id String @id @default(uuid()) @db.Uuid
code String @unique
name String
description String?
enabled Boolean @default(true)
requires_attachment Boolean @default(false)
attachment_mime_types Json?
sort_order Int @default(100)
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
leave_policy LeavePolicy?
leave_requests LeaveRequest[]
leave_ledgers LeaveLedger[]
@@index([enabled, sort_order], map: "idx_leave_types_enabled_sort_order")
@@map("leave_types")
}
model LeavePolicy {
id String @id @default(uuid()) @db.Uuid
leave_type_id String @unique @db.Uuid
annual_quota_days Decimal @db.Decimal(8, 1)
enabled Boolean @default(true)
description String?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
leave_type LeaveType @relation(fields: [leave_type_id], references: [id], onDelete: Cascade)
leave_ledgers LeaveLedger[]
@@index([enabled], map: "idx_leave_policies_enabled")
@@map("leave_policies")
}
model LeaveRequest {
id String @id @default(uuid()) @db.Uuid
owner_id String @db.Uuid
department_id String @db.Uuid
leave_type_id String @db.Uuid
start_date DateTime @db.Date
end_date DateTime @db.Date
start_session LeaveDaySession @default(am)
end_session LeaveDaySession @default(pm)
requested_days Decimal @db.Decimal(8, 1)
reason String?
attachment_storage_path String?
attachment_original_name String?
attachment_mime_type String?
status LeaveRequestStatus @default(draft)
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
owner Employee @relation(fields: [owner_id], references: [id], onDelete: Restrict)
department Department @relation(fields: [department_id], references: [id], onDelete: Restrict)
leave_type LeaveType @relation(fields: [leave_type_id], references: [id], onDelete: Restrict)
leave_ledgers LeaveLedger[]
workflow_instances WorkflowInstance[]
@@index([owner_id])
@@index([department_id])
@@index([leave_type_id])
@@index([status])
@@index([owner_id, status, updated_at], map: "idx_leave_owner_status_updated_at")
@@index([owner_id, start_date, end_date], map: "idx_leave_owner_date_range")
@@map("leave_requests")
}
model LeaveLedger {
id String @id @default(uuid()) @db.Uuid
employee_id String @db.Uuid
leave_type_id String @db.Uuid
leave_policy_id String? @db.Uuid
leave_request_id String? @db.Uuid
entry_type LeaveLedgerEntryType
days Decimal @db.Decimal(8, 1)
period_start DateTime? @db.Date
period_end DateTime? @db.Date
note String?
payload Json?
created_at DateTime @default(now())
created_by String? @db.Uuid
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Restrict)
leave_type LeaveType @relation(fields: [leave_type_id], references: [id], onDelete: Restrict)
leave_policy LeavePolicy? @relation(fields: [leave_policy_id], references: [id], onDelete: SetNull)
leave_request LeaveRequest? @relation(fields: [leave_request_id], references: [id], onDelete: SetNull)
@@index([employee_id, leave_type_id, created_at], map: "idx_leave_ledger_employee_type_created_at")
@@index([leave_request_id])
@@index([period_start, period_end], map: "idx_leave_ledger_period")
@@index([entry_type, employee_id], map: "idx_leave_ledger_entry_employee")
@@map("leave_ledgers")
}
model OcrResult {
id String @id @default(uuid()) @db.Uuid
file_sha256 String
ocr_provider OcrProvider @default(tencent)
ocr_type OcrType
result_json Json
confidence Int?
created_at DateTime @default(now())
invoice_pool_items InvoicePool[]
@@unique([file_sha256, ocr_provider, ocr_type])
@@index([file_sha256])
@@map("ocr_results")
}
model InvoicePool {
id String @id @default(uuid()) @db.Uuid
owner_employee_id String @db.Uuid
uploaded_by_employee_id String @db.Uuid
storage_path String
original_name String
mime_type String
file_size Int
file_sha256 String
status InvoicePoolStatus @default(pending_ocr)
archived_reason InvoiceArchivedReason?
archived_at DateTime?
linked_reimbursement_id String? @db.Uuid
linked_item_id String? @db.Uuid
travel_order_id String? @db.Uuid
remarks String?
ocr_provider OcrProvider @default(tencent)
ocr_type OcrType @default(general)
ocr_result_id String? @db.Uuid
ocr_attempts Int @default(0)
ocr_last_requested_at DateTime?
ocr_last_error String?
ocr_completed_at DateTime?
ocr_locked_at DateTime?
ocr_lock_owner String? @db.VarChar(128)
invoice_code String?
invoice_number String?
invoice_date DateTime? @db.Date
seller_name String?
buyer_name String?
amount Decimal? @db.Decimal(14, 2)
tax_amount Decimal? @db.Decimal(14, 2)
total_amount Decimal? @db.Decimal(14, 2)
is_duplicate_override Boolean @default(false)
override_ref_form_number String?
override_reason String?
overridden_by String? @db.Uuid
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
owner_employee Employee @relation("InvoiceOwner", fields: [owner_employee_id], references: [id], onDelete: Restrict)
uploaded_by_employee Employee @relation("InvoiceUploader", fields: [uploaded_by_employee_id], references: [id], onDelete: Restrict)
overridden_by_employee Employee? @relation("InvoiceOverrideBy", fields: [overridden_by], references: [id], onDelete: Restrict)
ocr_result OcrResult? @relation(fields: [ocr_result_id], references: [id], onDelete: SetNull)
linked_reimbursement ReimbursementForm? @relation(fields: [linked_reimbursement_id], references: [id], onDelete: SetNull)
linked_item ReimbursementItem? @relation(fields: [linked_item_id], references: [id], onDelete: SetNull)
travel_order TravelOrder? @relation(fields: [travel_order_id], references: [id], onDelete: SetNull)
verification_records InvoiceVerificationRecord[]
@@index([owner_employee_id])
@@index([uploaded_by_employee_id])
@@index([file_sha256])
@@index([invoice_code, invoice_number])
@@index([status])
@@index([status, ocr_locked_at, created_at], map: "idx_invoice_pool_ocr_queue")
@@index([owner_employee_id, status, created_at], map: "idx_invoice_pool_owner_status_created_at")
@@index([linked_reimbursement_id])
@@index([travel_order_id])
@@map("invoice_pool")
}
model InvoiceVerificationRecord {
id String @id @default(uuid()) @db.Uuid
invoice_id String @db.Uuid
provider String @db.VarChar(32)
status String @db.VarChar(16)
request_payload Json?
result_payload Json?
result_code String? @db.VarChar(64)
result_message String?
error_code String? @db.VarChar(64)
error_message String?
duration_ms Int?
requested_by String? @db.Uuid
requested_at DateTime @default(now())
completed_at DateTime?
retry_of_id String? @db.Uuid
attempts Int @default(1)
created_at DateTime @default(now())
updated_at DateTime @updatedAt
invoice InvoicePool @relation(fields: [invoice_id], references: [id], onDelete: Cascade)
requester Employee? @relation("InvoiceVerificationRequester", fields: [requested_by], references: [id], onDelete: SetNull)
retry_of InvoiceVerificationRecord? @relation("InvoiceVerificationRetry", fields: [retry_of_id], references: [id], onDelete: SetNull)
retries InvoiceVerificationRecord[] @relation("InvoiceVerificationRetry")
@@index([invoice_id, created_at], map: "idx_invoice_verification_invoice_created_at")
@@index([status, requested_at], map: "idx_invoice_verification_status_requested_at")
@@index([requested_by])
@@map("invoice_verification_records")
}
model BudgetPool {
id String @id @default(uuid()) @db.Uuid
code String @unique
name String
department_id String? @db.Uuid
legal_entity_id String? @db.Uuid
cost_center_id String? @db.Uuid
status String @default("active") @db.VarChar(16)
period_start DateTime @db.Date
period_end DateTime @db.Date
allocated_amount Decimal @db.Decimal(14, 2)
used_amount Decimal @default(0) @db.Decimal(14, 2)
warning_threshold_percent Int @default(80)
archived_at DateTime?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
department Department? @relation(fields: [department_id], references: [id], onDelete: SetNull)
legal_entity LegalEntity? @relation("BudgetPoolLegalEntity", fields: [legal_entity_id], references: [id], onDelete: SetNull)
cost_center CostCenter? @relation("BudgetPoolCostCenter", fields: [cost_center_id], references: [id], onDelete: SetNull)
ledger_entries BudgetLedgerEntry[]
purchase_requests PurchaseRequest[]
travel_requests TravelRequest[]
card_authorizations CardAuthorization[]
@@index([department_id, status], map: "idx_budget_pools_department_status")
@@index([legal_entity_id, cost_center_id, status], map: "idx_budget_pools_scope_status")
@@index([status, period_start, period_end], map: "idx_budget_pools_period")
@@map("budget_pools")
}
model BudgetLedgerEntry {
id String @id @default(uuid()) @db.Uuid
budget_pool_id String @db.Uuid
reimbursement_id String? @db.Uuid
entity_type String? @db.VarChar(32)
entity_id String? @db.Uuid
kind String @db.VarChar(32)
amount Decimal @db.Decimal(14, 2)
note String?
created_at DateTime @default(now())
created_by String? @db.Uuid
budget_pool BudgetPool @relation(fields: [budget_pool_id], references: [id], onDelete: Cascade)
reimbursement_form ReimbursementForm? @relation(fields: [reimbursement_id], references: [id], onDelete: SetNull)
@@index([budget_pool_id, created_at], map: "idx_budget_ledger_pool_created_at")
@@index([reimbursement_id])
@@index([entity_type, entity_id], map: "idx_budget_ledger_entity")
@@map("budget_ledger_entries")
}
model FeatureFlag {
id String @id @default(uuid()) @db.Uuid
flag_key String @unique @db.VarChar(64)
override_mode String @default("force_off") @db.VarChar(16)
rollout_type String @default("global") @db.VarChar(32)
department_ids Json?
note String?
version Int @default(1)
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
@@index([flag_key], map: "idx_feature_flags_key")
@@map("feature_flags")
}
model BudgetControlRule {
id String @id @default(uuid()) @db.Uuid
name String
entity_type BudgetControlRuleEntityType @default(all)
department_id String? @db.Uuid
legal_entity_id String? @db.Uuid
cost_center_id String? @db.Uuid
expense_type_id String? @db.Uuid
control_mode BudgetControlMode
priority Int @default(100)
enabled Boolean @default(true)
notes String?
archived_at DateTime?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
@@index([entity_type, enabled, archived_at], map: "idx_budget_control_rules_entity_enabled")
@@index([department_id, legal_entity_id, cost_center_id], map: "idx_budget_control_rules_scope")
@@index([expense_type_id], map: "idx_budget_control_rules_expense_type")
@@map("budget_control_rules")
}
model EntityRiskFlag {
id String @id @default(uuid()) @db.Uuid
entity_type String @db.VarChar(32)
entity_id String @db.Uuid
risk_code String @db.VarChar(64)
level String @db.VarChar(16)
title String
detail String?
source String @db.VarChar(32)
status RiskFlagStatus @default(open)
payload_json Json?
created_at DateTime @default(now())
created_by String? @db.Uuid
resolved_at DateTime?
resolved_by String? @db.Uuid
@@index([entity_type, entity_id, status], map: "idx_entity_risk_flags_entity_status")
@@index([status, source, created_at], map: "idx_entity_risk_flags_status_source_created")
@@map("entity_risk_flags")
}
model PaymentBatch {
id String @id @default(uuid()) @db.Uuid
batch_no String @unique @db.VarChar(64)
entity_type PaymentBatchEntityType
name String
status PaymentBatchStatus @default(draft)
default_payment_date DateTime? @db.Date
default_payment_channel String? @db.VarChar(20)
voucher_prefix String? @db.VarChar(32)
review_comment String?
secondary_review_comment String?
execution_summary_json Json?
created_at DateTime @default(now())
created_by String? @db.Uuid
reviewed_at DateTime?
reviewed_by String? @db.Uuid
secondary_reviewed_at DateTime?
secondary_reviewed_by String? @db.Uuid
executed_at DateTime?
executed_by String? @db.Uuid
cancelled_at DateTime?
cancelled_by String? @db.Uuid
updated_at DateTime @updatedAt
updated_by String? @db.Uuid
items PaymentBatchItem[]
@@index([entity_type, status, created_at], map: "idx_payment_batches_entity_status_created")
@@map("payment_batches")
}
model PaymentBatchItem {
id String @id @default(uuid()) @db.Uuid
payment_batch_id String @db.Uuid
entity_id String @db.Uuid
entity_code String
entity_title String
amount Decimal @db.Decimal(14, 2)
currency_code String @default("CNY") @db.VarChar(8)
payment_voucher_no_override String? @db.VarChar(50)
payment_date_override DateTime? @db.Date
payment_channel_override String? @db.VarChar(20)
status String @default("pending") @db.VarChar(16)
failure_code String? @db.VarChar(64)
failure_message String?
snapshot_json Json
created_at DateTime @default(now())
payment_batch PaymentBatch @relation(fields: [payment_batch_id], references: [id], onDelete: Cascade)
@@index([payment_batch_id, status], map: "idx_payment_batch_items_batch_status")
@@index([entity_id], map: "idx_payment_batch_items_entity_id")
@@map("payment_batch_items")
}
model PortalAnnouncement {
id String @id @default(uuid()) @db.Uuid
title String
summary String
link_url String?
starts_at DateTime?
ends_at DateTime?
enabled Boolean @default(true)
priority Int @default(100)
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
@@index([enabled, priority, starts_at, ends_at], map: "idx_portal_announcements_enabled_priority")
@@map("portal_announcements")
}
model WorkspaceSetting {
id String @id @default(uuid()) @db.Uuid
profile WorkspaceProfile @default(small_team_internal)
org_mode WorkspaceOrgMode @default(single_company)
primary_entry_mode WorkspacePrimaryEntryMode @default(password)
notification_strategy WorkspaceNotificationStrategy @default(in_app)
device_priority WorkspaceDevicePriority @default(desktop)
module_visibility_json Json?
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
@@map("workspace_settings")
}
model EmployeeLifecycleEvent {
id String @id @default(uuid()) @db.Uuid
employee_id String @db.Uuid
event_type EmployeeLifecycleEventType
effective_date DateTime @db.Date
payload_json Json?
created_at DateTime @default(now())
created_by String? @db.Uuid
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade)
@@index([employee_id, effective_date], map: "idx_employee_lifecycle_events_employee_date")
@@index([event_type, created_at], map: "idx_employee_lifecycle_events_type_created")
@@map("employee_lifecycle_events")
}
model EmployeeChecklistTask {
id String @id @default(uuid()) @db.Uuid
employee_id String @db.Uuid
scene EmployeeChecklistScene
task_type EmployeeChecklistTaskType
title String
status EmployeeChecklistTaskStatus @default(pending)
due_date DateTime? @db.Date
completed_at DateTime?
completed_by String? @db.Uuid
payload_json Json?
created_at DateTime @default(now())
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade)
@@index([employee_id, scene, status], map: "idx_employee_checklist_tasks_employee_scene_status")
@@index([scene, due_date], map: "idx_employee_checklist_tasks_scene_due")
@@map("employee_checklist_tasks")
}
model EmployeePresence {
id String @id @default(uuid()) @db.Uuid
employee_id String @db.Uuid
date DateTime @db.Date
status EmployeePresenceStatus
time_range EmployeePresenceTimeRange @default(full_day)
note String?
source EmployeePresenceSource @default(manual)
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade)
@@unique([employee_id, date], map: "employee_presence_employee_date_ux")
@@index([date, status], map: "idx_employee_presence_date_status")
@@map("employee_presence")
}
model WeeklyReport {
id String @id @default(uuid()) @db.Uuid
employee_id String @db.Uuid
week_start DateTime @db.Date
status WeeklyReportStatus @default(draft)
highlights_text String @default("")
blockers_text String @default("")
next_week_text String @default("")
needs_help Boolean @default(false)
submitted_at DateTime?
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade)
project_links ProjectLinkOnWeeklyReport[]
@@unique([employee_id, week_start], map: "weekly_reports_employee_week_start_ux")
@@index([week_start, status], map: "idx_weekly_reports_week_status")
@@map("weekly_reports")
}
model TodoItem {
id String @id @default(uuid()) @db.Uuid
title String
description String?
assignee_employee_id String @db.Uuid
created_by String @db.Uuid
due_date DateTime? @db.Date
priority TodoPriority @default(normal)
status TodoStatus @default(todo)
source TodoSource @default(manual)
project_id String? @db.Uuid
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
completed_at DateTime?
assignee Employee @relation("TodoAssignee", fields: [assignee_employee_id], references: [id], onDelete: Restrict)
creator Employee @relation("TodoCreator", fields: [created_by], references: [id], onDelete: Restrict)
project Project? @relation(fields: [project_id], references: [id], onDelete: SetNull)
@@index([assignee_employee_id, status, due_date], map: "idx_todo_items_assignee_status_due")
@@index([project_id, status], map: "idx_todo_items_project_status")
@@index([created_by, created_at], map: "idx_todo_items_creator_created")
@@map("todo_items")
}
model WecomIdentityBinding {
id String @id @default(uuid()) @db.Uuid
employee_id String @unique @db.Uuid
external_user_id String @unique @db.VarChar(128)
external_open_user_id String? @db.VarChar(128)
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade)
@@index([external_user_id], map: "idx_wecom_identity_bindings_external_user")
@@map("wecom_identity_bindings")
}
model Project {
id String @id @default(uuid()) @db.Uuid
project_no String @unique
name String
client_name String
client_alias String?
owner_employee_id String @db.Uuid
manager_employee_id String? @db.Uuid
status ProjectStatus @default(draft)
starts_at DateTime? @db.Date
target_end_at DateTime? @db.Date
actual_end_at DateTime? @db.Date
summary String?
is_confidential Boolean @default(false)
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
owner Employee @relation("ProjectOwner", fields: [owner_employee_id], references: [id], onDelete: Restrict)
manager Employee? @relation("ProjectManager", fields: [manager_employee_id], references: [id], onDelete: SetNull)
members ProjectMember[]
milestones ProjectMilestone[]
tasks ProjectTask[]
risks ProjectRisk[]
reimbursements ReimbursementForm[]
travel_requests TravelRequest[]
todo_items TodoItem[]
weekly_reports ProjectLinkOnWeeklyReport[]
communication_logs ProjectCommunicationLog[]
closure_checklist_items ProjectClosureChecklistItem[]
contracts ContractRegistry[]
deliveries ProjectDeliveryRecord[]
document_refs ConfidentialDocumentRef[]
@@index([owner_employee_id, status], map: "idx_projects_owner_status")
@@index([manager_employee_id, status], map: "idx_projects_manager_status")
@@index([status, target_end_at], map: "idx_projects_status_target_end")
@@index([client_name, name], map: "idx_projects_client_name")
@@map("projects")
}
model ProjectMember {
id String @id @default(uuid()) @db.Uuid
project_id String @db.Uuid
employee_id String @db.Uuid
role ProjectRole @default(member)
joined_at DateTime @default(now())
left_at DateTime?
project Project @relation(fields: [project_id], references: [id], onDelete: Cascade)
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Restrict)
@@unique([project_id, employee_id], map: "project_members_project_employee_ux")
@@index([employee_id, left_at], map: "idx_project_members_employee_open")
@@map("project_members")
}
model ProjectMilestone {
id String @id @default(uuid()) @db.Uuid
project_id String @db.Uuid
title String
description String?
status ProjectMilestoneStatus @default(pending)
due_date DateTime? @db.Date
completed_at DateTime?
owner_employee_id String? @db.Uuid
sort_order Int @default(100)
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
project Project @relation(fields: [project_id], references: [id], onDelete: Cascade)
owner Employee? @relation("ProjectMilestoneOwner", fields: [owner_employee_id], references: [id], onDelete: SetNull)
@@index([project_id, sort_order], map: "idx_project_milestones_project_sort")
@@index([owner_employee_id, due_date], map: "idx_project_milestones_owner_due")
@@map("project_milestones")
}
model ProjectTask {
id String @id @default(uuid()) @db.Uuid
project_id String @db.Uuid
title String
description String?
status ProjectTaskStatus @default(todo)
priority ProjectTaskPriority @default(normal)
assignee_employee_id String? @db.Uuid
due_date DateTime? @db.Date
blocked_reason String?
created_by String @db.Uuid
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
project Project @relation(fields: [project_id], references: [id], onDelete: Cascade)
assignee Employee? @relation("ProjectTaskAssignee", fields: [assignee_employee_id], references: [id], onDelete: SetNull)
creator Employee @relation("ProjectTaskCreator", fields: [created_by], references: [id], onDelete: Restrict)
@@index([project_id, status, priority], map: "idx_project_tasks_project_status_priority")
@@index([assignee_employee_id, status], map: "idx_project_tasks_assignee_status")
@@map("project_tasks")
}
model ProjectRisk {
id String @id @default(uuid()) @db.Uuid
project_id String @db.Uuid
title String
detail String
level ProjectRiskLevel @default(medium)
status String @default("open") @db.VarChar(32)
owner_employee_id String? @db.Uuid
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
project Project @relation(fields: [project_id], references: [id], onDelete: Cascade)
owner Employee? @relation("ProjectRiskOwner", fields: [owner_employee_id], references: [id], onDelete: SetNull)
@@index([project_id, status, level], map: "idx_project_risks_project_status_level")
@@index([owner_employee_id, status], map: "idx_project_risks_owner_status")
@@map("project_risks")
}
model ProjectLinkOnWeeklyReport {
id String @id @default(uuid()) @db.Uuid
weekly_report_id String @db.Uuid
project_id String @db.Uuid
progress_text String @default("")
risk_text String @default("")
next_step_text String @default("")
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
weekly_report WeeklyReport @relation(fields: [weekly_report_id], references: [id], onDelete: Cascade)
project Project @relation(fields: [project_id], references: [id], onDelete: Cascade)
@@unique([weekly_report_id, project_id], map: "weekly_report_project_links_report_project_ux")
@@index([project_id, updated_at], map: "idx_weekly_report_project_links_project_updated")
@@map("weekly_report_project_links")
}
model ProjectCommunicationLog {
id String @id @default(uuid()) @db.Uuid
project_id String @db.Uuid
happened_at DateTime
channel ProjectCommunicationChannel
external_contact_name String?
external_org_name String?
summary String
next_action String?
next_follow_up_date DateTime? @db.Date
created_by String @db.Uuid
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
project Project @relation(fields: [project_id], references: [id], onDelete: Cascade)
creator Employee @relation("ProjectCommunicationLogCreator", fields: [created_by], references: [id], onDelete: Restrict)
@@index([project_id, happened_at], map: "idx_project_communication_logs_project_happened")
@@index([project_id, next_follow_up_date], map: "idx_project_communication_logs_followup")
@@index([created_by, created_at], map: "idx_project_communication_logs_creator_created")
@@map("project_communication_logs")
}
model ProjectClosureChecklistItem {
id String @id @default(uuid()) @db.Uuid
project_id String @db.Uuid
item_key String @db.VarChar(64)
title String
status ProjectClosureChecklistStatus @default(pending)
note String?
sort_order Int @default(100)
completed_at DateTime?
completed_by String? @db.Uuid
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
project Project @relation(fields: [project_id], references: [id], onDelete: Cascade)
completer Employee? @relation("ProjectClosureChecklistItemCompleter", fields: [completed_by], references: [id], onDelete: SetNull)
@@unique([project_id, item_key], map: "project_closure_checklists_project_key_ux")
@@index([project_id, status, sort_order], map: "idx_project_closure_checklists_project_status")
@@index([completed_by, completed_at], map: "idx_project_closure_checklists_completed")
@@map("project_closure_checklist_items")
}
model ContractRegistry {
id String @id @default(uuid()) @db.Uuid
contract_no String @unique
project_id String? @db.Uuid
counterparty_name String
summary String
confidentiality_level ConfidentialityLevel @default(internal)
archive_location String
offline_archive_no String?
owner_employee_id String @db.Uuid
status String @default("active") @db.VarChar(32)
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
project Project? @relation(fields: [project_id], references: [id], onDelete: SetNull)
owner Employee @relation("ContractRegistryOwner", fields: [owner_employee_id], references: [id], onDelete: Restrict)
document_refs ConfidentialDocumentRef[]
@@index([project_id, status], map: "idx_contract_registry_project_status")
@@index([owner_employee_id, created_at], map: "idx_contract_registry_owner_created")
@@map("contract_registry")
}
model EmployeeContract {
id String @id @default(uuid()) @db.Uuid
employee_id String @db.Uuid
contract_no String
contract_type EmployeeContractType
start_date DateTime @db.Date
end_date DateTime @db.Date
terminated_date DateTime? @db.Date
attachment_path String?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade)
@@unique([employee_id, contract_no], map: "employee_contracts_employee_contract_no_ux")
@@index([employee_id, start_date], map: "idx_employee_contracts_employee_start")
@@index([employee_id, end_date], map: "idx_employee_contracts_employee_end")
@@map("employee_contracts")
}
model EmployeeTraining {
id String @id @default(uuid()) @db.Uuid
employee_id String @db.Uuid
name String
training_type EmployeeTrainingType
start_date DateTime @db.Date
end_date DateTime? @db.Date
status EmployeeTrainingStatus
notes String?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade)
@@index([employee_id, start_date], map: "idx_employee_trainings_employee_start")
@@index([employee_id, status], map: "idx_employee_trainings_employee_status")
@@map("employee_trainings")
}
model EmployeeCertificate {
id String @id @default(uuid()) @db.Uuid
employee_id String @db.Uuid
name String
issuer String
obtained_date DateTime @db.Date
expiry_date DateTime? @db.Date
certificate_no String?
attachment_path String?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade)
@@index([employee_id, obtained_date], map: "idx_employee_certificates_employee_obtained")
@@index([employee_id, expiry_date], map: "idx_employee_certificates_employee_expiry")
@@map("employee_certificates")
}
model ProjectDeliveryRecord {
id String @id @default(uuid()) @db.Uuid
project_id String @db.Uuid
title String
record_type ProjectDeliveryRecordType
summary String
occurred_at DateTime
owner_employee_id String @db.Uuid
created_at DateTime @default(now())
project Project @relation(fields: [project_id], references: [id], onDelete: Cascade)
owner Employee @relation("ProjectDeliveryRecordOwner", fields: [owner_employee_id], references: [id], onDelete: Restrict)
@@index([project_id, occurred_at], map: "idx_project_delivery_records_project_occurred")
@@map("project_delivery_records")
}
model ConfidentialDocumentRef {
id String @id @default(uuid()) @db.Uuid
project_id String? @db.Uuid
contract_registry_id String? @db.Uuid
title String
document_no String?
confidentiality_level ConfidentialityLevel @default(internal)
storage_type DocumentStorageType
storage_path String
offline_archive_no String?
owner_employee_id String @db.Uuid
access_rule_json Json?
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
project Project? @relation(fields: [project_id], references: [id], onDelete: SetNull)
contract_registry ContractRegistry? @relation(fields: [contract_registry_id], references: [id], onDelete: SetNull)
owner Employee @relation("ConfidentialDocumentRefOwner", fields: [owner_employee_id], references: [id], onDelete: Restrict)
@@index([project_id, confidentiality_level], map: "idx_confidential_doc_refs_project_level")
@@index([contract_registry_id], map: "idx_confidential_doc_refs_contract")
@@index([owner_employee_id, created_at], map: "idx_confidential_doc_refs_owner_created")
@@map("confidential_document_refs")
}
model MigrationRun {
id String @id @default(uuid()) @db.Uuid
run_type String @db.VarChar(32)
status String @default("draft") @db.VarChar(32)
source_filename String
summary_json Json?
error_json Json?
created_at DateTime @default(now())
created_by String? @db.Uuid
@@index([run_type, status, created_at], map: "idx_migration_runs_type_status_created")
@@map("migration_runs")
}
model CutoverScope {
id String @id @default(uuid()) @db.Uuid
scope_type String @db.VarChar(32)
scope_key String @db.VarChar(64)
enabled Boolean @default(false)
starts_at DateTime?
notes String?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
@@unique([scope_type, scope_key], map: "cutover_scopes_scope_type_scope_key_ux")
@@index([enabled, scope_type], map: "idx_cutover_scopes_enabled_type")
@@map("cutover_scopes")
}
model IntegrationOutbox {
id String @id @default(uuid()) @db.Uuid
event_type String @db.VarChar(64)
status String @default("pending") @db.VarChar(16)
payload Json
dedupe_key String? @db.VarChar(128)
attempts Int @default(0)
max_attempts Int @default(5)
last_error String?
next_retry_at DateTime?
processed_at DateTime?
dead_letter_at DateTime?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
@@unique([event_type, dedupe_key], map: "integration_outbox_event_dedupe_ux")
@@index([status, next_retry_at, created_at], map: "idx_integration_outbox_queue")
@@map("integration_outbox")
}
model IntegrationBinding {
id String @id @default(uuid()) @db.Uuid
provider String @db.VarChar(32)
entity_type String @db.VarChar(64)
local_id String @db.Uuid
external_id String? @db.VarChar(128)
external_code String? @db.VarChar(128)
last_synced_at DateTime?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
@@unique([provider, entity_type, local_id], map: "integration_bindings_provider_entity_local_ux")
@@unique([provider, entity_type, external_id], map: "integration_bindings_provider_entity_external_id_ux")
@@index([provider, entity_type, external_code], map: "idx_integration_bindings_provider_entity_external_code")
@@map("integration_bindings")
}
model IntegrationSyncState {
id String @id @default(uuid()) @db.Uuid
provider String @db.VarChar(32)
resource_type String @db.VarChar(32)
status String @default("idle") @db.VarChar(16)
last_result Json?
last_error String?
last_started_at DateTime?
last_finished_at DateTime?
last_success_at DateTime?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
@@unique([provider, resource_type], map: "integration_sync_states_provider_resource_ux")
@@index([provider, status], map: "idx_integration_sync_states_provider_status")
@@map("integration_sync_states")
}
model TravelRequest {
id String @id @default(uuid()) @db.Uuid
request_no String? @unique
applicant_employee_id String @db.Uuid
submitter_id String? @db.Uuid
department_id String @db.Uuid
legal_entity_id String @db.Uuid
cost_center_id String @db.Uuid
budget_pool_id String? @db.Uuid
project_id String? @db.Uuid
purpose String
trip_type TravelTripType
start_date DateTime @db.Date
end_date DateTime @db.Date
estimated_amount Decimal @db.Decimal(14, 2)
status TravelRequestStatus @default(draft)
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
applicant Employee @relation("TravelRequestApplicant", fields: [applicant_employee_id], references: [id], onDelete: Restrict)
submitter Employee? @relation("TravelRequestSubmitter", fields: [submitter_id], references: [id], onDelete: SetNull)
department Department @relation(fields: [department_id], references: [id], onDelete: Restrict)
legal_entity LegalEntity @relation(fields: [legal_entity_id], references: [id], onDelete: Restrict)
cost_center CostCenter @relation(fields: [cost_center_id], references: [id], onDelete: Restrict)
budget_pool BudgetPool? @relation(fields: [budget_pool_id], references: [id], onDelete: SetNull)
project Project? @relation(fields: [project_id], references: [id], onDelete: SetNull)
segments TravelRequestSegment[]
travel_orders TravelOrder[]
reconciliation_items TravelReconciliationItem[]
card_authorizations CardAuthorization[]
card_transactions CardTransaction[]
workflow_instances WorkflowInstance[]
@@index([applicant_employee_id, start_date], map: "idx_travel_requests_applicant_start")
@@index([submitter_id], map: "idx_travel_requests_submitter")
@@index([project_id], map: "idx_travel_requests_project")
@@index([department_id, status], map: "idx_travel_requests_department_status")
@@index([legal_entity_id, cost_center_id], map: "idx_travel_requests_legal_cost_center")
@@index([budget_pool_id], map: "idx_travel_requests_budget_pool")
@@index([status], map: "idx_travel_requests_status")
@@map("travel_requests")
}
model TravelRequestSegment {
id String @id @default(uuid()) @db.Uuid
travel_request_id String @db.Uuid
sort_order Int
segment_role TravelSegmentRole
transport_type TravelTransportType
from_city String
to_city String
depart_at DateTime
arrive_at DateTime
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
travel_request TravelRequest @relation(fields: [travel_request_id], references: [id], onDelete: Cascade)
@@index([travel_request_id, sort_order], map: "idx_travel_request_segments_request_sort")
@@map("travel_request_segments")
}
model TravelOrder {
id String @id @default(uuid()) @db.Uuid
source String @default("manual") @db.VarChar(32)
external_order_no String
employee_id String @db.Uuid
department_id String? @db.Uuid
linked_reimbursement_id String? @db.Uuid
travel_request_id String? @db.Uuid
occurred_at DateTime? @db.Date
amount Decimal? @db.Decimal(14, 2)
currency String @default("CNY") @db.VarChar(8)
status String @default("imported") @db.VarChar(20)
payment_voucher_no String? @db.VarChar(50)
payment_date DateTime? @db.Date
payment_channel String? @db.VarChar(20)
paid_at DateTime?
paid_by String? @db.Uuid
raw_payload Json?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
employee Employee @relation("TravelOrderEmployee", fields: [employee_id], references: [id], onDelete: Restrict)
department Department? @relation(fields: [department_id], references: [id], onDelete: SetNull)
linked_reimbursement ReimbursementForm? @relation(fields: [linked_reimbursement_id], references: [id], onDelete: SetNull)
travel_request TravelRequest? @relation(fields: [travel_request_id], references: [id], onDelete: SetNull)
paid_by_employee Employee? @relation("TravelOrderPaidBy", fields: [paid_by], references: [id], onDelete: SetNull)
invoice_pool_items InvoicePool[]
card_transactions CardTransaction[]
reconciliation_items TravelReconciliationItem[]
@@unique([source, external_order_no], map: "travel_orders_source_order_ux")
@@index([employee_id, occurred_at], map: "idx_travel_orders_employee_occurred")
@@index([linked_reimbursement_id])
@@index([travel_request_id])
@@index([paid_by])
@@map("travel_orders")
}
model CardAuthorization {
id String @id @default(uuid()) @db.Uuid
source String @default("manual") @db.VarChar(32)
external_auth_no String
employee_id String @db.Uuid
department_id String? @db.Uuid
travel_request_id String @db.Uuid
budget_pool_id String? @db.Uuid
legal_entity_id String @db.Uuid
cost_center_id String @db.Uuid
authorized_amount Decimal @db.Decimal(14, 2)
settled_amount Decimal @default(0) @db.Decimal(14, 2)
reversed_amount Decimal @default(0) @db.Decimal(14, 2)
currency String @default("CNY") @db.VarChar(8)
occurred_at DateTime?
expires_at DateTime?
status String @default("authorized") @db.VarChar(32)
raw_payload Json?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Restrict)
department Department? @relation(fields: [department_id], references: [id], onDelete: SetNull)
travel_request TravelRequest @relation(fields: [travel_request_id], references: [id], onDelete: Cascade)
budget_pool BudgetPool? @relation(fields: [budget_pool_id], references: [id], onDelete: SetNull)
legal_entity LegalEntity @relation(fields: [legal_entity_id], references: [id], onDelete: Restrict)
cost_center CostCenter @relation(fields: [cost_center_id], references: [id], onDelete: Restrict)
card_transactions CardTransaction[]
@@unique([source, external_auth_no], map: "card_authorizations_source_auth_ux")
@@index([employee_id, occurred_at], map: "idx_card_authorizations_employee_occurred")
@@index([department_id, status], map: "idx_card_authorizations_department_status")
@@index([travel_request_id, status], map: "idx_card_authorizations_request_status")
@@index([budget_pool_id], map: "idx_card_authorizations_budget_pool")
@@index([legal_entity_id, cost_center_id], map: "idx_card_authorizations_legal_cost_center")
@@map("card_authorizations")
}
model CardTransaction {
id String @id @default(uuid()) @db.Uuid
source String @default("manual") @db.VarChar(32)
external_txn_id String
card_authorization_id String? @db.Uuid
employee_id String @db.Uuid
department_id String? @db.Uuid
travel_request_id String @db.Uuid
travel_order_id String? @db.Uuid
amount Decimal @db.Decimal(14, 2)
currency String @default("CNY") @db.VarChar(8)
txn_type String @db.VarChar(20)
status String @default("pending") @db.VarChar(20)
occurred_at DateTime?
freeze_suggested_at DateTime?
freeze_suggested_by String? @db.Uuid
freeze_reason_code String? @db.VarChar(32)
freeze_reason_note String?
raw_payload Json?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
card_authorization CardAuthorization? @relation(fields: [card_authorization_id], references: [id], onDelete: SetNull)
employee Employee @relation("CardTransactionEmployee", fields: [employee_id], references: [id], onDelete: Restrict)
department Department? @relation(fields: [department_id], references: [id], onDelete: SetNull)
travel_request TravelRequest @relation(fields: [travel_request_id], references: [id], onDelete: Cascade)
travel_order TravelOrder? @relation(fields: [travel_order_id], references: [id], onDelete: SetNull)
freeze_suggested_by_employee Employee? @relation("CardTransactionFreezeSuggestedBy", fields: [freeze_suggested_by], references: [id], onDelete: SetNull)
reconciliation_items TravelReconciliationItem[]
@@unique([source, external_txn_id], map: "card_transactions_source_txn_ux")
@@index([card_authorization_id], map: "idx_card_transactions_authorization")
@@index([employee_id, occurred_at], map: "idx_card_transactions_employee_occurred")
@@index([department_id, status], map: "idx_card_transactions_department_status")
@@index([travel_request_id], map: "idx_card_transactions_request")
@@index([travel_order_id], map: "idx_card_transactions_order")
@@index([freeze_suggested_by], map: "idx_card_transactions_freeze_suggested_by")
@@map("card_transactions")
}
model TravelReconciliationBatch {
id String @id @default(uuid()) @db.Uuid
batch_no String @unique
legal_entity_id String @db.Uuid
cost_center_id String? @db.Uuid
period_start DateTime @db.Date
period_end DateTime @db.Date
status TravelReconciliationBatchStatus @default(draft)
summary_json Json
confirmed_at DateTime?
confirmed_by String? @db.Uuid
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
legal_entity LegalEntity @relation(fields: [legal_entity_id], references: [id], onDelete: Restrict)
cost_center CostCenter? @relation(fields: [cost_center_id], references: [id], onDelete: SetNull)
confirmed_by_employee Employee? @relation("TravelReconciliationBatchConfirmedBy", fields: [confirmed_by], references: [id], onDelete: SetNull)
items TravelReconciliationItem[]
@@index([status, period_start, period_end], map: "idx_travel_reconciliation_batches_status_period")
@@index([legal_entity_id, cost_center_id], map: "idx_travel_reconciliation_batches_legal_cost_center")
@@index([confirmed_by], map: "idx_travel_reconciliation_batches_confirmed_by")
@@map("travel_reconciliation_batches")
}
model TravelReconciliationItem {
id String @id @default(uuid()) @db.Uuid
travel_reconciliation_batch_id String @db.Uuid
item_kind TravelReconciliationItemKind
travel_request_id String? @db.Uuid
travel_order_id String? @db.Uuid
card_transaction_id String? @db.Uuid
match_status TravelReconciliationMatchStatus
resolution_status TravelReconciliationResolutionStatus @default(pending)
order_amount Decimal? @db.Decimal(14, 2)
transaction_amount Decimal? @db.Decimal(14, 2)
currency String @default("CNY") @db.VarChar(8)
anomaly_code String? @db.VarChar(32)
snapshot_json Json
confirmed_at DateTime?
confirmed_by String? @db.Uuid
confirm_note String?
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
batch TravelReconciliationBatch @relation(fields: [travel_reconciliation_batch_id], references: [id], onDelete: Cascade)
travel_request TravelRequest? @relation(fields: [travel_request_id], references: [id], onDelete: SetNull)
travel_order TravelOrder? @relation(fields: [travel_order_id], references: [id], onDelete: SetNull)
card_transaction CardTransaction? @relation(fields: [card_transaction_id], references: [id], onDelete: SetNull)
confirmed_by_employee Employee? @relation("TravelReconciliationItemConfirmedBy", fields: [confirmed_by], references: [id], onDelete: SetNull)
@@index([travel_reconciliation_batch_id, resolution_status], map: "idx_travel_reconciliation_items_batch_resolution")
@@index([travel_request_id], map: "idx_travel_reconciliation_items_request")
@@index([travel_order_id], map: "idx_travel_reconciliation_items_order")
@@index([card_transaction_id], map: "idx_travel_reconciliation_items_transaction")
@@index([confirmed_by], map: "idx_travel_reconciliation_items_confirmed_by")
@@map("travel_reconciliation_items")
}
model NotificationChannel {
id String @id @default(uuid()) @db.Uuid
channel_type String @db.VarChar(32)
name String
enabled Boolean @default(true)
config_json Json
template_json Json?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
@@unique([channel_type, name], map: "notification_channels_type_name_ux")
@@index([enabled, channel_type], map: "idx_notification_channels_enabled_type")
@@map("notification_channels")
}
model WorkflowDefinition {
id String @id @default(uuid()) @db.Uuid
name String
entity_type String
definition_json Json
version Int
is_active Boolean @default(true)
published_at DateTime?
archived_at DateTime?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
workflow_instances WorkflowInstance[]
@@unique([entity_type, version])
@@index([entity_type, is_active])
@@index([entity_type, archived_at], map: "idx_workflow_definitions_entity_archived")
@@map("workflow_definitions")
}
model WorkflowInstance {
id String @id @default(uuid()) @db.Uuid
workflow_id String @db.Uuid
entity_type String
entity_id String @db.Uuid
reimbursement_id String? @db.Uuid
leave_request_id String? @db.Uuid
purchase_request_id String? @db.Uuid
travel_request_id String? @db.Uuid
submitter_id String @db.Uuid
current_status WorkflowInstanceStatus @default(pending)
current_step_id String
context_data Json
started_at DateTime @default(now())
completed_at DateTime?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
workflow_definition WorkflowDefinition @relation(fields: [workflow_id], references: [id], onDelete: Restrict)
reimbursement_form ReimbursementForm? @relation(fields: [reimbursement_id], references: [id], onDelete: Cascade)
leave_request LeaveRequest? @relation(fields: [leave_request_id], references: [id], onDelete: Cascade)
purchase_request PurchaseRequest? @relation(fields: [purchase_request_id], references: [id], onDelete: Cascade)
travel_request TravelRequest? @relation(fields: [travel_request_id], references: [id], onDelete: Cascade)
submitter Employee @relation("WorkflowInstanceSubmitter", fields: [submitter_id], references: [id], onDelete: Restrict)
department Department? @relation(fields: [context_department_id], references: [id], onDelete: SetNull)
workflow_tasks WorkflowTask[]
workflow_history WorkflowHistory[]
/// Denormalized for fast filtering (must match context_data.department_id)
context_department_id String? @db.Uuid
@@index([workflow_id])
@@index([entity_type, entity_id])
@@index([reimbursement_id])
@@index([leave_request_id])
@@index([purchase_request_id])
@@index([travel_request_id])
@@index([current_status])
@@index([submitter_id])
@@index([context_department_id])
@@map("workflow_instances")
}
model WorkflowTask {
id String @id @default(uuid()) @db.Uuid
instance_id String @db.Uuid
step_id String
step_name String
assignee_id String? @db.Uuid
assignee_role RoleCode?
status WorkflowTaskStatus @default(pending)
outcome WorkflowTaskOutcome?
comment String?
due_date DateTime?
created_at DateTime @default(now())
completed_at DateTime?
completed_by String? @db.Uuid
workflow_instance WorkflowInstance @relation(fields: [instance_id], references: [id], onDelete: Cascade)
assignee_employee Employee? @relation("WorkflowTaskAssignee", fields: [assignee_id], references: [id], onDelete: SetNull)
completer_employee Employee? @relation("WorkflowTaskCompleter", fields: [completed_by], references: [id], onDelete: SetNull)
workflow_history WorkflowHistory[]
@@index([instance_id])
@@index([status])
@@index([assignee_id])
@@index([assignee_role])
@@index([status, assignee_id, assignee_role], map: "idx_workflow_tasks_status_assignee")
@@index([status, assignee_id, created_at], map: "idx_workflow_tasks_status_assignee_id_created_at")
@@index([status, assignee_role, created_at], map: "idx_workflow_tasks_status_assignee_role_created_at")
@@map("workflow_tasks")
}
model WorkflowHistory {
id String @id @default(uuid()) @db.Uuid
instance_id String @db.Uuid
task_id String? @db.Uuid
actor_id String @db.Uuid
action String
comment String?
payload Json?
created_at DateTime @default(now())
workflow_instance WorkflowInstance @relation(fields: [instance_id], references: [id], onDelete: Cascade)
workflow_task WorkflowTask? @relation(fields: [task_id], references: [id], onDelete: SetNull)
actor Employee @relation("WorkflowHistoryActor", fields: [actor_id], references: [id], onDelete: Restrict)
@@index([instance_id, created_at])
@@index([task_id])
@@index([actor_id])
@@map("workflow_history")
}
model ApprovalRule {
id String @id @default(uuid()) @db.Uuid
name String
min_amount Decimal @db.Decimal(14, 2)
max_amount Decimal? @db.Decimal(14, 2)
level1_role RoleCode
level2_role RoleCode?
level3_role RoleCode?
enabled Boolean @default(true)
priority Int
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
@@index([enabled, priority])
@@map("approval_rules")
}
model ApprovalTransfer {
id String @id @default(uuid()) @db.Uuid
reimbursement_id String @db.Uuid
from_approver_id String @db.Uuid
to_approver_id String @db.Uuid
reason String
status ApprovalTransferStatus @default(active)
created_at DateTime @default(now())
created_by String? @db.Uuid
reimbursement_form ReimbursementForm @relation(fields: [reimbursement_id], references: [id], onDelete: Cascade)
from_approver Employee @relation("TransferFrom", fields: [from_approver_id], references: [id], onDelete: Restrict)
to_approver Employee @relation("TransferTo", fields: [to_approver_id], references: [id], onDelete: Restrict)
@@index([reimbursement_id])
@@index([from_approver_id])
@@index([to_approver_id])
@@map("approval_transfers")
}