diff --git a/back/src/_testBuilders/emailAssertions.ts b/back/src/_testBuilders/emailAssertions.ts index 7739095902..b07833d87c 100644 --- a/back/src/_testBuilders/emailAssertions.ts +++ b/back/src/_testBuilders/emailAssertions.ts @@ -137,3 +137,24 @@ export const expectNotifyBeneficiaryAndEnterpriseThatApplicationIsRejected = ( }, }); }; + +export const expectNotifyBeneficiaryAndEnterpriseThatConventionIsDeprecated = ( + templatedEmail: TemplatedEmail, + recipients: string[], + convention: ConventionDto, +) => { + expectToEqual(templatedEmail, { + type: "DEPRECATED_CONVENTION_NOTIFICATION", + recipients, + params: { + internshipKind: convention.internshipKind, + beneficiaryFirstName: convention.signatories.beneficiary.firstName, + beneficiaryLastName: convention.signatories.beneficiary.lastName, + businessName: convention.businessName, + deprecationReason: convention.statusJustification || "", + immersionProfession: convention.immersionAppellation.appellationLabel, + dateEnd: convention.dateEnd, + dateStart: convention.dateStart, + }, + }); +}; diff --git a/back/src/adapters/primary/config/createUseCases.ts b/back/src/adapters/primary/config/createUseCases.ts index 21d6c0e5ae..f382684bb2 100644 --- a/back/src/adapters/primary/config/createUseCases.ts +++ b/back/src/adapters/primary/config/createUseCases.ts @@ -25,6 +25,7 @@ import { GetConvention } from "../../../domain/convention/useCases/GetConvention import { ConfirmToSignatoriesThatApplicationCorrectlySubmittedRequestSignature } from "../../../domain/convention/useCases/notifications/ConfirmToSignatoriesThatApplicationCorrectlySubmittedRequestSignature"; import { DeliverRenewedMagicLink } from "../../../domain/convention/useCases/notifications/DeliverRenewedMagicLink"; import { NotifyAllActorsOfFinalConventionValidation } from "../../../domain/convention/useCases/notifications/NotifyAllActorsOfFinalConventionValidation"; +import { NotifyAllActorsThatConventionIsDeprecated } from "../../../domain/convention/useCases/notifications/NotifyAllActorsThatConventionIsDeprecated"; import { NotifyBeneficiaryAndEnterpriseThatApplicationIsRejected } from "../../../domain/convention/useCases/notifications/NotifyBeneficiaryAndEnterpriseThatApplicationIsRejected"; import { NotifyBeneficiaryAndEnterpriseThatApplicationNeedsModification } from "../../../domain/convention/useCases/notifications/NotifyBeneficiaryAndEnterpriseThatApplicationNeedsModification"; import { NotifyConventionReminder } from "../../../domain/convention/useCases/notifications/NotifyConventionReminder"; @@ -365,6 +366,11 @@ export const createUseCases = ( uowPerformer, saveNotificationAndRelatedEvent, ), + notifyAllActorsThatConventionIsDeprecated: + new NotifyAllActorsThatConventionIsDeprecated( + uowPerformer, + saveNotificationAndRelatedEvent, + ), notifyBeneficiaryAndEnterpriseThatConventionNeedsModifications: new NotifyBeneficiaryAndEnterpriseThatApplicationNeedsModification( uowPerformer, diff --git a/back/src/adapters/primary/subscribeToEvents.ts b/back/src/adapters/primary/subscribeToEvents.ts index 8cd2178327..7cb6fc05b0 100644 --- a/back/src/adapters/primary/subscribeToEvents.ts +++ b/back/src/adapters/primary/subscribeToEvents.ts @@ -77,6 +77,7 @@ const getUseCasesByTopics = ( ImmersionApplicationCancelled: [ useCases.broadcastToPoleEmploiOnConventionUpdates, ], + ConventionDeprecated: [useCases.notifyAllActorsThatConventionIsDeprecated], ConventionReminderRequired: [useCases.notifyConventionReminder], // Establishment form related diff --git a/back/src/domain/convention/ports/PoleEmploiGateway.ts b/back/src/domain/convention/ports/PoleEmploiGateway.ts index 37f5b78909..6794a3deb3 100644 --- a/back/src/domain/convention/ports/PoleEmploiGateway.ts +++ b/back/src/domain/convention/ports/PoleEmploiGateway.ts @@ -18,6 +18,7 @@ export const conventionStatusToPoleEmploiStatus = { // si rejeté REJECTED: "REJETÉ", CANCELLED: "DEMANDE_ANNULEE", + DEPRECATED: "DEMANDE_OBSOLETE", // // à venir potentiellement // ABANDONNED: "ABANDONNÉ", diff --git a/back/src/domain/convention/useCases/UpdateConventionStatus.testHelpers.ts b/back/src/domain/convention/useCases/UpdateConventionStatus.testHelpers.ts index d88aa54ae9..74de02b73c 100644 --- a/back/src/domain/convention/useCases/UpdateConventionStatus.testHelpers.ts +++ b/back/src/domain/convention/useCases/UpdateConventionStatus.testHelpers.ts @@ -44,6 +44,7 @@ type ConventionDomainTopic = ExtractFromDomainTopics< | "ImmersionApplicationRejected" | "ImmersionApplicationRequiresModification" | "ImmersionApplicationCancelled" + | "ConventionDeprecated" > | null; // null is used to indicate that no domain event should be sent type SetupInitialStateParams = { diff --git a/back/src/domain/convention/useCases/UpdateConventionStatus.ts b/back/src/domain/convention/useCases/UpdateConventionStatus.ts index de74e249d2..59f5a915a8 100644 --- a/back/src/domain/convention/useCases/UpdateConventionStatus.ts +++ b/back/src/domain/convention/useCases/UpdateConventionStatus.ts @@ -32,6 +32,7 @@ const domainTopicByTargetStatusMap: Record< REJECTED: "ImmersionApplicationRejected", CANCELLED: "ImmersionApplicationCancelled", DRAFT: "ImmersionApplicationRequiresModification", + DEPRECATED: "ConventionDeprecated", }; type UpdateConventionStatusPayload = { @@ -76,7 +77,9 @@ export class UpdateConventionStatus extends TransactionalUseCase< : undefined, ) .withStatusJustification( - status === "CANCELLED" || status === "REJECTED" + status === "CANCELLED" || + status === "REJECTED" || + status === "DEPRECATED" ? params.statusJustification : undefined, ); @@ -95,7 +98,9 @@ export class UpdateConventionStatus extends TransactionalUseCase< updatedDto, domainTopic, role, - params.status === "REJECTED" || params.status === "DRAFT" + params.status === "REJECTED" || + params.status === "DRAFT" || + params.status === "DEPRECATED" ? params.statusJustification : undefined, ), diff --git a/back/src/domain/convention/useCases/UpdateConventionStatus.unit.test.ts b/back/src/domain/convention/useCases/UpdateConventionStatus.unit.test.ts index acce325d6f..4d697879c8 100644 --- a/back/src/domain/convention/useCases/UpdateConventionStatus.unit.test.ts +++ b/back/src/domain/convention/useCases/UpdateConventionStatus.unit.test.ts @@ -213,6 +213,25 @@ describe("UpdateConventionStatus", () => { }); }); + describe("* -> DEPRECATED transition", () => { + testForAllRolesAndInitialStatusCases({ + updateStatusParams: { + status: "DEPRECATED", + statusJustification: "my deprecation justification", + }, + expectedDomainTopic: "ConventionDeprecated", + updatedFields: { statusJustification: "my deprecation justification" }, + allowedRoles: ["backOffice", "validator", "counsellor"], + allowedInitialStatuses: [ + "PARTIALLY_SIGNED", + "READY_TO_SIGN", + "IN_REVIEW", + "ACCEPTED_BY_COUNSELLOR", + "DRAFT", + ], + }); + }); + it("fails for unknown application ids", async () => { const { updateConventionStatus, conventionRepository } = await setupInitialState({ initialStatus: "IN_REVIEW" }); diff --git a/back/src/domain/convention/useCases/notifications/NotifyAllActorsThatConventionIsDeprecated.ts b/back/src/domain/convention/useCases/notifications/NotifyAllActorsThatConventionIsDeprecated.ts new file mode 100644 index 0000000000..640423ac01 --- /dev/null +++ b/back/src/domain/convention/useCases/notifications/NotifyAllActorsThatConventionIsDeprecated.ts @@ -0,0 +1,70 @@ +import { uniq } from "ramda"; +import { ConventionDto, conventionSchema } from "shared"; +import { + UnitOfWork, + UnitOfWorkPerformer, +} from "../../../core/ports/UnitOfWork"; +import { TransactionalUseCase } from "../../../core/UseCase"; +import { SaveNotificationAndRelatedEvent } from "../../../generic/notifications/entities/Notification"; + +export class NotifyAllActorsThatConventionIsDeprecated extends TransactionalUseCase { + constructor( + uowPerformer: UnitOfWorkPerformer, + private readonly saveNotificationAndRelatedEvent: SaveNotificationAndRelatedEvent, + ) { + super(uowPerformer); + } + + inputSchema = conventionSchema; + + public async _execute( + convention: ConventionDto, + uow: UnitOfWork, + ): Promise { + const [agency] = await uow.agencyRepository.getByIds([convention.agencyId]); + if (!agency) { + throw new Error( + `Unable to send mail. No agency config found for ${convention.agencyId}`, + ); + } + + const { + beneficiary, + establishmentRepresentative, + beneficiaryCurrentEmployer, + beneficiaryRepresentative, + } = convention.signatories; + + const recipients = uniq([ + beneficiary.email, + establishmentRepresentative.email, + ...agency.counsellorEmails, + ...agency.validatorEmails, + ...(beneficiaryCurrentEmployer ? [beneficiaryCurrentEmployer.email] : []), + ...(beneficiaryRepresentative ? [beneficiaryRepresentative.email] : []), + ]); + + await this.saveNotificationAndRelatedEvent(uow, { + kind: "email", + templatedContent: { + type: "DEPRECATED_CONVENTION_NOTIFICATION", + recipients, + params: { + internshipKind: convention.internshipKind, + beneficiaryFirstName: beneficiary.firstName, + beneficiaryLastName: beneficiary.lastName, + businessName: convention.businessName, + deprecationReason: convention.statusJustification || "", + dateStart: convention.dateStart, + dateEnd: convention.dateEnd, + immersionProfession: convention.immersionAppellation.appellationLabel, + }, + }, + followedIds: { + conventionId: convention.id, + agencyId: convention.agencyId, + establishmentSiret: convention.siret, + }, + }); + } +} diff --git a/back/src/domain/convention/useCases/notifications/NotifyAllActorsThatConventionIsDeprecated.unit.test.ts b/back/src/domain/convention/useCases/notifications/NotifyAllActorsThatConventionIsDeprecated.unit.test.ts new file mode 100644 index 0000000000..9ae6eb6906 --- /dev/null +++ b/back/src/domain/convention/useCases/notifications/NotifyAllActorsThatConventionIsDeprecated.unit.test.ts @@ -0,0 +1,107 @@ +import { + AgencyDto, + AgencyDtoBuilder, + BeneficiaryCurrentEmployer, + BeneficiaryRepresentative, + ConventionDtoBuilder, +} from "shared"; +import { expectNotifyBeneficiaryAndEnterpriseThatConventionIsDeprecated } from "../../../../_testBuilders/emailAssertions"; +import { + createInMemoryUow, + InMemoryUnitOfWork, +} from "../../../../adapters/primary/config/uowConfig"; +import { CustomTimeGateway } from "../../../../adapters/secondary/core/TimeGateway/CustomTimeGateway"; +import { UuidV4Generator } from "../../../../adapters/secondary/core/UuidGeneratorImplementations"; +import { InMemoryUowPerformer } from "../../../../adapters/secondary/InMemoryUowPerformer"; +import { makeCreateNewEvent } from "../../../core/eventBus/EventBus"; +import { + EmailNotification, + makeSaveNotificationAndRelatedEvent, +} from "../../../generic/notifications/entities/Notification"; +import { NotifyAllActorsThatConventionIsDeprecated } from "./NotifyAllActorsThatConventionIsDeprecated"; + +const beneficiaryRepresentative: BeneficiaryRepresentative = { + role: "beneficiary-representative", + email: "legal@representative.com", + firstName: "The", + lastName: "Representative", + phone: "1234567", +}; + +const beneficiaryCurrentEmployer: BeneficiaryCurrentEmployer = { + firstName: "ali", + lastName: "baba", + businessName: "business", + businessSiret: "01234567890123", + email: "beneficiary-current-employer@gmail.com", + job: "job", + phone: "0011223344", + role: "beneficiary-current-employer", + signedAt: new Date().toISOString(), + businessAddress: "Rue des Bouchers 67065 Strasbourg", +}; + +const deprecatedConvention = new ConventionDtoBuilder() + .withStatus("DEPRECATED") + .withStatusJustification("test-deprecation-justification") + .withBeneficiaryRepresentative(beneficiaryRepresentative) + .withBeneficiaryCurrentEmployer(beneficiaryCurrentEmployer) + .build(); + +const counsellorEmails = ["counsellor1@email.fr", "counsellor2@email.fr"]; + +const validatorEmails = ["validator@gmail.com"]; + +const defaultAgency = AgencyDtoBuilder.create(deprecatedConvention.agencyId) + .withName("test-agency-name") + .withCounsellorEmails(counsellorEmails) + .withValidatorEmails(validatorEmails) + .build(); + +describe("NotifyAllActorsThatApplicationIsDeprecated", () => { + let useCase: NotifyAllActorsThatConventionIsDeprecated; + let agency: AgencyDto; + let uow: InMemoryUnitOfWork; + + beforeEach(() => { + agency = defaultAgency; + uow = createInMemoryUow(); + uow.agencyRepository.setAgencies([agency]); + + const timeGateway = new CustomTimeGateway(); + const uuidGenerator = new UuidV4Generator(); + const createNewEvent = makeCreateNewEvent({ uuidGenerator, timeGateway }); + const saveNotificationAndRelatedEvent = makeSaveNotificationAndRelatedEvent( + createNewEvent, + uuidGenerator, + timeGateway, + ); + useCase = new NotifyAllActorsThatConventionIsDeprecated( + new InMemoryUowPerformer(uow), + saveNotificationAndRelatedEvent, + ); + }); + + it("Sends a conevention deprecated notification to all actors", async () => { + await useCase.execute(deprecatedConvention); + + const templatedEmailsSent = uow.notificationRepository.notifications + .filter((notif): notif is EmailNotification => notif.kind === "email") + .map((notif) => notif.templatedContent); + + expect(templatedEmailsSent).toHaveLength(1); + + expectNotifyBeneficiaryAndEnterpriseThatConventionIsDeprecated( + templatedEmailsSent[0], + [ + deprecatedConvention.signatories.beneficiary.email, + deprecatedConvention.signatories.establishmentRepresentative.email, + ...counsellorEmails, + ...validatorEmails, + deprecatedConvention.signatories.beneficiaryCurrentEmployer!.email, + deprecatedConvention.signatories.beneficiaryRepresentative!.email, + ], + deprecatedConvention, + ); + }); +}); diff --git a/back/src/domain/core/eventBus/events.ts b/back/src/domain/core/eventBus/events.ts index fcf501297e..4549887d14 100644 --- a/back/src/domain/core/eventBus/events.ts +++ b/back/src/domain/core/eventBus/events.ts @@ -62,6 +62,7 @@ export type DomainEvent = | GenericEvent<"ImmersionApplicationRejected", ConventionDto> | GenericEvent<"ImmersionApplicationCancelled", ConventionDto> | GenericEvent<"ImmersionApplicationRequiresModification", ConventionRequiresModificationPayload> + | GenericEvent<"ConventionDeprecated", ConventionDto> // MAGIC LINK RENEWAL | GenericEvent<"MagicLinkRenewalRequested", RenewMagicLinkPayload> diff --git a/front/src/app/components/admin/ConventionManageActions.tsx b/front/src/app/components/admin/ConventionManageActions.tsx index f50f757bf7..117d773c5e 100644 --- a/front/src/app/components/admin/ConventionManageActions.tsx +++ b/front/src/app/components/admin/ConventionManageActions.tsx @@ -69,6 +69,16 @@ export const ConventionManageActions = ({ )} + {isAllowedTransition(convention.status, "DEPRECATED", role) && ( + + {t.verification.markAsDeprecated} + + )} + {isAllowedTransition(convention.status, "DRAFT", role) && ( = { PARTIALLY_SIGNED: "[✍️ Partiellement signée]", READY_TO_SIGN: "[📄 En cours de signature]", REJECTED: "[❌ DEMANDE REJETÉE]", + DEPRECATED: "[❌ DEMANDE OBSOLÈTE]", }; export interface ConventionValidationProps { diff --git a/front/src/app/components/forms/convention/ConventionFeedbackNotification.tsx b/front/src/app/components/forms/convention/ConventionFeedbackNotification.tsx index 990e24f407..88b9aa225b 100644 --- a/front/src/app/components/forms/convention/ConventionFeedbackNotification.tsx +++ b/front/src/app/components/forms/convention/ConventionFeedbackNotification.tsx @@ -70,6 +70,8 @@ export const createConventionFeedbackMessageByKind = ( ), cancelled: "Succès. La convention a bien été annulée.", + deprecated: + "Succès. La convention a bien été supprimé. La confirmation de cette suppression va être communiquée par mail à chacun des signataires.", }); const InitialSubmitSuccessMessageBase = ({ diff --git a/front/src/app/components/forms/convention/VerificationActionButton.tsx b/front/src/app/components/forms/convention/VerificationActionButton.tsx index 2bf4e8c8e4..baaeff93d1 100644 --- a/front/src/app/components/forms/convention/VerificationActionButton.tsx +++ b/front/src/app/components/forms/convention/VerificationActionButton.tsx @@ -50,6 +50,13 @@ const { CancelModal, openCancelModal, closeCancelModal } = createModal({ isOpenedByDefault: false, }); +const { DeprecateModal, openDeprecateModal, closeDeprecateModal } = createModal( + { + name: "deprecate", + isOpenedByDefault: false, + }, +); + const ModalByStatus = (status: VerificationActionsModal) => { const modals = { DRAFT: { @@ -67,6 +74,11 @@ const ModalByStatus = (status: VerificationActionsModal) => { openModal: openCancelModal, closeModal: closeCancelModal, }, + DEPRECATED: { + modal: DeprecateModal, + openModal: openDeprecateModal, + closeModal: closeDeprecateModal, + }, }; return modals[status]; }; @@ -91,6 +103,8 @@ export const VerificationActionButton = ({ ACCEPTED_BY_COUNSELLOR: domElementIds.manageConvention.conventionValidationValidateButton, CANCELLED: domElementIds.manageConvention.conventionValidationCancelButton, + DEPRECATED: + domElementIds.manageConvention.conventionValidationDeprecateButton, }; return ( @@ -249,11 +263,13 @@ const JustificationModalContent = ({ const inputLabelByStatus: Record = { DRAFT: "Précisez la raison et la modification nécessaire", REJECTED: "Pourquoi l'immersion est-elle refusée ?", - CANCELLED: "Pourquoi souhaitez-vous annuler cette convention?", + CANCELLED: "Pourquoi souhaitez-vous annuler cette convention ?", + DEPRECATED: "Pourquoi l'immersion est-elle obsolète ?", }; const confirmByStatus: Record = { DRAFT: "Confirmer la demande de modification", REJECTED: "Confirmer le refus", CANCELLED: "Confirmer l'annulation", + DEPRECATED: "Confirmer que la demande est obsolète", }; diff --git a/front/src/app/contents/forms/convention/textSetup.tsx b/front/src/app/contents/forms/convention/textSetup.tsx index 1d28d48cd4..84bdcf5ea5 100644 --- a/front/src/app/contents/forms/convention/textSetup.tsx +++ b/front/src/app/contents/forms/convention/textSetup.tsx @@ -173,6 +173,7 @@ const immersionTexts = (internshipKind: InternshipKind) => ({ markAsValidated: "Valider la demande", markAsCancelled: "Annuler la demande", conventionAlreadyCancelled: "Demande déjà annulée.", + markAsDeprecated: "Marquer la demande comme obsolète", }, sign: { title: diff --git a/front/src/app/pages/admin/EmailPreviewTab.tsx b/front/src/app/pages/admin/EmailPreviewTab.tsx index b9ff954189..703109ded1 100644 --- a/front/src/app/pages/admin/EmailPreviewTab.tsx +++ b/front/src/app/pages/admin/EmailPreviewTab.tsx @@ -449,4 +449,14 @@ export const defaultEmailValueByEmailKind: { IC_USER_RIGHTS_HAS_CHANGED: { agencyName: "AGENCY_NAME", }, + DEPRECATED_CONVENTION_NOTIFICATION: { + beneficiaryFirstName: "BENEFICIARY_FIRST_NAME", + beneficiaryLastName: "BENEFICIARY_LAST_NAME", + businessName: "BUSINESS_NAME", + dateStart: "DATE_START", + dateEnd: "DATE_END", + deprecationReason: "DEPRECATION_REASON", + immersionProfession: "IMMERSION_PROFESSION", + internshipKind: "immersion", + }, }; diff --git a/front/src/core-logic/domain/convention/convention.slice.ts b/front/src/core-logic/domain/convention/convention.slice.ts index d7d788c5a0..b6c938b0d6 100644 --- a/front/src/core-logic/domain/convention/convention.slice.ts +++ b/front/src/core-logic/domain/convention/convention.slice.ts @@ -16,7 +16,8 @@ type ConventionValidationFeedbackKind = | "modificationAskedFromCounsellorOrValidator" | "markedAsEligible" | "markedAsValidated" - | "cancelled"; + | "cancelled" + | "deprecated"; type ConventionSignatoryFeedbackKind = | "justSubmitted" diff --git a/shared/src/convention/ConventionDto.unit.test.ts b/shared/src/convention/ConventionDto.unit.test.ts index c04e3cc98f..b70eb2d7b9 100644 --- a/shared/src/convention/ConventionDto.unit.test.ts +++ b/shared/src/convention/ConventionDto.unit.test.ts @@ -303,6 +303,7 @@ describe("conventionDtoSchema", () => { "PARTIALLY_SIGNED", "REJECTED", "CANCELLED", + "DEPRECATED", ]); it.each(allowWithoutSignature.map((status) => ({ status })))( diff --git a/shared/src/convention/convention.dto.ts b/shared/src/convention/convention.dto.ts index 9aeabee494..4e1d5a8158 100644 --- a/shared/src/convention/convention.dto.ts +++ b/shared/src/convention/convention.dto.ts @@ -43,6 +43,7 @@ export const conventionStatusesWithJustification = [ "REJECTED", "DRAFT", "CANCELLED", + "DEPRECATED", ] as const; export const conventionStatuses = [ ...conventionStatusesWithoutJustification, diff --git a/shared/src/convention/conventionRefinements.ts b/shared/src/convention/conventionRefinements.ts index e1f0054438..9244f0b3ff 100644 --- a/shared/src/convention/conventionRefinements.ts +++ b/shared/src/convention/conventionRefinements.ts @@ -59,6 +59,7 @@ const statusesAllowedWithoutSign: ConventionStatus[] = [ "PARTIALLY_SIGNED", "REJECTED", "CANCELLED", + "DEPRECATED", ]; export const mustBeSignedByEveryone = ( diff --git a/shared/src/convention/conventionStatusTransitions.ts b/shared/src/convention/conventionStatusTransitions.ts index d87827b804..b89f61de5a 100644 --- a/shared/src/convention/conventionStatusTransitions.ts +++ b/shared/src/convention/conventionStatusTransitions.ts @@ -76,4 +76,14 @@ export const statusTransitionConfigs: Record< ...validSignatoryRoles, ], }, + DEPRECATED: { + validInitialStatuses: [ + "ACCEPTED_BY_COUNSELLOR", + "IN_REVIEW", + "PARTIALLY_SIGNED", + "READY_TO_SIGN", + "DRAFT", + ], + validRoles: ["counsellor", "validator", "backOffice"], + }, }; diff --git a/shared/src/domElementIds.ts b/shared/src/domElementIds.ts index 38f66434e0..982d2fe2de 100644 --- a/shared/src/domElementIds.ts +++ b/shared/src/domElementIds.ts @@ -228,6 +228,8 @@ export const domElementIds = { conventionValidationRequestEditButton: "im-convention-validation__request-edit-button", conventionValidationCancelButton: "im-convention-validation__cancel-button", + conventionValidationDeprecateButton: + "im-convention-validation__deprecate-button", justificationModalCancelButton: "im-justification-modal__cancel-button", justificationModalSubmitButton: "im-justification-modal__submit-button", }, diff --git a/shared/src/email/EmailParamsByEmailType.ts b/shared/src/email/EmailParamsByEmailType.ts index cb8d591fe7..ce49162477 100644 --- a/shared/src/email/EmailParamsByEmailType.ts +++ b/shared/src/email/EmailParamsByEmailType.ts @@ -123,6 +123,16 @@ export type EmailParamsByEmailType = { agency: string; agencyLogoUrl: AbsoluteUrl | undefined; }; + DEPRECATED_CONVENTION_NOTIFICATION: { + internshipKind: InternshipKind; + beneficiaryFirstName: string; + beneficiaryLastName: string; + deprecationReason: string; + businessName: string; + immersionProfession: string; + dateStart: string; + dateEnd: string; + }; CONVENTION_MODIFICATION_REQUEST_NOTIFICATION: { internshipKind: InternshipKind; beneficiaryFirstName: string; diff --git a/shared/src/email/emailTemplatesByName.ts b/shared/src/email/emailTemplatesByName.ts index 91c5a8edae..d60380f9d2 100644 --- a/shared/src/email/emailTemplatesByName.ts +++ b/shared/src/email/emailTemplatesByName.ts @@ -1153,4 +1153,48 @@ export const emailTemplatesByName = subContent: defaultSignature("immersion"), }), }, + DEPRECATED_CONVENTION_NOTIFICATION: { + niceName: "Notification de convention obsolète", + tags: ["dépreciation demande d'immersion"], + createEmailVariables: ({ + immersionProfession, + beneficiaryFirstName, + beneficiaryLastName, + deprecationReason, + businessName, + internshipKind, + dateStart, + dateEnd, + }) => ({ + subject: + internshipKind === "immersion" + ? `Demande d'immersion pour observer l'activité de ${immersionProfession} au sein de ${businessName} obsolète` + : `Mini Stage - Demande de mini stage pour l'activité de ${immersionProfession} au sein de ${businessName} obsolète`, + greetings: "Bonjour,", + content: ` + Nous vous informons que la demande ${ + internshipKind === "immersion" + ? "d'immersion professionnelle" + : "de mini stage" + } de ${beneficiaryFirstName} ${beneficiaryLastName} pour réaliser ${ + internshipKind === "immersion" + ? "une immersion professionnelle" + : "un mini stage" + } du ${ + isStringDate(dateStart) + ? toDisplayedDate(new Date(dateStart), true) + : "DATE INVALIDE" + } au ${ + isStringDate(dateEnd) + ? toDisplayedDate(new Date(dateEnd), true) + : "DATE INVALIDE" + } dans l'entreprise ${businessName} est supprimé. + + Les raisons en sont: ${deprecationReason}. + + Bien cordialement, + `, + subContent: defaultSignature(internshipKind), + }), + }, });