Aller au contenu
Candidater

Journal des modifications

Les évolutions de Cohez.io, de la plus récente à la plus ancienne.

93 versions correspondent à « inscription ».

Versions 51 à 60 sur 93

0.16.7.0 — 2026-06-05

Ajouts

  • Échange de cours sur une inscription (back-office) : l'équipe peut désormais remplacer un cours par un autre sur une inscription existante, directement depuis sa fiche. Chaque ligne de cours dispose d'un bouton « Échanger » ouvrant une fenêtre où l'on choisit le cours cible (les cours complets sont désactivés), avec un aperçu en temps réel du nouveau montant dû, du solde projeté et de la conséquence financière (trop-perçu à rembourser ou complément à régler). Un motif est obligatoire et consigné dans les notes de l'inscription. Fonctionne aussi sur les inscriptions validées et payées : l'argent n'est jamais déplacé automatiquement (le solde/trop-perçu absorbe l'écart, l'équipe gère remboursement ou complément manuellement). L'échange vers un cours complet est strictement bloqué.

Technique

  • Nouveau SwapRegistrationCourseService : échange atomique (transaction + lockForUpdate sur l'inscription et sur le cours cible), recalcul complet du montant dû (rebuild des items, remises cross-items), résolution serveur du tarif (un montant client n'est honoré que pour un cours à prix libre). Deux routes : PATCH registrations/{registration}/swap-course et POST .../swap-course/preview (aperçu sans persistance), toutes deux soumises à la permission UPDATE_MEMBERS.
  • Verrou sur l'inscription (et plus seulement sur le cours cible) avec recheck-after-lock : deux échanges simultanés sur une même inscription sont sérialisés, évitant un lost-update silencieux sur amount_due sur une inscription non validée (sans contrainte unique de snapshot pour servir de filet). lockForUpdate est un no-op SQLite (tests), effectif sur PostgreSQL (dev/prod).
  • L'acompte (deposit_amount_due) est borné au nouveau montant dû lors d'un échange vers un cours moins cher : sans cela, un acompte supérieur au total dû produisait un payment_status incohérent (partial_deposit).
  • Versionnement des snapshots facture : nouvelle colonne registration_invoices.version (source de vérité) + unique composite (registration_id, version). v1 = validation, v2+ = échanges. La migration renumérote les éventuels doublons préexistants avant de poser la contrainte (l'ancien ->unique() était un no-op chaîné sur la clé étrangère → jamais appliqué). Extraction du RegistrationInvoiceSnapshotter partagé entre la validation et l'échange.
  • La relation Registration::invoice() utilise orderByDesc('version') (et non latestOfMany) : latestOfMany générait un MAX(id) que PostgreSQL refuse sur une clé UUID (function max(uuid) does not exist). La contrainte d'unicité garantit l'absence d'égalité de version, l'ordre suffit.
  • Fiche inscription : les comptes d'inscrits par cours (pour griser les cours complets dans l'échange) sont calculés en une seule requête groupée au lieu d'un COUNT par cours, évitant un N+1 à chaque chargement de la page.
  • Mapper d'items tarifaires partagé (PricingCalculatorService::buildItem) entre la création d'inscription, l'estimation et l'échange. Aperçu côté front avec anti-rebond et annulation des requêtes périmées (AbortController).

0.16.6.0 — 2026-06-04

Ajouts

  • Indicateur « Trop-perçu » sur une inscription : lorsqu'un adhérent paie plus que le montant dû (par exemple un chèque qui dépasse le solde restant), l'écran de l'inscription affiche désormais clairement l'état « Trop-perçu » — une carte ambre indiquant le montant payé en trop (montant positif) et un badge « Trop-perçu » en en-tête. Auparavant, ce cas se traduisait par un « Solde : -10,00 € » négatif et déroutant. Le marquage d'un paiement comme « Reçu » n'a jamais été bloqué par un trop-perçu ; seul l'affichage manquait.

0.16.4.0 — 2026-06-04

