20. تمرين التطبيق: الإصدار 5

سنقوم بتطوير ثلاثة تطبيقات:
- التطبيق 1 سيقوم بتهيئة قاعدة البيانات التي ستحل محل ملف [admindata.json] من الإصدار 4؛
- التطبيق 2 سيحسب الضرائب في الوضع الدفعي؛
- التطبيق 3 سيحسب الضرائب في الوضع التفاعلي؛
20.1. التطبيق 1: تهيئة قاعدة البيانات
سيكون للتطبيق 1 البنية التالية:

هذا تطور لهيكل الإصدار 4 (انظر قسم |الإصدار 4|): سيتم تخزين بيانات الضرائب في قاعدة بيانات بدلاً من ملف JSON. سيتم تحديث طبقة [DAO] لتنفيذ هذا التغيير.
20.1.1. ملف [admindata.json]

ملف [admindata.json] هو نفسه كما كان في الإصدار 4:
{
"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
}
سنستخدم مفاتيح هذا القاموس كأعمدة في قاعدة البيانات.
20.1.2. إنشاء قواعد البيانات
كما هو موضح في قسم |إنشاء قاعدة بيانات MySQL|، نقوم بإنشاء قاعدة بيانات MySQL باسم [dbimpots-2019] مملوكة للمستخدم [admimpots] بكلمة مرور [mdpimpots]. في [phpMyAdmin]، يبدو هذا كما يلي:

وبالمثل، كما هو موضح في القسم |إنشاء قاعدة بيانات PostgreSQL|، نقوم بإنشاء قاعدة بيانات PostgreSQL باسم [dbimpots-2019] يملكها المستخدم [admimpots] بكلمة مرور [mdpimpots]. وفي [pgAdmin]، يبدو الأمر كما يلي:

تم إنشاء قواعد البيانات، ولكنها لا تحتوي على أي جداول في الوقت الحالي. سيتم إنشاء هذه الجداول بواسطة ORM [sqlalchemy].
20.1.3. الكيانات التي تم تعيينها بواسطة [sqlalchemy]
سننشئ جدولين لتغليف البيانات من [admindata.json]:
الجدول [tbtranches] المحدد بواسطة [sqlalchemy] سيجمع البيانات من المصفوفات [limites, coeffr, coeffn] في قاموس [admindata.json]:
# la table des tranches de l'impôt
tranches_table = Table("tbtranches", metadata,
Column('id', Integer, primary_key=True),
Column('limite', Float, nullable=False),
Column('coeffr', Float, nullable=False),
Column('coeffn', Float, nullable=False)
)
يتم تعريف الجدول [tbconstantes] بواسطة [sqlalchemy]، وسيحتوي على الثوابت الموجودة في قاموس [admindata.json]:
# la table des constantes
constantes_table = Table("tbconstantes", metadata,
Column('id', Integer, primary_key=True),
Column('plafond_qf_demi_part', Float, nullable=False),
Column('plafond_revenus_celibataire_pour_reduction', Float, nullable=False),
Column('plafond_revenus_couple_pour_reduction', Float, nullable=False),
Column('valeur_reduc_demi_part', Float, nullable=False),
Column('plafond_decote_celibataire', Float, nullable=False),
Column('plafond_decote_couple', Float, nullable=False),
Column('plafond_impot_celibataire_pour_decote', Float, nullable=False),
Column('plafond_impot_couple_pour_decote', Float, nullable=False),
Column('abattement_dixpourcent_max', Float, nullable=False),
Column('abattement_dixpourcent_min', Float, nullable=False)
)
الكيانات التي سيتم ربطها بهذين الجدولين هي كما يلي:

يحتوي الكيان [Constants] على الثوابت من قاموس [admindata.json]:
- السطر 5: تمتد فئة [Constants] إلى فئة [BaseEntity]؛
- السطر 7: عبر تعيين [sqlalchemy]، ستتلقى فئة [Constante] الخاصية [_sa_instance_state]. نستبعدها من قاموس [asdict] الخاص بالكيان؛
- الأسطر 11–23: خصائص الكيان. لقد أعيد استخدام الأسماء المستخدمة في قاموس [admindata.json] لتسهيل كتابة الكود؛
يغلف الكيان [Tranche] صفًا من المصفوفات الثلاثة [limites، coeffr، coeffn] في قاموس [admindata.json]:
- السطر 5: تمتد فئة [Tranche] من فئة [BaseEntity]؛
- السطر 7: تم استبعاد الخاصية [_sa_instance_state] التي أضافها [sqlalchemy] من قاموس [asdict] الخاص بالكيان؛
- الأسطر 10-12: خصائص الفئة؛
سيكون التعيين بين كيانات [Constants, Slice] وجداول [constants, slices] كما يلي:

