Facturia est une application web Django de facturation mono-entreprise pour la zone UEMOA (devise FCFA / XOF). Une seule société (l'émetteur) gère ses clients, son catalogue, et émet des factures conformes avec calcul de TVA, PDF, tableau de bord et API REST.
- Backend : Django 5.2 (LTS), Django REST Framework.
- Base de données : SQLite en développement, PostgreSQL en production (via
DATABASE_URL). - PDF : WeasyPrint (rendu HTML/CSS → PDF).
- Frontend : Tailwind CSS (CLI standalone, CSS compilé committé) + Alpine.js (auto-hébergé). Pas de build Node requis.
- Configuration : variables d'environnement via
python-decouple.
config/ Projet Django : settings (pilotés par l'env), urls, wsgi/asgi
core/ Transverse : CompanySettings (émetteur singleton), dashboard,
page Paramètres, formatage monétaire (filtre fcfa + montant en lettres)
billing/ Domaine métier : Customer, Product, Invoice, InvoiceItem,
InvoiceSequence ; services (calcul, numérotation, finalisation),
selectors (KPIs), API DRF, génération PDF
templates/ base.html + pages core/billing + modèles PDF (billing/pdf/)
static/ CSS Tailwind compilé, Alpine.js
theme/ Sources Tailwind (input.css, config)
Sens des dépendances : billing → core (paramètres). Le tableau de bord de
core consomme billing.selectors (aucune importation des modèles billing
dans core.models).
core.CompanySettings— singleton (pk=1) : émetteur unique (raison sociale, adresse, identifiant fiscal NINEA/IFU, RCCM, logo, devise, taux de TVA par défaut, délai de paiement, format de numéro, mentions de bas de page, modèle de PDF choisi).Customer— nom, société, contacts, adresse, identifiant fiscal.Product— produit ou service, prix HT, taux de TVA, actif/inactif.Invoice— client, statut, dates, totaux dénormalisés (HT/TVA/TTC + ventilation par taux), et champs « snapshot » (identité émetteur + client figés à la finalisation).InvoiceItem— ligne (désignation, quantité, PU HT, taux de TVA) ; les valeurs sont figées sur la ligne (le lienproductn'est qu'une référence).InvoiceSequence— compteur de numérotation par année.
- Montants : tout en FCFA sans décimales. Arrondi
ROUND_HALF_UP,Decimaluniquement. Par ligne :HT = arrondi(qté × PU),TVA = arrondi(HT × taux/100). Totaux = sommes ; ventilation TVA par taux. - Numérotation : un brouillon n'a pas de numéro. À la finalisation, un numéro
séquentiel par année est attribué de façon atomique (
InvoiceSequence+select_for_update, avec repliIntegrityError/retry pour SQLite). Le numéro est immuable et la finalisation est transactionnelle. - Statuts :
draft → sent → paid(+cancelled).overdueest dérivé (émise, échue, non payée), jamais stocké. - Immuabilité : à la finalisation, l'identité de l'émetteur et du client est copiée sur la facture (snapshot) ; les lignes sont verrouillées. Une facture finalisée n'est plus modifiable (garde côté vue, sérialiseur et admin).
- Recalcul des totaux : centralisé (
recalculate_totals), déclenché après modification des lignes (vues, API, signaux).
Le rendu PDF repose sur un registre (billing/pdf_templates.py) et un
corps partagé (templates/billing/pdf/_invoice_body.html) contenant toute la
donnée légale une seule fois. Chaque modèle (ledger, classic, compact)
n'est qu'un habillage CSS qui inclut ce corps. Le modèle est choisi dans les
Paramètres. Ajouter un modèle = un fichier <clé>.html + une entrée dans le
registre.
- Tailwind compilé (
theme/input.css→static/css/app.css, committé). Police d'affichage Fraunces + Hanken Grotesk (chargées via Google Fonts). - Alpine.js (local) pour l'interactivité légère ; l'éditeur de lignes de facture calcule les totaux en direct côté client, le serveur restant la source de vérité.
Endpoints DRF pour clients, produits et factures (avec lignes imbriquées) +
actions finalize / status / duplicate / pdf. Authentification par session
(UI) et par token (intégrations).
Suite pytest couvrant le calcul des montants, la numérotation atomique, les
transitions de statut, les vues, l'API, la génération PDF et les KPIs du
dashboard. Lancez pytest -q.