Skip to content

11. Ćwiczenie praktyczne: wersja 3

Image

Ta nowa wersja wprowadza dwie zmiany:

  • dane niezbędne do obliczenia podatku, dostarczone przez administrację podatkową, umieszczono w pliku 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
}
  • wyniki obliczeń podatku zostaną również umieszczone w pliku 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. Skrypt konfiguracyjny [config.py]

Skrypt konfiguracyjny będzie wyglądał następująco:


def configure():
    import os

    # absolutna ścieżka do folderu tego skryptu
    script_dir = os.path.dirname(os.path.abspath(__file__))
    # zależności aplikacji
    absolute_dependencies = [
        f"{script_dir}/../shared",
    ]
    # konfiguracja aplikacji
    config = {
        # absolutna ścieżka do pliku podatników
        "taxpayersFilename"f"{script_dir}/../data/taxpayersdata.txt",
        # absolutna ścieżka do pliku wyników
        "resultsFilename"f"{script_dir}/../data/résultats.json",
        # absolutna ścieżka do pliku z danymi administracji podatkowej
        "admindataFilename"f"{script_dir}/../data/admindata.json"
    }
    # aktualizacja ścieżki systemowej
    from myutils import set_syspath

    set_syspath(absolute_dependencies)

    # przywracamy konfigurację
    return config
  • wiersz 8: dodajemy folder [shared] do ścieżki Python Path. Folder ten zawiera moduł [impôts_module_02] używany przez skrypt główny;

11.2. Skrypt główny [main.py]

Główny skrypt wersji 3 wygląda następująco:


# konfigurujemy aplikację
import config

config = config.configure()

# ścieżka systemowa jest skonfigurowana – można przeprowadzić importy
from impôts_module_02 import calcul_impôt, get_admindata, get_taxpayers_data, record_results_in_json_file

# plik podatników
taxpayers_filename = config['taxpayersFilename']
# plik wyników
results_filename = config['resultsFilename']
# plik z danymi administracji podatkowej
admindata_filename = config['admindataFilename']
# kod
try:

    # odczyt danych administracji podatkowej
    admindata = get_admindata(admindata_filename)
    # odczyt danych podatników
    taxpayers = get_taxpayers_data(taxpayers_filename)
    # lista wyników
    results = []
    # obliczanie podatku podatników
    for taxpayer in taxpayers:
        # obliczenie podatku zwraca słownik kluczy
        # ['marié', 'enfants', 'salaire', 'impôt', 'surcôte', 'décôte', 'réduction', 'taux']
        result = calcul_impôt(admindata, taxpayer['marié'], taxpayer['enfants'], taxpayer['salaire'])
        # słownik został dodany do listy wyników
        results.append(result)
    # zapisano wyniki
    record_results_in_json_file(results_filename, results)
except BaseException as erreur:
    # mogą wystąpić różne błędy: brak pliku, nieprawidłowa zawartość pliku
    # wyświetlany jest komunikat o błędzie i aplikacja zostaje zamknięta
    print(f"L'erreur suivante s'est produite : {erreur}]\n")
finally:
    print("Travail terminé...")

Uwagi

  • w wierszach 2–4 konfiguruje się aplikację, w szczególności ścieżkę Python Path;
  • w wierszu 7 importuje się funkcje potrzebne w pliku [main.py];
  • w wierszach 9–14 pobierane są z konfiguracji nazwy plików wykorzystywanych przez aplikację;
  • główny skrypt wersji 3 różni się od skryptu z wersji 1 i 2 w trzech aspektach:
  • wiersz 21: dane z urzędu skarbowego są pobierane z pliku jSON [./data/admindata.json];
  • wiersz 32: wyniki obliczeń podatku są zapisywane w pliku jSON [./data/résultats.json];
  • wiersz 7: funkcje wersji 3 znajdują się w module [impots.modules.impôts_module_02];

11.3. Moduł [impots.v02.modules.impôts_module_02]

Moduł [impots.v02.modules.impôts_module_02] ma następującą strukturę:

Image

  • W module tym znajdują się funkcje już obecne w module używanym w wersji 1, jednak z jedną różnicą. Gdy moduł wersji 2 przejmuje funkcję obecną w module wersji 1, robi to z dodatkowym parametrem: [adminData] (wiersze 29, 51, 77, 127). Parametr ten reprezentuje słownik danych podatkowych pochodzących z pliku jSON [adminData.json]. W module wersji 1 dane te nie musiały być przekazywane do funkcji, ponieważ były one zdefiniowane globalnie dla tych funkcji, co sprawiało, że funkcje miały do nich dostęp;

