Logs de diagnostic et rapprochement SumUp ↔ bénévole
ci-cd / build-backend (push) Successful in 38s
ci-cd / build-frontend (push) Successful in 20s
ci-cd / deploy (push) Successful in 13s

- Une ligne de log par requête (route modèle, statut, durée, code d'erreur,
  compte), identifiant X-Request-ID repris dans chaque log et affiché à
  l'utilisateur sous une erreur, exceptions non gérées tracées (500 JSON)
- Journal d'accès d'uvicorn coupé : il exposait le jeton du formulaire public
- Logs métier : paiement SumUp, connexion, import, plannings, solveur
- Nom du bénévole dans la description du checkout SumUp
- Code et identifiant de transaction SumUp enregistrés au paiement
  (migration 0023), affichés sur la fiche et dans l'export Excel

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
AdamThomas
2026-10-12 18:54:35 +02:00
co-authored by Claude Opus 5.5
parent d722a81a87
commit d0fa1e109c
27 changed files with 798 additions and 16 deletions
+33 -4
View File
@@ -205,8 +205,8 @@ les tables sauf les tables d'association.
- **Export bénévoles** (Excel, `app/exporting/xlsx.py` + `GET
/editions/{id}/volunteers/export`, COORDINATION) : une ligne par
bénévole, mêmes champs que la fiche (coordonnées, vœux, cotisation,
mécénat) — les vœux (postes/artistes/compagnons) sont résolus en noms
lisibles, jamais en UUID. `openpyxl`, déjà utilisé en lecture par l'import
mécénat, transaction SumUp — code et identifiant) — les vœux (postes/
artistes/compagnons) sont résolus en noms lisibles, jamais en UUID. `openpyxl`, déjà utilisé en lecture par l'import
(`app/importing/`), cette fois en écriture. Bouton « Exporter en Excel »
sur l'écran de configuration des bénévoles (`ConfigVolunteers.tsx`), même
mécanique de téléchargement que les badges/feuilles de poste (blob →
@@ -358,7 +358,17 @@ les tables sauf les tables d'association.
ou supprimée ensuite. `Volunteer.fee_checkout_id` (identifiant SumUp,
jamais exposé) et `fee_payment_status` (`PENDING`/`PAID`/`FAILED`/
`EXPIRED`, `NULL` tant qu'aucun checkout n'a été créé) suivent l'état ;
`fee_paid_at` est posé à la première confirmation `PAID`. Une fois promue,
`fee_paid_at` est posé à la première confirmation `PAID`, avec la
transaction SumUp qui a réglé le checkout : `fee_transaction_code` (code
court du tableau de bord SumUp et de ses reçus, ex. `TEENSK4W2K` — lu sur
le checkout, ou à défaut sur son essai `SUCCESSFUL` dans `transactions`)
et `fee_transaction_id` (identifiant technique) — exposés sur la fiche
(section « Cotisation ») et dans l'export Excel, pour le rapprochement
comptable. Dans l'autre sens, l'API de création de checkout n'ayant
**aucun champ de métadonnées libre**, c'est la `description` du checkout
(« affichée dans les outils SumUp ») qui porte le nom du bénévole :
`Prénom Nom — libellé — édition AAAA` (`_checkout_description` ; donnée
transmise au prestataire de paiement, pas un log). Une fois promue,
la ligne `PendingFormSubmission` correspondante voit ses champs nominatifs
vidés (RGPD : l'information ne vit plus que sur `Volunteer`) ; une ligne
jamais payée est purgée après 48 h sans être relancée
@@ -724,7 +734,8 @@ l'étape 21.)*
│ │ ├── main.py # instance FastAPI
│ │ ├── config.py # settings (pydantic-settings)
│ │ ├── db.py # engine + session
│ │ ├── logging_config.py # RGPD : pas de PII dans les logs
│ │ ├── logging_config.py # RGPD : pas de PII dans les logs ; identifiant de requête
│ ├── request_logging.py # une ligne de log par requête, X-Request-ID, erreurs 500
│ │ ├── api/ # routers + schemas Pydantic + deps
│ │ │ # health, auth, accounts, editions,
│ │ │ # day_parts, imports, artists, volunteers,
@@ -785,6 +796,23 @@ Aucune dépendance locale requise hors Docker.
+ `locales/fr.json`. Frontend : fichier i18n dédié. Vrai même en monolingue.
- **RGPD :**
- Pas de log des noms, emails, téléphones. On logue des identifiants.
- **Logs** (`app/logging_config.py`, `app/request_logging.py`) : une ligne
par requête HTTP (`app.request` — méthode, route **modèle** + ses
identifiants, jamais l'URL brute ni un paramètre `*token*`, statut, durée,
code d'erreur métier `detail.code`, compte connecté) ; INFO / WARNING
(4xx) / ERROR (5xx), `/health` en DEBUG. Chaque requête a un identifiant
(`X-Request-ID`, repris s'il est fourni et bien formé) préfixé à toutes
ses lignes de log, tâches de fond comprises, et affiché à l'utilisateur
sous une erreur (`ErrorNote` : « Référence à communiquer ») — `grep` de
cette référence dans les logs du conteneur pour diagnostiquer. Erreur 422
de validation : emplacement + type, jamais la valeur saisie. Exception non
gérée : trace complète + `500 {detail: {code: "error.internal",
request_id}}`. Journal d'accès d'uvicorn **désactivé** (il écrivait la
chaîne de requête, donc le `access_token` du formulaire public). Logs
métier sur les parcours opaques : paiement SumUp (inscription en attente,
checkout créé/refusé avec le code d'erreur SumUp, changements de statut,
promotion, webhook), connexion (cause réelle d'un refus, côté logs
seulement), import, envoi de plannings, solveur (durées).
- `Edition.retention_days` : durée de rétention paramétrable.
- Endpoint de suppression complète d'un bénévole : *hard delete* en cascade.
- **Dates et heures :** stockées naïves, interprétées en heure locale
@@ -875,6 +903,7 @@ Aucune dépendance locale requise hors Docker.
| 40 | Écran Statistiques (`/statistiques`, COORDINATION, `GET /editions/{id}/stats`) : inscriptions (nombre, évolution cumulée), réponses au formulaire, artistes demandés, cotisations (en ligne / sur place / aucune, par type, mécénat, paiements jamais aboutis), besoins par journée de festival face aux disponibilités déclarées et aux affectations, places pourvues et vœux par poste — graphiques SVG maison avec infobulles et vues tableau | à valider |
| 41 | Début de « journée de festival » indépendant de l'ordre des créneaux types : début du plus long creux qu'ils laissent sur 24 h (`validation.engine.day_boundary`, miroir `lib/planning.ts:dayBoundaryFromDayParts`). Avant, un créneau type d'ordre 0 déplacé à 21:00 faisait commencer chaque journée des grilles (besoins, vue bénévoles) à 21:00, jusqu'à 03:00 le lendemain | à valider |
| 42 | Retrait des disponibilités déclarées (table `availability` supprimée, migration `0022` ; API, éditeur de la fiche bénévole, badge de couverture du panneau d'affectation, colonnes des statistiques) et des limites de temps dans la journée (règles `OUTSIDE_AVAILABILITY`, `DAILY_HOURS_EXCEEDED`, `INSUFFICIENT_REST`, malus de disponibilité et de repos du solveur, filtre « Surchargés » de la vue bénévoles) — seul le chevauchement reste contrôlé. La case « je suis disponible » du formulaire public est conservée | à valider |
| 43 | Logs de diagnostic (ligne par requête avec route modèle, statut, durée, code d'erreur et compte ; identifiant `X-Request-ID` repris dans chaque log et affiché à l'utilisateur sous une erreur ; exceptions non gérées tracées ; journal d'accès d'uvicorn coupé car il exposait le jeton du formulaire public ; logs métier SumUp, connexion, import, plannings, solveur) et rapprochement SumUp ↔ bénévole : nom du bénévole dans la description du checkout (pas de métadonnées libres dans l'API), code et identifiant de transaction enregistrés au paiement (`Volunteer.fee_transaction_code`/`fee_transaction_id`, migration `0023`), affichés sur la fiche et dans l'export Excel | à valider |
**Reste hors périmètre** (modèle prêt à les accueillir) : SMS, constructeur
de formulaire, pointage sur site, IA. Frontend : squelette + 6 écrans +