- يتم تعريف التعيينات في الأسطر 24–29. وقد حذفنا التعيينات بين خصائص الكيانات المعينة وجداول قاعدة البيانات. وهذا ممكن عندما تكون أسماء أعمدة الجدول هي نفسها أسماء الخصائص التي سيتم ربطها بها. ولهذا السبب، قمنا بتضمين أسماء خصائص الكيانات المعينة في الجداول. وهذا يجعل الكود أسهل في الكتابة والفهم؛
20.1.4. ملف تكوين [sqlalchemy]

لقد قمنا للتو بتفصيل جزء من تكوين [sqlalchemy]. فيما يلي ملف [config_database] الكامل:
- السطر 1: تتلقى الدالة [configure] قاموسًا كمعلمة، ويحدد مفتاح [dbms] فيه نظام إدارة قواعد البيانات (DBMS) المطلوب استخدامه: MySQL (mysql) أو PostgreSQL (pgres)؛
- الأسطر 6–12: يتم اختيار قاعدة البيانات المحددة في التكوين؛
- الأسطر 14–44: تعيينات الكيانات/الجداول. هذه التعيينات بسيطة لأنه لا توجد علاقة بين الجدولين [tranches] و [constantes]. فهما مستقلان. وبالتالي، لا توجد مفاتيح خارجية بينهما لتتم إدارتها؛
- الأسطر 46-51: إنشاء جلسة عمل التطبيق [session]؛
- الأسطر 53–58: يتم وضع المعلومات ذات الصلة في قاموس التكوين، الذي يتم إرجاعه بعد ذلك؛
20.1.5. طبقة [dao]
لنعد إلى بنية التطبيق 1 المراد بناؤه:

يجب أن تقرأ طبقة [dao] [1] ملف [admindata.json] [2] وتنقل محتوياته إلى إحدى قواعد البيانات [3، 4]؛

توفر طبقة [dao] الواجهة [1] ويتم تنفيذها بواسطة الفئة [2].
واجهة [InterfaceDao4TransferAdminData2Database] هي كما يلي:
- الأسطر 8–10: تحدد الواجهة طريقة واحدة فقط [transfer_admindata_in_database] بدون معلمات. وبما أن هذه الطريقة تتطلب معلمات (أي ملف؟، أي قاعدة بيانات؟)، فهذا يعني أن هذه المعلمات ستُمرر إلى مُنشئ الفئات التي تُنفذ هذه الواجهة؛
تنفذ الفئة [DaoTransferAdminDataFromJsonFile2Database] الواجهة [InterfaceDao4TransferAdminData2Database] على النحو التالي:
- السطر 13: الفئة [DaoTransferAdminDataFromJsonFile2Database] تنفذ الواجهة [InterfaceDao4TransferAdminData2Database]؛
- الأسطر 15–17: يأخذ منشئ الفئة قاموس التكوين كمعلمة. سيتم استخدام المفاتيح التالية:
- [admindataFilename] (السطر 27): اسم ملف JSON الذي يحتوي على بيانات إدارة الضرائب المراد نقلها إلى قاعدة البيانات؛
- [database] السطر 32: تكوين [sqlalchemy] للتطبيق؛
- الأسطر 34-37: حذف الجداول [constants] و [brackets] في حالة وجودها؛
- السطران 39-40: إعادة إنشاء الجدولين؛
- السطر 43: استرداد جلسة [sqlalchemy] من التكوين؛
- الأسطر 45–51: تُضاف المصفوفات [limits، coeffr، coeffn] من قاموس [admindata] إلى الجلسة. وللقيام بذلك، تُضاف مثيلات كيان [Tranche] إلى الجلسة؛
- الأسطر 52–64: تتم إضافة مثيل للكيان [Constantes] إلى الجلسة؛
- السطور 66-67: يتم التحقق من صحة الجلسة. إذا لم تكن بيانات الجلسة موجودة بعد في قاعدة البيانات، يتم إدراجها في هذه المرحلة؛
- الأسطر 68–70: معالجة الأخطاء؛
- الأسطر 71–74: يتم إغلاق الجلسة. وهذا ممكن لأن طبقة [dao] تُستخدم مرة واحدة فقط؛
20.1.6. تكوين التطبيق