11.4. Odczyt danych z urzędu skarbowego

Funkcja [get_admindata] ma następującą postać:


# odczyt danych z urzędu skarbowego do pliku jSON
# ----------------------------------------
def get_admindata(admindata_filename: str) -> dict:
    # odczyt danych z urzędu skarbowego
    # pozwala się na zgłoszenie ewentualnych wyjątków: brak pliku, nieprawidłowa zawartość pliku jSON
    file = None
    try:
        # otwarcie pliku jSON w trybie odczytu
        file = codecs.open(admindata_filename, "r", "utf8")
        # przeniesienie zawartości do słownika
        admin_data = json.load(file)
        # zwracanie wyniku
        return admin_data
    finally:
        # zamknięcie pliku, jeśli został otwarty
        if file:
            file.close()
  • wiersz 9: pobierany jest słownik obrazów z odczytanego pliku jSON;

11.5. Rejestracja wyników

Funkcja [record_results_in_json_file] ma następujący opis:


# zapis wyników do pliku jSON
# ----------------------------------------
def record_results_in_json_file(results_filename: str, results: list):
    file = None
    try:
        # otwarcie pliku wyników
        file = codecs.open(results_filename, "w", "utf8")
        # zapis blokowy
        json.dump(results, file, ensure_ascii=False)
    finally:
        # zamknięcie pliku, jeśli został otwarty
        if file:
            file.close()
  • wiersz 7: tworzy się plik zakodowany w formacie UTF-8;
  • wiersz 9: zapisuje się listę [results] do pliku jSON. Znaki UTF-8 nie są poddawane sekwencjom escape (ensure_ascii=False);

11.6. Modyfikacja funkcji

Niektóre funkcje otrzymują teraz dodatkowy parametr [admin_data]. Powoduje to niewielką zmianę w ich zapisie. Weźmy na przykład funkcję [calcul_impôt]:


# obliczanie podatku – etap 1
# ----------------------------------------
def calcul_impôt(admin_data: dict, marié: str, enfants: int, salaire: int) -> dict:
    # stan cywilny: tak, nie
    # dzieci: liczba dzieci
    # wynagrodzenie: roczne wynagrodzenie
    # limity, współczynnik, współczynnik n: tabele danych umożliwiające obliczenie podatku
    #
    # obliczenie podatku z uwzględnieniem dzieci
    result1 = calcul_impôt_2(admin_data, marié, enfants, salaire)
    impot1 = result1["impôt"]
    # obliczenie podatku bez uwzględnienia dzieci
    if enfants != 0:
        result2 = calcul_impôt_2(admin_data, marié, 0, salaire)
        impot2 = result2["impôt"]
        # zastosowanie limitu ilorazu rodzinnego
        if enfants < 3:
            # PLAFOND_QF_DEMI_PART euro na pierwsze dwoje dzieci
            impot2 = impot2 - enfants * admin_data['plafond_qf_demi_part']
        else:
            # PLAFOND_QF_DEMI_PART euro na pierwsze dwoje dzieci, dwukrotność tej kwoty na kolejne
            impot2 = impot2 - 2 * admin_data['plafond_qf_demi_part'] - (enfants - 2) * 2 * admin_data[
                'plafond_qf_demi_part']
    else:
        impot2 = impot1
        result2 = result1

    # bierzemy najwyższy podatek wraz z odpowiednią stawką i dopłatą
    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"]

    # obliczenie ewentualnej ulgi
    décôte = get_décôte(admin_data, marié, salaire, impot)
    impot -= décôte
    # obliczenie ewentualnej ulgi podatkowej
    réduction = get_réduction(admin_data, marié, salaire, enfants, impot)
    impot -= réduction
    # wynik
    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}

Uwagi

  • tam, gdzie funkcja [calcul_impôt] wywołuje inne funkcje, przekazuje ona [admin_data] jako pierwszy parametr (wiersze 10, 14, 39, 42);
  • tam, gdzie funkcja [calcul_impôt] wykorzystuje stałe podatkowe, przekazuje je teraz za pośrednictwem słownika [admin_data] (wiersze 19, 22);

Wszystkie funkcje, które przyjmują [admin_data] jako parametr, podlegają tym samym zmianom.

11.7. Wyniki

Uzyskane wyniki są zgodne z tymi przedstawionymi na początku paragrafu 8.3.