4. تمرين تطبيقي – الإصداران 1 و 2
4.1. المشكلة

يسمح الجدول أعلاه بحساب الضريبة في الحالة المبسطة لمكلف لا يملك سوى راتبه ليصرح به. وكما تشير الملاحظة (1)، فإن الضريبة المحسوبة بهذه الطريقة هي الضريبة قبل تطبيق الآليات الثلاث التالية:
- تحديد سقف الحصة العائلية الذي ينطبق على الدخل المرتفع؛
- الخصم والتخفيض الضريبي اللذين ينطبقان على الدخل المنخفض؛
وبالتالي، يتضمن حساب الضريبة الخطوات التالية [http://impotsurlerevenu.org/comprendre-le-calcul-de-l-impot/1217-calcul-de-l-impot-2019.php]:

نقترح كتابة برنامج يسمح بحساب الضريبة المستحقة على المكلف في الحالة المبسطة التي يكون فيها المكلف لا يعلن سوى راتبه الوحيد:
4.1.1. حساب الضريبة الإجمالية
يمكن حساب الضريبة الإجمالية بالطريقة التالية:
نحسب أولاً عدد حصص المكلف:
- يُحسب لكل والد حصة واحدة؛
- يُضاف لكل من الطفلين الأولين نصف حصة؛
- يُحسب لكل طفل من الأطفال التاليين حصة واحدة:
وبالتالي، فإن عدد الحصص هو:
- nbParts=1+nbEnfants*0,5+(عدد_الأطفال-2)*0,5 إذا كان الموظف غير متزوج؛
- nbParts=2+nbEnfants*0,5+(nbEnfants-2)*0,5 إذا كان متزوجًا؛
حيث nbEnfants هو عدد أطفاله؛
- يُحسب الدخل الخاضع للضريبة R=0.9*S حيث S هو الراتب السنوي؛
- يُحسب الحصيلة العائلية QF=R/nbParts؛
- يُحسب الضريبة الإجمالية I بناءً على البيانات التالية (2019):
9964 | 0 | 0 |
27519 | 0.14 | 1394.96 |
73779 | 0.3 | 5798 |
156244 | 0.41 | 13913.69 |
0 | 0.45 | 20163.45 |
يحتوي كل سطر على 3 حقول: champ1، champ2، champ3. لحساب الضريبة I، نبحث عن السطر الأول الذي يكون فيه QF <= الحقل 1 ونأخذ قيم هذا السطر. على سبيل المثال، بالنسبة لموظف متزوج ولديه طفلان وراتب سنوي S يبلغ 50000 يورو:
الدخل الخاضع للضريبة: R=0,9*S=45000
عدد الحصص: nbParts=2+2*0,5=3
الحصة العائلية: QF=45000/3=15000
السطر الأول الذي يكون فيه QF <= الحقل 1 هو التالي:
وبالتالي، فإن الضريبة I تساوي 0.14*R – 1394,96*nbParts=[0,14*45000-1394,96*3]=2115. ويتم تقريب الضريبة إلى أقرب يورو أقل.
إذا كانت العلاقة QF<=champ1 صحيحة من السطر الأول، فإن الضريبة تكون صفرًا.
إذا كان QF بحيث لا تتحقق العلاقة QF<=champ1 أبدًا، فسيتم استخدام معاملات السطر الأخير. هنا:
مما يعطي الضريبة الإجمالية I=0.45*R – 20163,45*nbParts.
4.1.2. الحد الأقصى للمنحة العائلية

لمعرفة ما إذا كان الحد الأقصى لحصة الأسرة QF ينطبق، نعيد حساب الضريبة الإجمالية بدون احتساب الأطفال. وبالنسبة للموظف المتزوج الذي لديه طفلان وراتب سنوي S يبلغ 50000 يورو:
الدخل الخاضع للضريبة: R=0,9*S=45000
عدد الحصص: nbParts=2 (لم يعد يتم احتساب الأطفال)
الحصة العائلية: QF=45000/2=22500
السطر الأول الذي يكون فيه QF <= الحقل 1 هو التالي:
وبالتالي، فإن الضريبة I تساوي 0.14*R – 1394,96*nbParts=[0,14*45000-1394,96*2]=3510.
الربح الأقصى المرتبط بالأطفال: 1551 * 2 = 3102 يورو
الضريبة الدنيا: 3510 - 3102 = 408 يورو
الضريبة الإجمالية مع 3 حصص تم حسابها مسبقًا 2115 يورو أعلى من الضريبة الدنيا 408 يورو، لذا لا ينطبق سقف الضريبة العائلية هنا.
بشكل عام، الضريبة الإجمالية هي sup(ضريبة 1، ضريبة 2) حيث:
- [impôt1]: هو الضريبة الإجمالية المحسوبة مع الأطفال؛
- [impôt2]: هو الضريبة الإجمالية المحسوبة بدون احتساب الأطفال، مطروحًا منها الحد الأقصى للزيادة (وهو هنا 1551 يورو لكل نصف حصة) المرتبطة بالأطفال؛
4.1.3. حساب الخصم

بالنسبة للموظف المتزوج ولديه طفلان ويبلغ راتبه السنوي S 50000 يورو:
الضريبة الإجمالية (2115) الناتجة عن الخطوة السابقة أقل من 2627 يورو للزوجين (1595 يورو للعازب): وبالتالي يُطبق الخصم. ويُحسب على النحو التالي:
الخصم = الحد الأدنى (للزوجين = 1970/للعازب = 1196) - 0.75 * الضريبة الإجمالية
الخصم = 1970 - 0,75 × 2115 = 383,75، مقربًا إلى 384 يورو.
الضريبة الإجمالية الجديدة = 2115 - 384 = 1731 يورو
4.1.4. حساب التخفيض الضريبي

تحت حد معين، يتم تطبيق تخفيض بنسبة 20% على الضريبة الإجمالية الناتجة عن الحسابات السابقة. في عام 2019، كانت الحدود كما يلي:
- الأعزب: 21037 يورو؛
- الزوجان: 42074 يورو؛ (يبدو أن الرقم 37968 المستخدم في المثال أعلاه خاطئ)؛
يُضاف إلى هذا الحد القيمة التالية: 3797 * (عدد نصف الحصص التي يضيفها الأبناء).
وبالنسبة للموظف المتزوج ولديه طفلان ويبلغ راتبه السنوي S 50000 يورو:
- دخله الخاضع للضريبة (45000 يورو) أقل من الحد الأدنى (42074+2*3797)=49668 يورو؛
- وبالتالي يحق له الحصول على تخفيض بنسبة 20٪ من ضريبته: 1731 * 0,2= 346,2 يورو مقربًا إلى 347 يورو؛
- يصبح الضريبة الإجمالية للمكلف: 1731-347= 1384 يورو؛
4.1.5. حساب الضريبة الصافية
سينتهي حسابنا عند هذا الحد: سيكون الضريبة الصافية المستحقة الدفع 1384 يورو. في الواقع، يمكن للمكلف بالضريبة الاستفادة من تخفيضات أخرى، لا سيما للتبرعات المقدمة إلى المنظمات ذات المصلحة العامة أو العامة.
4.1.6. حالة ذوي الدخل المرتفع
المثال السابق ينطبق على غالبية حالات الموظفين. ومع ذلك، يختلف حساب الضريبة في حالة أصحاب الدخل المرتفع.
4.1.6.1. الحد الأقصى للتخفيض بنسبة 10% على الدخل السنوي
في معظم الحالات، يتم حساب الدخل الخاضع للضريبة بالصيغة التالية: R=0,9*S حيث S هو الراتب السنوي. ويُطلق على ذلك اسم التخفيض بنسبة 10٪. ويخضع هذا التخفيض لسقف أقصى. في عام 2019:
- لا يمكن أن يتجاوز 12502 يورو؛
- لا يمكن أن يقل عن 437 يورو؛
لنأخذ مثالاً لموظف غير متزوج وليس لديه أطفال ويبلغ راتبه السنوي 200000 يورو:
- يبلغ التخفيض بنسبة 10% ما مقداره 20000 يورو > 12502 يورو. وبالتالي، يتم تخفيضه إلى 12502 يورو؛
4.1.6.2. الحد الأقصى للحصة العائلية
لنأخذ حالة ينطبق فيها الحد الأقصى للمعامل الأسري المذكور في الفقرة المرتبطة. لنأخذ مثال زوجين لديهما ثلاثة أطفال ودخل سنوي قدره 100000 يورو. لنستعرض خطوات الحساب:
- الخصم بنسبة 10% يبلغ 10000 يورو < 12502 يورو. وبالتالي، فإن الدخل الخاضع للضريبة R يساوي 100000 - 10000 = 90000 يورو؛
- يمتلك الزوجان nbParts = 2 + 0,5*2 + 1 = 4 حصص؛
- وبالتالي فإن نصيب الأسرة هو QF = R/nbParts = 90000/4 = 22500 يورو؛
- ضريبه الإجمالي I1 مع أطفال يبلغ I1=0,14*90000-1394,96*4= 7020 يورو؛
- ضريبته الإجمالية I2 بدون أطفال:
- QF=90000/2=45000 يورو؛
- I2=0,3*90000-5798*2=15404 يورو؛
- تنص قاعدة الحد الأقصى للحصة العائلية على أن المكسب الناتج عن وجود أطفال لا يمكن أن يتجاوز (1551 × 4 نصف حصص) = 6204 يورو. لكن في هذه الحالة، يبلغ المكسب I2-I1 = 15404 - 7020 = 8384 يورو، وهو بذلك أعلى من 6204 يورو؛
- وبالتالي، يُعاد حساب الضريبة الإجمالية على النحو التالي: I3 = I2-6204 = 15404 - 6204 = 9200 يورو؛
لن يحصل هذا الزوجان على أي خصم أو تخفيض، وستبلغ ضريبتهما النهائية 9200 يورو.
4.1.7. الأرقام الرسمية
يعد حساب الضريبة عملية معقدة. وستُجرى الاختبارات طوال هذا المستند باستخدام الأمثلة التالية. والنتائج مستمدة من محاكي مصلحة الضرائب [https://www3.impots.gouv.fr/simulateur/calcul_impot/2019/simplifie/index.htm]:
المكلف | النتائج الرسمية | نتائج الخوارزمية الواردة في الوثيقة |
زوجان لديهما طفلان ودخل سنوي قدره 55555 يورو | الضريبة = 2815 يورو معدل الضريبة = 14% | الضريبة = 2814 يورو معدل الضريبة = 14% |
زوجان ولديهما طفلان ودخلهما السنوي 50000 يورو | الضريبة = 1385 يورو الخصم = 720 يورو التخفيض = 0 يورو معدل الضريبة = 14% | الضريبة = 1384 يورو الخصم = 384 يورو الخصم = 347 يورو معدل الضريبة = 14% |
زوجان لديهما 3 أطفال ودخل سنوي يبلغ 50000 يورو | الضريبة = 0 يورو الخصم = 384 يورو الخصم = 346 يورو معدل الضريبة = 14% | الضريبة = 0 يورو الخصم = 720 يورو الخصم = 0 يورو معدل الضريبة = 14% |
أعزب ولديه طفلان ودخله السنوي 100000 يورو | الضريبة = 19884 يورو الخصم = 0 يورو الخصم = 0 يورو معدل الضريبة = 41% | الضريبة = 19884 يورو الزيادة = 4480 يورو الخصم = 0 يورو الخصم = 0 يورو معدل الضريبة = 41٪ |
أعزب ولديه 3 أطفال ودخل سنوي قدره 100000 يورو | الضريبة = 16782 يورو الخصم = 0 يورو الخصم = 0 يورو معدل الضريبة = 41% | الضريبة = 16782 يورو الزيادة = 7176 يورو الخصم = 0 يورو الخصم = 0 يورو معدل الضريبة = 41٪ |
زوجان لديهما 3 أطفال ودخل سنوي قدره 100000 يورو | الضريبة = 9200 يورو الخصم = 0 يورو الخصم = 0 يورو معدل الضريبة = 30% | الضريبة = 9200 يورو الزيادة = 2180 يورو الخصم = 0 يورو الخصم = 0 يورو معدل الضريبة = 30% |
زوجان لديهما 5 أطفال ودخل سنوي قدره 100000 يورو | الضريبة = 4230 يورو الخصم = 0 يورو الخصم = 0 يورو معدل الضريبة = 14% | الضريبة = 4230 يورو الخصم = 0 يورو الخصم = 0 يورو معدل الضريبة = 14% |
أعزب بدون أطفال ودخل سنوي قدره 100000 يورو | الضريبة = 22986 يورو الخصم = 0 يورو الخصم = 0 يورو معدل الضريبة = 41% | الضريبة = 22986 يورو الضريبة الإضافية = 0 يورو الخصم = 0 يورو التخفيض = 0 يورو معدل الضريبة = 41% |
زوجان مع طفلين ودخل سنوي قدره 30000 يورو | الضريبة = 0 يورو الخصم = 0 يورو الخصم = 0 يورو معدل الضريبة = 0% | الضريبة = 0 يورو الخصم = 0 يورو التخفيض = 0 يورو معدل الضريبة = 0% |
أعزب بدون أطفال ودخل سنوي قدره 200000 يورو | الضريبة = 64211 يورو الخصم = 0 يورو الخصم = 0 يورو معدل الضريبة = 45% | الضريبة = 64210 يورو الزيادة = 7498 يورو الخصم = 0 يورو الخصم = 0 يورو معدل الضريبة = 45٪ |
زوجان لديهما 3 أطفال ودخل سنوي قدره 200000 يورو | الضريبة = 42843 يورو الخصم = 0 يورو الخصم = 0 يورو معدل الضريبة = 41% | الضريبة = 42842 يورو الزيادة = 17283 يورو الخصم = 0 يورو الخصم = 0 يورو معدل الضريبة = 41% |
فيما سبق، يُطلق مصطلح «الزيادة» على المبلغ الإضافي الذي يدفعه أصحاب الدخل المرتفع بسبب ظاهرتين:
- تحديد سقف للخصم بنسبة 10% على الدخل السنوي؛
- تحديد سقف للمعامل الأسري؛
لم يتم التحقق من هذا المؤشر لأن محاكي إدارة الضرائب لا يقدمه.
ونلاحظ أن الخوارزمية الواردة في الوثيقة تعطي ضريبة صحيحة في كل مرة، مع هامش خطأ يبلغ 1 يورو. وينشأ هامش الخطأ هذا عن عمليات التقريب. فجميع المبالغ المالية يتم تقريبها أحيانًا إلى اليورو الأعلى، وأحيانًا إلى اليورو الأدنى. ونظرًا لعدم معرفتي بالقواعد الرسمية، فقد تم تقريب المبالغ النقدية الواردة في خوارزمية الوثيقة:
- إلى اليورو الأعلى بالنسبة للخصومات والتخفيضات؛
- إلى اليورو الأدنى بالنسبة للزيادة والضريبة النهائية؛
فيما يلي، سيتم إجراء اختبارات للتحقق من صحة النتائج. وستُجرى هذه الاختبارات باستخدام الأمثلة الواردة في الجدول السابق مع هامش خطأ مقبول قدره 1 يورو.
4.2. هيكل شجرة البرامج النصية

4.3. الإصدار 1
4.3.1. الخوارزمية
نقدم برنامجًا أوليًّا حيث:
- يتم ترميز البيانات اللازمة لحساب الضريبة بشكل ثابت في الكود على شكل جداول وثوابت؛
- توجد بيانات دافعي الضرائب (الحالة الاجتماعية، عدد الأبناء، الراتب) في ملف نصي أول [taxpayersdata.txt]؛
- يتم تخزين نتائج حساب الضريبة (المتزوج، والأطفال، والراتب، والضريبة) في ملف نصي ثانٍ [resultats.txt]؛
النص البرمجي [version-01/main.php] هو كما يلي:
<?php
// أنواع صارمة لمعلمات الدوال
declare(strict_types=1);
// الثوابت العالمية
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);
// تعريف الثوابت المحلية
$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);
// قراءة البيانات
$data = fopen($DATA, "r");
if (!$data) {
print "Impossible d'ouvrir en lecture le fichier des données [$DATA]\n";
exit;
}
// فتح ملف النتائج
$résultats = fopen($RESULTATS, "w");
if (!$résultats) {
print "Impossible de créer le fichier des résultats [$RESULTATS]\n";
exit;
}
// معالجة السطر الحالي من ملف البيانات
while ($ligne = fgets($data, 100)) {
// إزالة علامة نهاية السطر إن وجدت
$ligne = cutNewLineChar($ligne);
// استرجاع الحقول الثلاثة «متزوج:أطفال:راتب» التي تشكل $ligne
list($marié, $enfants, $salaire) = explode(",", $ligne);
// يتم حساب الضريبة
$result = calculImpot($marié, (int) $enfants, (float) $salaire, $limites, $coeffR, $coeffN);
// يتم تسجيل النتيجة في ملف النتائج
$result = ["marié" => $marié, "enfants" => $enfants, "salaire" => $salaire] + $result;
fputs($résultats, \json_encode($result, JSON_UNESCAPED_UNICODE) . "\n");
// البيانات التالية
}
// يتم إغلاق الملفات
fclose($data);
fclose($résultats);
// النهاية
exit;
// --------------------------------------------------------------------------
function cutNewLinechar(string $ligne): string {
// يتم حذف علامة نهاية السطر من $ligne إن وجدت
$L = strlen($ligne); // طول السطر
while (substr($ligne, $L - 1, 1) === "\n" or substr($ligne, $L - 1, 1) === "\r") {
$ligne = substr($ligne, 0, $L - 1);
$L--;
}
// النهاية
return($ligne);
}
// حساب الضريبة
// --------------------------------------------------------------------------
function calculImpot(string $marié, int $enfants, float $salaire, array $limites, array $coeffR, array $coeffN): array {
…
// النتيجة
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 {
…
// النتيجة
return ["impôt" => $impôt, "surcôte" => $surcôte, "taux" => $coeffR[$i]];
}
// revenuImposable=الراتب_السنوي-الخصم
// الخصم له حد أدنى وحد أقصى
function getRevenuImposable(float $salaire): float {
…
// النتيجة
return floor($revenuImposable);
}
// يحسب الخصم المحتمل
function getDecote(string $marié, float $salaire, float $impots): float {
…
// النتيجة
return ceil($décôte);
}
// يحسب التخفيض المحتمل
function getRéduction(string $marié, float $salaire, int $enfants, float $impots): float {
/…
// النتيجة
return ceil($réduction);
}
ملف البيانات taxpayersdata.txt (متزوج، أطفال، راتب):
ملف résultats.txt (الحالة الاجتماعية، عدد الأبناء، الراتب، الضريبة، الزيادة، التخفيض، التخفيض الضريبي، معدل الضريبة) للنتائج التي تم الحصول عليها:
تعليقات
- السطر 4: يتم فرض الالتزام الصارم بنوع معلمات الدوال؛
- الأسطر 7-16: تعريف جميع الثوابت اللازمة لحساب الضريبة؛
- السطر 19: اسم الملف النصي الذي يحتوي على بيانات دافعي الضرائب (الحالة الاجتماعية، عدد الأبناء، الراتب)؛
- السطر 20: اسم الملف النصي الذي يحتوي على نتائج (المتزوج، والأطفال، والراتب، والضريبة) حساب الضريبة؛
- الأسطر 21-23: الجداول الثلاثة للبيانات التي تحدد الشرائح الضريبية المختلفة لحساب الضريبة؛
- السطور 26-30: فتح ملف بيانات دافعي الضرائب للقراءة باستخدام الوظيفة [r]. تُرجع الوظيفة [fopen] القيمة المنطقية FALSE إذا تعذر فتح الملف؛
- الأسطر 33-37: فتح ملف النتائج للقراءة باستخدام الدالة [w]؛
- الأسطر 40-51: حلقة قراءة الأسطر (المتزوج، الأبناء، الراتب) من ملف بيانات دافعي الضرائب؛
- السطر 40: تقوم الدالة [fgets] بقراءة 100 حرف وتتوقف عند أول علامة نهاية سطر تصادفها. هنا، لا يتجاوز طول أي سطر 100 حرف. إذا تم العثور على علامة نهاية سطر، يتم تضمينها في السلسلة التي يتم إرجاعها. عند الوصول إلى نهاية الملف، ترجع الدالة [fgets] القيمة FALSE؛
- السطر 42: تتم إزالة علامة نهاية السطر؛
- السطر 44: يتم استرداد مكونات السطر (الزوج، الأبناء، الراتب)؛
- السطر 46: يتم حساب الضريبة. يتم عرض النتيجة في شكل جدول ترابطي (السطر 76)؛
- السطر 48: تضاف المفاتيح [marié, enfants, salaire] إلى الجدول الذي تم استرداده سابقًا؛
- السطر 49: يتم تخزين النتيجة في ملف النتائج على شكل سلسلة jSON؛
- السطران 53-54: بمجرد الانتهاء من معالجة ملف بيانات دافعي الضرائب بالكامل، يتم إغلاق الملفات؛
- السطر 60: الدالة التي تحذف علامة نهاية السطر من السطر $ligne. علامة نهاية السطر هي السلسلة "\r\n" في أنظمة ويندوز، و"\n" في أنظمة يونكس. والنتيجة هي سلسلة الإدخال بدون علامة نهاية السطر.
- السطران 63-64: substr($chaîne,$début,$taille) هي السلسلة الفرعية من $chaîne التي تبدأ من الحرف $début وتحتوي على ما لا يزيد عن $taille حرفًا؛
وظيفة [calculImpot] هي كما يلي:
// الثوابت العامة
define("PLAFOND_QF_DEMI_PART", 1551);
// حساب الضريبة
// --------------------------------------------------------------------------
function calculImpot(string $marié, int $enfants, float $salaire, array $limites, array $coeffR, array $coeffN): array {
// $marié: نعم، لا
// $enfants: عدد الأطفال
// $salaire: الراتب السنوي
// $limites, $coeffR, $coeffN: جداول البيانات التي تسمح بحساب الضريبة
//
// حساب الضريبة مع وجود أطفال
$result1 = calculImpot2($marié, $enfants, $salaire, $limites, $coeffR, $coeffN);
$impot1 = $result1["impôt"];
// حساب الضريبة بدون أطفال
if ($enfants != 0) {
$result2 = calculImpot2($marié, 0, $salaire, $limites, $coeffR, $coeffN);
$impot2 = $result2["impôt"];
// تطبيق الحد الأقصى للمعامل الأسري
if ($enfants < 3) {
// $PLAFOND_QF_DEMI_PART يورو للطفلين الأولين
$impot2 = $impot2 - $enfants * PLAFOND_QF_DEMI_PART;
} else {
// $PLAFOND_QF_DEMI_PART يورو للطفلين الأولين، ضعف هذا المبلغ للأطفال التاليين
$impot2 = $impot2 - 2 * PLAFOND_QF_DEMI_PART - ($enfants - 2) * 2 * PLAFOND_QF_DEMI_PART;
}
} else {
$impot2 = $impot1;
$result2 = $result1;
}
// يُحتسب الضريبة الأعلى مع المعدل والزيادة المرتبطة بها
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"];
}
// حساب أي خصم محتمل
$décôte = getDecote($marié, $salaire, $impot);
$impot -= $décôte;
// حساب أي تخفيض محتمل في الضرائب
$réduction = getRéduction($marié, $salaire, $enfants, $impot);
$impot -= $réduction;
// النتيجة
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 {
…
// النتيجة
return ["impôt" => $impôt, "surcôte" => $surcôte, "taux" => $coeffR[$i]];
}
// revenuImposable=الراتب_السنوي-الخصم
// الخصم بنسبة 10٪ له حد أدنى وحد أقصى
function getRevenuImposable(float $salaire): float {
…
}
// يحسب الخصم المحتمل
function getDecote(string $marié, float $salaire, float $impots): float {
…
// النتيجة
return ceil($décôte);
}
// يحسب التخفيض المحتمل
function getRéduction(string $marié, float $salaire, int $enfants, float $impots): float {
…
// النتيجة
return ceil($réduction);
}
تعليقات
- السطر 10: يتم حساب الضريبة الإجمالية مع احتساب الأبناء. نحصل على نتيجة بالصيغة ["impôt" => $impôt, "surcôte" => $surcôte, "taux" => $coeffR[$i]] مع:
- [‘impôt’]: الضريبة الإجمالية؛
- [‘surcôte’]: مبلغ الزيادة إن وجدت. وتكون هذه الزيادة موجودة عندما يتجاوز الخصم البالغ 10% الحد الأدنى البالغ 12502 يورو؛
- [‘taux’]: معدل الضريبة المطبق على المكلف؛
- السطر 11: الضريبة الإجمالية المستحقة الدفع [impot1]؛
- السطران 13-14: إذا كان لدى المكلف ضريبي طفل واحد على الأقل، يُعاد حساب الضريبة باستخدام نفس البيانات ولكن مع افتراض عدم وجود أطفال. هذا الحساب الثاني ضروري لمعرفة ما إذا كان التخفيض الناتج عن وجود أطفال (nbParts*coeffN) يتجاوز حدًا معينًا؛
- السطر 15: الضريبة الإجمالية المستحقة الدفع [impot2]؛
- السطور 16-23: بالنسبة للضريبة الإجمالية [impot2]، يتم الآن أخذ الأطفال في الاعتبار: كل نصف حصة يولدها الأطفال تسمح بتخفيض قدره [PLAFOND_QF_DEMI_PART] يورو؛
- السطور 25-26: الحالة التي لا يكون فيها للمكلف أطفال. في هذه الحالة، لا داعي لحساب [impot2]. فهو يساوي [impot1]؛
- السطور 29-37: تم حساب ضريبتين إجماليتين [impot1, impot2]. وتحتفظ مصلحة الضرائب بأعلى المبلغين. ونحصل على ضريبة إجمالية [impot]؛
- السطور 39-40: قد يخضع المبلغ الإجمالي [impot] لخصم؛
- السطران 42-43: قد يخضع المبلغ الإجمالي [impot] لتخفيض؛
- السطر 45: أصبح [impot] هو الضريبة الصافية المستحقة الدفع. يتم إرجاع النتائج؛
الدالة [calculImpot2] هي كما يلي:
// --------------------------------------------------------------------------
function calculImpot2(string $marié, int $enfants, float $salaire, array $limites, array $coeffR, array $coeffN): array {
// $marié: نعم، لا
// $enfants: عدد الأطفال
// $salaire: الراتب السنوي
// $limites, $coeffR, $coeffN: جداول البيانات التي تسمح بحساب الضريبة
//
// عدد الحصص
$marié = strtolower($marié);
if ($marié === "oui") {
$nbParts = $enfants / 2 + 2;
} else {
$nbParts = $enfants / 2 + 1;
}
// حصة واحدة لكل طفل ابتداءً من الطفل الثالث
if ($enfants >= 3) {
// نصف حصة إضافية لكل طفل ابتداءً من الطفل الثالث
$nbParts += 0.5 * ($enfants - 2);
}
// الدخل الخاضع للضريبة
$revenuImposable = getRevenuImposable($salaire);
// الزيادة
$surcôte = floor($revenuImposable - 0.9 * $salaire);
// بسبب مشاكل التقريب
if ($surcôte < 0) {
$surcôte = 0;
}
// الحصة العائلية
$quotient = $revenuImposable / $nbParts;
//في نهاية جدول الحدود لإنهاء الحلقة التالية
$limites[count($limites) - 1] = $quotient;
// حساب الضريبة
$i = 0;
while ($quotient > $limites[$i]) {
$i++;
}
// نظرًا لوضع $quotient في نهاية الجدول $limites، الحلقة السابقة
// لا يمكن أن تتجاوز حدود الجدول $limites
// الآن يمكننا حساب الضريبة
$impôt = floor($revenuImposable * $coeffR[$i] - $nbParts * $coeffN[$i]);
// النتيجة
return ["impôt" => $impôt, "surcôte" => $surcôte, "taux" => $coeffR[$i]];
}
// revenuImposable=الراتب_السنوي - الخصم
// للخصم حد أدنى وحد أقصى
function getRevenuImposable(float $salaire): float {
// النتيجة
return floor($revenuImposable);
}
تعليقات
- يتم هنا تطبيق حساب الضريبة وفقًا للجدول التصاعدي؛
- السطر 9: تعيد الدالة strtolower($chaîne) القيمة $chaîne بأحرف صغيرة؛
- الأسطر 10-19: حساب عدد حصص المكلف؛
- السطر 21: يتم حساب الدخل الخاضع للضريبة باستخدام دالة. فقد رأينا أن هذا المبلغ لا يساوي دائمًا 0.9*revenusAnnuels. فالخصم بنسبة 10% محدود فعليًا بمبلغ 12502 يورو؛
- السطر 23: حساب الزيادة المحتملة إذا كان الدخل الخاضع للضريبة أكبر من 0.9*revenusAnnuels؛
- الأسطر 25-27: تصحيح لظهور القيمة «[$surcôte=-1]» أحيانًا بسبب أخطاء التقريب؛
- السطر 29: الحصة العائلية؛
- الأسطر 30-36: يتيح هذا الحصيلة تحديد شريحة الضريبة الخاصة بالمكلف؛
- السطر 40: بمجرد تحديد شريحة الضريبة الخاصة بالمكلف، يمكن حساب ضريبته الإجمالية. الدالة floor($x) تُرجع القيمة الصحيحة الأقرب إلى الأسفل من [$x]؛
- السطر 42: يتم عرض المعلومات المحسوبة؛
الدالة [getRevenuImposable] هي كما يلي:
// الثوابت العامة
define("ABATTEMENT_DIXPOURCENT_MAX", 12502);
define("ABATTEMENT_DIXPOURCENT_MIN", 437);
// revenuImposable=الراتب_السنوي-الخصم
// الخصم له حد أدنى وحد أقصى
function getRevenuImposable(float $salaire): float {
// خصم بنسبة 10% من الراتب
$abattement = 0.1 * $salaire;
// لا يمكن أن يتجاوز هذا الخصم ABATTEMENT_DIXPOURCENT_MAX
if ($abattement > ABATTEMENT_DIXPOURCENT_MAX) {
$abattement = ABATTEMENT_DIXPOURCENT_MAX;
}
// لا يمكن أن يقل الخصم عن ABATTEMENT_DIXPOURCENT_MIN
if ($abattement < ABATTEMENT_DIXPOURCENT_MIN) {
$abattement = ABATTEMENT_DIXPOURCENT_MIN;
}
// الدخل الخاضع للضريبة
$revenuImposable = $salaire - $abattement;
// النتيجة
return floor($revenuImposable);
}
تعليقات
- السطر 5: الخصم العادي هو 10٪ من الراتب السنوي؛
- الأسطر 7-9: لا يمكن أن يتجاوز الخصم الحد الأقصى للخصم [ABATTEMENT_DIXPOURCENT_MAX]؛
- السطور 10-13: لا يمكن أن يقل الخصم عن الحد الأدنى للخصم [ABATTEMENT_DIXPOURCENT_MIN]؛
- السطر 15: حساب الدخل الخاضع للضريبة؛
الدالة [getDecote] هي كما يلي:
// الثوابت العامة
define("PLAFOND_DECOTE_CELIBATAIRE", 1196);
define("PLAFOND_DECOTE_COUPLE", 1970);
define("PLAFOND_IMPOT_COUPLE_POUR_DECOTE", 2627);
define("PLAFOND_IMPOT_CELIBATAIRE_POUR_DECOTE", 1595);
// يحسب أي خصم محتمل
function getDecote(string $marié, float $salaire, float $impots): float {
// في البداية، خصم صفري
$décôte = 0;
// الحد الأقصى لمبلغ الضريبة للحصول على الخصم
$plafondImpôtPourDécôte = $marié === "oui" ? PLAFOND_IMPOT_COUPLE_POUR_DECOTE : PLAFOND_IMPOT_CELIBATAIRE_POUR_DECOTE;
if ($impots < $plafondImpôtPourDécôte) {
// الحد الأقصى لمبلغ الخصم
$plafondDécôte = $marié === "oui" ? PLAFOND_DECOTE_COUPLE : PLAFOND_DECOTE_CELIBATAIRE;
// الخصم النظري
$décôte = $plafondDécôte - 0.75 * $impots;
// لا يمكن أن يتجاوز الخصم مبلغ الضريبة
if ($décôte > $impots) {
$décôte = $impots;
}
// لا يوجد خصم <0
if ($décôte < 0) {
$décôte = 0;
}
}
// النتيجة
return ceil($décôte);
}
تعليقات
- السطر 6: الحد الأقصى لمبلغ الضريبة الإجمالية للحصول على خصم. يختلف هذا المبلغ بين العزاب والأزواج؛
- السطر 7: ما إذا كان المكلف مستحقًا للخصم؛
- السطر 11: صيغة الخصم. [plafondDécôte] هو الحد الأقصى لمبلغ الخصم. ويتم حساب هذا الحد الأقصى في السطر 9. وهنا أيضًا يعتمد الأمر على حالة المكلف، سواء كان متزوجًا أو أعزبًا؛
- السطور 13-15: لا يمكن أن يتجاوز الخصم الضريبة الإجمالية المستحقة. وهذا هو الحال، على سبيل المثال، إذا كانت قيمة [impots] تساوي 0 في السطر 11؛
- الأسطر 17-19: لتجنب التقريب إلى -1؛
الدالة [getRéduction] هي كما يلي:
// الثوابت العامة
define("PLAFOND_REVENUS_CELIBATAIRE_POUR_REDUCTION", 21037);
define("PLAFOND_REVENUS_COUPLE_POUR_REDUCTION", 42074);
define("VALEUR_REDUC_DEMI_PART", 3797);
// يحسب التخفيض المحتمل
function getRéduction(string $marié, float $salaire, int $enfants, float $impots): float {
// الحد الأقصى للدخل للحصول على التخفيض بنسبة 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;
}
// الدخل الخاضع للضريبة
$revenuImposable = getRevenuImposable($salaire);
// التخفيض
$réduction = 0;
if ($revenuImposable < $plafondRevenuPourRéduction) {
// خصم بنسبة 20%
$réduction = 0.2 * $impots;
}
// النتيجة
return ceil($réduction);
}
تعليقات
- السطور 4-10: للحصول على تخفيض ضريبي، يجب أن يكون الدخل الخاضع للضريبة (السطر 10) أقل من الحد الأقصى المحسوب في السطور 4-8؛
- الأسطر 13-16: إذا استوفى المكلف الشروط، يحق له الحصول على تخفيض ضريبي بنسبة 20٪ (السطر 15)؛
4.3.2. النتائج
ملف البيانات taxpayersdata.txt (متزوج، أطفال، راتب):
ملف résultats.txt (متزوج، أطفال، راتب، ضريبة، زيادة، تخفيض، تخفيض، معدل الضريبة) للنتائج التي تم الحصول عليها:
النتائج التي تم الحصول عليها تتوافق مع تلك التي تم الحصول عليها باستخدام أداة المحاكاة الخاصة بإدارة الضرائب.
4.3.3. الخلاصة
إن خوارزمية حساب الضريبة، حتى في الحالات التي تعتبر بسيطة، معقدة. ولن نعود إليها مرة أخرى. ومع مرور الإصدارات، سيبقى جوهرها كما هو على الرغم من بعض التغييرات في العرض. ولن نعلق إلا على هذه التغييرات.
4.4. الإصدار 2
4.4.1. التعديلات
في الإصدار السابق، كانت البيانات اللازمة لحساب الضريبة مبرمجة بشكل ثابت على شكل ثوابت وجداول. هذه الطريقة غير مقبولة. في الإصدار الجديد، تم نقل هذه البيانات إلى ملف خارجي باسم jSON:

