Taux de change en comptabilité multidevises : une convention, huit garde-fous

admineci

admineci

Auteur

1893 mots

Une facture de 14 250 EUR comptabilisée comme 14 250 MAD. Pourquoi le sens d'un taux doit s'écrire, et les huit garde-fous qui l'imposent.

Une facture de 14 250 EUR. En comptabilité, une écriture de 14 250 MAD. Le montant est juste, l'unité ne l'est pas, et le chiffre d'affaires comptable se retrouve divisé par le taux de change — environ onze fois trop petit sur un déploiement marocain.

Nous avons rencontré ce défaut dans notre propre ERP, Cleo, et il nous a fallu trois versions pour le traiter correctement : une pour convertir, une pour découvrir que les taux déjà en base suivaient la convention inverse, une pour rouvrir le sujet proprement. Cet article raconte ce que nous en avons tiré, parce que le problème n'a rien de spécifique à notre produit : il attend toute application qui tient une comptabilité dans une devise et facture dans une autre.

Portée

  • S'adresse à qui conçoit, intègre ou audite une application de gestion multidevises — éditeur, intégrateur, direction financière.
  • Le contexte est celui d'une comptabilité tenue dans la devise du pays de déploiement, avec des documents commerciaux libellés en devise étrangère.
  • Les exemples sont en dirham marocain et en franc CFA, mais rien ici ne dépend du couple de devises.
  • Il ne s'agit pas d'un article sur le change lui-même, ni sur les écarts de conversion, qui relèvent de la doctrine comptable et non de la conception logicielle.

L'incident : un chiffre d'affaires divisé par onze

La première version du moteur d'écritures ne convertissait pas. Les montants du document passaient tels quels dans le journal, quelle que soit leur devise. Sur un tenant qui ne facture qu'en devise locale, cela ne se voit jamais. Dès qu'une facture en euro apparaît, le chiffre d'affaires comptable devient faux d'un facteur égal au taux, et le compte client ne se solde pas à l'encaissement.

Nous avons donc ajouté la conversion, en lisant le taux stocké sur le document. C'est là que la deuxième surprise est arrivée : plus de cent documents en production portaient un taux suivant la convention inverse — EUR à 0,0930 au lieu de 10,7272, XOF à 61,58 au lieu de 0,0164. Les utiliser n'aurait pas corrigé l'erreur, cela l'aurait aggravée en divisant au lieu de multiplier.

Ces valeurs venaient d'une reprise de données depuis un système antérieur, où la convention était l'autre. Personne ne l'avait écrite nulle part, donc personne ne l'avait vérifiée.

La convention s'écrit une fois, et une contrainte la tient

Un taux de change est un nombre sans unité apparente, et c'est exactement ce qui le rend dangereux. 10,7272 et 0,0930 sont tous deux des taux EUR/MAD valides ; seul le sens de lecture les distingue.

La règle que nous avons retenue, et qui figure désormais en tête du modèle de données :

Le taux exprime le nombre d'unités de devise de tenue pour une unité de devise étrangère.

Sur une installation marocaine, EUR = 10,7272 signifie qu'un euro vaut 10,7272 dirhams. La conséquence est simple et vérifiable partout dans le code : la conversion vers la devise de tenue est toujours une multiplication. Une division est un défaut, sans exception.

Cette convention ne suffit pas si elle reste une phrase dans une documentation. Nous l'avons doublée d'une contrainte en base : la devise de tenue porte obligatoirement un taux de 1. C'est un invariant trivial, et c'est précisément pour cela qu'il est utile — il échoue bruyamment au moment de la migration si les données ne le respectent pas, plutôt que silencieusement six mois plus tard sur un état financier.

Aucune valeur par défaut silencieuse

Le code d'origine résolvait le taux avec un repli implicite : à défaut de valeur, un. Cela paraît prudent. C'est en réalité la décision la plus destructrice de tout le mécanisme, parce qu'elle comptabilise une devise étrangère à sa valeur nominale et qu'elle ne produit aucun message.

