Comment je structure et teste les calculs URSSAF et d'impôt de MonSuiviCA
Taux versionnés, foyer fiscal, 199 tests et limites connues : comment le code de MonSuiviCA organise ses calculs URSSAF et d'impôt sur le revenu.
En bref
MonSuiviCA range ses calculs de cotisations URSSAF et d'impôt sur le revenu (IR) dans un seul module TypeScript. Son paquet compte 199 tests, dont 99 dans le fichier dédié à ce module. Le 1er novembre 2026, ils passent tous.
Tous les chiffres de cet article sont ceux du code au 1er novembre 2026. Ils décrivent un logiciel et ne constituent pas un conseil fiscal.
L'origine du produit est racontée dans l'article de lancement de MonSuiviCA.
Un seul module porte les nombres
Le fichier packages/shared/src/calculations.ts sert à la fois l'application et la vitrine. Trois constantes le structurent :
TAUX_VERSION: la version2026-02, la date d'effet etretrievedAt, date de la dernière vérification des taux (2026-08-06).TAUX: taux de métropole, abattements, seuils de TVA et plafonds du régime, soit 83 600 € en services et 203 100 € en vente.TAUX_ZONES: les taux par zone fiscale et par secteur.
Voici le contenu de TAUX_ZONES, hors meublé de tourisme. SSI désigne la Sécurité sociale des indépendants, CIPAV la caisse de retraite de certains libéraux, DROM les départements et régions d'outre-mer :
| Zone | Services | Vente | Libéral SSI | Libéral CIPAV |
|---|---|---|---|---|
| Métropole | 21,2 % | 12,3 % | 25,6 % | 23,2 % |
| DROM, moins de 2 ans | 3,6 % | 2,1 % | 4,3 % | 7,8 % |
| DROM, 2 à 3 ans | 10,6 % | 6,2 % | 12,8 % | 11,6 % |
| DROM, 4 ans et plus | 14,2 % | 8,2 % | 17,1 % | 15,5 % |
| Mayotte | 9,1 % | 5,3 % | 12 % | 16,6 % |
Cette centralisation répond à une panne réelle. Une page de la vitrine affichait un plafond de 77 700 € dans son texte et 83 600 € dans le résultat du calculateur placé dessous.
Depuis le 31 août 2026, les pages lisent leurs montants dans bareme.ts, une couche d'affichage au-dessus du module partagé. Un test échoue et nomme le fichier et la ligne si une ancienne valeur revient dans leur code.
Résoudre le bon taux
La fonction getEffectiveUrssafRates choisit les taux d'un compte dans cet ordre :
- Les taux personnalisés saisis dans les paramètres.
- Les taux issus de la zone, de la caisse et de l'ACRE (Aide à la création ou à la reprise d'entreprise).
null, qui fait retomber les calculs sur les taux de métropole.
L'ACRE se réduit à un multiplicateur daté. Il exprime la part du taux conservée, et le code le choisit d'après la date de début saisie :
export function getAcreMultiplier(refDate: Date = new Date()): number {
return refDate < new Date('2026-07-01') ? 0.50 : 0.75
}
Avec une date de début au 1er septembre 2026, le taux de services en métropole passe donc de 21,2 % à 15,9 %.
Ne compter que l'encaissé
Toute l'assiette passe par isEncaisse. Une facture au statut attente sort du calcul des cotisations et de l'IR.
La cotisation affichée vaut le taux multiplié par l'encaissé de la période déclarée, un mois ou un trimestre civil, sans lissage annuel. En secteur mixte, getRevenueRate applique le taux revenu par revenu : vente d'un côté, services de l'autre.
Calculer l'impôt au niveau du foyer
Le revenu imposable part du chiffre d'affaires, moins un abattement de 50 % en services, 71 % en vente et 34 % en libéral. Le barème s'applique ensuite tranche par tranche :
export function calculateIR(taxableIncome: number): number {
let total = 0
for (const b of IR_BRACKETS) {
if (taxableIncome <= b.from) break
total += (Math.min(taxableIncome, b.to) - b.from) * b.rate
}
return total
}
IR_BRACKETS contient cinq tranches, à 0, 11, 30, 41 et 45 %. Ce calcul isolé se trompe pour un foyer. Vingt mille euros de revenu imposable n'ont pas le même poids selon les parts fiscales et le salaire du conjoint.
J'ai donc écrit calculateIRFoyer. Elle restitue un surcoût : l'impôt du foyer avec l'activité, moins l'impôt du foyer sans elle. Pour 20 000 € de revenu imposable et 2 parts, le code donne :
- Sans autre revenu : 0 €.
- Avec 60 000 € d'autres revenus : 6 000 €, soit 10 207,98 € moins 4 207,98 €.
Un taux personnalisé garde la priorité, puis vient le foyer, puis le barème seul. Le plafonnement du quotient familial, la décote, les réductions et les crédits d'impôt restent hors du modèle. Le commentaire de la fonction le dit, et qualifie le résultat d'estimation indicative.
Trois niveaux de vérification
La CI exécute les tests du paquet partagé dans une étape dédiée. Ils vérifient trois choses :
- Des valeurs : chaque zone, et l'ACRE de part et d'autre du 1er juillet 2026.
- Des propriétés : le surcoût égale la différence des deux impôts, ne devient jamais négatif, et le foyer compte au moins 1 part.
- Un verrou :
bareme.test.tsfige les plafonds, seuils de TVA et taux contrôlés sur service-public.gouv.fr le 31 août 2026. Changer le barème oblige à modifier aussi le test.
Ce test de valeur reprend le cas de 20 000 € sur le barème seul :
it('taxes 11% between 11600 and 29579', () => {
// (20000 - 11600) * 0.11
expect(calculateIR(20000)).toBeCloseTo(924, 2)
})
Un script complète le dispositif au build de l'application web. check-taux-urssaf.js avertit quand retrievedAt dépasse 6 mois, ou dès qu'un 1er janvier ou un 1er juillet est franchi depuis cette date. Il n'arrête pas le build.
Ce que ces tests ne prouvent pas
Aucun audit comptable n'est prouvé au 1er novembre 2026. La liste de validation par un expert-comptable de docs/business/formules-comptables.md n'a aucune case cochée, et le document porte encore le statut de brouillon.
Le calculateur public affiche d'ailleurs sa limite sous son résultat : « Estimation indicative. Impôt calculé sur 1 part fiscale, sans déductions. »
Un problème qui ressemble à celui-là ? Raconte-le-moi, je te réponds sous 48 h.