Payroll and individual income tax engine for MoonBit: cumulative withholding, annual bonus traps, penny-exact money, auditable per-month breakdowns, and net-to-gross inversion.
| 30 秒判断 | |
|---|---|
| 解决什么 | 发工资要算的税:累计预扣跨月跳档、年终奖临界点、到手反推应发、政策换版重算——每一条都直接对上钱 |
| 谁用 | 招聘算 offer 税前 / 财务查每月预扣依据 / 薪酬系统评估换版成本 / 个人比较年终奖两种计税 |
| 凭什么信 | 42 项单元测试 + 21 项 Node 测试(CLI 15 + 批量 6) + 3,000 组 Python Decimal 独立实现逐字段对照;moon test 一条命令跑完,不用装 Python 也能验边界 |
| 生态位置 | 本包发布前全量检索(2026-10-29):Mooncakes 3,390 个包里薪酬/个税类 0 个;金额表示只有 haol-05/moonmoney,不做计税 |
| 怎么试 | 先开演示页(web/,零依赖零网络):五个场景一键切换,每个都有一句"该看什么" |
| 能力 | 说明 |
|---|---|
| 金额 | 全程整数分、四舍五入到分,浮点从不接触钱 |
| 累计预扣 | 工资薪金累计预扣预缴,跨月状态机,多月跳档正确 |
| 年终奖 | 单独计税 + 并入综合所得对比择优;6 档临界点全档穷举——跨档点落在 ÷12 商的第 +1 分(即多发 0.12 元),到手少 2,309.89 元 |
| 专项附加 | 子女教育/婴幼儿/房贷/租房互斥校验/赡养/继续教育/大病医疗,逐项上限 |
| 社保公积金 | 基数上下限 clamp、逐险种舍入;内置五城示例参数(标注 EXAMPLE) |
| 其他所得 | 收入类型枚举:劳务报酬(20/30/40%)、稿酬(20%)、特许权使用费(20%),共享 800/80% 费用减除 |
| 年度汇算 | 应退/应补分列输出;劳务报酬并入口径差产生真实退税场景(JSON other_income / other_prepaid → settlement) |
| 税后反推 | 给定到手金额求税前应发(gross_up):分段函数求逆,分位台阶上取最小可行解,不可精确命中时报告超出几分 |
| 税额分解 | 每月预扣追溯到底:第几档、什么税率、速算扣除数多少、完整算式(explain) |
| 规则集注入 | 两张税率表 + 起征点 + 版本号打包为 RuleSet,可换一整套规则重算,不改引擎 |
| 政策影响面 | 同一份工资在旧/新两版规则下逐月对比,输出每人每月增减(policy_impact;JSON preview 试算起征点) |
| 工资条 | Markdown 输出:应发、各项扣除、实发、累计口径 |
| 确定性 | 同一输入产生逐字节相同的 JSON;规则表数据驱动、按年度版本化 |
| 谁 | 问题 | 调用 |
|---|---|---|
| 招聘 / HR | "offer 写 25,000 到手,税前该开多少?";"这 1,000 人本月的工资条" | gross_up / bin/batch.mjs |
| 财务 | "这个月每个人的预扣为什么是这个数?" | explain_withholding / JSON 的 explain |
| 薪酬系统 | "政策换版,全公司要多掏多少钱?" | policy_impact |
| 个人 | "年终奖 36,000 还是 36,001 划算?" | bonus_compare |
node bin/wagekit.mjs examples/settlement.json
# {"settlement":{"tax_due":69000,"prepaid":205000,"refund":136000,"payable":0,...}}| deduction_months | 含义 | 本例结果 |
|---|---|---|
| 省略 | 跟着数据走(给几个月扣几个月),与产生已预扣额的预扣口径一致 | 应缴 690 → 退 1,360 |
| 12 | 居民个人法定年度减除 60,000 元,即使只给了部分月份 | 应缴 0 → 退 2,050 |
cat > /tmp/offer.json <<'JSON'
{"year":2026,"months":[{"gross":0,"social":0,"special":300000,"other":0}],"target_net":2000000}
JSON
node bin/wagekit.mjs /tmp/offer.json{"month":3,"taxable_cum":6600000,"bracket":2,"rate":10,"quick":252000,
"tax_cum":408000,"withheld":220000,"formula":"66000.00 × 10% − 2520.00 = 4080.00"}node bin/batch.mjs examples/employees.csv --out payslips
# 已生成 3 份工资条 → /path/to/payslips
# 月度预扣个税合计 2836.00 元|月实发合计 89864.00 元
# 年终奖按单独计税合计 12870.00 元(是否更省见每份工资条的对比)let inputs : Array[@wagekit.MonthInput] = [
{ gross: 3_000_000L, social: 300_000L, special: 100_000L, other: 0L },
]
let schedule = @wagekit.cumulative_withholding(inputs) // 逐月预扣
let why = @wagekit.explain_withholding(rules, inputs) // 为什么是这个数
let offer = @wagekit.gross_up(2_000_000L, 300_000L, 0L, 0L) // 到手反推税前
let impact = @wagekit.policy_impact(rules, candidate, inputs) // 换版影响面moon run examples/embed
# 本月预扣 630.00 元
# 档位 1:21000.00 × 3% − 0.00 = 630.00
# 到手 20000.00 元需要税前 23463.92 元(精确)
# 起征点 6000:全年少扣 30.00 元moon check --target all --deny-warn # 四目标零警告
moon run examples/embed # 嵌入示例(只用 pub 接口)
moon test --deny-warn # 单元测试:临界点穷举、状态机不变量、舍入口径、反推往返
node scripts/build.mjs
node --test tests/cli.test.mjs tests/batch.test.mjs # 21 pass
node bin/wagekit.mjs examples/months.json # JSON 报告
node bin/wagekit.mjs examples/months.json --md # 全年工资条(Markdown)
node bin/wagekit.mjs examples/explain-grossup.json # 税额分解 + 反推
node bin/wagekit.mjs examples/settlement.json # 年度汇算:应退 1,360 元
node bin/wagekit.mjs examples/policy-preview.json # 起征点改 6,000 的影响面
python tests/oracle.py 3000 # 独立 Decimal oracle,逐字段对比node scripts/build.mjs
node bench/bench.mjs 1000 5| 维度 | Excel / 在线计算器 | WageKit |
|---|---|---|
| 口径 | 黑盒公式,改没改不知道 | 每张税率表、每个常量都在 docs/RULES.md 里标了位置与来源;explain 把每月预扣还原成算式 |
| 方向 | 只能正向算:税前 → 税后 | 双向:gross_up 由到手反推应发,offer 谈薪直接用 |
| 年度更新 | 等厂商/作者改模板 | 规则是数据,换一个 RuleSet 即重算;policy_impact 直接给出换版前后的逐月增减 |
| 可嵌入 | 不能 | MoonBit 库 / JSON wire / Node CLI / wasm 宿主,四种调用 |
| 可验证 | 靠自己复核 | Python Decimal 独立实现做 oracle,3,000 组逐字段对比 |
| 批量与参数化 | 手工改单元格 | 五城社保参数、任意月数、任意所得组合,一次调用 |
| 项目 | 定位 | 与 WageKit 的边界 |
|---|---|---|
| Mooncakes salary / payroll / payslip / pension | 不存在(2026-10-28 实测搜索为空) | — |
| haol-05/moonmoney | 金额格式化 | 只管"钱的表示",不管"税怎么算",无代码复用 |
| lwmvr/moonbit-fixedincome | 固定收益金融工具 | 与工资个税无关 |
| structuraBlock/MoonStats | 统计工具 | 无关 |
| 本人前作 JoinWitness / Relcore | 数据校验 / 关系执行 | 无代码复用、不同赛道,旧项目保留 |
Install
Download zipPayroll and individual income tax engine for MoonBit: cumulative withholding, annual bonus traps, penny-exact money, auditable per-month breakdowns, and net-to-gross inversion.