Checkpoint: v4 : Ajout pages Mes Solutions Numériques et Solutions Logicielles, onglets Import Établissements et Import Contacts avec CSV dans Admin, correction moteur de recherche (filtres solutionId/editeurId/blocFonctionnelId), CGU réaffichée à chaque session. 33 tests passés, 0 erreur TypeScript.
This commit is contained in:
171
server/db.ts
171
server/db.ts
@@ -1,4 +1,4 @@
|
||||
import { and, desc, eq, ilike, like, or, sql } from "drizzle-orm";
|
||||
import { and, desc, eq, ilike, inArray, like, or, sql } from "drizzle-orm";
|
||||
import { drizzle } from "drizzle-orm/mysql2";
|
||||
import {
|
||||
InsertUser,
|
||||
@@ -177,19 +177,40 @@ export async function searchEtablissements(filters: {
|
||||
}) {
|
||||
const db = await getDb();
|
||||
if (!db) return [];
|
||||
|
||||
const conditions = [];
|
||||
const conditions: any[] = [];
|
||||
|
||||
// Visibilité : si pas gestionnaire, on ne montre que les fiches "tous"
|
||||
if (filters.sonumRole !== "gestionnaire") {
|
||||
conditions.push(eq(etablissements.visibilite, "tous"));
|
||||
}
|
||||
|
||||
if (filters.region) conditions.push(eq(etablissements.region, filters.region));
|
||||
if (filters.typeActivite) conditions.push(eq(etablissements.typeActivite, filters.typeActivite));
|
||||
if (filters.tailleEffectifs) conditions.push(eq(etablissements.tailleEffectifs, filters.tailleEffectifs));
|
||||
|
||||
let query = db
|
||||
// Filtres sur les logiciels (nécessitent une jointure)
|
||||
const needsJoin = filters.solutionId || filters.editeurId || filters.blocFonctionnelId || filters.etatDeploiement;
|
||||
|
||||
if (needsJoin) {
|
||||
// Filtres sur la table logiciels_etablissements
|
||||
const leConditions: any[] = [eq(logicielsEtablissements.etablissementId, etablissements.id)];
|
||||
if (filters.solutionId) leConditions.push(eq(logicielsEtablissements.solutionId, filters.solutionId));
|
||||
if (filters.etatDeploiement) leConditions.push(eq(logicielsEtablissements.etatDeploiement, filters.etatDeploiement as any));
|
||||
|
||||
// Filtres sur les solutions (editeurId, blocFonctionnelId)
|
||||
const solConditions: any[] = [eq(solutions.id, logicielsEtablissements.solutionId)];
|
||||
if (filters.editeurId) solConditions.push(eq(solutions.editeurId, filters.editeurId));
|
||||
if (filters.blocFonctionnelId) solConditions.push(eq(solutions.blocFonctionnelId, filters.blocFonctionnelId));
|
||||
|
||||
const subquery = db
|
||||
.select({ etablissementId: logicielsEtablissements.etablissementId })
|
||||
.from(logicielsEtablissements)
|
||||
.innerJoin(solutions, and(...solConditions))
|
||||
.where(and(...leConditions));
|
||||
|
||||
conditions.push(inArray(etablissements.id, subquery));
|
||||
}
|
||||
|
||||
const result = await db
|
||||
.select({
|
||||
id: etablissements.id,
|
||||
finess: etablissements.finess,
|
||||
@@ -205,8 +226,7 @@ export async function searchEtablissements(filters: {
|
||||
.from(etablissements)
|
||||
.where(conditions.length > 0 ? and(...conditions) : undefined)
|
||||
.orderBy(etablissements.nom);
|
||||
|
||||
return query;
|
||||
return result;
|
||||
}
|
||||
|
||||
// ─── Logiciels par Établissement ─────────────────────────────────────────────
|
||||
@@ -595,3 +615,140 @@ export async function getAllUsersWithAffectations() {
|
||||
hasLocalCredentials: false, // sera enrichi côté router si besoin
|
||||
}));
|
||||
}
|
||||
|
||||
// ─── Mes Solutions Numériques ─────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Retourne toutes les solutions utilisées par les établissements dont l'utilisateur est référent/adhérent,
|
||||
* groupées par solution avec la liste des établissements équipés.
|
||||
*/
|
||||
export async function getMesSolutionsGroupees(userId: number, sonumRole: string) {
|
||||
const db = await getDb();
|
||||
if (!db) return [];
|
||||
|
||||
// Récupérer les établissements accessibles selon le rôle
|
||||
let etablissementIds: number[] = [];
|
||||
if (sonumRole === "gestionnaire") {
|
||||
const all = await db.select({ id: etablissements.id }).from(etablissements);
|
||||
etablissementIds = all.map((e) => e.id);
|
||||
} else if (sonumRole === "adherent") {
|
||||
etablissementIds = await getAffectationsByUser(userId);
|
||||
} else {
|
||||
// référent : établissements dont il est référent
|
||||
const refs = await db
|
||||
.select({ id: etablissements.id })
|
||||
.from(etablissements)
|
||||
.where(eq(etablissements.referentId, userId));
|
||||
etablissementIds = refs.map((e) => e.id);
|
||||
}
|
||||
|
||||
if (etablissementIds.length === 0) return [];
|
||||
|
||||
const rows = await db
|
||||
.select({
|
||||
solutionId: solutions.id,
|
||||
solutionNom: solutions.nom,
|
||||
editeurNom: editeurs.nom,
|
||||
blocFonctionnelNom: blocsFonctionnels.nom,
|
||||
etablissementId: etablissements.id,
|
||||
etablissementNom: etablissements.nom,
|
||||
etablissementRegion: etablissements.region,
|
||||
etatDeploiement: logicielsEtablissements.etatDeploiement,
|
||||
})
|
||||
.from(logicielsEtablissements)
|
||||
.innerJoin(solutions, eq(logicielsEtablissements.solutionId, solutions.id))
|
||||
.innerJoin(editeurs, eq(solutions.editeurId, editeurs.id))
|
||||
.leftJoin(blocsFonctionnels, eq(solutions.blocFonctionnelId, blocsFonctionnels.id))
|
||||
.innerJoin(etablissements, eq(logicielsEtablissements.etablissementId, etablissements.id))
|
||||
.where(inArray(logicielsEtablissements.etablissementId, etablissementIds))
|
||||
.orderBy(solutions.nom, etablissements.nom);
|
||||
|
||||
// Grouper par solution
|
||||
const map = new Map<number, {
|
||||
solutionId: number;
|
||||
solutionNom: string;
|
||||
editeurNom: string;
|
||||
blocFonctionnelNom: string | null;
|
||||
etablissements: { id: number; nom: string; region: string | null; etatDeploiement: string }[];
|
||||
}>();
|
||||
|
||||
for (const row of rows) {
|
||||
if (!map.has(row.solutionId)) {
|
||||
map.set(row.solutionId, {
|
||||
solutionId: row.solutionId,
|
||||
solutionNom: row.solutionNom ?? "",
|
||||
editeurNom: row.editeurNom ?? "",
|
||||
blocFonctionnelNom: row.blocFonctionnelNom ?? null,
|
||||
etablissements: [],
|
||||
});
|
||||
}
|
||||
map.get(row.solutionId)!.etablissements.push({
|
||||
id: row.etablissementId,
|
||||
nom: row.etablissementNom ?? "",
|
||||
region: row.etablissementRegion ?? null,
|
||||
etatDeploiement: row.etatDeploiement ?? "",
|
||||
});
|
||||
}
|
||||
|
||||
return Array.from(map.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne toutes les solutions du référentiel avec les établissements équipés (vue globale).
|
||||
* Accessible à tous les utilisateurs connectés.
|
||||
*/
|
||||
export async function getToutesLesSolutionsGroupees() {
|
||||
const db = await getDb();
|
||||
if (!db) return [];
|
||||
|
||||
const rows = await db
|
||||
.select({
|
||||
solutionId: solutions.id,
|
||||
solutionNom: solutions.nom,
|
||||
editeurNom: editeurs.nom,
|
||||
blocFonctionnelNom: blocsFonctionnels.nom,
|
||||
etablissementId: etablissements.id,
|
||||
etablissementNom: etablissements.nom,
|
||||
etablissementRegion: etablissements.region,
|
||||
etatDeploiement: logicielsEtablissements.etatDeploiement,
|
||||
})
|
||||
.from(solutions)
|
||||
.leftJoin(editeurs, eq(solutions.editeurId, editeurs.id))
|
||||
.leftJoin(blocsFonctionnels, eq(solutions.blocFonctionnelId, blocsFonctionnels.id))
|
||||
.leftJoin(logicielsEtablissements, eq(logicielsEtablissements.solutionId, solutions.id))
|
||||
.leftJoin(etablissements, eq(logicielsEtablissements.etablissementId, etablissements.id))
|
||||
.orderBy(solutions.nom, etablissements.nom);
|
||||
|
||||
const map = new Map<number, {
|
||||
solutionId: number;
|
||||
solutionNom: string;
|
||||
editeurNom: string;
|
||||
blocFonctionnelNom: string | null;
|
||||
nbEtablissements: number;
|
||||
etablissements: { id: number; nom: string; region: string | null; etatDeploiement: string }[];
|
||||
}>();
|
||||
|
||||
for (const row of rows) {
|
||||
if (!map.has(row.solutionId)) {
|
||||
map.set(row.solutionId, {
|
||||
solutionId: row.solutionId,
|
||||
solutionNom: row.solutionNom ?? "",
|
||||
editeurNom: row.editeurNom ?? "",
|
||||
blocFonctionnelNom: row.blocFonctionnelNom ?? null,
|
||||
nbEtablissements: 0,
|
||||
etablissements: [],
|
||||
});
|
||||
}
|
||||
if (row.etablissementId) {
|
||||
map.get(row.solutionId)!.etablissements.push({
|
||||
id: row.etablissementId,
|
||||
nom: row.etablissementNom ?? "",
|
||||
region: row.etablissementRegion ?? null,
|
||||
etatDeploiement: row.etatDeploiement ?? "",
|
||||
});
|
||||
map.get(row.solutionId)!.nbEtablissements++;
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(map.values());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user