Skip to content

Commit

Permalink
feat: import des organismes depuis l'api alternance
Browse files Browse the repository at this point in the history
  • Loading branch information
moroine committed Jan 29, 2025
1 parent 3b6730e commit df7b7df
Show file tree
Hide file tree
Showing 22 changed files with 732 additions and 1,378 deletions.
6 changes: 3 additions & 3 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@
"@types/stream-json": "^1.7.7",
"adm-zip": "^0.5.16",
"api-alternance-sdk": "^2.7.0",
"axios": "0.28.1",
"axios-cache-interceptor": "^0.10.7",
"axios-retry": "3.3.1",
"axios": "^1.7.9",
"axios-cache-interceptor": "^1.6.2",
"axios-retry": "^4.5.0",
"body-parser": "^1.20.3",
"boom": "7.3.0",
"bson": "^5.5.1",
Expand Down
29 changes: 0 additions & 29 deletions server/src/common/actions/engine/engine.organismes.utils.ts

This file was deleted.

97 changes: 1 addition & 96 deletions server/src/common/actions/organismes/organismes.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,7 @@ import {
IUsersMigration,
} from "shared";
import { IEffectifQueue } from "shared/models/data/effectifsQueue.model";
import {
IOrganisme,
defaultValuesOrganisme,
hasRecentTransmissions,
withOrganismeListSummary,
} from "shared/models/data/organismes.model";
import { IOrganisme, hasRecentTransmissions, withOrganismeListSummary } from "shared/models/data/organismes.model";
import { v4 as uuidv4 } from "uuid";