Corrections

  • Cours affiché « complet » alors qu'il restait des places : un cours pouvait apparaître complet sur le formulaire d'inscription public alors que le compte admin affichait encore des places disponibles. Deux causes : (1) le formulaire et l'admin ne comptaient pas le même ensemble de statuts (les inscriptions Draft comptaient côté formulaire mais pas côté admin) ; (2) des inscriptions Draft issues d'un checkout HelloAsso abandonné (paiement jamais finalisé) restaient bloquées indéfiniment et occupaient une place à vie. Le comptage de capacité est désormais unifié sur tous les écrans (formulaire public, Cours, Campagnes, Réglages > Cours) et les réservations en attente non finalisées sont automatiquement libérées après un délai.

Ajouts

  • Délai d'expiration des réservations en attente : toute réservation non finalisée (Draft) reçoit désormais une date limite. Passé ce délai sans paiement, elle est automatiquement annulée et sa place libérée. Le délai par défaut est de 7 jours (configurable via REGISTRATION_DRAFT_TTL_DAYS).
  • Surcharge du délai par campagne : chaque campagne peut définir son propre délai d'expiration des réservations en attente, via un champ dédié dans le formulaire de campagne (laisser vide pour utiliser le défaut global).
  • Édition de campagne accessible : un bouton « Modifier » permet désormais d'éditer une campagne existante depuis sa page (la page d'édition existait mais n'était reliée à aucun lien).

