Aller au contenu principal

Guide des Calculs de Facturation

Ce document décrit les règles de calcul utilisées dans le système de facturation, incluant les calculs de TVA, remises, totaux, et les règles d'arrondi.

Date de création : 2024-12-19
Objectif : Documenter précisément les règles de calcul pour assurer la cohérence entre frontend et backend


Table des matières

  1. Vue d'ensemble
  2. Précision des Calculs
  3. Calcul des Totaux
  4. Gestion des Remises
  5. Calcul de la TVA
  6. Règles d'Arrondi
  7. Exemples de Calcul

Vue d'ensemble

Le système utilise une approche à double précision pour tous les calculs financiers :

  • Wrapper Money : Utilise decimal.js pour éviter les erreurs de précision des nombres flottants JavaScript
  • Calculs par ligne : Chaque ligne d'article est calculée indépendamment
  • Agrégation : Les totaux sont calculés par agrégation des lignes
  • Arrondi au centime : Tous les montants sont arrondis au centime près (2 décimales)

Précision des Calculs

Classe Money

La classe Money encapsule tous les calculs monétaires :

import { Money } from "./billing/domain/money";

// Création depuis un nombre
const amount = Money.fromNumber(100.5); // 100.50 EUR

// Création depuis des centimes (pour éviter les erreurs de précision)
const amount = Money.fromCents(10050); // 100.50 EUR

// Opérations arithmétiques
const total = amount1.add(amount2);
const difference = amount1.subtract(amount2);
const multiplied = amount.multiply(1.2); // 20% d'augmentation
const divided = amount.divide(2);

Avantages

  • Précision : Évite les erreurs de calcul dues aux nombres flottants (ex: 0.1 + 0.2 !== 0.3)
  • Arrondi cohérent : Tous les montants sont arrondis au centime
  • Type-safety : TypeScript garantit l'utilisation correcte

Calcul des Totaux

Flux de Calcul

  1. Pour chaque ligne d'article :

    • Calcul du sous-total HT avec remise (si applicable)
    • Calcul de la TVA sur le sous-total HT
    • Calcul du sous-total TTC
  2. Agrégation :

    • Total HT = somme des sous-totaux HT
    • Total TVA = somme des montants de TVA
    • Total TTC = somme des sous-totaux TTC

Service InvoiceTotalsService

Le service centralise tous les calculs :

import { InvoiceTotalsService } from "./billing/calculations/invoice-totals.service";

const totalsResult = totalsService.calculateTotals(items, "EUR");

// Résultat :
// {
// totals: {
// subtotal_ht: Money,
// total_tva: Money,
// total_ttc: Money,
// currency: "EUR"
// },
// taxBreakdown: [
// { rate: 20, base_ht: Money, amount_tva: Money }
// ]
// }

Gestion des Remises

Types de Remises

  1. Remise en pourcentage (discount_percentage)

    • Appliquée sur le prix unitaire HT
    • Exemple : discount_percentage: 10 → 10% de remise
  2. Remise fixe HT (discount_type: 'FIXED_WITHOUT_TAX')

    • Montant fixe en HT
    • Exemple : discount_amount: 50 → 50 EUR HT de remise
  3. Remise fixe TTC (discount_type: 'FIXED_WITH_TAX')

    • Montant fixe en TTC
    • Le montant HT est calculé en retirant la TVA

Calcul de la Remise

// Remise en pourcentage
const priceAfterDiscount = priceWithoutTax.multiply(
1 - discountPercentage / 100
);

// Remise fixe HT
const priceAfterDiscount = priceWithoutTax.subtract(discountAmount);

// Remise fixe TTC
const discountHT = discountAmount.divide(1 + vatPercentage / 100);
const priceAfterDiscount = priceWithoutTax.subtract(discountHT);

Calcul de la TVA

Taux de TVA

Les taux de TVA sont définis par ligne d'article :

  • TVA standard : 20% (France)
  • TVA réduite : 5.5% ou 10% (selon le type de bien/service)
  • TVA 0% : Exonération
  • TVA intracommunautaire : 0% (B2B UE, avec numéro TVA)

Calcul

// TVA = Sous-total HT × (Taux TVA / 100)
const vatAmount = subtotalHT.multiply(vatPercentage / 100);

// Total TTC = Sous-total HT + TVA
const totalTTC = subtotalHT.add(vatAmount);

Détail par Taux

Le système calcule automatiquement un détail des montants de TVA par taux :

// Exemple avec 2 taux différents :
// - 20% sur 100 EUR HT → 20 EUR TVA
// - 5.5% sur 50 EUR HT → 2.75 EUR TVA
// Total : 150 EUR HT, 22.75 EUR TVA, 172.75 EUR TTC

taxBreakdown: [
{ rate: 20, base_ht: 100.0, amount_tva: 20.0 },
{ rate: 5.5, base_ht: 50.0, amount_tva: 2.75 },
];

Règles d'Arrondi

Principe Général

