年终奖扣税变了!用Python写个工资计算器APP,竟少拿了这么多

共 11741字,需浏览 24分钟

 ·

2021-11-29 15:37

自2022年1月1日起,居民个人取得全年一次性奖金,应并入当年综合所得计算缴纳个人所得税。

这次调整会影响年终奖到手的金额,打算用上篇介绍的 Streamlit 框架做个工资计算器 web app,不但能知道年终奖少了多少,也能看到每月到手工资以及变化。
用Python制作销售数据可视化看板,展示分析一步到位!
对代码不感兴趣的朋友可以直接跳到文末看结论,那里列举对不同月薪群体的影响。
19年个税改为累计预扣法计算后,大家会发现年初到手的工资高一些,年底就低一些了。
这是累计带来的效应,19年之前是每个月独立算个税,互不影响,所以每月到手工资变化不大。19年之后是每个月工资需要跟前几个月累计到一起来算个税。从年初到年底,累计金额越来越大,税率就会越高,缴纳的个税也就会 越高。到手的工资自然就是前高后低。
每月累计的工资就叫当年综合所得调整前年终奖有单独的税率表,单独计算个税。调整后,并入当年综合所得,就会导致税率比之前更高,到手的金额会变少。
写代码前,先看看效果:

1

 计算公式


按照个税累计预扣法,看下到手工资的计算方法。

我们每月到手的工资需要从月薪里扣除个人所得税和缴纳的五险一金,所以第一个公式是:
到手工资 = 月薪 - 当月个税 - 当月缴纳五险一金
当月个税又涉及 2 个公式:

应税金额 = 累计月薪 - 累计缴纳五险一金 - 累计6项附加扣除 - 累计个税起征点

当月个税 = 应税金额 * 税率(查表)- 速算扣除 - 前几月累计个税
五险一金涉及1个公式:
每月缴纳五险一金 = 缴纳基数 * 缴纳比例

个税起征点是常量 5000,6项附加扣除是自己填报的,也可以看做是常量。

有了公式,计算到手工资就很简单了

2

 计算五险一金


五险一金包括养老保险、医疗保险、失业保险、工伤保险、生育保险和住房公积金,其中养老保险、医疗保险和公积金缴纳得多一些,对工资计算影响大,其他三个保险要么不需要个人缴纳,要么缴纳比例极低,可以忽略。

所以,代码里用养老保险、医疗保险和住房公积金代替五险一金。

缴纳基数是个范围,月薪落在该范围就按照 月薪 * 缴纳比例 来算,如果超过上限按照 上限 * 缴纳比例 来算。

缴纳比例在不同城市、不同企业是不一样的,这里,我按照北京市最高比例作为默认值。分别是 2%、8%、12%。
定义两个函数,一个用来获取缴纳基数,另一个根据基数和比例计算单月缴纳的五险一金。
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

 计算一年的到手工资


写一个 for 循环调用上面的函数即可算出全年的工资和应缴个税。
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


调用 calc_salary 函数,需要传入以下参数:月薪、社保缴纳比例、6项附加扣除和年终奖发几个月。
函数会循环12次计算每个月到手工资与个税。

5

 计算年终奖


每个企业发放年终奖的时间不一样,这里我默认按照12月处理。
为了对比年终奖变化,会把调整前后的年终奖到手金额都计算出来,调整后年终奖很简单,因为累计到全年所得,计算方式跟算每月个税是一样的。
调整前的需要定义新的税率表和个税计算函数


# 年终奖税率表
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)] # 调整index1开始
)

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)


页面效果如下:

左侧可以设置月薪、五险一金缴纳基数、年终奖,右侧展示每个月薪、年终奖到手金额和个税。
按照 4 个月年终奖算:
  • 月薪2w:年终奖少 5k

  • 月薪3w-4w:年终奖少 1.3w

  • 月薪 5w:年终奖少 2w
每个企业年终奖金额、发放时间、其他收入(如:补贴、加班费)都不太一样,感兴趣的朋友可以根据自己情况下载代码运行。
项目代码已上传,由于公众号后台配置的自动回复规则超过限制,
所以大家加我微信:pythonbrief,然后回复「扣税工具」即可获取。



扫码即可加我微信

回复:扣税工具 领取源码

万水千山总是情,点个 👍 行不行


--END--

如何找到我:

近期优质文章:

Linux里的宝塔,真正的宝塔!详细教程

10行代码!

拥有一台服务器后,我竟然这么酷?

学习更多:
整理了我开始分享学习笔记到现在超过250篇优质文章,涵盖数据分析、爬虫、机器学习等方面,别再说不知道该从哪开始,实战哪里找了
点赞”就是对博主最大的支持 
浏览 46
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报