我把年终奖填成 36000 元,页面算出应缴 1080 元。改成 36001 元,应缴变成 3390.10 元。
多发 1 元,多缴 2310.10 元,到手反而少 2309.10 元。
用 Excel 直接乘税率的写法会算出 1080.03 元,看着只比 36000 元那档多 3 分钱,其实漏了速算扣除数。

在线案例与源码:atomgit.com/deli007/dem… 成品只有一个 index.html,双击就能打开,不联网也能算。
一、为什么值得看:多 1 元,税差可能上千元
年终奖的税率不是拿奖金总额直接乘出来的。
先拿奖金除以 12,用商去查「按月换算后的综合所得税率表」,再用奖金乘对应税率,减去速算扣除数。
36000 除以 12 正好是 3000,落在 3% 那一档,速算扣除数是 0,税额 1080 元。
36001 除以 12 是 3000.08,越过 3000 这条线,掉进 10% 那一档,速算扣除数是 210,税额 3390.10 元。
同一套规则,一个月少 1 元,一个月少 2000 多元。这就是临界点的由来。

二、准备环境:进入码道 Web,把税率表和自检写进需求
浏览器打开码道 Web 版:devcloud.cn-north-4.huaweicloud.com/chat?source… ,登录后就能在对话框里写需求,不需要装软件。
码道有三种使用方式:WebUI(浏览器对话)、TUI(终端命令行)和桌面 IDE(IDE 插件)。本文用 WebUI 版演示。
这轮需求里最值钱的是三句话。
第一句:两张税率表的数字直接写死在需求里,别让它自己回忆。税收数字记错一位,整页结论都是错的。
第二句:要两种口径同屏对照,年终奖单独计税和并入综合所得各算一遍,最后给一句「哪种更省」。
第三句:页面自己带断言。我列了 9 条标准答案,比如 36000 元对应 1080 元、36001 元对应 3390.10 元、逐月预扣合计要等于年度口径应缴税额。
生成过程中模型有一次思考过长、回复被截断。我发了一句「继续」,它接着把文件写完,页面才跑起来。
三、一屏看清:页面上的六块内容
最上面是输入区:月薪、每月五险一金、每月专项附加扣除、年终奖金额、计发月数,最后跟着一句口径说明。
往下是两张结果卡,左边是年终奖单独计税,右边是并入综合所得,各自给出全年应缴税额和全年到手合计。
再往下是两张阶梯税率条形图,当前所处档位会高亮。
然后是工资逐月预扣表、年终奖 6 个临界点表。
最下面是错误算法对照,把「直接乘税率」和正确公式并排放。
右上角是 9 条自检徽章,全过就是 9 / 9。
四、实测:默认输入下两套算法差 3570 元
默认填的是月薪 20000 元、五险一金 3200 元、专项附加扣除 2000 元、年终奖 60000 元。
| 项目 | 方案一:年终奖单独计税 | 方案二:并入综合所得 |
|---|---|---|
| 全年应缴个税合计 | 15,030.00 | 18,600.00 |
| 全年到手合计 | 246,570.00 | 243,000.00 |
| 其中工资部分 | 9,240.00 | 9,240.00 |
| 其中年终奖部分 | 5,790.00 | 9,360.00 补缴 |
| 两者差额 | 少缴 3,570.00 | 多缴 3,570.00 |
工资部分的账是这样:每月 20000 元减去 5000 元减除费用、3200 元五险一金、2000 元专项附加扣除,每月计税基数是 9800 元。
12 个月累计 117,600 元,落在 10% 那一档,速算扣除数 2520,全年预扣 9240 元。
年终奖 60000 元除以 12 是 5000 元,落进 10% 档,税额 6000 减 210,等于 5790 元。
方案二把年终奖并进综合所得,计税基数变成 177,600 元,跳到 20% 档,全年 18,600 元。
结论那一行是页面自己打的:年终奖单独计税更省,全年少缴 3570 元。

