Comment sont calculés les chiffres de Statistique > Graphique

Détail exhaustif, chiffre par chiffre, graphique par graphique : la formule exacte, la période/le périmètre pris en compte, et l'endpoint qui la calcule. Toutes les statistiques de ce module lisent une seule et même table pivot : stocks_mouvement_officiel.

Sommaire

  1. D'où viennent les mouvements
  2. Onglet Général
  3. Onglet Carburant — Général
    1. Chiffre d'Affaires / Quantité Vendue
    2. Marge Brute et calcul du PMP
    3. Valeur Stock
    4. Les 4 graphiques
  4. Onglet Carburant — Évolution
  5. Onglet Carburant — Comparaison
  6. Onglet Lubrifiant & Service — Général
  7. Onglet Lubrifiant & Service — Évolution
  8. Onglet Lubrifiant & Service — Comparaison
  9. Le cas particulier de l'Adblue
  10. Conventions HT/TTC et limites connues
  11. Correctif appliqué le 27/07/2026

1. D'où viennent les mouvements

Chaque vente ou achat de carburant/produit/service finit par une ligne dans stocks_mouvement_officiel. Colonnes utilisées par tous les calculs de cette page : journee (date), id_produit, entre (quantité achetée), sortie (quantité vendue), prix (prix unitaire au moment du mouvement), tva, source.

OrigineQuand la ligne est crééesource
Ticket Point de Vente (ComptePv)À chaque produit/service ajouté sur un ticketTicket
Compte PS (ancien circuit point de service)À la clôture du compte, en un seul lot regroupant produits et services de toute la sessionPS
Vente directe ("Transfert Bon")À la clôture d'un Bon de Livraison Achat transformé en Bon clientAlimentationBL
Achat fournisseur (Bon de Livraison)À la clôture du Bon de LivraisonAlimentation

Toutes les lignes avec source='Verification' (écarts de stock constatés) sont exclues de tous les calculs Vente/Achat de cette page (elles ne représentent ni une vente ni un achat réel).

2. Onglet Général

Endpoint : /StatistiqueGraphique/evolution avec type_vente=tousstatistique_annee_optimisee('tous', année)

Chiffre affichéFormule exacte
Courbe Vente (par mois, année choisie) SUM(sortie * prix)aucun filtre type_vente (tous produits confondus), GROUP BY MONTH(journee), YEAR(journee) = année
Courbe Achat (par mois, année choisie) SUM(entre * prix)hors taxe (pas de ×(1+tva/100)), même filtre/regroupement

Comme ce total ne filtre par aucun type_vente, il est mathématiquement égal à (total Carburant hors Adblue) + (total Lubrifiant & Service avec Adblue) — voir §11 pour le détail du partage Carburant/Adblue.

3. Onglet Carburant — Général

Endpoint : /StatistiqueGraphique/statistique — filtres Du/Au/Fixateur/Taxe

Périmètre : uniquement stocks_produits.type_vente='carburant' AND tva≠20 (l'Adblue, seul produit carburant à tva=20, est exclu d'ici — voir §11). Le sélecteur Taxe choisit un diviseur appliqué à la toute fin de chaque calcul monétaire : diviseur = 1 si TTC, diviseur = 1,10 si HT (10% étant le taux de TVA réel du carburant).

3.1. Chiffre d'Affaires & Quantité Vendue

Pour chaque carburant, sur la période [Du, Au] :
montant_carburant = SUM(sortie × prix)  |  qte_carburant = SUM(sortie)

KPI Chiffre d'Affaires = Σ (montant_carburant) / diviseur, sur tous les carburants
KPI Quantité Vendue = Σ (qte_carburant), sur tous les carburants

3.2. Marge Brute et calcul du PMP (Prix Moyen Pondéré / CUMP)

La marge n'est pas un simple "vente − achat de la période" : elle valorise la quantité vendue à son coût moyen pondéré réel (CUMP), qui remonte jusqu'au stock initial de l'application si nécessaire. Calcul en 2 temps, par carburant :