La résolution en cascade que nous appliquons désormais s'arrête sur une exception explicite :

  1. devise de tenue, le taux vaut un ;
  2. taux daté le plus récent, antérieur ou égal à la date du document ;
  3. taux courant en cache ;
  4. sinon, refus, avec le nom de la devise et la date demandée.

Une écriture qui ne peut pas être convertie ne doit pas être créée. Un utilisateur bloqué par un message clair coûte cinq minutes ; une écriture fausse acceptée coûte un exercice.

Nuance à ne pas manquer : un taux égal à un sur une devise étrangère est parfaitement légitime — c'est le cas des parités fixes. Seule l'absence de taux constitue une anomalie, pas la valeur un elle-même.

Le taux appartient au document, figé à sa validation

Une écriture comptable doit rester justifiable par la pièce qui l'a produite, indéfiniment. Si le taux est relu dans une table au moment où l'on consulte l'écriture, alors toute correction ultérieure de cette table réécrit le passé.

Nous résolvons donc le taux à la date du document, au moment de la validation de la facture, et nous l'inscrivons sur le document. L'écriture est construite avec cette valeur figée, qui prime sur toute lecture ultérieure du référentiel.

Un détail d'ordonnancement s'est révélé nécessaire : l'invariant de la devise de tenue est évalué avant le taux figé. Sans cela, un document dont on change la devise pour la devise locale conserverait son ancien taux et produirait une écriture absurde.

Corollaire de conception que nous avions sous-estimé : l'encaissement doit reprendre le taux de sa facture, et non le taux du jour du paiement. Sinon le compte client ne se solde jamais exactement, et l'écart de change se trouve dilué dans les créances au lieu d'être traité comme tel.

Les connecteurs analysent, ils ne convertissent pas

Dès qu'on récupère les taux automatiquement, chaque source a ses habitudes. La Banque centrale européenne cote en euro et ne cote pas le franc CFA. La BCEAO publie en franc CFA. Bank Al-Maghrib cote en dirham. Une source de marché cote en dollar.

La tentation est de faire convertir chaque connecteur dans la devise de tenue. C'est cette dispersion qui produit les inversions : la logique de sens se retrouve écrite cinq fois, testée zéro fois, et une seule d'entre elles est fausse.

Nous avons donc posé une règle stricte : chaque connecteur renvoie ses cotations brutes en déclarant sa devise de base et son sens. La normalisation et la triangulation vers la devise de tenue sont assurées par une fonction unique, écrite une fois et testée une fois.

Deuxième règle, moins évidente : le registre des sources se résout par code de source, jamais par pays. Un déploiement marocain doit pouvoir choisir la BCE si son comptable le préfère. Le paramétrage initial propose une source selon le pays ; l'exécution ne lit que la configuration, jamais le pays.

Le contrôle canari : 655,957

La parité entre l'euro et le franc CFA est fixe : 655,957. Elle ne bouge pas, elle est publiée, et elle est vérifiable sans connaître le marché.

Nous l'utilisons comme témoin. Après normalisation d'un lot de taux récupérés, si cette valeur ne s'y retrouve pas, le lot entier est rejeté — pas la ligne fautive, le lot.

Ce contrôle a une propriété que nous n'avions pas anticipée en le concevant : il détecte simultanément deux erreurs de nature différente. Une inversion de sens, puisque l'inverse de 655,957 ne ressemble à rien de connu. Et une erreur d'unité de cotation, puisque Bank Al-Maghrib cote le franc CFA par cent et le dinar algérien par dix. La division par l'unité de cotation doit donc être systématique et jamais conditionnelle, et c'est le canari qui le rappelle quand on l'oublie.

Dernier point sur les parités fixes : elles s'appliquent en dernier et ne sont pas surchargeables. Une mise à jour automatique qui remplacerait 655,957 par une valeur de marché casserait la comptabilité d'un tenant de la zone UEMOA, silencieusement.

