Skip to content

11. Ejercicio práctico: version 3

Image

Esta nueva version introduce dos cambios:

  • los datos necesarios para el cálculo del impuesto y facilitados por la administración tributaria se incluyen en un archivo jSON [admindata.json]:

{
    "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],
    "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
}
  • los resultados del cálculo del impuesto también se colocarán en un archivo jSON [résultats.json]:

[
  {
    "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": 200000,
    "impôt": 42842,
    "surcôte": 17283,
    "décôte": 0,
    "réduction": 0,
    "taux": 0.41
  }
]

11.1. El script de configuración [config.py]

El script de configuración será el siguiente:


def configure():
    import os

    # ruta absoluta de la carpeta de este script
    script_dir = os.path.dirname(os.path.abspath(__file__))
    # dependencias de la aplicación
    absolute_dependencies = [
        f"{script_dir}/../shared",
    ]
    # Configuración de la aplicación
    config = {
        # ruta absoluta del archivo de contribuyentes
        "taxpayersFilename"f"{script_dir}/../data/taxpayersdata.txt",
        # ruta absoluta del archivo de resultados
        "resultsFilename"f"{script_dir}/../data/résultats.json",
        # ruta absoluta del archivo de datos de la administración tributaria
        "admindataFilename"f"{script_dir}/../data/admindata.json"
    }
    # actualización de la ruta del sistema
    from myutils import set_syspath

    set_syspath(absolute_dependencies)

    # se genera el config
    return config
  • línea 8: colocamos la carpeta [shared] en el Python Path. Esta carpeta contiene el módulo [impôts_module_02] utilizado por el script principal;

11.2. Script principal [main.py]

El script principal de version 3 es el siguiente:


# se configura la aplicación
import config

config = config.configure()

# el syspath está configurado: ya se pueden realizar las importaciones
from impôts_module_02 import calcul_impôt, get_admindata, get_taxpayers_data, record_results_in_json_file

# archivo de contribuyentes
taxpayers_filename = config['taxpayersFilename']
# archivo de resultados
results_filename = config['resultsFilename']
# archivo de datos de la administración tributaria
admindata_filename = config['admindataFilename']
# código
try:

    # lectura de los datos de la administración tributaria
    admindata = get_admindata(admindata_filename)
    # lectura de los datos de los contribuyentes
    taxpayers = get_taxpayers_data(taxpayers_filename)
    # lista de resultados
    results = []
    # se calcula el impuesto de los contribuyentes
    for taxpayer in taxpayers:
        # el cálculo del impuesto devuelve un diccionario de claves
        # ['marié', 'enfants', 'salaire', 'impôt', 'surcôte', 'décôte', 'réduction', 'taux']
        result = calcul_impôt(admindata, taxpayer['marié'], taxpayer['enfants'], taxpayer['salaire'])
        # el diccionario se añade a la lista de resultados
        results.append(result)
    # se guardan los resultados
    record_results_in_json_file(results_filename, results)
except BaseException as erreur:
    # pueden producirse diferentes errores: falta de archivo, contenido del archivo incorrecto
    # se muestra el error y se sale de la aplicación
    print(f"L'erreur suivante s'est produite : {erreur}]\n")
finally:
    print("Travail terminé...")

Notas

  • líneas 2-4: se configura la aplicación, en particular su Python Path;
  • línea 7: se importan las funciones que se necesitan en [main.py];
  • líneas 9-14: los nombres de los archivos utilizados por la aplicación se recuperan de la configuración;
  • el script principal de version 3 presenta tres diferencias con respecto al de las versiones 1 y 2:
  • línea 21: los datos de la administración tributaria se extraen del archivo jSON [./data/admindata.json];
  • línea 32: los resultados del cálculo del impuesto se colocan en el archivo jSON [./data/résultats.json];
  • línea 7: las funciones de version 3 se encuentran en el módulo [impots.modules.impôts_module_02];

11.3. El módulo [impots.v02.modules.impôts_module_02]

El módulo [impots.v02.modules.impôts_module_02] tiene la siguiente estructura:

