4. Exercício prático - IMPOTS
4.1. O problema
Propõe-se escrever um programa que permita calcular o imposto de um contribuinte. Consideramos o caso simplificado de um contribuinte que tenha apenas o seu salário para declarar:
- calcula-se o número de quotas do trabalhador nbParts = nbEnfants/2 + 1 se não for casado, nbEnfants/2 + 2 se for casado, em que nbEnfants é o número de filhos que tem.
- calcula-se o seu rendimento tributável R = 0,72 * S, em que S é o seu salário anual
- calcula-se o seu coeficiente familiar Q = R/N
calcula-se o seu imposto I com base nos seguintes dados
12620,0 | 0 | 0 |
13 190 | 0,05 | 631 |
15640 | 0,1 | 1290,5 |
24 740 | 0,15 | 2072,5 |
31 810 | 0,2 | 3309,5 |
39 970 | 0,25 | 4900 |
48 360 | 0,3 | 6898,5 |
55 790 | 0,35 | 9316,5 |
92 970 | 0,4 | 12106 |
127 860 | 0,45 | 16 754,5 |
151 250 | 0,50 | 23 147,5 |
172 040 | 0,55 | 30710 |
195 000 | 0,60 | 39312 |
0 | 0,65 | 49062 |
Cada linha tem 3 campos. Para calcular o imposto I, procura-se a primeira linha em que QF <= campo1. Por exemplo, se QF = 30000, encontrar-se-á a linha
O imposto I é, então, igual a 0,15*R - 2072,5*nbParts. 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 resulta no imposto I = 0,65*R – 49062*nbParts.
4.2. Versão com tabelas (impots_01)
Apresentamos um primeiro programa em que:
- os dados necessários para o cálculo do imposto estão em três tabelas (limites, coeffR, coeffN)
- os dados dos contribuintes (casado, filhos, salário) encontram-se num primeiro ficheiro de texto
- os resultados do cálculo do imposto (estado civil, filhos, salário, imposto) são guardados num segundo ficheiro de texto
<?php
// definição das constantes
$DATA = "data.txt";
$RESULTATS = "resultats.txt";
$limites = array(12620, 13190, 15640, 24740, 31810, 39970, 48360, 55790, 92970, 127860, 151250, 172040, 195000);
$coeffR = array(0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65);
$coeffN = array(0, 631, 1290.5, 2072.5, 3309.5, 4900, 6898.5, 9316.5, 12106, 16754.5, 23147.5, 30710, 39312, 49062);
// leitura dos 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» e «salário» que formam $ligne
list($marié, $enfants, $salaire) = explode(",", $ligne);
// calcula-se o imposto
$impôt = calculImpots($marié, $enfants, $salaire, $limites, $coeffR, $coeffN);
// regista-se o resultado no ficheiro de resultados
fputs($résultats, "$marié:$enfants:$salaire:$impôt\n");
// próximo contribuinte
}
// fecha-se os ficheiros
fclose($data);
fclose($résultats);
// fim
exit;
// --------------------------------------------------------------------------
function cutNewLinechar($ligne) {
// 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);
}
// --------------------------------------------------------------------------
function calculImpots($marié, $enfants, $salaire, $limites, $coeffR, $coeffN) {
// $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;
// mais 1/2 quota se houver pelo menos 3 filhos
if ($enfants >= 3)
$nbParts+=0.5;
// rendimento tributável
$revenuImposable = 0.72 * $salaire;
// quociente familiar
$quotient = $revenuImposable / $nbParts;
// é colocado no final da tabela de limites para interromper o ciclo seguinte
array_push($limites, $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
return floor($revenuImposable * $coeffR[$i] - $nbParts * $coeffN[$i]);
}
?>
O ficheiro de dados data.txt (casado, filhos, salário):
Os ficheiros résultats.txt (estado civil, filhos, salário, impostos) dos resultados obtidos:
oui:2:200000:22504
non:2:200000:33388
oui:3:200000:16400
non:3:200000:22504
oui:5:50000:0
non:0:3000000:1354938
Comentários
- linha 4: o nome do ficheiro de texto que contém os dados dos contribuintes (estado civil, filhos, salário)
- linha 5: o nome do ficheiro de texto que contém os resultados (estado civil, filhos, salário, imposto) do cálculo do imposto
- linhas 6-8: as três tabelas de dados que permitem o cálculo do imposto
- linhas 11-15: abertura do ficheiro de dados dos contribuintes em modo de leitura
- linhas 18-22: abertura do ficheiro de resultados para gravação
- linha 25: ciclo de leitura das linhas (casado, filhos, salário) do ficheiro dos contribuintes
- linha 27: o marcador de fim de linha é removido
- linha 29: os componentes (casado, filhos, salário) da linha são recuperados
- linha 31: o imposto é calculado
- linha 33: o imposto é guardado no ficheiro de resultados
- linhas 37-38: assim que o ficheiro dos contribuintes for totalmente processado, os ficheiros são fechados
- linha 44: 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 47-48: 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.
- linha 63: strtolower($chaîne) transforma $chaîne em minúsculas
- linha 76: array_push($tableau,$élément) adiciona $élément ao final da matriz $tableau. Note-se que a matriz $limites foi passada por valor para a função calculerImpot (linha 56: )). Esta adição de elemento é, portanto, efetuada na cópia do parâmetro efetivo, permanecendo este inalterado.
- linha 84: a função floor($x) devolve o valor inteiro imediatamente inferior a $x.
4.3. Versão com ficheiros de texto (impots_02)
Esta nova versão difere da anterior apenas pelo facto de os dados ($limites, $coeffR, $coeffN) necessários para o cálculo do imposto se encontrarem agora num ficheiro de texto com o seguinte formato:
Ficheiro impots.txt:
12620:13190:15640:24740:31810:39970:48360:55790:92970:127860:151250:172040:195000:0
0:0.05:0.1:0.15:0.2:0.25:0.3:0.35:0.4:0.45:0.5:0.55:0.6:0.65
0:631:1290.5:2072.5:3309.5:4900:6898.5:9316.5:12106:16754.5:23147.5:30710:39312:49062
A nova versão limita-se a ler os dados do ficheiro [impots.txt] para os inserir em três tabelas ($limites, $coeffR, $coeffN). Assim, voltamos ao caso anterior. Apresentamos apenas as alterações:
<?php
// definição das constantes
$DATA = "data.txt";
$RESULTATS = "resultats.txt";
$IMPOTS = "impots.txt";
// os dados necessários para o cálculo do imposto foram colocados no ficheiro $IMPOTS
// à razão de uma linha por tabela, na forma
// val1:val2:val3,...
list($erreur, $limites, $coeffR, $coeffN) = getTables($IMPOTS);
// Houve algum erro?
if ($erreur) {
print "$erreur\n";
exit;
}//if
// leitura dos dados do utilizador
...
// fim
exit;
// --------------------------------------------------------------------------
function cutNewLinechar($ligne) {
// elimina-se o marcador de fim de linha de $ligne, caso exista
...
}
// --------------------------------------------------------------------------
function calculImpots($marié, $enfants, $salaire, $limites, $coeffR, $coeffN) {
// $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
...
}
// --------------------------------------------------------------------------
function getTables($IMPOTS) {
// $IMPOTS: o nome do ficheiro que contém os dados das tabelas $limites, $coeffR, $coeffN
// O ficheiro $IMPOTS existe?
if (!file_exists($IMPOTS))
return array("Le fichier $IMPOTS n'existe pas","","","");
// leitura em bloco do ficheiro
$tables = file($IMPOTS);
if (!$tables)
return array("Erreur lors de l'exploitation du fichier $IMPOTS","","","");
// criação das 3 tabelas — parte-se do princípio de que as linhas estão sintaticamente corretas
$limites = explode(":", cutNewLineChar($tables[0]));
$coeffR = explode(":", cutNewLineChar($tables[1]));
$coeffN = explode(":", cutNewLineChar($tables[2]));
// fim
return array("", $limites, $coeffR, $coeffN);
}
?>
Comentários
- linha 11: a função getTables($IMPOTS) utiliza o ficheiro de texto $IMPOTS para extrair as três tabelas ($limites, $coeffR, $coeffN). O resultado obtido é ($erreur, $limites, $coeffR, $coeffN), em que $erreur é uma mensagem de erro, vazia caso não tenha ocorrido qualquer erro.
- linha 40: a função getTables($IMPOTS).
- linha 43: a função files_exists(nom_fichier) devolve o valor booleano true se o ficheiro nom_fichier existir; caso contrário, devolve o valor booleano false.
- linha 46: a função file(nom_fichier) lê todo o ficheiro de texto nom_fichier. Devolve um tabuleiro em que cada elemento corresponde a uma linha do ficheiro de texto. Como, neste caso, o ficheiro impots.txt tem três linhas de texto, a função irá devolver um tabuleiro com três elementos.
- linhas 50-52: utilização das três linhas recuperadas para criar os três tabuletos ($limites, $coeffR, $coeffN)
Os resultados obtidos são os mesmos que os da versão anterior.