Mise en place des objectifs

This commit is contained in:
2026-07-13 17:53:22 +02:00
parent 63f38b5ff0
commit 86a047a0c5
7 changed files with 508 additions and 1 deletions
+52
View File
@@ -12,6 +12,7 @@ import SoldeChart from '../components/SoldeChart.jsx';
import DistributionChart from '../components/DistributionChart.jsx';
import { fmtEUR, fmtDate, today } from '../utils/format.js';
import DepotsMensuelTable from '../components/DepotsMensuelTable.jsx';
import SuiviObjectifs from '../components/SuiviObjectifs.jsx';
import * as XLSX from 'xlsx';
/* ── Helpers export ──────────────────────────────────────────── */
@@ -397,6 +398,14 @@ export default function DepotsRetraits() {
const [allCorrections, setAllCorrections] = useState([]);
const [deleteConfirm, setDeleteConfirm] = useState(null);
/* Objectifs de versement annuel — pour enrichir le KPI "Diff. Dépôts vs Retraits" */
const [objectifsKpi, setObjectifsKpi] = useState([]);
useEffect(() => {
api.get('/objectifs', { type: 'versement_annuel' })
.then(r => setObjectifsKpi(Array.isArray(r) ? r : []))
.catch(() => setObjectifsKpi([]));
}, []);
/* Lecture des params de navigation depuis la page Plateformes */
useEffect(() => {
const p = new URLSearchParams(search);
@@ -681,6 +690,26 @@ export default function DepotsRetraits() {
return balance;
})();
/* ── Enrichissement du KPI "Diff. Dépôts vs Retraits" avec l'objectif de l'année ──
Toujours calculé sur le portefeuille entier (ignore le filtre plateforme),
pour l'année sélectionnée (drPlatYear, ou l'année en cours si "Toutes les années"). */
const scopeInvestisseurIdsKpi = activeView === 'all' ? investisseurs.map(i => i.id) : [activeId];
const kpiObjectifYear = Number(drPlatYear || new Date().getFullYear());
const kpiObjectifTotal = (() => {
const list = objectifsKpi.filter(o => o.annee === kpiObjectifYear && scopeInvestisseurIdsKpi.includes(o.investisseur_id));
return list.length ? list.reduce((s, o) => s + o.montant, 0) : null;
})();
const kpiDiffGlobalYear = (() => {
let d = 0, r = 0;
for (const row of allRows) {
if (!scopeInvestisseurIdsKpi.includes(row.investisseur_id)) continue;
if (row.date_operation?.slice(0, 4) !== String(kpiObjectifYear)) continue;
if (row.type === 'depot') d += row.montant; else r += row.montant;
}
return d - r;
})();
const kpiEcartObjectif = kpiObjectifTotal != null ? kpiDiffGlobalYear - kpiObjectifTotal : null;
const multiDetenteur = new Set(plats.map(p => p.investisseur_id)).size > 1;
/** Investisseur par défaut selon la vue active */
@@ -966,6 +995,16 @@ export default function DepotsRetraits() {
<TrendBadge current={totals.depots - totals.retraits} prev={prevTotals.depots - prevTotals.retraits} />
</div>
<div style={{ fontSize: '0.8em', color: 'var(--text-muted)', marginTop: 5 }}>{fmtEUR(prevTotals.depots - prevTotals.retraits)} en {prevYear}</div>
{kpiEcartObjectif != null && (
<div style={{
fontSize: '0.8em', marginTop: 4, fontWeight: 600,
color: kpiEcartObjectif >= 0 ? 'var(--success)' : 'var(--warning)',
}}>
{kpiEcartObjectif >= 0
? `+${fmtEUR(kpiEcartObjectif)} au-delà de l'objectif ${kpiObjectifYear}`
: `Reste ${fmtEUR(-kpiEcartObjectif)} pour l'objectif ${kpiObjectifYear}`}
</div>
)}
</div>
<div className="kpi" title="Cash disponible sur les porte-monnaie des plateformes (hors compte courant)">
<div className="label">Porte-monnaie</div>
@@ -981,12 +1020,25 @@ export default function DepotsRetraits() {
{!listFocused && <div className="dr-tabs">
<button className={`dr-tab${activeTab === 'plateformes' ? ' active' : ''}`}
onClick={() => setActiveTab('plateformes')}>Plateformes</button>
<button className={`dr-tab${activeTab === 'vision-annuelle' ? ' active' : ''}`}
onClick={() => setActiveTab('vision-annuelle')}>Vision annuelle</button>
<button className={`dr-tab${activeTab === 'vision-mensuelle' ? ' active' : ''}`}
onClick={() => setActiveTab('vision-mensuelle')}>Vision mensuelle</button>
<button className={`dr-tab${activeTab === 'mouvements' ? ' active' : ''}`}
onClick={() => { setActiveTab('mouvements'); setSelectedRow(r => r ?? (rows[0] || null)); }}>Mouvements</button>
</div>}
{/* ====== ONGLET VISION ANNUELLE ====== */}
{activeTab === 'vision-annuelle' && (
<div style={{ padding: '0 24px' }}>
<SuiviObjectifs
rows={allRows}
investisseurs={investisseurs}
scopeInvestisseurIds={scopeInvestisseurIdsKpi}
/>
</div>
)}
{/* ====== ONGLET PLATEFORMES ====== */}
{activeTab === 'plateformes' && (
<div style={{ padding: '0 24px' }}>
+9
View File
@@ -7,6 +7,7 @@ import PageIcon from '../components/PageIcon.jsx';
import EmptyState from '../components/EmptyState.jsx';
import InvChart from '../components/InvChart.jsx';
import InvMensuelTable from '../components/InvMensuelTable.jsx';
import SuiviObjectifs from '../components/SuiviObjectifs.jsx';
import { fmtEUR, fmtDate, fmtStatut, memberLabel, today } from '../utils/format.js';
import { xirr } from '../utils/xirr.js';
import { usePagination } from '../hooks/usePagination.js';
@@ -1510,6 +1511,14 @@ export default function Plateformes() {
</div>
);
})()}
{/* Suivi des objectifs de versement — toujours sur le portefeuille entier,
indépendamment du filtre plateforme de cette page (cf. décision produit). */}
<SuiviObjectifs
rows={allDepots}
investisseurs={investisseurs}
scopeInvestisseurIds={activeView === 'all' ? investisseurs.map(i => i.id) : [activeId]}
/>
</div>
</div>
);