Save France
Backend

Simulateur CSV vs code – alignement des calculs

Référence pour reproduire les calculs du fichier Simulateur (export CSV) dans le service CalculateOfferFromRequestService.

Simulateur CSV vs code – alignement des calculs

Référence pour reproduire les calculs du fichier Simulateur (export CSV) dans le service CalculateOfferFromRequestService.

Données du CSV (cas Particulier, 1 PAC Air/Eau, zone Gradignan)

DonnéeValeur CSV (€)En centimes (code)
Déplacement HT35,003500
Déplacement TTC38,503850
Tarif MO / h73,887388
Heures MO2-
Coef MO (équipement)1,001.0
Total MO HT147,7614776
Total MO TTC155,8915589
Fournitures HT31,293129
Fournitures TTC33,013301

Formules (colonnes) : Entretien Ponctuel, ECO, ESSENTIEL, CONFORT, SERENITE.

FormuleDurée (mois)VisitesCoef majorationRemise fidélité
Entretien Ponctuel-11,150%
ECO2411,150%
ESSENTIEL1211,23%
CONFORT1211,23%
SERENITE1221,75%

Résultats CSV (ex. ECO, sans remise) :

  • HT : Déplacement 35 €, MO 169,92 €, Fournitures 35,98 € → Total 240,91 €
  • TTC : Déplacement 38,50 €, MO 179,27 €, Fournitures 37,96 € → Total 255,73 €
  • Mensualité TTC : 10,66 € (255,73 / 24)

Logique du simulateur (déduite du CSV)

  1. Bases (ligne équipement) : MO HT = 73,88 × 2 × 1 = 147,76 € ; TTC = HT × (1 + TVA).
  2. Par formule : coefficient de majoration appliqué sur chaque poste HT (déplacement, MO, fournitures), puis TTC = HT × (1 + TVA).
  3. Déplacement : base zone × coefficient (ponctuel = 1,2 pour avoir 42 € à partir de 35 €).
  4. Mensualité : Total TTC / durée (mois), arrondi à 2 décimales.
  5. Remise : appliquée après (pour ESSENTIEL/CONFORT/SERENITE).

Correspondance avec le code

ÉlémentSimulateur CSVCode (CalculateOfferFromRequestService)
UnitéEuros (affichage)Centimes (stockage Price, DTOs en centimes)
DéplacementPrix zone × coef × visitesPostalCode.price (centimes) × punctual_shift_coefficient × getSubscriptionVisit() × punctual_coefficient si ponctuel
MOTarif h × heures × coef MO × coef formulehourlyPrice × hourRequire × coefficientRate × maintenanceOffer.getCoefficient() (+ punctual_coefficient si ponctuel)
FournituresPrix fourniture × coef formulesupplyPrice × maintenanceOffer.getCoefficient()
MensualitéTotal TTC / duréetotalTtc / duration (en centimes), arrondi Math::roundAwayFromZero
TVA particulier5,5 % (155,89/147,76 ≈ 1,055)Paramètre individual_vat : 0,1 (10 %) par défaut

Différences susceptibles d’empêcher de reproduire les montants

  1. TVA particulier
    • CSV : 5,5 % (TTC MO = 155,89 pour 147,76 HT).
    • Code : 10 % (individual_vat = 0,1).
      → Vérifier si le métier impose 5,5 % (TVA réduite) et, le cas échéant, mettre individual_vat à 0,055 (ou valeur paramétrée).
  2. Prix déplacement zone
    • CSV : 35 € HT (Gradignan).
    • Code : PostalCode.price en centimes (ex. 3500 pour 35 €).
      → S’assurer que la zone “Gradignan” a bien price = 3500 (ou 35.0 si le champ est en euros selon le modèle).
  3. Coefficient ponctuel déplacement
    • CSV : 42 / 35 = 1,2 pour “Entretien Ponctuel”.
    • Code : punctual_shift_coefficient (ex. 1,2) × punctual_coefficient (ex. 1,0).
      → Aligner ces paramètres avec le simulateur (1,2 sur le déplacement pour le ponctuel).
  4. Mapping Formule → MaintenanceOffer
    • ECO / ESSENTIEL / CONFORT / SERENITE doivent correspondre à des MaintenanceOffer avec les bons coefficient, subscriptionDuration, subscriptionVisit (et type ponctuel/récurrent).
      → Vérifier en base ou en fixtures que chaque formule a le même coefficient, durée et nombre de visites que le CSV.
  5. Arrondis
    • Code : Math::roundAwayFromZero (mode “away from zero”).
    • CSV : à confirmer (souvent arrondi 2 décimales par poste puis somme).
      → Pour coller au CSV, appliquer le même mode d’arrondi et le même ordre (par poste puis totaux).

Vérification rapide (exemple ECO, 1 visite, coef 1,15, durée 24)

  • Déplacement : 35 × 1 = 35 € HT → 38,50 € TTC (si TVA 10 % : 38,50 ; si 5,5 % : 36,93).
    Le CSV donne 38,50 → cohérent avec 10 % sur cette ligne (35 × 1,1 = 38,50). Donc le déplacement utilise bien 10 % dans le CSV, alors que MO/fournitures semblent en 5,5 %. À clarifier côté métier (TVA par type de poste ?).
  • MO : 147,76 × 1,15 = 169,92 € HT ; 169,92 × 1,055 ≈ 179,26 → 179,27 € TTC dans le CSV.
    Donc TVA 5,5 % sur MO/fournitures dans le simulateur.
  • Fournitures : 31,29 × 1,15 = 35,98 € HT ; 35,98 × 1,055 ≈ 37,96 → cohérent avec le CSV.

Conclusion : le simulateur peut utiliser deux TVA (déplacement 10 %, MO/fournitures 5,5 %). Le code actuel utilise une seule TVA par client (pro/particulier). Pour reproduire le CSV, il faudrait soit :

  • une TVA par type de poste (déplacement vs MO vs fournitures), soit
  • confirmer la règle métier (une seule TVA ou deux) et aligner paramètres + code en conséquence.

Suite recommandée

  1. Clarifier avec le métier : TVA unique ou différenciée (déplacement 10 %, MO/fournitures 5,5 %) et valeur officielle “particulier”.
  2. Aligner les paramètres : individual_vat, pro_vat, punctual_shift_coefficient, punctual_coefficient, et prix/zone (Gradignan = 3500 centimes si prix en cts).
  3. Ajouter un test (ex. testCalculatePriceMatchesSimulatorEco) avec les données du CSV en centimes, mêmes coefficients et durée, et assertions sur totaux HT/TTC et mensualité une fois la règle TVA fixée.

Test de non-régression (écarts CSV vs code)

Le test testCalculatePriceVsSimulatorCsvEco dans CalculateOfferFromRequestServiceTest reproduit le scénario ECO du CSV (Particulier, 1 PAC, Gradignan 35€, coef 1,15, 24 mois, 1 visite) et :

  • vérifie que le total HT est proche du CSV (240,91 € = 24091 cts), avec une tolérance de 5 cts (arrondis) ;
  • vérifie que le total TTC diffère tant que la TVA MO/fournitures reste à 10 % dans le code (vs 5,5 % dans le simulateur).

Pour lancer uniquement ce test :

docker compose exec api env APP_ENV=test php bin/phpunit tests/Service/CalculateOfferFromRequestServiceTest.php --filter testCalculatePriceVsSimulatorCsvEco

Une fois la TVA (ou d’autres paramètres) alignée avec le simulateur, on pourra remplacer l’assertNotEquals sur le TTC par une égalité stricte sur les totaux et la mensualité.