import {
Expand All @@ -26,7 +21,6 @@ import {
findOrganismeFormateursIds,
findOrganismeResponsablesIds,
} from "@/common/actions/helpers/permissions";
import { findDataFromSiret } from "@/common/actions/infoSiret.actions";
import { listContactsOrganisation } from "@/common/actions/organisations.actions";
import logger from "@/common/logger";
import {
Expand All @@ -37,101 +31,12 @@ import {
effectifsDECADb,
} from "@/common/model/collections";
import { AuthContext } from "@/common/model/internal/AuthContext";
import { stripEmptyFields } from "@/common/utils/miscUtils";
import { cleanProjection } from "@/common/utils/mongoUtils";
import { IReqPostVerifyUser } from "@/common/validation/ApiERPSchema";
import { ConfigurationERP } from "@/common/validation/configurationERPSchema";

import { OrganismeWithPermissions, buildOrganismePermissions } from "../helpers/permissions-organisme";
import { buildOrganismePerimetreMongoFilters } from "../indicateurs/organismes/organismes-filters";
import { InfoSiret } from "../infoSiret.actions-struct";

export type IOrganismeCreate = Partial<
Pick<
IOrganisme,
| "reseaux"
| "erps"
| "relatedFormations"
| "fiabilisation_statut"
| "ferme"
| "qualiopi"
| "contacts_from_referentiel"
| "created_at"
| "updated_at"
>
> &
Omit<
IOrganisme,
| "_id"
| "reseaux"
| "erps"
| "relatedFormations"
| "fiabilisation_statut"
| "ferme"
| "qualiopi"
| "prepa_apprentissage"
| "created_at"
| "updated_at"
>;

/**
* Méthode de création d'un organisme
* Checks uai format & existence
*/
export const createOrganisme = async (data: IOrganismeCreate): Promise<IOrganisme> => {
if ((await organismesDb().countDocuments({ uai: data.uai, siret: data.siret })) > 0) {
throw new Error(`Un organisme avec l'UAI ${data.uai} et le siret ${data.siret} existe déjà`);
}

const organisme: IOrganisme = {
_id: new ObjectId(),
...defaultValuesOrganisme(),
...stripEmptyFields(data),
};
await organismesDb().insertOne(organisme);
return organisme;
};

type OrganismeInfoFromSiret = Pick<IOrganisme, "nom" | "enseigne" | "raison_sociale" | "adresse" | "ferme">;

/**
* Fonction de récupération d'informations depuis SIRET via API Entreprise via siret
*/
export const getOrganismeInfosFromSiret = async (siret: string): Promise<Partial<OrganismeInfoFromSiret>> => {
let organismeInfos: Partial<OrganismeInfoFromSiret> = {};

if (siret) {
const dataSiret: InfoSiret = await findDataFromSiret(siret);

if (dataSiret.messages.api_entreprise_status === "OK") {
organismeInfos.ferme = !!dataSiret.result.ferme;

if (dataSiret.result.enseigne) {
organismeInfos.enseigne = dataSiret.result.enseigne;
organismeInfos.nom = dataSiret.result.enseigne;
}

if (dataSiret.result.raison_sociale) organismeInfos.raison_sociale = dataSiret.result.raison_sociale;

organismeInfos.adresse = {
...(dataSiret.result.numero_voie ? { numero: dataSiret.result.numero_voie } : {}),
...(dataSiret.result.voie_complete ? { voie: dataSiret.result.voie_complete } : {}),
...(dataSiret.result.complement_adresse ? { complement: dataSiret.result.complement_adresse } : {}),
...(dataSiret.result.code_postal ? { code_postal: dataSiret.result.code_postal } : {}),
...(dataSiret.result.code_insee_localite ? { code_insee: dataSiret.result.code_insee_localite } : {}),
...(dataSiret.result.localite ? { commune: dataSiret.result.localite } : {}),
...(dataSiret.result.num_departement ? { departement: dataSiret.result.num_departement as any } : {}),
...(dataSiret.result.num_region ? { region: dataSiret.result.num_region as any } : {}),
...(dataSiret.result.num_academie ? { academie: dataSiret.result.num_academie as any } : {}),
...(dataSiret.result.adresse ? { complete: dataSiret.result.adresse } : {}),
};
} else {
logger.error(`getOrganismeInfosFromSiret > Erreur > ${dataSiret.messages.api_entreprise_info}`);
}
}

return organismeInfos;
};

/**
* Méthode de récupération d'un organisme depuis un UAI et un SIRET
Expand Down
22 changes: 10 additions & 12 deletions server/src/common/actions/organismes/organismes.admin.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,9 @@ import { IUsersMigration } from "shared/models/data/usersMigration.model";

import { getCfdInfo } from "@/common/apis/apiAlternance/apiAlternance";
import { getEtablissement } from "@/common/apis/ApiEntreprise";
import { fetchOrganismeReferentielBySiret } from "@/common/apis/apiReferentielMna";
import logger from "@/common/logger";
import {
effectifsDb,
formationsCatalogueDb,
organisationsDb,
organismesDb,
organismesReferentielDb,
} from "@/common/model/collections";
import { effectifsDb, formationsCatalogueDb, organisationsDb, organismesDb } from "@/common/model/collections";

import { getTransmissionRelatedToOrganismeByDate } from "../indicateurs/transmissions/transmission.action";

Expand Down Expand Up @@ -214,7 +209,7 @@ async function findOrganismesSupportInfoBySiret(siret: string): Promise<Organism
return null;
}),
organismesDb().find({ siret }).toArray(),
organismesReferentielDb().find({ siret }).toArray(),
fetchOrganismeReferentielBySiret(siret),
getOffreFormations(siret),
organisationsDb()
.aggregate<IOrganisationOrganismeFormation & { users: IUsersMigration[] }>([
Expand All @@ -237,7 +232,6 @@ async function findOrganismesSupportInfoBySiret(siret: string): Promise<Organism
]);

const tdbByUai = new Map(tdb.map((o) => [o.uai ?? null, o]));
const referentielByUai = new Map(referentiel.map((o) => [o.uai ?? null, o]));
const organisationByUai = new Map(organisations.map((o) => [o.uai ?? null, o]));

const formationsByUai = new Map();
Expand All @@ -251,7 +245,11 @@ async function findOrganismesSupportInfoBySiret(siret: string): Promise<Organism
}
}

const uais = new Set([...tdbByUai.keys(), ...referentielByUai.keys(), ...formationsByUai.keys()]);
const uais = new Set([...tdbByUai.keys(), ...formationsByUai.keys()]);

if (referentiel) {
uais.add(referentiel.uai);
}

if (apiEntreprise && uais.size === 0) {
uais.add(null);
Expand All @@ -260,7 +258,7 @@ async function findOrganismesSupportInfoBySiret(siret: string): Promise<Organism
const organismes: OrganismeSupportInfo[] = [];
for (const uai of uais) {
const tdbOrganisme = tdbByUai.get(uai);
const referentielOrganisme = referentielByUai.get(uai);
const referentielOrganisme = referentiel?.uai === uai ? referentiel : null;

const etat = new Set<"fermé" | "actif" | "inconnu">();
if (tdbOrganisme) {
Expand Down Expand Up @@ -300,7 +298,7 @@ async function findOrganismesSupportInfoBySiret(siret: string): Promise<Organism
apiEntreprise?.unite_legale?.personne_morale_attributs?.raison_sociale ??
"Organisme inconnu",
tdb: tdbByUai.get(uai) ?? null,
referentiel: referentielByUai.get(uai) ?? null,
referentiel: referentiel?.uai === uai ? referentiel : null,
formations,
apiEntreprise: apiEntreprise,
organisation: organisationByUai.get(uai) ?? null,
Expand Down
59 changes: 17 additions & 42 deletions server/src/common/apis/apiReferentielMna.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IOrganismeReferentiel } from "shared/models/data/organismesReferentiel.model";
import { IOrganismeReferentiel, zOrganismeReferentiel } from "shared/models/data/organismesReferentiel.model";

import config from "@/config";

Expand All @@ -10,47 +10,22 @@ const axiosClient = getApiClient({
baseURL: config.mnaReferentielApi.endpoint,
});

const DEFAULT_REFERENTIEL_FIELDS_TO_FETCH = [
"adresse",
"enseigne",
"contacts",
"etat_administratif",
"forme_juridique",
"nature",
"numero_declaration_activite",
"qualiopi",
"raison_sociale",
"siret",
"siege_social",
"uai",
"lieux_de_formation",
"relations",
];

export const fetchOrganismes = async () => {
const {
data: { organismes },
} = await axiosClient.get<{ organismes: IOrganismeReferentiel[] }>("/organismes", {
params: {
items_par_page: 50000,
champs: DEFAULT_REFERENTIEL_FIELDS_TO_FETCH.join(","),
},
});

// comme le référentiel n'expose pas l'UAI directement dans les relations mais seulement le SIRET,
// on complète l'UAI des relations avec l'UAI stockées dans les organismes
const uaiBySiret = organismes.reduce((acc, organisme) => {
acc[organisme.siret] = organisme.uai;
return acc;
}, {});

organismes.forEach((organisme) => {
organisme?.relations?.forEach((relation) => {
if (relation.siret) {
relation.uai = uaiBySiret[relation.siret];
const DEFAULT_REFERENTIEL_FIELDS_TO_FETCH = Object.keys(zOrganismeReferentiel.shape);

export const fetchOrganismeReferentielBySiret = async (siret: string): Promise<IOrganismeReferentiel | null> => {
return axiosClient
.get<unknown>(`/organismes/${siret}`, {
params: {
champs: DEFAULT_REFERENTIEL_FIELDS_TO_FETCH.join(","),
},
})
.then((d) => zOrganismeReferentiel.parse(d.data))
.catch((error) => {
// Return null if the organism is not found
if (error.response?.status === 404) {
return null;
}
});
});

return organismes;
throw error;
});
};
5 changes: 3 additions & 2 deletions server/src/common/apis/client.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import http from "http";
import https from "https";

import axios from "axios";
import axios, { type AxiosRequestConfig } from "axios";
import { setupCache } from "axios-cache-interceptor";
import type { AxiosCacheInstance } from "axios-cache-interceptor";

const getApiClient = (options) =>
const getApiClient = (options: AxiosRequestConfig): AxiosCacheInstance =>
setupCache(
axios.create({
timeout: 5000,
Expand Down
6 changes: 0 additions & 6 deletions server/src/common/model/collections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ import opcosDescriptor, { IOpcos } from "shared/models/data/opco/opcos.model";
import opcosRncpDescriptor, { IOpcoRncp } from "shared/models/data/opco/opcosRncp.model";
import organisationsModelDescriptor, { IOrganisation } from "shared/models/data/organisations.model";
import OrganismesModelDescriptor, { IOrganisme } from "shared/models/data/organismes.model";
import OrganismesReferentielModelDescriptor, {
IOrganismeReferentiel,
} from "shared/models/data/organismesReferentiel.model";
import rncpModelDescriptor, { IRncp } from "shared/models/data/rncp.model";
import telechargementListesNominativesLogsDescriptor, {
ITelechargementListeNomLogs,
Expand All @@ -48,7 +45,6 @@ export const modelDescriptors = [
invitationsModelDescriptor,
organisationsModelDescriptor,
OrganismesModelDescriptor,
OrganismesReferentielModelDescriptor,
effectifsModelDescriptor,
effectifsQueueModelDescriptor,
rncpModelDescriptor,
Expand Down Expand Up @@ -76,8 +72,6 @@ export const jwtSessionsDb = () => getDbCollection<IJwtSession>(JwtSessionsModel
export const organismesDb = () => getDbCollection<IOrganisme>(OrganismesModelDescriptor.collectionName);
export const invitationsDb = () => getDbCollection<IInvitation>(invitationsModelDescriptor.collectionName);
export const organisationsDb = () => getDbCollection<IOrganisation>(organisationsModelDescriptor.collectionName);
export const organismesReferentielDb = () =>
getDbCollection<IOrganismeReferentiel>(OrganismesReferentielModelDescriptor.collectionName);
export const maintenanceMessageDb = () =>
getDbCollection<IMaintenanceMessage>(MaintenanceMessagesModelDescriptor.collectionName);
export const effectifsDb = () => getDbCollection<IEffectif>(effectifsModelDescriptor.collectionName);
Expand Down
Loading

0 comments on commit df7b7df

Please sign in to comment.