# Module — Rapports & analytics

> Fiche de conception. Voir le [catalogue des modules](README.md), les [conventions](../notes/conventions.md) et les [décisions (ADR)](../notes/decisions.md).

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

Premier rapport **livré et testé** : résumé des ventes par période, alimentant le **tableau
de bord** de l'onglet Journal (PWA).

| Livré ✅ | Cible non encore faite ⬜ |
|---|---|
| `GET /api/v1/reports/sales/summary?period=today\|7d\|all` : nb ventes, CA TTC, HT, TVA, **ticket moyen**, **ventilation par moyen de paiement** | rapports Z par session, séries temporelles (graphes) |
| **Top produits** `GET /reports/products/top?period=&limit=` : meilleures ventes par quantité + CA (barres dans le tableau de bord) | classement par CA, par catégorie |
| **Export CSV** `GET /sales/export` (une ligne/vente, libellés FR, montants FCFA, BOM Excel) + **rapport PDF** `GET /sales/summary/pdf` (dompdf, entête boutique, KPIs, ventilation) | plage de dates personnalisée, export par produit |
| Écran PWA **Journal** : tableau de bord (période) + **boutons Export CSV / Rapport PDF** + **historique des ventes** paginé + **détail de ticket** (articles, totaux, paiements, caissier) | filtres avancés, graphiques |
| Gating : membre de la boutique (`CompanyVoter::VIEW`) | permission dédiée `report.view/export` (bascule RBAC phase 2) |
| Tests `tests/Functional/Api/ReportApiTest.php` (résumé + export CSV + PDF) | tests plages de dates |

Fichiers : `src/Controller/Api/V1/ReportController.php`, `SaleRepository::summaryByCompany()` /
`paymentTotalsByMethod()` / `findByCompanyForExport()`, `yima-shop/src/app/features/journal/`,
`core/services/reports.service.ts`.

## Objectif

Donner à la boutique une **vision agrégée** de son activité : ventes, caisse, stock — en **lecture seule**, à partir de la source de vérité centrale.

- **Manager** : consulte tous les rapports de sa boutique, exporte (CSV/PDF) pour la comptabilité/le pilotage.
- **Employé** : accès restreint (par défaut aucun ; peut recevoir `report.view` si le Manager le décide, ex. Z de sa session).

## Périmètre

