# MAREA · Notice de vérification de l'intégrité des clôtures de caisse

Notice explicative en langue française, jointe au fichier d'archive des clôtures
de caisse (BOFiP BOI-TVA-DECLA-30-10-30 § 230 ; référentiel LNE, exigence 12).
Elle décrit le fichier, le mécanisme d'intégrité, l'algorithme de cryptographie
employé, et l'outil qui permet de tout revérifier **sans MAREA**, y compris
après que l'établissement a cessé d'utiliser le logiciel.

**Où obtenir l'outil et cette notice.** Les deux se téléchargent sans compte,
avec leur empreinte SHA-256, sur la page publique de l'éditeur de MAREA :

https://www.app-marea.com/verification-des-clotures

Cette page reste accessible après la fin de l'abonnement de l'établissement.
L'établissement les trouve aussi dans le Back Office de MAREA, « Vos
données », à côté de l'export des clôtures de caisse. Pour vérifier qu'une
copie est bien celle que la page sert, on compare son empreinte à celle que la
page affiche (`certutil -hashfile <fichier> SHA256` sous Windows,
`shasum -a 256 <fichier>` sous macOS, `sha256sum <fichier>` sous Linux).
L'outil n'est pas signé par une clé : cette empreinte prouve que la copie est
celle de la page, pas davantage.

## 1. Le fichier que vous avez reçu

C'est l'export « Clôtures de caisse » de MAREA (`caisse-clotures`), un fichier
texte au format ouvert CSV :

- encodage UTF-8, avec la marque d'ordre des octets (BOM, trois octets
  `EF BB BF`) en tête ;
- séparateur point-virgule ; les champs qui portent un point-virgule, un
  guillemet ou un saut de ligne sont entourés de guillemets, un guillemet dans
  le champ étant doublé ; fins de ligne CRLF ;
- une ligne d'en-tête, puis **une ligne par clôture de caisse**, avec 32
  colonnes : `id, numero, jour, fond_ouverture_eur, especes_entrees_eur,
  especes_sorties_eur, especes_attendues_eur, especes_comptees_eur, ecart_eur,
  constat_depasse, total_entrees_eur, total_sorties_eur, taxe_entrees_eur,
  taxe_sorties_eur, ca_ttc_eur, cumul_entrees_eur, cumul_sorties_eur,
  cumul_taxe_entrees_eur, cumul_taxe_sorties_eur, cumul_ca_ttc_eur,
  cumul_par_moyen_json, ventilation_json, ca_par_nature_json, suspens_json,
  empreinte_precedente, empreinte, note, cloture_par, cloture_le,
  ca_periode_du, ca_par_jour_json, canon` ;
- les montants sont en euros, deux décimales, **virgule** décimale (`318,00`) ;
  les colonnes `_json` portent du JSON ; `constat_depasse` vaut `oui` ou `non`.

Les lignes sont dans l'ordre technique de la base, pas dans celui des numéros :
l'outil les trie par numéro, l'ordre du fichier n'a aucune valeur.

Le fichier ne porte pas le nom de la société : il porte, dans chaque texte
canonique (champ 2, voir plus bas), l'**identifiant technique de
l'établissement** dans MAREA, sous l'empreinte. L'outil l'affiche en tête.
Le nom du fichier produit par MAREA porte le code court de l'établissement
(`<code>-export-caisse-clotures-<date>-<n>-lignes.csv`).

## 2. Le mécanisme d'intégrité : un chaînage d'empreintes SHA-256

À chaque clôture, MAREA compose un **texte canonique** (colonne `canon`) qui
porte tout ce que le Z de caisse imprime, puis calcule son **empreinte
SHA-256** (colonne `empreinte`, 64 caractères hexadécimaux) sur les octets
UTF-8 de ce texte. Le texte canonique de la clôture n° N porte, en dernier
champ, l'empreinte de la clôture n° N-1 (colonne `empreinte_precedente`) : les
clôtures forment une **chaîne**. La première clôture est chaînée à
**64 zéros**. Aucune clé secrète n'intervient : l'algorithme est
`empreinte = hex(SHA-256(UTF-8(canon)))`, celui d'OpenSSL via la bibliothèque
standard de Node.js. Ce que cela prouve : qu'aucune clôture n'a été modifiée
ni retirée du milieu de la chaîne depuis sa production, et que ce que les
colonnes lisibles disent est ce que le texte signé dit.

### Le texte canonique, version `MAREA-Z-2` : 30 champs séparés par `|`