محتوى الملف [taxadmindata.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
}
الإصدار الجديد [version-02/main.php] هو كما يلي:
<?php
// الالتزام الصارم بأنواع المعلمات المعلنة للدوال
declare (strict_types=1);
// تعريف الثوابت
$TAXPAYERSDATA = "taxpayersdata.txt";
$RESULTATS = "resultats.txt";
$TAXADMINDATA = "taxadmindata.json";
// يتم استرداد محتوى ملف البيانات الضريبية
$fileContents = \file_get_contents($TAXADMINDATA);
$erreur = FALSE;
// خطأ؟
if (!$fileContents) {
// تدوين الخطأ
$erreur = TRUE;
$message = "Le fichier des données [$TAXADMINDATA] n'existe pas";
}
if (!$erreur) {
// استرداد الرمز jSON من ملف التكوين في مصفوفة مرتبطة
$taxAdminData = \json_decode($fileContents, true);
// خطأ؟
if (!$taxAdminData) {
// يتم تسجيل الخطأ
$erreur = TRUE;
$message = "Le fichier de données jSON [$TAXADMINDATA] n'a pu être exploité correctement";
}
}
// خطأ؟
if ($erreur) {
print "$message\n";
exit;
}
// فتح ملف النتائج
$résultats = fopen($RESULTATS, "w");
if (!$résultats) {
print "Impossible de créer le fichier des résultats [$RESULTATS]\n";
// الإخراج
exit;
}
// فتح ملف بيانات دافعي الضرائب
$taxpayersdata = fopen($TAXPAYERSDATA, "r");
if (!$taxpayersdata) {
print "Impossible d'ouvrir le fichier des contribuables [$TAXPAYERSDATA]\n";
// الإخراج
exit;
}
// استخدام السطر الحالي من ملف البيانات
while ($ligne = fgets($taxpayersdata, 100)) {
// إزالة علامة نهاية السطر إن وجدت
$ligne = cutNewLineChar($ligne);
// استرداد الحقول الثلاثة marié:enfants:salaire التي تشكل $ligne
list($marié, $enfants, $salaire) = explode(",", $ligne);
// يتم حساب الضريبة
$result = calculImpot($taxAdminData, $marié, (int) $enfants, (int) $salaire);
// يتم تسجيل النتيجة في ملف النتائج
$result = ["marié" => $marié, "enfants" => $enfants, "salaire" => $salaire] + $result;
fputs($résultats, \json_encode($result, JSON_UNESCAPED_UNICODE) . "\n");
// البيانات التالية
}
// يتم إغلاق الملفات
fclose($taxpayersdata);
fclose($résultats);
// النهاية
exit;
// --------------------------------------------------------------------------
function cutNewLinechar(string $ligne) {
…
// النهاية
return($ligne);
}
// حساب الضريبة
// --------------------------------------------------------------------------
function calculImpot(array $taxAdminData, string $marié, int $enfants, float $salaire) {
// $marié: نعم، لا
// $enfants: عدد الأطفال
// $salaire: الراتب السنوي
// $taxAdminData: بيانات مصلحة الضرائب
//
// حساب الضريبة مع الأطفال
$result1 = calculImpot2($taxAdminData, $marié, $enfants, $salaire);
$impot1 = $result1["impôt"];
// حساب الضريبة بدون أطفال
if ($enfants != 0) {
$result2 = calculImpot2($taxAdminData, $marié, 0, $salaire);
$impot2 = $result2["impôt"];
// تطبيق الحد الأقصى للمعامل الأسري
if ($enfants < 3) {
// $PLAFOND_QF_DEMI_PART يورو للطفلين الأولين
$impot2 = $impot2 - $enfants * $taxAdminData["PLAFOND_QF_DEMI_PART"];
} else {
// $PLAFOND_QF_DEMI_PART يورو لأول طفلين، ضعف ذلك للأطفال التاليين
$impot2 = $impot2 - 2 * $taxAdminData["PLAFOND_QF_DEMI_PART"] - ($enfants - 2) * 2 * $taxAdminData["PLAFOND_QF_DEMI_PART"];
}
} else {
$impot2 = $impot1;
$result2 = $result1;
}
// يُحتسب الضريبة الأعلى مع المعدل والزيادة المرتبطة بها
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"];
}
// حساب أي خصم محتمل
$décôte = getDecote($taxAdminData, $marié, $salaire, $impot);
$impot -= $décôte;
// حساب أي تخفيض محتمل في الضرائب
$réduction = getRéduction($taxAdminData, $marié, $salaire, $enfants, $impot);
$impot -= $réduction;
// النتيجة
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é: نعم، لا
…
// النتيجة
return ["impôt" => $impôt, "surcôte" => $surcôte, "taux" => $coeffR[$i]];
}
// revenuImposable=الراتب السنوي - الخصم
// الخصم له حد أدنى وحد أقصى
function getRevenuImposable(array $taxAdminData, float $salaire): float {
…
// النتيجة
return floor($revenuImposable);
}
// يحسب أي خصم محتمل
function getDecote(array $taxAdminData, string $marié, float $salaire, float $impots): float {
…
// النتيجة
return ceil($décôte);
}
// يحسب التخفيض المحتمل
function getRéduction(array $taxAdminData, string $marié, float $salaire, int $enfants, float $impots): float {
…
// النتيجة
return ceil($réduction);
}
تعليقات
- الأسطر 11-19: نحاول قراءة محتوى الملف jSON المسمى [TAXADMINDATA]؛
- الأسطر 21-30: إذا نجحنا في قراءة الملف jSON، يتم فك تشفير محتواه في الجدول الترابطي [$taxAdminData]؛
- الأسطر 32-36: إذا حدث خطأ في إحدى العمليتين السابقتين، يتم كتابة رسالة خطأ على وحدة التحكم ويتم إيقاف البرنامج؛
- الفرق مع الإصدار 01 هو أن بيانات مصلحة الضرائب (المصفوفات والثوابت) موجودة هنا في المصفوفة الترابطية [$taxAdminData]، بينما كانت في الإصدار 01 موجودة في مصفوفات وثوابت عامة. إن الطابع العام لهذه الثوابت هو ما يميز بين الإصدارين:
- في الإصدار 01، كانت الثوابت معروفة في جميع دوال [main.php]؛
- للحصول على نفس النتيجة في الإصدار 02، يجب تمرير الجدول الترابطي [$taxAdminData] كمعلمة إلى جميع الدوال (الأسطر 83، 129، 138، 145، 152)؛
- يجب أن تستخدم كل دالة في الإصدار 02 محتوى المصفوفة [$taxAdminData]؛
- الأسطر 83-126: في الدالة [calculerImpot]، حيث كان يتم استخدام الثوابت العالمية أو المصفوفات [limites, coeffR, coeffN]، يتم الآن استخدام محتوى المصفوفة [$taxAdminData] التي يتم تلقيها كمعلمة (الأسطر 99، 102)؛
- تمت إعادة كتابة جميع الوظائف الأخرى بالطريقة نفسها؛
النتائج التي تم الحصول عليها هي نفسها التي تم الحصول عليها في الإصدار السابق.
4.4.2. الخلاصة
تتميز النسخة 02 بمرونة أكبر بكثير من النسخة 01. في عام 2020، من المرجح أن تكون خوارزمية حساب الضريبة هي نفسها التي كانت سارية في عام 2019. ولن يتغير سوى شرائح الضريبة والثوابت الحسابية. وسيكفي عندئذٍ تحديث الملف [taxadmindata.json]. أما في الإصدار 01، فيجب الدخول إلى الكود لتعديل شرائح الضرائب وثوابت الحساب. لكن من المحتمل ألا يكون لدى الأشخاص المكلفين بتغيير قيم شرائح الضرائب وثوابت الحساب إمكانية الوصول إلى كود الخوارزمية.