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 21 à 30 sur 93

0.21.0.0 — 2026-07-26

Ajouts

  • Alerte dashboard « campagnes bientôt clôturées » : le tableau de bord affiche désormais les campagnes d'inscription ouvertes dont la clôture programmée arrive dans les 7 jours, pour éviter de découvrir une fermeture automatique après coup.
  • Page de clôture dédiée pour les inscriptions publiques (/register/{token}/closed) : quand une campagne se ferme ou qu'un lien d'inscription expire, l'adhérent est redirigé vers une page qui explique clairement pourquoi, au lieu d'un message d'erreur générique.

Modifications

  • Message d'erreur convivial à la clôture d'une campagne : le motif de fermeture (campagne close vs lien expiré) est désormais dérivé côté serveur (CampaignPublicToken::closedReason()) plutôt que déclaré par le client, ce qui empêche un motif erroné ou falsifié d'être affiché. En cas de cumul des deux causes, le message priorise le lien expiré.
  • L'endpoint d'estimation tarifaire (/register/{token}/estimate) répond désormais systématiquement en JSON 410 quand la campagne se ferme en cours de saisie, au lieu de rediriger vers une page HTML.

0.20.0.2 — 2026-07-20

Ajouts

  • Preuve d'acceptation des conditions à l'inscription sur invitation (users.terms_accepted_at) : la case « J'accepte les conditions d'utilisation » était validée côté serveur puis jetée — aucune trace de l'acceptation n'était conservée, la rendant inopposable (art. 7 RGPD). L'horodatage est désormais persisté sur les 3 parcours de création de compte (employé, membre lié, nouveau membre) ; laissé null pour les employés, pour qui la case n'est pas affichée. Aucun backfill des comptes existants : leur consentement n'a jamais été recueilli de façon prouvable, un backfill fabriquerait une fausse preuve.

