Changes for docs/inscription-etudiant-atelier.md: 176 added lines, 0 removed lines.
Original line number
Diff line number
Diff line
# Inscription d’un·e étudiant·e à un atelier
## 1. Objectif et périmètre
Cette page décrit le parcours de l’application `Atelier` : une inscription associe un·e `Etudiant` à une `Session`, c’est-à-dire une occurrence datée et capacitaire d’un `Atelier`. La demande déposée par l’étudiant·e est persistée avec l’état `INSCRIPTION_DEMANDE` ; elle n’est donc pas encore une inscription validée ni affectée à une liste.
Il ne s’agit pas du parcours d’inscription au module `Evenementiel` (`Evenementiel\Entity\Db\Evenement` et `Evenementiel\Entity\Db\Inscription`). Le contrôleur décrit ici est `Atelier\Controller\SessionController`.
## 2. Vue d’ensemble du processus
Le parcours étudiant part de **« À un atelier »**, qui affiche les sessions dont l’état actif est « inscription ouverte ». Pour chaque session, l’interface masque l’action si l’étudiant·e a déjà une inscription active ; elle masque aussi le bouton si les listes principale et complémentaire sont toutes deux complètes. Le formulaire de demande recueille une motivation et des informations facultatives.
À la soumission, le serveur vérifie l’état de complétude, valide le formulaire et refuse un doublon actif. Il crée ensuite l’entité `Inscription`, lui ajoute l’état `INSCRIPTION_DEMANDE`, puis déclenche un courriel de demande à l’étudiant·e, avec l’adresse de service configurée en copie. Aucun classement automatique sur liste n’est exécuté dans ce parcours.
Une inscription créée directement par un gestionnaire suit un autre chemin : elle est marquée validée dès sa création puis classée sur la liste principale ou complémentaire si une place est disponible.
## 3. Point d’entrée et routes
Les routes sont déclarées sous le préfixe `/atelier` dans [atelier.config.php](../module/Atelier/config/merged/atelier.config.php#L95).
| Route nommée | Chemin HTTP | Action et usage |
| --- | --- | --- |
| `atelier/inscription/auto-inscription` | `GET /atelier/inscription/auto-inscription` | [InscriptionController::autoInscriptionAction](../module/Atelier/src/Controller/InscriptionController.php#L402) affiche les sessions ouvertes de l’étudiant·e connecté·e. L’accès requiert `INSCRIPTION_AUTOINSCRIPTION` ([inscription.config.php](../module/Atelier/config/merged/inscription.config.php#L98)). |
| `atelier/session/inscription-etudiant` | `GET/POST /atelier/session/inscription-etudiant/:session/:etudiant` | [SessionController::inscriptionEtudiantAction](../module/Atelier/src/Controller/SessionController.php#L461) affiche puis traite la demande. Le chemin est défini dans [session.config.php](../module/Atelier/config/merged/session.config.php#L384). |
| `atelier/session/inscription-gestionnaire` | `GET/POST /atelier/session/inscription-gestionnaire/:session` | [SessionController::inscriptionGestionnaireAction](../module/Atelier/src/Controller/SessionController.php#L421) crée une inscription directe par un gestionnaire ; route déclarée dans [session.config.php](../module/Atelier/config/merged/session.config.php#L374). |
| `atelier/inscription/ajouter` | `GET/POST /atelier/inscription/ajouter[/:session]` | [InscriptionController::ajouterAction](../module/Atelier/src/Controller/InscriptionController.php#L76), ajout générique côté gestionnaire ; route déclarée dans [inscription.config.php](../module/Atelier/config/merged/inscription.config.php#L160). |
| `atelier/inscription/valider` | `GET/POST /atelier/inscription/valider/:inscription` | [InscriptionController::validerAction](../module/Atelier/src/Controller/InscriptionController.php#L199), décision ultérieure sur une demande déjà créée. |
Le bouton « Je m’inscris » est généré par [View/Helper/partial/inscription.phtml](../module/Atelier/src/View/Helper/partial/inscription.phtml#L69) et ouvre le formulaire dans une modale. La route `auto-inscription` est également celle exposée dans la navigation « S’inscrire » et « Mes ateliers » ([inscription.config.php](../module/Atelier/config/merged/inscription.config.php#L363)).
## 4. Parcours détaillé de l’inscription
### 4.1 Affichage des sessions éligibles
1.`InscriptionController::autoInscriptionAction()` appelle `EtudiantService::getConnectedEtudiant()` et `SessionService::getSessionsByEtat(SessionEtats::ETAT_INSCRIPTION_OUVERTE)`.
2.`getConnectedEtudiant()` retrouve l’étudiant·e à partir du nom d’utilisateur connecté, via `EtudiantService::getEtudiantByLogin()` ([EtudiantService.php](../module/Etudiant/src/Service/Etudiant/EtudiantService.php#L128)).
3. La vue [auto-inscription.phtml](../module/Atelier/view/atelier/inscription/auto-inscription.phtml#L27) appelle `Session::getInscription($etudiant)`. Si une inscription non historisée existe, elle n’affiche pas de nouvelle action ; sinon elle construit un objet temporaire et le transmet au helper d’affichage.
4. Le helper vérifie le privilège de session et `Session::isComplete()`. Si l’étudiant·e peut s’inscrire et qu’une capacité reste disponible, il affiche le lien AJAX vers la route `atelier/session/inscription-etudiant` ([partial/inscription.phtml](../module/Atelier/src/View/Helper/partial/inscription.phtml#L69)).
`SessionService::getSessionsByEtat()` filtre sur le code d’état actif ouvert ([SessionService.php](../module/Atelier/src/Service/Session/SessionService.php#L97)). Le booléen `Session::autoInscription` existe dans le modèle, mais cette requête et les contrôles du parcours ci-dessus ne le consultent pas.
### 4.2 Ouverture et soumission du formulaire étudiant
`SessionController::inscriptionEtudiantAction()` charge la session et l’étudiant·e indiqués dans la route avec `SessionService::getRequestedSession()` et `EtudiantService::getRequestedEtudiant()`. Il crée une nouvelle `Inscription`, la rattache à la session et à l’étudiant·e, et positionne l’étape de formulaire `ETAPE_ETUDIANT`.
Le formulaire est un `JustificationForm`, configuré pour ce contrôleur par `SessionControllerFactory`. À l’affichage, l’action rend le template `default/default-form`. Lors d’un POST :
1. Le contrôleur vérifie d’abord `Session::isComplete()`.
2. Il transmet les données à `JustificationForm::isValid()`.
3. Il retire les espaces de la motivation et refuse une valeur vide.
4. Il appelle `Session::hasEtudiant()` pour empêcher une seconde inscription active du même objet `Etudiant` à cette session.
5. Si les contrôles passent, il crée l’inscription, lui ajoute l’état de demande et déclenche la notification décrite à la section 10.
Après un POST, cette action termine par `exit()` ; elle ne retourne pas de redirection HTTP explicite. La vue de la page d’auto-inscription écoute l’événement `modification`, ferme la modale et recharge la page ; elle affiche alors les messages FlashMessenger ([auto-inscription.phtml](../module/Atelier/view/atelier/inscription/auto-inscription.phtml#L25)).
### 4.3 Traitement ultérieur d’une demande
La demande étudiante reste à l’état `INSCRIPTION_DEMANDE` sans liste. Un gestionnaire peut la traiter via `InscriptionController::validerAction()` : ce formulaire exige également une motivation, puis l’action passe l’état à `INSCRIPTION_VALIDER` et met à jour l’entité. L’appel à `NotificationService::triggerValidation()` est commenté dans cette action. Le classement sur une liste est un traitement distinct (`InscriptionController::classerAction()`), et n’est pas déclenché par le dépôt de la demande.
## 5. Contrôleurs et formulaires
### Parcours étudiant
-**Contrôleur d’entrée de la liste :**`Atelier\Controller\InscriptionController::autoInscriptionAction()`.
-**Contrôleur de la demande :**`Atelier\Controller\SessionController::inscriptionEtudiantAction()`.
-**Formulaire :**`Atelier\Form\Justification\JustificationForm`, construit par `JustificationFormFactory`. Il contient :
-`motivation` (zone de texte, `required => true`) ;
-`internationnal` (case à cocher facultative ; orthographe du nom de champ dans le code) ;
-`telephone` (champ texte facultatif).
-**Hydratation :**`JustificationHydrator` choisit le champ de justification selon `Inscription::getEtape()`. Pour `ETAPE_ETUDIANT`, il affecte la motivation à `justificationEtudiant`, ainsi que les valeurs facultatives international et téléphone ([JustificationHydrator.php](../module/Atelier/src/Form/Justification/JustificationHydrator.php#L30)).
-**Validation :** outre le filtre du formulaire, le contrôleur effectue une vérification explicite après `trim()` ; une motivation vide provoque un message d’erreur.
La session et l’étudiant·e ne sont pas sélectionnés dans ce formulaire étudiant : ils sont chargés à partir des paramètres de route, puis affectés à l’objet `Inscription`.
### Parcours gestionnaire
`SessionController::inscriptionGestionnaireAction()` utilise `Atelier\Form\Inscription\InscriptionForm`, construit par `InscriptionFormFactory` et hydraté par `InscriptionHydrator`. Il propose des champs de recherche/sélection pour la session et l’étudiant·e, tous deux obligatoires, ainsi que les champs facultatifs `internationnal` et `telephone`. Les sources d’autocomplétion sont `atelier/session/rechercher` et `etudiant/rechercher`.
L’action initialise la session depuis la route et met l’étape à `ETAPE_VALIDATION`, puis traite le POST avec le formulaire. Un étudiant·e doit être résolu pour que la création ait lieu. L’action générique `InscriptionController::ajouterAction()` utilise aussi `InscriptionForm`, mais n’effectue pas ensuite le classement automatique décrit à la section 6.
## 6. Services et logique métier
| Composant | Rôle dans le parcours |
| --- | --- |
| `EtudiantService` | Résout l’étudiant·e connecté·e pour la liste ; l’action de demande résout l’identifiant étudiant de la route. |
| `SessionService` | Charge la session, liste les sessions à l’état ouvert et, pour le parcours gestionnaire, classe l’inscription selon les places disponibles. |
| `InscriptionService` | `create()` persiste et flush l’entité ; `update()` flush ses modifications ([InscriptionService.php](../module/Atelier/src/Service/Inscription/InscriptionService.php#L23)). |
| `EtatInstanceService` | `setEtatActif()` ajoute l’état demandé ou validé à l’inscription ; par défaut cette méthode persiste et flush l’élément ([EtatInstanceService.php](../vendor/unicaen/etat/src/UnicaenEtat/Service/EtatInstance/EtatInstanceService.php#L189)). |
| `NotificationService` | Après une demande étudiant, rend le modèle `MailTemplates::INSCRIPTION_DEMANDE` et appelle le service de mail. Après classement gestionnaire, rend et envoie le modèle de liste correspondant. |
`SessionAssertion::computeAssertion()` exige le privilège correspondant et un état actif. Pour l’inscription étudiant, cet état doit être `ETAT_INSCRIPTION_OUVERTE` ; pour le gestionnaire, les états terminaux de `SessionEtats::TERMINE` sont refusés ([SessionAssertion.php](../module/Atelier/src/Assertion/SessionAssertion.php#L22)). La route d’inscription étudiant est aussi gardée par `SESSION_INSCRIPTION_ETUDIANT` dans `session.config.php`.
## 7. Entités et persistance
-`Atelier\Entity\Db\Inscription` est l’inscription persistée. Son mapping XML la relie par `ManyToOne` à `Etudiant` et à `Session`, et définit notamment la liste, la motivation étudiant, la justification de validation, le téléphone, l’indicateur international, l’historisation et la relation aux présences ([Atelier.Entity.Db.Inscription.dcm.xml](../module/Atelier/src/Entity/Db/Mapping/Atelier.Entity.Db.Inscription.dcm.xml#L3)).
-`Session` est persistée dans `atelier_session`, liée à `Atelier`, et expose une collection d’inscriptions ; elle porte aussi les capacités principale et complémentaire ([Atelier.Entity.Db.Session.dcm.xml](../module/Atelier/src/Entity/Db/Mapping/Atelier.Entity.Db.Session.dcm.xml#L3)).
-`Etudiant` est l’entité rattachée à l’inscription. La page étudiant déduit l’étudiant·e connecté·e depuis le login utilisateur ; l’inscription conserve une relation vers cette entité, pas directement vers l’utilisateur.
- Les états d’inscription sont associés par la table de jointure `atelier_inscription_etat` vers `UnicaenEtat\Entity\Db\EtatInstance`. Les états de session utilisent la table `atelier_session_etat`.
- Le champ PHP `Inscription::$etape` pilote le comportement de `JustificationHydrator`, mais il n’est pas déclaré comme champ persistant dans le mapping XML. Il ne faut pas le confondre avec l’état métier `INSCRIPTION_DEMANDE` / `INSCRIPTION_VALIDER`, qui est géré par `HasEtats`.
Sur le parcours étudiant, `InscriptionService::create()` exécute `persist()` puis `flush()`. `EtatInstanceService::setEtatActif()` persiste et flush ensuite l’état, puis le contrôleur appelle encore `InscriptionService::update()`. Aucun `beginTransaction()`, `commit()` ou `rollback()` explicite n’englobe la création, le changement d’état et le courriel dans les méthodes de ce parcours : ce sont des étapes/flush distincts, pas une transaction métier unique visible dans le code.
## 8. Règles de gestion et contrôles
| Contrôle | Comportement constaté |
| --- | --- |
| Session proposée | L’écran étudiant ne récupère que les sessions à l’état actif `ETAT_INSCRIPTION_OUVERTE`. L’assertion contrôle également ce statut à l’accès à l’action. |
| Capacités | `Session::isComplete()` ne renvoie vrai que si les listes principale **et** complémentaire sont toutes deux complètes. Ces listes comptent les inscriptions non historisées affectées à la liste correspondante. Une demande non classée, dont `liste` est nulle, ne consomme donc pas de place. |
| Doublon étudiant/session | L’interface masque le bouton si `Session::getInscription($etudiant)` trouve une inscription non historisée. Le POST recontrôle côté serveur avec `Session::hasEtudiant($etudiant)`. Une inscription historisée ne bloque pas une nouvelle demande. |
| Étudiant | La page de liste calcule l’étudiant·e connecté·e avec `getConnectedEtudiant()`. En revanche, la route de soumission porte aussi `:etudiant` et `inscriptionEtudiantAction()` le charge via `getRequestedEtudiant()` ; ni cette action ni `SessionAssertion` ne comparent explicitement cet étudiant·e demandé à l’identité connectée. Le contrôle observable est donc le privilège d’accès à la route, tandis que le lien de l’interface transmet l’identifiant de l’étudiant·e de la page. |
| Motivation | Champ requis dans `JustificationForm`, puis contrôle serveur après `trim()`. Une chaîne vide est refusée même si les données ont satisfait le formulaire. |
| Session complète | Le bouton n’est pas rendu si les deux listes sont complètes. Le contrôleur refait le contrôle et ajoute « Session complète » avant de terminer l’action. |
| Gestionnaire | L’assertion exige `SESSION_INSCRIPTION_GESTIONNAIRE` et refuse une session terminée ; elle n’impose pas que les inscriptions soient ouvertes. L’action de création gestionnaire n’appelle pas `hasEtudiant()`, donc le contrôle anti-doublon étudiant du parcours autonome n’est pas répété ici. |
| Atelier / événement | L’inscription est à une `Session`, liée à un `Atelier`. Le parcours ne charge ni ne contrôle séparément une entité `Evenementiel\Evenement` ; aucun contrôle de ce type n’a été observé dans cette chaîne d’appels. |
Le mapping d’inscription consulté ne déclare pas de contrainte d’unicité sur le couple session/étudiant ; le contrôle de doublon décrit ci-dessus est applicatif.
## 9. Gestion des erreurs et cas particuliers
-**Motivation absente ou blanche :** le contrôleur ajoute un message FlashMessenger d’échec et ne crée pas l’inscription.
-**Doublon actif :** le contrôleur ajoute « Échec de l'inscription : vous êtes déjà inscrit·e. » et ne crée rien.
-**Session complète :** message « Session complète » ; la demande n’est pas créée.
-**Formulaire invalide :** le formulaire est vérifié, mais le contrôleur n’ajoute pas de message Flash explicite lorsque `isValid()` renvoie faux ; après le POST, il termine néanmoins par `exit()`.
-**Inscription historisée :** elle ne bloque ni l’action de l’interface ni le contrôle serveur de doublon.
-**Échec de notification :**`NotificationService::getMailForService()` lève une `RuntimeException` si le paramètre d’adresse de service est absent ou vide ; `triggerInscriptionEtudiant()` lève aussi une `RuntimeException` si l’étudiant·e n’a pas d’adresse courriel. Aucun rattrapage de ces exceptions ni rollback métier global n’est visible dans le parcours ; la persistance antérieure n’est donc pas annulée par une transaction englobante du contrôleur.
-**Identifiant étudiant :** l’interface utilise l’étudiant·e issu de la session connectée, mais l’action charge l’identifiant reçu dans l’URL. Voir le contrôle d’identité de la section 8 ; aucune égalité explicite n’est vérifiée dans ces méthodes.
## 10. Traitements déclenchés après inscription
### Demande déposée par l’étudiant·e
Après persistance de la demande, `SessionController::inscriptionEtudiantAction()` appelle `NotificationService::triggerInscriptionEtudiant()`. Cette méthode prépare les variables `etudiant`, `session` et `inscription`, rend le template `MailTemplates::INSCRIPTION_DEMANDE`, puis envoie le message à l’adresse de l’étudiant·e (`getMailInscrit()`) avec l’adresse de service trouvée via `GlobalParametres::EMAIL_SERVICE` en copie (`sendMail()` reçoit cette adresse dans son paramètre `copie`). Le message est ensuite mis à jour avec ses mots-clés ([NotificationService.php](../module/Atelier/src/Service/Notification/NotificationService.php#L144)).
Le contrôleur ajoute aussi le flash de succès « Demande d'inscription faite. ». La page d’auto-inscription l’affiche après son rechargement. Si l’étudiant·e n’a pas d’adresse courriel, la notification échoue explicitement après la persistance de la demande. L’action de dépôt ne classe pas la demande et n’appelle pas de notification de validation.
### Inscription créée ou classée par un gestionnaire
`SessionController::inscriptionGestionnaireAction()` crée l’inscription à l’état `INSCRIPTION_VALIDER`, renseigne automatiquement la justification de validation avec le nom du gestionnaire, puis appelle `SessionService::classerInscription()`. La méthode affecte d’abord la liste principale si une place y est disponible, sinon la liste complémentaire si elle a une place. Elle flush le classement et appelle alors `triggerListePrincipale()` ou `triggerListeComplementaire()`, destinés à l’adresse de l’étudiant·e. Si les deux capacités sont atteintes, l’inscription reste sans liste et aucun de ces courriels n’est déclenché ([SessionService.php](../module/Atelier/src/Service/Session/SessionService.php#L340)).
Le chemin générique `InscriptionController::ajouterAction()` crée et valide aussi l’inscription, mais ne déclenche ni `classerInscription()` ni notification dans cette action. De même, dans `validerAction()`, l’appel à `triggerValidation()` est commenté ; aucun envoi n’est donc déclenché par cette méthode telle qu’écrite.
if($email===null)thrownewRuntimeException("Aucune valeur pour le paramètre [".GlobalParametres::TYPE."|".GlobalParametres::EMAIL_SERVICE."]");
return$email;
if($email===null||trim($email)==='')thrownewRuntimeException("Aucune valeur pour le paramètre [".GlobalParametres::TYPE."|".GlobalParametres::EMAIL_SERVICE."]");
returntrim($email);
}
/** Tableau de variables **********************************************************************************/