fix: afficher les noms lisibles des types de jours et des véhicules
- Filtre Jinja2 day_type_fr (WORK→Travail, TT→Télétravail, etc.) - Appliqué dans entry_list.html et dashboard.html - Passage de vehicles au template dashboard pour afficher vehicle.name - Mise à jour du test test_entry_list en conséquence - Ajout du plan docs/plans/2026-03-11-bareme-kilometrique.md
This commit is contained in:
@@ -36,6 +36,23 @@ _MOIS_FR = ["", "janvier", "février", "mars", "avril", "mai", "juin",
|
||||
"juillet", "août", "septembre", "octobre", "novembre", "décembre"]
|
||||
|
||||
|
||||
_DAY_TYPE_LABELS = {
|
||||
"WORK": "Travail",
|
||||
"TT": "Télétravail",
|
||||
"GARDE": "Garde",
|
||||
"ASTREINTE": "Astreinte",
|
||||
"FORMATION": "Formation",
|
||||
"RTT": "RTT",
|
||||
"CONGE": "Congé",
|
||||
"MALADE": "Maladie",
|
||||
"FERIE": "Férié",
|
||||
}
|
||||
|
||||
|
||||
def _day_type_fr(code):
|
||||
return _DAY_TYPE_LABELS.get(code, code)
|
||||
|
||||
|
||||
def _date_fr(d):
|
||||
"""Formate une date en français : 'mercredi 11 mars 2026'."""
|
||||
from datetime import date as date_type
|
||||
@@ -64,6 +81,7 @@ def create_app(config_path=None):
|
||||
|
||||
db.init_app(app)
|
||||
app.jinja_env.filters["date_fr"] = _date_fr
|
||||
app.jinja_env.filters["day_type_fr"] = _day_type_fr
|
||||
|
||||
from app.routes.dashboard import bp as dashboard_bp
|
||||
from app.routes.entries import bp as entries_bp
|
||||
|
||||
@@ -54,6 +54,7 @@ def index():
|
||||
today=today,
|
||||
today_entry=today_entry,
|
||||
journeys=journeys,
|
||||
vehicles=vehicles,
|
||||
week_actual_str=minutes_to_str(week_actual),
|
||||
week_balance=week_balance,
|
||||
week_balance_str=minutes_to_str(abs(week_balance)),
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
</div>
|
||||
<div class="flex items-center justify-between mt-3">
|
||||
<span class="text-xs font-semibold px-2 py-0.5 rounded" style="background:var(--parchment); color:#6A6258; letter-spacing:0.06em;">
|
||||
{{ today_entry.day_type }}{% if today_entry.motor_vehicle_id %} · {{ today_entry.motor_vehicle_id }}{% endif %}
|
||||
{{ today_entry.day_type | day_type_fr }}{% if today_entry.motor_vehicle_id %} · {{ today_entry.motor_vehicle_id }}{% endif %}
|
||||
</span>
|
||||
<a href="/entries/{{ today_entry.id }}/edit"
|
||||
class="text-xs font-semibold" style="color:var(--amber);">Modifier →</a>
|
||||
@@ -55,7 +55,7 @@
|
||||
<div class="space-y-2 mt-1">
|
||||
{% for vehicle_id, km in month_km.items() %}
|
||||
<div class="flex items-baseline justify-between">
|
||||
<span class="text-xs" style="color:#8A8278;">{{ vehicle_id | capitalize }}</span>
|
||||
<span class="text-xs" style="color:#8A8278;">{{ vehicles[vehicle_id].name if vehicle_id in vehicles else vehicle_id | capitalize }}</span>
|
||||
<span class="font-data font-semibold text-sm" style="color:var(--ink);">{{ km }} <span class="font-normal text-xs" style="color:#9A9288;">km</span></span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="font-semibold text-sm" style="color:var(--ink);">{{ entry.date | date_fr }}</p>
|
||||
<div class="flex items-center gap-2 mt-0.5">
|
||||
<span class="text-xs font-semibold" style="color:{{ dot_color }}; letter-spacing:0.06em;">{{ entry.day_type }}</span>
|
||||
<span class="text-xs font-semibold" style="color:{{ dot_color }}; letter-spacing:0.06em;">{{ entry.day_type | day_type_fr }}</span>
|
||||
{% if entry.time_slots %}
|
||||
<span class="font-data text-xs" style="color:#8A8278;">{{ entry.total_hours_str() }}</span>
|
||||
{% endif %}
|
||||
|
||||
12
config.toml
12
config.toml
@@ -1,21 +1,21 @@
|
||||
[vehicles.citadine]
|
||||
name = "Citadine électrique"
|
||||
name = "Twingo ZE"
|
||||
fuel = "electric"
|
||||
co2_per_km = 0
|
||||
cv = 3
|
||||
type = "moteur"
|
||||
|
||||
[vehicles.familiale]
|
||||
name = "Familiale thermique"
|
||||
name = "Duster"
|
||||
fuel = "diesel"
|
||||
co2_per_km = 142
|
||||
co2_per_km = 135
|
||||
cv = 5
|
||||
type = "moteur"
|
||||
|
||||
[vehicles.moto]
|
||||
name = "Moto"
|
||||
fuel = "essence"
|
||||
co2_per_km = 90
|
||||
name = "CE04"
|
||||
fuel = "electric"
|
||||
co2_per_km = 0
|
||||
cv = 3
|
||||
type = "moteur"
|
||||
|
||||
|
||||
322
docs/plans/2026-03-11-bareme-kilometrique.md
Normal file
322
docs/plans/2026-03-11-bareme-kilometrique.md
Normal file
@@ -0,0 +1,322 @@
|
||||
# Barème kilométrique — Refonte complète
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** Corriger le barème kilométrique pour qu'il corresponde au barème fiscal officiel : tranches correctes (0–5 000 / 5 001–20 000 / >20 000 km), CV individuels (3 CV, 4 CV, 5 CV, 6 CV, 7 CV+), et support de la majoration +20 % pour les véhicules électriques.
|
||||
|
||||
**Architecture:** Le barème est stocké dans `config.toml`, chargé via `get_bareme()` dans `config_loader.py`, et utilisé par `compute_frais_reels()` dans `travel_calc.py`. La majoration électrique sera un paramètre booléen de `compute_frais_reels()`, passé depuis `reports.py` en lisant `vehicle["fuel"] == "electric"`.
|
||||
|
||||
**Tech Stack:** Python 3, Flask, TOML, pytest
|
||||
|
||||
---
|
||||
|
||||
## Contexte
|
||||
|
||||
### Barème officiel 2025 (voitures)
|
||||
|
||||
| Puissance | ≤ 5 000 km | 5 001–20 000 km | > 20 000 km |
|
||||
|-----------|-------------------|--------------------------|---------------|
|
||||
| 3 CV max | d × 0,529 | (d × 0,316) + 1 065 | d × 0,370 |
|
||||
| 4 CV | d × 0,606 | (d × 0,340) + 1 330 | d × 0,407 |
|
||||
| 5 CV | d × 0,636 | (d × 0,357) + 1 395 | d × 0,427 |
|
||||
| 6 CV | d × 0,665 | (d × 0,374) + 1 457 | d × 0,447 |
|
||||
| 7 CV+ | d × 0,697 | (d × 0,394) + 1 515 | d × 0,470 |
|
||||
|
||||
**Électrique :** majoration de +20 % sur le montant final calculé.
|
||||
|
||||
### Problèmes actuels dans config.toml
|
||||
- Tranches fausses : 0–3 000 / 3 001–6 000 / >6 000 (au lieu de 0–5 000 / 5 001–20 000 / >20 000)
|
||||
- Groupes CV incorrects : `cv_5` regroupe tout ≤ 5 CV
|
||||
- Taux incorrects
|
||||
- Électrique absent
|
||||
|
||||
---
|
||||
|
||||
## Tâche 1 : Mettre à jour les tests de `compute_frais_reels`
|
||||
|
||||
**Fichier :** `tests/test_travel_calc.py`
|
||||
|
||||
### Étape 1 : Remplacer `TRANCHES_CV5` par les tranches officielles et ajouter `TRANCHES_CV3`
|
||||
|
||||
Remplacer dans `tests/test_travel_calc.py` les constantes de test :
|
||||
|
||||
```python
|
||||
TRANCHES_CV3 = [
|
||||
{"km_max": 5000, "taux": 0.529, "forfait": 0},
|
||||
{"km_max": 20000, "taux": 0.316, "forfait": 1065},
|
||||
{"km_max": 0, "taux": 0.370, "forfait": 0},
|
||||
]
|
||||
|
||||
TRANCHES_CV5 = [
|
||||
{"km_max": 5000, "taux": 0.636, "forfait": 0},
|
||||
{"km_max": 20000, "taux": 0.357, "forfait": 1395},
|
||||
{"km_max": 0, "taux": 0.427, "forfait": 0},
|
||||
]
|
||||
```
|
||||
|
||||
### Étape 2 : Remplacer les assertions existantes et ajouter les nouveaux tests
|
||||
|
||||
```python
|
||||
# Remplacer les tests frais_reels existants :
|
||||
def test_frais_reels_tranche1():
|
||||
result = compute_frais_reels(2000, TRANCHES_CV5)
|
||||
assert abs(result - 1272.0) < 0.01 # 2000 × 0.636
|
||||
|
||||
def test_frais_reels_tranche2():
|
||||
result = compute_frais_reels(10000, TRANCHES_CV5)
|
||||
assert abs(result - 4965.0) < 0.01 # 10000 × 0.357 + 1395
|
||||
|
||||
def test_frais_reels_tranche3():
|
||||
result = compute_frais_reels(25000, TRANCHES_CV5)
|
||||
assert abs(result - 10675.0) < 0.01 # 25000 × 0.427
|
||||
|
||||
# Ajouter les tests électrique :
|
||||
def test_frais_reels_electrique_majoration_20_pct():
|
||||
result = compute_frais_reels(2000, TRANCHES_CV3, electric=True)
|
||||
assert abs(result - 1269.6) < 0.01 # 2000 × 0.529 × 1.2
|
||||
|
||||
def test_frais_reels_non_electrique_sans_majoration():
|
||||
result = compute_frais_reels(2000, TRANCHES_CV3, electric=False)
|
||||
assert abs(result - 1058.0) < 0.01 # 2000 × 0.529
|
||||
```
|
||||
|
||||
### Étape 3 : Vérifier que les tests échouent
|
||||
|
||||
```bash
|
||||
.venv/bin/python -m pytest tests/test_travel_calc.py -v
|
||||
```
|
||||
|
||||
Attendu : `FAILED` sur les tests `test_frais_reels_*` (mauvaises valeurs et paramètre `electric` inexistant).
|
||||
|
||||
---
|
||||
|
||||
## Tâche 2 : Modifier `compute_frais_reels` pour supporter la majoration électrique
|
||||
|
||||
**Fichier :** `app/business/travel_calc.py`
|
||||
|
||||
### Étape 1 : Ajouter le paramètre `electric`
|
||||
|
||||
Modifier la signature et la logique de `compute_frais_reels` :
|
||||
|
||||
```python
|
||||
def compute_frais_reels(total_km_moteur: float, tranches: list[dict], electric: bool = False) -> float:
|
||||
"""
|
||||
Calcule les frais réels fiscaux selon le barème kilométrique.
|
||||
km_max = 0 signifie "pas de limite" (dernière tranche).
|
||||
electric=True applique la majoration de 20 % pour véhicules électriques.
|
||||
"""
|
||||
if not tranches or total_km_moteur <= 0:
|
||||
return 0.0
|
||||
for tranche in tranches:
|
||||
km_max = tranche["km_max"]
|
||||
if km_max == 0 or total_km_moteur <= km_max:
|
||||
result = total_km_moteur * tranche["taux"] + tranche.get("forfait", 0)
|
||||
return result * 1.2 if electric else result
|
||||
last = tranches[-1]
|
||||
result = total_km_moteur * last["taux"] + last.get("forfait", 0)
|
||||
return result * 1.2 if electric else result
|
||||
```
|
||||
|
||||
### Étape 2 : Vérifier que les tests passent
|
||||
|
||||
```bash
|
||||
.venv/bin/python -m pytest tests/test_travel_calc.py -v
|
||||
```
|
||||
|
||||
Attendu : tous les tests `PASSED`.
|
||||
|
||||
### Étape 3 : Commit
|
||||
|
||||
```bash
|
||||
git -c commit.gpgsign=false add tests/test_travel_calc.py app/business/travel_calc.py
|
||||
git -c commit.gpgsign=false commit -m "feat: compute_frais_reels supporte la majoration +20% électrique"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tâche 3 : Modifier `get_bareme()` pour les CV individuels
|
||||
|
||||
**Fichier :** `app/config_loader.py`
|
||||
|
||||
### Étape 1 : Remplacer la logique de groupement des CV
|
||||
|
||||
Remplacer la fonction `get_bareme` :
|
||||
|
||||
```python
|
||||
def get_bareme(year: int, cv: int) -> list[dict]:
|
||||
bareme = current_app.config.get("TOML", {}).get("bareme_kilometrique", {})
|
||||
year_data = bareme.get(str(year), {})
|
||||
if cv <= 3:
|
||||
key = "cv_3"
|
||||
elif cv == 4:
|
||||
key = "cv_4"
|
||||
elif cv == 5:
|
||||
key = "cv_5"
|
||||
elif cv == 6:
|
||||
key = "cv_6"
|
||||
else:
|
||||
key = "cv_7plus"
|
||||
return year_data.get(key, {}).get("tranches", [])
|
||||
```
|
||||
|
||||
### Étape 2 : Vérifier que les tests existants passent toujours
|
||||
|
||||
```bash
|
||||
.venv/bin/python -m pytest -v
|
||||
```
|
||||
|
||||
Attendu : tous `PASSED` (les tests de routes utilisent la DB en mémoire et le TOML de test — pas le vrai barème).
|
||||
|
||||
---
|
||||
|
||||
## Tâche 4 : Mettre à jour `reports.py` pour passer le flag électrique
|
||||
|
||||
**Fichier :** `app/routes/reports.py`
|
||||
|
||||
### Étape 1 : Lire le champ `fuel` du véhicule et le passer à `compute_frais_reels`
|
||||
|
||||
Remplacer le bloc `frais_reels` :
|
||||
|
||||
```python
|
||||
frais_reels = {}
|
||||
for vehicle_id, km in total_km.items():
|
||||
vehicle = vehicles.get(vehicle_id, {})
|
||||
cv = vehicle.get("cv")
|
||||
if cv:
|
||||
tranches = get_bareme(year, cv)
|
||||
electric = vehicle.get("fuel") == "electric"
|
||||
frais_reels[vehicle_id] = round(compute_frais_reels(km, tranches, electric=electric), 2)
|
||||
```
|
||||
|
||||
### Étape 2 : Vérifier les tests de routes
|
||||
|
||||
```bash
|
||||
.venv/bin/python -m pytest tests/test_routes.py -v
|
||||
```
|
||||
|
||||
Attendu : tous `PASSED`.
|
||||
|
||||
### Étape 3 : Commit
|
||||
|
||||
```bash
|
||||
git -c commit.gpgsign=false add app/config_loader.py app/routes/reports.py
|
||||
git -c commit.gpgsign=false commit -m "feat: get_bareme par CV individuel, majoration électrique dans reports"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tâche 5 : Corriger le barème dans `config.toml`
|
||||
|
||||
**Fichier :** `config.toml`
|
||||
|
||||
### Étape 1 : Remplacer entièrement la section `bareme_kilometrique`
|
||||
|
||||
Supprimer toutes les lignes `[[bareme_kilometrique.*]]` existantes et les remplacer par :
|
||||
|
||||
```toml
|
||||
# --- Barème kilométrique voitures 2025 (revenus 2024) ---
|
||||
# Source : https://www.service-public.gouv.fr/particuliers/actualites/A14686
|
||||
# Majoration +20% pour véhicules électriques gérée dans travel_calc.py
|
||||
|
||||
[[bareme_kilometrique.2025.cv_3.tranches]]
|
||||
km_max = 5000
|
||||
taux = 0.529
|
||||
forfait = 0
|
||||
|
||||
[[bareme_kilometrique.2025.cv_3.tranches]]
|
||||
km_max = 20000
|
||||
taux = 0.316
|
||||
forfait = 1065
|
||||
|
||||
[[bareme_kilometrique.2025.cv_3.tranches]]
|
||||
km_max = 0
|
||||
taux = 0.370
|
||||
forfait = 0
|
||||
|
||||
[[bareme_kilometrique.2025.cv_4.tranches]]
|
||||
km_max = 5000
|
||||
taux = 0.606
|
||||
forfait = 0
|
||||
|
||||
[[bareme_kilometrique.2025.cv_4.tranches]]
|
||||
km_max = 20000
|
||||
taux = 0.340
|
||||
forfait = 1330
|
||||
|
||||
[[bareme_kilometrique.2025.cv_4.tranches]]
|
||||
km_max = 0
|
||||
taux = 0.407
|
||||
forfait = 0
|
||||
|
||||
[[bareme_kilometrique.2025.cv_5.tranches]]
|
||||
km_max = 5000
|
||||
taux = 0.636
|
||||
forfait = 0
|
||||
|
||||
[[bareme_kilometrique.2025.cv_5.tranches]]
|
||||
km_max = 20000
|
||||
taux = 0.357
|
||||
forfait = 1395
|
||||
|
||||
[[bareme_kilometrique.2025.cv_5.tranches]]
|
||||
km_max = 0
|
||||
taux = 0.427
|
||||
forfait = 0
|
||||
|
||||
[[bareme_kilometrique.2025.cv_6.tranches]]
|
||||
km_max = 5000
|
||||
taux = 0.665
|
||||
forfait = 0
|
||||
|
||||
[[bareme_kilometrique.2025.cv_6.tranches]]
|
||||
km_max = 20000
|
||||
taux = 0.374
|
||||
forfait = 1457
|
||||
|
||||
[[bareme_kilometrique.2025.cv_6.tranches]]
|
||||
km_max = 0
|
||||
taux = 0.447
|
||||
forfait = 0
|
||||
|
||||
[[bareme_kilometrique.2025.cv_7plus.tranches]]
|
||||
km_max = 5000
|
||||
taux = 0.697
|
||||
forfait = 0
|
||||
|
||||
[[bareme_kilometrique.2025.cv_7plus.tranches]]
|
||||
km_max = 20000
|
||||
taux = 0.394
|
||||
forfait = 1515
|
||||
|
||||
[[bareme_kilometrique.2025.cv_7plus.tranches]]
|
||||
km_max = 0
|
||||
taux = 0.470
|
||||
forfait = 0
|
||||
```
|
||||
|
||||
### Étape 2 : Lancer le serveur et vérifier visuellement la page `/reports/`
|
||||
|
||||
```bash
|
||||
.venv/bin/python run.py
|
||||
```
|
||||
|
||||
Naviguer sur `http://localhost:5000/reports/` et vérifier que les frais réels sont calculés (pas d'erreur 500, valeurs cohérentes).
|
||||
|
||||
### Étape 3 : Commit final
|
||||
|
||||
```bash
|
||||
git -c commit.gpgsign=false add config.toml
|
||||
git -c commit.gpgsign=false commit -m "fix: barème kilométrique 2025 — tranches et CV corrects, toutes puissances"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Récapitulatif des fichiers modifiés
|
||||
|
||||
| Fichier | Nature |
|
||||
|---|---|
|
||||
| `tests/test_travel_calc.py` | Mise à jour des tranches de test et nouvelles assertions électrique |
|
||||
| `app/business/travel_calc.py` | Paramètre `electric` dans `compute_frais_reels` |
|
||||
| `app/config_loader.py` | `get_bareme()` avec CV individuels (cv_3 à cv_7plus) |
|
||||
| `app/routes/reports.py` | Passage du flag `electric` à `compute_frais_reels` |
|
||||
| `config.toml` | Barème complet et correct avec toutes les puissances 2025 |
|
||||
@@ -46,7 +46,7 @@ def test_entry_list(client, app):
|
||||
|
||||
response = client.get("/entries/")
|
||||
assert response.status_code == 200
|
||||
assert "TT" in response.text
|
||||
assert "Télétravail" in response.text
|
||||
|
||||
|
||||
def test_reports_page(client):
|
||||
|
||||
Reference in New Issue
Block a user