五、逐月预扣表:第 4 月才跳档
工资部分的税不是每月平均扣的,是按累计额算、再减去前面已扣的部分。
默认输入下,前 3 个月的累计计税基数还没过 36000 元,每月只扣 294 元。
第 4 个月累计到 39200 元,进了 10% 档,累计应纳税额变 1400 元,当月就要补 518 元。
从第 5 个月开始每月稳定扣 980 元,最后一个月累计 117,600 元,累计税额 9240 元。
| 月份 | 累计应纳税所得额 | 累计应缴税额 | 本月预扣税额 |
|---|---|---|---|
| 第 1 月 | 9,800.00 | 294.00 | 294.00 |
| 第 3 月 | 29,400.00 | 882.00 | 294.00 |
| 第 4 月 | 39,200.00 | 1,400.00 | 518.00 |
| 第 12 月 | 117,600.00 | 9,240.00 | 980.00 |
表格下面还有一行提示:全年工资部分合计预扣 9240 元,等于年度口径应缴税额,工资部分汇算不用补退。
六、6 个临界点与跳升区间
临界点不止 36000 元这一个,一共 6 个。
| 临界点(元) | 该点应缴税 | 多发 1 元应缴税 | 多 1 元多缴 | 跳升区间 |
|---|---|---|---|---|
| 36,000.00 | 1,080.00 | 3,390.10 | 2,310.10 | 36,000.00 ~ 38,566.67 |
| 144,000.00 | 14,190.00 | 27,390.20 | 13,200.20 | 144,000.00 ~ 160,500.00 |
| 300,000.00 | 58,590.00 | 72,340.25 | 13,750.25 | 300,000.00 ~ 318,333.33 |
| 420,000.00 | 102,340.00 | 121,590.30 | 19,250.30 | 420,000.00 ~ 447,500.00 |
| 660,000.00 | 193,590.00 | 223,840.35 | 30,250.35 | 660,000.00 ~ 706,538.46 |
| 960,000.00 | 328,840.00 | 416,840.45 | 88,000.45 | 960,000.00 ~ 1,120,000.00 |
区间右端点是「追平点」:奖金涨到这个数,到手才刚好回到临界点的水平。
36000 元的追平点是 38,566.67 元。中间这段多发 1 元,到手就少 1 元以上。
七、验证与踩坑:33 项独立复算,抓到一处 1 元的注释错
我不信页面自己报的 9 / 9,所以另写了一份 Python 脚本,用 Decimal 重算了税率表、六档跳升额、六个追平点、逐月预扣和两套方案的差额。
脚本跑出来 33 / 33 项一致。
复现命令就两行,脚本在案例目录里。
cd codearts-bonus-tax-calculator
python verify_bonus_tax.py
本地预览用这条命令,然后开浏览器看。
python -m http.server 8000
追平点是用二分法逼近出来的,精度到 0.01 元,六个点分别是 38,566.67、160,500.00、318,333.33、447,500.00、706,538.46、1,120,000.00。
复算过程中抓到一处真错:页面注释原本写「36,001 元比 36,000 元多发 1 元,到手反而少 2,310.10 元」。
这个数把多缴的税额当成到手差额了。多发的 1 元本身也算收入,到手只少 2,309.10 元,多缴的税才是 2,310.10 元。
我把注释改成 2,309.10 元,并把两个数字都写在括号里。页面上的结果卡本来就算对了,错的是那一句话。
失败路径我试了四组输入。
| 输入 | 页面反馈 |
|---|---|
| 年终奖 -100 | 年终奖金额不能为负数 |
| 年终奖留空 | 请输入年终奖金额 |
| 输入非数字 | 请输入有效数字 |
| 年终奖 200000000 | 年终奖不能超过 1 亿元 |
年终奖填 -100 元时,输入框变红,结果卡的数字全部换成「—」,逐月表显示「输入有误,未输出结果」,上方还有一条红色横条提示先修正红色输入项。
页面没有白屏,图表也还在,只是暂停输出结果。


八、边界与不足
页面按公开的综合所得税率表估算,税率表和速算扣除数写死在代码里。
五险一金和专项附加扣除都由你手填每月固定值,页面不做缴费基数上下限、不做年中变更、不做补扣补缴。
年终奖单独计税这个口径本身有政策有效期,到期后算法可能要调,页面不会自己跟着变。
这里算的是税额,不是税务建议,具体申报以扣缴单位和税务机关为准。
九、使用码道体会
这轮最大的体会是:涉及数字的应用,必须把标准答案和断言一起写进需求。
税率表让模型自己回忆,它大概率会把速算扣除数记串一档;写死在需求里,它只用管算法和界面。
9 条自检加上我自己 33 项复算,正好互补:页面自检证明代码按我给的规则跑,独立复算证明我给的规则本身没写错。