先说结论
Odoo Payroll 最容易被误解成一件事:
- 先建几条 salary rule
- 写点 Python 公式
- 然后工资单从上到下跑完就结束
这只看到了最表面的一层。
真正的计算链更像这样:
- Structure Type 决定你在什么工资制度里算
- Salary Structure 决定本次工资单挂哪套规则组合
- Worked Days 提供工资期里的工时 / 缺勤口径
- Inputs 提供补贴、扣款、一次性调整等额外变量
- Salary Rules 最后才消费这些输入,产出工资明细
所以 rule 很重要,但 rule 从来不是全部。
为什么 Structure Type 不是“多余的一层”
很多实施第一次做薪资,会觉得 structure type 很像多包了一层壳。
其实它承担的是制度语义:
- 这类员工按月薪算、周薪算还是小时工算
- 这类工资单默认需要哪些 worked days 口径
- 某些规则和输入是否在这一类人群上适用
- 这一套结构是否对应某地区 / 某法律制度 / 某员工群体
所以它不是纯技术分组,而是工资引擎的制度入口。
如果没有这层,后面所有 rule 都会被迫在公式里自己判断:
- 你是不是小时工
- 你是不是实习生
- 你是不是不同国家制度
那规则会很快失控。
Salary Rule 只负责“怎么算”,不负责“数据从哪来”
规则最适合做的是:
- 取 category / code / amount / quantity / rate
- 根据 worked days、inputs、contract 或其他上下文算金额
- 决定某条工资项是否出现
但它并不天然负责:
- Attendance 和 Time Off 怎么被翻译成 payroll 可用工时
- 哪些缺勤该进 worked days,哪些只做统计
- 本月补贴或罚款从哪里注入
所以很多工资单算错,不是 rule 公式有 bug,而是上游输入口径已经错了。
Worked Days 是“工资期时间口径”,不是普通出勤报表
Worked Days 之所以重要,是因为它把工资期里的时间结果标准化了。
它通常不是原始考勤流水本身,而是经过 payroll 视角翻译后的工时口径,比如:
- 正常出勤
- 带薪假
- 无薪假
- 病假
- 合同外区间
- 其他需要进入工资单的时间类型
这和前面人力资源文章里讲过的 work entry 心智是连起来的:
- Attendance 模块不等于 payroll 本身
- work entry / worked day 才是 payroll 可消费的标准输入
所以工资实施时,第一件事不该问“规则怎么写”,而该先问:
这张工资单里的 Worked Days 到底代表哪套时间翻译结果?
Inputs 是“临时变量入口”,不是规则失败时的补丁
Inputs 常被误用成一个垃圾桶:
- 规则不好配,就让 HR 手输
- 数据对不上,就临时补一个 input
- 什么都可以塞进去
这会把薪资系统越用越脆。
更稳的理解是:
Inputs 适合承载那些:
- 本期特有
- 不是固定每月发生
- 无法从 worked days 或合同主数据稳定推出
- 但又必须进入工资计算
典型例如:
- 一次性奖金
- 临时补贴
- 手工罚款
- 某月补发 / 扣回
也就是说,input 是受控的人工入口,不是制度空洞的万能补锅位。
为什么很多规则越写越复杂
因为团队把本该放在前面几层决定的事,全都塞进了 salary rule。
例如他们会在规则里同时判断:
- 员工属于哪个制度
- 这个月哪些天该算薪
- 假勤怎么折算
- 有没有补贴
- 哪些国家规则生效
结果就是:
- rule 读不懂
- 改一个小政策,十几条公式全得动
- 调试时看不出错在规则还是错在输入
更好的做法是把职责拆开:
- structure type 管制度入口
- worked days 管时间口径
- inputs 管额外变量
- rules 只做消费与计算
这样规则才会越来越清晰,而不是越来越像迷宫。
我最建议的排错顺序
工资单结果不对时,我通常按这个顺序看:
1)先看 Structure Type / Structure 是否挂对
如果制度入口就错了,后面所有规则都可能“算得很努力,但方向错了”。
2)再看 Worked Days 是否符合工资期事实
很多金额异常其实是工时口径异常,不是公式异常。
3)再看 Inputs 有没有缺、有没有多、有没有被手工乱输
一次性数据入口最容易被忽视。
4)最后才看 Salary Rule 公式
把公式放在最后看,排错效率通常更高。
这对实施的直接含义
如果你们要做本地化薪资、复杂加班、请假扣薪、补贴和奖金混算,那别把项目设计成“配规则工程”。
更稳的方案应该是:
- 先定义员工分组和 structure type
- 再定义 worked days / work entry 翻译口径
- 再定义哪些数据走 input
- 最后才设计 rules
这样一来,后续政策变更时你可以改入口层,而不是每次都重写整套公式。
我会怎么跟业务方解释
如果业务方问:
“薪资不就是几条公式吗,为什么还要 structure type、worked days、inputs 这些层?”
我会说:
因为公式只会算,它不会自己决定制度、时间口径和临时变量。Odoo 把这些前置层拆出来,就是为了让工资单不是一堆写满 if/else 的脆弱脚本。
一句话记忆
Odoo Payroll 不是“规则表”,而是“制度入口 + 时间口径 + 临时变量 + 计算规则”组成的多层管线;只盯 salary rule,迟早会把系统配成一团。
DISCUSSION
评论区