4. Ćwiczenie praktyczne – wersje 1 i 2
4.1. Zadanie

Powyższa tabela pozwala obliczyć podatek w uproszczonym przypadku podatnika, który ma do zadeklarowania wyłącznie swoje wynagrodzenie. Jak wskazuje uwaga (1), tak obliczony podatek jest podatkiem przed zastosowaniem trzech mechanizmów:
- ograniczenia współczynnika rodzinnego, które ma zastosowanie w przypadku wysokich dochodów;
- ulgi i obniżki podatkowe, które mają zastosowanie w przypadku niskich dochodów;
W związku z tym obliczenie podatku obejmuje następujące etapy [http://impotsurlerevenu.org/comprendre-le-calcul-de-l-impot/1217-calcul-de-l-impot-2019.php]:

Proponujemy napisanie programu umożliwiającego obliczenie podatku podatnika w uproszczonym przypadku, gdy podatnik ma do zadeklarowania wyłącznie swoje wynagrodzenie:
4.1.1. Obliczanie podatku brutto
Podatek brutto można obliczyć w następujący sposób:
Najpierw oblicza się liczbę udziałów podatnika:
- każdy z rodziców wnosi 1 udział;
- dwoje pierwszych dzieci wnosi po 1/2 udziału;
- kolejne dzieci wnoszą po jednej części:
Liczba udziałów wynosi zatem:
- nbParts=1+nbEnfants*0,5+(liczbaDzieci-2)*0,5, jeśli pracownik nie jest żonaty;
- nbParts = 2 + nbEnfants * 0,5 + (nbEnfants – 2) * 0,5, jeśli jest żonaty;
gdzie nbEnfants oznacza liczbę dzieci;
- oblicza się dochód podlegający opodatkowaniu R = 0,9 * S, gdzie S to roczne wynagrodzenie;
- oblicza się współczynnik rodzinny QF = R / nbParts;
- oblicza się podatek brutto I na podstawie następujących danych (2019):
9964 | 0 | 0 |
27519 | 0,14 | 1394,96 |
73779 | 0,3 | 5798 |
156 244 | 0,41 | 13913,69 |
0 | 0,45 | 20163,45 |
Każdy wiersz zawiera 3 pola: champ1, champ2, champ3. Aby obliczyć podatek I, szukamy pierwszego wiersza, w którym QF ≤ pole1, i pobieramy wartości z tego wiersza. Na przykład dla pracownika pozostającego w związku małżeńskim, mającego dwoje dzieci i roczne wynagrodzenie S w wysokości 50 000 euro:
Dochód podlegający opodatkowaniu: R = 0,9 * S = 45 000
Liczba udziałów: nbParts = 2 + 2 * 0,5 = 3
Współczynnik rodzinny: QF = 45 000 / 3 = 15 000
Pierwszy wiersz, w którym QF <= pole1, wygląda następująco:
Podatek I wynosi zatem 0,14*R – 1394,96*nbParts=[0,14*45000-1394,96*3]=2115. Podatek jest zaokrąglany w dół do najbliższego euro.
Jeśli już w pierwszym wierszu zachodzi relacja QF ≤ pole1, wówczas podatek wynosi zero.
Jeśli QF jest takie, że warunek QF <= pole1 nigdy nie jest spełniony, wówczas stosuje się współczynniki z ostatniego wiersza. W tym przypadku:
co daje podatek brutto I = 0,45*R – 20163,45*nbParts.
4.1.2. Ograniczenie ilorazu rodzinnego

Aby sprawdzić, czy ma zastosowanie limit ilorazu rodzinnego QF, ponownie obliczamy podatek brutto bez uwzględnienia dzieci. Nadal dla pracownika pozostającego w związku małżeńskim, mającego dwoje dzieci i roczne wynagrodzenie S w wysokości 50 000 euro:
Dochód podlegający opodatkowaniu: R = 0,9 * S = 45 000
Liczba udziałów: nbParts=2 (nie uwzględnia się już dzieci)
Współczynnik rodzinny: QF = 45000/2 = 22500
Pierwszy wiersz, w którym QF <= pole1, wygląda następująco:
Podatek I wynosi zatem 0,14*R – 1394,96*nbParts=[0,14*45000-1394,96*2]=3510.
Maksymalna ulga na dzieci: 1551 * 2 = 3102 euro
Podatek minimalny: 3510 – 3102 = 408 euro
Podatek brutto z uwzględnieniem 3 udziałów, wynoszący 2115 euro, jest wyższy od podatku minimalnego wynoszącego 408 euro, dlatego w tym przypadku nie ma zastosowania limit rodzinny.
Ogólnie rzecz biorąc, podatek brutto jest większy niż (podatek1, podatek2), gdzie:
- [impôt1]: to podatek brutto obliczony z uwzględnieniem dzieci;
- [impôt2]: to podatek brutto obliczony bez uwzględnienia dzieci i pomniejszony o maksymalną ulgę (w tym przypadku 1551 euro na pół części) związaną z dziećmi;
4.1.3. Obliczanie ulgi

Nadal w przypadku pracownika pozostającego w związku małżeńskim, mającego dwoje dzieci i roczne wynagrodzenie S w wysokości 50 000 euro:
Podatek brutto (2115) uzyskany w poprzednim etapie jest niższy niż 2627 euro dla pary (1595 euro dla osoby samotnej): w związku z tym ma zastosowanie ulga. Oblicza się ją w następujący sposób:
obniżka = próg (para = 1970 / osoba samotna = 1196) – 0,75 * podatek brutto
obniżka = 1970 – 0,75 * 2115 = 383,75, zaokrąglone do 384 euro.
Nowy podatek brutto = 2115 – 384 = 1731 euro
4.1.4. Obliczenie ulgi podatkowej

Poniżej określonego progu od podatku brutto wynikającego z powyższych obliczeń przyznaje się ulgę w wysokości 20%. W 2019 r. progi te wynoszą:
- osoba samotna: 21 037 euro;
- para: 42 074 euro; (liczba 37 968 użyta w powyższym przykładzie wydaje się błędna);
Próg ten zwiększa się o wartość: 3797 * (liczba pół-udziałów przypisanych dzieciom).
W przypadku pracownika pozostającego w związku małżeńskim, mającego dwoje dzieci i roczne wynagrodzenie S w wysokości 50 000 euro:
- jego dochód podlegający opodatkowaniu (45 000 euro) jest niższy od progu (42 074 + 2 * 3797) = 49 668 euro;
- ma zatem prawo do 20-procentowej ulgi podatkowej: 1731 * 0,2 = 346,2 euro, zaokrąglone do 347 euro;
- podatek brutto podatnika wynosi: 1731 – 347 = 1384 euro;
4.1.5. Obliczenie podatku netto
Na tym zakończymy nasze obliczenia: podatek netto do zapłaty wyniesie 1384 euro. W rzeczywistości podatnik może skorzystać z innych ulg, w szczególności z tytułu darowizn na rzecz organizacji pożytku publicznego lub ogólnego.
4.1.6. Przypadek wysokich dochodów
Nasz poprzedni przykład dotyczy większości pracowników najemnych. Jednak w przypadku wysokich dochodów obliczenie podatku przebiega inaczej.
4.1.6.1. Ograniczenie 10-procentowej ulgi od rocznych dochodów
W większości przypadków dochód podlegający opodatkowaniu oblicza się według wzoru: R = 0,9 * S, gdzie S oznacza roczne wynagrodzenie. Nazywa się to 10-procentową ulgą. Ulga ta podlega ograniczeniu. W 2019 r.:
- nie może przekraczać 12 502 euro;
- nie może być niższa niż 437 euro;
Weźmy na przykład pracownika niebędącego w związku małżeńskim, bezdzietnego, o rocznym wynagrodzeniu w wysokości 200 000 euro:
- 10-procentowa redukcja wynosi 200 000 euro > 12 502 euro. Zostaje zatem ograniczona do 12 502 euro;
4.1.6.2. Ograniczenie ilorazu rodzinnego
Rozważmy przypadek, w którym ma zastosowanie limit rodzinny przedstawiony w akapicie [link]. Weźmy pod uwagę parę z trójką dzieci i rocznym dochodem w wysokości 100 000 euro. Przeanalizujmy kolejne etapy obliczeń:
- odliczenie w wysokości 10 % wynosi 10 000 euro < 12 502 euro. Dochód podlegający opodatkowaniu R wynosi zatem 100 000 – 10 000 = 90 000 euro;
- para posiada nbParts = 2 + 0,5 × 2 + 1 = 4 części;
- jej współczynnik rodzinny wynosi zatem QF = R / nbParts = 90 000 / 4 = 22 500 euro;
- jej podatek brutto I1 z uwzględnieniem dzieci wynosi I1=0,14*90 000 - 1 394,96*4 = 7 020 euro;
- jego podatek brutto I2 bez dzieci:
- QF = 90 000 / 2 = 45 000 euro;
- I2 = 0,3 × 90 000 – 5 798 × 2 = 15 404 euro;
- zasada ograniczenia ilorazu rodzinnego stanowi, że korzyść wynikająca z posiadania dzieci nie może przekroczyć (1551 × 4 półczęści) = 6204 euro. W tym przypadku wynosi ona jednak I2-I1 = 15404 – 7020 = 8384 euro, a więc jest wyższa niż 6204 euro;
- podatek brutto jest zatem przeliczany jako I3 = I2-6204 = 15 404 – 6 204 = 9 200 euro;
Para ta nie będzie miała prawa do ulgi ani obniżki, a jej ostateczny podatek wyniesie 9200 euro.
4.1.7. Dane oficjalne
Obliczanie podatku jest skomplikowane. W całym dokumencie testy będą przeprowadzane na podstawie poniższych przykładów. Wyniki pochodzą z symulatora urzędu skarbowego [https://www3.impots.gouv.fr/simulateur/calcul_impot/2019/simplifie/index.htm]:
Podatnik | Oficjalne wyniki | Wyniki algorytmu przedstawionego w dokumencie |
Para z dwójką dzieci i rocznym dochodem w wysokości 55 555 euro | Podatek = 2815 euro Stawka podatkowa = 14 % | Podatek = 2814 euro Stawka podatkowa = 14 % |
Para z dwójką dzieci i rocznym dochodem w wysokości 50 000 euro | Podatek = 1385 euro Ulga = 720 euro Ulga = 0 euro Stawka podatkowa = 14 % | Podatek = 1384 euro Odliczenie = 384 euro Ulga = 347 euro Stawka podatkowa = 14 % |
Para z trójką dzieci i rocznym dochodem w wysokości 50 000 euro | Podatek = 0 euro Odliczenie = 384 euro Ulga = 346 euro Stawka podatkowa = 14 % | Podatek = 0 euro Odliczenie = 720 euro Ulga = 0 euro Stawka podatkowa = 14 % |
Osoba samotna z dwójką dzieci i rocznym dochodem w wysokości 100 000 euro | Podatek = 19 884 euro Odliczenie = 0 euro Ulga = 0 euro Stawka podatkowa = 41 % | Podatek = 19 884 euro Dodatek = 4480 euro Odliczenie = 0 euro Ulga = 0 euro Stawka podatkowa = 41 % |
Osoba samotna z trójką dzieci i rocznym dochodem w wysokości 100 000 euro | Podatek = 16 782 euro Ulga = 0 euro Ulga = 0 euro Stawka podatkowa = 41 % | Podatek = 16 782 euro Dodatek = 7176 euro Odliczenie = 0 euro Ulga = 0 euro Stawka podatkowa = 41 % |
Para z trójką dzieci i rocznym dochodem w wysokości 100 000 euro | Podatek = 9200 euro Ulga = 0 euro Ulga = 0 euro Stawka podatkowa = 30 % | Podatek = 9200 euro Dodatek = 2180 euro Odliczenie = 0 euro Ulga = 0 euro Stawka podatkowa = 30 % |
Para z pięciorgiem dzieci i rocznym dochodem w wysokości 100 000 euro | Podatek = 4230 euro Ulga = 0 euro Ulga = 0 euro Stawka podatkowa = 14 % | Podatek = 4230 euro Odliczenie = 0 euro Ulga = 0 euro Stawka podatkowa = 14 % |
Osoba samotna bez dzieci, z rocznym dochodem w wysokości 100 000 euro | Podatek = 22 986 euro Ulga = 0 euro Ulga = 0 euro Stawka podatkowa = 41 % | Podatek = 22 986 euro Dodatek = 0 euro Odliczenie = 0 euro Ulga = 0 euro Stawka podatkowa = 41 % |
Para z dwójką dzieci i rocznym dochodem w wysokości 30 000 euro | Podatek = 0 euro Ulga = 0 euro Ulga = 0 euro Stawka podatkowa = 0 % | Podatek = 0 euro Odliczenie = 0 euro Ulga = 0 euro Stawka podatkowa = 0 % |
Osoba samotna bez dzieci, z rocznym dochodem w wysokości 200 000 euro | Podatek = 64 211 euro Odliczenie = 0 euro Ulga = 0 euro Stawka podatkowa = 45 % | Podatek = 64 210 euro Dodatek = 7498 euro Odliczenie = 0 euro Ulga = 0 euro Stawka podatkowa = 45 % |
Para z trójką dzieci i rocznym dochodem w wysokości 200 000 euro | Podatek = 42 843 euro Ulga = 0 euro Ulga = 0 euro Stawka podatkowa = 41 % | Podatek = 42 842 euro Dodatek = 17 283 euro Odliczenie = 0 euro Ulga = 0 euro Stawka podatkowa = 41 % |
W powyższym przykładzie jako dopłatę określa się kwotę, którą osoby o wysokich dochodach płacą dodatkowo z powodu dwóch czynników:
- ograniczenie ulgi podatkowej do 10% rocznego dochodu;
- ograniczenie współczynnika rodzinnego;
Wskaźnika tego nie udało się zweryfikować, ponieważ symulator urzędu skarbowego go nie podaje.
Widać, że algorytm zawarty w dokumencie za każdym razem podaje prawidłową kwotę podatku, jednak z marginesem błędu wynoszącym 1 euro. Margines ten wynika z zaokrągleń. Wszystkie kwoty są czasami zaokrąglane w górę do pełnego euro, a czasami w dół do pełnego euro. Ponieważ nie znałem oficjalnych zasad, kwoty w algorytmie zawartym w dokumencie zostały zaokrąglone:
- w górę do pełnego euro w przypadku obniżek i zniżek;
- w dół do pełnego euro w przypadku dopłat i podatku końcowego;
W dalszej części zostaną przeprowadzone testy w celu sprawdzenia poprawności wyników. Zostaną one wykonane na przykładach z poprzedniej tabeli przy dopuszczalnym marginesie błędu wynoszącym 1 euro.
4.2. Struktura drzewa skryptów

4.3. Wersja 1
4.3.1. Algorytm
Przedstawiamy pierwszy program, w którym:
- dane niezbędne do obliczenia podatku są na stałe zapisane w kodzie w postaci tabel i stałych;
- dane podatników (stan cywilny, dzieci, wynagrodzenie) znajdują się w pierwszym pliku tekstowym [taxpayersdata.txt];
- wyniki obliczeń podatku (stan cywilny, liczba dzieci, wynagrodzenie, podatek) są zapisane w drugim pliku tekstowym o nazwie [resultats.txt];
Skrypt [version-01/main.php] wygląda następująco:
<?php
// ścisłe typy dla parametrów funkcji
declare(strict_types=1);
// stałe globalne
define("PLAFOND_QF_DEMI_PART", 1551);
define("PLAFOND_REVENUS_CELIBATAIRE_POUR_REDUCTION", 21037);
define("PLAFOND_REVENUS_COUPLE_POUR_REDUCTION", 42074);
define("VALEUR_REDUC_DEMI_PART", 3797);
define("PLAFOND_DECOTE_CELIBATAIRE", 1196);
define("PLAFOND_DECOTE_COUPLE", 1970);
define("PLAFOND_IMPOT_COUPLE_POUR_DECOTE", 2627);
define("PLAFOND_IMPOT_CELIBATAIRE_POUR_DECOTE", 1595);
define("ABATTEMENT_DIXPOURCENT_MAX", 12502);
define("ABATTEMENT_DIXPOURCENT_MIN", 437);
// definiowanie stałych lokalnych
$DATA = "taxpayersdata.txt";
$RESULTATS = "resultats.txt";
$limites = array(9964, 27519, 73779, 156244, 0);
$coeffR = array(0, 0.14, 0.3, 0.41, 0.45);
$coeffN = array(0, 1394.96, 5798, 13913.69, 20163.45);
// odczyt danych
$data = fopen($DATA, "r");
if (!$data) {
print "Impossible d'ouvrir en lecture le fichier des données [$DATA]\n";
exit;
}
// otwieranie pliku wyników
$résultats = fopen($RESULTATS, "w");
if (!$résultats) {
print "Impossible de créer le fichier des résultats [$RESULTATS]\n";
exit;
}
// przetwarzanie bieżącego wiersza pliku danych
while ($ligne = fgets($data, 100)) {
// usuwanie ewentualnego znaku końca linii
$ligne = cutNewLineChar($ligne);
// pobierane są 3 pola: żonaty:dzieci:wynagrodzenie, które tworzą $ligne
list($marié, $enfants, $salaire) = explode(",", $ligne);
// oblicza się podatek
$result = calculImpot($marié, (int) $enfants, (float) $salaire, $limites, $coeffR, $coeffN);
// wynik zapisuje się w pliku wyników
$result = ["marié" => $marié, "enfants" => $enfants, "salaire" => $salaire] + $result;
fputs($résultats, \json_encode($result, JSON_UNESCAPED_UNICODE) . "\n");
// kolejny rekord
}
// zamykamy pliki
fclose($data);
fclose($résultats);
// koniec
exit;
// --------------------------------------------------------------------------
function cutNewLinechar(string $ligne): string {
// usuwa się znak końca linii z $ligne, jeśli istnieje
$L = strlen($ligne); // długość wiersza
while (substr($ligne, $L - 1, 1) === "\n" or substr($ligne, $L - 1, 1) === "\r") {
$ligne = substr($ligne, 0, $L - 1);
$L--;
}
// koniec
return($ligne);
}
// obliczenie podatku
// --------------------------------------------------------------------------
function calculImpot(string $marié, int $enfants, float $salaire, array $limites, array $coeffR, array $coeffN): array {
…
// wynik
return ["impôt" => floor($impot), "surcôte" => $surcôte, "décôte" => $décôte, "réduction" => $réduction, "taux" => $taux];
}
// --------------------------------------------------------------------------
function calculImpot2(string $marié, int $enfants, float $salaire, array $limites, array $coeffR, array $coeffN): array {
…
// wynik
return ["impôt" => $impôt, "surcôte" => $surcôte, "taux" => $coeffR[$i]];
}
// revenuImposable=wynagrodzenieRoczne-ulga
// odliczenie ma wartość minimalną i maksymalną
function getRevenuImposable(float $salaire): float {
…
// wynik
return floor($revenuImposable);
}
// oblicza ewentualną obniżkę
function getDecote(string $marié, float $salaire, float $impots): float {
…
// wynik
return ceil($décôte);
}
// oblicza ewentualną obniżkę
function getRéduction(string $marié, float $salaire, int $enfants, float $impots): float {
/…
// wynik
return ceil($réduction);
}
Plik danych taxpayersdata.txt (stan cywilny, dzieci, wynagrodzenie):
Pliki résultats.txt (stan cywilny, dzieci, wynagrodzenie, podatek, dopłata, ulga, obniżka, stawka podatkowa) zawierające uzyskane wyniki:
Uwagi
- wiersz 4: wymuszamy ścisłe przestrzeganie typów parametrów funkcji;
- wiersze 7–16: definicja wszystkich stałych niezbędnych do obliczenia podatku;
- wiersz 19: nazwa pliku tekstowego zawierającego dane podatników (stan cywilny, dzieci, wynagrodzenie);
- wiersz 20: nazwa pliku tekstowego zawierającego wyniki (stan cywilny, dzieci, wynagrodzenie, podatek) obliczeń podatku;
- wiersze 21–23: trzy tabele danych określające różne progi podatkowe stosowane w obliczeniach podatku;
- wiersze 26–30: otwarcie w trybie odczytu pliku z danymi podatników za pomocą funkcji [r]. Funkcja [fopen] zwraca wartość logiczną FALSE, jeśli otwarcie pliku nie powiodło się;
- wiersze 33–37: otwarcie pliku wyników w trybie zapisu za pomocą funkcji [w];
- wiersze 40–51: pętla odczytu wierszy (małżonek, dzieci, wynagrodzenie) z pliku danych podatników;
- wiersz 40: funkcja [fgets] odczytuje 100 znaków i zatrzymuje się przy pierwszym napotkanym znaku końca wiersza. W tym przypadku wszystkie wiersze mają mniej niż 100 znaków. Jeśli napotkano znak końca wiersza, jest on uwzględniony w zwracanym ciągu znaków. Po osiągnięciu końca pliku funkcja [fgets] zwraca wartość FALSE;
- wiersz 42: znak końca wiersza zostaje usunięty;
- wiersz 44: pobierane są składniki (małżonek, dzieci, wynagrodzenie) z tego wiersza;
- wiersz 46: obliczany jest podatek. Wynik jest zwracany w postaci tablicy asocjacyjnej (wiersz 76);
- wiersz 48: do pobranej wcześniej tablicy dodaje się klucze [marié, enfants, salaire];
- wiersz 49: wynik jest zapisywany w pliku wyników w postaci ciągu znaków jSON;
- wiersze 53–54: po pełnym przetworzeniu pliku danych podatników pliki są zamykane;
- wiersz 60: funkcja usuwająca znak końca wiersza z wiersza $ligne. Znakiem końca wiersza jest ciąg „\r\n” w systemach Windows oraz „\n” w systemach Unix. Wynikiem jest ciąg wejściowy bez znaku końca wiersza.
- wiersze 63–64: substr($chaîne,$début,$taille) to podciąg ciągu $chaîne, zaczynający się od znaku $début i zawierający co najwyżej $taille znaków;
Funkcja [calculImpot] jest następująca:
// stałe globalne
define("PLAFOND_QF_DEMI_PART", 1551);
// obliczenie podatku
// --------------------------------------------------------------------------
function calculImpot(string $marié, int $enfants, float $salaire, array $limites, array $coeffR, array $coeffN): array {
// $marié: tak, nie
// $enfants: liczba dzieci
// $salaire: roczne wynagrodzenie
// $limites, $coeffR, $coeffN: tabele danych umożliwiające obliczenie podatku
//
// obliczenie podatku z uwzględnieniem dzieci
$result1 = calculImpot2($marié, $enfants, $salaire, $limites, $coeffR, $coeffN);
$impot1 = $result1["impôt"];
// obliczenie podatku bez uwzględnienia dzieci
if ($enfants != 0) {
$result2 = calculImpot2($marié, 0, $salaire, $limites, $coeffR, $coeffN);
$impot2 = $result2["impôt"];
// zastosowanie limitu ilorazu rodzinnego
if ($enfants < 3) {
// $PLAFOND_QF_DEMI_PART euro na pierwsze dwoje dzieci
$impot2 = $impot2 - $enfants * PLAFOND_QF_DEMI_PART;
} else {
// $PLAFOND_QF_DEMI_PART euro na pierwsze dwoje dzieci, dwukrotność tej kwoty na kolejne
$impot2 = $impot2 - 2 * PLAFOND_QF_DEMI_PART - ($enfants - 2) * 2 * 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 = getDecote($marié, $salaire, $impot);
$impot -= $décôte;
// obliczenie ewentualnej ulgi podatkowej
$réduction = getRéduction($marié, $salaire, $enfants, $impot);
$impot -= $réduction;
// wynik
return ["impôt" => floor($impot), "surcôte" => $surcôte, "décôte" => $décôte, "réduction" => $réduction, "taux" => $taux];
}
// --------------------------------------------------------------------------
function calculImpot2(string $marié, int $enfants, float $salaire, array $limites, array $coeffR, array $coeffN): array {
…
// wynik
return ["impôt" => $impôt, "surcôte" => $surcôte, "taux" => $coeffR[$i]];
}
// revenuImposable=wynagrodzenieRoczne-odliczenie
// odliczenie w wysokości 10% ma wartość minimalną i maksymalną
function getRevenuImposable(float $salaire): float {
…
}
// oblicza ewentualną obniżkę
function getDecote(string $marié, float $salaire, float $impots): float {
…
// wynik
return ceil($décôte);
}
// oblicza ewentualną obniżkę
function getRéduction(string $marié, float $salaire, int $enfants, float $impots): float {
…
// wynik
return ceil($réduction);
}
Uwagi
- wiersz 10: podatek brutto jest obliczany z uwzględnieniem dzieci. Otrzymujemy wynik w postaci ["impôt" => $impôt, "surcôte" => $surcôte, "taux" => $coeffR[$i]] z:
- [‘impôt’]: podatek brutto;
- [‘surcôte’]: kwota dopłaty, jeśli występuje. Występuje ona, gdy ulga w wysokości 10% przekracza próg 12 502 euro;
- [‘taux’]: stawka podatkowa podatnika;
- wiersz 11: podatek brutto [impot1] do zapłaty;
- wiersze 13–14: jeśli podatnik ma co najmniej jedno dziecko, obliczenie podatku przeprowadza się ponownie przy użyciu tych samych danych, ale z założeniem, że nie ma dzieci. To drugie obliczenie jest konieczne, aby sprawdzić, czy ulga wynikająca z posiadania dzieci (nbParts*coeffN) przekracza określony próg;
- wiersz 15: podatek brutto [impot2] do zapłaty;
- wiersze 16–23: w przypadku podatku brutto [impot2] uwzględnia się teraz dzieci: każda 1/2 części wynikająca z posiadania dzieci pozwala na obniżkę o [PLAFOND_QF_DEMI_PART] euro;
- wiersze 25–26: przypadek, w którym podatnik nie ma dzieci. W takim przypadku obliczanie wartości [impot2] nie ma sensu. Jest ona równa wartości [impot1];
- wiersze 29–37: obliczono dwie kwoty podatku brutto [impot1, impot2]. Urząd skarbowy wybiera wyższą z nich. Otrzymujemy kwotę podatku brutto [impot];
- wiersze 39–40: kwota brutto [impot] może podlegać obniżce;
- wiersze 42–43: kwota brutto [impot] może podlegać obniżce;
- wiersz 45: [impot] stanowi teraz podatek netto do zapłaty. Zwracamy wyniki;
Funkcja [calculImpot2] wygląda następująco:
// --------------------------------------------------------------------------
function calculImpot2(string $marié, int $enfants, float $salaire, array $limites, array $coeffR, array $coeffN): array {
// $marié: tak, nie
// $enfants: liczba dzieci
// $salaire: roczne wynagrodzenie
// $limites, $coeffR, $coeffN: tabele danych umożliwiające obliczenie podatku
//
// liczba udziałów
$marié = strtolower($marié);
if ($marié === "oui") {
$nbParts = $enfants / 2 + 2;
} else {
$nbParts = $enfants / 2 + 1;
}
// 1 część na dziecko, począwszy od trzeciego
if ($enfants >= 3) {
// pół udziału dodatkowo na każde dziecko od trzeciego
$nbParts += 0.5 * ($enfants - 2);
}
// dochód podlegający opodatkowaniu
$revenuImposable = getRevenuImposable($salaire);
// dopłata
$surcôte = floor($revenuImposable - 0.9 * $salaire);
// z powodu problemów z zaokrągleniem
if ($surcôte < 0) {
$surcôte = 0;
}
// współczynnik rodzinny
$quotient = $revenuImposable / $nbParts;
// jest umieszczany na końcu tabeli limitów, aby zatrzymać następującą po niej pętlę
$limites[count($limites) - 1] = $quotient;
// obliczenie podatku
$i = 0;
while ($quotient > $limites[$i]) {
$i++;
}
// ponieważ umieszczono $quotient na końcu tablicy $limites, poprzednia pętla
// nie może wykraczać poza tablicę $limites
// teraz można obliczyć podatek
$impôt = floor($revenuImposable * $coeffR[$i] - $nbParts * $coeffN[$i]);
// wynik
return ["impôt" => $impôt, "surcôte" => $surcôte, "taux" => $coeffR[$i]];
}
// revenuImposable = roczne wynagrodzenie – ulga
// odliczenie ma wartość minimalną i maksymalną
function getRevenuImposable(float $salaire): float {
// wynik
return floor($revenuImposable);
}
Komentarze
- stosuje się tutaj obliczenie podatku według tzw. skali progresywnej;
- wiersz 9: strtolower($chaîne) zamienia $chaîne na małe litery;
- wiersze 10–19: obliczanie liczby udziałów podatnika;
- wiersz 21: dochód podlegający opodatkowaniu oblicza się za pomocą funkcji. Jak bowiem zauważono, nie zawsze wynosi on 0,9*revenusAnnuels. Odliczenie w wysokości 10% jest bowiem ograniczone do 12 502 euro;
- wiersz 23: obliczenie ewentualnej dopłaty, jeśli dochód podlegający opodatkowaniu jest wyższy niż 0,9*revenusAnnuels;
- wiersze 25–27: koryguje fakt, że z powodu błędów zaokrąglenia czasami otrzymujemy wartość [$surcôte=-1];
- wiersz 29: iloraz rodzinny;
- wiersze 30–36: iloraz ten pozwala określić przedział podatkowy podatnika;
- wiersz 40: po ustaleniu przedziału podatkowego podatnika można obliczyć jego podatek brutto. Funkcja floor($x) zwraca liczbę całkowitą bezpośrednio mniejszą od [$x];
- wiersz 42: zwracane są obliczone informacje;
Funkcja [getRevenuImposable] ma następującą postać:
// stałe globalne
define("ABATTEMENT_DIXPOURCENT_MAX", 12502);
define("ABATTEMENT_DIXPOURCENT_MIN", 437);
// revenuImposable=wynagrodzenieRoczne-odliczenie
// odliczenie ma wartość minimalną i maksymalną
function getRevenuImposable(float $salaire): float {
// odliczenie w wysokości 10% wynagrodzenia
$abattement = 0.1 * $salaire;
// odliczenie to nie może przekroczyć wartości ABATTEMENT_DIXPOURCENT_MAX
if ($abattement > ABATTEMENT_DIXPOURCENT_MAX) {
$abattement = ABATTEMENT_DIXPOURCENT_MAX;
}
// odliczenie nie może być niższe niż ABATTEMENT_DIXPOURCENT_MIN
if ($abattement < ABATTEMENT_DIXPOURCENT_MIN) {
$abattement = ABATTEMENT_DIXPOURCENT_MIN;
}
// dochód podlegający opodatkowaniu
$revenuImposable = $salaire - $abattement;
// wynik
return floor($revenuImposable);
}
Uwagi
- wiersz 5: standardowa ulga wynosi 10% rocznego wynagrodzenia;
- wiersze 7–9: odliczenie nie może przekroczyć maksymalnego odliczenia [ABATTEMENT_DIXPOURCENT_MAX];
- wiersze 10–13: odliczenie nie może być niższe od minimalnego odliczenia [ABATTEMENT_DIXPOURCENT_MIN];
- wiersz 15: obliczenie dochodu podlegającego opodatkowaniu;
Funkcja [getDecote] ma następujący przebieg:
// stałe ogólne
define("PLAFOND_DECOTE_CELIBATAIRE", 1196);
define("PLAFOND_DECOTE_COUPLE", 1970);
define("PLAFOND_IMPOT_COUPLE_POUR_DECOTE", 2627);
define("PLAFOND_IMPOT_CELIBATAIRE_POUR_DECOTE", 1595);
// oblicza ewentualną dyskontę
function getDecote(string $marié, float $salaire, float $impots): float {
// początkowo odliczenie wynosi zero
$décôte = 0;
// maksymalna kwota podatku uprawniająca do ulgi
$plafondImpôtPourDécôte = $marié === "oui" ? PLAFOND_IMPOT_COUPLE_POUR_DECOTE : PLAFOND_IMPOT_CELIBATAIRE_POUR_DECOTE;
if ($impots < $plafondImpôtPourDécôte) {
// maksymalna kwota ulgi
$plafondDécôte = $marié === "oui" ? PLAFOND_DECOTE_COUPLE : PLAFOND_DECOTE_CELIBATAIRE;
// obniżka teoretyczna
$décôte = $plafondDécôte - 0.75 * $impots;
// ulga nie może przekroczyć kwoty podatku
if ($décôte > $impots) {
$décôte = $impots;
}
// brak obniżki <0
if ($décôte < 0) {
$décôte = 0;
}
}
// wynik
return ceil($décôte);
}
Uwagi
- wiersz 6: maksymalna kwota podatku brutto uprawniająca do ulgi podatkowej. Kwota ta jest różna dla osób samotnych i par;
- wiersz 7: czy podatnik ma prawo do ulgi;
- wiersz 11: wzór na obniżkę. [plafondDécôte] to maksymalna kwota obniżki. Ta maksymalna kwota jest obliczana w wierszu 9. Również w tym przypadku zależy ona od sytuacji podatnika – czy jest on w związku małżeńskim, czy jest osobą samotną;
- wiersze 13–15: ulga nie może przekraczać kwoty podatku brutto do zapłaty. Ma to miejsce na przykład wtedy, gdy wartość [impots] w wierszu 11 wynosi 0;
- wiersze 17–19: aby uniknąć zaokrąglenia do -1;
Funkcja [getRéduction] ma następujący wygląd:
// stałe globalne
define("PLAFOND_REVENUS_CELIBATAIRE_POUR_REDUCTION", 21037);
define("PLAFOND_REVENUS_COUPLE_POUR_REDUCTION", 42074);
define("VALEUR_REDUC_DEMI_PART", 3797);
// oblicza ewentualną ulgę
function getRéduction(string $marié, float $salaire, int $enfants, float $impots): float {
// pułap dochodów uprawniający do ulgi w wysokości 20%
$plafondRevenuPourRéduction = $marié === "oui" ? PLAFOND_REVENUS_COUPLE_POUR_REDUCTION : PLAFOND_REVENUS_CELIBATAIRE_POUR_REDUCTION;
$plafondRevenuPourRéduction += $enfants * VALEUR_REDUC_DEMI_PART;
if ($enfants > 2) {
$plafondRevenuPourRéduction += ($enfants - 2) * VALEUR_REDUC_DEMI_PART;
}
// dochód podlegający opodatkowaniu
$revenuImposable = getRevenuImposable($salaire);
// ulga
$réduction = 0;
if ($revenuImposable < $plafondRevenuPourRéduction) {
// ulga w wysokości 20%
$réduction = 0.2 * $impots;
}
// wynik
return ceil($réduction);
}
Uwagi
- wiersze 4–10: aby uzyskać prawo do ulgi podatkowej, dochód podlegający opodatkowaniu (wiersz 10) musi być niższy od pułapu obliczonego w wierszach 4–8;
- wiersze 13–16: jeśli podatnik spełnia warunki, przysługuje mu ulga podatkowa w wysokości 20% (wiersz 15);
4.3.2. Wyniki
Plik danych taxpayersdata.txt (stan cywilny, dzieci, wynagrodzenie):
Pliki résultats.txt (stan cywilny, dzieci, wynagrodzenie, podatek, dopłata, ulga, obniżka, stawka podatkowa) zawierające uzyskane wyniki:
Uzyskane wyniki są zgodne z wynikami uzyskanymi za pomocą symulatora urzędu skarbowego.
4.3.3. Wniosek
Algorytm obliczania podatku, nawet w przypadkach uważanych za proste, jest złożony. Nie będziemy już do niego wracać. W kolejnych wersjach jego rdzeń pozostanie ten sam, pomimo pewnych zmian w prezentacji. Skupimy się zatem wyłącznie na tych ostatnich.
4.4. Wersja 2
4.4.1. Zmiany
W poprzedniej wersji dane niezbędne do obliczenia podatku były zakodowane na stałe w postaci stałych i tablic. Metoda ta jest niedopuszczalna. W nowej wersji dane te zostały przeniesione do pliku jSON:

Zawartość pliku [taxadmindata.json] jest następująca:
{
"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
}
Nowa wersja [version-02/main.php] wygląda następująco:
<?php
// ścisłe przestrzeganie zadeklarowanych typów parametrów funkcji
declare (strict_types=1);
// definicja stałych
$TAXPAYERSDATA = "taxpayersdata.txt";
$RESULTATS = "resultats.txt";
$TAXADMINDATA = "taxadmindata.json";
// pobieramy zawartość pliku z danymi podatkowymi
$fileContents = \file_get_contents($TAXADMINDATA);
$erreur = FALSE;
// błąd?
if (!$fileContents) {
// odnotowujemy błąd
$erreur = TRUE;
$message = "Le fichier des données [$TAXADMINDATA] n'existe pas";
}
if (!$erreur) {
// pobieranie kodu jSON z pliku konfiguracyjnego do tablicy asocjacyjnej
$taxAdminData = \json_decode($fileContents, true);
// błąd?
if (!$taxAdminData) {
// odnotowuje się błąd
$erreur = TRUE;
$message = "Le fichier de données jSON [$TAXADMINDATA] n'a pu être exploité correctement";
}
}
// błąd?
if ($erreur) {
print "$message\n";
exit;
}
// otwieranie pliku wyników
$résultats = fopen($RESULTATS, "w");
if (!$résultats) {
print "Impossible de créer le fichier des résultats [$RESULTATS]\n";
// wyjście
exit;
}
// otwieranie pliku z danymi podatników
$taxpayersdata = fopen($TAXPAYERSDATA, "r");
if (!$taxpayersdata) {
print "Impossible d'ouvrir le fichier des contribuables [$TAXPAYERSDATA]\n";
// wyjście
exit;
}
// przetwarzanie bieżącego wiersza pliku danych
while ($ligne = fgets($taxpayersdata, 100)) {
// usuwanie ewentualnego znacznika końca wiersza
$ligne = cutNewLineChar($ligne);
// pobierane są 3 pola: stan cywilny:liczba dzieci:wynagrodzenie, które tworzą $ligne
list($marié, $enfants, $salaire) = explode(",", $ligne);
// oblicza się podatek
$result = calculImpot($taxAdminData, $marié, (int) $enfants, (int) $salaire);
// wynik zapisuje się w pliku wyników
$result = ["marié" => $marié, "enfants" => $enfants, "salaire" => $salaire] + $result;
fputs($résultats, \json_encode($result, JSON_UNESCAPED_UNICODE) . "\n");
// kolejny rekord
}
// zamykamy pliki
fclose($taxpayersdata);
fclose($résultats);
// koniec
exit;
// --------------------------------------------------------------------------
function cutNewLinechar(string $ligne) {
…
// koniec
return($ligne);
}
// obliczenie podatku
// --------------------------------------------------------------------------
function calculImpot(array $taxAdminData, string $marié, int $enfants, float $salaire) {
// $marié: tak, nie
// $enfants: liczba dzieci
// $salaire: roczne wynagrodzenie
// $taxAdminData: dane z urzędu skarbowego
//
// obliczenie podatku z uwzględnieniem dzieci
$result1 = calculImpot2($taxAdminData, $marié, $enfants, $salaire);
$impot1 = $result1["impôt"];
// obliczenie podatku bez uwzględnienia dzieci
if ($enfants != 0) {
$result2 = calculImpot2($taxAdminData, $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 * $taxAdminData["PLAFOND_QF_DEMI_PART"];
} else {
// $PLAFOND_QF_DEMI_PART euro na pierwsze dwoje dzieci, dwukrotnie więcej na kolejne
$impot2 = $impot2 - 2 * $taxAdminData["PLAFOND_QF_DEMI_PART"] - ($enfants - 2) * 2 * $taxAdminData["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 = getDecote($taxAdminData, $marié, $salaire, $impot);
$impot -= $décôte;
// obliczenie ewentualnej ulgi podatkowej
$réduction = getRéduction($taxAdminData, $marié, $salaire, $enfants, $impot);
$impot -= $réduction;
// wynik
return ["impôt" => floor($impot), "surcôte" => $surcôte, "décôte" => $décôte, "réduction" => $réduction, "taux" => $taux];
}
// --------------------------------------------------------------------------
function calculImpot2(array $taxAdminData, string $marié, int $enfants, float $salaire) {
// $marié: tak, nie
…
// wynik
return ["impôt" => $impôt, "surcôte" => $surcôte, "taux" => $coeffR[$i]];
}
// revenuImposable=wynagrodzenieRoczne-ulga
// ulga ma wartość minimalną i maksymalną
function getRevenuImposable(array $taxAdminData, float $salaire): float {
…
// wynik
return floor($revenuImposable);
}
// oblicza ewentualną obniżkę
function getDecote(array $taxAdminData, string $marié, float $salaire, float $impots): float {
…
// wynik
return ceil($décôte);
}
// oblicza ewentualną redukcję
function getRéduction(array $taxAdminData, string $marié, float $salaire, int $enfants, float $impots): float {
…
// wynik
return ceil($réduction);
}
Komentarze
- wiersze 11–19: podejmowana jest próba odczytania zawartości pliku jSON o nazwie [TAXADMINDATA];
- wiersze 21–30: jeśli udało się odczytać plik jSON, jego zawartość jest dekodowana do tablicy asocjacyjnej [$taxAdminData];
- wiersze 32–36: jeśli w jednej z dwóch poprzednich operacji wystąpił błąd, na konsoli wyświetlany jest komunikat o błędzie i program się zatrzymuje;
- różnica w stosunku do wersji 01 polega na tym, że tutaj dane (tabele i stałe) urzędu skarbowego znajdują się w tablicy asocjacyjnej [$taxAdminData], podczas gdy w wersji 01 znajdowały się one w tablicach i stałych globalnych. To właśnie globalny charakter tych stałych stanowi różnicę między obiema wersjami:
- w wersji 01 stałe były znane we wszystkich funkcjach tablicy [main.php];
- aby uzyskać ten sam wynik w wersji 02, należy przekazać tablicę asocjacyjną [$taxAdminData] jako parametr do wszystkich funkcji (wiersze 83, 129, 138, 145, 152);
- każda funkcja w wersji 02 musi wykorzystywać zawartość tablicy [$taxAdminData];
- wiersze 83–126: w funkcji [calculerImpot], tam gdzie wcześniej używano stałych globalnych lub tablic [limites, coeffR, coeffN], teraz wykorzystuje się zawartość tablicy [$taxAdminData] otrzymanej jako parametr (wiersze 99, 102);
- wszystkie pozostałe funkcje zostały przerobione w ten sam sposób;
Uzyskane wyniki są takie same jak w poprzedniej wersji.
4.4.2. Wniosek
Wersja 02 jest znacznie bardziej elastyczna niż wersja 01. W 2020 r. algorytm obliczania podatku będzie prawdopodobnie taki sam jak w 2019 r. Zmieniły się jedynie progi podatkowe i stałe obliczeniowe. Wystarczy wtedy zaktualizować plik [taxadmindata.json]. W wersji 01 konieczne jest przejście do kodu i zmiana przedziałów podatkowych oraz stałych obliczeniowych. Jest jednak prawdopodobne, że osoby, które muszą zmienić wartości przedziałów podatkowych i stałych obliczeniowych, nie mają dostępu do kodu algorytmu.