资讯详情

资讯详情

把工时表和工资单打通:Gauzy 时间追踪→薪资联动配置实战

把工时表和工资单打通Gauzy 时间追踪→薪资联动配置实战【免费下载链接】ever-gauzyEver® Gauzy™ - Open Business Management Platform (ERP/CRM/HRM/ATS/PM) - https://gauzy.co项目地址: https://gitcode.com/GitHub_Trending/ev/ever-gauzyGauzyEver® Gauzy™是近年来开源 ERP/CRM/HRM 一体化赛道上最活跃的 TypeScript 全栈项目之一NestJS 后端 Angular 前端 PostgreSQL一套代码覆盖客户、项目、工时、费用、发票到报表的完整业务闭环。社区里围绕它的讨论大多集中在怎么部署、怎么排错但真正让业务跑起来的是把**时间追踪Time Tracking和薪资Payroll**这两张表联动起来——员工在计时器上打的每一分钟最终如何变成工资单上的一笔金额。本文不聊部署直接从源码切入以 Gauzy 仓库本仓库即 ever-gauzy 主仓中的真实实体、命令处理器和状态机为准拆解工时记录 → 审批 → 计薪的三步联动配置、多项目工时分摊与成本核算以及联动失效时的排查思路。一、先把链路画清楚工时数据是如何流向薪资的在 Gauzy 中时间追踪的数据模型是一条清晰的三层链TimeLog时间日志计时器每次开始/停止产生一条记录落在 time_log 表。它承载员工、项目、任务、团队、联系人等维度并带isBillable是否可计费标记。Timesheet工时表按员工 × 周期聚合 TimeLog 的容器落在 timesheet 表。它记录duration、status、submittedAt、approvedAt、isBilled等关键状态字段。PayrollRun / PayrollItem薪资批次与条目一个薪资批次对应一个计薪周期条目即每个员工的应发/扣减行落在 payroll_run 与 payroll_item 表。关键点在于Timesheet 上的employeeId与 PayrollItem 上的employeeId指向同一份员工档案而员工档案上配置的billRateValue计费费率、payPeriod发薪周期、billRateCurrency费率币种正是把小时数换算成金额的桥梁见 employee.entity.ts 第 144–177 行。也就是说Gauzy 不是用一个自动脚本把工时直接算成工资而是提供一套受控的状态机工时先经员工提交、主管审批再人工/系统将批准的工时与费率结合生成薪资批次的计薪条目。这套设计的价值在于可审计——每一笔钱都能追溯到某条已审批的工时记录。二、联动配置三步走工时记录 → 审批 → 计薪第 1 步员工的计薪前置条件配置打开员工档案需要确认三个字段payPeriod发薪周期枚举值见 payroll.model.ts支持 WEEKLY / BI_WEEKLY / SEMI_MONTHLY / MONTHLY / QUARTERLY / ANNUALLY。它决定了你在创建薪资批次时选择哪个频率两者必须对齐。billRateValue / minimumBillingRate计费费率 / 最低费率数值字段经ColumnNumericTransformerPipe以numeric精度存储避免浮点丢精度这正是 1790000014000-AlterEmployeeBillingRateColumnsToNumeric 这类迁移存在的意义。billRateCurrency费率币种ISO 4217 三位码与薪资批次的currency保持一致否则跨币种求和毫无意义。同时确认员工的 **isTrackingEnabled时间追踪开关**为开启状态——如果关闭计时器 API 会直接抛出ForbiddenException见 timer.service.ts 第 244 行。第 2 步工时提交与审批的状态机工时表的状态枚举定义在 timesheet.model.ts 第 70–76 行export enum TimesheetStatus { DRAFT DRAFT, PENDING PENDING, IN_REVIEW IN REVIEW, DENIED DENIED, APPROVED APPROVED }员工在 Web/桌面计时器结束计时后工时记录默认是PENDING员工从工时表页面执行提交动作后端TimesheetSubmitHandler会写入submittedAt并触发邮件通知见 timesheet-submit.handler.ts。随后管理员/主管在审批页面对工时表执行通过/驳回const updatePayload: PartialITimesheet { status, approvedById: status TimesheetStatus.APPROVED ? RequestContext.currentUserId() : undefined, approvedAt: status TimesheetStatus.APPROVED ? new Date() : null };这段代码来自 timesheet-update-status.handler.ts 第 41–45 行它揭示了联动中最重要的一条规则只有APPROVED状态才会记录审批人approvedById与审批时间approvedAt。工时表被驳回DENIED时这两列会被清空。审批通过后Timesheet实体上的isBilled标记默认false见 timesheet.entity.ts 第 84–89 行就是这笔工时是否已进入计费/计薪流程的账务锚点。联动含义薪资操作应该只信任status APPROVED且approvedAt非空的工时表。如果发现薪资批次里混入了未审批工时先回去查状态机。第 3 步创建并推进薪资批次薪资批次的生命周期是一台严格的状态机见 payroll.model.ts 第 9–19 行export enum PayrollRunStatusEnum { DRAFT DRAFT, PENDING_APPROVAL PENDING_APPROVAL, APPROVED APPROVED, PROCESSING PROCESSING, PAID PAID, CANCELLED CANCELLED }对应控制器提供的 REST 端点见 payroll-run.controller.tsPOST /创建批次初始为DRAFT且totalGross/totalDeductions/totalNet强制清零——服务端明确拒绝信任客户端传入的合计见 payroll-run.service.ts 第 43–45 行的注释POST /:id/items添加计薪条目仅允许DRAFT状态且校验员工必须属于同一组织PUT /:id/submit→PUT /:id/approve→PUT /:id/process依次推进提交前服务端会先recalculateTotals刷新合计让审批人看到真实金额再签字见第 177–183 行只有APPROVED/PROCESSING的批次能被process标记为PAID且状态转移与合计重算在同一事务内完成杜绝已付款但合计是旧值的脏状态见第 212–245 行。批次实体上的totalGross/totalDeductions/totalNet以numeric(14,2)存储见 payroll-run.entity.ts 第 74–105 行并配有专门注释已付批次保留实际付款时的数字即使之后条目被更正也不影响历史。三、多项目工时分摊与成本核算设置工时分摊项目挂在哪一层Gauzy 的工时分摊发生在 TimeLog 层而非 Timesheet 层——一个工时表Timesheet天然可以包含多个项目的日志。TimeLog实体上的projectId、taskId、organizationContactId、organizationTeamId四个外键见 time-log.entity.ts 第 170–255 行决定了每一分钟归到哪个项目、哪个任务、哪个客户。项目侧的计费配置则定义在 organization-project.entity.ts 第 101–112 行MultiORMColumn({ nullable: true }) billing?: ProjectBillingEnum; // RATE | FLAT_FEE | MILESTONES MultiORMColumn({ nullable: true }) currency?: CurrenciesEnum;ProjectBillingEnum见 organization.model.ts 第 230–234 行提供三种计费模式RATE按费率计费适合人力外包/咨询场景。工时 × 员工费率或项目费率即可核算成本与应收这也是最常用于薪资联动的模式FLAT_FEE一口价项目总额固定工时只用于进度监控与成本归集不直接参与金额计算MILESTONES里程碑按里程碑节点结算工时作为验收佐证。联动建议需要工时→薪资/应收自动换算的项目务必选RATE并给项目指定currency若某项目选FLAT_FEE就不要期望工时数字直接变成工资金额——这不是 Bug而是计费模式设计如此。项目错记的补救机制正因为项目挂在日志层跨项目挪工时是高频需求。Gauzy 为它专门设计了工时表项目变更请求Timesheet Project Change Request机制实体见 timesheet-project-change-request.entity.ts。这个设计有两个精妙之处一次请求记录两个端点previousProjectId现在错记的项目与requestedProjectId应归属的项目都被持久化。因为一张工时表里可能有多个项目的日志只有同时记录两端审批通过时才能只动当前确实挂在旧项目上的日志不影响同一工时表里其他正确归属的记录。审批流与工时表审批解耦请求带reason原因必填与statusPENDING/APPROVED/REJECTED见 timesheet.model.ts 第 81–85 行审核后写入reviewedAt与reviewNote保证改项目这件事本身可审计。联动含义成本核算的准确性依赖项目归属的正确性。建议把项目变更请求的审批纳入与工时表审批同级别的流程纪律——先纠正归属再跑薪资批次否则成本报表与薪资依据会同时失真。四、联动失效的常见原因与验证方法社区实践中最常见的工时表通了但薪资不对问题几乎都能归到以下几类1. 状态未达 APPROVED 就进了薪资流程后端在TimesheetUpdateStatusHandler中只有APPROVED才写approvedAtTimesheetSubmitHandler只负责submittedAt。如果员工只提交submitted而未获审批approved工时表永远停在IN REVIEW或PENDING。验证方法查库确认timesheet表中目标周期内status APPROVED且approvedAt IS NOT NULL审批人在工时表 → 审批页面approvals.component.ts批量执行时同样只对选中行生效——漏选一行那行就不会进入计薪口径。2. 周期/币种/费率对不上员工payPeriod是MONTHLY批次却建成了WEEKLY两者口径必然对不上员工billRateCurrency是 USD批次currency是 EURPayrollRunService.getStatistics明确按币种分组求和绝不跨币种相加见 payroll-run.service.ts 第 398–402 行注释混币种只会得到毫无意义的总数员工billRateValue未配置或为 0计出的金额自然是 0。验证方法在薪资批次详情页核对频率 × 币种 × 计薪区间三项再抽查员工档案的费率字段。服务端createRun会拒绝periodEnd periodStart、payDate periodStart的非法批次见第 63–82 行这类建不出来的错误反而好排查。3. 已付款批次被误改或并发重复支付updateRun/addItem/removeItem只允许OPEN_STATUSESDRAFT/PENDING_APPROVAL/APPROVED/PROCESSING操作PAID是终态process用WHERE 仍处于 APPROVED/PROCESSING的单条 UPDATE 抢占状态防止两个并发请求同时把同一批次标记为已付见第 220–235 行与transition方法第 470–482 行。所以报错This payroll run has already been processed不是故障而是防重复支付的护栏。验证方法若怀疑重复检查payroll_run.paidAt与payroll_item行数已付批次的合计是存储值而非实时计算值若手工改库导致不一致应以条目重算recalculateTotals为准。4. 权限不足导致看不见、批不了薪资相关端点挂有显式权限声明ORG_PAYROLL_EDIT创建/编辑/提交/取消与ORG_PAYROLL_APPROVE审批/处理见 payroll-run.controller.ts 第 141、161、180、199 行的Permissions(...)装饰器。员工侧的工时查询则受CHANGE_SELECTED_EMPLOYEE权限约束见 timesheet.service.ts 第 41–46 行。验证方法角色权限页确认审批人拥有ORG_PAYROLL_APPROVE普通员工默认只能看自己的工时onlyMe逻辑这是功能设计而非 bug。若 API 直接返回 403先查租户/组织上下文与权限矩阵而不是怀疑联动逻辑。五、给集成者的三条纪律永远以 APPROVED 为入账闸门无论走 UI 还是直接调 API薪资批次条目的来源只能取status APPROVED的工时。approvedByIdapprovedAt是审计链路的起点不要绕过。金额计算交给服务端PayrollRunService反复强调totals 永远不接受调用方传入、以整数分cents重算见 payroll-run.service.ts 第 43–45、495–523 行numeric(14,2)列配合ColumnNumericTransformerPipe处理精度。二次开发时不要在前端自行累加金额并回填。先纠正项目归属再跑薪资跨项目工时先走变更请求审批流修正TimeLog.projectId成本核算与薪资口径才能同时保持准确。Gauzy 把工时与薪资设计成两套状态机而非一个一键算薪的黑盒初看增加了操作步骤实则为每笔钱留下了可追溯的证据链。理解了TimesheetStatus与PayrollRunStatusEnum这两台状态机如何咬合你就掌握了在这个开源 ERP 里让时间真正变现的核心钥匙。【免费下载链接】ever-gauzyEver® Gauzy™ - Open Business Management Platform (ERP/CRM/HRM/ATS/PM) - https://gauzy.co项目地址: https://gitcode.com/GitHub_Trending/ev/ever-gauzy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →