Amélioration de la logique des investissements vs dépôts
This commit is contained in:
1 parent
c7bbe5dee6
commit
f6829dec9d
4 files changed
+349
-52
No files matched your search
@@ -293,6 +293,11 @@ export default function Investissements() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [deleteConfirm, setDeleteConfirm] = useState(null);
|
||||
const [openMenu, setOpenMenu] = useState(null);
|
||||
// Proposition de dépôt après création d'un investissement si le porte-monnaie ne
|
||||
// couvre pas l'opération (contrôle création uniquement, cf. submit()).
|
||||
const [depositPrompt, setDepositPrompt] = useState({
|
||||
open: false, plateforme_id: null, plateforme_nom: '', montant: 0, investissementId: null,
|
||||
});
|
||||
|
||||
/* ── Chargement (sans filtre — filtrage côté client) ── */
|
||||
const load = async () => {
|
||||
@@ -625,6 +630,40 @@ export default function Investissements() {
|
||||
setModalOpen(true);
|
||||
}, [plats]); /* eslint-disable-next-line */
|
||||
|
||||
/* ── Reprise de la saisie après un aller-retour "Déposer" vers Dépôts/Retraits (bouton
|
||||
dans InvestissementFormModal, étape 1) : la modale se rouvre avec exactement ce qui
|
||||
avait été saisi avant le dépôt. */
|
||||
const pendingResume = useRef(null);
|
||||
useEffect(() => {
|
||||
if (searchParams.get('resumeInvestissement') !== '1') return;
|
||||
const n = new URLSearchParams(searchParams); n.delete('resumeInvestissement');
|
||||
navigate({ pathname: '/investissements', search: n.toString() ? '?' + n.toString() : '' }, { replace: true });
|
||||
let draft = null;
|
||||
try {
|
||||
const raw = sessionStorage.getItem('cl_investissement_draft');
|
||||
if (raw) { draft = JSON.parse(raw); sessionStorage.removeItem('cl_investissement_draft'); }
|
||||
} catch { /* ignore */ }
|
||||
if (!draft?.form) return;
|
||||
if (plats.length > 0) {
|
||||
setEditingId(draft.editingId || null);
|
||||
setForm(draft.form);
|
||||
setErr(null);
|
||||
setModalOpen(true);
|
||||
} else {
|
||||
pendingResume.current = draft;
|
||||
}
|
||||
}, [searchParams]); /* eslint-disable-next-line */
|
||||
|
||||
useEffect(() => {
|
||||
if (!pendingResume.current || !plats.length) return;
|
||||
const draft = pendingResume.current;
|
||||
pendingResume.current = null;
|
||||
setEditingId(draft.editingId || null);
|
||||
setForm(draft.form);
|
||||
setErr(null);
|
||||
setModalOpen(true);
|
||||
}, [plats]); /* eslint-disable-next-line */
|
||||
|
||||
/* ── CRUD ── */
|
||||
const openNew = () => {
|
||||
setEditingId(null);
|
||||
@@ -747,8 +786,34 @@ export default function Investissements() {
|
||||
api.put(`/investissements/${created.id}/categories-inv`, { ids: form.categories_inv_ids || [] }),
|
||||
api.put(`/investissements/${created.id}/secteurs-inv`, { ids: form.secteurs_inv_ids || [] }),
|
||||
]);
|
||||
|
||||
// Contrôle du porte-monnaie — création uniquement (jamais en édition). Le solde
|
||||
// recalculé APRÈS création tient déjà compte du capital de ce nouvel investissement
|
||||
// (formule officielle, cf. mémoire project_solde_portefeuille) : s'il ressort négatif,
|
||||
// le porte-monnaie ne couvrait pas l'opération. On propose alors un dépôt du montant
|
||||
// du découvert (rectifiable par l'utilisateur) plutôt que de bloquer la création.
|
||||
let soldeApres = null;
|
||||
try {
|
||||
const r = await api.get('/dashboard/solde-historique', { plateforme_id: form.plateforme_id, date: today() });
|
||||
soldeApres = r?.solde ?? null;
|
||||
} catch {
|
||||
// Contrôle indisponible — n'empêche pas la création pour autant.
|
||||
}
|
||||
|
||||
close();
|
||||
navigate(`/investissements/${created.id}`);
|
||||
|
||||
if (soldeApres !== null && soldeApres < -0.005) {
|
||||
const plat = plats.find(p => String(p.id) === String(form.plateforme_id));
|
||||
setDepositPrompt({
|
||||
open: true,
|
||||
plateforme_id: Number(form.plateforme_id),
|
||||
plateforme_nom: plat?.nom || '',
|
||||
montant: Math.round(Math.abs(soldeApres) * 100) / 100,
|
||||
investissementId: created.id,
|
||||
});
|
||||
} else {
|
||||
navigate(`/investissements/${created.id}`);
|
||||
}
|
||||
}
|
||||
} catch (e) { setErr(e.message); }
|
||||
};
|
||||
@@ -1335,6 +1400,63 @@ export default function Investissements() {
|
||||
onConfirm={deleteConfirm?.onConfirm}
|
||||
onCancel={() => setDeleteConfirm(null)}
|
||||
/>
|
||||
|
||||
{/* ── Proposition de dépôt si le porte-monnaie ne couvrait pas le nouvel
|
||||
investissement (cf. submit()) — montant rectifiable avant de créer le dépôt. ── */}
|
||||
{depositPrompt.open && (
|
||||
<div style={{
|
||||
position: 'fixed', inset: 0, zIndex: 1000,
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
background: 'rgba(0,0,0,0.4)',
|
||||
}}>
|
||||
<div style={{
|
||||
background: 'var(--surface)', borderRadius: 10,
|
||||
padding: '24px 28px', width: 420, maxWidth: '90vw',
|
||||
boxShadow: '0 8px 32px rgba(0,0,0,0.18)',
|
||||
border: '1px solid var(--border)',
|
||||
}}>
|
||||
<div style={{ marginBottom: 6, fontWeight: 700, fontSize: 'var(--fs-base)' }}>
|
||||
Porte-monnaie insuffisant
|
||||
</div>
|
||||
<div style={{ fontSize: 'var(--fs-sm)', color: 'var(--text-muted)', marginBottom: 18, lineHeight: 1.5 }}>
|
||||
Une fois cet investissement pris en compte, le porte-monnaie de <strong>{depositPrompt.plateforme_nom}</strong> est
|
||||
à découvert. Voulez-vous créer un dépôt pour le couvrir ?
|
||||
</div>
|
||||
<div style={{ marginBottom: 18 }}>
|
||||
<label style={{ fontSize: 'var(--fs-sm)', fontWeight: 500, display: 'block', marginBottom: 4 }}>
|
||||
Montant du dépôt (€)
|
||||
</label>
|
||||
<input
|
||||
type="number" step="0.01" min="0"
|
||||
value={depositPrompt.montant}
|
||||
onChange={e => setDepositPrompt(p => ({ ...p, montant: e.target.value }))}
|
||||
style={{ width: '100%', fontSize: 'var(--fs-base)' }}
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
|
||||
<button onClick={() => {
|
||||
const id = depositPrompt.investissementId;
|
||||
setDepositPrompt(p => ({ ...p, open: false }));
|
||||
navigate(`/investissements/${id}`);
|
||||
}}>
|
||||
Plus tard
|
||||
</button>
|
||||
<button
|
||||
className="primary"
|
||||
disabled={!depositPrompt.montant || Number(depositPrompt.montant) <= 0}
|
||||
onClick={() => {
|
||||
const { plateforme_id, montant } = depositPrompt;
|
||||
setDepositPrompt(p => ({ ...p, open: false }));
|
||||
navigate(`/depots-retraits?new=1&plateforme_id=${plateforme_id}&montant=${encodeURIComponent(montant)}&autoCreate=1`);
|
||||
}}
|
||||
>
|
||||
Créer le dépôt
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user