diff --git a/backend/src/modules/mail/mail.service.spec.ts b/backend/src/modules/mail/mail.service.spec.ts index d88d1d8..6d474a6 100644 --- a/backend/src/modules/mail/mail.service.spec.ts +++ b/backend/src/modules/mail/mail.service.spec.ts @@ -98,4 +98,21 @@ describe('MailService (ticket #28)', () => { expect(html).toContain(`https://app.test/reset-password?token=${encodeURIComponent(token)}`); expect(html).not.toContain('create-password'); }); + + it('sendResoumissionPendingEmail — accusé resoumission avec n° dossier', async () => { + await service.sendResoumissionPendingEmail( + 'parent@test.fr', + 'Claire', + 'MARTIN', + '2026-000024', + ); + expect(sendEmailSpy).toHaveBeenCalledTimes(1); + const [to, subject, html] = sendEmailSpy.mock.calls[0]; + expect(to).toBe('parent@test.fr'); + expect(subject).toContain('resoumis'); + expect(subject).toContain('2026-000024'); + expect(html).toContain('Claire'); + expect(html).toContain('2026-000024'); + expect(html).toContain('en attente de validation'); + }); }); diff --git a/backend/src/modules/mail/mail.service.ts b/backend/src/modules/mail/mail.service.ts index 2d96a6e..f063426 100644 --- a/backend/src/modules/mail/mail.service.ts +++ b/backend/src/modules/mail/mail.service.ts @@ -301,6 +301,43 @@ export class MailService { await this.sendEmail(to, subject, html); } + /** + * Accusé de resoumission après refus (reprise #112) : dossier à nouveau en attente de validation. + */ + async sendResoumissionPendingEmail( + to: string, + prenom: string, + nom: string, + numeroDossier: string, + ): Promise { + const appName = this.configService.get('app_name', "P'titsPas"); + const appUrl = this.configService.get('app_url', 'https://app.ptits-pas.fr'); + + const safe = (s: string) => + (s || '') + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + + const subject = `Votre dossier a été resoumis — ${safe(numeroDossier)}`; + const html = ` +
+

Bonjour ${safe(prenom)} ${safe(nom)},

+

Nous avons bien reçu la resoumission de votre dossier sur ${safe(appName)}.

+

Numéro de dossier : ${safe(numeroDossier)}

+

Votre dossier est de nouveau en attente de validation par notre équipe. Vous recevrez un email lorsqu'il aura été traité.

+
+ Accéder au site +
+
+

Cet email a été envoyé automatiquement. Merci de ne pas y répondre.

+
+ `; + + await this.sendEmail(to, subject, html); + } + /** * Email de refus de dossier avec lien reprise (token). * Ticket #110 – Refus sans suppression diff --git a/backend/src/routes/auth/auth.service.ts b/backend/src/routes/auth/auth.service.ts index 9a42ae9..f78a06e 100644 --- a/backend/src/routes/auth/auth.service.ts +++ b/backend/src/routes/auth/auth.service.ts @@ -1004,7 +1004,7 @@ export class AuthService { dto.texte_motivation?.trim() ?? dto.presentation_dossier?.trim(); - return this.usersRepo.manager.transaction(async (manager) => { + const result = await this.usersRepo.manager.transaction(async (manager) => { const familyUsers = titulaire.numero_dossier ? await manager.find(Users, { where: { @@ -1098,8 +1098,29 @@ export class AuthService { statut: StatutUtilisateurType.EN_ATTENTE, user_id: titulaireLive.id, numero_dossier: titulaireLive.numero_dossier, + parentsPourEmail: familyUsers, }; }); + + const numeroDossier = result.numero_dossier ?? ''; + try { + for (const parent of result.parentsPourEmail) { + await this.mailService.sendResoumissionPendingEmail( + parent.email, + parent.prenom ?? '', + parent.nom ?? '', + numeroDossier, + ); + } + } catch (err) { + this.logger.error( + "[resoumettreRepriseParent] Échec envoi email accusé resoumission (dossier conservé)", + err instanceof Error ? err.stack : String(err), + ); + } + + const { parentsPourEmail: _parentsPourEmail, ...response } = result; + return response; } private async resoumettreRepriseAM( diff --git a/docs/tmp/112-back-reprise-alignement-front.md b/docs/tmp/112-back-reprise-alignement-front.md index d15350b..65eff55 100644 --- a/docs/tmp/112-back-reprise-alignement-front.md +++ b/docs/tmp/112-back-reprise-alignement-front.md @@ -179,6 +179,15 @@ Code HTTP : **200** (pas de corps `Users` brut comme l’ancien back). - **Parent :** tous les users `role=parent` avec le même `numero_dossier` passent en `en_attente` ; `token_reprise` invalidé sur **tous** (symétrique refus #110). - **AM :** un seul user. +### E-mail accusé resoumission (parent) + +Après `PATCH` réussi, un e-mail est envoyé à **chaque parent** du dossier (`sendResoumissionPendingEmail`) : +- confirmation de resoumission ; +- rappel du **numéro de dossier** ; +- mention « en attente de validation ». + +Échec SMTP : logué, **ne bloque pas** la resoumission (même règle que l'inscription initiale). + --- ## 4. Fichiers front à modifier (checklist)