يتم تكوين التطبيق بواسطة ثلاثة ملفات [1]:
- [config] هو ملف التكوين العام. وهو يقوم بتكوين التطبيق [main]. ويساعده في ذلك الملفان الآخران:
- [config_database]، الذي سبق أن تناولناه والذي يقوم بتكوين ORM [sqlalchemy]؛
- [config_layers]، الذي يقوم بتكوين طبقات التطبيق؛
ملف [config] هو كما يلي:
- الأسطر 8–36: إنشاء مسار Python للتطبيق؛
- الأسطر 38–43: إضافة مسار ملف [admindata.json] إلى التكوين؛
- الأسطر 45–48: تكوين [SQLAlchemy]؛
- الأسطر 50–53: إنشاء مثيلات لطبقات التطبيق؛
- السطر 56: إرجاع التكوين العام؛
ملف [config_layers] كما يلي:
- السطران 3-4: إنشاء مثيل لطبقة [dao]. رأينا أن منشئ فئة [DaoTransferAdminDataFromJsonFile2Database] يتوقع قاموس التكوين العام للتطبيق كمعلمة؛
- السطر 4: تتم إضافة الإشارة إلى طبقة [dao] إلى التكوين؛
- السطر 7: إرجاع التكوين؛
20.1.7. نص [main] الخاص بالتطبيق


النص البرمجي الرئيسي [main] هو كما يلي:
- الأسطر 1–10: ننتظر معلمة. نتحقق من وجودها وصحتها؛
- الأسطر 12–14: نقوم بتكوين التطبيق (عام، SQLAlchemy، طبقات) عن طريق تمرير نوع DBMS المختار كمعلمة؛
- السطور 19-20: سنحتاج إلى طبقة [dao]. نقوم باستردادها؛
- السطر 25: نقوم بالنقل إلى قاعدة البيانات. جميع المعلومات المطلوبة بواسطة طريقة [transfer_admindata_in_database] متوفرة في خصائص طبقة [dao] من السطر 20. ومن هناك ستستردها؛
بعد تشغيل البرنامج النصي مع قاعدة بيانات MySQL، فإنه يحتوي على العناصر التالية (phpMyAdmin):



يُظهر العمود [3] القيم التي عيّنها MySQL للمفتاح الأساسي [id]. يبدأ الترقيم من 1. تم التقاط لقطة الشاشة أعلاه بعد تشغيل البرنامج النصي عدة مرات.


مع قاعدة بيانات PostgreSQL، تكون النتائج كما يلي:

- انقر بزر الماوس الأيمن على [1]، ثم على [2-3]؛
- في [4]، تظهر بيانات الشريحة الضريبية بوضوح؛
نقوم بنفس الشيء بالنسبة لجدول الثوابت [tbconstantes]:



20.2. التطبيق 2: حساب الضريبة في الوضع الدفعي

20.2.1. البنية
استخدم تطبيق حساب الضريبة في الإصدار 4 البنية التالية:

تنفذ طبقة [dao] واجهة [InterfaceImpôtsDao]. قمنا بإنشاء فئة تنفذ هذه الواجهة:
- [TaxDaoWithAdminDataInJsonFile]، التي كانت تسترد بيانات الضرائب من ملف JSON. كان ذلك في الإصدار 3؛
سنقوم بتنفيذ واجهة [InterfaceImpôtsDao] باستخدام فئة جديدة [ImpotsDaoWithTaxAdminDataInDatabase] التي ستسترد بيانات إدارة الضرائب من قاعدة بيانات. ستقوم طبقة [dao]، كما في السابق، بكتابة النتائج في ملف JSON واسترداد بيانات دافعي الضرائب من ملف نصي. نحن نعلم أنه إذا واصلنا الالتزام بواجهة [InterfaceImpôtsDao]، فلن تكون هناك حاجة لتعديل طبقة [business].
وستكون البنية الجديدة على النحو التالي:

20.2.2. تكوين التطبيق

