diff --git a/server/src/common/actions/effectifs.actions.ts b/server/src/common/actions/effectifs.actions.ts index 2863f8bef..b464132eb 100644 --- a/server/src/common/actions/effectifs.actions.ts +++ b/server/src/common/actions/effectifs.actions.ts @@ -2,7 +2,7 @@ import type { ICertification } from "api-alternance-sdk"; import Boom from "boom"; import { cloneDeep, isObject, merge, mergeWith, reduce, set, uniqBy } from "lodash-es"; import { ObjectId, type WithoutId } from "mongodb"; -import { IOpcos, IOrganisation, IRncp, IUsersMigration } from "shared/models"; +import { IEffecifMissionLocale, IOpcos, IOrganisation, IRncp, IUsersMigration } from "shared/models"; import { IEffectif } from "shared/models/data/effectifs.model"; import { IEffectifDECA } from "shared/models/data/effectifsDECA.model"; import { IOrganisme } from "shared/models/data/organismes.model"; @@ -468,8 +468,14 @@ export const updateEffectifComputedFromRNCP = async (rncp: IRncp, opco: IOpcos) }; export const buildEffectifForMissionLocale = ( - effectif: IEffectif & { organisation: IOrganisation } & { cfa_users: IUsersMigration } -) => { + effectif: IEffectif & { organisation: IOrganisation } & { cfa_users: Array } +): IEffecifMissionLocale => { + const users = effectif.cfa_users.map(({ nom, prenom, email, telephone }) => ({ + nom, + prenom, + email, + telephone, + })); const result = { _id: effectif._id, apprenant: { @@ -484,15 +490,10 @@ export const buildEffectifForMissionLocale = ( statut: effectif._computed?.statut, formation: effectif.formation, organisme: effectif._computed?.organisme, - user: { - nom: effectif.cfa_users?.nom, - fonction: effectif.cfa_users?.prenom, - email: effectif.cfa_users?.email, - telephone: effectif.cfa_users?.telephone, - }, + users, organisme_id: effectif.organisme_id, annee_scolaire: effectif.annee_scolaire, source: effectif.source, }; - return result; + return result as IEffecifMissionLocale; }; diff --git a/server/src/common/actions/effectifs/effectifs.actions.ts b/server/src/common/actions/effectifs/effectifs.actions.ts index f9088eb23..45e41c87a 100644 --- a/server/src/common/actions/effectifs/effectifs.actions.ts +++ b/server/src/common/actions/effectifs/effectifs.actions.ts @@ -1,5 +1,5 @@ import { ObjectId } from "mongodb"; -import { IEffectif, IOrganisation, IUsersMigration } from "shared/models"; +import { IEffecifMissionLocale, IEffectif, IOrganisation, IUsersMigration } from "shared/models"; import { getAnneesScolaireListFromDate } from "shared/utils"; import { organismeLookup } from "@/common/actions/helpers/filters"; @@ -172,12 +172,6 @@ export const getPaginatedEffectifsByMissionLocaleId = async ( as: "cfa_users", }, }, - { - $unwind: { - path: "$cfa_users", - preserveNullAndEmptyArrays: true, - }, - }, { $facet: { pagination: [{ $count: "total" }, { $addFields: { page, limit } }], @@ -189,16 +183,17 @@ export const getPaginatedEffectifsByMissionLocaleId = async ( const result = (await effectifsDb().aggregate(aggregation).next()) as { pagination: any; - data: Array; + data: Array }>; }; if (!result) { return { pagination: { total: 0, page, limit }, data: [] }; } + const { pagination, data } = result; - if (result.pagination) { - result.pagination.lastPage = Math.ceil(result.pagination.total / limit); + if (pagination) { + pagination.lastPage = Math.ceil(result.pagination.total / limit); } - result.data = result.data.map((effectif) => buildEffectifForMissionLocale(effectif)); - return result; + const effectifs: Array = data.map((effectif) => buildEffectifForMissionLocale(effectif)); + return { pagination, data: effectifs }; }; diff --git a/shared/models/data/effectifs.model.ts b/shared/models/data/effectifs.model.ts index f406c3299..32756b30c 100644 --- a/shared/models/data/effectifs.model.ts +++ b/shared/models/data/effectifs.model.ts @@ -103,6 +103,40 @@ export const zEffectifComputedStatut = z.object({ ), }); +export const zEffectifComputedOrganisme = z.object({ + region: zAdresse.shape.region.nullish(), + departement: zAdresse.shape.departement.nullish(), + academie: zAdresse.shape.academie.nullish(), + reseaux: z.array(zodEnumFromObjKeys(TETE_DE_RESEAUX_BY_ID)).describe("Réseaux du CFA, s'ils existent").nullish(), + bassinEmploi: z.string({}).nullish(), + + // 2 champs utiles seulement pour les indicateurs v1 + // à supprimer avec les prochains dashboards indicateurs/effectifs pour utiliser organisme_id + uai: z + .string({ + description: "Code UAI de l'établissement", + }) + .regex(UAI_REGEX) + .nullish(), + siret: z + .string({ + description: "N° SIRET de l'établissement", + }) + .regex(SIRET_REGEX) + .nullish(), + fiable: z + .boolean({ + description: `organismes.fiabilisation_statut == "FIABLE" && ferme != false`, + }) + .nullish(), +}); + +export const zEffectifAnneeScolaire = z + .string({ + description: `Année scolaire sur laquelle l'apprenant est enregistré (ex: "2020-2021")`, + }) + .regex(YEAR_RANGE_REGEX); + export const zEffectif = z.object({ _id: zObjectId.describe("Identifiant MongoDB de l'effectif"), organisme_id: zObjectId.describe("Organisme id (lieu de formation de l'apprenant pour la v3)"), @@ -118,11 +152,7 @@ export const zEffectif = z.object({ description: "Identifiant de l'organisme id source transmettant", }) .nullish(), - annee_scolaire: z - .string({ - description: `Année scolaire sur laquelle l'apprenant est enregistré (ex: "2020-2021")`, - }) - .regex(YEAR_RANGE_REGEX), + annee_scolaire: zEffectifAnneeScolaire, apprenant: zApprenant, formation: zFormationEffectif.nullish(), contrats: z @@ -187,38 +217,7 @@ export const zEffectif = z.object({ _computed: z .object( { - organisme: z - .object({ - region: zAdresse.shape.region.nullish(), - departement: zAdresse.shape.departement.nullish(), - academie: zAdresse.shape.academie.nullish(), - reseaux: z - .array(zodEnumFromObjKeys(TETE_DE_RESEAUX_BY_ID)) - .describe("Réseaux du CFA, s'ils existent") - .nullish(), - bassinEmploi: z.string({}).nullish(), - - // 2 champs utiles seulement pour les indicateurs v1 - // à supprimer avec les prochains dashboards indicateurs/effectifs pour utiliser organisme_id - uai: z - .string({ - description: "Code UAI de l'établissement", - }) - .regex(UAI_REGEX) - .nullish(), - siret: z - .string({ - description: "N° SIRET de l'établissement", - }) - .regex(SIRET_REGEX) - .nullish(), - fiable: z - .boolean({ - description: `organismes.fiabilisation_statut == "FIABLE" && ferme != false`, - }) - .nullish(), - }) - .nullish(), + organisme: zEffectifComputedOrganisme.nullish(), formation: z .object({ codes_rome: z.array(z.string()).nullish(), diff --git a/shared/models/data/usersMigration.model.ts b/shared/models/data/usersMigration.model.ts index 01eb1d12f..3a9d8d4c2 100644 --- a/shared/models/data/usersMigration.model.ts +++ b/shared/models/data/usersMigration.model.ts @@ -11,7 +11,7 @@ const indexes: [IndexSpecification, CreateIndexesOptions][] = [ [{ organisation_id: 1 }, {}], ]; -const zUsersMigration = z.object({ +export const zUsersMigration = z.object({ _id: zObjectId, email: z.string().describe("Email utilisateur"), password: z.string().describe("Le mot de passe hashé"), diff --git a/shared/models/routes/index.ts b/shared/models/routes/index.ts index a98d6f621..7d02d8793 100644 --- a/shared/models/routes/index.ts +++ b/shared/models/routes/index.ts @@ -1 +1,2 @@ export * from "./OrganismeSupportInfo"; +export * from "./mission-locale"; diff --git a/shared/models/routes/mission-locale/MissionLocaleEffectif.ts b/shared/models/routes/mission-locale/MissionLocaleEffectif.ts index 5ed0b1346..1beee5d5a 100644 --- a/shared/models/routes/mission-locale/MissionLocaleEffectif.ts +++ b/shared/models/routes/mission-locale/MissionLocaleEffectif.ts @@ -1,11 +1,18 @@ import { z } from "zod"; import { zObjectId } from "zod-mongodb-schema"; -import { zEffectif, zEffectifComputedStatut } from "../../data"; +import { SourceApprenantEnum } from "shared/constants/effectifs"; + +import { + zEffectifAnneeScolaire, + zEffectifComputedOrganisme, + zEffectifComputedStatut, + zUsersMigration, +} from "../../data"; import { zApprenant } from "../../data/effectifs/apprenant.part"; import { zFormationEffectif } from "../../data/effectifs/formation.part"; -const zEffectifMissionLocale = z.object({ +export const zEffectifMissionLocale = z.object({ _id: zObjectId, apprenant: zApprenant.pick({ nom: true, @@ -18,4 +25,11 @@ const zEffectifMissionLocale = z.object({ }), statut: zEffectifComputedStatut, formation: zFormationEffectif, + organisme: zEffectifComputedOrganisme, + users: z.array(zUsersMigration.pick({ nom: true, prenom: true, email: true, telephone: true })), + organisme_id: zObjectId, + annee_scolaire: zEffectifAnneeScolaire, + source: SourceApprenantEnum, }); + +export type IEffecifMissionLocale = z.infer; diff --git a/shared/models/routes/mission-locale/index.ts b/shared/models/routes/mission-locale/index.ts new file mode 100644 index 000000000..989f777a9 --- /dev/null +++ b/shared/models/routes/mission-locale/index.ts @@ -0,0 +1 @@ +export * from "./MissionLocaleEffectif";