# Module — Facturation & taxes

> Phase 3. Génération de factures conformes depuis les ventes POS, avec TVA et mentions légales sénégalaises.
> Liens : [README modules](README.md) · [Vision](../README.md) · [Architecture](../01-architecture.md) · [Modèle de données](../02-modele-de-donnees.md) · [Sécurité](../03-securite.md) · [API](../04-api.md) · [Décisions](../notes/decisions.md).

## 0. État d'implémentation — incrément 1 (2026-07-24) 🚧

**Facture PDF depuis une vente** livrée et testée.

| Livré ✅ | Cible non encore faite ⬜ |
|---|---|
| `Invoice` (append-only, **une par vente**, numéro `FAC-AAAA-000000`, snapshot HT/TVA/remise/TTC, nom client dénormalisé) | avoirs (facture d'avoir), factures proforma, annulation |
| `POST /api/v1/sales/{uuid}/invoice` (create-or-get **idempotent**) + `GET /api/v1/invoices/{uuid}/pdf` (**dompdf**) | numérotation par exercice fiscal dédiée, archivage/journal légal |
| **PDF** : entête boutique (nom, adresse, contact, **NINEA/RCCM**), lignes, sous-total HT, **TVA**, remise, total TTC, « arrêtée à la somme de… » | modèles multiples, logo, langue/mentions configurables |
| Gating : membre de la boutique (`COMPANY_VIEW`) | permission dédiée `invoice.*` (bascule RBAC phase 2) |
| PWA : bouton **« Facture PDF »** dans le détail de ticket (Journal) → génère et ouvre le PDF (blob authentifié) | envoi par e-mail/partage, aperçu intégré |
| Test `tests/Functional/Api/InvoiceApiTest.php` (émission idempotente, PDF `%PDF`, 404, auth) | tests avoirs |

**Note** : le premier rendu dompdf peut être lent (chargement des polices) → un premier appel
peut renvoyer 503 sur le serveur de dev, le rejeu réussit. Fichiers : `src/Entity/Invoice.php`,
`src/Controller/Api/V1/InvoiceController.php` (HTML + dompdf), `yima-shop/.../invoices.service.ts`,
`features/journal/`.

## 1. Objectif

Produire des **factures légales** à partir des ventes encaissées et gérer les **taux de TVA** applicables.

- **Manager / Employé (caisse)** : générer/imprimer une facture à la demande du client à partir d'une vente.
- **Manager / comptable** : consulter le registre des factures, exporter en PDF, préparer les déclarations de TVA.
- **Admin** : superviser la numérotation et les taux par boutique.

## 2. Périmètre

**In (v1)**
- Génération d'une `Invoice` **depuis une `Sale`** existante (1 vente → 0..1 facture).
- **Numérotation séquentielle par boutique et par exercice** (`Company` + année), sans trou, via service dédié.
- Calcul **HT / TVA / TTC** par ligne et total, plusieurs taux possibles dans une même facture.
- **Mentions légales sénégalaises** : raison sociale, **NINEA**, **RCCM**, adresse de la boutique, devise FCFA.
- **Export PDF** (téléchargement + réimpression). Facture **append-only** (ADR-006).

**Out (v1, évolutions)**
- Avoirs/factures d'annulation formels (s'appuient sur les avoirs POS, itération ultérieure).
- Facture électronique / télétransmission DGID, e-signature.
- Facturation récurrente / abonnements ; acomptes.

## 3. Entités

