diff --git a/server/src/http/controllers/etablissementRecruteur.controller.ts b/server/src/http/controllers/etablissementRecruteur.controller.ts index 1b321264f0..d8c7f146a6 100644 --- a/server/src/http/controllers/etablissementRecruteur.controller.ts +++ b/server/src/http/controllers/etablissementRecruteur.controller.ts @@ -22,11 +22,10 @@ import { validateCreationEntrepriseFromCfa, } from "@/services/etablissement.service" import { getMainRoleManagement, getPublicUserRecruteurPropsOrError } from "@/services/roleManagement.service" -import { getUser2ByEmail, validateUser2Email } from "@/services/user2.service" +import { emailHasActiveRole, getUser2ByEmail, validateUser2Email } from "@/services/user2.service" import { autoValidateUser, createOrganizationUser, - getUserRecruteurByEmail, isUserEmailChecked, sendWelcomeEmailToUserRecruteur, setUserHasToBeManuallyValidated, @@ -177,8 +176,7 @@ export default (server: Server) => { const origin = req.body.origin ?? "formulaire public de création" const formatedEmail = email.toLocaleLowerCase() // check if user already exist - const userRecruteurOpt = await getUserRecruteurByEmail(formatedEmail) - if (userRecruteurOpt) { + if (await emailHasActiveRole(formatedEmail)) { throw Boom.forbidden("L'adresse mail est déjà associée à un compte La bonne alternance.") } diff --git a/server/src/jobs/lba_recruteur/formulaire/createUser.ts b/server/src/jobs/lba_recruteur/formulaire/createUser.ts index e16e309f5e..f65243e34b 100644 --- a/server/src/jobs/lba_recruteur/formulaire/createUser.ts +++ b/server/src/jobs/lba_recruteur/formulaire/createUser.ts @@ -2,8 +2,10 @@ import { VALIDATION_UTILISATEUR } from "shared/constants/recruteur" import { IUserRecruteur } from "shared/models" import { AccessStatus } from "shared/models/roleManagement.model" +import { emailHasActiveRole } from "@/services/user2.service" + import { logger } from "../../../common/logger" -import { createUser, getUserRecruteurByEmail } from "../../../services/userRecruteur.service" +import { createUser } from "../../../services/userRecruteur.service" export const createUserFromCLI = async ( { @@ -19,10 +21,8 @@ export const createUserFromCLI = async ( { options }: { options: { Type: IUserRecruteur["type"]; Email_valide: IUserRecruteur["is_email_checked"] } } ) => { const { Type, Email_valide } = options - const exist = await getUserRecruteurByEmail(email) - - if (exist) { - logger.error(`Users ${email} already exist - ${exist._id}`) + if (await emailHasActiveRole(email)) { + logger.error(`User ${email} already have an active role`) return } diff --git a/server/src/services/etablissement.service.ts b/server/src/services/etablissement.service.ts index ba372fbffb..88f3fa84f0 100644 --- a/server/src/services/etablissement.service.ts +++ b/server/src/services/etablissement.service.ts @@ -41,11 +41,11 @@ import { createFormulaire, getFormulaire } from "./formulaire.service" import mailer, { sanitizeForEmail } from "./mailer.service" import { getOpcoBySirenFromDB, saveOpco } from "./opco.service" import { modifyPermissionToUser } from "./roleManagement.service" +import { emailHasActiveRole } from "./user2.service" import { UserAndOrganization, autoValidateUser as authorizeUserOnEntreprise, createOrganizationUser, - getUserRecruteurByEmail, isUserEmailChecked, setEntrepriseInError, setEntrepriseValid, @@ -700,26 +700,25 @@ export const getEntrepriseDataFromSiret = async ({ siret, type }: { siret: strin const isCfaCreationValid = async (siret: string): Promise => { const cfa = await Cfa.findOne({ siret }).lean() if (!cfa) return true - const role = await RoleManagement.findOne({ authorized_type: AccessEntityType.CFA, authorized_id: cfa._id.toString() }).lean() - if (!role) return true - if (getLastStatusEvent(role.status)?.status !== AccessStatus.DENIED) { - return false - } - return true + const roles = await RoleManagement.find({ authorized_type: AccessEntityType.CFA, authorized_id: cfa._id.toString() }).lean() + const managingAccess = [AccessStatus.GRANTED, AccessStatus.AWAITING_VALIDATION] + const managingRoles = roles.filter((role) => { + const roleStatus = getLastStatusEvent(role.status)?.status + return roleStatus ? managingAccess.includes(roleStatus) : false + }) + return managingRoles.length ? false : true } -export const getOrganismeDeFormationDataFromSiret = async (siret: string, shouldValidate = true) => { - if (shouldValidate) { - const isValid = await isCfaCreationValid(siret) - if (!isValid) { - throw Boom.forbidden("Ce numéro siret est déjà associé à un compte utilisateur.", { reason: BusinessErrorCodes.ALREADY_EXISTS }) - } +export const getOrganismeDeFormationDataFromSiret = async (siret: string) => { + const isValid = await isCfaCreationValid(siret) + if (!isValid) { + throw Boom.forbidden("Ce numéro siret est déjà associé à un compte utilisateur.", { reason: BusinessErrorCodes.ALREADY_EXISTS }) } const referentiel = await getEtablissementFromReferentiel(siret) if (!referentiel) { throw Boom.badRequest("Le numéro siret n'est pas référencé comme centre de formation.", { reason: BusinessErrorCodes.UNKNOWN }) } - if (shouldValidate && referentiel.etat_administratif === "fermé") { + if (referentiel.etat_administratif === "fermé") { throw Boom.badRequest("Le numéro siret indique un établissement fermé.", { reason: BusinessErrorCodes.CLOSED }) } if (!referentiel.adresse) { @@ -728,7 +727,7 @@ export const getOrganismeDeFormationDataFromSiret = async (siret: string, should }) } const formattedReferentiel = formatReferentielData(referentiel) - if (shouldValidate && !formattedReferentiel.is_qualiopi) { + if (!formattedReferentiel.is_qualiopi) { throw Boom.badRequest("L’organisme rattaché à ce SIRET n’est pas certifié Qualiopi", { reason: BusinessErrorCodes.NOT_QUALIOPI, ...formattedReferentiel }) } return formattedReferentiel @@ -767,8 +766,7 @@ export const entrepriseOnboardingWorkflow = { const cfaErrorOpt = await validateCreationEntrepriseFromCfa({ siret, cfa_delegated_siret }) if (cfaErrorOpt) return cfaErrorOpt const formatedEmail = email.toLocaleLowerCase() - const userRecruteurOpt = await getUserRecruteurByEmail(formatedEmail) - if (userRecruteurOpt) { + if (await emailHasActiveRole(formatedEmail)) { return errorFactory("L'adresse mail est déjà associée à un compte La bonne alternance.", BusinessErrorCodes.ALREADY_EXISTS) } let entrepriseData: Partial diff --git a/server/src/services/user2.service.ts b/server/src/services/user2.service.ts index 42499efc4e..682bb7cae4 100644 --- a/server/src/services/user2.service.ts +++ b/server/src/services/user2.service.ts @@ -1,8 +1,10 @@ import Boom from "boom" import { VALIDATION_UTILISATEUR } from "shared/constants/recruteur" +import { AccessStatus } from "shared/models/roleManagement.model" import { IUser2, IUserStatusEvent, UserEventType } from "shared/models/user2.model" +import { getLastStatusEvent } from "shared/utils" -import { User2 } from "@/common/model" +import { RoleManagement, User2 } from "@/common/model" import { ObjectId } from "@/common/mongodb" import { isUserEmailChecked } from "./userRecruteur.service" @@ -74,3 +76,15 @@ export const validateUser2Email = async (id: string): Promise => { } export const getUser2ByEmail = async (email: string): Promise => User2.findOne({ email: email.toLocaleLowerCase() }).lean() + +export const emailHasActiveRole = async (email: string) => { + const userOpt = await getUser2ByEmail(email) + if (!userOpt) return + const roles = await RoleManagement.find({ user_id: userOpt._id }).lean() + const activeStatus = [AccessStatus.GRANTED, AccessStatus.AWAITING_VALIDATION] + const activeRoles = roles.filter((role) => { + const roleStatus = getLastStatusEvent(role.status)?.status + return roleStatus ? activeStatus.includes(roleStatus) : false + }) + return Boolean(activeRoles.length) +} diff --git a/server/src/services/userRecruteur.service.ts b/server/src/services/userRecruteur.service.ts index ca7cf646ed..bd5a7ae780 100644 --- a/server/src/services/userRecruteur.service.ts +++ b/server/src/services/userRecruteur.service.ts @@ -76,7 +76,6 @@ const roleStatusToUserRecruteurStatus = (roleStatus: AccessStatus): ETAT_UTILISA } export const getUserRecruteurById = (id: string | ObjectIdType) => getUserRecruteurByUser2Query({ _id: typeof id === "string" ? new ObjectId(id) : id }) -export const getUserRecruteurByEmail = (email: string) => getUserRecruteurByUser2Query({ email }) export const userAndRoleAndOrganizationToUserRecruteur = ( user: IUser2,