4. Exercício prático – versões 1 e 2
4.1. O problema

A tabela acima permite calcular o imposto no caso simplificado de um contribuinte que tenha apenas o seu salário para declarar. Tal como indicado na nota (1), o imposto assim calculado é o imposto antes de três mecanismos:
- o limite máximo do quociente familiar, que se aplica aos rendimentos elevados;
- a dedução e a redução de impostos, que se aplicam aos rendimentos baixos;
Assim, o cálculo do imposto inclui as seguintes etapas [http://impotsurlerevenu.org/comprendre-le-calcul-de-l-impot/1217-calcul-de-l-impot-2019.php]:

Propõe-se escrever um programa que permita calcular o imposto de um contribuinte no caso simplificado de um contribuinte que tenha apenas o seu salário para declarar:
4.1.1. Cálculo do imposto bruto
O imposto bruto pode ser calculado da seguinte forma:
Primeiro, calcula-se o número de quotas do contribuinte:
- cada progenitor contribui com 1 quota;
- os dois primeiros filhos contribuem com 1/2 parte cada um;
- os filhos seguintes contribuem com uma parte cada:
O número de quotas é, portanto:
- nbParts=1+nbEnfants*0,5+(n.º de filhos-2)*0,5 se o trabalhador não for casado;
- nbParts = 2 + nbEnfants * 0,5 + (nbEnfants - 2) * 0,5 se for casado;
onde nbEnfants é o número de filhos;
- calcula-se o rendimento tributável R = 0,9 * S, em que S é o salário anual;
- calcula-se o quociente familiar QF = R / nbParts;
- calcula-se o imposto bruto I com base nos seguintes dados (2019):
9964 | 0 | 0 |
27519 | 0,14 | 1394,96 |
73779 | 0,3 | 5798 |
156 244 | 0,41 | 13 913,69 |
0 | 0,45 | 20163,45 |
Cada linha tem 3 campos: champ1, champ2, champ3. Para calcular o imposto I, procura-se a primeira linha em que QF <= campo1 e utilizam-se os valores dessa linha. Por exemplo, para um trabalhador casado com dois filhos e um salário anual S de 50 000 euros:
Rendimento tributável: R = 0,9 * S = 45 000
Número de quotas: nbParts = 2 + 2 * 0,5 = 3
Quociente familiar: QF = 45 000 / 3 = 15 000
A primeira linha em que QF <= campo1 é a seguinte:
O imposto I é, então, igual a 0,14*R – 1394,96*nbParts=[0,14*45000-1394,96*3]=2115. O imposto é arredondado para o euro inferior.
Se a relação QF <= campo1 for verdadeira desde a 1.ª linha, então o imposto é nulo.
Se QF for tal que a relação QF <= campo1 nunca for verificada, então são utilizados os coeficientes da última linha. Neste caso:
o que dá o imposto bruto I = 0,45*R – 20 163,45*nbParts.
4.1.2. Limite máximo do quociente familiar

Para saber se o limite máximo do quociente familiar QF se aplica, refaz-se o cálculo do imposto bruto sem os filhos. Continuando com o exemplo do trabalhador casado com dois filhos e um salário anual S de 50 000 euros:
Rendimento tributável: R = 0,9 * S = 45 000
Número de quotas: nbParts=2 (já não se contabilizam os filhos)
Quociente familiar: QF=45000/2=22500
A primeira linha em que QF <= campo1 é a seguinte:
O imposto I é, então, igual a 0,14*R – 1394,96*nbParts=[0,14*45000-1394,96*2]=3510.
Ganho máximo relacionado com os filhos: 1551 * 2 = 3102 euros
Imposto mínimo: 3510 – 3102 = 408 euros
O imposto bruto com 3 quotas, já calculado em 2115 euros, é superior ao imposto mínimo de 408 euros, pelo que o limite máximo familiar não se aplica neste caso.
De um modo geral, o imposto bruto é maior que (imposto1, imposto2), em que:
- [impôt1]: é o imposto bruto calculado com os filhos;
- [impôt2]: é o imposto bruto calculado sem os filhos e deduzido do benefício máximo (neste caso, 1551 euros por meia quota) relacionado com os filhos;
4.1.3. Cálculo da redução

Continuando com o exemplo do trabalhador casado, com dois filhos e um salário anual S de 50 000 euros:
O imposto bruto (2115) resultante da etapa anterior é inferior a 2627 euros para um casal (1595 euros para um solteiro): aplica-se, portanto, a redução. Esta é obtida através do seguinte cálculo:
redução = limiar (casal = 1970 / solteiro = 1196) – 0,75 * Imposto bruto
redução = 1970 – 0,75 * 2115 = 383,75, arredondado para 384 euros.
Novo imposto bruto = 2115 – 384 = 1731 euros
4.1.4. Cálculo da redução fiscal

Abaixo de um determinado limiar, é aplicada uma redução de 20 % sobre o imposto bruto resultante dos cálculos anteriores. Em 2019, os limiares são os seguintes:
- solteiro: 21 037 euros;
- casal: 42 074 euros; (o valor de 37 968 utilizado no exemplo acima parece estar errado);
Este limiar é aumentado pelo valor: 3 797 * (número de meias-partes atribuídas pelos filhos).
Ainda no caso do trabalhador casado com dois filhos e um salário anual S de 50 000 euros:
- o seu rendimento tributável (45 000 euros) é inferior ao limiar (42 074 + 2 * 3 797) = 49 668 euros;
- tem, portanto, direito a uma redução de 20 % do seu imposto: 1731 * 0,2 = 346,2 euros, arredondado para 347 euros;
- o imposto bruto do contribuinte passa a ser: 1 731 – 347 = 1 384 euros;
4.1.5. Cálculo do imposto líquido
O nosso cálculo termina aqui: o imposto líquido a pagar será de 1384 euros. Na realidade, o contribuinte pode beneficiar de outras reduções, nomeadamente por doações a organismos de interesse público ou geral.
4.1.6. Caso dos rendimentos elevados
O nosso exemplo anterior corresponde à maioria dos casos de assalariados. No entanto, o cálculo do imposto é diferente no caso dos rendimentos elevados.
4.1.6.1. Limite máximo da redução de 10 % sobre os rendimentos anuais
Na maioria dos casos, o rendimento tributável é obtido através da fórmula: R = 0,9 * S, em que S é o salário anual. A isto chama-se a redução de 10 %. Esta redução está sujeita a um limite máximo. Em 2019:
- não pode ser superior a 12 502 euros;
- não pode ser inferior a 437 euros;
Consideremos o caso de um trabalhador solteiro, sem filhos, com um salário anual de 200 000 euros:
- a redução de 10 % é de 20 000 euros > 12 502 euros. É, portanto, reduzida para 12 502 euros;
4.1.6.2. Limite máximo do quociente familiar
Consideremos um caso em que se aplica o limite máximo familiar referido no parágrafo anterior. Tomemos o caso de um casal com três filhos e rendimentos anuais de 100 000 euros. Repassemos as etapas do cálculo:
- A dedução de 10 % corresponde a 10 000 euros < 12 502 euros. O rendimento tributável R é, portanto, 100 000 - 10 000 = 90 000 euros;
- o casal tem nbParts = 2 + 0,5 × 2 + 1 = 4 quotas;
- o seu quociente familiar é, portanto, QF = R / nbParts = 90 000 / 4 = 22 500 euros;
- o seu imposto bruto I1 com filhos é I1=0,14*90 000-1 394,96*4= 7 020 euros;
- o seu imposto bruto I2 sem filhos:
- QF = 90 000 / 2 = 45 000 euros;
- I2 = 0,3 × 90 000 − 5 798 × 2 = 15 404 euros;
- a regra do limite máximo do quociente familiar estipula que o benefício resultante dos filhos não pode exceder (1551 × 4 meias-partes) = 6204 euros. Ora, neste caso, é I2-I1 = 15404 – 7020 = 8384 euros, ou seja, superior a 6204 euros;
- o imposto bruto é, portanto, recalculado como I3 = I2-6204 = 15 404 – 6 204 = 9 200 euros;
Este casal não terá qualquer dedução nem redução e o seu imposto final será de 9200 euros.
4.1.7. Números oficiais
O cálculo do imposto é complexo. Ao longo do documento, os testes serão realizados com os seguintes exemplos. Os resultados são os do simulador da administração fiscal [https://www3.impots.gouv.fr/simulateur/calcul_impot/2019/simplifie/index.htm]:
Contribuinte | Resultados oficiais | Resultados do algoritmo do documento |
Casal com 2 filhos e rendimentos anuais de 55 555 euros | Imposto = 2815 euros Taxa de imposto = 14 % | Imposto = 2814 euros Taxa de imposto = 14 % |
Casal com 2 filhos e rendimentos anuais de 50 000 euros | Imposto = 1 385 euros Abatimento = 720 euros Redução = 0 euros Taxa de imposto = 14 % | Imposto = 1 384 euros Abatimento = 384 euros Redução = 347 euros Taxa de imposto = 14 % |
Casal com 3 filhos e rendimentos anuais de 50 000 euros | Imposto = 0 euros Abatimento = 384 euros Redução = 346 euros Taxa de imposto = 14 % | Imposto = 0 euros Desconto = 720 euros Redução = 0 euros Taxa de imposto = 14 % |
Solteiro, com 2 filhos e rendimentos anuais de 100 000 euros | Imposto = 19 884 euros Abatimento = 0 euros Redução = 0 euros Taxa de imposto = 41 % | Imposto = 19 884 euros Sobretaxa = 4 480 euros Desconto = 0 euros Redução = 0 euros Taxa de imposto = 41 % |
Solteiro, com 3 filhos e rendimentos anuais de 100 000 euros | Imposto = 16 782 euros Abatimento = 0 euros Redução = 0 euros Taxa de imposto = 41 % | Imposto = 16 782 euros Sobretaxa = 7 176 euros Desconto = 0 euros Redução = 0 euros Taxa de imposto = 41 % |
Casal com 3 filhos e rendimentos anuais de 100 000 euros | Imposto = 9 200 euros Abatimento = 0 euros Redução = 0 euros Taxa de imposto = 30 % | Imposto = 9 200 euros Sobretaxa = 2 180 euros Desconto = 0 euros Redução = 0 euros Taxa de imposto = 30 % |
Casal com 5 filhos e rendimentos anuais de 100 000 euros | Imposto = 4 230 euros Abatimento = 0 euros Redução = 0 euros Taxa de imposto = 14 % | Imposto = 4 230 euros Abatimento = 0 euros Redução = 0 euros Taxa de imposto = 14 % |
Solteiro, sem filhos e com rendimentos anuais de 100 000 euros | Imposto = 22 986 euros Abatimento = 0 euros Redução = 0 euros Taxa de imposto = 41 % | Imposto = 22 986 euros Sobretaxa = 0 euros Desconto = 0 euros Redução = 0 euros Taxa de imposto = 41 % |
Casal com 2 filhos e rendimentos anuais de 30 000 euros | Imposto = 0 euros Abatimento = 0 euros Redução = 0 euros Taxa de imposto = 0 % | Imposto = 0 euros Desconto = 0 euros Redução = 0 euros Taxa de imposto = 0 % |
Solteiro, sem filhos e com rendimentos anuais de 200 000 euros | Imposto = 64 211 euros Abatimento = 0 euros Redução = 0 euros Taxa de imposto = 45 % | Imposto = 64 210 euros Sobretaxa = 7 498 euros Desconto = 0 euros Redução = 0 euros Taxa de imposto = 45 % |
Casal com 3 filhos e rendimentos anuais de 200 000 euros | Imposto = 42 843 euros Abatimento = 0 euros Redução = 0 euros Taxa de imposto = 41 % | Imposto = 42 842 euros Sobretaxa = 17 283 euros Desconto = 0 euros Redução = 0 euros Taxa de imposto = 41 % |
No exemplo acima, denomina-se «sobretaxa» o montante adicional que os rendimentos mais elevados pagam devido a dois fenómenos:
- o limite máximo de 10 % na dedução sobre os rendimentos anuais;
- o limite máximo do quociente familiar;
Este indicador não pôde ser verificado, uma vez que o simulador da administração fiscal não o fornece.
Verifica-se que o algoritmo do documento apresenta sempre um imposto correto, embora com uma margem de erro de 1 euro. Esta margem de erro resulta dos arredondamentos. Todos os montantes são arredondados, por vezes para o euro superior, por vezes para o euro inferior. Como eu não conhecia as regras oficiais, os montantes do algoritmo do documento foram arredondados:
- para o euro superior no caso dos descontos e reduções;
- para o euro inferior no caso dos acréscimos e do imposto final;
Posteriormente, serão realizados testes para verificar a validade dos resultados. Estes serão efetuados com os exemplos da tabela anterior, com uma margem de erro aceitável de 1 euro.
4.2. A estrutura dos scripts

4.3. Versão 1
4.3.1. O algoritmo
Apresentamos um primeiro programa em que:
- os dados necessários para o cálculo do imposto estão codificados de forma estática no código, sob a forma de tabelas e constantes;
- os dados dos contribuintes (estado civil, filhos, salário) encontram-se num primeiro ficheiro de texto [taxpayersdata.txt];
- os resultados do cálculo do imposto (estado civil, filhos, salário, imposto) são armazenados num segundo ficheiro de texto [resultats.txt];
O script [version-01/main.php] é o seguinte:
<?php
// tipos rigorosos para os parâmetros das funções
declare(strict_types=1);
// constantes globais
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);
// definição de constantes locais
$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);
// leitura de dados
$data = fopen($DATA, "r");
if (!$data) {
print "Impossible d'ouvrir en lecture le fichier des données [$DATA]\n";
exit;
}
// abertura do ficheiro de resultados
$résultats = fopen($RESULTATS, "w");
if (!$résultats) {
print "Impossible de créer le fichier des résultats [$RESULTATS]\n";
exit;
}
// processa-se a linha atual do ficheiro de dados
while ($ligne = fgets($data, 100)) {
// remove-se o eventual caractere de fim de linha
$ligne = cutNewLineChar($ligne);
// recuperam-se os 3 campos «casado:filhos:salário» que formam $ligne
list($marié, $enfants, $salaire) = explode(",", $ligne);
// calcula-se o imposto
$result = calculImpot($marié, (int) $enfants, (float) $salaire, $limites, $coeffR, $coeffN);
// regista-se o resultado no ficheiro de resultados
$result = ["marié" => $marié, "enfants" => $enfants, "salaire" => $salaire] + $result;
fputs($résultats, \json_encode($result, JSON_UNESCAPED_UNICODE) . "\n");
// dado seguinte
}
// fecha-se os ficheiros
fclose($data);
fclose($résultats);
// fim
exit;
// --------------------------------------------------------------------------
function cutNewLinechar(string $ligne): string {
// elimina-se o marcador de fim de linha de $ligne, caso exista
$L = strlen($ligne); // comprimento da linha
while (substr($ligne, $L - 1, 1) === "\n" or substr($ligne, $L - 1, 1) === "\r") {
$ligne = substr($ligne, 0, $L - 1);
$L--;
}
// fim
return($ligne);
}
// cálculo do imposto
// --------------------------------------------------------------------------
function calculImpot(string $marié, int $enfants, float $salaire, array $limites, array $coeffR, array $coeffN): array {
…
// resultado
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 {
…
// resultado
return ["impôt" => $impôt, "surcôte" => $surcôte, "taux" => $coeffR[$i]];
}
// revenuImposable=salárioAnual-abatimento
// a dedução tem um valor mínimo e um valor máximo
function getRevenuImposable(float $salaire): float {
…
// resultado
return floor($revenuImposable);
}
// calcula uma eventual redução
function getDecote(string $marié, float $salaire, float $impots): float {
…
// resultado
return ceil($décôte);
}
// calcula uma eventual redução
function getRéduction(string $marié, float $salaire, int $enfants, float $impots): float {
/…
// resultado
return ceil($réduction);
}
O ficheiro de dados taxpayersdata.txt (casado, filhos, salário):
Os ficheiros résultats.txt (estado civil, filhos, salário, imposto, sobretaxa, abatimento, redução, taxa de imposto) dos resultados obtidos:
Comentários
- linha 4: impõe-se o cumprimento rigoroso do tipo dos parâmetros das funções;
- linhas 7-16: definição de todas as constantes necessárias para o cálculo do imposto;
- linha 19: o nome do ficheiro de texto que contém os dados dos contribuintes (casado, filhos, salário);
- linha 20: o nome do ficheiro de texto que contém os resultados (estado civil, filhos, salário, imposto) do cálculo do imposto;
- linhas 21-23: as três tabelas de dados que definem as diferentes faixas de tributação do cálculo do imposto;
- linhas 26-30: abertura em modo de leitura [r] do ficheiro de dados dos contribuintes. A função [fopen] devolve o valor booleano FALSE caso a abertura não tenha sido possível;
- linhas 33-37: abertura em modo de gravação [w] do ficheiro de resultados;
- linhas 40-51: ciclo de leitura das linhas (cônjuge, filhos, salário) do ficheiro de dados dos contribuintes;
- linha 40: a função [fgets] lê 100 caracteres e pára na primeira marca de fim de linha encontrada. Aqui, todas as linhas têm menos de 100 caracteres. Se for encontrada uma marca de fim de linha, esta é incluída na cadeia de caracteres devolvida. Quando se chega ao fim do ficheiro, a função [fgets] devolve o valor FALSE;
- linha 42: o símbolo de fim de linha é removido;
- linha 44: os componentes (cônjuge, filhos, salário) da linha são recuperados;
- linha 46: o imposto é calculado. O resultado é devolvido sob a forma de uma tabela associativa (linha 76);
- linha 48: ao tabuleiro recuperado anteriormente, são adicionadas as chaves [marié, enfants, salaire];
- linha 49: o resultado é guardado no ficheiro de resultados sob a forma de uma cadeia de caracteres jSON;
- linhas 53-54: assim que o ficheiro de dados dos contribuintes for totalmente processado, os ficheiros são fechados;
- linha 60: a função que remove o marcador de fim de linha de uma linha $ligne. O marcador de fim de linha é a cadeia «\r\n» nos sistemas Windows e «\n» nos sistemas Unix. O resultado é a cadeia de entrada sem o seu marcador de fim de linha.
- linhas 63-64: substr($chaîne,$début,$taille) é a subcadeia de $chaîne que começa no carácter $début e tem, no máximo, $taille caracteres;
A função [calculImpot] é a seguinte:
// constantes globais
define("PLAFOND_QF_DEMI_PART", 1551);
// cálculo do imposto
// --------------------------------------------------------------------------
function calculImpot(string $marié, int $enfants, float $salaire, array $limites, array $coeffR, array $coeffN): array {
// $marié: sim, não
// $enfants: número de filhos
// $salaire: salário anual
// $limites, $coeffR, $coeffN: tabelas de dados que permitem o cálculo do imposto
//
// cálculo do imposto com filhos
$result1 = calculImpot2($marié, $enfants, $salaire, $limites, $coeffR, $coeffN);
$impot1 = $result1["impôt"];
// cálculo do imposto sem filhos
if ($enfants != 0) {
$result2 = calculImpot2($marié, 0, $salaire, $limites, $coeffR, $coeffN);
$impot2 = $result2["impôt"];
// aplicação do limite máximo do quociente familiar
if ($enfants < 3) {
// $PLAFOND_QF_DEMI_PART euros para os dois primeiros filhos
$impot2 = $impot2 - $enfants * PLAFOND_QF_DEMI_PART;
} else {
// $PLAFOND_QF_DEMI_PART euros para os dois primeiros filhos, o dobro para os seguintes
$impot2 = $impot2 - 2 * PLAFOND_QF_DEMI_PART - ($enfants - 2) * 2 * PLAFOND_QF_DEMI_PART;
}
} else {
$impot2 = $impot1;
$result2 = $result1;
}
// considera-se o imposto mais elevado, com a taxa e a sobretaxa correspondentes
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 uma eventual dedução
$décôte = getDecote($marié, $salaire, $impot);
$impot -= $décôte;
// cálculo de uma eventual redução de impostos
$réduction = getRéduction($marié, $salaire, $enfants, $impot);
$impot -= $réduction;
// resultado
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 {
…
// resultado
return ["impôt" => $impôt, "surcôte" => $surcôte, "taux" => $coeffR[$i]];
}
// revenuImposable=salárioAnual-abatimento
// a dedução de 10 % tem um valor mínimo e um valor máximo
function getRevenuImposable(float $salaire): float {
…
}
// calcula uma eventual redução
function getDecote(string $marié, float $salaire, float $impots): float {
…
// resultado
return ceil($décôte);
}
// calcula uma eventual redução
function getRéduction(string $marié, float $salaire, int $enfants, float $impots): float {
…
// resultado
return ceil($réduction);
}
Comentários
- linha 10: o imposto bruto é calculado incluindo os filhos. O resultado obtido tem o formato ["impôt" => $impôt, "surcôte" => $surcôte, "taux" => $coeffR[$i]] com:
- [‘impôt’]: o imposto bruto;
- [‘surcôte’]: o montante da sobretaxa, se houver. Esta existe quando a dedução de 10 % ultrapassa o limiar de 12 502 euros;
- [‘taux’]: a taxa de imposto do contribuinte;
- linha 11: o imposto bruto a pagar [impot1];
- linhas 13-14: se o contribuinte tiver pelo menos um filho, o cálculo do imposto é repetido com os mesmos dados, mas considerando 0 filhos. Este segundo cálculo é necessário para verificar se a redução resultante dos filhos (nbParts*coeffN) é superior a um determinado limiar;
- linha 15: o imposto bruto [impot2] a pagar;
- linhas 16-23: para o imposto bruto [impot2], considera-se agora a influência dos filhos: cada 1/2 parte resultante dos filhos permite uma redução de [PLAFOND_QF_DEMI_PART] euros;
- linhas 25-26: caso em que o contribuinte não tenha filhos. Neste caso, o cálculo de [impot2] é desnecessário. É igual a [impot1];
- linhas 29-37: foram calculados dois impostos brutos [impot1, impot2]. A administração fiscal retém o mais elevado dos dois. Obtém-se um imposto bruto [impot];
- linhas 39-40: o montante bruto [impot] pode ser objeto de uma redução;
- linhas 42-43: o montante bruto [impot] pode ser objeto de uma redução;
- linha 45: [impot] é agora o imposto líquido a pagar. Devolvemos os resultados;
A função [calculImpot2] é a seguinte:
// --------------------------------------------------------------------------
function calculImpot2(string $marié, int $enfants, float $salaire, array $limites, array $coeffR, array $coeffN): array {
// $marié: sim, não
// $enfants: número de filhos
// $salaire: salário anual
// $limites, $coeffR, $coeffN: tabelas de dados que permitem o cálculo do imposto
//
// número de quotas
$marié = strtolower($marié);
if ($marié === "oui") {
$nbParts = $enfants / 2 + 2;
} else {
$nbParts = $enfants / 2 + 1;
}
// 1 quota por filho a partir do terceiro
if ($enfants >= 3) {
// meia quota adicional por cada filho a partir do terceiro
$nbParts += 0.5 * ($enfants - 2);
}
// rendimento tributável
$revenuImposable = getRevenuImposable($salaire);
// sobretaxa
$surcôte = floor($revenuImposable - 0.9 * $salaire);
// devido a problemas de arredondamento
if ($surcôte < 0) {
$surcôte = 0;
}
// quociente familiar
$quotient = $revenuImposable / $nbParts;
// é colocado no final da tabela de limites para interromper o ciclo seguinte
$limites[count($limites) - 1] = $quotient;
// cálculo do imposto
$i = 0;
while ($quotient > $limites[$i]) {
$i++;
}
// uma vez que se colocou $quotient no final da tabela $limites, o ciclo anterior
// não pode ultrapassar os limites da tabela $limites
// agora podemos calcular o imposto
$impôt = floor($revenuImposable * $coeffR[$i] - $nbParts * $coeffN[$i]);
// resultado
return ["impôt" => $impôt, "surcôte" => $surcôte, "taux" => $coeffR[$i]];
}
// revenuImposable = salárioAnual - dedução
// a dedução tem um valor mínimo e um valor máximo
function getRevenuImposable(float $salaire): float {
// resultado
return floor($revenuImposable);
}
Comentários
- Aqui aplica-se o cálculo do imposto segundo a tabela progressiva;
- linha 9: strtolower($chaîne) converte $chaîne em minúsculas;
- linhas 10-19: cálculo do número de quotas do contribuinte;
- linha 21: calcula-se o rendimento tributável com a ajuda de uma função. Com efeito, verificou-se que nem sempre é igual a 0,9*revenusAnnuels. A dedução de 10 % está, de facto, limitada a 12 502 euros;
- linha 23: cálculo da eventual sobretaxa se o rendimento tributável for superior a 0,9*revenusAnnuels;
- linhas 25-27: corrige o facto de, devido a erros de arredondamento, por vezes se obter [$surcôte=-1];
- linha 29: o quociente familiar;
- linhas 30-36: este quociente permite determinar a faixa de tributação do contribuinte;
- linha 40: uma vez determinada a faixa de tributação do contribuinte, é possível calcular o seu imposto bruto. A função floor($x) devolve o valor inteiro imediatamente inferior a [$x];
- linha 42: apresentam-se as informações calculadas;
A função [getRevenuImposable] é a seguinte:
// constantes globais
define("ABATTEMENT_DIXPOURCENT_MAX", 12502);
define("ABATTEMENT_DIXPOURCENT_MIN", 437);
// revenuImposable=salárioAnual-abatimento
// a dedução tem um valor mínimo e um valor máximo
function getRevenuImposable(float $salaire): float {
// abatimento de 10% do salário
$abattement = 0.1 * $salaire;
// esta dedução não pode exceder ABATTEMENT_DIXPOURCENT_MAX
if ($abattement > ABATTEMENT_DIXPOURCENT_MAX) {
$abattement = ABATTEMENT_DIXPOURCENT_MAX;
}
// a dedução não pode ser inferior a ABATTEMENT_DIXPOURCENT_MIN
if ($abattement < ABATTEMENT_DIXPOURCENT_MIN) {
$abattement = ABATTEMENT_DIXPOURCENT_MIN;
}
// rendimento tributável
$revenuImposable = $salaire - $abattement;
// resultado
return floor($revenuImposable);
}
Comentários
- linha 5: a dedução normal é de 10 % do salário anual;
- linhas 7-9: a dedução não pode exceder a dedução máxima [ABATTEMENT_DIXPOURCENT_MAX];
- linhas 10-13: a dedução não pode ser inferior à dedução mínima [ABATTEMENT_DIXPOURCENT_MIN];
- linha 15: cálculo do rendimento tributável;
A função [getDecote] é a seguinte:
// constantes globais
define("PLAFOND_DECOTE_CELIBATAIRE", 1196);
define("PLAFOND_DECOTE_COUPLE", 1970);
define("PLAFOND_IMPOT_COUPLE_POUR_DECOTE", 2627);
define("PLAFOND_IMPOT_CELIBATAIRE_POUR_DECOTE", 1595);
// calcula um eventual desconto
function getDecote(string $marié, float $salaire, float $impots): float {
// inicialmente, um desconto nulo
$décôte = 0;
// montante máximo de imposto para beneficiar da redução
$plafondImpôtPourDécôte = $marié === "oui" ? PLAFOND_IMPOT_COUPLE_POUR_DECOTE : PLAFOND_IMPOT_CELIBATAIRE_POUR_DECOTE;
if ($impots < $plafondImpôtPourDécôte) {
// montante máximo da redução
$plafondDécôte = $marié === "oui" ? PLAFOND_DECOTE_COUPLE : PLAFOND_DECOTE_CELIBATAIRE;
// abatimento teórico
$décôte = $plafondDécôte - 0.75 * $impots;
// a redução não pode exceder o montante do imposto
if ($décôte > $impots) {
$décôte = $impots;
}
// não há dedução <0
if ($décôte < 0) {
$décôte = 0;
}
}
// resultado
return ceil($décôte);
}
Comentários
- linha 6: montante máximo do imposto bruto para ter direito a uma redução. Este montante é diferente para solteiros e casais;
- linha 7: se o contribuinte tem direito à dedução;
- linha 11: a fórmula da dedução. [plafondDécôte] é o montante máximo da dedução. Este montante máximo é calculado na linha 9. Também neste caso, depende da situação do contribuinte, casado ou solteiro;
- linhas 13-15: a dedução não pode ser superior ao imposto bruto a pagar. É o caso, por exemplo, se [impots] for igual a 0 na linha 11;
- linhas 17-19: para evitar um arredondamento para -1;
A função [getRéduction] é a seguinte:
// constantes globais
define("PLAFOND_REVENUS_CELIBATAIRE_POUR_REDUCTION", 21037);
define("PLAFOND_REVENUS_COUPLE_POUR_REDUCTION", 42074);
define("VALEUR_REDUC_DEMI_PART", 3797);
// calcula uma eventual redução
function getRéduction(string $marié, float $salaire, int $enfants, float $impots): float {
// o limite máximo de rendimentos para ter direito à redução de 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;
}
// rendimento tributável
$revenuImposable = getRevenuImposable($salaire);
// redução
$réduction = 0;
if ($revenuImposable < $plafondRevenuPourRéduction) {
// redução de 20%
$réduction = 0.2 * $impots;
}
// resultado
return ceil($réduction);
}
Comentários
- linhas 4-10: para ter direito a uma redução fiscal, é necessário que o rendimento tributável (linha 10) seja inferior a um limite máximo calculado nas linhas 4-8;
- linhas 13-16: se preencher as condições, o contribuinte tem direito a uma redução fiscal de 20 % (linha 15);
4.3.2. Resultados
O ficheiro de dados taxpayersdata.txt (casado, filhos, salário):
O ficheiro résultats.txt (casado, filhos, salário, imposto, majoração, redução, abatimento, taxa de imposto) dos resultados obtidos:
Os resultados obtidos estão em conformidade com os obtidos com o simulador da administração fiscal.
4.3.3. Conclusão
O algoritmo de cálculo do imposto, mesmo em casos considerados simples, é complexo. Não voltaremos a abordar este assunto. Ao longo das versões, a sua essência permanecerá a mesma, apesar de algumas alterações na apresentação. Por isso, comentaremos apenas estas últimas.
4.4. Versão 2
4.4.1. As alterações
Na versão anterior, os dados necessários ao cálculo do imposto estavam codificados de forma estática sob a forma de constantes e tabelas. Este método deve ser evitado. Na nova versão, esses dados são externalizados para um ficheiro jSON:

O conteúdo do ficheiro [taxadmindata.json] é o seguinte:
{
"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
}
A nova versão [version-02/main.php] é a seguinte:
<?php
// respeito rigoroso dos tipos declarados dos parâmetros das funções
declare (strict_types=1);
// definição das constantes
$TAXPAYERSDATA = "taxpayersdata.txt";
$RESULTATS = "resultats.txt";
$TAXADMINDATA = "taxadmindata.json";
// recupera-se o conteúdo do ficheiro de dados fiscais
$fileContents = \file_get_contents($TAXADMINDATA);
$erreur = FALSE;
// erro?
if (!$fileContents) {
// regista-se o erro
$erreur = TRUE;
$message = "Le fichier des données [$TAXADMINDATA] n'existe pas";
}
if (!$erreur) {
// recuperar o código jSON do ficheiro de configuração numa tabela associativa
$taxAdminData = \json_decode($fileContents, true);
// erro?
if (!$taxAdminData) {
// regista-se o erro
$erreur = TRUE;
$message = "Le fichier de données jSON [$TAXADMINDATA] n'a pu être exploité correctement";
}
}
// erro?
if ($erreur) {
print "$message\n";
exit;
}
// abertura do ficheiro de resultados
$résultats = fopen($RESULTATS, "w");
if (!$résultats) {
print "Impossible de créer le fichier des résultats [$RESULTATS]\n";
// saída
exit;
}
// abertura do ficheiro de dados dos contribuintes
$taxpayersdata = fopen($TAXPAYERSDATA, "r");
if (!$taxpayersdata) {
print "Impossible d'ouvrir le fichier des contribuables [$TAXPAYERSDATA]\n";
// saída
exit;
}
// processa-se a linha atual do ficheiro de dados
while ($ligne = fgets($taxpayersdata, 100)) {
// remove-se a eventual marca de fim de linha
$ligne = cutNewLineChar($ligne);
// recuperam-se os 3 campos «casado:filhos:salário» que formam $ligne
list($marié, $enfants, $salaire) = explode(",", $ligne);
// calcula-se o imposto
$result = calculImpot($taxAdminData, $marié, (int) $enfants, (int) $salaire);
// regista-se o resultado no ficheiro de resultados
$result = ["marié" => $marié, "enfants" => $enfants, "salaire" => $salaire] + $result;
fputs($résultats, \json_encode($result, JSON_UNESCAPED_UNICODE) . "\n");
// dado seguinte
}
// fecha-se os ficheiros
fclose($taxpayersdata);
fclose($résultats);
// fim
exit;
// --------------------------------------------------------------------------
function cutNewLinechar(string $ligne) {
…
// fim
return($ligne);
}
// cálculo do imposto
// --------------------------------------------------------------------------
function calculImpot(array $taxAdminData, string $marié, int $enfants, float $salaire) {
// $marié: sim, não
// $enfants: número de filhos
// $salaire: salário anual
// $taxAdminData: dados da administração fiscal
//
// cálculo do imposto com filhos
$result1 = calculImpot2($taxAdminData, $marié, $enfants, $salaire);
$impot1 = $result1["impôt"];
// cálculo do imposto sem filhos
if ($enfants != 0) {
$result2 = calculImpot2($taxAdminData, $marié, 0, $salaire);
$impot2 = $result2["impôt"];
// aplicação do limite máximo do quociente familiar
if ($enfants < 3) {
// $PLAFOND_QF_DEMI_PART euros para os dois primeiros filhos
$impot2 = $impot2 - $enfants * $taxAdminData["PLAFOND_QF_DEMI_PART"];
} else {
// $PLAFOND_QF_DEMI_PART euros para os dois primeiros filhos, o dobro para os seguintes
$impot2 = $impot2 - 2 * $taxAdminData["PLAFOND_QF_DEMI_PART"] - ($enfants - 2) * 2 * $taxAdminData["PLAFOND_QF_DEMI_PART"];
}
} else {
$impot2 = $impot1;
$result2 = $result1;
}
// considera-se o imposto mais elevado, com a taxa e a sobretaxa correspondentes
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 uma eventual dedução
$décôte = getDecote($taxAdminData, $marié, $salaire, $impot);
$impot -= $décôte;
// cálculo de uma eventual redução de impostos
$réduction = getRéduction($taxAdminData, $marié, $salaire, $enfants, $impot);
$impot -= $réduction;
// resultado
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é: sim, não
…
// resultado
return ["impôt" => $impôt, "surcôte" => $surcôte, "taux" => $coeffR[$i]];
}
// revenuImposable=salárioAnual-abatimento
// a dedução tem um valor mínimo e um valor máximo
function getRevenuImposable(array $taxAdminData, float $salaire): float {
…
// resultado
return floor($revenuImposable);
}
// calcula uma eventual redução
function getDecote(array $taxAdminData, string $marié, float $salaire, float $impots): float {
…
// resultado
return ceil($décôte);
}
// calcula uma eventual redução
function getRéduction(array $taxAdminData, string $marié, float $salaire, int $enfants, float $impots): float {
…
// resultado
return ceil($réduction);
}
Comentários
- linhas 11-19: tenta-se ler o conteúdo do ficheiro jSON, denominado [TAXADMINDATA];
- linhas 21-30: se se conseguiu ler o ficheiro jSON, o seu conteúdo é descodificado na tabela associativa [$taxAdminData];
- linhas 32-36: se tiver ocorrido um erro numa das duas operações anteriores, é gravada uma mensagem de erro na consola e o programa é interrompido;
- a diferença em relação à versão 01 é que, aqui, os dados (tabelas e constantes) da administração fiscal encontram-se na tabela associativa [$taxAdminData], enquanto que na versão 01 se encontravam em tabelas e constantes globais. É o caráter global destas constantes que faz a diferença entre as duas versões:
- na versão 01, as constantes eram conhecidas em todas as funções de [main.php];
- para obter o mesmo resultado na versão 02, é necessário passar a tabela associativa [$taxAdminData] como parâmetro a todas as funções (linhas 83, 129, 138, 145, 152);
- cada função da versão 02 deve utilizar o conteúdo da matriz [$taxAdminData];
- linhas 83-126: na função [calculerImpot], onde antes se utilizavam constantes globais ou as tabelas [limites, coeffR, coeffN], passa-se agora a utilizar o conteúdo da tabela [$taxAdminData] recebida como parâmetro (linhas 99, 102);
- todas as outras funções foram reescritas da mesma forma;
Os resultados obtidos são os mesmos que os da versão anterior.
4.4.2. Conclusão
A versão 02 é muito mais flexível do que a versão 01. Em 2020, o algoritmo de cálculo do imposto será provavelmente o mesmo que em 2019. Apenas as faixas de tributação e as constantes de cálculo terão mudado. Bastará, então, atualizar o ficheiro [taxadmindata.json]. Com a versão 01, é necessário aceder ao código para alterar as faixas de tributação e as constantes de cálculo. No entanto, é provável que as pessoas que têm de alterar os valores das faixas de tributação e das constantes de cálculo não tenham acesso ao código do algoritmo.