**In (v1)**
- **Rapport de ventes** par période, par produit, par caissier.
- **Z de caisse** (rappel/consultation ; l'émission appartient à [Caisse & paiements](caisse-paiements.md)).
- **Valorisation du stock** (quantité × coût d'achat moyen) à une date.
- **Top produits** (quantités / chiffre d'affaires).
- **Exports CSV et PDF** des rapports ci-dessus.

**Out (v1)**
- Tableaux de bord temps réel, prévisions, cohortes.
- Rapports multi-boutiques consolidés (vue groupe) → phase ultérieure.
- Data warehouse / BI externe.

## Entités

**Aucune entité métier lourde.** Le module s'appuie sur des **requêtes d'agrégation** et des **vues en lecture** sur les données existantes (`Sale`, `SaleLine`, `Payment`, `CashSession`, `StockMovement`, `Product`).

- Repositories dédiés (`ReportRepository`) avec requêtes DQL/SQL agrégées, **scopées `Company`**.
- **Performance** :
  - S'appuyer sur les index existants : `Sale(company, createdAt)`, `SaleLine(product)`, `Payment(cashSession)`, `StockMovement(company, product, createdAt)`.
  - Prévoir, si volumétrie élevée, une table d'**agrégats journaliers** optionnelle `DailySalesAggregate` (`company`, `date`, `revenueCents`, `salesCount`, `itemsCount`) alimentée en asynchrone (Messenger) — **repli** seulement si les requêtes live dépassent la cible de latence.
  - Bornes de période **obligatoires** ; pagination des lignes de détail.
- Pas de soft-delete (rien de persistant à effacer) ; montants en **centimes XOF**.

## Permissions

| Code | Description | Rôles par défaut |
|---|---|---|
| `report.view` | Consulter les rapports | Manager |
| `report.export` | Exporter (CSV/PDF) | Manager |

Gating : `denyAccessUnlessGranted('report.view', $company)` / `is_granted('report.export', company)`. Scope `Company` **imposé serveur** (jamais de période/boutique dictée sans contrôle).

## API

Base `/api/v1`, enveloppe `ApiResponseFormatter`, lecture seule. Paramètres de période requis (`from`, `to` ISO 8601).

| Méthode | Route | Corps / query | Permission |
|---|---|---|---|
| GET | `/api/v1/reports/sales?from=&to=&groupBy=day\|product\|cashier` | — | `report.view` |
| GET | `/api/v1/reports/top-products?from=&to=&metric=qty\|revenue&limit=` | — | `report.view` |
| GET | `/api/v1/reports/stock-valuation?at=` | — | `report.view` |
| GET | `/api/v1/reports/z?cashSessionId=` | — | `report.view` |
| GET | `/api/v1/reports/sales/export?from=&to=&format=csv\|pdf&groupBy=` | — | `report.export` |
| GET | `/api/v1/reports/stock-valuation/export?at=&format=csv\|pdf` | — | `report.export` |

- Montants renvoyés en **centimes XOF**. Exports : `Content-Disposition: attachment`.
- Validation des bornes → 422 (période absente/incohérente).

## Écrans

**Web (Twig, espace Manager)**
- Page « Rapports » : sélecteur de période + regroupement (jour/produit/caissier), tableau + total.
- Vue « Top produits » (quantités / CA) avec limite configurable.
- Vue « Valorisation du stock » à une date.
- Rappel « Z de caisse » (lien vers le module caisse).
- Boutons **Exporter CSV / PDF** sur chaque rapport (soumis à `report.export`).

**PWA**
- Consultation légère (ventes du jour, top produits) ; exports non prioritaires (renvoi web). Lectures caché-ables côté service worker.

## Règles métier

- **Lecture seule** : aucun écrit ; les **agrégations sont calculées au centre** (source de vérité, [ADR-001](../notes/decisions.md)).
- Cohérence append-only ([ADR-006](../notes/decisions.md)) : les remboursements/avoirs apparaissent comme **écritures compensatoires** ; le net est calculé, jamais par suppression.
- Toute période est **bornée** et **scopée boutique** ; les totaux d'une boutique n'incluent jamais une autre.
- Valorisation stock = Σ(quantité en stock × coût d'achat moyen du produit) à la date demandée (coût issu du module [Achats / Fournisseurs](fournisseurs-achats.md)).
- Cohérence d'affichage : montants formatés depuis les centimes ; arrondis uniquement à la présentation.
- Les exports reflètent exactement le rapport affiché (mêmes filtres/bornes).

## Projection e-commerce

**Non répliqué.** Les rapports sont un usage **interne de gestion** ; ils lisent la base centrale et ne sont pas exposés à la base client ([ADR-001](../notes/decisions.md), [projection e-commerce](ecommerce-projection.md)).

## Tests

- **Unitaires** : calculs d'agrégats (CA, quantités, net après avoirs), valorisation stock, bornage des périodes.
- **Fonctionnels** : endpoints `/reports/*` (regroupements, top produits, valorisation) ; génération CSV/PDF (en-têtes, contenu) ; erreurs 422 sur période invalide.
- **Accès** : `report.view`/`report.export` refusées hors permission (403) ; **isolation boutique** (aucun chiffre inter-boutiques) ; un Employé sans `report.view` reçoit 403.
- **Performance** (non-régression) : jeu de données volumineux → latence sous cible ; vérifier l'usage des index (plan de requête).

## Dépendances

- Socle : RBAC/permissions, `EntityManagerService` (lecture), `FileLogger`, `ApiResponseFormatter`, scope `Company`.
- [POS / Ventes](pos-ventes.md) : `Sale`/`SaleLine` (source des ventes).
- [Caisse & paiements](caisse-paiements.md) : `CashSession`/Z de caisse.
- [Stock & inventaire](stock-inventaire.md) : quantités pour la valorisation.
- [Achats / Fournisseurs](fournisseurs-achats.md) : coût d'achat pour la valorisation/marge.
- (Optionnel) Messenger pour agrégats journaliers.

## Notes / ADR liés

- [ADR-001](../notes/decisions.md) — Central autoritaire (agrégations au centre).
- [ADR-004](../notes/decisions.md) — Montants en centimes XOF.
- [ADR-006](../notes/decisions.md) — Append-only (net via écritures compensatoires).
- Décision ouverte : table d'agrégats journaliers uniquement si la performance live l'exige.
- Voir aussi : [modèle de données](../02-modele-de-donnees.md), [sécurité](../03-securite.md), [API](../04-api.md), [README projet](../README.md).