Corrections

  • Logs d'audit d'impersonation en UUID plutôt qu'en emails : admin_email/user_email ne sont plus écrits dans les logs start/stop (ImpersonationService), qui partent en prod vers syslog Clever Cloud et Laravel Nightwatch. admin_id/user_id/tenant_id suffisent à la traçabilité ; IP et user-agent restent journalisés.
  • Suppression de Gravatar (fuite d'email vers un tiers non déclaré) : l'email de l'utilisateur partait en clair dans l'URL Gravatar vers Automattic (États-Unis) à chaque affichage du header, sans figurer au registre des sous-traitants. L'avatar ne se résolvait de toute façon jamais (URL construite sans hash MD5/SHA-256) — le fallback initiales, déjà le rendu effectif, devient le rendu unique. gravatar.com retiré de la CSP img-src.
  • Documentation de SESSION_SECURE_COOKIE : la variable était absente de .env.example, exposant au risque qu'un déploiement prod tourne sans le flag Secure sur le cookie de session (atténué par HSTS, mais à fermer proprement). Ajoutée à .env.example et à Docs/Deployment/Environment.md. Action de suivi en production : clever env set SESSION_SECURE_COOKIE true.

0.19.1.0 — 2026-07-06

Modifications

  • Découpage du contrôleur des inscriptions par ressource HTTP : le RegistrationController monolithique (2 170 lignes, 27 actions) est scindé en 6 contrôleurs dédiés — CRUD inscriptions, transitions de statut (valider/refuser/annuler), gestion de cours (ajout/retrait/échange + aperçus), panier (détail, paiement, facture PDF, membres, notes), relances et export CSV. Aucun changement de comportement : URLs, noms de routes et autorisations strictement identiques (déplacements verbatim vérifiés mécaniquement).
  • Les aides partagées (filtres de liste, représentant de panier, options de campagne, noms de cours) deviennent des traits réutilisables Concerns/, et la liste des raisons comptant comme « relance manuelle » est centralisée sur l'énum RelanceReason::MANUAL_REASONS (valeurs inchangées).

Ajouts

  • Test unitaire de l'énum RelanceReason : libellés et contenu exact de MANUAL_REASONS désormais verrouillés.

0.19.0.1 — 2026-07-03

Corrections

  • Mail « Paiement reçu » reflète le solde du panier, pas la ventilation par inscription : PaymentReceivedMail interrogeait le solde de la seule inscription réglée, qui peut diverger du solde réel du panier après une re-ventilation des paiements. Le mail agrège désormais toutes les inscriptions non terminales du panier et affiche le solde VersementService::cartOutstandingCents — élimine les mails annonçant un reste à payer alors que le panier est soldé.

0.19.0.0 — 2026-07-03

Ajouts

  • Lien de paiement en ligne pour panier offline impayé : depuis le détail panier, un bouton du panneau de relance génère un lien de paiement HelloAsso pour un panier initialement réglé hors ligne (chèque, virement, espèces) qui reste impayé — sans obliger l'adhérent à repasser par tout le formulaire. Guardé par token public valide, solde panier > 0 relu sous verrou, et HelloAsso configuré pour le tenant (le bouton est masqué sinon).
  • Verrou de checkout HelloAsso anti double-clic/double-onglet : Cache::lock par panier (30s, attente 5s) sérialise les checkouts concurrents sur le même jeu d'inscriptions ; le solde est relu sous verrou juste avant chaque appel HelloAsso, avec dégradation gracieuse (HelloAssoAlreadySettledException) si un règlement concurrent a déjà tout soldé. Les appels HTTP HelloAsso sont bornés à 10s pour ne jamais dépasser la durée du verrou.

Corrections

  • Le montant réclamé par la relance et le checkout HelloAsso se base désormais sur le solde du panier (VersementService::cartOutstandingCents), plus sur la ventilation par inscription — élimine les relances erronées après une re-ventilation des paiements à l'annulation.
  • Élimination de plusieurs N+1 (registration.payments, registration.balance) dans HelloAssoRelanceService et le flux de retry HelloAsso.
  • Exclusion des paiements HelloAsso fantômes de la relance panier : payableCartPayments() (et canSendManualRelance(), createOnlinePaymentLink()) retournent vide dès que le panier n'a plus de solde dû au niveau versement (VersementService::cartOutstandingCents), même si un Payment Pending/Rejected résiduel survit sur une inscription réglée hors ligne entre-temps ; garde sur destinataire absent ; resynchronisation de Payment.amount au moment du checkout.

0.18.3.0 — 2026-07-01

Ajouts

  • Refactoring MemberController::show() → CQRS read model (GetMemberDetailQuery) : la méthode show() passe de 263 lignes à ~12 lignes en déléguant à une query orchestratrice qui coordonne 7 assembleurs, chacun produisant un DTO final readonly typé. Les DTOs couvrent : PersonSectionData, MemberSectionData, UsagerSectionData, LegalGuardianData, PayeurSectionData, RegistrationSummaryData, MeetingActionData.
  • Limitation des inscriptions à 50 + hasMoreRegistrations : la query charge 51 inscriptions (LIMIT+1), n'en retourne que 50, et expose le flag hasMoreRegistrations pour signaler qu'il y en a davantage — sans pagination complète.
  • Migration index composite (tenant_id, responsible_user_id, due_date) sur meeting_actions : élimine le filesort sur ORDER BY due_date LIMIT 50 dans MeetingActionsAssembler.

Corrections

  • tenant_id ajouté aux selects contraints sur legalGuardians.person.adherent et payeurs.person.adherent (évite un null silencieux si $adherent->tenant_id est accédé ultérieurement).
  • toISOString() dans MeetingActionsAssembler (cohérence avec les autres assembleurs qui utilisaient déjà cette forme).
  • MemberDetailResponse::$member déclaré non-nullable (toujours valorisé par memberAssembler->assemble()).

Tests

  • Test 403 sur members.show sans permission (couverture manquante sur l'accès à la fiche membre).
  • Test LIMIT+1 (hasMoreRegistrations) avec 51 campagnes distinctes pour contourner la contrainte unique (member_id, campaign_id).
  • Test ModelNotFoundException sur un member_id inexistant.
  • Test isolation cross-tenant sur MeetingActionsAssembler : vérifie que le scope BelongsToTenant filtre les actions d'un autre tenant même si responsible_user_id est identique.
  • Test positif MeetingActionsAssembler avec fixture MeetingAction réelle (l'ancien test ne créait aucune action).

0.18.2.0 — 2026-06-30

Ajouts

  • Visibilité publique des moyens de paiement (is_public) : nouveau flag indépendant de is_enabled sur TenantPaymentMethod. Permet de configurer un moyen de paiement disponible uniquement en back-office (ex. CB physique, virement interne) sans l'exposer sur le formulaire public d'inscription.
  • Toggle « Proposé au public » dans les paramètres moyens de paiement : second interrupteur par méthode (désactivé si la méthode est désactivée). Désactiver une méthode force automatiquement is_public = false côté serveur.
  • Méthodes custom dans la saisie de versement back-office : les méthodes custom activées (payment_method IS NULL) sont désormais proposées dans le dialog « Enregistrer un versement », avec validation du custom_payment_method_id.
  • Scope scopePublic() sur TenantPaymentMethod : filtre is_enabled = true AND is_public = true, utilisé par le formulaire public d'inscription.

Modifications

  • Le formulaire public d'inscription filtre désormais les moyens de paiement via scopePublic() (au lieu de scopeEnabled()) — seules les méthodes activées et marquées publiques sont proposées.

0.18.1.0 — 2026-06-29

Ajouts

  • Formulaire pleine page « Ajouter un inscrit » (AddCartMember.vue) : parcours guidé en 3 étapes (Identité → Activités → Récapitulatif) pour ajouter un membre à un panier depuis le back-office. Supporte la sélection d'un membre existant ou la création inline d'un nouveau membre (avec tuteur légal pour les mineurs). Les membres déjà actifs dans le panier sont automatiquement exclus de la liste.
  • Test d'isolation cross-tenant sur CartMutationControllerTest : vérifie que le scope BelongsToTenant rend un panier invisible hors de son tenant (couche Eloquent).

Modifications

  • MemberForm : nouveau mode simplifié (simplified) pour les contextes d'inscription back-office — masque les champs non nécessaires (photo, bio, nationalité…) et réduit le formulaire aux données essentielles.

Corrections

  • Performance : requête availableMembers dans buildPanierPayload — ajout de ->with('person') (évite N+1 sur l'accesseur first_name/last_name) et ->limit(200) (borne la requête pour les tenants avec de nombreux membres).

0.18.0.0 — 2026-06-29

Ajouts

  • Gérer le panier — CartMutationService : trois nouvelles opérations atomiques sur le panier, chacune sous verrou lockForUpdate global sur toutes les inscriptions du panier :
    • Ajouter un inscrit : sélection d'un membre existant (liste filtrée — inscrits actifs exclus) ou création inline d'un nouveau membre, avec choix du/des cours. Les tarifs multi-membres sont recalculés pour l'ensemble du panier à chaque ajout. La facture de chaque inscription déjà facturée est re-versionnée.
    • Déplacer un cours : transfert atomique d'un cours d'un inscrit vers un autre inscrit du même panier. Les deux inscriptions sont recalculées et leur facture re-versionnée dans la même transaction.
    • Supprimer le panier : annulation de toutes les inscriptions non-terminales avec remise à zéro des montants. Bloqué si des versements encaissés existent (atomique — vérification sous verrou).
  • Dialogs back-office sur la page « Détail du panier » : trois dialogs dédiés (Ajouter un inscrit, Déplacer un cours, Supprimer le panier) accessibles via un menu « Gérer le panier ». Les actions destructives nécessitent une confirmation explicite.
  • Sécurité — isolation tenant renforcée : les propositions sont désormais scopées explicitement au tenant du gestionnaire (via rubriques.tenant_id) dans les Form Requests et dans le contrôleur, en complément de la vérification campaign_id existante dans le service.

Corrections

  • Sécurité : la vérification de versements encaissés dans deleteCart est désormais effectuée à l'intérieur de la transaction sous lockForUpdate, éliminant une fenêtre de race condition entre la vérification et l'annulation des inscriptions.

0.16.33.0 — 2026-06-26

Ajouts

  • Versioning immuable du panier (cart snapshot). Chaque validation d'inscription et chaque mutation de cours (ajout, retrait, échange) crée désormais un snapshot versionné du panier, archivant les tarifs, remises et composition à l'instant t. La facture PDF lit systématiquement depuis ce snapshot ; le contenu de la facture est ainsi figé même si les inscriptions évoluent ultérieurement.
  • Annulation avec recalcul automatique. L'annulation d'une inscription réindexe les member_index des inscrits restants et recalcule leurs tarifs (remises multi-membres incluses) de manière atomique. Un nouveau snapshot est créé après l'annulation pour refléter l'état final du panier.

Modifications

  • La facture PDF d'un panier (/facture-pdf) affiche désormais les inscriptions annulées de façon distincte (montant à 0, lignes vides) et les exclut du total et des remises affichés.
  • Le total du panier et les remises dans le back-office excluent les inscriptions terminales (annulées, refusées) — seules les inscriptions actives contribuent aux montants affichés.
  • Le statut de paiement "Encaissé" est renommé "Reçu" dans l'interface.

Corrections

  • La route /facture-pdf ne plante plus avec function max(uuid) does not exist sur PostgreSQL (la relation latestSnapshot utilisait ofMany() qui génère un MAX(id) incompatible avec les UUID).