Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: lbac 1490: reprise des entreprises à mettre à jour #1214

Merged
merged 5 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cypress/pages/FlowCreationEntreprise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export const FlowCreationEntreprise = {
cy.get("[data-testid='offre-metier'] input").type(typedRomeLabel)
cy.wait("@romeSearch")
// cy.get(`[data-testid='offre-metier'] #downshift-1-item-0 p:first-of-type`, { timeout: 10000 }).should("have.text", romeLabel)
cy.get(`[data-testid='offre-metier'] [data-testid='${romeLabel}']`).click()
cy.get(`[data-testid='offre-metier'] [data-testid='${romeLabel}']`, { timeout: 20000 }).click({ timeout: 20000 })

cy.get("[data-testid='offre-job-type'] [data-testid='Apprentissage']").click()
if (Apprentissage) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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.")
}

Expand All @@ -195,7 +193,7 @@ export default (server: Server) => {
}
if (!contacts.length) {
// Validation manuelle de l'utilisateur à effectuer pas un administrateur
await setUserHasToBeManuallyValidated(creationResult, origin, "pas d'email de contact")
await setUserHasToBeManuallyValidated(creationResult, origin)
await notifyToSlack(slackNotification)
return res.status(200).send({ user: userCfa, validated: false })
}
Expand All @@ -218,7 +216,7 @@ export default (server: Server) => {
}
}
// Validation manuelle de l'utilisateur à effectuer pas un administrateur
await setUserHasToBeManuallyValidated(creationResult, origin, "pas de validation automatique possible")
await setUserHasToBeManuallyValidated(creationResult, origin)
await notifyToSlack(slackNotification)
// Keep the same structure as ENTREPRISE
return res.status(200).send({ user: userCfa, validated: false })
Expand Down
10 changes: 5 additions & 5 deletions server/src/jobs/lba_recruteur/formulaire/createUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
{
Expand All @@ -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
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Boom from "boom"
import { JOB_STATUS } from "shared"
import { CFA, RECRUITER_STATUS } from "shared/constants/recruteur"
import { EntrepriseStatus } from "shared/models/entreprise.model"
import { CFA, RECRUITER_STATUS, VALIDATION_UTILISATEUR } from "shared/constants/recruteur"
import { EntrepriseStatus, IEntrepriseStatusEvent } from "shared/models/entreprise.model"
import { AccessEntityType, AccessStatus } from "shared/models/roleManagement.model"
import { getLastStatusEvent } from "shared/utils/getLastStatusEvent"

Expand Down Expand Up @@ -40,13 +40,21 @@ const updateEntreprisesInfosInError = async () => {
if (!updatedEntreprise) {
throw Boom.internal(`could not find and update entreprise with id=${_id}`)
}
const entrepriseEvent: IEntrepriseStatusEvent = {
date: new Date(),
reason: "reprise des données des entreprises en erreur / MAJ",
status: EntrepriseStatus.VALIDE,
validation_type: VALIDATION_UTILISATEUR.AUTO,
granted_by: "",
}
await Entreprise.updateOne({ _id }, { $push: { status: entrepriseEvent } })
await Recruiter.updateMany({ establishment_siret: siret }, entrepriseData)
const recruiters = await Recruiter.find({ establishment_siret: siret }).lean()
const roles = await RoleManagement.find({ authorized_type: AccessEntityType.ENTREPRISE, authorized_id: updatedEntreprise._id.toString() }).lean()
const rolesToUpdate = roles.filter((role) => getLastStatusEvent(role.status)?.status !== AccessStatus.DENIED)
const users = await User2.find({ _id: { $in: rolesToUpdate.map((role) => role.user_id) } }).lean()
await Promise.all(
users.map(async (user) => {
if (getLastStatusEvent(entreprise.status)?.status === EntrepriseStatus.ERROR) {
const recruiters = await Recruiter.find({ establishment_siret: siret }).lean()
const roles = await RoleManagement.find({ authorized_type: AccessEntityType.ENTREPRISE, authorized_id: updatedEntreprise._id.toString() }).lean()
const rolesToUpdate = roles.filter((role) => getLastStatusEvent(role.status)?.status !== AccessStatus.DENIED)
const users = await User2.find({ _id: { $in: rolesToUpdate.map((role) => role.user_id) } }).lean()
await asyncForEach(users, async (user) => {
const userAndOrganization: UserAndOrganization = { user, type: ENTREPRISE, organization: updatedEntreprise }
const result = await autoValidateUserRoleOnCompany(userAndOrganization, "reprise des entreprises en erreur")
if (result.validated) {
Expand All @@ -63,7 +71,7 @@ const updateEntreprisesInfosInError = async () => {
await sendEmailConfirmationEntreprise(user, recruiter, status, EntrepriseStatus.VALIDE)
}
})
)
}
stats.success++
}
} catch (err) {
Expand Down
2 changes: 1 addition & 1 deletion server/src/jobs/multiCompte/migrationUsers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ const migrationUserRecruteurs = async () => {
}
} else {
if (getLastStatusEvent(newEntreprise.status)?.status !== EntrepriseStatus.ERROR) {
if (!newEntreprise.address || !newEntreprise.address_detail || !newEntreprise.enseigne || !newEntreprise.raison_sociale || !newEntreprise.geo_coordinates) {
if (!newEntreprise.address || !newEntreprise.address_detail || !newEntreprise.raison_sociale || !newEntreprise.geo_coordinates) {
newEntreprise.status.push({
date: new Date(),
reason: "champ manquant",
Expand Down
34 changes: 16 additions & 18 deletions server/src/services/etablissement.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -569,7 +569,7 @@ export const autoValidateUserRoleOnCompany = async (userAndEntreprise: UserAndOr
if (validated) {
await authorizeUserOnEntreprise(userAndEntreprise, origin, reason)
} else {
await setUserHasToBeManuallyValidated(userAndEntreprise, origin, reason)
await setUserHasToBeManuallyValidated(userAndEntreprise, origin)
}
return { validated }
}
Expand Down Expand Up @@ -700,26 +700,25 @@ export const getEntrepriseDataFromSiret = async ({ siret, type }: { siret: strin
const isCfaCreationValid = async (siret: string): Promise<boolean> => {
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) {
Expand All @@ -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
Expand Down Expand Up @@ -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<EntrepriseData>
Expand Down
16 changes: 15 additions & 1 deletion server/src/services/user2.service.ts
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -74,3 +76,15 @@ export const validateUser2Email = async (id: string): Promise<IUser2> => {
}

export const getUser2ByEmail = async (email: string): Promise<IUser2 | null> => 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)
}
3 changes: 1 addition & 2 deletions server/src/services/userRecruteur.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -375,7 +374,7 @@ export const autoValidateUser = async (props: UserAndOrganization, origin: strin
await setAccessOfUserOnOrganization(props, AccessStatus.GRANTED, origin, reason)
}

export const setUserHasToBeManuallyValidated = async (props: UserAndOrganization, origin: string, reason: string) => {
export const setUserHasToBeManuallyValidated = async (props: UserAndOrganization, origin: string, reason = "pas de validation automatique possible") => {
await setAccessOfUserOnOrganization(props, AccessStatus.AWAITING_VALIDATION, origin, reason)
}

Expand Down
Loading