Skip to content

8. 应用练习 – 版本 1

8.1. 问题

上表使我们能够计算仅需申报工资收入的纳税人的简化税额。如注释(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+nbChildren*0.5+(nbChildren-2)*0.5(若员工未婚);
  • 若已婚,则 nbParts=2+nbChildren*0.5+(nbChildren-2)*0.5;
    • 其中 nbChildren 表示子女人数;
  • 我们计算应税收入 R = 0.9 * S,其中 S 为年薪;
  • 家庭商 QF 按 QF = R / nbParts 计算
  • 根据以下数据(2019年),我们计算应纳税额 I
9964
0
0
27,519
0.14
1,394.96
73,779
0.3
5,798
156,244
0.4
13,913.69
0
0.45
20163.45

每行包含 3 个字段:field1field2field3。要计算税款 I,我们找出满足 QF <= field1 的第一行,并取该行的数值。例如,对于一名已婚员工,有两个孩子,年薪 S 为 50,000 欧元:

应税收入:R=0.9*S=45,000

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

家庭商:QF = 45,000 / 3 = 15,000

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

    27519    0.14    1394.96

因此,税款 I 等于 0.14*R – 1394.96*股数=[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. 家族商数上限

为确定家庭商数(QF)上限是否适用,我们需重新计算不包含子女的应纳税额。再次以那位有两个子女、年薪S为50,000欧元的已婚雇员为例:

应税收入:R = 0.9 * S = 45,000

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

家庭商数:QF = 45,000 / 2 = 22,500

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

    27519    0.14    1394.96

因此,税款 I 等于 0.14*R – 1394.96*股数 = [0.14*45,000 – 1394.96*2] = 3,510

最高子女相关福利:1551 × 2 = 3102 欧元

最低税额:3,510 – 3,102 = 408 欧元

按3个税率档次计算的应纳税额(已计算为2,115欧元)高于408欧元的最低税额,因此此处不适用家庭限额。

一般而言,税前税额大于 (tax1, tax2),其中:

  • [税1]:包含子女计算的应纳税额;
  • [税款2]:不计入子女且扣除与子女相关的最高抵免额(此处为每半份1,551欧元)后计算出的税款总额;

8.1.3. 减免额的计算

Image

仍以一名已婚、育有两名子女且年薪S为50,000欧元的雇员为例:

上一步计算出的应纳税额(2,115)低于夫妇的2,627欧元(单身人士为1,595欧元):因此适用减免。其计算方法如下:

减免额 = 起征点(已婚夫妇 = 1,970 / 单身 = 1,196)- 0.75 * 税前税额

减免额 = 1,970 – 0.75 * 2,115 = 383.75,四舍五入后为384欧元。

新应纳税额 = 2,115 – 384 = 1,731 欧元

8.1.4. 减税额计算

Image

当应纳税额低于特定门槛时,将对前文计算得出的应纳税额适用20%的减免。2019年的门槛如下:

  • 单身:21,037欧元;
  • 已婚夫妇:42,074欧元;(上文示例中使用的37,968欧元似乎有误);

该门槛值需增加以下数值:3,797 *(子女所贡献的半份份额数量)。

再次以那位有两个孩子、年薪S为50,000欧元的已婚雇员为例:

  • 其应税收入(45,000欧元)低于门槛值(42,074 + 2 × 3,797)= 49,668欧元;
  • 因此,他有权享受20%的减税:1,731 * 0.2 = 346.2欧元,四舍五入为347欧元;
  • 纳税人的应纳税额为:1,731 – 347 = 1,384欧元;

8.1.5. 净税额计算

我们的计算到此结束:应缴税额为1,384欧元。实际上,纳税人可能还有资格享受其他扣除,特别是针对向公共或公益组织捐款的扣除。

8.1.6. 高收入人群

前面的例子适用于大多数雇员。然而,高收入者的税款计算方式有所不同。

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

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

  • 不得超过12,502欧元;
  • 不得低于437欧元;

让我们以一名无子女的未婚雇员为例,其年薪为200,000欧元:

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

8.1.6.2. 家庭系数上限

让我们考虑一个适用第 |家庭系数上限| 节所述家庭上限的情况。以一对育有三个子女、年收入为100,000欧元的夫妇为例。让我们再次回顾计算步骤:

  • 10%的扣除额为10,000欧元 < 12,502欧元。因此,应税收入R为100,000 - 10,000 = 90,000欧元;
  • 该夫妇的份额数 nbParts = 2 + 0.5 × 2 + 1 = 4 份;
  • 因此,其家庭商为 QF = R / nbParts = 90,000 / 4 = 22,500 欧元;
  • 他们子女的应纳税额 I1 为 I1 = 0.14 × 90,000 – 1,394.96 × 4 = 7,020 欧元;
  • 子女的税款总额 I2
  • QF = 90,000 / 2 = 45,000 欧元;
  • I2 = 0.3 × 90,000 – 5,798 × 2 = 15,404 欧元;
  • 家庭商数上限规则规定,子女带来的减免额不得超过(1,551 × 4个半份额)= 6,204欧元。然而,此处I2 – I1 = 15,404 – 7,020 = 8,384欧元,大于6,204欧元;
  • 因此,应纳税额重新计算为 I3 = I2 - 6,204 = 15,404 - 6,204 = 9,200 欧元;

这对夫妇既无法获得税收抵免,也无法享受减免,其最终应纳税额为9,200欧元。

8.1.7. 官方数据

税款计算较为复杂。本文档中的计算均基于以下示例。结果来源于税务机关的模拟器 |https://www3.impots.gouv.fr/simulateur/calcul_impot/2019/simplifie/index.htm|:

纳税人
官方结果
本文算法计算结果
一对夫妇,育有2名子女,年收入55,555欧元
税额 = 2,815欧元
税率 = 14%
税额 = 2,814 欧元
税率 = 14%
一对夫妇,育有2名子女,年收入为50,000欧元( )
税额 = 1,385 欧元
税收抵免 = 720欧元
减免 = 0 欧元
税率 = 14%
应纳税额 = 1,384欧元
折扣 = 384 欧元
抵免额 = 347 欧元
税率 = 14%
一对夫妇,育有3个孩子,年收入50,000欧元
税额 = 0 欧元
税收抵免 = 384 欧元
减免额 = 346 欧元
税率 = 14%
应纳税额 = 0欧元
折扣=720欧元
扣除额=0欧元
税率 = 14%
单身,有2个孩子,年收入100,000欧元
应缴税额 = 19,884欧元
税收抵免 = 0 欧元
扣除额 = 0 欧元
税率 = 41%
应纳税额 = 19,884欧元
附加费 = 4,480 欧元
折扣 = 0 欧元
减免 = 0 欧元
税率 = 41%
单身,育有3名子女,年收入100,000欧元
应纳税额 = 16,782 欧元
税收抵免=0欧元
扣除额 = 0 欧元
税率 = 41%
应纳税额 = 16,782欧元
附加费 = 7,176 欧元
折扣=0欧元
减免 = 0 欧元
税率 = 41%
一对夫妇,育有3名子女,年收入100,000欧元
应缴税款 = 9,200 欧元
税收抵免=0欧元
扣除额 = 0 欧元
税率 = 30%
应纳税额 = 9,200 欧元
附加费 = 2,180 欧元
折扣=0欧元
减免 = 0 欧元
税率 = 30%
一对夫妇,育有5个孩子,年收入100,000欧元
应缴税款 = 4,230 欧元
税收抵免=0欧元
扣除额 = 0 欧元
税率 = 14%
应纳税额 = 4,230 欧元
折扣 = 0 欧元
扣除额=0欧元
税率 = 14%
单身,无子女,年收入100,000欧元
应纳税额 = 22,986 欧元
税收抵免 = 0 欧元
扣除额 = 0 欧元
税率 = 41%
应纳税额 = 22,986 欧元
附加费 = 0 欧元
折扣 = 0 欧元
减免 = 0 欧元
税率 = 41%
一对夫妇,育有两名子女,年收入为30,000欧元
应纳税额 = 0 欧元
税收抵免 = 0 欧元
扣除额 = 0 欧元
税率 = 0%
应纳税额 = 0 欧元
折扣 = 0 欧元
减免=0欧元
税率 = 0%
单身无子女,年收入200,000欧元
应纳税额 = 64,211 欧元
税收抵免=0欧元
扣除额 = 0 欧元
税率 = 45%
应纳税额 = 64,210欧元
附加费 = 7,498 欧元
折扣=0欧元
减免 = 0 欧元
税率 = 45%
一对夫妇,育有3名子女,年收入200,000欧元
应纳税额 = 42,843 欧元
税收抵免=0欧元
扣除额 = 0 欧元
税率 = 41%
应纳税额 = 42,842欧元
附加费 = 17,283 欧元
折扣=0欧元
减免 = 0 欧元
税率 = 41%

在上例中,“附加费”是指高收入者因以下两个因素而需额外支付的金额:

  • 年收入10%扣除额的上限;
  • 家庭津贴的上限;

由于税务机关的模拟器未提供该数据,因此无法核实此指标。

我们可以看到,该文档的算法每次都能计算出正确的税额,尽管存在1欧元的误差。这种误差源于四舍五入。在某些情况下,所有金额会被四舍五入至最接近的整数欧元,而在其他情况下则会四舍五入至最接近的整数欧元。由于我不熟悉官方规则,因此文档算法中的金额进行了如下四舍五入:

  • 折扣和减免金额向上取整至最接近的欧元;
  • 附加费和最终税额则向下舍入至最接近的欧元;

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

8.2. 版本 1

Image

8.2.1. 主脚本

我们提供一个初始程序,其中:

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

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

#  modules
import sys

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

#  hand -----------------------

#  constants
#  taxpayer file
DATA = "./data/taxpayersdata.txt"
#  results file
RESULTATS = "./data/résultats.txt"

try:
    #  reading taxpayer data
    tax_payers = get_taxpayers_data(DATA)
    #  results list
    results = []
    #  taxpayers' taxes are calculated
    for tax_payer in tax_payers:
        #  tax calculation returns a dictionary of keys
        #  ['married', 'children', 'salary', 'tax', 'surcôte', 'décôte', 'réduction', 'taux']
        result = calcul_impôt(tax_payer['marié'], tax_payer['enfants'], tax_payer['salaire'])
        #  the dictionary is added to the list of results
        results.append(result)
    #  we record the results
    record_results(RESULTATS, results)
except BaseException as erreur:
    #  there may be various errors: no file, incorrect file content
    #  display the error and exit the application
    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

每行代表一个包含三个元素的元组 [是否已婚或处于民事伴侣关系、子女数量、年薪(单位:欧元]

  • 第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] 中包含的纳税人数据。我们获取一个键为 [已婚, 子女, 工资] 的字典列表,每个字典代表一名纳税人;
  • 第17–25行:计算[taxPayers]列表中纳税人的税额。返回一个[results]列表,其中每个元素仍是键值对为[已婚, 子女, 工资, 税额, 附加费, 折扣, 减免, 税率]的字典;
  • 第 27 行:将 [results] 列表按上述格式保存到文件 [results.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] 函数如下:

#  imports
import codecs


#  reading taxpayer data
# ----------------------------------------
def get_taxpayers_data(taxpayers_filename: str) -> list:
    #  reading taxpayer data
    file = None
    try:
        #  list of taxpayers
        taxpayers = []
        #  open file
        file = codecs.open(taxpayers_filename, "r", "utf8")
        #  read the first line of the taxpayer file
        ligne = file.readline().strip()
        #  as long as there's a line left to operate
        while ligne != '':
            #  we retrieve the 3 fields married,children,salary which form the line
            (marié, enfants, salaire) = ligne.split(",")
            #  we add them to the list of taxpayers
            taxpayers.append({'marié': marié.strip().lower(), 'enfants': int(enfants), 'salaire': int(salaire)})
            #  a new line is read from the taxpayer file
            ligne = file.readline().strip()
        #  we return the result
        return taxpayers
    finally:
        #  close the file if it has been opened
        if file:
            file.close()

注释

  • 第 7 行:[taxpayers_filename] 是要处理的文件名。该函数返回一个列表;
  • 第 18–24 行:循环处理文本文件中的 [married, children, salary] 行;
  • 第 20 行:提取该行的三个元素。此处假设该行语法正确,即确实包含预期的三个元素;
  • 第 22 行:构建一个键为 [married, children, salary] 的字典,并将该字典添加到 [taxPayers] 列表中;
  • 第 26 行:文件处理完成后,返回 [taxPayers] 列表;
  • 第 10–30 行:请注意,第 10 行的 [try] 代码块中未添加 [catch] 子句。[catch] 子句并非强制要求。在第 27 行,添加了 [finally] 子句,用于在所有情况下(无论是否发生错误)关闭文本文件;
  • 这种 try/finally 结构允许潜在的异常逸出(因为没有 catch)。该异常将传播到主脚本 [main],主脚本将停止运行并显示该异常(参见 |主脚本| 部分)。该机制已被用于该模块的大多数函数;

8.2.4. [calcul_impôt] 函数

[calcul_impôt] 函数如下:

#  imports
import codecs
import math

#  2019 tax brackets
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]

#  constant for 2019 tax calculation
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


#  tAX CALCULATION
# ----------------------------------------
def calcul_impôt(marié: str, enfants: int, salaire: int) -> dict:
    #  married: yes, no
    #  children: number of children
    #  salary: annual salary
    #  limits, coeffr, coeffn: data tables for tax calculation
    #
    #  tax calculation with children
    result1 = calcul_impôt_2(marié, enfants, salaire)
    impot1 = result1["impôt"]
    #  tax calculation without children
    if enfants != 0:
        result2 = calcul_impôt_2(marié, 0, salaire)
        impot2 = result2["impôt"]
        #  application of the family allowance ceiling
        if enfants < 3:
            #  PLAFOND_QF_DEMI_PART euros for the first 2 children
            impot2 = impot2 - enfants * PLAFOND_QF_DEMI_PART
        else:
            #  PLAFOND_QF_DEMI_PART euros for the first 2 children, double for subsequent children
            impot2 = impot2 - 2 * PLAFOND_QF_DEMI_PART - (enfants - 2) * 2 * PLAFOND_QF_DEMI_PART
    else:
        impot2 = impot1
        result2 = result1

    #  we take the highest tax with the rate and surcharge that go with it
    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"]

    #  calculation of any discount
    décôte = get_décôte(marié, salaire, impot)
    impot -= décôte
    #  calculation of any tax reduction
    réduction = get_réduction(marié, salaire, enfants, impot)
    impot -= réduction
    #  result
    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行:[calculate_tax]函数接受三个参数:
    • [married]:是/否,表示纳税人是否已婚或处于民事伴侣关系;
    • [children]:子女数量;
    • [salary]:其年薪(单位为欧元);
  • 第31–33行:考虑子女因素的税额计算;
  • 第34–47行:这些行实现了家庭商数的上限(参见|家庭商数上限|章节);
  • 第49–57行:这些行用于计算纳税人的税率以及任何附加税(参见“高收入情况”部分);
  • 第59–61行:计算任何税收抵免(参见章节 |税收抵免的计算|);
  • 第62–64行:计算应纳税额的减免(参见“税额减免的计算”一节);

该算法相当复杂,我们不会比注释中提供的内容更深入地探讨。该算法实现了|问题|一节中描述的税款计算方法。

8.2.5. [calcul_tax_2] 函数

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

def calcul_impôt_2(marié: str, enfants: int, salaire: int) -> list:
    #  married: yes, no
    #  children: number of children
    #  salary: annual salary
    #  limits, coeffr, coeffn: data tables for tax calculation
    #
    #  number of shares
    marié = marié.strip().lower()
    if marié == "oui":
        nb_parts = enfants / 2 + 2
    else:
        nb_parts = enfants / 2 + 1

    #  1 part per child from the 3rd
    if enfants >= 3:
        #  an additional half share for each child from the 3rd onwards
        nb_parts += 0.5 * (enfants - 2)

    #  taxable income
    revenu_imposable = get_revenu_imposable(salaire)
    #  surcharge
    surcôte = math.floor(revenu_imposable - 0.9 * salaire)
    #  for rounding problems
    if surcôte < 0:
        surcôte = 0

    #  family quotient
    quotient = revenu_imposable / nb_parts
    #  is set at the end of the limit table to stop the following loop
    limites[len(limites) - 1] = quotient
    #  tAX CALCULATION
    i = 0
    while quotient > limites[i]:
        i += 1
        #  because we've placed quotient at the end of the limit array, the previous loop
    #  cannot go beyond the limit table

    #  now we can calculate the tax
    impôt = math.floor(revenu_imposable * coeffr[i] - nb_parts * coeffn[i])
    #  result
    return {"impôt": impôt, "surcôte": surcôte, "taux": coeffr[i]}

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

8.2.6. [get_discount] 函数

[get_discount] 函数实现了任何税收折扣的计算(参见 |折扣计算| 章节):

#  calculates any discount
def get_décôte(marié: str, salaire: int, impots: int) -> int:
    #  at the outset, a zero discount
    décôte = 0
    #  maximum tax amount to qualify for discount
    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:
        #  maximum discount
        plafond_décôte = PLAFOND_DECOTE_COUPLE if marié == "oui" else PLAFOND_DECOTE_CELIBATAIRE
        #  theoretical discount
        décôte = plafond_décôte - 0.75 * impots
        #  the discount cannot exceed the amount of tax due
        if décôte > impots:
            décôte = impots

        #  no discount <0
        if décôte < 0:
            décôte = 0

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

8.2.7. [get_reduction] 函数

[get_reduction] 函数实现了任何税收减免的计算(参见 |税收减免的计算| 章节):

#  calculates any reduction
def get_réduction(marié: str, salaire: int, enfants: int, impots: int) -> int:
    #  the income ceiling to qualify for the 20% reduction
    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

    #  taxable income
    revenu_imposable = get_revenu_imposable(salaire)
    #  reduction
    réduction = 0
    if revenu_imposable < plafond_revenu_pour_réduction:
        #  20% discount
        réduction = 0.2 * impots

    #  result
    return math.ceil(réduction)

8.2.8. [get_taxable_income] 函数

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

#  revenu_imposable = salaireAnnuel - allowance
#  the allowance has a minimum and a maximum
# ----------------------------------------
def get_revenu_imposable(salaire: int) -> int:
    #  10% salary deduction
    abattement = 0.1 * salaire
    #  this allowance may not exceed ABATTEMENT_DIXPOURCENT_MAX
    if abattement > ABATTEMENT_DIXPOURCENT_MAX:
        abattement = ABATTEMENT_DIXPOURCENT_MAX

    #  the allowance cannot be less than ABATTEMENT_DIXPOURCENT_MIN
    if abattement < ABATTEMENT_DIXPOURCENT_MIN:
        abattement = ABATTEMENT_DIXPOURCENT_MIN

    #  taxable income
    revenu_imposable = salaire - abattement
    #  result
    return math.floor(revenu_imposable)

8.2.9. [record_results] 函数

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

#  writing results to a text file
# ----------------------------------------
def record_results(results_filename: str, results: list):
    #  results_filename: the name of the text file in which to place the results
    #  results: the results list in the form of a dictionary list
    #  each dictionary is written on a line of text
    résultats = None
    try:
        #  opening the results file
        résultats = codecs.open(results_filename, "w", "utf8")
        #  taxpayer exploitation
        for result in results:
            #  enter the result in the results file
            résultats.write(f"{result}\n")
            #  next taxpayer
    finally:
        #  close the file if it has been opened
        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] 会生成以下文件 [results.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}

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

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


(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 路径中的所有目录;
  • 但在这些目录中,均未找到包含 [impots.py] 脚本的目录;

版本 [v02] 将提供此问题的解决方案。