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

feat: ajout du type de l'organisation #3933

18 changes: 13 additions & 5 deletions server/src/common/actions/account.actions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Boom from "boom";
import { STATUT_FIABILISATION_ORGANISME } from "shared";
import { IOrganisme, STATUT_FIABILISATION_ORGANISME } from "shared";

import logger from "@/common/logger";
import { auditLogsDb, invitationsDb, organisationsDb, usersMigrationDb } from "@/common/model/collections";
Expand All @@ -23,36 +23,44 @@ export async function register(registration: RegistrationSchema): Promise<{
account_status: "PENDING_EMAIL_VALIDATION" | "CONFIRMED";
}> {
const alreadyExists = await getUserByEmail(registration.user.email);
let registrationExtraData: { organisme_id?: string } = {};
if (alreadyExists) {
throw Boom.conflict("Cet email est déjà utilisé.");
}

// on s'assure que l'organisme existe pour un OF
const type = registration.organisation.type;
if (type === "ORGANISME_FORMATION") {
let organisme: IOrganisme | undefined;
const { uai, siret } = registration.organisation;
try {
await getOrganismeByUAIAndSIRET(uai, siret);
organisme = await getOrganismeByUAIAndSIRET(uai, siret);
} catch (err) {
// si pas d'organisme en base (et donc le référentiel), on en crée un à partir depuis API entreprise
logger.warn({ action: "register", uai, siret }, "organisme inconnu créé");
// Récupération des infos depuis API Entreprise si option active, sinon renvoi des nom / adresse passé en paramètres
const data = await getOrganismeInfosFromSiret(siret);

// si aucun champ ferme fourni en entrée on récupère celui de l'organisme trouvé par son id
await createOrganisme({
organisme = await createOrganisme({
...data,
...(uai ? { uai } : {}),
siret,
fiabilisation_statut: STATUT_FIABILISATION_ORGANISME.NON_FIABLE,
organismesFormateurs: [],
organismesResponsables: [],
});
} finally {
registrationExtraData = { organisme_id: organisme?._id.toString() };
}
}

const organisation = await organisationsDb().findOne(registration.organisation);
const organisationId = organisation ? organisation._id : await createOrganisation(registration.organisation);
const organisationId = organisation
? organisation._id
: await createOrganisation({
...registration.organisation,
...registrationExtraData,
});

const userId = await createUser(registration.user, organisationId);

Expand Down
32 changes: 31 additions & 1 deletion server/src/common/actions/effectifs.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, IRncp } from "shared/models";
import { 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";
Expand Down Expand Up @@ -466,3 +466,33 @@ export const updateEffectifComputedFromRNCP = async (rncp: IRncp, opco: IOpcos)
)
);
};

export const buildEffectifForMissionLocale = (
effectif: IEffectif & { organisation: IOrganisation } & { cfa_users: IUsersMigration }
) => {
const result = {
_id: effectif._id,
apprenant: {
nom: effectif.apprenant.nom,
prenom: effectif.apprenant.prenom,
date_de_naissance: effectif.apprenant.date_de_naissance,
adresse: effectif.apprenant.adresse,
telephone: effectif.apprenant.telephone,
courriel: effectif.apprenant.courriel,
rqth: effectif.apprenant.rqth,
},
parcours: 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,
},
organisme_id: effectif.organisme_id,
annee_scolaire: effectif.annee_scolaire,
source: effectif.source,
};
return result;
};
61 changes: 61 additions & 0 deletions server/src/common/actions/effectifs/effectifs.actions.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { ObjectId } from "mongodb";
import { IEffectif, IOrganisation, IUsersMigration } from "shared/models";
import { getAnneesScolaireListFromDate } from "shared/utils";

import { organismeLookup } from "@/common/actions/helpers/filters";
import { effectifsDb } from "@/common/model/collections";

import { buildEffectifForMissionLocale } from "../effectifs.actions";

