feat: budget-tracker — spec, constitution, data-model, API contracts, plan
This commit is contained in:
@@ -0,0 +1,247 @@
|
||||
# Contrats API REST — Budget Tracker
|
||||
|
||||
**Version** : 1.0.0 | **Base URL** : `/api/v1` | **Format** : JSON
|
||||
|
||||
## Conventions
|
||||
|
||||
- Tous les montants sont en **centimes** (integer)
|
||||
- Authentification : `Authorization: Bearer <access_token>`
|
||||
- Dates : ISO 8601 (`YYYY-MM-DD`)
|
||||
- Mois : `YYYY-MM`
|
||||
- Soft-delete : les ressources supprimées retournent 404
|
||||
- Erreurs : `{"detail": "message d'erreur"}`
|
||||
|
||||
---
|
||||
|
||||
## Authentification
|
||||
|
||||
### POST /auth/register
|
||||
Créer un compte utilisateur.
|
||||
|
||||
**Body** :
|
||||
```json
|
||||
{"email": "user@example.com", "password": "...", "full_name": "Jean Dupont"}
|
||||
```
|
||||
**Réponse 201** :
|
||||
```json
|
||||
{"id": "uuid", "email": "user@example.com", "full_name": "Jean Dupont"}
|
||||
```
|
||||
|
||||
### POST /auth/login
|
||||
Obtenir les tokens JWT.
|
||||
|
||||
**Body** : `application/x-www-form-urlencoded` : `username=email&password=...`
|
||||
|
||||
**Réponse 200** :
|
||||
```json
|
||||
{"access_token": "...", "refresh_token": "...", "token_type": "bearer"}
|
||||
```
|
||||
|
||||
### POST /auth/refresh
|
||||
Renouveler l'access token.
|
||||
|
||||
**Body** : `{"refresh_token": "..."}`
|
||||
**Réponse 200** : `{"access_token": "...", "token_type": "bearer"}`
|
||||
|
||||
### POST /auth/logout
|
||||
Invalider le refresh token.
|
||||
**Réponse 204** : no content
|
||||
|
||||
---
|
||||
|
||||
## Transactions
|
||||
|
||||
### GET /transactions
|
||||
Lister les transactions (paginées, filtrables).
|
||||
|
||||
**Query params** : `?month=2026-03&category_id=uuid&type=expense&page=1&per_page=20`
|
||||
|
||||
**Réponse 200** :
|
||||
```json
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"id": "uuid",
|
||||
"amount_cents": 4500,
|
||||
"type": "expense",
|
||||
"description": "Courses Leclerc",
|
||||
"category": {"id": "uuid", "name": "Alimentation", "color": "#4CAF50"},
|
||||
"transaction_date": "2026-03-15",
|
||||
"created_at": "2026-03-15T10:30:00Z"
|
||||
}
|
||||
],
|
||||
"total": 42,
|
||||
"page": 1,
|
||||
"per_page": 20
|
||||
}
|
||||
```
|
||||
|
||||
### POST /transactions
|
||||
Créer une transaction.
|
||||
|
||||
**Body** :
|
||||
```json
|
||||
{
|
||||
"amount_cents": 4500,
|
||||
"type": "expense",
|
||||
"category_id": "uuid",
|
||||
"description": "Courses Leclerc",
|
||||
"transaction_date": "2026-03-15"
|
||||
}
|
||||
```
|
||||
**Réponse 201** : transaction complète
|
||||
|
||||
### GET /transactions/{id}
|
||||
Détail d'une transaction. **Réponse 200** : transaction complète
|
||||
|
||||
### PUT /transactions/{id}
|
||||
Modifier une transaction. **Body** : mêmes champs que POST. **Réponse 200** : transaction mise à jour
|
||||
|
||||
### DELETE /transactions/{id}
|
||||
Soft-delete. **Réponse 204** : no content
|
||||
|
||||
---
|
||||
|
||||
## Catégories
|
||||
|
||||
### GET /categories
|
||||
Lister les catégories de l'utilisateur (y compris les défauts système).
|
||||
|
||||
**Query** : `?type=expense`
|
||||
|
||||
**Réponse 200** :
|
||||
```json
|
||||
[
|
||||
{"id": "uuid", "name": "Alimentation", "type": "expense", "color": "#4CAF50", "icon": "shopping-cart", "is_default": true}
|
||||
]
|
||||
```
|
||||
|
||||
### POST /categories
|
||||
Créer une catégorie personnalisée.
|
||||
|
||||
**Body** : `{"name": "Loisirs", "type": "expense", "color": "#9C27B0", "icon": "gamepad"}`
|
||||
**Réponse 201** : catégorie créée
|
||||
|
||||
### PUT /categories/{id}
|
||||
Modifier une catégorie. **Réponse 200** : catégorie mise à jour
|
||||
|
||||
### DELETE /categories/{id}
|
||||
Soft-delete. Erreur 409 si des transactions y sont liées.
|
||||
|
||||
---
|
||||
|
||||
## Budgets (Enveloppes)
|
||||
|
||||
### GET /budgets
|
||||
Lister les budgets du mois courant ou d'un mois donné.
|
||||
|
||||
**Query** : `?month=2026-03`
|
||||
|
||||
**Réponse 200** :
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "uuid",
|
||||
"category": {"id": "uuid", "name": "Alimentation"},
|
||||
"month": "2026-03",
|
||||
"limit_cents": 30000,
|
||||
"spent_cents": 12500,
|
||||
"remaining_cents": 17500,
|
||||
"percentage_used": 41.7
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### POST /budgets
|
||||
Définir un budget pour une catégorie/mois.
|
||||
|
||||
**Body** : `{"category_id": "uuid", "month": "2026-03", "limit_cents": 30000}`
|
||||
**Réponse 201** : budget créé
|
||||
|
||||
### PUT /budgets/{id}
|
||||
Modifier la limite d'un budget. **Réponse 200** : budget mis à jour
|
||||
|
||||
### DELETE /budgets/{id}
|
||||
Supprimer un budget. **Réponse 204**
|
||||
|
||||
---
|
||||
|
||||
## Dashboard
|
||||
|
||||
### GET /dashboard
|
||||
KPIs du mois courant ou d'un mois donné.
|
||||
|
||||
**Query** : `?month=2026-03`
|
||||
|
||||
**Réponse 200** :
|
||||
```json
|
||||
{
|
||||
"month": "2026-03",
|
||||
"balance_cents": 150000,
|
||||
"total_income_cents": 250000,
|
||||
"total_expense_cents": 100000,
|
||||
"by_category": [
|
||||
{"category_id": "uuid", "name": "Alimentation", "total_cents": 45000, "percentage": 45.0}
|
||||
],
|
||||
"monthly_trend": [
|
||||
{"month": "2026-01", "income_cents": 240000, "expense_cents": 110000},
|
||||
{"month": "2026-02", "income_cents": 245000, "expense_cents": 95000},
|
||||
{"month": "2026-03", "income_cents": 250000, "expense_cents": 100000}
|
||||
],
|
||||
"budget_alerts": [
|
||||
{"category_id": "uuid", "name": "Loisirs", "percentage_used": 92.0, "status": "warning"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Historique
|
||||
|
||||
### GET /history
|
||||
Résumé mensuel navigable.
|
||||
|
||||
**Query** : `?year=2026`
|
||||
|
||||
**Réponse 200** :
|
||||
```json
|
||||
[
|
||||
{
|
||||
"month": "2026-03",
|
||||
"income_cents": 250000,
|
||||
"expense_cents": 100000,
|
||||
"balance_cents": 150000,
|
||||
"transaction_count": 42
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Export
|
||||
|
||||
### GET /export/csv
|
||||
Exporter les transactions en CSV.
|
||||
|
||||
**Query** : `?month=2026-03` (optionnel — tout exporter si absent)
|
||||
**Réponse 200** : `Content-Type: text/csv`, fichier `transactions_2026-03.csv`
|
||||
|
||||
### GET /export/pdf
|
||||
Exporter le rapport mensuel en PDF.
|
||||
|
||||
**Query** : `?month=2026-03`
|
||||
**Réponse 200** : `Content-Type: application/pdf`, fichier `rapport_2026-03.pdf`
|
||||
|
||||
---
|
||||
|
||||
## Codes d'erreur
|
||||
|
||||
| Code | Signification |
|
||||
|------|--------------|
|
||||
| 400 | Données invalides (validation Pydantic) |
|
||||
| 401 | Token manquant ou expiré |
|
||||
| 403 | Ressource appartenant à un autre utilisateur |
|
||||
| 404 | Ressource introuvable (ou soft-deleted) |
|
||||
| 409 | Conflit (doublon, contrainte métier) |
|
||||
| 422 | Erreur de validation (détail par champ) |
|
||||
| 500 | Erreur serveur |
|
||||
Reference in New Issue
Block a user