يظل ملف التكوين [config_database] كما كان في التطبيق 1. يتضمن ملف التكوين [config] عناصر جديدة:
# étape 2 ------
# on complète la configuration de l'application
config.update({
# chemins absolus des fichiers de données
"admindataFilename": f"{script_dir}/../../data/input/admindata.json",
"taxpayersFilename": f"{script_dir}/../../data/input/taxpayersdata.txt",
"errorsFilename": f"{script_dir}/../../data/output/errors.txt",
"resultsFilename": f"{script_dir}/../../data/output/résultats.json"
})
- الأسطر 6–8: المسارات المطلقة للملفات النصية التي يستخدمها التطبيق 2؛
يتطور تكوين الطبقات [config_layers] على النحو التالي:
- السطران 3-4: يتم الآن تنفيذ طبقة [dao] بواسطة فئة [TaxDaoWithAdminDataInDatabase]. هذه الفئة جديدة ولكنها تنفذ نفس واجهة [DaoInterface] الموجودة في الإصدار 4 من تمرين التطبيق؛
- السطران 7-8: يتم تنفيذ طبقة [business] بواسطة فئة [ImpôtsMétier]. هذه هي الفئة المستخدمة في الإصدار 4 من تمرين التطبيق؛
20.2.3. طبقة [DAO]
ستكون فئة التنفيذ [ImpotsDaoWithAdminDataInDatabase] للواجهة [InterfaceImpôtsDao] كما يلي:
ملاحظات
- السطر 11: ترث فئة [ImpotsDaoWithAdminDataInDatabase] من فئة [AbstractImpôtsDao] المعروضة في الإصدار 4. ونعلم أن هذه الأخيرة تنفذ واجهة [InterfaceDao] المعروضة في نفس الإصدار. إن التوافق مع هذه الواجهة هو ما يسمح لنا بإبقاء طبقة [business] دون تغيير؛
- السطر 13: يتلقى منشئ الفئة قاموس تكوين التطبيق كمعلمة؛
- السطر 20: يتم تهيئة الفئة الأم []. وهي تنفذ جزئيًا واجهة [InterfaceDao]:
- [get_taxpayers_data] يقرأ ملف [taxpayersdata.txt] الذي يحتوي على بيانات دافعي الضرائب؛
- تقوم [write_taxpayers_results] بكتابة النتائج إلى ملف JSON [results.json]؛
- [get_admindata] غير مُنفَّذ؛
- السطر 22: يتم تخزين التكوين الذي تم تمريره كمعلمات؛
- السطر 27: تنفيذ طريقة [get_admindata] للواجهة [InterfaceDao]:
- الأسطر 28-30: تسترد طريقة [get_admindata] البيانات من إدارة الضرائب إلى كائن من النوع [AdminData] وتخزن هذا الكائن في [self.__admindata]. إذا تم استدعاء طريقة [get_admindata] عدة مرات، لا يتم الاستعلام عن قاعدة البيانات عدة مرات. يتم الاستعلام عنها فقط في المرة الأولى. في الاستدعاءات اللاحقة، يتم إرجاع كائن [self.__admindata]؛
- الأسطر 36-37: استرجاع جلسة [sqlalchemy] التي تم إنشاؤها أثناء تكوين التطبيق بواسطة [config_database]؛
- السطر 40: نسترد شرائح الضرائب في قائمة؛
- السطر 43: نسترد الثوابت لحساب الضريبة؛
- السطر 46: نقوم بإنشاء مثيل لفئة [AdminData]. تذكر أنها مشتقة من [BaseEntity]؛
- الأسطر 48-54: نقوم بتهيئة المصفوفات [limites, coeffr, coeffn] لمثيل [AdminData]؛
- السطران 55-56: تهيئة الخصائص الأخرى لـ [AdminData] باستخدام ثوابت حساب الضرائب. حرصنا على إعطاء نفس الأسماء لخصائص فئتي [AdminData] و [Constantes]، مما يبسط الكود؛
- السطور 57-58: يتم تخزين مثيل [AdminData] في طبقة [dao] ليتم إرجاعه خلال الاستدعاءات اللاحقة لطريقة [get_admindata]؛
- السطر 60: يتم إرجاع القيمة المطلوبة بواسطة الكود المستدعي؛
- الأسطر 61-63: معالجة الأخطاء؛
- الأسطر 64-67: يتم الاستعلام عن قاعدة البيانات مرة واحدة فقط. لذلك يمكننا إغلاق جلسة [sqlalchemy]؛
20.2.4. اختبار طبقة [dao]
في الإصدار 4 من هذا التطبيق، أنشأنا فئة اختبار لطبقة [business]. وبشكل أكثر تحديدًا، اختبرت الفئة كل من طبقتي [business] و[DAO]. نحن نعيد استخدام هذا الاختبار للتحقق من أن طبقة [DAO] تعمل كما هو متوقع. ومع ذلك، تظل طبقة [business] دون تغيير.