// Méthode de récupération de la liste des effectifs en base
export const getAllEffectifs = async (
query = {},
Expand Down Expand Up @@ -132,3 +136,60 @@ export const getDetailedEffectifById = async (_id: any) => {

return organisme;
};

export const getPaginatedEffectifsByMissionLocaleId = async (
missionLocaleId: string,
page: number = 1,
limit: number = 20
) => {
const aggregation = [
{
$match: {
"_computed.missionLocale.id": missionLocaleId,
annee_scolaire: { $in: getAnneesScolaireListFromDate(new Date()) },
},
},
{ $addFields: { stringify_organisme_id: { $toString: "$organisme_id" } } },
{
$lookup: {
from: "organisations",
localField: "stringify_organisme_id",
foreignField: "organisme_id",
as: "organisation",
},
},
{
$unwind: {
path: "$organisation",
preserveNullAndEmptyArrays: true,
},
},
{
$lookup: {
from: "usersMigration",
localField: "organisation._id",
foreignField: "organisation_id",
as: "cfa_users",
},
},
{
$unwind: {
path: "$cfa_users",
preserveNullAndEmptyArrays: true,
},
},
{
$facet: {
pagination: [{ $count: "total" }, { $addFields: { page, limit } }],
data: [{ $skip: (page - 1) * limit }, { $limit: limit }],
},
},
{ $unwind: { path: "$pagination" } },
];
const { pagination, data } = (await effectifsDb().aggregate(aggregation).next()) as {
pagination: any;
data: Array<IEffectif & { organisation: IOrganisation } & { cfa_users: IUsersMigration }>;
};
const effectifs = data.map((effectif) => buildEffectifForMissionLocale(effectif));
return { pagination, data: effectifs };
};
20 changes: 20 additions & 0 deletions server/src/common/actions/helpers/permissions-organisme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,26 @@ export type OrganismeWithPermissions = IOrganisme & { permissions: PermissionsOr
};
export async function getAcl(organisation: IOrganisation): Promise<Acl> {
switch (organisation.type) {
// Tout a false pour les missions locales
// Cela assure aucun accès potentiels aux autres apis

case "MISSION_LOCALE": {
return {
viewContacts: false,
infoTransmissionEffectifs: false,
indicateursEffectifs: false,
effectifsNominatifs: {
apprenant: false,
apprenti: false,
inscritSansContrat: false,
rupturant: false,
abandon: false,
inconnu: false,
},
manageEffectifs: false,
configurerModeTransmission: false,
};
}
case "ORGANISME_FORMATION": {
const userOrganisme = await organismesDb().findOne({
siret: organisation.siret,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { effectifsDb } from "@/common/model/collections";

import { DateFilters } from "../helpers/filters";

import { buildIndicateursEffectifsPipeline } from "./indicateurs.actions";

export const getEffectifIndicateursForMissionLocaleId = async (filters: DateFilters, missionLocaleId: string) => {
const aggregation = [
{
$match: {
"_computed.missionLocale.id": missionLocaleId,
},
},
...buildIndicateursEffectifsPipeline(null, filters.date),
{
$project: {
_id: 0,
inscrits: 1,
abandons: 1,
rupturants: 1,
},
},
];
const indicateurs = await effectifsDb().aggregate(aggregation).toArray();
return indicateurs ?? { inscrits: 0, abandons: 0, rupturants: 0 };
};
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { buildEffectifMongoFilters } from "./effectifs/effectifs-filters";
import { buildDECAFilter } from "./indicateurs-with-deca.actions";
import { buildOrganismeMongoFilters } from "./organismes/organismes-filters";

function buildIndicateursEffectifsPipeline(groupBy: string | null, currentDate: Date) {
export function buildIndicateursEffectifsPipeline(groupBy: string | null, currentDate: Date) {
return [
{
$addFields: {
Expand Down
2 changes: 2 additions & 0 deletions server/src/common/actions/organisations.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,8 @@ async function getInvitationById(ctx: AuthContext, invitationId: ObjectId): Prom
export async function buildOrganisationLabel(organisationId: ObjectId): Promise<string> {
const organisation = await getOrganisationById(organisationId);
switch (organisation.type) {
case "MISSION_LOCALE":
return `Mission locale ${organisation.nom}`;
case "ORGANISME_FORMATION": {
const organisme = await organismesDb().findOne({
siret: organisation.siret,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,25 @@ exports[`validation-schema > should create validation schema for effectifs > eff
},
],
},
"missionLocale": {
"anyOf": [
{
"additionalProperties": false,
"bsonType": "object",
"properties": {
"id": {
"bsonType": "string",
},
},
"required": [
"id",
],
},
{
"bsonType": "null",
},
],
},
"organisme": {
"anyOf": [
{
Expand Down Expand Up @@ -2700,6 +2719,25 @@ exports[`validation-schema > should create validation schema for effectifsArchiv
},
],
},
"missionLocale": {
"anyOf": [
{
"additionalProperties": false,
"bsonType": "object",
"properties": {
"id": {
"bsonType": "string",
},
},
"required": [
"id",
],
},
{
"bsonType": "null",
},
],
},
"organisme": {
"anyOf": [
{
Expand Down Expand Up @@ -8947,6 +8985,43 @@ exports[`validation-schema > should create validation schema for opcos > opcos 1
exports[`validation-schema > should create validation schema for organisations > organisations 1`] = `
{
"anyOf": [
{
"additionalProperties": false,
"bsonType": "object",
"properties": {
"_id": {
"bsonType": "objectId",
},
"created_at": {
"bsonType": "date",
"description": "Date de création en base de données",
},
"ml_id": {
"bsonType": "number",
"description": "Identifiant de la mission locale",
},
"nom": {
"bsonType": "string",
"description": "Nom de la mission locale",
},
"siret": {
"bsonType": "string",
"description": "N° SIRET",
"pattern": "^[0-9]{14}$",
},
"type": {
"bsonType": "string",
},
},
"required": [
"_id",
"created_at",
"type",
"nom",
"siret",
"ml_id",
],
},
{
"additionalProperties": false,
"bsonType": "object",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { addJob } from "job-processor";

export const up = async () => {
await addJob({
name: "tmp:migration:organisation-organisme",
queued: true,
});
};
15 changes: 14 additions & 1 deletion server/src/http/middlewares/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Boom from "boom";
import { NextFunction, Request, RequestHandler, Response } from "express";
import { ObjectId } from "mongodb";
import { IEffectif, ORGANISATION_TYPE, PermissionOrganisme } from "shared";
import { IEffectif, IOrganisationMissionLocale, ORGANISATION_TYPE, PermissionOrganisme } from "shared";
import { IEffectifDECA } from "shared/models/data/effectifsDECA.model";

import { getOrganismePermission } from "@/common/actions/helpers/permissions-organisme";
Expand Down Expand Up @@ -41,6 +41,19 @@ export function requireAdministrator(req: Request, _res: Response, next: NextFun
next();
}

export function requireMissionLocale(req: Request, res: Response, next: NextFunction) {
const user = req.user as AuthContext;
ensureValidUser(user);
if (user.organisation.type !== "MISSION_LOCALE") {
throw Boom.forbidden("Accès non autorisé");
}

const orga = user.organisation as IOrganisationMissionLocale;

res.locals.missionLocale = orga;
next();
}

interface MyLocals {
organismeId: ObjectId;
}
Expand Down
Loading
Loading