generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } model User { id String @id @default(uuid()) tenantId String @default("1") username String passwordHash String displayName String status String @default("ACTIVE") employeeId String? mustChangePassword Boolean @default(true) profileCompletedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt employee Employee? @relation(fields: [employeeId], references: [id]) roles UserRole[] refreshTokens RefreshToken[] todos TodoTask[] @relation("TodoAssignee") approvals ApprovalTask[] @relation("ApprovalAssignee") bidCreated BidCase[] @relation("BidCreator") bidReviews BidReviewer[] bidActions BidActionLog[] bidAssignments BidAssignee[] bidDocVersions BidDocVersion[] contractsCreated Contract[] @relation("ContractCreator") contractReviews ContractReviewer[] contractActions ContractActionLog[] expensesCreated ExpenseClaim[] @relation("ExpenseApplicant") expenseDecided ExpenseClaim[] @relation("ExpenseDecider") expensesPaid ExpenseClaim[] @relation("ExpensePayer") loansCreated LoanRecord[] @relation("LoanApplicant") loanDecided LoanRecord[] @relation("LoanDecider") invoicesCreated InvoiceRecord[] @relation("InvoiceCreator") timesheets Timesheet[] assignedTasks ProjectTask[] @relation("TaskAssignee") projectAcceptances ProjectAcceptance[] sealApplied SealRequest[] @relation("SealApplicant") sealDecided SealRequest[] @relation("SealDecider") credentialBorrows CredentialBorrow[] employmentEvents EmploymentEvent[] @relation("EventActor") attendanceImports AttendanceMonth[] @relation("AttendanceImporter") leaveDecided LeaveRequest[] @relation("LeaveDecider") payrollRuns PayrollRun[] @relation("PayrollCreator") workReports WorkReport[] workReportsTo WorkReport[] @relation("ReportTo") workReportsCc WorkReport[] @relation("ReportCc") operationLogs OperationLog[] purchases PurchaseRequest[] @relation("PurchaseApplicant") purchaseDecided PurchaseRequest[] @relation("PurchaseDecider") paymentsCreated PaymentRecord[] @relation("PaymentCreator") paymentsReceived PaymentRecord[] @relation("PaymentPayee") projectChanges ProjectChange[] @relation("ChangeActor") assetsHeld AssetOccupancy[] @relation("AssetHolder") laborContractsCreated LaborContract[] @relation("LaborContractCreator") workAssigned WorkAssignment[] @relation("WorkAssigner") workReceived WorkAssignment[] @relation("WorkAssignee") assetsRegistered RegisteredAsset[] @relation("AssetRegistrar") officeApplies OfficeApply[] @relation("OfficeApplicant") officeApplyDecided OfficeApply[] @relation("OfficeDecider") noticesCreated Notice[] @relation("NoticeAuthor") pushDevices PushDevice[] @@unique([tenantId, username]) } model PushDevice { id String @id @default(uuid()) userId String vendor String regId String brand String? model String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([vendor, regId]) @@index([userId, vendor]) } model RefreshToken { id String @id @default(uuid()) userId String tokenHash String clientKind String @default("web") expiresAt DateTime revokedAt DateTime? createdAt DateTime @default(now()) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId, clientKind]) } model AuthQrTicket { id String @id @default(uuid()) ticket String @unique status String @default("pending") userId String? confirmCodeHash String? confirmCodeIssuedAt DateTime? confirmCodeAttempts Int @default(0) expiresAt DateTime consumedAt DateTime? createdAt DateTime @default(now()) @@index([status, expiresAt]) } model Department { id String @id @default(uuid()) tenantId String @default("1") parentId String? name String code String deptType String categoryCode String? sortNo Int @default(0) parent Department? @relation("DeptTree", fields: [parentId], references: [id]) children Department[] @relation("DeptTree") employees Employee[] projectTasks ProjectTask[] eventsFrom EmploymentEvent[] @relation("EventFromDept") eventsTo EmploymentEvent[] @relation("EventToDept") @@unique([tenantId, code]) } model Employee { id String @id @default(uuid()) tenantId String @default("1") name String employeeNo String departmentId String title String? restSchedule String @default("SINGLE") employmentStatus String @default("REGULAR") hiredAt DateTime? internStartAt DateTime? probationEndAt DateTime? leftAt DateTime? managerId String? managerIds String? gender String? email String? mobile String? idNo String? idType String? education String? birthday DateTime? ethnicity String? maritalStatus String? hukouAddress String? liveAddress String? emergencyName String? emergencyPhone String? idIssuer String? idValidFrom DateTime? idValidTo DateTime? personCategory String? ccTo String? shortMobile String? workMobile String? school String? major String? graduatedAt DateTime? professionalTitle String? certificates String? companyName String? jobRole String? jobLevel String? attendGroup String? probationMonths Int? contractStartAt DateTime? contractEndAt DateTime? recruitSource String? landline String? landlineExt String? groupShortNo String? socialNo String? housingFundNo String? familyNote String? jobType String? ccDaily String? ccWeekly String? ccMonthly String? bankName String? bankAccount String? remark String? baseSalary Decimal @default(0) @db.Decimal(18, 2) postSalary Decimal @default(0) @db.Decimal(18, 2) attendanceBonus Decimal @default(0) @db.Decimal(18, 2) phoneSubsidy Decimal @default(0) @db.Decimal(18, 2) transportSubsidy Decimal @default(0) @db.Decimal(18, 2) mealSubsidy Decimal @default(0) @db.Decimal(18, 2) probationSalary Decimal @default(0) @db.Decimal(18, 2) perfBase Decimal @default(0) @db.Decimal(18, 2) socialBase Decimal @default(0) @db.Decimal(18, 2) compLeaveHours Decimal @default(0) @db.Decimal(10, 2) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt department Department @relation(fields: [departmentId], references: [id]) manager Employee? @relation("EmpManager", fields: [managerId], references: [id]) reports Employee[] @relation("EmpManager") users User[] events EmploymentEvent[] attendance AttendanceMonth[] punches AttendancePunch[] leaves LeaveRequest[] payrollItems PayrollItem[] laborContracts LaborContract[] @@unique([tenantId, employeeNo]) @@index([departmentId]) @@index([managerId]) } model LaborContract { id String @id @default(uuid()) tenantId String @default("1") employeeId String contractNo String? startAt DateTime? endAt DateTime? remark String? createdById String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade) createdBy User? @relation("LaborContractCreator", fields: [createdById], references: [id]) @@index([employeeId]) @@index([tenantId, createdAt]) } model EmploymentEvent { id String @id @default(uuid()) tenantId String @default("1") employeeId String kind String fromStatus String? toStatus String? fromDeptId String? toDeptId String? effectiveAt DateTime comment String? actorId String? createdAt DateTime @default(now()) employee Employee @relation(fields: [employeeId], references: [id]) fromDept Department? @relation("EventFromDept", fields: [fromDeptId], references: [id]) toDept Department? @relation("EventToDept", fields: [toDeptId], references: [id]) actor User? @relation("EventActor", fields: [actorId], references: [id]) @@index([employeeId]) @@index([effectiveAt]) } model AttendanceMonth { id String @id @default(uuid()) tenantId String @default("1") employeeId String yearMonth String workDays Decimal @default(0) @db.Decimal(6, 2) leaveDays Decimal @default(0) @db.Decimal(6, 2) sickDays Decimal @default(0) @db.Decimal(6, 2) overtimeHours Decimal @default(0) @db.Decimal(6, 2) compLeaveDays Decimal @default(0) @db.Decimal(6, 2) remark String? importedAt DateTime @default(now()) importedById String? employee Employee @relation(fields: [employeeId], references: [id]) importedBy User? @relation("AttendanceImporter", fields: [importedById], references: [id]) @@unique([employeeId, yearMonth]) @@index([yearMonth]) } model AttendancePunch { id String @id @default(uuid()) tenantId String @default("1") employeeId String kind String punchedAt DateTime @default(now()) remark String? lat Decimal? @db.Decimal(10, 7) lng Decimal? @db.Decimal(10, 7) address String? accuracy Float? employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade) @@index([employeeId, punchedAt]) } model LeaveRequest { id String @id @default(uuid()) tenantId String @default("1") employeeId String kind String startAt DateTime endAt DateTime days Decimal @db.Decimal(6, 2) reason String toDeptId String? targetTitle String? status String @default("PENDING") comment String? decidedById String? decidedAt DateTime? createdAt DateTime @default(now()) employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade) decidedBy User? @relation("LeaveDecider", fields: [decidedById], references: [id]) @@index([employeeId]) @@index([status]) } model HrSetting { id String @id @default("default") tenantId String @default("1") @unique idMask Boolean @default(true) autoAccount Boolean @default(false) probationMonths Int @default(3) defaultRest String @default("SINGLE") workStart String @default("09:00") workEnd String @default("18:00") updatedAt DateTime @updatedAt } model AttendanceRule { id String @id @default(uuid()) tenantId String @default("1") name String restSchedule String workStart String workEnd String lateGraceMinutes Int @default(0) punchPlace String? punchAddress String? punchLat Decimal? @db.Decimal(10, 7) punchLng Decimal? @db.Decimal(10, 7) punchRadiusMeters Int @default(300) enabled Boolean @default(true) sortNo Int @default(0) remark String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([tenantId, name]) } model PayrollRun { id String @id @default(uuid()) tenantId String @default("1") yearMonth String status String @default("DRAFT") createdById String? createdAt DateTime @default(now()) confirmedAt DateTime? createdBy User? @relation("PayrollCreator", fields: [createdById], references: [id]) items PayrollItem[] @@unique([tenantId, yearMonth]) } model PayrollItem { id String @id @default(uuid()) runId String employeeId String prorationFactor Decimal @default(1) @db.Decimal(8, 6) scheduledDays Decimal @default(0) @db.Decimal(6, 2) dailyRate Decimal @default(0) @db.Decimal(18, 2) basePay Decimal @default(0) @db.Decimal(18, 2) performancePay Decimal @default(0) @db.Decimal(18, 2) performanceRatio Decimal @default(100) @db.Decimal(5, 2) leaveDeduct Decimal @default(0) @db.Decimal(18, 2) sickDeduct Decimal @default(0) @db.Decimal(18, 2) socialPersonal Decimal @default(0) @db.Decimal(18, 2) housingFund Decimal @default(0) @db.Decimal(18, 2) tax Decimal @default(0) @db.Decimal(18, 2) grossPay Decimal @default(0) @db.Decimal(18, 2) netPay Decimal @default(0) @db.Decimal(18, 2) overtimeHours Decimal @default(0) @db.Decimal(6, 2) compLeaveUsedDays Decimal @default(0) @db.Decimal(6, 2) compLeaveHours Decimal @default(0) @db.Decimal(10, 2) run PayrollRun @relation(fields: [runId], references: [id], onDelete: Cascade) employee Employee @relation(fields: [employeeId], references: [id]) @@unique([runId, employeeId]) } model Role { id String @id @default(uuid()) tenantId String @default("1") name String code String description String @default("") dataScope String @default("ALL") aclCustom Boolean @default(false) permissions RolePermission[] users UserRole[] @@unique([tenantId, code]) } model Permission { id String @id @default(uuid()) parentId String? name String code String @unique permType String path String? icon String? sortNo Int @default(0) visible Boolean @default(true) parent Permission? @relation("PermTree", fields: [parentId], references: [id]) children Permission[] @relation("PermTree") roles RolePermission[] } model RolePermission { roleId String permissionId String role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade) @@id([roleId, permissionId]) } model UserRole { userId String roleId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) @@id([userId, roleId]) } model DictItem { id String @id @default(uuid()) tenantId String @default("1") dictType String label String value String sortNo Int @default(0) @@unique([tenantId, dictType, value]) } model NumberRule { id String @id @default(uuid()) tenantId String @default("1") code String name String prefix String padLength Int @default(5) @@unique([tenantId, code]) } model SealItem { id String @id @default(uuid()) tenantId String @default("1") name String sealType String @default("公章") keeper String @default("行政") legalEntityId String? status String @default("IN_STOCK") createdAt DateTime @default(now()) legalEntity LegalEntity? @relation(fields: [legalEntityId], references: [id]) } model LegalEntity { id String @id @default(uuid()) tenantId String @default("1") name String shortName String creditNo String? isDefault Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt bidCases BidCase[] contracts Contract[] sealItems SealItem[] assets RegisteredAsset[] @@unique([tenantId, name]) } model BusinessParty { id String @id @default(uuid()) tenantId String @default("1") name String creditNo String? level String @default("B") riskFlag Boolean @default(false) remark String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt contacts PartyContact[] bidCases BidCase[] @@unique([tenantId, name]) } model PartyContact { id String @id @default(uuid()) tenantId String @default("1") partyId String name String phone String? title String? party BusinessParty @relation(fields: [partyId], references: [id], onDelete: Cascade) } model Qualification { id String @id @default(uuid()) tenantId String @default("1") name String code String expiresAt DateTime? keeper String? createdAt DateTime @default(now()) borrows CredentialBorrow[] } model SealRequest { id String @id @default(uuid()) tenantId String @default("1") sealType String reason String bidCaseId String? contractId String? applicantId String? takeOut Boolean @default(false) returnAt DateTime? returnedAt DateTime? decidedAt DateTime? decidedById String? rejectComment String? status String @default("PENDING") createdAt DateTime @default(now()) contract Contract? @relation(fields: [contractId], references: [id]) bidCase BidCase? @relation(fields: [bidCaseId], references: [id]) applicant User? @relation("SealApplicant", fields: [applicantId], references: [id]) decidedBy User? @relation("SealDecider", fields: [decidedById], references: [id]) } model CredentialBorrow { id String @id @default(uuid()) tenantId String @default("1") itemName String borrower String borrowerId String? qualificationId String? bidCaseId String? dueAt DateTime? returnedAt DateTime? remark String? status String @default("BORROWED") createdAt DateTime @default(now()) borrowerUser User? @relation(fields: [borrowerId], references: [id]) qualification Qualification? @relation(fields: [qualificationId], references: [id]) bidCase BidCase? @relation(fields: [bidCaseId], references: [id]) } model BidCase { id String @id @default(uuid()) tenantId String @default("1") bidNo String name String externalNo String? projectType String sourceType String @default("FLOW") source String? qualificationNeed String? legalEntityId String? tenderMethod String? applyStartAt DateTime? applyEndAt DateTime? applyMethod String? openAt DateTime? openMethod String? openPlace String? securityLevel String @default("INTERNAL") projectNature String? partyId String? contactName String? contactPhone String? bondMethod String? bondStatus String? bondPaidAt DateTime? delivererName String? priceCap String? quoteAmount String? summary String? remark String? status String @default("TECH_INITIAL") nasPath String? fetchedAt DateTime? reviewCycle Int @default(1) printCost Decimal? @db.Decimal(18, 2) printNote String? printedAt DateTime? costDisposition String? resultNote String? winnerName String? winnerAmount Decimal? @db.Decimal(18, 2) winnerNote String? openedResultAt DateTime? needSurvey Boolean @default(false) createdById String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt legalEntity LegalEntity? @relation(fields: [legalEntityId], references: [id]) party BusinessParty? @relation(fields: [partyId], references: [id]) createdBy User? @relation("BidCreator", fields: [createdById], references: [id]) reviewers BidReviewer[] actionLogs BidActionLog[] assignees BidAssignee[] versions BidDocVersion[] expenses ExpenseClaim[] loans LoanRecord[] contracts Contract[] bonds BondRecord[] sealRequests SealRequest[] credentialBorrows CredentialBorrow[] @@index([tenantId, bidNo]) } model BidReviewer { id String @id @default(uuid()) bidCaseId String userId String stage String cycle Int @default(1) result String @default("PENDING") comment String? decidedAt DateTime? bidCase BidCase @relation(fields: [bidCaseId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id]) @@unique([bidCaseId, userId, stage, cycle]) @@index([bidCaseId]) } model BidAssignee { id String @id @default(uuid()) bidCaseId String userId String role String createdAt DateTime @default(now()) bidCase BidCase @relation(fields: [bidCaseId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id]) @@unique([bidCaseId, userId, role]) @@index([bidCaseId]) } model BidDocVersion { id String @id @default(uuid()) bidCaseId String versionNo Int nasPath String note String? submittedById String createdAt DateTime @default(now()) bidCase BidCase @relation(fields: [bidCaseId], references: [id], onDelete: Cascade) submittedBy User @relation(fields: [submittedById], references: [id]) @@unique([bidCaseId, versionNo]) @@index([bidCaseId]) } model BidActionLog { id String @id @default(uuid()) bidCaseId String actorId String action String fromStatus String? toStatus String? comment String? createdAt DateTime @default(now()) bidCase BidCase @relation(fields: [bidCaseId], references: [id], onDelete: Cascade) actor User @relation(fields: [actorId], references: [id]) @@index([bidCaseId]) } model TodoTask { id String @id @default(uuid()) tenantId String @default("1") title String bizType String bizId String? assigneeId String dueAt DateTime? status String @default("OPEN") createdAt DateTime @default(now()) assignee User @relation("TodoAssignee", fields: [assigneeId], references: [id]) } model ApprovalTask { id String @id @default(uuid()) tenantId String @default("1") title String bizType String bizId String? assigneeId String status String @default("PENDING") createdAt DateTime @default(now()) assignee User @relation("ApprovalAssignee", fields: [assigneeId], references: [id]) } model WorkReport { id String @id @default(uuid()) tenantId String @default("1") authorId String toUserId String? ccUserId String? period String periodType String @default("DAY") periodStart DateTime? hours Decimal @default(0) @db.Decimal(8, 2) content String @default("") status String @default("DRAFT") submittedAt DateTime? createdAt DateTime @default(now()) author User @relation(fields: [authorId], references: [id]) toUser User? @relation("ReportTo", fields: [toUserId], references: [id]) ccUser User? @relation("ReportCc", fields: [ccUserId], references: [id]) @@index([authorId]) @@index([toUserId]) @@index([ccUserId]) } model Contract { id String @id @default(uuid()) tenantId String @default("1") contractNo String? name String kind String @default("OTHER") bidCaseId String? legalEntityId String? amount Decimal @default(0) @db.Decimal(18, 2) status String @default("DRAFT") partyA String? partyB String? nasPath String? remark String? signedAt DateTime? submittedAt DateTime? approvedAt DateTime? createdById String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt bidCase BidCase? @relation(fields: [bidCaseId], references: [id]) legalEntity LegalEntity? @relation(fields: [legalEntityId], references: [id]) createdBy User? @relation("ContractCreator", fields: [createdById], references: [id]) projects Project[] milestones PaymentMilestone[] reviewers ContractReviewer[] actionLogs ContractActionLog[] sealRequests SealRequest[] invoices InvoiceRecord[] bonds BondRecord[] @@unique([tenantId, contractNo]) } model ContractReviewer { id String @id @default(uuid()) contractId String userId String result String @default("PENDING") comment String? decidedAt DateTime? contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id]) @@unique([contractId, userId]) @@index([contractId]) } model ContractActionLog { id String @id @default(uuid()) contractId String actorId String action String fromStatus String? toStatus String? comment String? createdAt DateTime @default(now()) contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade) actor User @relation(fields: [actorId], references: [id]) @@index([contractId]) } model PaymentMilestone { id String @id @default(uuid()) contractId String name String ratio Decimal @db.Decimal(5, 2) dueAt DateTime? trigger String? contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade) invoice InvoiceRecord? } model Project { id String @id @default(uuid()) tenantId String @default("1") projectNo String name String partyName String? contractId String? projectType String @default("") amount Decimal @default(0) @db.Decimal(18, 2) status String @default("ACTIVE") presalesCost Decimal @default(0) @db.Decimal(18, 2) hourRate Decimal @default(200) @db.Decimal(18, 2) internalAcceptedAt DateTime? customerAcceptedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt contract Contract? @relation(fields: [contractId], references: [id]) tasks ProjectTask[] timesheets Timesheet[] expenses ExpenseClaim[] loans LoanRecord[] bonds BondRecord[] budgets ProjectBudget[] acceptances ProjectAcceptance[] acceptMaterials ProjectAcceptMaterial[] purchases PurchaseRequest[] changes ProjectChange[] assets AssetOccupancy[] officeApplies OfficeApply[] @@unique([tenantId, projectNo]) } model ProjectBudget { id String @id @default(uuid()) projectId String category String amount Decimal @db.Decimal(18, 2) project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) @@unique([projectId, category]) } model ProjectAcceptance { id String @id @default(uuid()) projectId String kind String result String comment String? actorId String decidedAt DateTime @default(now()) project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) actor User @relation(fields: [actorId], references: [id]) @@index([projectId]) } model ProjectAcceptMaterial { id String @id @default(uuid()) projectId String name String remark String? required Boolean @default(true) sortNo Int @default(0) createdAt DateTime @default(now()) project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) @@index([projectId]) } model ProjectTask { id String @id @default(uuid()) projectId String name String department String? departmentId String? assigneeId String? sortNo Int @default(0) dueAt DateTime? status String @default("OPEN") createdAt DateTime @default(now()) project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) dept Department? @relation(fields: [departmentId], references: [id]) assignee User? @relation("TaskAssignee", fields: [assigneeId], references: [id]) timesheets Timesheet[] @@index([projectId]) } model Timesheet { id String @id @default(uuid()) tenantId String @default("1") projectId String taskId String? userId String workDate DateTime hours Decimal @db.Decimal(6, 2) costAmount Decimal @default(0) @db.Decimal(18, 2) note String? createdAt DateTime @default(now()) project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) task ProjectTask? @relation(fields: [taskId], references: [id], onDelete: SetNull) user User @relation(fields: [userId], references: [id]) @@index([projectId]) @@index([userId]) } model ExpenseClaim { id String @id @default(uuid()) tenantId String @default("1") claimNo String applicantId String costType String budgetCategory String? bidCaseId String? projectId String? loanId String? expenseCategory String? purpose String? amount Decimal @default(0) @db.Decimal(18, 2) status String @default("DRAFT") remark String? submittedAt DateTime? decidedAt DateTime? decidedById String? rejectComment String? prepaid Boolean @default(false) payerId String? createdAt DateTime @default(now()) bidCase BidCase? @relation(fields: [bidCaseId], references: [id]) project Project? @relation(fields: [projectId], references: [id]) loan LoanRecord? @relation(fields: [loanId], references: [id]) applicant User @relation("ExpenseApplicant", fields: [applicantId], references: [id]) decidedBy User? @relation("ExpenseDecider", fields: [decidedById], references: [id]) payer User? @relation("ExpensePayer", fields: [payerId], references: [id]) lines ExpenseLine[] @@unique([tenantId, claimNo]) @@index([applicantId]) @@index([loanId]) @@index([payerId]) } model ExpenseLine { id String @id @default(uuid()) tenantId String @default("1") claimId String kind String amount Decimal @db.Decimal(18, 2) invoiceNo String? occurAt DateTime? remark String? sortNo Int @default(0) createdAt DateTime @default(now()) claim ExpenseClaim @relation(fields: [claimId], references: [id], onDelete: Cascade) @@index([claimId]) } model LoanRecord { id String @id @default(uuid()) tenantId String @default("1") loanNo String applicantId String costType String bidCaseId String? projectId String? amount Decimal @db.Decimal(18, 2) offsetAmount Decimal @default(0) @db.Decimal(18, 2) purpose String status String @default("DRAFT") remark String? submittedAt DateTime? decidedAt DateTime? decidedById String? createdAt DateTime @default(now()) bidCase BidCase? @relation(fields: [bidCaseId], references: [id]) project Project? @relation(fields: [projectId], references: [id]) applicant User @relation("LoanApplicant", fields: [applicantId], references: [id]) decidedBy User? @relation("LoanDecider", fields: [decidedById], references: [id]) expenses ExpenseClaim[] @@unique([tenantId, loanNo]) @@index([applicantId]) } model BondRecord { id String @id @default(uuid()) tenantId String @default("1") bondNo String bidCaseId String? projectId String? contractId String? bondType String amount Decimal @db.Decimal(18, 2) paidAt DateTime? dueBackAt DateTime? returnedAt DateTime? status String @default("UNPAID") remark String? createdAt DateTime @default(now()) bidCase BidCase? @relation(fields: [bidCaseId], references: [id], onDelete: SetNull) project Project? @relation(fields: [projectId], references: [id]) contract Contract? @relation(fields: [contractId], references: [id]) @@unique([tenantId, bondNo]) } model InvoiceRecord { id String @id @default(uuid()) tenantId String @default("1") invoiceNo String? contractId String milestoneId String @unique amount Decimal @db.Decimal(18, 2) status String @default("TO_ISSUE") dueAt DateTime? issuedAt DateTime? receivedAt DateTime? remark String? createdById String? createdAt DateTime @default(now()) contract Contract @relation(fields: [contractId], references: [id]) milestone PaymentMilestone @relation(fields: [milestoneId], references: [id]) createdBy User? @relation("InvoiceCreator", fields: [createdById], references: [id]) } model FileAsset { id String @id @default(uuid()) tenantId String @default("1") bizType String bizId String fileName String mimeType String size Int storageKey String uploadedById String createdAt DateTime @default(now()) @@index([bizType, bizId]) @@index([uploadedById]) } model PurchaseRequest { id String @id @default(uuid()) tenantId String @default("1") title String projectId String? amount Decimal @default(0) @db.Decimal(18, 2) status String @default("DRAFT") remark String? applicantId String? decidedById String? decidedAt DateTime? createdAt DateTime @default(now()) project Project? @relation(fields: [projectId], references: [id]) applicant User? @relation("PurchaseApplicant", fields: [applicantId], references: [id]) decidedBy User? @relation("PurchaseDecider", fields: [decidedById], references: [id]) officeApplies OfficeApply[] } model AssetOccupancy { id String @id @default(uuid()) tenantId String @default("1") assetName String holder String holderId String? projectId String? registeredAssetId String? status String @default("IN_USE") dueAt DateTime? returnedAt DateTime? createdAt DateTime @default(now()) project Project? @relation(fields: [projectId], references: [id]) holderUser User? @relation("AssetHolder", fields: [holderId], references: [id]) registeredAsset RegisteredAsset? @relation(fields: [registeredAssetId], references: [id]) officeApplies OfficeApply[] @relation("ApplyOccupancy") @@index([registeredAssetId]) } model ProjectChange { id String @id @default(uuid()) tenantId String @default("1") projectId String summary String amountDelta Decimal @default(0) @db.Decimal(18, 2) status String @default("PENDING") comment String? actorId String? createdAt DateTime @default(now()) decidedAt DateTime? project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) actor User? @relation("ChangeActor", fields: [actorId], references: [id]) @@index([projectId]) } model PaymentRecord { id String @id @default(uuid()) tenantId String @default("1") payNo String kind String refId String? amount Decimal @db.Decimal(18, 2) status String @default("TODO") remark String? paidAt DateTime? createdById String? payeeUserId String? payeeName String? payeeKind String? bankName String? bankAccount String? payChannel String @default("MANUAL") bankStatus String @default("NONE") bankTxnNo String? bankPayload String? createdAt DateTime @default(now()) createdBy User? @relation("PaymentCreator", fields: [createdById], references: [id]) payeeUser User? @relation("PaymentPayee", fields: [payeeUserId], references: [id]) @@unique([tenantId, payNo]) @@index([refId]) @@index([payeeUserId]) } model OperationLog { id String @id @default(uuid()) tenantId String @default("1") userId String? username String? action String path String detail String? success Boolean @default(true) requestId String? ip String? createdAt DateTime @default(now()) user User? @relation(fields: [userId], references: [id]) @@index([createdAt]) @@index([action]) } model ImConversation { id String @id @default(uuid()) tenantId String @default("1") type String @default("direct") name String @default("") ownerId String? userAId String? userBId String? lastText String @default("") lastAt DateTime @default(now()) nextSeq Int @default(1) createdAt DateTime @default(now()) @@unique([userAId, userBId]) @@index([lastAt]) @@index([type, lastAt]) } model ImConversationMember { id String @id @default(uuid()) conversationId String userId String role String @default("member") joinedAt DateTime @default(now()) leftAt DateTime? @@unique([conversationId, userId]) @@index([userId]) } model ImMessage { id String @id @default(uuid()) tenantId String @default("1") conversationId String seq Int @default(0) fromUserId String toUserId String @default("") kind String @default("chat") contentType String @default("text") body String meta String @default("") fingerprint String createdAt DateTime @default(now()) @@unique([fingerprint]) @@index([conversationId, createdAt]) @@index([conversationId, seq]) @@index([toUserId, createdAt]) } model ImReadCursor { id String @id @default(uuid()) userId String conversationId String lastReadAt DateTime @default(now()) lastReadSeq Int @default(0) @@unique([userId, conversationId]) } model ImMessageReceipt { id String @id @default(uuid()) messageId String userId String readAt DateTime @default(now()) @@unique([messageId, userId]) @@index([messageId]) } model ImCall { id String @id @default(uuid()) tenantId String @default("1") conversationId String? kind String status String @default("ringing") initiatorId String title String @default("") createdAt DateTime @default(now()) endedAt DateTime? @@index([status, createdAt]) } model ImCallMember { id String @id @default(uuid()) callId String userId String status String @default("invited") joinedAt DateTime? leftAt DateTime? @@unique([callId, userId]) } model ImCallSignal { id String @id @default(uuid()) callId String fromUserId String toUserId String kind String payload String createdAt DateTime @default(now()) @@index([callId, createdAt]) @@index([toUserId, createdAt]) } model SmsSendLog { id String @id @default(uuid()) tenantId String @default("1") phones String signName String templateCode String templateParam String? eventKey String? bizId String? code String message String? createdById String? createdAt DateTime @default(now()) @@index([createdAt]) @@index([eventKey]) } model SmsEventBind { id String @id @default(uuid()) tenantId String @default("1") eventKey String @unique enabled Boolean @default(true) templateCode String @default("") signName String? updatedAt DateTime @updatedAt } model SmsOtp { id String @id @default(uuid()) phone String codeHash String purpose String expiresAt DateTime usedAt DateTime? createdAt DateTime @default(now()) @@index([phone, purpose]) } model WorkAssignment { id String @id @default(uuid()) tenantId String @default("1") title String content String @default("") assigneeId String dueAt DateTime? status String @default("OPEN") createdById String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt assignee User @relation("WorkAssignee", fields: [assigneeId], references: [id]) createdBy User @relation("WorkAssigner", fields: [createdById], references: [id]) @@index([assigneeId]) @@index([createdById]) } model RegisteredAsset { id String @id @default(uuid()) tenantId String @default("1") kind String name String code String? spec String? keeper String? location String? amount Decimal? @db.Decimal(18, 2) remark String? status String @default("ACTIVE") usage String @default("GENERAL") legalEntityId String? createdById String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt legalEntity LegalEntity? @relation(fields: [legalEntityId], references: [id]) createdBy User? @relation("AssetRegistrar", fields: [createdById], references: [id]) occupancies AssetOccupancy[] officeApplies OfficeApply[] inboundFrom OfficeApply[] @relation("ApplyInbound") @@index([kind]) @@index([legalEntityId]) } model OfficeApply { id String @id @default(uuid()) tenantId String @default("1") kind String title String reason String? status String @default("PENDING") assetKind String? registeredAssetId String? amount Decimal? @db.Decimal(18, 2) destination String? startAt DateTime? endAt DateTime? projectId String? purchaseId String? occupancyId String? inboundAssetId String? applicantId String decidedById String? decidedAt DateTime? remark String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt applicant User @relation("OfficeApplicant", fields: [applicantId], references: [id]) decidedBy User? @relation("OfficeDecider", fields: [decidedById], references: [id]) registeredAsset RegisteredAsset? @relation(fields: [registeredAssetId], references: [id]) project Project? @relation(fields: [projectId], references: [id]) purchase PurchaseRequest? @relation(fields: [purchaseId], references: [id]) occupancy AssetOccupancy? @relation("ApplyOccupancy", fields: [occupancyId], references: [id]) inboundAsset RegisteredAsset? @relation("ApplyInbound", fields: [inboundAssetId], references: [id]) @@index([applicantId]) @@index([status]) @@index([kind]) } model Notice { id String @id @default(uuid()) tenantId String @default("1") title String content String status String @default("PUBLISHED") kind String @default("ANNOUNCEMENT") audience String @default("ALL") departmentIds String[] @default([]) userIds String[] @default([]) createdById String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt createdBy User @relation("NoticeAuthor", fields: [createdById], references: [id]) @@index([status]) @@index([kind]) @@index([createdAt]) }