年终奖扣税变了!用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_insurances3
计算个税

在代码里用数组嵌套元组的方式实现
# 个税税率表
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_month5
计算年终奖

# 年终奖税率表
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篇优质文章,涵盖数据分析、爬虫、机器学习等方面,别再说不知道该从哪开始,实战哪里找了 “点赞”就是对博主最大的支持 

