CDL Copilot · en production
La sortie de secours qui manquait. Elle ne supprime rien : elle défait le paiement, après quoi la suppression redevient possible — et si c'est justifié, vous la faites.
migration 020 en base déployé en prod 14/14 contrôles prod 29/29 Postgres local 344 tests unitaires
C'est fait, et vérifié en production. Les deux résidus de mes propres tests (marqués payés) ont été nettoyés par l'enchaînement réel : annulation du paiement, puis suppression. Votre base est propre : Pionix, polydruck, HEPPNER, Michael BOUTON EURL — vos quatre vraies inscriptions, intactes.
La migration 019 refuse de supprimer une inscription payée — et elle a raison : derrière, il y a une vente, une facture, un client. Mais un paiement peut être faux : un événement Stripe de test, une saisie manuelle, un encaissement annulé par le client.
Sans porte de sortie, le seul recours serait d'aller modifier la base à la main — exactement ce qu'on veut éviter. D'où la migration 020.
public.annuler_paiement(uuid, motif) → 'ok' | 'refuse' | 'introuvable'
| 'non_payee' | 'motif_manquant'
| 'motif_trop_long'
Elle repasse l'inscription à prospect, efface la date de paiement, et journalise le motif obligatoire. Une fois revenue prospect, la suppression de 019 redevient utilisable. Les deux gestes restent séparés, et chacun est tracé.
Elle ne touche pas :
dernier_appel_le — un contact commercial n'est pas un paiement ;lien_paiement — le lien Stripe reste valable, le client peut payer ;Elle n'annule PAS le paiement chez Stripe. Si l'argent a réellement été encaissé, un remboursement est nécessaire — et il se fait dans Stripe, pas ici. L'écran le dit en clair, en orange, avant de valider.
Sur un Postgres reconstruit à l'identique (toutes les migrations
rejouées, rôles réels, auth.uid() jouable) : 29 contrôles.
| Contrôle | Attendu | Résultat |
|---|---|---|
| Formateur, compte sans profil, anonyme | refusés | refusés |
| Motif vide, trop court, trop long | refusés | refusés |
| Inscription non payée | non_payee | non_payee |
| Aucun refus n'a rien modifié | état intact | intact |
Payée par etat seul | ok | ok |
Payée par date_paiement seule | ok | ok |
Lien Stripe, dernier_appel_le, relances, stagiaires | intacts | intacts |
| Trace : motif + auteur + état d'avant, sans doublon | 1 ligne | 1 |
| Annuler, puis supprimer | ok | ok |
Le verrou : authenticated n'écrit toujours que opco | opco seul | opco |
Et sur la production, avec une session membre réelle : 14 contrôles, dont l'enchaînement complet annulation → suppression, vérifié en base.