La précision est une décision comptable

Le champ d'origine stockait quatre décimales. Cela paraît généreux tant qu'on pense en euro contre dirham.

Un franc CFA vaut environ 0,01645361 dirham. Tronqué à quatre décimales, cela donne 0,0165, soit une erreur systématique de 0,2 % sur tous les montants convertis. Six décimales ne suffisent pas davantage dès qu'une devise faible est exprimée dans une devise de tenue forte.

Nous sommes passés à huit décimales sur le référentiel comme sur les documents. Le coût de stockage est nul ; le coût de l'erreur ne l'était pas.

Rendre l'inversion visible au moment de la saisie

Toutes les protections précédentes agissent sur des taux automatiques. Reste la saisie manuelle, qui reste nécessaire — une source ne publie pas tous les jours, et certains taux sont contractuels.

Deux choses ont réellement changé le taux d'erreur à la saisie, et aucune n'est technique.

D'abord, afficher la valeur réciproque sous chaque taux, et la recalculer à chaque frappe. Un utilisateur qui saisit 0,0930 voit s'afficher « soit 1 MAD = 10,75 EUR » et comprend immédiatement qu'il s'est trompé de sens. C'est le seul contrôle qui fonctionne sans que l'utilisateur connaisse la convention.

Ensuite, nommer le sens dans le libellé : « 1 EUR = ? MAD » plutôt que « Taux ». Et signaler tout écart supérieur à vingt pour cent avec le taux en vigueur, en nommant ce taux.

Un piège d'interface mérite d'être connu au passage : les composants de saisie numérique configurés pour l'anglais suppriment la virgule décimale française. Un utilisateur qui tape 0,0163 enregistre 163, sans le moindre avertissement. Le séparateur décimal et l'analyseur du champ doivent être configurés explicitement.

En cas d'échec, ne rien écrire

La tâche de récupération quotidienne peut échouer : source indisponible, format modifié, clé expirée. La réaction naturelle est de retomber sur une valeur.

Nous avons tranché l'inverse : en cas d'échec, rien n'est écrit. Un taux périmé vaut mieux qu'un taux faux, parce qu'un taux périmé finit par déclencher l'alerte de péremption tandis qu'un taux faux passe l'ensemble des contrôles.

Deux distinctions se sont révélées nécessaires. Une source qui ne publie pas un jour donné — week-end, jour férié — est une absence de publication, pas un échec, et ne doit pas alerter. Et une devise que la source principale ne couvre pas est demandée à la source de repli, puis nommée dans le journal si elle reste introuvable. Jamais inventée.

Ce que cet article ne traite pas

Les écarts de conversion et de change à la clôture, qui relèvent de la doctrine comptable de chaque référentiel et non de la conception logicielle. La couverture de change et les instruments qui s'y rattachent. Le rapprochement bancaire en devise, qui suppose de conserver le montant d'origine par ligne d'écriture — nous le faisons, cela mérite son propre traitement. Et le cas des comptabilités tenues dans deux devises simultanément, qui est une autre architecture.

La leçon la plus utile n'est pas dans la liste des garde-fous, elle est dans leur ordre d'apparition : chacun a été ajouté après un incident, et chacun aurait pu être écrit avant. Une convention posée noir sur blanc dès le premier jour, doublée d'une contrainte en base et d'une réciproque affichée à l'écran, aurait évité l'ensemble.

Nous éditons Cleo ERP et nous accompagnons sa mise en place, y compris les reprises de données depuis un système existant — c'est là que les conventions implicites se révèlent. Les audits de conception sur une application de gestion en place relèvent de notre conseil en architecture. Si vous n'en retenez qu'une chose : écrivez le sens de votre taux, et faites-le tenir par une contrainte.

Partager cet article

Twitter LinkedIn

Vous avez un projet similaire ?

Nos experts sont là pour vous accompagner dans vos projets cloud et infrastructure.

Articles similaires