| n° | champ | forme |
|---|---|---|
| 1 | version | toujours `MAREA-Z-2` |
| 2 | établissement | identifiant technique (uuid) |
| 3 | numéro de la clôture | entier, continu depuis 1 |
| 4 | jour clôturé | `AAAA-MM-JJ` |
| 5 | premier jour de la période du chiffre d'affaires | `AAAA-MM-JJ` (le lendemain de la clôture précédente) |
| 6 à 11 | fond d'ouverture, espèces entrées, espèces sorties, espèces attendues, espèces comptées, écart | montants, **point** décimal, deux décimales |
| 12 | marque du constat | `constat` (le comptage du tiroir était à jour) ou `perime` (un mouvement d'espèces l'a suivi) |
| 13 à 17 | total des entrées, total des sorties, taxe de séjour entrée, taxe de séjour sortie, chiffre d'affaires TTC de la période | montants |
| 18 à 22 | cumul perpétuel des entrées, des sorties, de la taxe entrée, de la taxe sortie, du chiffre d'affaires TTC | montants, **cumul de la n° N = cumul de la n° N-1 + total de la n° N** |
| 23 | cumul perpétuel par moyen de paiement | `moyen:entrees:sorties` joints par `;`, triés par moyen (vide si aucun) |
| 24 | ventilation du jour par moyen et catégorie | JSON (tableau) |
| 25 | chiffre d'affaires par nature | JSON (tableau) |
| 26 | chiffre d'affaires par jour de la période | JSON (tableau) |
| 27 | suspens (notes ouvertes, constat) | JSON (objet) |
| 28 | note de la personne qui a clôturé | texte, vide si aucune |
| 29 | qui a clôturé | texte |
| 30 | empreinte de la clôture précédente | 64 hexadécimaux, 64 zéros pour la n° 1 |

Les quatre JSON du canon sont écrits par la base de données (clés triées,
`89.00` avec ses deux décimales) ; les colonnes `_json` du CSV sont réécrites
par l'export (`89`). Les deux portent la **même valeur** : l'outil les compare
au contenu, jamais à l'octet, et lit les nombres en texte, en décimal exact.

## 3. L'outil : `verifier-clotures-marea.mjs`

