Skip to content

8. 应用练习 – 版本 1

8.1. 问题

Image

上表用于计算纳税人仅申报工资收入的简化情况下的税额。如注释(1)所示,由此计算出的税额是未考虑以下三项机制的税额:

  • 针对高收入群体的家庭商数上限;
  • 针对低收入群体的减免和税额减免;

因此,税额计算包括以下步骤 [http://impotsurlerevenu.org/comprendre-le-calcul-de-l-impot/1217-calcul-de-l-impot-2019.php]

Image

我们拟编写一个程序,用于计算纳税人的税额,具体针对仅需申报工资收入的简化情况:

8.1.1. 毛税额计算

应纳税额可按以下方式计算:

首先计算纳税人的份额数:

  • 每位父母计为1份;
  • 前两名子女各计1/2份;
  • 后续子女每人计1份:

因此份额总数为:

  • nbParts=1+nbEnfants*0.5+(子女数-2)*0.5(若雇员未婚);
  • 若已婚,则nbParts=2+nbEnfants*0.5+(nbEnfants-2)*0.5;
    • 其中 nbEnfants 为其子女数;
  • 计算应税收入 R=0.9*S,其中 S 为年薪;
  • 计算家庭分摊系数 QF=R/nbParts;
  • 根据以下数据(2019年)计算应纳税额 I
9964
0
0
27519
0.14
1394.96
73779
0.3
5798
156244
0.4
13913.69
0
0.45
20163.45

每行有 3 个字段:champ1champ2champ3。 要计算税款 I,需查找满足 QF<=字段1 的第一行,并取该行的数值。例如,对于一名已婚且有两个孩子的员工,年薪 S 为 50000 欧元:

应税收入:R=0.9*S=45000

份额数:nbParts=2+2*0.5=3

家庭分额:QF=45000/3=15000

满足 QF<=field1 的第一行如下:

    27519    0.14    1394.96

因此,税额 I 等于 0.14*R – 1394.96*nbParts=[0,14*45000-1394,96*3]=2115。税额向下四舍五入至整欧元。

若从第一行起即满足QF<=field1,则税额为零。

若 QF 满足关系 QF<=field1 从未成立,则使用最后一行中的系数。此处:

    0    0.45    20163.45

由此得出应纳税额 I=0.45*R – 20163.45*nbParts

8.1.2. 家庭商数上限

Image

要判断家庭商数上限 QF 是否适用,需重新计算不含子女的应纳税额。仍以已婚、育有两名子女且年薪 S 为 50000 欧元的雇员为例:

应税收入:R=0.9*S=45000

分额数:nbParts=2(不再计入子女)

家庭分额:QF=45000/2=22500

满足 QF<=field1 的第一行如下:

    27519    0.14    1394.96

因此,税额 I 等于 0.14*R – 1394.96*nbParts=[0,14*45000-1394,96*2]=3510

与子女相关的最高收益:1551 * 2 = 3102 欧元

最低税额:3510-3102 = 408 欧元

已计算的含3个税额份额的应纳税额2115欧元高于最低税额408欧元,因此此处不适用家庭税额上限。

一般而言,应纳税额为 (税款1, 税款2) 中的较大值,其中:

  • [impôt1]:为包含子女计算的应纳税额;
  • [impôt2]:是不计入子女的应纳税额,并减去与子女相关的最高抵扣额(此处为每半份1551欧元);

8.1.3. 减免额的计算

Image

仍以已婚且有两名子女、年薪S为50000欧元的雇员为例:

上一步计算出的应纳税额(2115)低于已婚夫妇的2627欧元(单身人士为1595欧元):因此适用减免。其计算方式如下:

减免额 = 阈值(已婚夫妇=1970/单身=1196)- 0.75 × 应纳税额

折算额=1970-0.75×2115=383.75,四舍五入后为384欧元。

新应纳税额=2115-384=1731欧元

8.1.4. 减税额计算

Image

当收入低于一定门槛时,对前文计算出的应纳税额给予20%的减免。2019年的门槛如下:

  • 单身:21037欧元;
  • 已婚夫妇:42074欧元;(上文示例中使用的37968这一数字似乎有误);

该门槛值需增加以下数值:3797 *(子女带来的半份数)。

仍以那位已婚、育有两名子女且年薪S为50000欧元的雇员为例:

  • 其应税收入(45000欧元)低于门槛(42074+2*3797)=49668欧元;
  • 因此他有权享受20%的税额减免:1731 * 0.2 = 346.2欧元(四舍五入为347欧元);
  • 纳税人的应纳税额为:1731-347=1384欧元;

8.1.5. 净税额计算

我们的计算到此为止:应缴净税额1384欧元。实际上,纳税人还可以享受其他减免,特别是针对向公益或公共利益组织捐赠的减免。

8.1.6. 高收入人群的情况

前面的例子适用于大多数工薪阶层。然而,高收入群体的税款计算方式有所不同。

8.1.6.1. 年度收入10%减免的上限

在大多数情况下,应税收入按以下公式计算:R=0.9*S,其中S为年薪。这被称为10%的减免。该减免设有上限。2019年:

  • 减免额不得超过12502欧元;
  • 不得低于437欧元;

以一名未婚且无子女、年薪为200000欧元的雇员为例:

  • 10%的减免额为200,000欧元 > 12,502欧元。因此,减免额被调整为12,502欧元;

8.1.6.2. 家庭系数上限

假设出现第 |家庭系数上限| 段所述的家庭上限情况。以一对育有三个子女、年收入为100,000欧元的夫妇为例。重新梳理计算步骤:

  • 10%的免税额为10000欧元 < 12502欧元。因此应税收入R为100000-10000=90000欧元;
  • 该夫妇拥有nbParts=2+0.5×2+1=4个份额;
  • 因此其家庭分额为 QF = R / nbParts = 90000 / 4 = 22500 欧元;
  • 子女应纳税额I1为I1=0.14×90000-1394.96×4=7020欧元;
  • 子女的应纳税额I2:
  • QF=90000/2=45000欧元;
  • I2=0.3*90000-5798*2=15404欧元;
  • 家庭配额上限规则规定,子女带来的增益不得超过(1551×4个半份额)=6204欧元。但此处该数值为I2-I1=15404-7020=8384欧元,因此高于6204欧元;
  • 因此,应纳税额重新计算为 I3=I2-6204=15404-6204=9200欧元;

这对夫妇既无免税额也无减免,其最终应纳税额为9200欧元。

8.1.7. 官方数据

税款计算较为复杂。在本文中,我们将通过以下示例进行演示。 结果来自税务部门的模拟器 |https://www3.impots.gouv.fr/simulateur/calcul_impot/2019/simplifie/index.htm|:

纳税人
官方结果
本文算法计算结果
一对夫妇,育有2个孩子,年收入55555欧元
税额=2815欧元
税率=14%
税额=2814欧元
税率=14%
一对夫妇,育有2个孩子,年收入50000欧元
税额=1385欧元
减免额=720欧元
减免=0欧元
税率=14%
应纳税额=1384欧元
减免额=384欧元
减免额=347欧元
税率=14%
一对夫妇,育有3个孩子,年收入50000欧元
税额=0欧元
减免额=384欧元
减免额=346欧元
税率=14%
应纳税额=0欧元
折扣=720欧元
减免额=0欧元
税率=14%
单身,有2个孩子,年收入100000欧元
应纳税额=19884欧元
减免额=0欧元
减免额=0欧元
税率=41%
应纳税额=19884欧元
附加税=4480欧元
减免=0欧元
减免额=0欧元
税率=41%
单身,育有3名子女,年收入100000欧元
应纳税额=16782欧元
减免额=0欧元
减免额=0欧元
税率=41%
应纳税额=16782欧元
附加税=7176欧元
减免=0欧元
减免额=0欧元
税率=41%
一对夫妇,育有3个孩子,年收入100000欧元
应纳税额=9200欧元
减免额=0欧元
减免额=0欧元
税率=30%
应纳税额=9200欧元
附加税=2180欧元
减免=0欧元
减免额=0欧元
税率=30%
一对夫妇,育有5个孩子,年收入100000欧元
应纳税额=4230欧元
减免额=0欧元
减免额=0欧元
税率=14%
应纳税额=4230欧元
减免额=0欧元
减免=0欧元
税率=14%
单身无子女,年收入100000欧元
应纳税额=22986欧元
减免额=0欧元
减免额=0欧元
税率=41%
应纳税额=22986欧元
附加费=0欧元
减免=0欧元
减免额=0欧元
税率=41%
一对夫妇,育有2个孩子,年收入30000欧元
应纳税额=0欧元
减免额=0欧元
减免额=0欧元
税率=0%
税款=0欧元
折扣=0欧元
减免=0欧元
税率=0%
单身无子女,年收入200000欧元
应纳税额=64211欧元
减免额=0欧元
减免额=0欧元
税率=45%
应纳税额=64210欧元
附加税=7498欧元
减免=0欧元
减免额=0欧元
税率=45%
一对夫妇,育有3个孩子,年收入200000欧元
应纳税额=42,843欧元
减免额=0欧元
减免额=0欧元
税率=41%
应纳税额=42,842欧元
附加税=17283欧元
减免=0欧元
减免额=0欧元
税率=41%

上文所称的附加税,是指高收入者因以下两种情况而需额外缴纳的税款:

  • 年度收入10%免税额的上限;
  • 家庭分摊额的上限;

该指标无法验证,因为税务部门的模拟器未提供该数据。

可以看出,该文档中的算法每次计算出的税额均准确无误,但存在1欧元的误差范围。该误差源于四舍五入。所有金额有时四舍五入至更高的一欧元,有时则四舍五入至更低的一欧元。 由于我并不了解官方规则,因此文档中算法的金额进行了如下四舍五入:

  • 折扣和减免金额向上取整至整欧元;
  • 附加费和最终税额向下取整;

我们将开发多个版本的税款计算应用程序。

8.2. 版本 1

Image

8.2.1. 主脚本

我们首先介绍一个初始程序,其中:

  • 计算税款所需的数据以列表和常量的形式硬编码在代码中;
  • 纳税人的数据(已婚、子女、工资)存储在第一个文本文件 [taxpayersdata.txt] 中;
  • 税款计算结果(已婚、子女、工资、税额)存储在第二个文本文件 [résultats.txt] 中;

脚本 [v-01/main.py] 如下:


# 模块
import sys

from impots.v01.shared.impôts_module_01 import *

# main -----------------------

# 常量
# 纳税人文件
DATA = "./data/taxpayersdata.txt"
# 结果文件
RESULTATS = "./data/résultats.txt"

try:
    # 读取纳税人数据
    tax_payers = get_taxpayers_data(DATA)
    # 结果列表
    results = []
    # 计算纳税人税款
    for tax_payer in tax_payers:
        # 税款计算返回一个键值表
        # ['marié', 'enfants', 'salaire', 'impôt', 'surcôte', 'décôte', 'réduction', 'taux']
        result = calcul_impôt(tax_payer['marié'], tax_payer['enfants'], tax_payer['salaire'])
        # 词典已添加到结果列表中
        results.append(result)
    # 保存结果
    record_results(RESULTATS, results)
except BaseException as erreur:
    # 可能出现各种错误:文件不存在、文件内容不正确
    # 显示错误并退出应用程序
    print(f"l'erreur suivante s'est produite : {erreur}]\n")
    sys.exit()

注释

  • 第 4 行:使用模块 [impots.v01.modules.impôts_module_01]需注意,此路径是从项目根目录 PyCharm 开始计算的;
  • 第 10 行:文件 [data/taxpayersdata.txt] 内容如下:
oui,2,55555
oui,2,50000
oui,3,50000
non,2,100000
non,3,100000
oui,3,100000
oui,5,100000
non,0,100000
oui,2,30000
non,0,200000
oui,3,200000

每行代表一个由三个元素组成的元组 [marié / pacsé ou pas, nombre d'enfants, salaire annuel en euros]

  • 第 12 行:用于存放文件 [taxpayersdata.txt] 中每位纳税人税款计算结果的文件。其内容如下:
{'marié': 'oui', 'enfants': 2, 'salaire': 55555, 'impôt': 2814, 'surcôte': 0, 'décôte': 0, 'réduction': 0, 'taux': 0.14}
{'marié': 'oui', 'enfants': 2, 'salaire': 50000, 'impôt': 1384, 'surcôte': 0, 'décôte': 384, 'réduction': 347, 'taux': 0.14}
{'marié': 'oui', 'enfants': 3, 'salaire': 50000, 'impôt': 0, 'surcôte': 0, 'décôte': 720, 'réduction': 0, 'taux': 0.14}
{'marié': 'non', 'enfants': 2, 'salaire': 100000, 'impôt': 19884, 'surcôte': 4480, 'décôte': 0, 'réduction': 0, 'taux': 0.41}
{'marié': 'non', 'enfants': 3, 'salaire': 100000, 'impôt': 16782, 'surcôte': 7176, 'décôte': 0, 'réduction': 0, 'taux': 0.41}
{'marié': 'oui', 'enfants': 3, 'salaire': 100000, 'impôt': 9200, 'surcôte': 2180, 'décôte': 0, 'réduction': 0, 'taux': 0.3}
{'marié': 'oui', 'enfants': 5, 'salaire': 100000, 'impôt': 4230, 'surcôte': 0, 'décôte': 0, 'réduction': 0, 'taux': 0.14}
{'marié': 'non', 'enfants': 0, 'salaire': 100000, 'impôt': 22986, 'surcôte': 0, 'décôte': 0, 'réduction': 0, 'taux': 0.41}
{'marié': 'oui', 'enfants': 2, 'salaire': 30000, 'impôt': 0, 'surcôte': 0, 'décôte': 0, 'réduction': 0, 'taux': 0}
{'marié': 'non', 'enfants': 0, 'salaire': 200000, 'impôt': 64210, 'surcôte': 7498, 'décôte': 0, 'réduction': 0, 'taux': 0.45}
{'marié': 'oui', 'enfants': 3, 'salaire': 200000, 'impôt': 42842, 'surcôte': 17283, 'décôte': 0, 'réduction': 0, 'taux': 0.41}
  • 第16行:从[taxpayersdata.txt]中提取纳税人数据。获取一组键值对列表[marié, enfants, salaire],每个键值对代表一名纳税人;
  • 第17-25行:计算列表[taxPayers]中纳税人的税额。获取列表[results],其中每个元素再次是一个键值对列表[marié, enfants, salaire, impôt, surcôte, décôte, réduction, taux]
  • 第27行:将结果列表[results]以上述形式保存到文件[résultats.txt]中
  • 第28-32行:捕获模块[impots.v01.modules.impôts_module_01]中可能抛出的所有异常;

接下来我们将详细介绍脚本[main]所使用的三个函数:

  • [get_taxpayers_data]:用于读取纳税人的数据;
  • [calcul_impôt]:用于计算纳税人的税款;
  • [record_results]:用于将结果保存到文本文件中;

所有这些功能均位于模块 [impots.modules.impôts_module_01] 中。

8.2.2. 模块 [impots.v01.shared.impôts_module_01]

用于计算税款的必要函数已汇集在模块 [impots.v01.shared.impôts_module_01] 中:

Image

  • [1] 中:定义税额计算常量;
  • [2] 中:模块函数列表;

8.2.3. 函数 [get_taxpayers_data]

函数 [get_taxpayers_data] 如下:


# 导入
import codecs


# 读取纳税人数据
# ----------------------------------------
def get_taxpayers_data(taxpayers_filename: str) -> list:
    # 读取纳税人数据
    file = None
    try:
        # 纳税人列表
        taxpayers = []
        # 打开文件
        file = codecs.open(taxpayers_filename, "r", "utf8")
        # 读取纳税人文件的第一行
        ligne = file.readline().strip()
        # 只要还有待处理的行
        while ligne != '':
            # 提取构成该行的“已婚”、“子女”和“工资”这3个字段
            (marié, enfants, salaire) = ligne.split(",")
            # 将其添加到纳税人列表中
            taxpayers.append({'marié': marié.strip().lower(), 'enfants': int(enfants), 'salaire': int(salaire)})
            # 读取纳税人文件中的新一行
            ligne = file.readline().strip()
        # 输出结果
        return taxpayers
    finally:
        # 若文件已打开,则关闭该文件
        if file:
            file.close()

  • 第 7 行:[taxpayers_filename] 是待处理文件的名称。该函数返回一个列表;
  • 第18-24行:处理文本文件中[marié, enfants, salaire]行的循环;
  • 第20行:提取该行的三个元素。此处假设该行语法正确,即确实包含预期的三个元素;
  • 第22行:使用键[marié, enfants, salaire]构建一个字典,并将该字典添加到列表[taxPayers]中
  • 第26行:文件处理完成后,返回列表[taxPayers]
  • 第10-30行:请注意,第10行的[try]中未添加[catch]子句。[catch]子句并非必需。 第27行,我们添加了一个[finally]语句,用于在任何情况下(无论是否发生错误)关闭文本文件;
  • 该 try / finally 结构会放行可能出现的异常(因为没有 catch 语句)。该异常将上报至主脚本 [main],后者将终止执行并显示该异常(参见“主脚本”部分)。该机制已被用于该模块的大多数函数;

8.2.4. 函数 [calcul_impôt]

函数 [calcul_impôt] 如下所示:


# 导入
import codecs
import math

# 2019年所得税税率档次
limites = [9964, 27519, 73779, 156244, 0]
coeffr = [0, 0.14, 0.3, 0.41, 0.45]
coeffn = [0, 1394.96, 5798, 13913.69, 20163.45]

# 2019年税额计算常数
PLAFOND_QF_DEMI_PART = 1551
PLAFOND_REVENUS_CELIBATAIRE_POUR_REDUCTION = 21037
PLAFOND_REVENUS_COUPLE_POUR_REDUCTION = 42074
VALEUR_REDUC_DEMI_PART = 3797
PLAFOND_DECOTE_CELIBATAIRE = 1196
PLAFOND_DECOTE_COUPLE = 1970
PLAFOND_IMPOT_COUPLE_POUR_DECOTE = 2627
PLAFOND_IMPOT_CELIBATAIRE_POUR_DECOTE = 1595
ABATTEMENT_DIXPOURCENT_MAX = 12502
ABATTEMENT_DIXPOURCENT_MIN = 437


# 税款计算
# ----------------------------------------
def calcul_impôt(marié: str, enfants: int, salaire: int) -> dict:
    # 已婚:是,否
    # 子女:子女人数
    # 工资:年薪
    # 限额、系数:用于计算税款的数据表
    #
    # 含子女的税款计算
    result1 = calcul_impôt_2(marié, enfants, salaire)
    impot1 = result1["impôt"]
    # 不含子女的税款计算
    if enfants != 0:
        result2 = calcul_impôt_2(marié, 0, salaire)
        impot2 = result2["impôt"]
        # 家庭商数上限的适用
        if enfants < 3:
            # PLAFOND_QF_DEMI_PART 欧元(前两名子女)
            impot2 = impot2 - enfants * PLAFOND_QF_DEMI_PART
        else:
            # PLAFOND_QF_DEMI_PART 欧元(前两名子女),后续子女金额翻倍
            impot2 = impot2 - 2 * PLAFOND_QF_DEMI_PART - (enfants - 2) * 2 * PLAFOND_QF_DEMI_PART
    else:
        impot2 = impot1
        result2 = result1

    # 取税率最高的那项,并适用相应的税率和附加费
    if impot1 > impot2:
        impot = impot1
        taux = result1["taux"]
        surcôte = result1["surcôte"]
    else:
        surcôte = impot2 - impot1 + result2["surcôte"]
        impot = impot2
        taux = result2["taux"]

    # 计算可能的减免额
    décôte = get_décôte(marié, salaire, impot)
    impot -= décôte
    # 计算可能的减税额
    réduction = get_réduction(marié, salaire, enfants, impot)
    impot -= réduction
    # 结果
    return {"marié": marié, "enfants": enfants, "salaire": salaire, "impôt": math.floor(impot), "surcôte": surcôte,
            "décôte": décôte, "réduction": réduction, "taux": taux}

注释

  • 第6-8行:税率区间(参见“毛税计算”一节);
  • 第11-20行:税额计算的常量;
  • 需注意,第5-20行初始化的元素在后续将描述的函数中均为全局变量。因此,只要调用这些元素的函数未声明同名变量,这些元素的值即为已知;
  • 第5-20行的数值每年都会变化。此处为2019年的数值;
  • 第25行:函数[calcul_impôt]接收三个参数:
    • [marié]:是/否,表示纳税人是否已婚或处于民事伴侣关系;
    • [enfants]:其子女数量;
    • [salaire]:其年薪(单位:欧元);
  • 第 31-33 行:考虑子女情况计算税额;
  • 第34-47行:这些行用于实施家庭系数上限(参见“家庭系数上限”段落);
  • 第49-57行:计算纳税人的税率以及可能的附加税(参见|高收入情况|章节);
  • 第59-61行:计算可能的减免额(参见|减免额计算|章节);
  • 第62-64行:计算应缴税款的减免额(参见“减税计算”一节);

该算法较为复杂,除注释中所述内容外,我们将不再赘述。该算法实现了|问题|段落中描述的税款计算方式。

8.2.5. 函数 [calcul_impôt_2]

函数 [calcul_impôt] 调用了以下函数 [calcul_impôt_2]


def calcul_impôt_2(marié: str, enfants: int, salaire: int) -> list:
    # 已婚:是,否
    # 子女:子女人数
    # 工资:年薪
    # 限额、系数:用于计算税款的数据表
    #
    # 份额数
    marié = marié.strip().lower()
    if marié == "oui":
        nb_parts = enfants / 2 + 2
    else:
        nb_parts = enfants / 2 + 1

    # 从第3个子女起,每个子女计1份
    if enfants >= 3:
        # 第3个及以后的每个孩子额外增加半份
        nb_parts += 0.5 * (enfants - 2)

    # 应税收入
    revenu_imposable = get_revenu_imposable(salaire)
    # 附加费
    surcôte = math.floor(revenu_imposable - 0.9 * salaire)
    # 用于四舍五入问题
    if surcôte < 0:
        surcôte = 0

    # 家庭分摊系数
    quotient = revenu_imposable / nb_parts
    # 被放置在限额表的末尾,以终止后续循环
    limites[len(limites) - 1] = quotient
    # 计算税额
    i = 0
    while quotient > limites[i]:
        i += 1
        # 由于将家庭收入商置于限额表末尾,因此前面的循环
    # 不会超出限值数组范围

    # 现在可以计算税额
    impôt = math.floor(revenu_imposable * coeffr[i] - nb_parts * coeffn[i])
    # 结果
    return {"impôt": impôt, "surcôte": surcôte, "taux": coeffr[i]}

该算法已在第 8.1.1 节中进行了描述。

8.2.6. 函数 [get_décôte]

函数 [get_décôte] 实现了税款折扣(如有)的计算(参见“折扣计算”一节):


# 计算可能的折扣
def get_décôte(marié: str, salaire: int, impots: int) -> int:
    # 初始时,折扣为零
    décôte = 0
    # 享受减免的最高税额
    plafond_impôt_pour_décôte = PLAFOND_IMPOT_COUPLE_POUR_DECOTE if marié == "oui" else PLAFOND_IMPOT_CELIBATAIRE_POUR_DECOTE
    if impots < plafond_impôt_pour_décôte:
        # 折扣上限
        plafond_décôte = PLAFOND_DECOTE_COUPLE if marié == "oui" else PLAFOND_DECOTE_CELIBATAIRE
        # 理论折扣
        décôte = plafond_décôte - 0.75 * impots
        # 折减额不得超过税额
        if décôte > impots:
            décôte = impots

        # 无折减 <0
        if décôte < 0:
            décôte = 0

    # 结果
    return math.ceil(décôte)

8.2.7. 函数 [get_réduction]

函数 [get_réduction] 实现了应缴税款减免额的计算(参见“税款减免计算”一节):


# 计算可能的减免
def get_réduction(marié: str, salaire: int, enfants: int, impots: int) -> int:
    # 享受20%减免的收入上限
    plafond_revenu_pour_réduction = PLAFOND_REVENUS_COUPLE_POUR_REDUCTION if marié == "oui" else PLAFOND_REVENUS_CELIBATAIRE_POUR_REDUCTION
    plafond_revenu_pour_réduction += enfants * VALEUR_REDUC_DEMI_PART
    if enfants > 2:
        plafond_revenu_pour_réduction += (enfants - 2) * VALEUR_REDUC_DEMI_PART

    # 应税收入
    revenu_imposable = get_revenu_imposable(salaire)
    # 减免
    réduction = 0
    if revenu_imposable < plafond_revenu_pour_réduction:
        # 20%的减免
        réduction = 0.2 * impots

    # 结果
    return math.ceil(réduction)

8.2.8. 函数 [get_revenu_imposable]

函数 [get_revenu_imposable] 根据年薪计算应税收入:


# revenu_imposable = salaireAnnuel - 免税额
# 免税额有最低和最高限额
# ----------------------------------------
def get_revenu_imposable(salaire: int) -> int:
    # 扣除额为工资的10%
    abattement = 0.1 * salaire
    # 该扣除额不得超过 ABATTEMENT_DIXPOURCENT_MAX
    if abattement > ABATTEMENT_DIXPOURCENT_MAX:
        abattement = ABATTEMENT_DIXPOURCENT_MAX

    # 扣除额不得低于 ABATTEMENT_DIXPOURCENT_MIN
    if abattement < ABATTEMENT_DIXPOURCENT_MIN:
        abattement = ABATTEMENT_DIXPOURCENT_MIN

    # 应税收入
    revenu_imposable = salaire - abattement
    # 结果
    return math.floor(revenu_imposable)

8.2.9. 函数 [record_results]

函数 [record_results] 将税额计算结果保存到文本文件中:


# 将结果写入文本文件
# ----------------------------------------
def record_results(results_filename: str, results: list):
    # results_filename:用于存储结果的文本文件名
    # results:以字典列表形式呈现的结果列表
    # 每个字典都写在一行文本中
    résultats = None
    try:
        # 打开结果文件
        résultats = codecs.open(results_filename, "w", "utf8")
        # 处理纳税人数据
        for result in results:
            # 将结果写入结果文件
            résultats.write(f"{result}\n")
            # 下一位纳税人
    finally:
        # 若文件已打开,则关闭
        if résultats:
            résultats.close()

8.2.10. 结果

如前所述,使用以下纳税人文件 [taxpayersdata.txt]

oui,2,55555
oui,2,50000
oui,3,50000
non,2,100000
non,3,100000
oui,3,100000
oui,5,100000
non,0,100000
oui,2,30000
non,0,200000
oui,3,200000

脚本 [main.py] 会生成以下文件:

{'marié': 'oui', 'enfants': 2, 'salaire': 55555, 'impôt': 2814, 'surcôte': 0, 'décôte': 0, 'réduction': 0, 'taux': 0.14}
{'marié': 'oui', 'enfants': 2, 'salaire': 50000, 'impôt': 1384, 'surcôte': 0, 'décôte': 384, 'réduction': 347, 'taux': 0.14}
{'marié': 'oui', 'enfants': 3, 'salaire': 50000, 'impôt': 0, 'surcôte': 0, 'décôte': 720, 'réduction': 0, 'taux': 0.14}
{'marié': 'non', 'enfants': 2, 'salaire': 100000, 'impôt': 19884, 'surcôte': 4480, 'décôte': 0, 'réduction': 0, 'taux': 0.41}
{'marié': 'non', 'enfants': 3, 'salaire': 100000, 'impôt': 16782, 'surcôte': 7176, 'décôte': 0, 'réduction': 0, 'taux': 0.41}
{'marié': 'oui', 'enfants': 3, 'salaire': 100000, 'impôt': 9200, 'surcôte': 2180, 'décôte': 0, 'réduction': 0, 'taux': 0.3}
{'marié': 'oui', 'enfants': 5, 'salaire': 100000, 'impôt': 4230, 'surcôte': 0, 'décôte': 0, 'réduction': 0, 'taux': 0.14}
{'marié': 'non', 'enfants': 0, 'salaire': 100000, 'impôt': 22986, 'surcôte': 0, 'décôte': 0, 'réduction': 0, 'taux': 0.41}
{'marié': 'oui', 'enfants': 2, 'salaire': 30000, 'impôt': 0, 'surcôte': 0, 'décôte': 0, 'réduction': 0, 'taux': 0}
{'marié': 'non', 'enfants': 0, 'salaire': 200000, 'impôt': 64210, 'surcôte': 7498, 'décôte': 0, 'réduction': 0, 'taux': 0.45}
{'marié': 'oui', 'enfants': 3, 'salaire': 200000, 'impôt': 42842, 'surcôte': 17283, 'décôte': 0, 'réduction': 0, 'taux': 0.41}

这些结果与“官方数据”一节中的官方数据一致。

现在,让我们在控制台窗口中运行此版本:


(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\impots\v01>python main.py
Traceback (most recent call last):
  File "main.py", line 4, in <module>
    from impots.v01.shared.impôts_module_01 import *
ModuleNotFoundError: No module named 'impots'

我们再次遇到了之前出现过的错误:即找不到某个模块,此处是 [impots] 模块。需要提醒的是,这意味着:

  • Python 解释器已逐一遍历 Python Path 中的所有目录;
  • 但在任何一个文件夹中,都没有找到包含脚本 [impots.py] 的文件夹;

[v02]版本将解决此问题。