اختبار [TestDaoMétier] هو كما يلي:
import unittest
class TestDaoMétier(unittest.TestCase):
def test_1(self) -> None:
from TaxPayer import TaxPayer
# {'marié': 'oui', 'enfants': 2, 'salaire': 55555,
# 'impôt': 2814, 'surcôte': 0, 'décôte': 0, 'réduction': 0, 'taux': 0.14}
taxpayer = TaxPayer().fromdict({"marié": "oui", "enfants": 2, "salaire": 55555})
métier.calculate_tax(taxpayer, admindata)
# vérification
self.assertAlmostEqual(taxpayer.impôt, 2815, delta=1)
self.assertEqual(taxpayer.décôte, 0)
self.assertEqual(taxpayer.réduction, 0)
self.assertAlmostEqual(taxpayer.taux, 0.14, delta=0.01)
self.assertEqual(taxpayer.surcôte, 0)
…
def test_11(self) -> None:
from TaxPayer import TaxPayer
# {'marié': 'oui', 'enfants': 3, 'salaire': 200000,
# 'impôt': 42842, 'surcôte': 17283, 'décôte': 0, 'réduction': 0, 'taux': 0.41}
taxpayer = TaxPayer().fromdict({'marié': 'oui', 'enfants': 3, 'salaire': 200000})
métier.calculate_tax(taxpayer, admindata)
# vérifications
self.assertAlmostEqual(taxpayer.impôt, 42842, 1)
self.assertEqual(taxpayer.décôte, 0)
self.assertEqual(taxpayer.réduction, 0)
self.assertAlmostEqual(taxpayer.taux, 0.41, delta=0.01)
self.assertAlmostEqual(taxpayer.surcôte, 17283, delta=1)
if __name__ == '__main__':
# on attend un paramètre mysql ou pgres
import sys
syntaxe = f"{sys.argv[0]} mysql / pgres"
erreur = len(sys.argv) != 2
if not erreur:
sgbd = sys.argv[1].lower()
erreur = sgbd != "mysql" and sgbd != "pgres"
if erreur:
print(f"syntaxe : {syntaxe}")
sys.exit()
# on configure l'application
import config
config = config.configure({'sgbd': sgbd})
# couche métier
métier = config['métier']
try:
# admindata
admindata = config['dao'].get_admindata()
except BaseException as ex:
# affichage
print((f"L'erreur suivante s'est produite : {ex}"))
# fin
sys.exit()
# on enève le paramètre reçu par le script
sys.argv.pop()
# on exécute les méthodes de test
print("tests en cours...")
unittest.main()
- لن نعيد النظر في الاختبارات الـ 11 الموصوفة في القسم |[business] layer test version 4|؛
- الأسطر 37–66: سنقوم بتشغيل البرنامج النصي للاختبار كتطبيق عادي بدلاً من UnitTest. السطر 66 هو ما سيطلق إطار عمل UnitTest. في الاختبارات السابقة، استخدمنا طريقة [setUp] لتكوين تنفيذ كل اختبار. كنا نكرر نفس التهيئة 11 مرة لأن دالة [setUp] تُنفَّذ قبل كل اختبار. هنا، نقوم بالتهيئة مرة واحدة. وهي تتألف من تعريف المتغيرات العالمية [business] في السطر 53 و[admindata] في السطر 56، والتي ستُستخدم بعد ذلك بواسطة طرق [TestDaoBusiness]، على سبيل المثال في السطر 12؛
- الأسطر 39-47: يتوقع نص الاختبار معلمة [mysql / pgres] تشير إلى ما إذا كان يتم استخدام قاعدة بيانات MySQL أو PostgreSQL؛
- السطور 50-51: يتم تكوين الاختبار؛
- السطر 53: يتم استرداد طبقة [business] من التكوين؛
- السطر 56: نقوم بنفس الشيء مع طبقة [dao]. ثم نسترد مثيل [admindata]، الذي يغلف البيانات اللازمة لحساب الضريبة؛
- أظهرت الاختبارات أن الدالة [unittest.main()] في السطر 66 لم تتجاهل المعلمة [mysql / pgres] التي تم تمريرها إلى البرنامج النصي، بل أعطتها معنىً مختلفًا. ويضمن السطر 63 أن هذه الدالة لم تعد تحتوي على أي معلمات؛
نقوم بإنشاء تكوينين للتنفيذ:


