diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx
index 13397d4..6d40929 100644
--- a/frontend/src/pages/Dashboard.jsx
+++ b/frontend/src/pages/Dashboard.jsx
@@ -156,10 +156,11 @@ function TrendBadge({ current, prev, invert = false }) {
}
/* ── Carte KPI individuelle ── */
-function KpiCard({ title, value, badge, refValue, onClick }) {
+function KpiCard({ title, value, badge, refValue, onClick, hint }) {
return (
{ e.currentTarget.style.boxShadow = '0 0 0 2px var(--primary)'; e.currentTarget.style.opacity = '0.88'; } : undefined}
@@ -178,7 +179,7 @@ function KpiCard({ title, value, badge, refValue, onClick }) {
}
/* ── KPI filtrés par année ── */
-function DashboardKpis({ portfolio, netMode, pfuRates, capitalMensuelData }) {
+function DashboardKpis({ portfolio, netMode, pfuRates, capitalMensuelData, activeId, activeView, soldePortefeuilleLive }) {
const {
annee, modeGlobal, rawDataGlobal, rawData, currentYear, currentMonth,
setInclureInterets, setInclureCapital, setInclureCashback,
@@ -187,6 +188,31 @@ function DashboardKpis({ portfolio, netMode, pfuRates, capitalMensuelData }) {
const isCurrentYear = !modeGlobal && Number(annee) === currentYear;
const isFutureYear = !modeGlobal && Number(annee) > currentYear;
+ // ── Porte-monnaie — même snapshot backend que DepotsRetraits/Plateformes
+ // (GET /dashboard[?asOf=]), formule unique cf. mémoire
+ // project_taxreport_interets_plateforme. "Depuis le début" → valeur live
+ // déjà chargée par le parent ; année précise → snapshot fin d'année.
+ const [walletAsOf, setWalletAsOf] = useState({});
+ useEffect(() => {
+ if (modeGlobal) return;
+ if (!activeId && activeView !== 'all') return;
+ const scopeParams = activeView === 'all' ? { scope: 'all' } : undefined;
+ const prevYear = Number(annee) - 1;
+ const keys = [`${annee}-12-31`, `${prevYear}-12-31`];
+ Promise.all(keys.map(asOf =>
+ api.get('/dashboard', { ...scopeParams, asOf }).then(d => [asOf, d])
+ )).then(entries => {
+ setWalletAsOf(prev => ({ ...prev, ...Object.fromEntries(entries) }));
+ }).catch(e => console.error('walletAsOf load error:', e));
+ }, [annee, modeGlobal, activeId, activeView]); // eslint-disable-line
+
+ const soldePortefeuilleValue = modeGlobal
+ ? soldePortefeuilleLive
+ : (walletAsOf[`${annee}-12-31`]?.solde_portefeuille_total ?? null);
+ const soldePortefeuillePrev = modeGlobal
+ ? null
+ : (walletAsOf[`${Number(annee) - 1}-12-31`]?.solde_portefeuille_total ?? null);
+
// Capital de référence pour l'année sélectionnée
// Pour les années passées : dernier mois de capitalMensuel (= capital déployé fin décembre)
// Pour l'année courante / mode global : capital actuellement déployé (portfolio)
@@ -380,6 +406,17 @@ function DashboardKpis({ portfolio, netMode, pfuRates, capitalMensuelData }) {
? fmtEUR(enDefautPrev) + ' en ' + prevMonthLabel
: null}
/>
+
+ : null}
+ refValue={!modeGlobal && soldePortefeuillePrev != null
+ ? fmtEUR(soldePortefeuillePrev) + ' en ' + (Number(annee) - 1)
+ : null}
+ />
-
+
diff --git a/frontend/src/pages/DepotsRetraits.jsx b/frontend/src/pages/DepotsRetraits.jsx
index 3ab9980..c5494af 100644
--- a/frontend/src/pages/DepotsRetraits.jsx
+++ b/frontend/src/pages/DepotsRetraits.jsx
@@ -467,8 +467,14 @@ export default function DepotsRetraits() {
const years = [...new Set(allRows.map(r => r.date_operation.slice(0,4)))].sort().reverse();
/* Données tableau Plateformes — filtrées par drPlatYear si actif */
+ // Tri unique (alphabétique, détenteur en second critère) appliqué aux deux
+ // branches de drPlatData — évite que l'ordre des plateformes change selon le
+ // filtre année sélectionné.
+ const sortPlatAlpha = (a, b) =>
+ a.nom.localeCompare(b.nom, 'fr') || (a.detenteur_nom ?? '').localeCompare(b.detenteur_nom ?? '', 'fr');
+
const drPlatData = useMemo(() => {
- if (!drPlatYear) return dashData?.cashByPlatform ?? [];
+ if (!drPlatYear) return [...(dashData?.cashByPlatform ?? [])].sort(sortPlatAlpha);
// Mouvements de l'année sélectionnée (dépôts/retraits de l'année)
const src = allRows.filter(r => r.date_operation.slice(0, 4) === drPlatYear);
@@ -480,6 +486,22 @@ export default function DepotsRetraits() {
else map[key].retraits += r.montant;
}
+ // Une plateforme sans dépôt/retrait cette année-là (ex. uniquement des
+ // remboursements ou un réinvestissement) doit quand même apparaître dans le
+ // tableau — sinon elle disparaît alors qu'elle a eu de vrais mouvements.
+ for (const r of allRemb) {
+ if (!r.date_remb || r.date_remb.slice(0, 4) !== drPlatYear || !r.plateforme_id) continue;
+ const key = String(r.plateforme_id);
+ if (!map[key]) map[key] = { plateforme_id: r.plateforme_id, nom: r.plateforme_nom, detenteur_nom: r.plateforme_detenteur_nom || null, depots: 0, retraits: 0, solde_net: 0, solde_portefeuille: 0 };
+ }
+ for (const rv of allReinvests) {
+ if (!rv.date_reinvestissement || rv.date_reinvestissement.slice(0, 4) !== drPlatYear) continue;
+ const inv = allInv.find(i => i.id === rv.investissement_id);
+ if (!inv?.plateforme_id) continue;
+ const key = String(inv.plateforme_id);
+ if (!map[key]) map[key] = { plateforme_id: inv.plateforme_id, nom: inv.plateforme_nom, detenteur_nom: inv.plateforme_detenteur_nom || null, depots: 0, retraits: 0, solde_net: 0, solde_portefeuille: 0 };
+ }
+
// Solde porte-monnaie = cumul jusqu'au 31/12 de l'année sélectionnée, calculé
// côté backend (GET /dashboard?asOf=) pour rester la même formule que le solde
// "live" — bonus, intérêts plateforme et corrections de solde inclus.
@@ -492,8 +514,8 @@ export default function DepotsRetraits() {
p.solde_net = p.depots - p.retraits;
p.solde_portefeuille = soldeMap[String(p.plateforme_id)] ?? 0;
return p;
- }).sort((a, b) => b.solde_net - a.solde_net);
- }, [allRows, dashDataAsOf, drPlatYear]);
+ }).sort(sortPlatAlpha);
+ }, [allRows, allRemb, allInv, allReinvests, dashDataAsOf, drPlatYear]);
const drPlatTotals = useMemo(() =>
drPlatData.reduce((acc, p) => {