generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" } enum RoleCode { employee manager ceo finance finance_mgr administration hr admin } enum EmployeeStatus { active archived } enum DelegationGrantScope { reimbursement_submit all } enum ReimbursementStatus { draft reviewing approved paid rejected cancelled } enum LeaveRequestStatus { draft reviewing approved rejected cancelled } enum PurchaseRequestStatus { draft reviewing approved rejected cancelled } enum TravelRequestStatus { draft reviewing approved rejected } enum TravelTripType { domestic international } enum TravelSegmentRole { outbound return connection } enum TravelTransportType { flight train car bus other } enum TravelReconciliationBatchStatus { draft reconciled } enum TravelReconciliationItemKind { order orphan_transaction } enum TravelReconciliationMatchStatus { matched amount_mismatch currency_mismatch missing_transaction duplicate_charge orphan_transaction reversed_after_payment } enum TravelReconciliationResolutionStatus { pending confirmed } enum AssetCardStatus { in_stock in_use under_repair retired archived } enum InventoryCheckScopeType { all legal_entity cost_center location } enum InventoryCheckStatus { draft counting completed } enum InventoryCheckResult { matched missing extra damaged } enum LeaveDaySession { am pm } enum LeaveLedgerEntryType { grant reserve consume release adjust } enum InvoicePoolStatus { pending_ocr ocr_failed available bound archived } enum InvoiceArchivedReason { used_complete unused_timeout manual } enum OcrProvider { tencent 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 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[] 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[] owned_cost_centers CostCenter[] @relation("CostCenterOwner") purchase_requests_applicant PurchaseRequest[] @relation("PurchaseRequestApplicant") travel_requests_applicant TravelRequest[] @relation("TravelRequestApplicant") travel_requests_submitted TravelRequest[] @relation("TravelRequestSubmitter") purchase_requests_submitted PurchaseRequest[] @relation("PurchaseRequestSubmitter") purchase_attachments_uploaded PurchaseAttachment[] @relation("PurchaseAttachmentUploader") purchase_order_archives PurchaseOrderArchive[] @relation("PurchaseOrderArchiveArchivedBy") assigned_asset_cards AssetCard[] @relation("AssetCardAssignee") asset_assignments AssetAssignment[] asset_transfers_from AssetTransfer[] @relation("AssetTransferFromEmployee") asset_transfers_to AssetTransfer[] @relation("AssetTransferToEmployee") owned_invoices InvoicePool[] @relation("InvoiceOwner") uploaded_invoices InvoicePool[] @relation("InvoiceUploader") overridden_invoices InvoicePool[] @relation("InvoiceOverrideBy") requested_invoice_verifications InvoiceVerificationRecord[] @relation("InvoiceVerificationRequester") travel_orders TravelOrder[] @relation("TravelOrderEmployee") paid_travel_orders TravelOrder[] @relation("TravelOrderPaidBy") confirmed_travel_reconciliation_batches TravelReconciliationBatch[] @relation("TravelReconciliationBatchConfirmedBy") card_authorizations CardAuthorization[] card_transactions CardTransaction[] @relation("CardTransactionEmployee") freeze_suggested_card_transactions CardTransaction[] @relation("CardTransactionFreezeSuggestedBy") confirmed_travel_reconciliation_items TravelReconciliationItem[] @relation("TravelReconciliationItemConfirmedBy") workflow_instances_started WorkflowInstance[] @relation("WorkflowInstanceSubmitter") workflow_history_actions WorkflowHistory[] @relation("WorkflowHistoryActor") workflow_tasks_assigned WorkflowTask[] @relation("WorkflowTaskAssignee") workflow_tasks_completed WorkflowTask[] @relation("WorkflowTaskCompleter") approval_transfers_from ApprovalTransfer[] @relation("TransferFrom") approval_transfers_to ApprovalTransfer[] @relation("TransferTo") password_reset_codes PasswordResetCode[] lifecycle_events EmployeeLifecycleEvent[] checklist_tasks EmployeeChecklistTask[] presence_records EmployeePresence[] 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: Cascade) @@index([employee_id, created_at]) @@index([expires_at]) @@map("password_reset_codes") } model EmployeeRole { employee_id String @db.Uuid role_code RoleCode created_at DateTime @default(now()) created_by String? @db.Uuid employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade) @@id([employee_id, role_code]) @@map("employee_roles") } model DelegationGrant { id String @id @default(uuid()) @db.Uuid principal_employee_id String @db.Uuid delegate_employee_id String @db.Uuid scope DelegationGrantScope valid_from DateTime @default(now()) valid_to DateTime? revoked_at DateTime? created_at DateTime @default(now()) created_by String? @db.Uuid principal_employee Employee @relation("DelegationPrincipal", fields: [principal_employee_id], references: [id], onDelete: Restrict) delegate_employee Employee @relation("DelegationDelegate", fields: [delegate_employee_id], references: [id], onDelete: Restrict) @@index([principal_employee_id]) @@index([delegate_employee_id]) @@map("delegation_grants") } model AuthSession { id String @id @default(uuid()) @db.Uuid employee_id String @db.Uuid refresh_token_hash String device_fingerprint String ip_address String user_agent String created_at DateTime @default(now()) last_seen_at DateTime @default(now()) expires_at DateTime revoked_at DateTime? employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade) @@index([employee_id]) @@index([expires_at]) @@index([revoked_at]) @@map("auth_sessions") } model AuditLog { id String @id @default(uuid()) @db.Uuid request_id String actor_id String @db.Uuid action String target_type String target_id String? @db.Uuid ip_address String? user_agent String? extra Json created_at DateTime @default(now()) actor Employee @relation("AuditActor", fields: [actor_id], references: [id], onDelete: Restrict) @@index([request_id]) @@index([actor_id, created_at]) @@index([created_at, actor_id, action], map: "idx_audit_logs_query") @@index([target_type, created_at], map: "idx_audit_logs_target_type_created_at") @@map("audit_logs") } model Notification { id String @id @default(uuid()) @db.Uuid recipient_employee_id String @db.Uuid type String @db.VarChar(64) title String @db.VarChar(200) body String? target_type String? @db.VarChar(64) target_id String? @db.Uuid read_at DateTime? created_at DateTime @default(now()) recipient Employee @relation(fields: [recipient_employee_id], references: [id], onDelete: Cascade) 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 uploaded_by String @db.Uuid created_at DateTime @default(now()) purchase_request PurchaseRequest @relation(fields: [purchase_request_id], references: [id], onDelete: Cascade) uploader Employee @relation("PurchaseAttachmentUploader", fields: [uploaded_by], references: [id], onDelete: Restrict) @@index([purchase_request_id, created_at], map: "idx_purchase_attachments_request_created") @@index([uploaded_by], map: "idx_purchase_attachments_uploader") @@map("purchase_attachments") } model PurchaseOrderArchive { id String @id @default(uuid()) @db.Uuid purchase_request_id String @unique @db.Uuid po_no String contract_no String? archived_files Json? archived_at DateTime @default(now()) archived_by String @db.Uuid purchase_request PurchaseRequest @relation(fields: [purchase_request_id], references: [id], onDelete: Cascade) archived_by_employee Employee @relation("PurchaseOrderArchiveArchivedBy", fields: [archived_by], references: [id], onDelete: Restrict) @@index([archived_by], map: "idx_purchase_order_archives_archived_by") @@index([archived_at], map: "idx_purchase_order_archives_archived_at") @@map("purchase_order_archives") } model AssetCategory { id String @id @default(uuid()) @db.Uuid code String @unique name String document_category_id String? @db.Uuid enabled Boolean @default(true) archived_at DateTime? created_at DateTime @default(now()) created_by String? @db.Uuid updated_at DateTime @default(now()) @updatedAt updated_by String? @db.Uuid document_category DocumentCategory? @relation(fields: [document_category_id], references: [id], onDelete: SetNull) asset_cards AssetCard[] @@index([enabled, archived_at], map: "idx_asset_categories_status") @@index([document_category_id], map: "idx_asset_categories_document_category") @@map("asset_categories") } model AssetCard { id String @id @default(uuid()) @db.Uuid asset_no String @unique name String category_id String @db.Uuid legal_entity_id String @db.Uuid cost_center_id String @db.Uuid purchase_request_id String? @db.Uuid serial_no String? location String? status AssetCardStatus @default(in_stock) assignee_employee_id String? @db.Uuid activated_at DateTime? @db.Date retired_at DateTime? @db.Date created_at DateTime @default(now()) created_by String? @db.Uuid updated_at DateTime @default(now()) @updatedAt updated_by String? @db.Uuid category AssetCategory @relation(fields: [category_id], references: [id], onDelete: Restrict) legal_entity LegalEntity @relation(fields: [legal_entity_id], references: [id], onDelete: Restrict) cost_center CostCenter @relation(fields: [cost_center_id], references: [id], onDelete: Restrict) purchase_request PurchaseRequest? @relation(fields: [purchase_request_id], references: [id], onDelete: SetNull) assignee Employee? @relation("AssetCardAssignee", fields: [assignee_employee_id], references: [id], onDelete: SetNull) assignments AssetAssignment[] transfers AssetTransfer[] inventory_items InventoryCheckItem[] @@index([category_id], map: "idx_asset_cards_category") @@index([legal_entity_id], map: "idx_asset_cards_legal_entity") @@index([cost_center_id], map: "idx_asset_cards_cost_center") @@index([purchase_request_id], map: "idx_asset_cards_purchase_request") @@index([assignee_employee_id], map: "idx_asset_cards_assignee") @@index([status], map: "idx_asset_cards_status") @@map("asset_cards") } model AssetAssignment { id String @id @default(uuid()) @db.Uuid asset_id String @db.Uuid employee_id String @db.Uuid assigned_at DateTime @default(now()) returned_at DateTime? note String? asset AssetCard @relation(fields: [asset_id], references: [id], onDelete: Cascade) employee Employee @relation(fields: [employee_id], references: [id], onDelete: Restrict) @@index([asset_id, returned_at], map: "idx_asset_assignments_asset_open") @@index([employee_id, returned_at], map: "idx_asset_assignments_employee_open") @@map("asset_assignments") } model AssetTransfer { id String @id @default(uuid()) @db.Uuid asset_id String @db.Uuid from_employee_id String? @db.Uuid to_employee_id String? @db.Uuid from_location String? to_location String? transferred_at DateTime @default(now()) note String? asset AssetCard @relation(fields: [asset_id], references: [id], onDelete: Cascade) from_employee Employee? @relation("AssetTransferFromEmployee", fields: [from_employee_id], references: [id], onDelete: SetNull) to_employee Employee? @relation("AssetTransferToEmployee", fields: [to_employee_id], references: [id], onDelete: SetNull) @@index([asset_id, transferred_at], map: "idx_asset_transfers_asset_time") @@index([from_employee_id], map: "idx_asset_transfers_from_employee") @@index([to_employee_id], map: "idx_asset_transfers_to_employee") @@map("asset_transfers") } model InventoryCheck { id String @id @default(uuid()) @db.Uuid check_no String @unique scope_type InventoryCheckScopeType scope_ref_id String? @db.VarChar(200) status InventoryCheckStatus @default(draft) started_at DateTime? completed_at DateTime? created_at DateTime @default(now()) created_by String? @db.Uuid updated_at DateTime @default(now()) @updatedAt updated_by String? @db.Uuid items InventoryCheckItem[] @@index([status, created_at], map: "idx_inventory_checks_status_created") @@index([scope_type, scope_ref_id], map: "idx_inventory_checks_scope") @@map("inventory_checks") } model InventoryCheckItem { id String @id @default(uuid()) @db.Uuid inventory_check_id String @db.Uuid asset_id String @db.Uuid result InventoryCheckResult note String? inventory_check InventoryCheck @relation(fields: [inventory_check_id], references: [id], onDelete: Cascade) asset AssetCard @relation(fields: [asset_id], references: [id], onDelete: Restrict) @@unique([inventory_check_id, asset_id], map: "inventory_check_asset_ux") @@index([asset_id], map: "idx_inventory_check_items_asset") @@map("inventory_check_items") } model ReimbursementForm { id String @id @default(uuid()) @db.Uuid form_number String? @unique submitter_id String @db.Uuid owner_id String @db.Uuid department_id String @db.Uuid reimbursement_date DateTime @db.Date total_amount Decimal @default(0) @db.Decimal(14, 2) status ReimbursementStatus @default(draft) description String? payment_voucher_no String? @db.VarChar(50) payment_date DateTime? @db.Date payment_channel String? @db.VarChar(20) paid_at DateTime? paid_by String? @db.Uuid project_id String? @db.Uuid 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()) 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") @@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()) 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") @@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) created_at DateTime @default(now()) created_by String? @db.Uuid updated_at DateTime @default(now()) @updatedAt updated_by String? @db.Uuid @@index([enabled, priority, starts_at, ends_at], map: "idx_portal_announcements_enabled_priority") @@map("portal_announcements") } model WorkspaceSetting { id String @id @default(uuid()) @db.Uuid profile WorkspaceProfile @default(small_team_internal) org_mode WorkspaceOrgMode @default(single_company) primary_entry_mode WorkspacePrimaryEntryMode @default(password) notification_strategy WorkspaceNotificationStrategy @default(in_app) device_priority WorkspaceDevicePriority @default(desktop) module_visibility_json Json? created_at DateTime @default(now()) updated_at DateTime @default(now()) @updatedAt updated_by String? @db.Uuid @@map("workspace_settings") } model EmployeeLifecycleEvent { id String @id @default(uuid()) @db.Uuid employee_id String @db.Uuid event_type EmployeeLifecycleEventType effective_date DateTime @db.Date payload_json Json? created_at DateTime @default(now()) created_by String? @db.Uuid employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade) @@index([employee_id, effective_date], map: "idx_employee_lifecycle_events_employee_date") @@index([event_type, created_at], map: "idx_employee_lifecycle_events_type_created") @@map("employee_lifecycle_events") } model EmployeeChecklistTask { id String @id @default(uuid()) @db.Uuid employee_id String @db.Uuid scene EmployeeChecklistScene task_type EmployeeChecklistTaskType title String status EmployeeChecklistTaskStatus @default(pending) due_date DateTime? @db.Date completed_at DateTime? completed_by String? @db.Uuid payload_json Json? created_at DateTime @default(now()) employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade) @@index([employee_id, scene, status], map: "idx_employee_checklist_tasks_employee_scene_status") @@index([scene, due_date], map: "idx_employee_checklist_tasks_scene_due") @@map("employee_checklist_tasks") } model EmployeePresence { id String @id @default(uuid()) @db.Uuid employee_id String @db.Uuid date DateTime @db.Date status EmployeePresenceStatus time_range EmployeePresenceTimeRange @default(full_day) note String? source EmployeePresenceSource @default(manual) created_at DateTime @default(now()) updated_at DateTime @default(now()) @updatedAt updated_by String? @db.Uuid employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade) @@unique([employee_id, date], map: "employee_presence_employee_date_ux") @@index([date, status], map: "idx_employee_presence_date_status") @@map("employee_presence") } model 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 WeeklyReport { id String @id @default(uuid()) @db.Uuid employee_id String @db.Uuid week_start DateTime @db.Date status WeeklyReportStatus @default(draft) highlights_text String @default("") blockers_text String @default("") next_week_text String @default("") needs_help Boolean @default(false) submitted_at DateTime? created_at DateTime @default(now()) updated_at DateTime @default(now()) @updatedAt employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade) project_links ProjectLinkOnWeeklyReport[] @@unique([employee_id, week_start], map: "weekly_reports_employee_week_start_ux") @@index([week_start, status], map: "idx_weekly_reports_week_status") @@map("weekly_reports") } model TodoItem { id String @id @default(uuid()) @db.Uuid title String description String? assignee_employee_id String @db.Uuid created_by String @db.Uuid due_date DateTime? @db.Date priority TodoPriority @default(normal) status TodoStatus @default(todo) source TodoSource @default(manual) project_id String? @db.Uuid created_at DateTime @default(now()) updated_at DateTime @default(now()) @updatedAt completed_at DateTime? assignee Employee @relation("TodoAssignee", fields: [assignee_employee_id], references: [id], onDelete: Restrict) creator Employee @relation("TodoCreator", fields: [created_by], references: [id], onDelete: Restrict) project Project? @relation(fields: [project_id], references: [id], onDelete: SetNull) @@index([assignee_employee_id, status, due_date], map: "idx_todo_items_assignee_status_due") @@index([project_id, status], map: "idx_todo_items_project_status") @@index([created_by, created_at], map: "idx_todo_items_creator_created") @@map("todo_items") } model WecomIdentityBinding { id String @id @default(uuid()) @db.Uuid employee_id String @unique @db.Uuid external_user_id String @unique @db.VarChar(128) external_open_user_id String? @db.VarChar(128) created_at DateTime @default(now()) created_by String? @db.Uuid updated_at DateTime @default(now()) @updatedAt updated_by String? @db.Uuid employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade) @@index([external_user_id], map: "idx_wecom_identity_bindings_external_user") @@map("wecom_identity_bindings") } model Project { id String @id @default(uuid()) @db.Uuid project_no String @unique name String client_name String client_alias String? owner_employee_id String @db.Uuid manager_employee_id String? @db.Uuid status ProjectStatus @default(draft) starts_at DateTime? @db.Date target_end_at DateTime? @db.Date actual_end_at DateTime? @db.Date summary String? is_confidential Boolean @default(false) created_at DateTime @default(now()) updated_at DateTime @default(now()) @updatedAt owner Employee @relation("ProjectOwner", fields: [owner_employee_id], references: [id], onDelete: Restrict) manager Employee? @relation("ProjectManager", fields: [manager_employee_id], references: [id], onDelete: SetNull) members ProjectMember[] milestones ProjectMilestone[] tasks ProjectTask[] risks ProjectRisk[] reimbursements ReimbursementForm[] 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 submitter_id String @db.Uuid current_status WorkflowInstanceStatus @default(pending) current_step_id String context_data Json started_at DateTime @default(now()) completed_at DateTime? created_at DateTime @default(now()) created_by String? @db.Uuid updated_at DateTime @default(now()) @updatedAt updated_by String? @db.Uuid workflow_definition WorkflowDefinition @relation(fields: [workflow_id], references: [id], onDelete: Restrict) reimbursement_form ReimbursementForm? @relation(fields: [reimbursement_id], references: [id], onDelete: Cascade) leave_request LeaveRequest? @relation(fields: [leave_request_id], references: [id], onDelete: Cascade) purchase_request PurchaseRequest? @relation(fields: [purchase_request_id], references: [id], onDelete: Cascade) travel_request TravelRequest? @relation(fields: [travel_request_id], references: [id], onDelete: Cascade) submitter Employee @relation("WorkflowInstanceSubmitter", fields: [submitter_id], references: [id], onDelete: Restrict) department Department? @relation(fields: [context_department_id], references: [id], onDelete: SetNull) workflow_tasks WorkflowTask[] workflow_history WorkflowHistory[] /// Denormalized for fast filtering (must match context_data.department_id) context_department_id String? @db.Uuid @@index([workflow_id]) @@index([entity_type, entity_id]) @@index([reimbursement_id]) @@index([leave_request_id]) @@index([purchase_request_id]) @@index([travel_request_id]) @@index([current_status]) @@index([submitter_id]) @@index([context_department_id]) @@map("workflow_instances") } model WorkflowTask { id String @id @default(uuid()) @db.Uuid instance_id String @db.Uuid step_id String step_name String assignee_id String? @db.Uuid assignee_role RoleCode? status WorkflowTaskStatus @default(pending) outcome WorkflowTaskOutcome? comment String? due_date DateTime? created_at DateTime @default(now()) completed_at DateTime? completed_by String? @db.Uuid workflow_instance WorkflowInstance @relation(fields: [instance_id], references: [id], onDelete: Cascade) assignee_employee Employee? @relation("WorkflowTaskAssignee", fields: [assignee_id], references: [id], onDelete: SetNull) completer_employee Employee? @relation("WorkflowTaskCompleter", fields: [completed_by], references: [id], onDelete: SetNull) workflow_history WorkflowHistory[] @@index([instance_id]) @@index([status]) @@index([assignee_id]) @@index([assignee_role]) @@index([status, assignee_id, assignee_role], map: "idx_workflow_tasks_status_assignee") @@index([status, assignee_id, created_at], map: "idx_workflow_tasks_status_assignee_id_created_at") @@index([status, assignee_role, created_at], map: "idx_workflow_tasks_status_assignee_role_created_at") @@map("workflow_tasks") } model WorkflowHistory { id String @id @default(uuid()) @db.Uuid instance_id String @db.Uuid task_id String? @db.Uuid actor_id String @db.Uuid action String comment String? payload Json? created_at DateTime @default(now()) workflow_instance WorkflowInstance @relation(fields: [instance_id], references: [id], onDelete: Cascade) workflow_task WorkflowTask? @relation(fields: [task_id], references: [id], onDelete: SetNull) actor Employee @relation("WorkflowHistoryActor", fields: [actor_id], references: [id], onDelete: Restrict) @@index([instance_id, created_at]) @@index([task_id]) @@index([actor_id]) @@map("workflow_history") } model ApprovalRule { id String @id @default(uuid()) @db.Uuid name String min_amount Decimal @db.Decimal(14, 2) max_amount Decimal? @db.Decimal(14, 2) level1_role RoleCode level2_role RoleCode? level3_role RoleCode? enabled Boolean @default(true) priority Int created_at DateTime @default(now()) created_by String? @db.Uuid updated_at DateTime @default(now()) @updatedAt updated_by String? @db.Uuid @@index([enabled, priority]) @@map("approval_rules") } model ApprovalTransfer { id String @id @default(uuid()) @db.Uuid reimbursement_id String @db.Uuid from_approver_id String @db.Uuid to_approver_id String @db.Uuid reason String status ApprovalTransferStatus @default(active) created_at DateTime @default(now()) created_by String? @db.Uuid reimbursement_form ReimbursementForm @relation(fields: [reimbursement_id], references: [id], onDelete: Cascade) from_approver Employee @relation("TransferFrom", fields: [from_approver_id], references: [id], onDelete: Restrict) to_approver Employee @relation("TransferTo", fields: [to_approver_id], references: [id], onDelete: Restrict) @@index([reimbursement_id]) @@index([from_approver_id]) @@index([to_approver_id]) @@map("approval_transfers") }