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

3631 行
139 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 AssetType {
fixed_asset
electronic_document
electronic_system
code_repository
backup_media
}
enum AssetSensitivityLevel {
public_candidate
internal
confidential
strictly_confidential
}
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
mock
}
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 AttendanceCorrectionKind {
missed_punch
out
overtime
}
enum AttendanceCorrectionStatus {
reviewing
approved
rejected
cancelled
}
enum EmployeeContractType {
labor
nda
non_compete
internship
}
enum EmployeeTrainingType {
internal
external
online
}
enum EmployeeTrainingStatus {
planned
in_progress
completed
cancelled
}
enum SalaryItemType {
earning
deduction
}
enum PayslipStatus {
draft
issued
}
enum PerformanceReviewCycleType {
monthly
quarterly
semi_annual
annual
}
enum PerformanceReviewCycleStatus {
draft
active
completed
}
enum PerformanceReviewDimensionType {
work_quality
work_efficiency
teamwork
initiative
professional_skill
}
enum WeeklyReportStatus {
draft
submitted
}
enum TodoPriority {
low
normal
high
}
enum TodoStatus {
todo
doing
done
cancelled
}
enum NotificationDeliveryStatus {
pending
success
failed
}
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 ContractRegistryType {
purchase
sales
service
framework
other
}
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[]
attendance_corrections AttendanceCorrection[]
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 @default(dbgenerated("generate_employee_number()"))
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?
notification_push_enabled Boolean @default(true)
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")
reimbursement_attachments_uploaded ReimbursementAttachment[] @relation("ReimbursementAttachmentUploader")
payment_batch_receipts_uploaded PaymentBatchReceiptAttachment[] @relation("PaymentBatchReceiptAttachmentUploader")
leave_requests LeaveRequest[]
leave_ledgers LeaveLedger[]
wecom_checkin_records WecomCheckinRecord[]
wecom_checkin_daily_summaries WecomCheckinDailySummary[]
wecom_checkin_monthly_summaries WecomCheckinMonthlySummary[]
wecom_checkin_rule_snapshots WecomCheckinRuleSnapshot[]
wecom_checkin_sync_runs WecomCheckinSyncRun[] @relation("WecomCheckinSyncRunTrigger")
wecom_directory_sync_runs WecomDirectorySyncRun[] @relation("WecomDirectorySyncRunEmployee")
triggered_wecom_directory_sync_runs WecomDirectorySyncRun[] @relation("WecomDirectorySyncRunTrigger")
wecom_hr_master_sync_items WecomHrMasterSyncItem[] @relation("WecomHrMasterSyncItemEmployee")
triggered_wecom_hr_master_sync_runs WecomHrMasterSyncRun[] @relation("WecomHrMasterSyncRunTrigger")
wecom_approval_archives_applicant WecomApprovalArchive[] @relation("WecomApprovalArchiveApplicant")
wecom_approval_participants WecomApprovalParticipant[] @relation("WecomApprovalParticipantEmployee")
wecom_approval_actions_operator WecomApprovalAction[] @relation("WecomApprovalActionOperator")
attendance_corrections AttendanceCorrection[]
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")
owned_asset_cards AssetCard[] @relation("AssetCardOwner")
kept_asset_cards AssetCard[] @relation("AssetCardKeeper")
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[]
email_login_codes EmailLoginCode[]
lifecycle_events EmployeeLifecycleEvent[]
checklist_tasks EmployeeChecklistTask[]
portal_announcement_acknowledgements PortalAnnouncementAcknowledgement[] @relation("PortalAnnouncementAcknowledgementEmployee")
portal_announcement_acknowledgement_actions PortalAnnouncementAcknowledgement[] @relation("PortalAnnouncementAcknowledgementActor")
presence_records EmployeePresence[]
attendance_records AttendanceRecord[]
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[]
salary_profiles EmployeeSalary[]
payslips Payslip[]
performance_reviews_received PerformanceReview[] @relation("PerformanceReviewEmployee")
performance_reviews_authored PerformanceReview[] @relation("PerformanceReviewReviewer")
@@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: Restrict)
@@index([employee_id, created_at])
@@index([expires_at])
@@map("password_reset_codes")
}
model EmailLoginCode {
id String @id @default(uuid()) @db.Uuid
employee_id String @db.Uuid
code_hash String
expires_at DateTime
used_at DateTime?
attempts_count Int @default(0)
created_at DateTime @default(now())
created_ip String?
created_user_agent String?
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Restrict)
@@index([employee_id, created_at])
@@index([expires_at])
@@map("email_login_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: Restrict)
@@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)
deliveries NotificationDelivery[]
@@index([recipient_employee_id, created_at])
@@index([recipient_employee_id, read_at, created_at], map: "idx_notifications_unread_query")
@@map("notifications")
}
model NotificationDelivery {
id String @id @default(uuid()) @db.Uuid
notification_id String @db.Uuid
channel_id String @db.Uuid
status NotificationDeliveryStatus
attempt_no Int
result_code String? @db.VarChar(64)
error_message String?
response_body Json?
attempted_at DateTime @default(now())
notification Notification @relation(fields: [notification_id], references: [id], onDelete: Cascade)
channel NotificationChannel @relation(fields: [channel_id], references: [id], onDelete: Cascade)
@@index([notification_id, attempted_at], map: "idx_notification_deliveries_notification_attempted")
@@index([channel_id, attempted_at], map: "idx_notification_deliveries_channel_attempted")
@@index([status, attempted_at], map: "idx_notification_deliveries_status_attempted")
@@map("notification_deliveries")
}
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
standards ExpenseStandard[]
reimbursement_items ReimbursementItem[]
@@index([enabled, sort_order])
@@map("expense_types")
}
model ExpenseStandard {
id String @id @default(uuid()) @db.Uuid
expense_type_id String @db.Uuid
name String @db.VarChar(100)
max_amount Decimal @db.Decimal(14, 2)
per_unit String @default("item") @db.VarChar(20)
control_mode String @default("warn") @db.VarChar(16)
scope_type String @default("global") @db.VarChar(32)
scope_key String? @db.Uuid
enabled Boolean @default(true)
note String?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
expense_type ExpenseType @relation(fields: [expense_type_id], references: [id], onDelete: Restrict)
@@index([enabled, expense_type_id], map: "idx_expense_standards_enabled_type")
@@map("expense_standards")
}
model ReimbursementTemplate {
id String @id @default(uuid()) @db.Uuid
name String @db.VarChar(100)
description String?
items Json @default("[]")
sort_order Int @default(0)
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
@@index([enabled, sort_order])
@@map("reimbursement_templates")
}
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
project_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)
project Project? @relation(fields: [project_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([project_id], map: "idx_purchase_requests_project")
@@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
file_sha256 String?
uploaded_by String @db.Uuid
created_at DateTime @default(now())
governance_status String @default("approved") @db.VarChar(32)
sensitivity_level String @default("internal") @db.VarChar(32)
attachment_type String @default("other") @db.VarChar(64)
reviewed_by String? @db.Uuid
reviewed_at DateTime?
review_note String?
archived_by String? @db.Uuid
archived_at DateTime?
archive_note String?
download_policy String @default("standard") @db.VarChar(32)
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")
@@index([file_sha256], map: "idx_purchase_attachments_file_sha256")
@@index([governance_status], map: "idx_purchase_attachments_governance_status")
@@index([sensitivity_level], map: "idx_purchase_attachments_sensitivity_level")
@@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
asset_type AssetType @default(fixed_asset)
sensitivity_level AssetSensitivityLevel @default(internal)
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
owner_employee_id String? @db.Uuid
keeper_employee_id String? @db.Uuid
archive_location String?
external_ref String?
source_hash String?
verification_no String? @unique
confidentiality_note String?
metadata Json?
last_verified_at DateTime? @db.Date
expires_at DateTime? @db.Date
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)
owner Employee? @relation("AssetCardOwner", fields: [owner_employee_id], references: [id], onDelete: SetNull)
keeper Employee? @relation("AssetCardKeeper", fields: [keeper_employee_id], references: [id], onDelete: SetNull)
assignments AssetAssignment[]
transfers AssetTransfer[]
inventory_items InventoryCheckItem[]
@@index([asset_type], map: "idx_asset_cards_asset_type")
@@index([sensitivity_level], map: "idx_asset_cards_sensitivity")
@@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([owner_employee_id], map: "idx_asset_cards_owner")
@@index([keeper_employee_id], map: "idx_asset_cards_keeper")
@@index([source_hash], map: "idx_asset_cards_source_hash")
@@index([last_verified_at], map: "idx_asset_cards_last_verified")
@@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
linked_travel_request_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)
travel_request TravelRequest? @relation(fields: [linked_travel_request_id], references: [id], onDelete: SetNull)
items ReimbursementItem[]
attachments ReimbursementAttachment[]
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([linked_travel_request_id], map: "idx_reimbursement_forms_linked_travel_request")
@@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 ReimbursementAttachment {
id String @id @default(uuid()) @db.Uuid
reimbursement_id String @db.Uuid
storage_path String
original_name String
mime_type String
file_size Int
file_sha256 String
uploaded_by String @db.Uuid
created_at DateTime @default(now())
governance_status String @default("approved") @db.VarChar(32)
sensitivity_level String @default("internal") @db.VarChar(32)
attachment_type String @default("supporting_material") @db.VarChar(64)
reviewed_by String? @db.Uuid
reviewed_at DateTime?
review_note String?
archived_by String? @db.Uuid
archived_at DateTime?
archive_note String?
download_policy String @default("standard") @db.VarChar(32)
reimbursement_form ReimbursementForm @relation(fields: [reimbursement_id], references: [id], onDelete: Cascade)
uploader Employee @relation("ReimbursementAttachmentUploader", fields: [uploaded_by], references: [id], onDelete: Restrict)
@@index([reimbursement_id, created_at], map: "idx_reimbursement_attachments_form_created")
@@index([uploaded_by], map: "idx_reimbursement_attachments_uploader")
@@index([file_sha256], map: "idx_reimbursement_attachments_file_sha256")
@@index([governance_status], map: "idx_reimbursement_attachments_governance_status")
@@index([sensitivity_level], map: "idx_reimbursement_attachments_sensitivity_level")
@@map("reimbursement_attachments")
}
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[]
receipt_attachments PaymentBatchReceiptAttachment[]
@@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 PaymentBatchReceiptAttachment {
id String @id @default(uuid()) @db.Uuid
payment_batch_id String @db.Uuid
storage_path String
original_name String
mime_type String
file_size Int
file_sha256 String
uploaded_by String @db.Uuid
created_at DateTime @default(now())
governance_status String @default("approved") @db.VarChar(32)
sensitivity_level String @default("confidential") @db.VarChar(32)
attachment_type String @default("receipt") @db.VarChar(64)
reviewed_by String? @db.Uuid
reviewed_at DateTime?
review_note String?
archived_by String? @db.Uuid
archived_at DateTime?
archive_note String?
download_policy String @default("reason_required") @db.VarChar(32)
payment_batch PaymentBatch @relation(fields: [payment_batch_id], references: [id], onDelete: Cascade)
uploader Employee @relation("PaymentBatchReceiptAttachmentUploader", fields: [uploaded_by], references: [id], onDelete: Restrict)
@@index([payment_batch_id, created_at], map: "idx_payment_batch_receipts_batch_created")
@@index([uploaded_by], map: "idx_payment_batch_receipts_uploader")
@@index([file_sha256], map: "idx_payment_batch_receipts_file_sha256")
@@index([governance_status], map: "idx_payment_batch_receipts_governance_status")
@@index([sensitivity_level], map: "idx_payment_batch_receipts_sensitivity_level")
@@map("payment_batch_receipt_attachments")
}
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)
category String @default("general")
requires_acknowledgement Boolean @default(false)
acknowledgement_statement String?
acknowledgement_due_at DateTime?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
acknowledgements PortalAnnouncementAcknowledgement[]
@@index([enabled, priority, starts_at, ends_at], map: "idx_portal_announcements_enabled_priority")
@@map("portal_announcements")
}
model PortalAnnouncementAcknowledgement {
id String @id @default(uuid()) @db.Uuid
announcement_id String @db.Uuid
employee_id String @db.Uuid
acknowledged_at DateTime @default(now())
statement_snapshot String
title_snapshot String
summary_snapshot String
actor_id String @db.Uuid
created_at DateTime @default(now())
announcement PortalAnnouncement @relation(fields: [announcement_id], references: [id], onDelete: Cascade)
employee Employee @relation("PortalAnnouncementAcknowledgementEmployee", fields: [employee_id], references: [id], onDelete: Restrict)
actor Employee @relation("PortalAnnouncementAcknowledgementActor", fields: [actor_id], references: [id], onDelete: Restrict)
@@unique([announcement_id, employee_id])
@@index([employee_id, acknowledged_at])
@@index([announcement_id, acknowledged_at])
@@map("portal_announcement_acknowledgements")
}
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 AttendanceRecord {
id String @id @default(uuid()) @db.Uuid
employee_id String @db.Uuid
work_date DateTime @db.Date
clock_in_at DateTime?
clock_out_at DateTime?
work_minutes Int?
is_late Boolean @default(false)
late_minutes Int @default(0)
is_early_leave Boolean @default(false)
early_minutes Int @default(0)
note String?
source String @default("web") @db.VarChar(16)
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, work_date], map: "attendance_records_employee_date_ux")
@@index([work_date], map: "idx_attendance_records_work_date")
@@map("attendance_records")
}
model AttendancePolicy {
id String @id @default(uuid()) @db.Uuid
work_start_time String @default("09:00") @db.VarChar(5)
work_end_time String @default("18:00") @db.VarChar(5)
lunch_start_time String @default("12:00") @db.VarChar(5)
lunch_end_time String @default("13:00") @db.VarChar(5)
late_tolerance_minutes Int @default(0)
enabled Boolean @default(true)
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
@@map("attendance_policies")
}
model WecomCheckinSyncRun {
id String @id @default(uuid()) @db.Uuid
start_date DateTime @db.Date
end_date DateTime @db.Date
status String @default("running") @db.VarChar(24)
employee_count Int @default(0)
record_count Int @default(0)
error_code String? @db.VarChar(80)
error_message String?
triggered_by String? @db.Uuid
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
trigger Employee? @relation("WecomCheckinSyncRunTrigger", fields: [triggered_by], references: [id], onDelete: SetNull)
records WecomCheckinRecord[]
@@index([status, created_at], map: "idx_wecom_checkin_sync_runs_status_created")
@@index([start_date, end_date], map: "idx_wecom_checkin_sync_runs_range")
@@index([triggered_by], map: "idx_wecom_checkin_sync_runs_triggered_by")
@@map("wecom_checkin_sync_runs")
}
model WecomCheckinRecord {
id String @id @default(uuid()) @db.Uuid
sync_run_id String @db.Uuid
employee_id String @db.Uuid
external_user_id String @db.VarChar(128)
checkin_time DateTime
checkin_date DateTime @db.Date
checkin_type String @db.VarChar(40)
exception_type String? @db.VarChar(80)
source_type String? @db.VarChar(40)
result String? @db.VarChar(80)
raw_hash String @db.VarChar(64)
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
sync_run WecomCheckinSyncRun @relation(fields: [sync_run_id], references: [id], onDelete: Cascade)
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade)
@@unique([external_user_id, checkin_time, checkin_type], map: "wecom_checkin_records_user_time_type_ux")
@@index([employee_id, checkin_date], map: "idx_wecom_checkin_records_employee_date")
@@index([sync_run_id], map: "idx_wecom_checkin_records_sync_run")
@@map("wecom_checkin_records")
}
model WecomCheckinDailySummary {
id String @id @default(uuid()) @db.Uuid
employee_id String @db.Uuid
external_user_id_prefix String? @db.VarChar(32)
checkin_date DateTime @db.Date
record_count Int @default(0)
first_checkin_at DateTime?
last_checkin_at DateTime?
exception_count Int @default(0)
abnormal_count Int @default(0)
checkin_type_summary Json?
result_summary Json?
source_type_summary Json?
raw_hash_digest String? @db.VarChar(64)
source_record_count Int @default(0)
last_sync_run_id String? @db.Uuid
archived_at DateTime @default(now())
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade)
@@unique([employee_id, checkin_date], map: "wecom_checkin_daily_summaries_emp_date_ux")
@@index([checkin_date], map: "idx_wecom_checkin_daily_summaries_date")
@@index([last_sync_run_id], map: "idx_wecom_checkin_daily_summaries_run")
@@map("wecom_checkin_daily_summaries")
}
model WecomCheckinMonthlySummary {
id String @id @default(uuid()) @db.Uuid
employee_id String @db.Uuid
month String @db.VarChar(7)
start_date DateTime @db.Date
end_date DateTime @db.Date
summary_day_count Int @default(0)
checkin_day_count Int @default(0)
exception_day_count Int @default(0)
abnormal_record_count Int @default(0)
missing_day_candidate_count Int @default(0)
first_checkin_at DateTime?
last_checkin_at DateTime?
result_summary Json?
raw_hash_digest String? @db.VarChar(64)
archived_at DateTime @default(now())
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade)
@@unique([employee_id, month], map: "wecom_checkin_monthly_summaries_emp_month_ux")
@@index([month], map: "idx_wecom_checkin_monthly_summaries_month")
@@map("wecom_checkin_monthly_summaries")
}
model WecomCheckinRuleSnapshot {
id String @id @default(uuid()) @db.Uuid
employee_id String? @db.Uuid
external_user_id_prefix String? @db.VarChar(32)
snapshot_date DateTime @db.Date
rule_hash String @db.VarChar(64)
rule_summary Json?
source_endpoint String @default("/cgi-bin/checkin/getcheckinoption") @db.VarChar(120)
archived_at DateTime @default(now())
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
employee Employee? @relation(fields: [employee_id], references: [id], onDelete: SetNull)
@@unique([employee_id, snapshot_date, rule_hash], map: "wecom_checkin_rule_snapshots_emp_date_hash_ux")
@@index([snapshot_date], map: "idx_wecom_checkin_rule_snapshots_date")
@@index([rule_hash], map: "idx_wecom_checkin_rule_snapshots_hash")
@@map("wecom_checkin_rule_snapshots")
}
model WecomDirectorySyncRun {
id String @id @default(uuid()) @db.Uuid
employee_id String @db.Uuid
external_user_id String? @db.VarChar(128)
operation String @db.VarChar(32)
status String @default("running") @db.VarChar(24)
attempts Int @default(1)
error_code String? @db.VarChar(80)
error_message String?
response_summary Json?
triggered_by String? @db.Uuid
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
employee Employee @relation("WecomDirectorySyncRunEmployee", fields: [employee_id], references: [id], onDelete: Cascade)
trigger Employee? @relation("WecomDirectorySyncRunTrigger", fields: [triggered_by], references: [id], onDelete: SetNull)
@@index([employee_id, created_at], map: "idx_wecom_directory_sync_runs_employee_created")
@@index([status, created_at], map: "idx_wecom_directory_sync_runs_status_created")
@@index([operation, created_at], map: "idx_wecom_directory_sync_runs_operation_created")
@@index([triggered_by], map: "idx_wecom_directory_sync_runs_triggered_by")
@@map("wecom_directory_sync_runs")
}
model AttendanceCorrection {
id String @id @default(uuid()) @db.Uuid
owner_id String @db.Uuid
department_id String? @db.Uuid
kind AttendanceCorrectionKind
status AttendanceCorrectionStatus @default(reviewing)
work_date DateTime @db.Date
start_at DateTime?
end_at DateTime?
missed_punch_at DateTime?
reason String
location_summary String?
compensation_policy String?
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: Cascade)
department Department? @relation(fields: [department_id], references: [id], onDelete: SetNull)
workflow_instances WorkflowInstance[]
@@index([owner_id, work_date], map: "idx_attendance_corrections_owner_date")
@@index([department_id, status], map: "idx_attendance_corrections_department_status")
@@index([status, updated_at], map: "idx_attendance_corrections_status_updated")
@@map("attendance_corrections")
}
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 WecomHrMasterSyncRun {
id String @id @default(uuid()) @db.Uuid
mode String @db.VarChar(16)
status String @default("running") @db.VarChar(24)
token_source Json?
requested_userids Json?
limit Int?
summary Json?
error_code String? @db.VarChar(80)
error_message String?
triggered_by String? @db.Uuid
started_at DateTime @default(now())
finished_at DateTime?
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
trigger Employee? @relation("WecomHrMasterSyncRunTrigger", fields: [triggered_by], references: [id], onDelete: SetNull)
items WecomHrMasterSyncItem[]
@@index([status, created_at], map: "idx_wecom_hr_master_sync_runs_status_created")
@@index([mode, created_at], map: "idx_wecom_hr_master_sync_runs_mode_created")
@@index([triggered_by], map: "idx_wecom_hr_master_sync_runs_triggered_by")
@@map("wecom_hr_master_sync_runs")
}
model WecomHrMasterSyncItem {
id String @id @default(uuid()) @db.Uuid
run_id String @db.Uuid
employee_id String? @db.Uuid
external_user_id_masked String @db.VarChar(80)
action String @db.VarChar(32)
status String @db.VarChar(24)
error_code String? @db.VarChar(80)
error_message String?
retry_count Int @default(0)
field_changes Json?
source_summary Json?
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
run WecomHrMasterSyncRun @relation(fields: [run_id], references: [id], onDelete: Cascade)
employee Employee? @relation("WecomHrMasterSyncItemEmployee", fields: [employee_id], references: [id], onDelete: SetNull)
@@index([run_id, status], map: "idx_wecom_hr_master_sync_items_run_status")
@@index([employee_id, created_at], map: "idx_wecom_hr_master_sync_items_employee_created")
@@index([action, status], map: "idx_wecom_hr_master_sync_items_action_status")
@@map("wecom_hr_master_sync_items")
}
model WecomApprovalArchive {
id String @id @default(uuid()) @db.Uuid
corp_id_hash String @db.VarChar(64)
sp_no String @db.VarChar(160)
template_id String? @db.VarChar(160)
template_type String @default("unknown") @db.VarChar(40)
sp_name String? @db.VarChar(160)
normalized_status String @default("unknown") @db.VarChar(32)
wecom_status String? @db.VarChar(40)
applicant_employee_id String? @db.Uuid
applicant_external_user_id String? @db.VarChar(128)
submitted_at DateTime?
applied_at DateTime?
completed_at DateTime?
status_observed_at DateTime?
last_callback_at DateTime?
last_compensation_at DateTime?
last_source String? @db.VarChar(40)
last_detail_digest String? @db.VarChar(64)
readability_status String? @db.VarChar(40)
single_tap_approve_risk Boolean @default(false)
template_adjustment_required Boolean @default(false)
external_config_status String? @db.VarChar(40)
source_summary Json?
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
applicant Employee? @relation("WecomApprovalArchiveApplicant", fields: [applicant_employee_id], references: [id], onDelete: SetNull)
participants WecomApprovalParticipant[]
actions WecomApprovalAction[]
field_summaries WecomApprovalFieldSummary[]
attachment_summaries WecomApprovalAttachmentSummary[]
callback_events WecomApprovalCallbackEvent[]
compensation_items WecomApprovalCompensationItem[]
reconcile_findings WecomApprovalReconcileFinding[]
@@unique([corp_id_hash, sp_no], map: "wecom_approval_archives_corp_sp_no_ux")
@@index([applicant_employee_id, status_observed_at], map: "idx_wecom_approval_arch_applicant_status_at")
@@index([template_type, normalized_status], map: "idx_wecom_approval_arch_type_status")
@@index([last_source], map: "idx_wecom_approval_arch_last_source")
@@map("wecom_approval_archives")
}
model WecomApprovalParticipant {
id String @id @default(uuid()) @db.Uuid
archive_id String @db.Uuid
role String @db.VarChar(32)
node_index Int @default(0)
external_user_id String @db.VarChar(128)
employee_id String? @db.Uuid
is_current Boolean @default(false)
mapping_status String @default("unknown") @db.VarChar(32)
display_summary String?
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
archive WecomApprovalArchive @relation(fields: [archive_id], references: [id], onDelete: Cascade)
employee Employee? @relation("WecomApprovalParticipantEmployee", fields: [employee_id], references: [id], onDelete: SetNull)
@@unique([archive_id, role, node_index, external_user_id], map: "wecom_approval_participants_node_user_ux")
@@index([employee_id, role], map: "idx_wecom_approval_part_employee_role")
@@index([external_user_id], map: "idx_wecom_approval_part_external_user")
@@map("wecom_approval_participants")
}
model WecomApprovalAction {
id String @id @default(uuid()) @db.Uuid
archive_id String @db.Uuid
action_type String @db.VarChar(40)
operator_external_user_id String? @db.VarChar(128)
operator_employee_id String? @db.Uuid
action_time DateTime?
node_index Int @default(0)
comment_summary String?
source String @db.VarChar(40)
idempotency_key String @db.VarChar(96)
evidence_digest String? @db.VarChar(64)
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
archive WecomApprovalArchive @relation(fields: [archive_id], references: [id], onDelete: Cascade)
operator Employee? @relation("WecomApprovalActionOperator", fields: [operator_employee_id], references: [id], onDelete: SetNull)
@@unique([archive_id, idempotency_key], map: "wecom_approval_actions_archive_key_ux")
@@index([operator_employee_id, action_time], map: "idx_wecom_approval_actions_operator_time")
@@index([action_type, action_time], map: "idx_wecom_approval_actions_type_time")
@@map("wecom_approval_actions")
}
model WecomApprovalFieldSummary {
id String @id @default(uuid()) @db.Uuid
archive_id String @db.Uuid
field_key String @db.VarChar(120)
field_index Int @default(0)
field_name String? @db.VarChar(160)
field_type String? @db.VarChar(80)
normalized_value String?
value_preview String?
sensitive_level String @default("normal") @db.VarChar(32)
display_allowed Boolean @default(false)
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
archive WecomApprovalArchive @relation(fields: [archive_id], references: [id], onDelete: Cascade)
@@unique([archive_id, field_key, field_index], map: "wecom_approval_fields_archive_key_ux")
@@index([field_key], map: "idx_wecom_approval_fields_key")
@@map("wecom_approval_field_summaries")
}
model WecomApprovalAttachmentSummary {
id String @id @default(uuid()) @db.Uuid
archive_id String @db.Uuid
attachment_index Int @default(0)
attachment_type String? @db.VarChar(80)
external_ref_summary String?
downloaded Boolean @default(false)
sensitive_level String @default("restricted") @db.VarChar(32)
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
archive WecomApprovalArchive @relation(fields: [archive_id], references: [id], onDelete: Cascade)
@@index([archive_id, attachment_index], map: "idx_wecom_approval_attach_archive_idx")
@@map("wecom_approval_attachment_summaries")
}
model WecomApprovalCallbackEvent {
id String @id @default(uuid()) @db.Uuid
archive_id String? @db.Uuid
corp_id_hash String @db.VarChar(64)
sp_no String @db.VarChar(160)
template_id String? @db.VarChar(160)
event_type String @db.VarChar(80)
callback_kind String @db.VarChar(80)
approval_type String @default("unknown") @db.VarChar(40)
normalized_status String @default("unknown") @db.VarChar(32)
wecom_status String? @db.VarChar(40)
received_at DateTime @default(now())
event_time DateTime?
apply_time DateTime?
processing_status String @default("accepted") @db.VarChar(40)
processing_error_code String? @db.VarChar(80)
processing_error_message String?
retry_count Int @default(0)
idempotency_key String @unique @db.VarChar(96)
event_digest String @db.VarChar(64)
source_summary Json?
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
archive WecomApprovalArchive? @relation(fields: [archive_id], references: [id], onDelete: SetNull)
@@index([sp_no], map: "idx_wecom_approval_callbacks_sp_no")
@@index([archive_id, received_at], map: "idx_wecom_approval_callbacks_archive_at")
@@index([processing_status, received_at], map: "idx_wecom_approval_callbacks_status_at")
@@map("wecom_approval_callback_events")
}
model WecomApprovalCompensationRun {
id String @id @default(uuid()) @db.Uuid
run_key String? @db.VarChar(64)
mode String @default("live_read") @db.VarChar(32)
status String @default("running") @db.VarChar(32)
start_time DateTime
end_time DateTime
template_filter Json?
cursor_summary String?
page_size Int @default(100)
page_count Int @default(0)
item_count Int @default(0)
detail_count Int @default(0)
finding_count Int @default(0)
error_code String? @db.VarChar(80)
error_message String?
started_at DateTime @default(now())
finished_at DateTime?
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
items WecomApprovalCompensationItem[]
findings WecomApprovalReconcileFinding[]
@@index([run_key], map: "idx_wecom_approval_comp_runs_key")
@@index([status, started_at], map: "idx_wecom_approval_comp_runs_status")
@@index([start_time, end_time], map: "idx_wecom_approval_comp_runs_window")
@@map("wecom_approval_compensation_runs")
}
model WecomApprovalCompensationItem {
id String @id @default(uuid()) @db.Uuid
run_id String @db.Uuid
archive_id String? @db.Uuid
sp_no String @db.VarChar(160)
template_id String? @db.VarChar(160)
template_type String @default("unknown") @db.VarChar(40)
normalized_status String @default("unknown") @db.VarChar(32)
detail_status String @default("pending") @db.VarChar(40)
detail_digest String? @db.VarChar(64)
error_code String? @db.VarChar(80)
error_message String?
source_summary Json?
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
run WecomApprovalCompensationRun @relation(fields: [run_id], references: [id], onDelete: Cascade)
archive WecomApprovalArchive? @relation(fields: [archive_id], references: [id], onDelete: SetNull)
@@unique([run_id, sp_no], map: "wecom_approval_comp_items_run_sp_no_ux")
@@index([archive_id], map: "idx_wecom_approval_comp_items_archive")
@@index([detail_status], map: "idx_wecom_approval_comp_items_status")
@@map("wecom_approval_compensation_items")
}
model WecomApprovalReconcileFinding {
id String @id @default(uuid()) @db.Uuid
archive_id String? @db.Uuid
run_id String? @db.Uuid
finding_type String @db.VarChar(80)
status String @default("open") @db.VarChar(32)
severity String @default("info") @db.VarChar(32)
evidence_digest String @db.VarChar(64)
source String @db.VarChar(40)
description String?
detected_at DateTime @default(now())
resolved_at DateTime?
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
archive WecomApprovalArchive? @relation(fields: [archive_id], references: [id], onDelete: SetNull)
run WecomApprovalCompensationRun? @relation(fields: [run_id], references: [id], onDelete: SetNull)
@@unique([archive_id, finding_type, evidence_digest], map: "wecom_approval_findings_archive_digest_ux")
@@unique([run_id, finding_type, evidence_digest], map: "wecom_approval_findings_run_digest_ux")
@@index([status, detected_at], map: "idx_wecom_approval_findings_status_at")
@@index([run_id], map: "idx_wecom_approval_findings_run")
@@map("wecom_approval_reconcile_findings")
}
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[]
purchase_requests PurchaseRequest[]
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
contract_type ContractRegistryType @default(other)
counterparty_name String
summary String
amount Decimal? @db.Decimal(14, 2)
currency String @default("CNY") @db.VarChar(8)
signed_date DateTime? @db.Date
effective_date DateTime? @db.Date
expiry_date DateTime? @db.Date
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([status, expiry_date], map: "idx_contract_registry_status_expiry")
@@index([contract_type, status], map: "idx_contract_registry_type_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 SalaryItem {
id String @id @default(uuid()) @db.Uuid
name String @unique
item_type SalaryItemType
sort_order Int
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
employee_salaries EmployeeSalary[]
payslip_items PayslipItem[]
@@index([enabled, sort_order], map: "idx_salary_items_enabled_sort")
@@map("salary_items")
}
model EmployeeSalary {
id String @id @default(uuid()) @db.Uuid
employee_id String @db.Uuid
salary_item_id String @db.Uuid
monthly_amount Decimal @db.Decimal(12, 2)
effective_date DateTime @db.Date
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)
salary_item SalaryItem @relation(fields: [salary_item_id], references: [id], onDelete: Restrict)
@@unique([employee_id, salary_item_id, effective_date], map: "employee_salaries_employee_item_effective_ux")
@@index([employee_id, effective_date], map: "idx_employee_salaries_employee_effective")
@@index([salary_item_id], map: "idx_employee_salaries_salary_item")
@@map("employee_salaries")
}
model Payslip {
id String @id @default(uuid()) @db.Uuid
employee_id String @db.Uuid
pay_month DateTime @db.Date
status PayslipStatus @default(draft)
gross_amount Decimal @default(0) @db.Decimal(12, 2)
deduction_amount Decimal @default(0) @db.Decimal(12, 2)
net_amount Decimal @default(0) @db.Decimal(12, 2)
issued_at DateTime?
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)
items PayslipItem[]
@@unique([employee_id, pay_month], map: "payslips_employee_month_ux")
@@index([pay_month, status], map: "idx_payslips_month_status")
@@index([employee_id, pay_month], map: "idx_payslips_employee_month")
@@map("payslips")
}
model PayslipItem {
id String @id @default(uuid()) @db.Uuid
payslip_id String @db.Uuid
salary_item_id String @db.Uuid
amount Decimal @default(0) @db.Decimal(12, 2)
payslip Payslip @relation(fields: [payslip_id], references: [id], onDelete: Cascade)
salary_item SalaryItem @relation(fields: [salary_item_id], references: [id], onDelete: Restrict)
@@unique([payslip_id, salary_item_id], map: "payslip_items_payslip_salary_item_ux")
@@index([salary_item_id], map: "idx_payslip_items_salary_item")
@@map("payslip_items")
}
model PerformanceReviewCycle {
id String @id @default(uuid()) @db.Uuid
name String
cycle_type PerformanceReviewCycleType
start_date DateTime @db.Date
end_date DateTime @db.Date
status PerformanceReviewCycleStatus @default(draft)
activated_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
reviews PerformanceReview[]
@@index([status, start_date], map: "idx_performance_review_cycles_status_start")
@@index([status, completed_at], map: "idx_performance_review_cycles_status_completed")
@@map("performance_review_cycles")
}
model PerformanceReview {
id String @id @default(uuid()) @db.Uuid
cycle_id String @db.Uuid
employee_id String @db.Uuid
reviewer_employee_id String @db.Uuid
overall_score Decimal @db.Decimal(3, 1)
comment String?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
cycle PerformanceReviewCycle @relation(fields: [cycle_id], references: [id], onDelete: Cascade)
employee Employee @relation("PerformanceReviewEmployee", fields: [employee_id], references: [id], onDelete: Cascade)
reviewer_employee Employee @relation("PerformanceReviewReviewer", fields: [reviewer_employee_id], references: [id], onDelete: Restrict)
dimensions PerformanceReviewDimension[]
@@unique([cycle_id, employee_id], map: "performance_reviews_cycle_employee_ux")
@@index([cycle_id, reviewer_employee_id], map: "idx_performance_reviews_cycle_reviewer")
@@index([employee_id, created_at], map: "idx_performance_reviews_employee_created")
@@map("performance_reviews")
}
model PerformanceReviewDimension {
id String @id @default(uuid()) @db.Uuid
review_id String @db.Uuid
dimension_type PerformanceReviewDimensionType
score Decimal @db.Decimal(3, 1)
review PerformanceReview @relation(fields: [review_id], references: [id], onDelete: Cascade)
@@unique([review_id, dimension_type], map: "performance_review_dimensions_review_dimension_ux")
@@index([dimension_type, score], map: "idx_performance_review_dimensions_dimension_score")
@@map("performance_review_dimensions")
}
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[]
linked_reimbursements ReimbursementForm[]
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
deliveries NotificationDelivery[]
@@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
attendance_correction_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)
attendance_correction AttendanceCorrection? @relation(fields: [attendance_correction_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([attendance_correction_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?
owner_self_fallback_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")
}