年终奖扣税变了!用Python写个工资计算器APP,竟少拿了这么多
自2022年1月1日起,居民个人取得全年一次性奖金,应并入当年综合所得计算缴纳个人所得税。
1
计算公式
按照个税累计预扣法,看下到手工资的计算方法。
应税金额 = 累计月薪 - 累计缴纳五险一金 - 累计6项附加扣除 - 累计个税起征点
个税起征点是常量 5000,6项附加扣除是自己填报的,也可以看做是常量。
有了公式,计算到手工资就很简单了
2
计算五险一金
五险一金包括养老保险、医疗保险、失业保险、工伤保险、生育保险和住房公积金,其中养老保险、医疗保险和公积金缴纳得多一些,对工资计算影响大,其他三个保险要么不需要个人缴纳,要么缴纳比例极低,可以忽略。
所以,代码里用养老保险、医疗保险和住房公积金代替五险一金。
缴纳基数是个范围,月薪落在该范围就按照 月薪 * 缴纳比例 来算,如果超过上限按照 上限 * 缴纳比例 来算。
def get_cardinal_number(salary, low, high):
"""
根据月薪获取社保缴费基数
:param salary: 月薪
:param low: 下限基数
:param high: 上限基数
:return: 实际缴费基数
"""
if salary <= low:
return low
elif low < salary <= high:
return salary
else:
return high
def get_five_insurances(salary, yi_liao_rate=0.02, yang_lao_rate=0.08, gong_ji_jin_rate=0.12):
"""
计算个人五险一金的缴纳金额
:param salary: 月薪
:param yi_liao_rate: 医疗保险缴纳比例
:param yang_lao_rate: 养老保险缴纳比例
:param gong_ji_jin_rate: 公积金缴纳比例
:return: 缴纳金额
"""
yi_liao_cn = get_cardinal_number(salary, 5360, 29732)
yang_lao_cn = get_cardinal_number(salary, 3613, 26541)
gong_ji_jin_cn = get_cardinal_number(salary, 2320, 27786)
five_insurances = yi_liao_cn * yi_liao_rate + yang_lao_cn * yang_lao_rate + round(gong_ji_jin_cn * gong_ji_jin_rate)
return five_insurances
3
计算个税
在代码里用数组嵌套元组的方式实现
# 个税税率表
salary_tax_rate_tb = [
(0, 36000, 0.03, 0),
(36000, 144000, 0.1, 2520),
(144000, 300000, 0.2, 16920),
(300000, 420000, 0.25, 31920),
(420000, 660000, 0.3, 52920),
(660000, 960000, 0.35, 85920),
(960000, sys.maxsize, 0.45, 181920),
]
def get_tax_rate(tax_rate_tb, salary):
"""
根据月薪(年终奖)获取税率
:param tax_rate_tb: 税率表,格式 [(分段下限, 分段上限, 税率, 速算扣除), (...), ...]
:param salary: 月薪或年终奖
:return: salary所在分段的税率和速算扣除金额
"""
for tax_rate in tax_rate_tb:
if tax_rate[0] < salary <= tax_rate[1]:
return tax_rate[2], tax_rate[3]
def calc_salary_tax(salary, deduct_salary):
"""
计算月薪个税
:param salary: 累计月薪
:param deduct_salary: 累计缴纳五险一金 + 累计6项附加扣除 + 累计个税起征点
:return:
"""
# 应纳税额 = 累计月薪 - 累计缴纳五险一金 - 累计6项附加扣除 - 累计个税起征点
taxable_amount = salary - deduct_salary
# 查表获取税率和速算扣除
tax_rate = get_tax_rate(salary_tax_rate_tb, taxable_amount)
# 累计个税 = 应税金额 * 税率- 速算扣除
return taxable_amount * tax_rate[0] - tax_rate[1]
4
计算一年的到手工资
def calc_salary(monthly_salary=20000, yi_liao_rate=0.02, yang_lao_rate=0.08, gong_ji_jin_rate=0.12,
six_special=1000, bonus_months=4):
total_salary = 0 # 累计月薪
total_five_insurances = 0 # 累计五险一金
total_tax_threshold = 0 # 累计个税起征点
total_six_special = 0 # 累计6项附加扣除
pre_tax_amount = 0 # 前几月累计个税
money_every_month = [] # 返回每个月到手工资
tax_every_month = [] # 返回每个月缴纳个税
for i in range(1, 13):
total_salary += monthly_salary
five_insurances = get_five_insurances(monthly_salary, yi_liao_rate, yang_lao_rate, gong_ji_jin_rate)
total_five_insurances += five_insurances
total_tax_threshold += 5000
total_six_special += six_special
# 不需要纳税的部分
to_deduct = total_five_insurances + total_tax_threshold + total_six_special
# 当月个税 = 当年所得累计个税 - 前几月累计个税
taxed_amount = round(calc_salary_tax(total_salary, to_deduct) - pre_tax_amount, 2)
tax_every_month.append(taxed_amount)
# pre_tax_amount 累计当月个税,为下个月做准备
pre_tax_amount += taxed_amount
# 到手工资 = 月薪 - 当月个税 - 当月缴纳五险一金
money = round(monthly_salary - taxed_amount - five_insurances, 2)
money_every_month.append(money)
return money_every_month, tax_every_month
5
计算年终奖
# 年终奖税率表
bonus_tax_rate_tb = [
(0, 3000, 0.03, 0),
(3000, 12000, 0.1, 210),
(12000, 25000, 0.2, 1410),
(25000, 35000, 0.25, 2660),
(35000, 55000, 0.3, 4410),
(55000, 80000, 0.35, 7160),
(80000, sys.maxsize, 0.45, 15160),
]
# 调整前年终奖个税计算
def calc_bonus_tax(salary):
"""
计算年终奖v1个税,需要把年终奖总额除以12,再查税率表,相当于平均每月的个税
:param salary:
:return:
"""
tax_rate = get_tax_rate(bonus_tax_rate_tb, salary / 12)
return salary * tax_rate[0] - tax_rate[1]
# 默认按照12月份发年终奖计算
if i == 12:
# 年终奖 = bonus_months * 月薪
bonus = bonus_months * monthly_salary
# v1版本 调整前
bonus_taxed_amount_v1 = calc_bonus_tax(bonus)
tax_every_month.append(bonus_taxed_amount_v1)
bonus_money_v1 = bonus - bonus_taxed_amount_v1
money_every_month.append(round(bonus_money_v1, 2))
# v2 版本 调整后
bonus_taxed_amount_v2 = round(calc_salary_tax(total_salary + bonus, to_deduct) - pre_tax_amount, 2)
tax_every_month.append(bonus_taxed_amount_v2)
bonus_money_v2 = bonus - bonus_taxed_amount_v2
money_every_month.append(round(bonus_money_v2, 2))
上述代码加到 cacl_salary 函数中,return 语句之前即可。
6
前端页面
工资计算部分就结束了,剩下还需编写前端页面。
根据用户在页面输入的值来调用 calc_salary 函数,并在页面展示返回的结果。
页面用 Streamlit 实现,这里就直接贴代码,没用过 Streamlit 的朋友可以参考上篇《详解Streamlit》。
import streamlit as st
import pandas as pd
from salary_funcs import calc_salary
st.title('工资计算器')
city_selectbox = st.sidebar.selectbox(
"选择城市",
('北京市', '')
)
monthly_salary = st.sidebar.text_input(
'月薪',
value=20000
)
yi_liao_rate = st.sidebar.text_input(
'医疗保险缴纳比例(%)',
value=2
)
yang_lao_rate = st.sidebar.text_input(
'养老保险缴纳比例(%)',
value=8
)
gong_ji_jin_rate = st.sidebar.text_input(
'公积金缴纳比例(%)',
value=12
)
six_special = st.sidebar.text_input(
'六项附加扣除',
value=1000
)
bonus_months = st.sidebar.text_input(
'年终奖几个月',
value=4
)
money, tax = calc_salary(float(monthly_salary), float(yi_liao_rate) / 100, float(yang_lao_rate) / 100,
float(gong_ji_jin_rate) / 100, int(six_special), int(bonus_months))
money_pd = pd.DataFrame(
{'到手工资': money[:12]},
index=[i for i in range(1, 13)] # 调整index从1开始
)
st.bar_chart(money_pd)
bonus_v1 = money[12]
bonus_v2 = money[13]
st.write('改版前的年终奖:', bonus_v1, '元,改版后:', bonus_v2, '元')
bonus_delta, tax_delta = st.columns(2)
with bonus_delta:
st.metric('年终奖到手变化', value=bonus_v2, delta=round(bonus_v2-bonus_v1, 2), delta_color='inverse')
with tax_delta:
bonus_tax_v1 = tax[12]
bonus_tax_v2 = tax[13]
st.metric('年终奖纳税变化', value=bonus_tax_v2, delta=round(bonus_tax_v2-bonus_tax_v1, 2), delta_color='inverse')
all_pd = pd.DataFrame(
{
'缴纳个税': tax[:12],
'到手工资': money[:12],
}
, index=[f'{i}月' for i in range(1, 13)]
)
st.table(all_pd)
页面效果如下:
月薪2w:年终奖少 5k
月薪3w-4w:年终奖少 1.3w
月薪 5w:年终奖少 2w
扫码即可加我微信
回复:扣税工具 领取源码
万水千山总是情,点个 👍 行不行。
--END--
如何找到我:
近期优质文章:
学习更多: 整理了我开始分享学习笔记到现在超过250篇优质文章,涵盖数据分析、爬虫、机器学习等方面,别再说不知道该从哪开始,实战哪里找了 “点赞”就是对博主最大的支持