Étape A — Valoriser le stock au début de la période (fonction initial())

  1. Quantité en stock à la date Fixateur = stock initial de l'application + Σ(entre − sortie) sur toutes les lignes avant le Fixateur (source ≠ Verification).
  2. CUMP à la date Fixateur = (valeur du stock initial saisi manuellement à l'application + Σ achats TTC avant le Fixateur) / (quantité initiale saisie + Σ quantités achetées avant le Fixateur). Achat TTC = entre × prix × (1+tva/100).
  3. Coût du stock au Fixateur = (quantité de l'étape 1) × (CUMP de l'étape 2).
  4. Achats entre le Fixateur et Du = Σ entre et Σ entre×prix×(1+tva/100) sur cet intervalle.
  5. CUMP à la date Du = (coût de l'étape 3 + achats TTC de l'étape 4) / (quantité de l'étape 1 + quantités achetées à l'étape 4).
  6. Quantité en stock à la date Du = stock initial de l'application + Σ(entre − sortie) sur toutes les lignes avant Du.
  7. Coût du stock initial de la période = (quantité de l'étape 6) × (CUMP de l'étape 5). C'est le résultat renvoyé par initial() : montant = ce coût, qte = la quantité de l'étape 6.

Étape B — PMP final et marge (fonction statistique())

  1. Achats pendant la période filtrée [Du, Au] = Σ entre et Σ entre×prix×(1+tva/100) sur cet intervalle (fonction achat_periode()).
  2. PMP final = (coût de l'étape A.7 + achats TTC de l'étape B.1) / (quantité de l'étape A.7 + quantités achetées à l'étape B.1). C'est ce PMP qui sert de référence pour la marge et pour le graphique PMP vs PMV (§3.4).
  3. Marge du carburant = montant_carburant (vente de la période, §3.1) − (qte_carburant × PMP final).
KPI Marge Brute = Σ (marge du carburant / diviseur), sur tous les carburants

À retenir : si le Fixateur est très ancien, le calcul remonte potentiellement à l'historique complet du produit (tous les achats jamais enregistrés avant cette date) pour obtenir un CUMP de départ fiable — un Fixateur mal choisi peut donc rendre le calcul plus lourd, mais n'affecte la marge que via le CUMP de départ, pas la vente de la période.

3.3. Valeur Stock

Pour chaque carburant :
en_stock = stock initial application + Σ(entre − sortie) sur toutes les lignes AVANT la date (Au + 1 jour)
valeur_carburant = (en_stock × PMP final de l'étape B.2) / diviseur

KPI Valeur Stock = Σ (valeur_carburant), sur tous les carburants

3.4. Les 4 graphiques

GraphiqueFormule
Chiffre d'Affaires par Carburant (barres) 1 barre par carburant = montant_carburant / diviseur (§3.1, avant sommation)
PMV vs PMP par Carburant (barres, 2 séries) PMV (Prix Moyen de Vente constaté) = montant_carburant / qte_carburant (÷ diviseur)
PMP (CUMP) = PMP final de l'étape B.2 (÷ diviseur)
Remplissage des Citernes (barres horizontales, %) % = ROUND(en_stock / volume_max × 100), où en_stock est valorisé à la date (Au + 1 jour) comme en §3.3, et volume_max = SUM(carburant_citerne.volume) pour les citernes de ce carburant
Répartition Encaissement (anneau, 5 catégories) Voir tableau détaillé ci-dessous — chaque catégorie = SUM(carburant_compte_bon.montant) sur les comptes carburant clôturés dans [Du, Au]
Catégorie d'encaissementFiltre exact sur carburant_compte_bon
Crédit Clientid_type = 1
Espèceid_type = 6
CMIid_type = 2, lié à une ligne carte_ticket dont le type de carte a id_fournisseur = 0 (carte bancaire classique)
TOMCARDid_type = 2, lié à une ligne carte_ticket dont le type de carte a id_fournisseur ≠ 0 (carte carburant fournisseur)
Chèqueid_type = 5, lié à une ligne coffre_piece avec id_mode = 1
LCNid_type = 5, lié à une ligne coffre_piece avec id_mode = 2
Chèque/LCN (affiché)Chèque + LCN additionnés
TotalSomme des 5 catégories

4. Onglet Carburant — Évolution

Endpoint : /StatistiqueGraphique/evolution avec type_vente=carburantstatistique_annee_optimisee('carburant', année)

Filtre unique : l'année (pas de Du/Au/Fixateur/Taxe ici). Une seule courbe à 2 axes (CA à gauche, Quantité à droite), un point par mois.

ChiffreFormule
Chiffre d'Affaires (par mois)SUM(sortie × prix), produits type_vente='carburant' AND tva≠20 (Adblue exclu), YEAR(journee)=année, GROUP BY MONTH(journee)
Quantité (par mois)SUM(sortie), mêmes filtres

Ces montants sont toujours TTC bruts (prix directement, sans aucun diviseur HT/TVA) — contrairement à l'onglet Général (§3) où le sélecteur Taxe s'applique. Pour l'année en cours, les mois futurs (et le mois en cours, toujours incomplet) sont automatiquement retirés du graphique.

5. Onglet Carburant — Comparaison

Endpoints : /StatistiqueGraphique/charte_comparaison (courbes + tableau Achat/Vente) et /StatistiqueGraphique/charte_encaissement (tableau Encaissement)

Filtres : une ou plusieurs années, et une plage Mois du → Mois au (appliquée à chaque année sélectionnée). Bouton Montants/Quantités = bascule d'affichage seulement (aucun recalcul serveur).

ÉlémentFormule
Courbes "Vente {année}" / "Achat {année}"Mêmes valeurs mensuelles que l'onglet Évolution (§4), simplement tronquées à la plage Mois du→Au et affichées pour chaque année choisie sur le même graphique
Tableau Achat / Vente — total par annéeΣ des mois de la plage Mois du→Au sélectionnée (Vente = Σ CA mensuel ; Achat = Σ Achat mensuel HT, voir §10)
Badge % d'évolution(valeur_année − valeur_année_précédente) / |valeur_année_précédente| × 100 — "précédente" = l'année juste avant dans la LISTE TRIÉE choisie (pas forcément l'année civile −1). À partir de la 3ᵉ année de la liste, un 2ᵉ badge compare aussi à la toute première année choisie
Tableau Encaissement — total par annéeMêmes 5 catégories que §3.4, mais encaissement_periode(année, mois_du, mois_au) : YEAR(journee)=année AND MONTH(journee) BETWEEN mois_du AND mois_au sur carburant_compte_bon

6. Onglet Lubrifiant & Service — Général

Endpoint : /StatistiqueGraphique/statistique_psStatistiques::par_produit('ps', Du, Au)

Périmètre : type_vente IN ('produit','service') OU (type_vente='carburant' AND tva=20, c.-à-d. l'Adblue — voir §11). Filtres : Du/Au seulement.

Le sélecteur Taxe (TTC/HT) visible sur cet onglet n'a aucun effet : le paramètre n'est jamais lu côté serveur pour ce calcul (contrairement à l'onglet Carburant, §3). Tous les chiffres ci-dessous sont donc toujours dans l'unité brute stockée en base (TTC).

Chiffre affichéFormule exacte
CA VenteΣ SUM(sortie × prix) sur tous les produits/services du périmètre, [Du, Au], toutes sources confondues (Ticket/PS/AlimentationBL)
CA AchatΣ SUM(entre × prix × (1+tva/100)) — uniquement source='Alimentation', et uniquement les lignes type_vente='produit' ou Adblue (un service n'a jamais d'achat, il n'a pas de prix d'achat)
MargeCA Vente − CA Achat — soustraction simple, pas de calcul PMP pondéré comme pour le Carburant (§3.2) : c'est un vrai delta vente/achat sur la période, pas une marge sur coût moyen
Quantité VendueΣ SUM(sortie) sur tout le périmètre
Chiffre d'Affaires par Produit/Service (barres)Les 10 produits/services au CA le plus élevé, chacun affiché individuellement ; tout le reste est regroupé dans une barre "Autres" (somme du CA et de la quantité restants)

7. Onglet Lubrifiant & Service — Évolution

Endpoint : /StatistiqueGraphique/evolution avec type_vente=psstatistique_annee_optimisee('ps', année)

Même principe que l'Évolution Carburant (§4), mais périmètre = type_vente IN ('produit','service') OU Adblue. Chiffre d'Affaires et Quantité par mois pour l'année choisie, mêmes formules SUM(sortie×prix) / SUM(sortie).

8. Onglet Lubrifiant & Service — Comparaison

Endpoint : /StatistiqueGraphique/charte_comparaison avec type_vente=ps

Identique au Carburant (§5) pour les courbes multi-années et le tableau Achat/Vente + badges d'évolution. Pas de tableau Encaissement ici : le circuit d'encaissement (carburant_compte_bon) est propre au module Carburant et lié à une session de caisse, pas à une ligne produit — un même paiement peut mélanger carburant/lubrifiant/service, donc une ventilation par mode de paiement au niveau produit n'aurait pas de sens.

9. Le cas particulier de l'Adblue

L'Adblue est vendu au pistolet, comme du carburant (même circuit index/citerne), mais c'est économiquement un produit soumis à la TVA normale de 20% — alors que le vrai carburant (gasoil, SP95...) est à 10%. En base, stocks_produits a une ligne type_vente='carburant' pour l'Adblue mais avec tva=20, contre tva=10 pour les carburants réels.

Règle appliquée partout dans ce module : toute ligne type_vente='carburant' AND tva=20 est traitée comme un "produit vendu par index" — exclue du total Carburant (§3, §4, §5) et incluse dans le total Lubrifiant & Service (§6, §7, §8), donc aussi dans le Général (§2, somme des deux). Règle générique basée sur le taux de TVA (pas sur le nom du produit) : un futur produit configuré de la même façon serait automatiquement classé pareil, sans modification de code.

10. Conventions HT/TTC et limites connues

Onglet / sous-ongletLe sélecteur Taxe a-t-il un effet ?Convention Achat
GénéralPas de sélecteurHT (entre×prix, sans TVA)
Carburant → GénéralOui (diviseur 1 ou 1,10 sur CA/Marge/Valeur Stock/PMV/PMP)TTC (entre×prix×(1+tva/100))
Carburant → Évolution / ComparaisonPas de sélecteurHT (entre×prix, sans TVA)
Lubrifiant & Service → GénéralSélecteur présent mais inactif (voir §6)TTC (entre×prix×(1+tva/100))
Lubrifiant & Service → Évolution / ComparaisonPas de sélecteurHT (entre×prix, sans TVA)

Ces conventions différentes entre onglets sont un comportement déjà en place (non modifié) — à garder en tête si vous comparez un montant "Achat" d'un onglet à un autre : ils ne sont pas toujours calculés sur la même base HT/TTC.

11. Correctif appliqué le 27/07/2026

Un bug a été trouvé et corrigé : la vente directe "Transfert Bon" (achat transformé directement en vente client, sans passer par un ticket ni une clôture de compte) créait bien son mouvement de stock, mais ne l'enregistrait jamais dans stocks_mouvement_officiel — ces ventes étaient donc invisibles dans toutes les statistiques ci-dessus. Corrigé pour les nouvelles clôtures, et rattrapé pour l'historique (migration de base de données rejouée sur toutes les bases).

Cette page est un complément à SKILL_MODULE_VENTE.md (journal détaillé des chantiers de développement) — elle explique le résultat final, pas l'historique des décisions. Fichiers sources de ces calculs : app/models/home/Statistiques.php et app/controllers/statistique/StatistiqueGraphiqueController.php.