Image

  • En el módulo se encuentran funciones que ya están presentes en el módulo utilizado por el version 1, aunque con una diferencia. Cuando el módulo version 2 retoma una función presente en el módulo version 1, lo hace con un parámetro adicional: [adminData] (líneas 29, 51, 77, 127). Este parámetro representa el diccionario de datos fiscales procedentes del archivo jSON [adminData.json]. En el módulo version 1, no era necesario pasar estos datos a las funciones, ya que estaban definidos globalmente en ellas, por lo que las funciones los conocían;

11.4. Lectura de los datos de la administración tributaria

La función [get_admindata] es la siguiente:


# lectura de los datos de la administración tributaria en un archivo jSON
# ----------------------------------------
def get_admindata(admindata_filename: str) -> dict:
    # lectura de los datos de la administración tributaria
    # se permiten las posibles excepciones: ausencia del archivo, contenido incorrecto de jSON
    file = None
    try:
        # apertura del archivo jSON en modo lectura
        file = codecs.open(admindata_filename, "r", "utf8")
        # transferencia del contenido a un diccionario
        admin_data = json.load(file)
        # se devuelve el resultado
        return admin_data
    finally:
        # cierre del archivo si se ha abierto
        if file:
            file.close()
  • línea 9: se recupera el diccionario de imágenes del archivo jSON leído;

11.5. Registro de los resultados

La función [record_results_in_json_file] es la siguiente:


# escritura de los resultados en un archivo jSON
# ----------------------------------------
def record_results_in_json_file(results_filename: str, results: list):
    file = None
    try:
        # apertura del archivo de resultados
        file = codecs.open(results_filename, "w", "utf8")
        # escritura en bloque
        json.dump(results, file, ensure_ascii=False)
    finally:
        # se cierra el archivo si se ha abierto
        if file:
            file.close()
  • línea 7: se crea un archivo codificado en UTF-8;
  • línea 9: se escribe la lista [results] en el archivo jSON. Los caracteres UTF-8 no se escapan (ensure_ascii=False);

11.6. Modificación de las funciones

Algunas funciones reciben ahora un parámetro adicional [admin_data]. Esto modifica ligeramente su sintaxis. Tomemos, por ejemplo, la función [calcul_impôt]:


# cálculo del impuesto - paso 1
# ----------------------------------------
def calcul_impôt(admin_data: dict, marié: str, enfants: int, salaire: int) -> dict:
    # casado: sí, no
    # hijos: número de hijos
    # salario: salario anual
    # límites, coeffr, coeffn: las tablas de datos que permiten el cálculo del impuesto
    #
    # cálculo del impuesto con hijos
    result1 = calcul_impôt_2(admin_data, marié, enfants, salaire)
    impot1 = result1["impôt"]
    # cálculo del impuesto sin hijos
    if enfants != 0:
        result2 = calcul_impôt_2(admin_data, marié, 0, salaire)
        impot2 = result2["impôt"]
        # aplicación del límite máximo del coeficiente familiar
        if enfants < 3:
            # PLAFOND_QF_DEMI_PART euros para los dos primeros hijos
            impot2 = impot2 - enfants * admin_data['plafond_qf_demi_part']
        else:
            # PLAFOND_QF_DEMI_PART euros para los dos primeros hijos, el doble para los siguientes
            impot2 = impot2 - 2 * admin_data['plafond_qf_demi_part'] - (enfants - 2) * 2 * admin_data[
                'plafond_qf_demi_part']
    else:
        impot2 = impot1
        result2 = result1

    # se aplica el impuesto más alto con el tipo y el recargo correspondientes
    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"]

    # cálculo de una posible deducción
    décôte = get_décôte(admin_data, marié, salaire, impot)
    impot -= décôte
    # cálculo de una posible reducción de impuestos
    réduction = get_réduction(admin_data, marié, salaire, enfants, impot)
    impot -= réduction
    # resultado
    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}

Notas

  • donde [calcul_impôt] llama a otras funciones, pasa [admin_data] como primer parámetro (líneas 10, 14, 39, 42);
  • allí donde [calcul_impôt] utiliza constantes fiscales, ahora pasa por el diccionario [admin_data] (líneas 19, 22);

Todas las funciones que reciben [admin_data] como parámetro sufren este mismo tipo de modificaciones.

11.7. Resultados

Los resultados obtenidos son los presentados al principio del apartado 8.3.