Checkpoint: - Ajout table opex_bases_repartition (11e table) avec 58 bases de répartition 2026 seedées depuis le JSON source
- Ajout procédure tRPC opex.getBasesRepartition et opex.setBaseRepartition - DsiOpex utilise maintenant les bases de répartition depuis la BDD (priorité BDD > JSON fallback pour 2025) - Total en bas à droite de la vue établissements affiche totalGlobal (somme postes BDD = 1 257 678 €) au lieu de la somme des arrondis par établissement (1 259 641 €) - 0 erreur TypeScript
This commit is contained in:
@@ -386,6 +386,8 @@ export default function DsiOpex() {
|
||||
const { data: postesRaw, isLoading: loadingPostes } = trpc.opex.getPostes.useQuery({ annee });
|
||||
const { data: montantsEtabRaw, isLoading: loadingMontants } = trpc.opex.getMontantsEtab.useQuery({ annee });
|
||||
const { data: validatedRow } = trpc.opex.getValidated.useQuery({ annee });
|
||||
// Bases de répartition depuis la BDD (charges classe 6 par établissement)
|
||||
const { data: basesRepartitionRaw } = trpc.opex.getBasesRepartition.useQuery({ annee });
|
||||
|
||||
// ── Mutations tRPC ─────────────────────────────────────────────────────────
|
||||
const upsertPoste = trpc.opex.upsertPoste.useMutation({
|
||||
@@ -685,12 +687,26 @@ export default function DsiOpex() {
|
||||
);
|
||||
|
||||
// Vue établissements : recalcul dynamique depuis les postes éditables
|
||||
// Les bases de répartition viennent de la BDD (table opex_bases_repartition)
|
||||
// Fallback sur le JSON source si la BDD n'a pas de données pour cette année
|
||||
const etablissementsRecalcules = useMemo(() => {
|
||||
const srcData = getOpexDataForAnnee(annee);
|
||||
const totalBase = srcData.etablissements.reduce((s, e) => s + e.base_repartition, 0);
|
||||
if (totalBase === 0) return srcData.etablissements.map(e => ({ ...e, montants: {} as Record<string, number>, total: 0 }));
|
||||
// Priorité : BDD > JSON source
|
||||
const basesFromBdd = basesRepartitionRaw && basesRepartitionRaw.length > 0
|
||||
? basesRepartitionRaw.map(b => ({
|
||||
code: b.etablissementCode,
|
||||
nom: b.etablissementNom ?? b.etablissementCode,
|
||||
base_repartition: parseFloat(b.baseRepartition ?? '0') || 0,
|
||||
}))
|
||||
: getOpexDataForAnnee(annee).etablissements.map(e => ({
|
||||
code: e.code,
|
||||
nom: e.nom,
|
||||
base_repartition: e.base_repartition,
|
||||
}));
|
||||
|
||||
return srcData.etablissements.map(etab => {
|
||||
const totalBase = basesFromBdd.reduce((s, e) => s + e.base_repartition, 0);
|
||||
if (totalBase === 0) return basesFromBdd.map(e => ({ ...e, montants: {} as Record<string, number>, total: 0 }));
|
||||
|
||||
return basesFromBdd.map(etab => {
|
||||
const ratio = etab.base_repartition / totalBase;
|
||||
const etabOverrides: Record<string, number> = montantsManuelEtab[etab.code] ?? {};
|
||||
|
||||
@@ -712,7 +728,7 @@ export default function DsiOpex() {
|
||||
|
||||
return { ...etab, montants: montantsRecalcules, total: totalEtab };
|
||||
});
|
||||
}, [lignes, montantsManuelEtab, annee]);
|
||||
}, [lignes, montantsManuelEtab, annee, basesRepartitionRaw]);
|
||||
|
||||
const filteredEtabs = useMemo(() => {
|
||||
return etablissementsRecalcules.filter(e => {
|
||||
@@ -1398,7 +1414,8 @@ export default function DsiOpex() {
|
||||
</td>
|
||||
{lignes.map(ligne => {
|
||||
const isManuel = ligne.mode_ventilation === 'Manuel' || ligne.isCustom;
|
||||
const totalColonne = sortedEtabs.reduce((s, e) => s + (e.montants[ligne.libelle] ?? 0), 0);
|
||||
// Utiliser le montant du poste (source BDD) pour éviter les écarts d'arrondis
|
||||
const montantPoste = ligne.montant;
|
||||
return (
|
||||
<td
|
||||
key={ligne.id}
|
||||
@@ -1406,14 +1423,15 @@ export default function DsiOpex() {
|
||||
isManuel ? 'text-blue-700 bg-blue-50/40' : 'text-emerald-700 bg-emerald-50/30'
|
||||
}`}
|
||||
>
|
||||
{totalColonne > 0
|
||||
? new Intl.NumberFormat('fr-FR', { maximumFractionDigits: 0 }).format(totalColonne) + ' €'
|
||||
{montantPoste > 0
|
||||
? new Intl.NumberFormat('fr-FR', { maximumFractionDigits: 0 }).format(montantPoste) + ' €'
|
||||
: '—'}
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
<td className="px-3 py-3 text-right font-bold text-orange-600 text-sm tabular-nums" style={{ position: 'sticky', right: 0, zIndex: 20, background: 'oklch(0.94 0.005 80)', boxShadow: '-3px 0 8px -2px rgba(0,0,0,0.18)', width: '120px', minWidth: '120px' }}>
|
||||
{formatEuros(sortedEtabs.reduce((s, e) => s + e.total, 0))}
|
||||
{/* Total depuis les montants BDD — pas la somme des arrondis par établissement */}
|
||||
{formatEuros(totalGlobal)}
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
|
||||
Reference in New Issue
Block a user