From 1abd40cc1c3d846bf61a5bc99d21cf153c33a077 Mon Sep 17 00:00:00 2001 From: Manus Date: Wed, 10 Jun 2026 09:43:30 +0000 Subject: [PATCH] =?UTF-8?q?Checkpoint:=201.=20Bouton=20cliquable=20dans=20?= =?UTF-8?q?la=20colonne=20Ventilation=20(vue=20Postes,=20onglet=20D=C3=A9t?= =?UTF-8?q?ails)=20pour=20basculer=20Prorata=20classe=206=20=E2=86=94=20Ma?= =?UTF-8?q?nuel=20avec=20r=C3=A9percussion=20imm=C3=A9diate=20sur=20la=20v?= =?UTF-8?q?ue=20=C3=89tablissements.=202.=20Scroll=20vertical=20du=20table?= =?UTF-8?q?au=20vue=20=C3=89tablissements=20avec=20en-t=C3=AAte=20de=20col?= =?UTF-8?q?onnes=20sticky=20(top:0=20sur=20tous=20les=20th).=203.=20Recalc?= =?UTF-8?q?ul=20des=20montants=20par=20=C3=A9tablissement=20utilise=20lign?= =?UTF-8?q?e.mode=5Fventilation=20en=20priorit=C3=A9=20(valeur=20modifi?= =?UTF-8?q?=C3=A9e=20par=20l'utilisateur)=20au=20lieu=20de=20la=20source?= =?UTF-8?q?=20JSON.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/pages/DsiOpex.tsx | 64 ++++++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 10 deletions(-) diff --git a/client/src/pages/DsiOpex.tsx b/client/src/pages/DsiOpex.tsx index c71efd9..227078b 100644 --- a/client/src/pages/DsiOpex.tsx +++ b/client/src/pages/DsiOpex.tsx @@ -493,6 +493,19 @@ export default function DsiOpex() { const CATEGORIES_FILTER = useMemo(() => ['Toutes', ...categoriesPresentes], [categoriesPresentes]); + // Basculer le mode de ventilation d'une ligne (Prorata classe 6 ↔ Manuel) + const handleToggleVentilation = useCallback((id: string) => { + if (isValidated) return; + setOpexState(prev => ({ + ...prev, + lignes: prev.lignes.map(l => l.id === id ? { + ...l, + mode_ventilation: (l.mode_ventilation === 'Manuel') ? 'Prorata classe 6' : 'Manuel', + } : l), + })); + setIsDirty(true); + }, [isValidated]); + // Modifier un montant directement dans le tableau const handleMontantChange = useCallback((id: string, value: number) => { if (isValidated) return; @@ -683,14 +696,20 @@ export default function DsiOpex() { const montantsRecalcules: Record = {}; let totalEtab = 0; for (const ligne of opexState.lignes) { + // Priorité : mode_ventilation de la ligne (modifié par l'utilisateur) > source JSON > isCustom const srcPoste = srcData.postes.find(p => p.libelle === ligne.libelle); - const isManuel = srcPoste?.mode_ventilation === 'Manuel' || ligne.isCustom; + const modeVentilation = ligne.mode_ventilation ?? srcPoste?.mode_ventilation ?? null; + const isManuel = modeVentilation === 'Manuel' || ligne.isCustom; let montant: number; if (isManuel && etabOverrides[ligne.libelle] !== undefined) { // Override saisi manuellement par l'utilisateur montant = etabOverrides[ligne.libelle]; - } else if (ligne.montant > 0) { + } else if (!isManuel && ligne.montant > 0) { + // Prorata automatique montant = Math.round(ligne.montant * ratio); + } else if (isManuel) { + // Manuel sans override : 0 (en attente de saisie) + montant = 0; } else { montant = 0; } @@ -1122,7 +1141,28 @@ export default function DsiOpex() { {ligne.type || '—'} {ligne.facturation || '—'} {showDetails && {ligne.compte || '—'}} - {showDetails && {(() => { const mv = ligne.mode_ventilation || 'Prorata'; const isManuelV = mv === 'Manuel'; return {mv}; })()}} + {showDetails && ( + + {isValidated ? ( + {ligne.mode_ventilation || 'Prorata classe 6'} + ) : ( + + )} + + )} {formatNum(ligne.budget_n1)} @@ -1265,21 +1305,23 @@ export default function DsiOpex() { {/* Tableau scrollable horizontalement avec tous les postes */}
-
+
- - + {/* Colonnes postes — vert = prorata auto, bleu = manuel */} {opexState.lignes.map(ligne => { const srcPoste = getOpexDataForAnnee(annee).postes.find(p => p.libelle === ligne.libelle); - const isManuel = srcPoste?.mode_ventilation === 'Manuel' || ligne.isCustom; + const modeVentilation = ligne.mode_ventilation ?? srcPoste?.mode_ventilation ?? null; + const isManuel = modeVentilation === 'Manuel' || ligne.isCustom; return ( @@ -1308,7 +1350,8 @@ export default function DsiOpex() { {opexState.lignes.map(ligne => { const srcPoste = getOpexDataForAnnee(annee).postes.find(p => p.libelle === ligne.libelle); - const isManuel = srcPoste?.mode_ventilation === 'Manuel' || ligne.isCustom; + const modeVentilation = ligne.mode_ventilation ?? srcPoste?.mode_ventilation ?? null; + const isManuel = modeVentilation === 'Manuel' || ligne.isCustom; const montant = etab.montants[ligne.libelle] ?? 0; const cellKey = `${etab.code}|${ligne.libelle}`; const isEditing = inlineEditKey === cellKey; @@ -1373,7 +1416,8 @@ export default function DsiOpex() { {opexState.lignes.map(ligne => { const srcPoste = getOpexDataForAnnee(annee).postes.find(p => p.libelle === ligne.libelle); - const isManuel = srcPoste?.mode_ventilation === 'Manuel' || ligne.isCustom; + const modeVentilation = ligne.mode_ventilation ?? srcPoste?.mode_ventilation ?? null; + const isManuel = modeVentilation === 'Manuel' || ligne.isCustom; const totalColonne = sortedEtabs.reduce((s, e) => s + (e.montants[ligne.libelle] ?? 0), 0); return (
Code + Code ); })} - +
{etab.nom}