Tous les montants sont arrondis au centime près (2 décimales).

Règle d'Arrondi

  • Arrondi bancaire : Arrondi à la valeur paire la plus proche en cas d'égalité (IEEE 754)
  • Précision : 2 décimales pour les montants, 2 décimales pour les pourcentages

Exemples

// Arrondi au centime
Money.fromNumber(100.125).round(); // 100.13 (arrondi vers le haut)
Money.fromNumber(100.124).round(); // 100.12 (arrondi vers le bas)
Money.fromNumber(100.125).round(); // 100.12 si arrondi bancaire (valeur paire)

Calcul par Ligne vs Global

Approche actuelle : Calcul par ligne puis agrégation

  • Avantage : Cohérence avec les lignes individuelles
  • Précision : Chaque ligne est précise
  • ⚠️ Note : De légères différences peuvent apparaître si on arrondit d'abord puis qu'on agrège

Exemples de Calcul

Exemple 1 : Facture Simple avec TVA 20%

Ligne 1 :

  • Prix unitaire HT : 100.00 EUR
  • Quantité : 2
  • TVA : 20%

Calcul :

Sous-total HT = 100.00 × 2 = 200.00 EUR
TVA = 200.00 × 20% = 40.00 EUR
Sous-total TTC = 200.00 + 40.00 = 240.00 EUR

Totaux :

  • Total HT : 200.00 EUR
  • Total TVA : 40.00 EUR
  • Total TTC : 240.00 EUR

Exemple 2 : Facture avec Remise en Pourcentage

Ligne 1 :

  • Prix unitaire HT : 100.00 EUR
  • Quantité : 1
  • Remise : 10%
  • TVA : 20%

Calcul :

Prix après remise = 100.00 × (1 - 10%) = 90.00 EUR
TVA = 90.00 × 20% = 18.00 EUR
Sous-total TTC = 90.00 + 18.00 = 108.00 EUR

Exemple 3 : Facture avec Taux TVA Multiples

Ligne 1 (TVA 20%) :

  • Prix unitaire HT : 100.00 EUR
  • Quantité : 1
  • Sous-total HT : 100.00 EUR
  • TVA : 20.00 EUR
  • Sous-total TTC : 120.00 EUR

Ligne 2 (TVA 5.5%) :

  • Prix unitaire HT : 50.00 EUR
  • Quantité : 1
  • Sous-total HT : 50.00 EUR
  • TVA : 2.75 EUR
  • Sous-total TTC : 52.75 EUR

Totaux :

  • Total HT : 150.00 EUR
  • Total TVA : 22.75 EUR
  • Total TTC : 172.75 EUR

Détail TVA :

TVA à 20% : 20.00 EUR (base 100.00 EUR)
TVA à 5.5% : 2.75 EUR (base 50.00 EUR)

Exemple 4 : Facture avec Remise Fixe TTC

Ligne 1 :

  • Prix unitaire HT : 120.00 EUR
  • Quantité : 1
  • Remise fixe TTC : 20.00 EUR
  • TVA : 20%

Calcul :

Prix TTC initial = 120.00 × 1.20 = 144.00 EUR
Prix TTC après remise = 144.00 - 20.00 = 124.00 EUR
Prix HT après remise = 124.00 / 1.20 = 103.33 EUR (arrondi)
TVA = 103.33 × 20% = 20.67 EUR
Vérification : 103.33 + 20.67 = 124.00 EUR ✓

Synchronisation Frontend/Backend

Les calculs doivent être identiques entre le frontend et le backend pour éviter les incohérences.

Frontend

Le frontend utilise la même logique dans frontend/src/utils/document-totals.utils.ts :

// Exemple de calcul frontend (doit correspondre au backend)
const calculateLineTotal = (item) => {
let priceHT = item.price_without_tax;

// Appliquer remise
if (item.discount_percentage > 0) {
priceHT = priceHT * (1 - item.discount_percentage / 100);
} else if (item.discount_amount) {
// ... logique remise fixe
}

// Calculer TVA
const vatAmount = priceHT * (item.vat_percentage / 100);

// Total TTC
const totalTTC = priceHT + vatAmount;

return { priceHT, vatAmount, totalTTC };
};

Validation

Lors de la création/modification d'une facture :

  1. Le frontend calcule les totaux
  2. Le backend recalcule et valide
  3. Si différence, le backend utilise ses calculs (source de vérité)

Bonnes Pratiques

  1. Toujours utiliser Money pour les calculs monétaires
  2. Ne jamais utiliser Number() directement pour les montants
  3. Arrondir au centime avant d'afficher ou stocker
  4. Vérifier la cohérence entre frontend et backend
  5. Documenter les exceptions (ex: remises spéciales)

Références

  • backend/src/billing/domain/money.ts - Classe Money
  • backend/src/billing/calculations/invoice-totals.service.ts - Service de calculs
  • frontend/src/utils/document-totals.utils.ts - Calculs frontend