From c5e8929305faae5b387a6a47937a8c94f01737ff Mon Sep 17 00:00:00 2001 From: C2Chandelier Date: Wed, 15 Jan 2025 12:40:38 +0100 Subject: [PATCH 1/3] first --- api/src/referent/referentController.ts | 67 +++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/api/src/referent/referentController.ts b/api/src/referent/referentController.ts index a057b95822..cdda5308fa 100644 --- a/api/src/referent/referentController.ts +++ b/api/src/referent/referentController.ts @@ -107,8 +107,9 @@ import { getDepartmentForInscriptionGoal, isAdmin, isReferentReg, - isReferentDep, canValidateYoungToLP, + WITHRAWN_REASONS, + formatDateFRTimezoneUTC, } from "snu-lib"; import { getFilteredSessions, getAllSessions, getFilteredSessionsForCLE } from "../utils/cohort"; import scanFile from "../utils/virusScanner"; @@ -675,6 +676,12 @@ router.put("/young/:id", passport.authenticate("referent", { session: false, fai } } + if (newYoung.status === YOUNG_STATUS.WITHDRAWN && young.status !== YOUNG_STATUS.WITHDRAWN) { + const { withdrawnReason } = newYoung; + await handleNotifForWithdrawn(young, cohort, withdrawnReason); + newYoung.statusPhase1 = young.statusPhase1 === YOUNG_STATUS_PHASE1.AFFECTED ? YOUNG_STATUS_PHASE1.WAITING_AFFECTATION : young.statusPhase1; + } + young.set(newYoung); await young.save({ fromUser: req.user }); @@ -707,6 +714,64 @@ router.put("/young/:id", passport.authenticate("referent", { session: false, fai } }); +async function handleNotifForWithdrawn(young, cohort, withdrawnReason) { + const oldStatusPhase1 = young.statusPhase1; + + // We notify the ref dep and the young + try { + const youngFullName = young.firstName + " " + young.lastName; + const referents: ReferentDocument[] = await ReferentModel.find({ role: ROLES.REFERENT_DEPARTMENT, department: young.department }); + const SUB_ROLES_PRIORITY = [SUB_ROLES.manager_department, SUB_ROLES.assistant_manager_department, SUB_ROLES.secretariat, SUB_ROLES.manager_phase2]; + let selectedReferent: ReferentDocument | undefined = referents.find((referent) => referent.subRole && SUB_ROLES_PRIORITY.includes(referent.subRole)); + if (!selectedReferent && referents.length > 0) { + selectedReferent = referents[0]; + } + if (selectedReferent) { + await sendTemplate(SENDINBLUE_TEMPLATES.referent.YOUNG_WITHDRAWN_NOTIFICATION, { + emailTo: [{ name: `${selectedReferent.firstName} ${selectedReferent.lastName}`, email: selectedReferent.email }], + params: { student_name: youngFullName, message: WITHRAWN_REASONS.find((r) => r.value === withdrawnReason)?.label || "" }, + }); + } + // If they are CLE, we notify the class referent. + if (cohort?.type === YOUNG_SOURCE.CLE) { + const classe = await ClasseModel.findById(young.classeId); + const referent = await ReferentModel.findById(classe?.referentClasseIds[0]); + const datecohorte = `du ${formatDateFRTimezoneUTC(cohort.dateStart)} au ${formatDateFRTimezoneUTC(cohort.dateEnd)}`; + if (referent) { + await sendTemplate(SENDINBLUE_TEMPLATES.referent.YOUNG_WITHDRAWN_CLE, { + emailTo: [{ name: `${referent.firstName} ${referent.lastName}`, email: referent.email }], + params: { + youngFirstName: young.firstName, + youngLastName: young.lastName, + datecohorte, + raisondesistement: WITHRAWN_REASONS.find((r) => r.value === withdrawnReason)?.label || "", + }, + }); + } + } + + // If young affected, we notify the head center + if (oldStatusPhase1 === YOUNG_STATUS_PHASE1.AFFECTED && young.sessionPhase1Id != null) { + const session = await SessionPhase1Model.findById(young.sessionPhase1Id); + const headCenter = await ReferentModel.findById(session?.headCenterId); + + if (headCenter) { + await sendTemplate(SENDINBLUE_TEMPLATES.headCenter.YOUNG_WITHDRAWN, { + emailTo: [{ name: `${headCenter.firstName} ${headCenter.lastName}`, email: headCenter.email }], + params: { contact_name: youngFullName, message: WITHRAWN_REASONS.find((r) => r.value === withdrawnReason)?.label || "" }, + }); + } + } + + await sendTemplate(SENDINBLUE_TEMPLATES.young.WITHDRAWN, { + emailTo: [{ name: `${young.firstName} ${young.lastName}`, email: young.email }], + params: { message: WITHRAWN_REASONS.find((r) => r.value === withdrawnReason)?.label || "" }, + }); + } catch (e) { + capture(e); + } +} + /** * Permet de valider plusieurs jeunes CLE à la fois (pas d'objectifs pour les CLE). * Gère uniquement le changement de status vers VALIDATED et n'est pas compatible HTS From fc900e98ed5af2362becc2b6fa3102cc252ee6f1 Mon Sep 17 00:00:00 2001 From: C2Chandelier Date: Fri, 17 Jan 2025 11:56:06 +0100 Subject: [PATCH 2/3] up --- api/src/__tests__/referent.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/__tests__/referent.test.ts b/api/src/__tests__/referent.test.ts index 40d79ac9e9..384d11625a 100644 --- a/api/src/__tests__/referent.test.ts +++ b/api/src/__tests__/referent.test.ts @@ -245,7 +245,7 @@ describe("Referent", () => { ); expect(response.statusCode).toEqual(200); expect(young?.status).toEqual("WITHDRAWN"); - expect(young?.statusPhase1).toEqual("AFFECTED"); + expect(young?.statusPhase1).toEqual("WAITING_AFFECTATION"); expect(young?.statusPhase2).toEqual("WAITING_REALISATION"); expect(young?.statusPhase3).toEqual("WAITING_REALISATION"); }); From 3161ac94350531e9a08b457fb1c97ba2b8197777 Mon Sep 17 00:00:00 2001 From: C2Chandelier Date: Fri, 17 Jan 2025 16:17:27 +0100 Subject: [PATCH 3/3] service --- api/src/controllers/young/index.ts | 61 +------------------- api/src/referent/referentController.ts | 64 +-------------------- api/src/young/youngService.ts | 80 +++++++++++++++++++++++++- 3 files changed, 82 insertions(+), 123 deletions(-) diff --git a/api/src/controllers/young/index.ts b/api/src/controllers/young/index.ts index b96b9deab8..eb44cb4638 100644 --- a/api/src/controllers/young/index.ts +++ b/api/src/controllers/young/index.ts @@ -60,10 +60,7 @@ import { MissionType, ContractType, CohortType, - SUB_ROLES, ReferentType, - WITHRAWN_REASONS, - formatDateFRTimezoneUTC, } from "snu-lib"; import { getFilteredSessionsForChangementSejour } from "../../cohort/cohortService"; import { anonymizeApplicationsFromYoungId } from "../../services/application"; @@ -77,6 +74,7 @@ import { FileTypeResult } from "file-type"; import { requestValidatorMiddleware } from "../../middlewares/requestValidatorMiddleware"; import { authMiddleware } from "../../middlewares/authMiddleware"; import { accessControlMiddleware } from "../../middlewares/accessControlMiddleware"; +import { handleNotifForYoungWithdrawn } from "../../young/youngService"; const router = express.Router(); const YoungAuth = new AuthObject(YoungModel); @@ -926,7 +924,8 @@ router.put("/withdraw", passport.authenticate("young", { session: false, failWit } const { withdrawnMessage, withdrawnReason } = value; - const oldStatusPhase1 = young.statusPhase1; + + await handleNotifForYoungWithdrawn(young, cohort, withdrawnReason); young.set({ status: YOUNG_STATUS.WITHDRAWN, @@ -948,60 +947,6 @@ router.put("/withdraw", passport.authenticate("young", { session: false, failWit if (bus) await updateSeatsTakenInBusLine(bus); } - // We notify the ref dep and the young - try { - const youngFullName = young.firstName + " " + young.lastName; - const referents: ReferentDocument[] = await ReferentModel.find({ role: ROLES.REFERENT_DEPARTMENT, department: young.department }); - const SUB_ROLES_PRIORITY = [SUB_ROLES.manager_department, SUB_ROLES.assistant_manager_department, SUB_ROLES.secretariat, SUB_ROLES.manager_phase2]; - let selectedReferent: ReferentDocument | undefined = referents.find((referent) => referent.subRole && SUB_ROLES_PRIORITY.includes(referent.subRole)); - if (!selectedReferent && referents.length > 0) { - selectedReferent = referents[0]; - } - if (selectedReferent) { - await sendTemplate(SENDINBLUE_TEMPLATES.referent.YOUNG_WITHDRAWN_NOTIFICATION, { - emailTo: [{ name: `${selectedReferent.firstName} ${selectedReferent.lastName}`, email: selectedReferent.email }], - params: { student_name: youngFullName, message: WITHRAWN_REASONS.find((r) => r.value === withdrawnReason)?.label || "" }, - }); - } - // If they are CLE, we notify the class referent. - if (cohort?.type === YOUNG_SOURCE.CLE) { - const classe = await ClasseModel.findById(young.classeId); - const referent = await ReferentModel.findById(classe?.referentClasseIds[0]); - const datecohorte = `du ${formatDateFRTimezoneUTC(cohort.dateStart)} au ${formatDateFRTimezoneUTC(cohort.dateEnd)}`; - if (referent) { - await sendTemplate(SENDINBLUE_TEMPLATES.referent.YOUNG_WITHDRAWN_CLE, { - emailTo: [{ name: `${referent.firstName} ${referent.lastName}`, email: referent.email }], - params: { - youngFirstName: young.firstName, - youngLastName: young.lastName, - datecohorte, - raisondesistement: WITHRAWN_REASONS.find((r) => r.value === withdrawnReason)?.label || "", - }, - }); - } - } - - // If young affected, we notify the head center - if (oldStatusPhase1 === YOUNG_STATUS_PHASE1.AFFECTED && young.sessionPhase1Id != null) { - const session = await SessionPhase1Model.findById(young.sessionPhase1Id); - const headCenter = await ReferentModel.findById(session?.headCenterId); - - if (headCenter) { - await sendTemplate(SENDINBLUE_TEMPLATES.headCenter.YOUNG_WITHDRAWN, { - emailTo: [{ name: `${headCenter.firstName} ${headCenter.lastName}`, email: headCenter.email }], - params: { contact_name: youngFullName, message: WITHRAWN_REASONS.find((r) => r.value === withdrawnReason)?.label || "" }, - }); - } - } - - await sendTemplate(SENDINBLUE_TEMPLATES.young.WITHDRAWN, { - emailTo: [{ name: `${young.firstName} ${young.lastName}`, email: young.email }], - params: { message: WITHRAWN_REASONS.find((r) => r.value === withdrawnReason)?.label || "" }, - }); - } catch (e) { - capture(e); - } - res.status(200).send({ ok: true, data: serializeYoung(updatedYoung, updatedYoung) }); } catch (error) { capture(error); diff --git a/api/src/referent/referentController.ts b/api/src/referent/referentController.ts index cdda5308fa..e98a55d10d 100644 --- a/api/src/referent/referentController.ts +++ b/api/src/referent/referentController.ts @@ -108,8 +108,6 @@ import { isAdmin, isReferentReg, canValidateYoungToLP, - WITHRAWN_REASONS, - formatDateFRTimezoneUTC, } from "snu-lib"; import { getFilteredSessions, getAllSessions, getFilteredSessionsForCLE } from "../utils/cohort"; import scanFile from "../utils/virusScanner"; @@ -121,9 +119,9 @@ import { getCompletionObjectifs } from "../services/inscription-goal"; import SNUpport from "../SNUpport"; import { requestValidatorMiddleware } from "../middlewares/requestValidatorMiddleware"; import { accessControlMiddleware } from "../middlewares/accessControlMiddleware"; -import { isAfter } from "date-fns/isAfter"; import { authMiddleware } from "../middlewares/authMiddleware"; import { CohortDocumentWithPlaces } from "../utils/cohort"; +import { handleNotifForYoungWithdrawn } from "../young/youngService"; const router = express.Router(); const ReferentAuth = new AuthObject(ReferentModel); @@ -678,7 +676,7 @@ router.put("/young/:id", passport.authenticate("referent", { session: false, fai if (newYoung.status === YOUNG_STATUS.WITHDRAWN && young.status !== YOUNG_STATUS.WITHDRAWN) { const { withdrawnReason } = newYoung; - await handleNotifForWithdrawn(young, cohort, withdrawnReason); + await handleNotifForYoungWithdrawn(young, cohort, withdrawnReason); newYoung.statusPhase1 = young.statusPhase1 === YOUNG_STATUS_PHASE1.AFFECTED ? YOUNG_STATUS_PHASE1.WAITING_AFFECTATION : young.statusPhase1; } @@ -714,64 +712,6 @@ router.put("/young/:id", passport.authenticate("referent", { session: false, fai } }); -async function handleNotifForWithdrawn(young, cohort, withdrawnReason) { - const oldStatusPhase1 = young.statusPhase1; - - // We notify the ref dep and the young - try { - const youngFullName = young.firstName + " " + young.lastName; - const referents: ReferentDocument[] = await ReferentModel.find({ role: ROLES.REFERENT_DEPARTMENT, department: young.department }); - const SUB_ROLES_PRIORITY = [SUB_ROLES.manager_department, SUB_ROLES.assistant_manager_department, SUB_ROLES.secretariat, SUB_ROLES.manager_phase2]; - let selectedReferent: ReferentDocument | undefined = referents.find((referent) => referent.subRole && SUB_ROLES_PRIORITY.includes(referent.subRole)); - if (!selectedReferent && referents.length > 0) { - selectedReferent = referents[0]; - } - if (selectedReferent) { - await sendTemplate(SENDINBLUE_TEMPLATES.referent.YOUNG_WITHDRAWN_NOTIFICATION, { - emailTo: [{ name: `${selectedReferent.firstName} ${selectedReferent.lastName}`, email: selectedReferent.email }], - params: { student_name: youngFullName, message: WITHRAWN_REASONS.find((r) => r.value === withdrawnReason)?.label || "" }, - }); - } - // If they are CLE, we notify the class referent. - if (cohort?.type === YOUNG_SOURCE.CLE) { - const classe = await ClasseModel.findById(young.classeId); - const referent = await ReferentModel.findById(classe?.referentClasseIds[0]); - const datecohorte = `du ${formatDateFRTimezoneUTC(cohort.dateStart)} au ${formatDateFRTimezoneUTC(cohort.dateEnd)}`; - if (referent) { - await sendTemplate(SENDINBLUE_TEMPLATES.referent.YOUNG_WITHDRAWN_CLE, { - emailTo: [{ name: `${referent.firstName} ${referent.lastName}`, email: referent.email }], - params: { - youngFirstName: young.firstName, - youngLastName: young.lastName, - datecohorte, - raisondesistement: WITHRAWN_REASONS.find((r) => r.value === withdrawnReason)?.label || "", - }, - }); - } - } - - // If young affected, we notify the head center - if (oldStatusPhase1 === YOUNG_STATUS_PHASE1.AFFECTED && young.sessionPhase1Id != null) { - const session = await SessionPhase1Model.findById(young.sessionPhase1Id); - const headCenter = await ReferentModel.findById(session?.headCenterId); - - if (headCenter) { - await sendTemplate(SENDINBLUE_TEMPLATES.headCenter.YOUNG_WITHDRAWN, { - emailTo: [{ name: `${headCenter.firstName} ${headCenter.lastName}`, email: headCenter.email }], - params: { contact_name: youngFullName, message: WITHRAWN_REASONS.find((r) => r.value === withdrawnReason)?.label || "" }, - }); - } - } - - await sendTemplate(SENDINBLUE_TEMPLATES.young.WITHDRAWN, { - emailTo: [{ name: `${young.firstName} ${young.lastName}`, email: young.email }], - params: { message: WITHRAWN_REASONS.find((r) => r.value === withdrawnReason)?.label || "" }, - }); - } catch (e) { - capture(e); - } -} - /** * Permet de valider plusieurs jeunes CLE à la fois (pas d'objectifs pour les CLE). * Gère uniquement le changement de status vers VALIDATED et n'est pas compatible HTS diff --git a/api/src/young/youngService.ts b/api/src/young/youngService.ts index fa61686e2f..0fe130e58f 100644 --- a/api/src/young/youngService.ts +++ b/api/src/young/youngService.ts @@ -1,13 +1,29 @@ import { format } from "date-fns"; -import { ERRORS, FUNCTIONAL_ERRORS, UserDto, YOUNG_PHASE, YOUNG_STATUS, YOUNG_STATUS_PHASE1, YoungDto, YoungType } from "snu-lib"; - -import { YoungDocument, YoungModel } from "../models"; +import { + ERRORS, + FUNCTIONAL_ERRORS, + UserDto, + YOUNG_PHASE, + YOUNG_STATUS, + YOUNG_STATUS_PHASE1, + YoungDto, + YoungType, + SUB_ROLES, + WITHRAWN_REASONS, + formatDateFRTimezoneUTC, + YOUNG_SOURCE, + ROLES, + SENDINBLUE_TEMPLATES, +} from "snu-lib"; +import { sendTemplate } from "../brevo"; +import { YoungDocument, YoungModel, ReferentDocument, ReferentModel, ClasseModel, SessionPhase1Model } from "../models"; import { generatePdfIntoBuffer } from "../utils/pdf-renderer"; import { YOUNG_DOCUMENT, YOUNG_DOCUMENT_PHASE_TEMPLATE } from "./youngDocument"; import { isLocalTransport } from "./youngCertificateService"; import { logger } from "../logger"; +import { capture } from "../sentry"; export const generateConvocationsForMultipleYoungs = async (youngs: YoungDto[]): Promise => { const validatedYoungsWithSession = getValidatedYoungsWithSession(youngs); @@ -169,3 +185,61 @@ export const mightAddInProgressStatus = async (young: YoungDocument, user: UserD logger.info(`YoungService - mightAddInProgressStatus(), Status set to IN_PROGRESS for YoungId:${young.id}`); } }; + +export async function handleNotifForYoungWithdrawn(young, cohort, withdrawnReason) { + const oldStatusPhase1 = young.statusPhase1; + + // We notify the ref dep and the young + try { + const youngFullName = young.firstName + " " + young.lastName; + const referents: ReferentDocument[] = await ReferentModel.find({ role: ROLES.REFERENT_DEPARTMENT, department: young.department }); + const SUB_ROLES_PRIORITY = [SUB_ROLES.manager_department, SUB_ROLES.assistant_manager_department, SUB_ROLES.secretariat, SUB_ROLES.manager_phase2]; + let selectedReferent: ReferentDocument | undefined = referents.find((referent) => referent.subRole && SUB_ROLES_PRIORITY.includes(referent.subRole)); + if (!selectedReferent && referents.length > 0) { + selectedReferent = referents[0]; + } + if (selectedReferent) { + await sendTemplate(SENDINBLUE_TEMPLATES.referent.YOUNG_WITHDRAWN_NOTIFICATION, { + emailTo: [{ name: `${selectedReferent.firstName} ${selectedReferent.lastName}`, email: selectedReferent.email }], + params: { student_name: youngFullName, message: WITHRAWN_REASONS.find((r) => r.value === withdrawnReason)?.label || "" }, + }); + } + // If they are CLE, we notify the class referent. + if (cohort?.type === YOUNG_SOURCE.CLE) { + const classe = await ClasseModel.findById(young.classeId); + const referent = await ReferentModel.findById(classe?.referentClasseIds[0]); + const datecohorte = `du ${formatDateFRTimezoneUTC(cohort.dateStart)} au ${formatDateFRTimezoneUTC(cohort.dateEnd)}`; + if (referent) { + await sendTemplate(SENDINBLUE_TEMPLATES.referent.YOUNG_WITHDRAWN_CLE, { + emailTo: [{ name: `${referent.firstName} ${referent.lastName}`, email: referent.email }], + params: { + youngFirstName: young.firstName, + youngLastName: young.lastName, + datecohorte, + raisondesistement: WITHRAWN_REASONS.find((r) => r.value === withdrawnReason)?.label || "", + }, + }); + } + } + + // If young affected, we notify the head center + if (oldStatusPhase1 === YOUNG_STATUS_PHASE1.AFFECTED && young.sessionPhase1Id != null) { + const session = await SessionPhase1Model.findById(young.sessionPhase1Id); + const headCenter = await ReferentModel.findById(session?.headCenterId); + + if (headCenter) { + await sendTemplate(SENDINBLUE_TEMPLATES.headCenter.YOUNG_WITHDRAWN, { + emailTo: [{ name: `${headCenter.firstName} ${headCenter.lastName}`, email: headCenter.email }], + params: { contact_name: youngFullName, message: WITHRAWN_REASONS.find((r) => r.value === withdrawnReason)?.label || "" }, + }); + } + } + + await sendTemplate(SENDINBLUE_TEMPLATES.young.WITHDRAWN, { + emailTo: [{ name: `${young.firstName} ${young.lastName}`, email: young.email }], + params: { message: WITHRAWN_REASONS.find((r) => r.value === withdrawnReason)?.label || "" }, + }); + } catch (e) { + capture(e); + } +}