Technique

  • Nouvelle colonne registrations.draft_expires_at (date limite générique de réservation) et campaigns.draft_ttl_days (surcharge par campagne). La date limite d'acompte (deposit_expires_at) reste prioritaire quand elle existe, avec repli sur draft_expires_at.
  • Les échéances de réservation et d'acompte sont désormais calées sur la fin de journée (endOfDay()) à la création comme au backfill, cohérent avec un cron quotidien : l'expiration correspond à la date affichée à l'adhérent et lui laisse sa dernière journée entière.
  • Le job registrations:abandon-expired-deposits annule désormais tout Draft expiré (plus seulement les acomptes) ; il n'expire jamais un PendingValidation via draft_expires_at (un paiement enregistré tient la place). Garde-fou : aucune réservation ayant déjà reçu un paiement n'est annulée (total_paid non nul). Il est désormais planifié une fois par jour à 07:00, après helloasso:sync-pending-payments (06:00) au lieu de toutes les heures : la synchronisation réconcilie d'abord les paiements HelloAsso réellement reçus, évitant d'abandonner par erreur une inscription payée mais pas encore synchronisée (sync ignore les statuts terminaux).
  • Comptage de capacité centralisé sur RegistrationStatus::terminalStatuses() (exclut Cancelled/Refused/Abandoned, compte les Draft) avec COUNT(DISTINCT), partagé par le modèle Course, CourseController, Settings/CourseController et CampaignController. Les listes de membres (roster, export CSV) continuent d'exclure les Draft.
  • Nouvelle commande ponctuelle registrations:backfill-draft-deadlines (avec --dry-run) pour poser une échéance sur les Draft existants créés avant ce correctif. À lancer une fois après déploiement.
  • Le balayage d'abandon parcourt les inscriptions avec chunkById (et non each/offset) : comme il fait sortir chaque ligne du jeu de résultats, une pagination par offset sauterait des lignes au-delà de la première page (places retenues à vie) — chunkById les traite toutes.
  • Garde de fraîcheur : si au moins un tenant utilise HelloAsso, l'abandon ne tourne que si helloasso:sync-pending-payments a réussi dans les 6 dernières heures (heartbeat en cache). Sinon il s'abstient (log d'avertissement) pour ne pas abandonner une inscription payée mais pas encore réconciliée si le sync échoue.

0.16.3.0 — 2026-06-03

Ajouts

  • Traçabilité des emails de relance : chaque email de relance envoyé pour un dossier d'inscription est désormais enregistré (paiement HelloAsso rejeté, paiement abandonné, ou acompte expirant), avec le destinataire, la raison, la référence de checkout et la date d'envoi.
  • Historique des relances sur la vue dossier : une carte « Relances envoyées » liste les relances du dossier, avec un badge coloré par type (rejeté, abandonné, acompte) et l'horodatage de chaque envoi.

Technique

  • Nouvelle table registration_relance_emails (une ligne par email) et table pivot vers les dossiers concernés (un email groupé peut couvrir plusieurs dossiers).
  • L'enregistrement des traces est centralisé dans un recorder best-effort (RegistrationRelanceEmail::record()), branché sur le goulot d'envoi HelloAsso et sur la relance d'acompte. Une défaillance de la trace n'interrompt jamais l'envoi du mail.

0.16.2.0 — 2026-06-03

Corrections

  • Webhook HelloAsso Refused tardif : un webhook de refus livré en retard pour un checkout périmé (la référence ayant été écrasée lors d'un retry) ne re-marque plus Rejected un paiement déjà retenté sous une nouvelle référence. resolvePayments() filtre désormais sur la référence du checkout courant (filtre gardé : repli sur la résolution par registration_id si le checkoutIntentId est absent du payload). Évite qu'une inscription reste bloquée en Draft alors que le paiement a abouti sur le nouveau checkout. Contrat data.checkoutIntentId confirmé sur un webhook de production.

0.16.0.0 — 2026-06-02

Ajouts

  • Recherche par adhérent ou payeur sur la liste des inscriptions : tapez un nom, prénom ou email pour filtrer instantanément. La recherche est insensible à la casse et couvre à la fois le membre et le payeur (le cas échéant).
  • Tri par date d'arrivée : un bouton bascule permet d'afficher les inscriptions du plus récent au plus ancien ou inversement.
  • Bouton « Réinitialiser les filtres » : apparaît dès qu'un filtre actif est détecté (campagne, statut, recherche ou tri non-défaut) ; un clic remet tout à zéro en une seule action.
  • États vides distincts : une icône et un message différenciés pour « aucune inscription » (liste vide) et « aucun résultat » (filtres actifs sans correspondance).

Corrections

  • Les liens de pagination conservent désormais tous les filtres actifs (campagne, statut, recherche, tri) lorsqu'on navigue entre les pages.

0.15.0.0 — 2026-06-02

Ajouts

  • Relance paiement HelloAsso : les payeurs dont le paiement HelloAsso est rejeté par la banque ou abandonné depuis plus de 2h reçoivent automatiquement un email de relance avec le récapitulatif de leur inscription et un lien sécurisé pour relancer le paiement (URL signée, valable 7 jours).
  • Route de retry HelloAsso (/register/{token}/helloasso/retry) : crée un nouveau checkout HelloAsso à la volée sans effacer l'inscription existante. Protégée par URL signée temporaire pour prévenir les accès non autorisés.
  • Statuts de paiement HelloAsso dans l'interface admin : les inscriptions affichent désormais "HelloAsso en attente" (badge orange) et "Paiement refusé" (badge rouge) pour distinguer visuellement les checkouts en cours des paiements rejetés par la banque.
  • Détail complet du panier dans l'email de relance : l'email de relance affiche désormais chaque adhérent inscrit avec le détail par rubrique, les réductions appliquées, le montant de l'acompte le cas échéant et sa date d'expiration.
  • Planification nocturne : helloasso:sync-pending-payments s'exécute automatiquement chaque nuit à 06:00 (Europe/Paris) pour rattraper les paiements en attente non notifiés par webhook.
  • Annulation automatique : lorsqu'une inscription passe en statut Abandonné ou Annulé, les paiements HelloAsso Pending/Rejected associés sont automatiquement annulés pour éviter les relances intempestives.

Modifications

  • La commande helloasso:sync-pending-payments envoie maintenant un email de relance lors de la détection d'un paiement rejeté ou d'un checkout abandonné (>2h), avec idempotence garantie par le flag helloasso_relance_sent_at sur les paiements.
  • Le webhook HelloAsso (Refused) envoie immédiatement un email de relance au payeur après avoir marqué le paiement comme rejeté.
  • Les inscriptions en statut terminal (Refusé, Annulé, Abandonné) sont exclues des relances automatiques.
  • La logique d'envoi de l'email de relance est désormais centralisée dans HelloAssoRelanceService pour éviter la duplication entre le webhook et la commande de synchronisation.

Corrections

  • Correction d'un bug de double-email : après un retry, le délai de grâce de 2h pour la relance des abandonnés repart maintenant correctement de la date de modification du paiement (et non de sa création initiale).
  • json_decode sur les notes de paiement corrompues retourne désormais [] au lieu de null, évitant un recalcul incorrect du montant lors d'un retry de dépôt.
  • La route de retry ne supprime plus les inscriptions existantes en cas d'échec de l'API HelloAsso (contrairement au flux initial d'inscription).
  • L'URL de relance (bouton "Finaliser mon paiement") utilisait la racine de l'application au lieu du sous-domaine tenant, provoquant une erreur 404 à la réception de l'email.
  • Ajout du statut Cancelled dans l'enum PaymentStatus (manquant depuis la création, causait une erreur 500 lors de l'annulation d'inscription).

0.14.2.0 — 2026-06-01

Modifications

  • Confirmation des paiements HelloAsso : la logique de confirmation (paiement reçu, transition de statut, envoi d'email) est désormais centralisée dans un service dédié, éliminant trois implémentations divergentes (retour URL, webhook, commande de synchronisation). Les guards d'idempotence sont désormais uniformes sur tous les états terminaux (Received, Rejected, Refunded, Contested).
  • Checkout HelloAsso : les deux blocs de création de paiement dans le formulaire d'inscription (paiement standard et acompte) sont fusionnés en un service unique.

Corrections

  • L'événement RegistrationValidated (déclenchant la création de facture et l'email de confirmation) est maintenant émis après le commit de la transaction de paiement, garantissant que les listeners synchrones opèrent sur des données pleinement persistées.
  • L'idempotence du retour HelloAsso ignorait les états Rejected, Refunded et Contested — désormais alignée avec le webhook.

0.14.1.4 — 2026-06-01

Ajouts

  • Couverture complète des états de paiement HelloAsso : le webhook et la commande helloasso:sync-pending-payments gèrent désormais tous les états HelloAsso — Refused → Rejected, Refunding/Refunded → Remboursé, Contested → Contesté. L'interface affiche les badges correspondants avec les variantes visuelles adaptées.

Corrections

  • Guard d'idempotence webhook étendu : lors d'un retry de webhook Paid, le handler vérifie maintenant tous les états terminaux (Received, Refunded, Contested, Rejected) avant d'écrire — empêchant un webhook retardé d'écraser un remboursement déjà enregistré.
  • Guard d'idempotence sync étendu : helloasso:sync-pending-payments applique le même guard (tous les états terminaux) pour éviter qu'une exécution concurrente avec un webhook ne réécrive un paiement Refunded/Contested avec Received.
  • Filtre méthode de paiement dans resolvePayments() : les paths 1 et 2 de résolution (par registration_ids[] et registration_id) filtraient tous les paiements d'une inscription ; ils filtrent maintenant uniquement ceux de méthode HelloAsso, évitant de marquer des paiements espèces/chèque comme rejetés/remboursés.
  • Compteur --dry-run : le compteur de paiements mis à jour n'était pas incrémenté dans le bon bloc conditionnel pour les checkouts refusés, affichant un compte erroné en mode dry-run.
  • Types TypeScript : Payment.status dans la page inscription inclut désormais 'refunded' et 'contested' ; les badges s'affichent correctement pour ces états.

0.14.1.0 — 2026-05-29

Ajouts

  • Configuration HelloAsso per-tenant (sandbox / production) : chaque tenant peut désormais utiliser un environnement HelloAsso distinct. Un super-admin bascule le mode depuis la fiche Filament via le bouton "Environnement HelloAsso" — un modal avec sélection Radio et confirmation obligatoire ("PRODUCTION") protège contre les clics accidentels. Le badge SANDBOX/PRODUCTION est visible en temps réel dans l'infolist. Le token OAuth2 est automatiquement invalidé lors du changement d'environnement.
  • Bandeau "Mode test HelloAsso" dans le wizard d'inscription public : quand un tenant est en mode sandbox, un bandeau amber persistant ("Aucun paiement réel ne sera encaissé") s'affiche sur toutes les étapes du formulaire, empêchant toute confusion entre paiements réels et tests.