- **`Invoice`** (append-only, **pas de soft-delete**) : `id`, `uuid` (public), `company` (boutique), `sale` (1:1), `number` (chaîne finale, ex. `FAC-2026-000042`), `sequence` (entier), `fiscalYear`, `issuedAt`, `sellerNinea`, `sellerRccm`, `sellerAddress` (snapshot des mentions au moment de l'émission), `customerLabel?`, `customerNinea?`, `amountExclTax`, `amountTax`, `amountInclTax` (centimes XOF), `currency` = `XOF`, `status` (`issued`|`cancelled`), `pdfPath?`.
- **`InvoiceLine`** : `invoice`, `label`, `quantity`, `unitPriceExclTax`, `taxRate` (snapshot du taux %), `amountExclTax`, `amountTax`, `amountInclTax`.
- **`TaxRate`** (effaçable, soft-delete) : `company`, `code` (`TVA18`), `label`, `ratePercent` (ex. `18.00`), `appliesTo` (`product`|`category`|`default`), `isDefault`, `isActive`. Rattachable à un produit/catégorie du [catalogue](catalogue-produits.md).
- **`InvoiceSequence`** (registre technique) : `company`, `fiscalYear`, `lastNumber` — verrou d'unicité `(company, fiscalYear)` pour l'allocation atomique.

Toutes rattachées à `Company`. Montants en **centimes XOF** (ADR-004).

## 4. Permissions (RBAC)

| Code | Rôle type | Usage |
|---|---|---|
| `invoice.view` | Manager, Employé | Consulter/exporter les factures de sa boutique |
| `invoice.create` | Manager, Employé | Générer une facture depuis une vente |
| `tax.manage` | Manager, Admin | Créer/éditer les taux de TVA de la boutique |

Seeding idempotent via `RbacSeedCommand`. Gating `denyAccessUnlessGranted('<perm>', $company)` (contrôleur) / `is_granted('<perm>', company)` (Twig).

## 5. API (`/api/v1`)

| Méthode | Route | Corps / params | Permission |
|---|---|---|---|
| `POST` | `/api/v1/invoices` | `{ saleId, customer?: {label, ninea?} }`, en-tête `Idempotency-Key` | `invoice.create` |
| `GET` | `/api/v1/invoices` | `?page=&limit=&filter[fiscalYear]=&sort=-issuedAt` | `invoice.view` |
| `GET` | `/api/v1/invoices/{uuid}` | — | `invoice.view` |
| `GET` | `/api/v1/invoices/{uuid}/pdf` | Réponse `application/pdf` | `invoice.view` |
| `GET` | `/api/v1/tax-rates` | `?filter[active]=1` | `invoice.view` |
| `POST` | `/api/v1/tax-rates` | `{ code, label, ratePercent, appliesTo }` | `tax.manage` |

Enveloppe standard `ApiResponseFormatter` (`{code, success, message, data, meta}`) ; validation → 422 ; scoping `Company` implicite.

## 6. Écrans

**Web (Twig, espace manager)**
- **Registre des factures** : liste filtrable (exercice, période, statut), badges de statut, total TTC.
- **Détail facture** : mentions légales, lignes HT/TVA/TTC, bouton **Télécharger le PDF** / réimprimer.
- **Paramètres TVA** : CRUD des `TaxRate`, taux par défaut, association produit/catégorie.
- Bouton **« Générer la facture »** sur le détail d'une vente ([POS](pos-ventes.md)).

**PWA (Ionic)**
- Action **facturer** depuis le ticket de vente ; aperçu et partage/impression du PDF.

## 7. Règles métier

- Une facture ne peut être créée que depuis une **vente encaissée** ; **1 vente ↔ 1 facture** (2e appel idempotent renvoie la facture existante).
- **Numérotation** allouée par service dédié (`InvoiceNumberingService`) : transaction + verrou sur `InvoiceSequence(company, fiscalYear)`, incrément atomique, **aucun trou**, format `FAC-{année}-{séquence:6}`.
- L'**exercice fiscal** = année civile de `issuedAt` (paramétrable ultérieurement).
- Les **mentions légales** (NINEA, RCCM, adresse) sont **figées en snapshot** sur la facture à l'émission (indépendantes des modifications ultérieures de la boutique).
- Calcul : `amountTax = round(amountExclTax * ratePercent/100)` par ligne ; `TTC = HT + TVA` ; totaux = somme des lignes (cohérence recontrôlée). Devise **XOF** uniquement.
- **Append-only (ADR-006)** : une facture émise n'est jamais modifiée ni supprimée ; correction = **annulation** (`status = cancelled`) + réémission, avec traçabilité `FileLogger`.

## 8. Projection e-commerce

- Les factures POS ne sont **pas** exposées par défaut au site (données de gestion).
- **Optionnel** : si le site e-commerce émet ses propres commandes/factures, celles-ci remontent au centre (table `order` de la [projection](ecommerce-projection.md)) et sont **facturées côté central** via ce module, en réutilisant la même numérotation par boutique. Les `TaxRate` peuvent être projetés (lecture seule) pour l'affichage TTC sur le site.

## 9. Tests

- **Unitaires** : allocation de numéro (séquentialité, absence de trou, isolation par boutique/exercice), calcul HT/TVA/TTC multi-taux et arrondis (centimes), snapshot des mentions légales.
- **Fonctionnels** : `POST /invoices` (idempotence, vente inexistante → 404/422), export PDF (`Content-Type`), filtrage par exercice.
- **Accès** : `invoice.*` et `tax.manage` respectés ; isolation `Company` (une boutique ne voit pas les factures d'une autre) ; tentative de modification → refus (append-only).

## 10. Dépendances

- [POS / Ventes](pos-ventes.md) (source `Sale`), [Catalogue produits](catalogue-produits.md) (rattachement TVA), [Caisse & paiements](caisse-paiements.md).
- Socle : RBAC (`PermissionResolver`, `PermissionVoter`), `EntityManagerService`, `FileLogger`, `ApiResponseFormatter`.
- Générateur PDF (ex. dompdf/wkhtmltopdf) — à intégrer.

## 11. Notes / ADR liés

- **ADR-004** — Montants en centimes XOF.
- **ADR-006** — Écritures append-only (factures jamais supprimées ; annulation = compensation).
- **ADR-002** — RBAC par boutique (codes de permission).
- À trancher : gestion d'exercice à cheval sur l'année civile ; format d'avoir formel ; conformité DGID / facture électronique (hors v1).