Un seul fichier, à côté de cette notice, sans aucune dépendance à installer.
Il faut **Node.js** (gratuit, https://nodejs.org, version 18 ou plus ; il
s'installe hors ligne depuis son installateur). Puis, dans un terminal, depuis
le dossier où sont l'outil et le fichier :

```
node verifier-clotures-marea.mjs clotures.csv
```

L'outil ne lit **que** ce fichier : ni réseau, ni base de données, ni autre
fichier. Il imprime, en français, l'établissement dont le fichier parle, les
numéros lus, le nombre de contrôles joués, chaque DÉFAUT avec son numéro de
clôture et sa raison, l'empreinte de la dernière ligne, puis le verdict :

- **Sortie 0**, `VERDICT : INTÈGRE` : chaque empreinte est le SHA-256 de son
  texte canonique, la chaîne tient, la suite des numéros est continue, les
  colonnes disent ce que les textes signent, les cumuls suivent.
- **Sortie 1**, `VERDICT : ALTÉRÉ` : au moins un contrôle tombe, chacun est
  imprimé (`DÉFAUT R3 : n° 2 : la chaîne est rompue entre la n° 1 et la n° 2`).
- **Sortie 2**, `VERDICT : ILLISIBLE` : le fichier n'est pas l'export de MAREA
  (BOM absent, en-tête différent), ou il a été réécrit par un tableur. C'est
  aussi le verdict, nommé, d'un fichier fabriqué que l'outil ne saurait pas lire
  jusqu'au bout : il ne rend jamais une erreur sans verdict.

`--json` rend le même verdict en données, pour un traitement automatique.

### Les contrôles, en liste fermée

| règle | ce qu'elle vérifie |
|---|---|
| R0 | le BOM UTF-8, l'en-tête des 32 colonnes, 32 champs par ligne |
| R1 | pour chaque clôture, `SHA-256(UTF-8(canon))` égale la colonne `empreinte` |
| R2 | le canon a 30 champs, en version `MAREA-Z-2`, et toutes les lignes nomment le même établissement |
| R3 | la chaîne : la colonne `empreinte_precedente` de la n° N égale l'empreinte de la n° N-1 (64 zéros pour la première), et le canon signe la même |
| R4 | la **continuité** des numéros depuis 1, sans trou ni doublon (la même règle que le vérificateur interne de MAREA) |
| R5 | les 22 champs scalaires du canon égalent les colonnes lisibles (numéro, deux dates, 16 montants, marque, note, auteur), la virgule du CSV valant le point du canon ; chaque montant du canon a la forme que la base écrit (point décimal, deux décimales exactement) ; la version et l'établissement sont jugés par R2, l'empreinte précédente par R3 |
| R6 | les quatre JSON du canon égalent les colonnes JSON, au contenu ; un JSON se lit selon sa norme (RFC 8259) : un nombre à zéro de tête, un échappement inconnu, un caractère de contrôle dans une chaîne ou une clé écrite deux fois dans le même objet rendent la colonne illisible, donc le contrôle tombe ; un nombre n'est jamais confondu avec un texte ou un objet qui lui ressemble |
| R7 | le cumul par moyen du canon (champ 23, moyens triés, chacun une fois, montants à deux décimales) égale la colonne `cumul_par_moyen_json`, où chaque moyen porte exactement `entrees` et `sorties`, deux nombres |
| R8 | l'arithmétique du cumul perpétuel, refaite en centimes entiers : chaque cumul de la n° N vaut celui de la n° N-1 plus le total de la n° N (zéro plus le total pour la première), et le cumul par moyen suit le précédent plus les ventes du jour |
| R9 | le chiffre d'affaires TTC est la somme de son détail par jour et par nature ; les totaux et la taxe sont la somme des lignes de la ventilation |

### Ce que l'outil ne voit pas

- **Une clôture absente à la FIN du fichier** : la chaîne n'a pas de bout. Le
  fichier s'arrête proprement sur sa dernière ligne, et rien dedans ne dit
  qu'une autre a suivi. L'outil imprime l'empreinte de la dernière ligne : elle
  se compare au dernier Z de caisse, ou à l'écran « Clôtures » de MAREA.
- **Des montants faux mais cohérents et signés** : l'outil prouve que le
  fichier est celui que MAREA a produit et qu'il n'a pas bougé ; que les
  montants soient ceux de la journée réelle, seules la base et ses pièces le
  disent.
- **L'identifiant technique (`id`) et l'instant de clôture (`cloture_le`)** :
  ces deux colonnes ne sont pas sous l'empreinte, le canon `MAREA-Z-2` ne les
  porte pas ; le jour clôturé et le numéro, eux, le sont. Une réécriture de ces
  deux colonnes ne se voit pas.
- **Un retour chariot dans une note** : l'export le ramène à un saut de ligne
  dans toutes ses colonnes, la colonne `canon` comprise ; ce texte n'est alors
  plus celui qui a été signé, et l'outil tomberait (R1) sur un fichier que
  personne n'a touché. Aucune clôture de MAREA n'en porte (deux clôtures en base
  au 22 septembre 2026, aucune).
- **L'apostrophe que l'export pose en tête d'une note ou d'un auteur** qui
  commence par `=`, `+`, `@`, une tabulation, ou `-` quand ce n'est pas un
  nombre (pour qu'un tableur ne l'exécute pas) : R5 l'admet là, et là seulement ;
  devant tout autre texte, une apostrophe de tête est une altération. L'export
  épargne en outre un numéro de téléphone international qui commence par `+` ;
  l'outil ne rejoue pas cette exception, donc une apostrophe ajoutée devant un tel
  texte passerait.
- **Un auteur dont le nom porte `|`** : la note et l'auteur se confondraient
  au découpage ; l'empreinte et la chaîne resteraient jugées justes, seule la
  confrontation note/auteur (R5) tomberait, à tort, sur cette ligne.
- **Les clôtures de période (mois, année, grand total)** : elles ont leur propre
  fichier (l'export « Clôtures de période de caisse »), leur propre chaîne
  d'empreintes et leur propre texte signé. L'outil ne les lit pas : sur ce
  fichier, il répond ILLISIBLE. Chaque clôture de période porte l'empreinte du Z
  qui clôt la fin de sa période ; elle se compare, à la main, à la ligne de ce Z
  dans le fichier des clôtures journalières, que l'outil vérifie.

### Comment le fichier a été produit

Depuis le Back Office de MAREA, « Vos données », domaine « Clôtures de
caisse » ; chaque export laisse une trace (qui, quand, combien de lignes).
L'éditeur éprouve l'outil sur un fichier réel de l'établissement de
démonstration de MAREA (deux clôtures) et sur des copies altérées, nommées
par ce qu'elles cassent, pour le voir tomber. Ces fichiers d'essai restent
dans le code de l'éditeur, qui n'est pas public : la page publique ne remet
que l'outil et cette notice.