إذا قمنا بتشغيل أي من هذين التكوينين، فسنحصل على النتائج التالية:
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/impots/v05/tests/TestDaoMétier.py mysql
tests en cours...
...........
----------------------------------------------------------------------
Ran 11 tests in 0.001s
OK
Process finished with exit code 0
- السطران 5 و7: اجتازت جميع الاختبارات الـ11؛
لاحظ أن هذه الاختبارات تتحقق فقط من 11 حالة لحساب الضريبة. ومع ذلك، قد يكون نجاحها كافياً لمنحنا الثقة في طبقة [dao].
20.2.5. البرنامج النصي الرئيسي


النص البرمجي الرئيسي [main] هو نفسه الموجود في الإصدار 4:
ملاحظات
- الأسطر 1-10: نسترد المعلمة [mysql / pgres]، التي تحدد نظام إدارة قواعد البيانات (DBMS) المطلوب استخدامه؛
- الأسطر 12–14: يتم تكوين التطبيق؛
- السطور 16-17: يتم استيراد فئة [ImpôtsError]. نحتاجها في السطر 38؛
- الأسطر 19-21: نسترد المراجع إلى طبقات التطبيق؛
- السطر 25: نطلب بيانات إدارة الضرائب من طبقة [dao]. تحتاج طبقة [business] إلى هذه البيانات لحساب الضريبة؛
- السطر 27: نسترد بيانات دافعي الضرائب (المعرف، الحالة الاجتماعية، عدد الأبناء، الراتب) في قائمة؛
- السطران 29-30: إذا كانت هذه القائمة فارغة، يتم إصدار استثناء؛
- السطور 32-35: حساب الضريبة للبنود الموجودة في قائمة [taxpayers]؛
- السطر 37: كتابة النتائج في ملف JSON [results.json]؛
- الأسطر 38-40: معالجة أي أخطاء؛
لتشغيل البرنامج النصي، نقوم بإنشاء |تكوينات تنفيذ|:

النتائج التي تم الحصول عليها في ملف [results.json] هي تلك الموجودة في الإصدار 4.

20.3. التطبيق 3: حساب الضريبة في الوضع التفاعلي
نقدم الآن التطبيق الذي يسمح بحساب الضريبة التفاعلي. هذا هو نسخة من التطبيق 2 من الإصدار 4.


- يبدأ البرنامج النصي [main] حوار المستخدم باستخدام طريقة [ui.run] الخاصة بطبقة [ui]؛
- طبقة [ui]:
- تستخدم طبقة [dao] لاسترداد البيانات اللازمة لحساب الضريبة؛
- تطلب من المستخدم معلومات تتعلق بالمكلف الذي سيتم حساب الضريبة عليه؛
- تستخدم طبقة [business] لإجراء هذا الحساب؛
يقوم ملف [config_layers] بإنشاء مثيل لطبقة إضافية:
فئة [ImpôtsConsole]، الأسطر 11–12، هي نفسها الموجودة في |الإصدار 4|.
النص البرمجي الرئيسي [main] هو كما يلي:
- الأسطر 1-10: يتوقع البرنامج النصي معلمة [mysql / pgres] تحدد نظام إدارة قواعد البيانات (DBMS) المطلوب استخدامه؛
- الأسطر 12-14: يتم تكوين التطبيق؛
- السطور 19-20: يتم استرداد طبقة [ui] من التكوين؛
- السطر 25: يتم تنفيذها؛
النتائج مطابقة لتلك الخاصة بـ |الإصدار 4|. ولا يمكن أن يكون الأمر خلاف ذلك، حيث تم الحفاظ على جميع واجهات الإصدار 4 في الإصدار 5.