Amélioration de la logique des investissements vs dépôts

This commit is contained in:
ocroguennec committed 2026-08-29 16:03:55 +02:00
1 parent c7bbe5dee6
commit f6829dec9d
4 files changed
+349 -52

No files matched your search

+139 -51
View File
@@ -653,30 +653,100 @@ export default function DepotsRetraits() {
return plat?.investisseur_id ? String(plat.investisseur_id) : defaultInvestisseurId();
};
/* Ouverture auto depuis le bouton global "Nouveau dépôt/retrait" */
/* Ouverture auto depuis le bouton global "Nouveau dépôt/retrait", ou depuis le formulaire
investissement — soit le bouton "Déposer" (ajoute ?plateforme_id=… pour présélectionner
la plateforme plutôt que de retomber sur plats[0], et ?resumeInvestissement=1 pour
demander un retour automatique vers l'investissement une fois le dépôt créé — cf.
pendingReturnActive/maybeReturnToInvestissement plus bas), soit la proposition de dépôt
après création d'un investissement dont le porte-monnaie ne couvrait pas le montant
(ajoute ?montant=… pour pré-remplir le montant du dépôt, et ?autoCreate=1 pour créer le
dépôt directement sans repasser par la modale — l'utilisateur a déjà validé/rectifié le
montant dans la proposition affichée côté formulaire investissement). */
const pendingNew = useRef(false);
const pendingNewPlatId = useRef(null);
const pendingNewMontant = useRef(null);
const pendingNewAutoCreate = useRef(false);
const pendingReturnActive = useRef(false);
useEffect(() => {
if (new URLSearchParams(search).get('new') !== '1') return;
const params = new URLSearchParams(search);
if (params.get('new') !== '1') return;
const platParam = params.get('plateforme_id');
const montantParam = params.get('montant');
const autoCreateParam = params.get('autoCreate') === '1';
if (params.get('resumeInvestissement') === '1') pendingReturnActive.current = true;
navigate('/depots-retraits', { replace: true });
if (plats.length > 0) {
setEditingId(null);
const firstId = plats[0]?.id || '';
setForm({ ...emptyMouvementForm, plateforme_id: firstId, investisseur_id: investisseurForPlat(firstId) });
setErr(null);
setModalOpen(true);
const firstId = (platParam && plats.some(p => String(p.id) === platParam)) ? platParam : (plats[0]?.id || '');
const built = { ...emptyMouvementForm, plateforme_id: firstId, investisseur_id: investisseurForPlat(firstId), montant: montantParam || '' };
if (autoCreateParam && montantParam) {
setErr(null);
doCreateDeposit(built).catch(err => {
// Création auto échouée (réseau, validation serveur…) — on retombe sur la modale
// pré-remplie pour que l'utilisateur voie l'erreur et puisse retenter manuellement.
setForm(built);
setErr(err.message);
setModalOpen(true);
});
} else {
setForm(built);
setErr(null);
setModalOpen(true);
}
} else {
pendingNew.current = true;
pendingNewPlatId.current = platParam;
pendingNewMontant.current = montantParam;
pendingNewAutoCreate.current = autoCreateParam;
}
}, [search]); // eslint-disable-line
/** Si le dépôt en cours de création vient du bouton "Déposer" du formulaire investissement
* (cf. ci-dessus), navigue vers la page d'origine avec la saisie restaurée depuis
* sessionStorage. Ne fait rien sinon (comportement normal, inchangé). À appeler après
* que le dépôt est effectivement créé (pas à l'annulation) — les 3 points de sortie
* possibles de la création (sans correction de solde / correction confirmée / correction
* "Passer") appellent tous cette fonction. */
const maybeReturnToInvestissement = () => {
if (!pendingReturnActive.current) return;
pendingReturnActive.current = false;
try {
const raw = sessionStorage.getItem('cl_investissement_draft');
if (!raw) return;
sessionStorage.removeItem('cl_investissement_draft');
const draft = JSON.parse(raw);
if (!draft?.returnTo) return;
const sep = draft.returnTo.includes('?') ? '&' : '?';
navigate(`${draft.returnTo}${sep}resumeInvestissement=1`);
} catch {
// sessionStorage/JSON invalide — pas de retour auto, on reste simplement sur cette page.
}
};
useEffect(() => {
if (!pendingNew.current || !plats.length) return;
pendingNew.current = false;
setEditingId(null);
const firstId = plats[0]?.id || '';
setForm({ ...emptyMouvementForm, plateforme_id: firstId, investisseur_id: investisseurForPlat(firstId) });
setErr(null);
setModalOpen(true);
const platParam = pendingNewPlatId.current;
pendingNewPlatId.current = null;
const montantParam = pendingNewMontant.current;
pendingNewMontant.current = null;
const autoCreateParam = pendingNewAutoCreate.current;
pendingNewAutoCreate.current = false;
const firstId = (platParam && plats.some(p => String(p.id) === platParam)) ? platParam : (plats[0]?.id || '');
const built = { ...emptyMouvementForm, plateforme_id: firstId, investisseur_id: investisseurForPlat(firstId), montant: montantParam || '' };
if (autoCreateParam && montantParam) {
setErr(null);
doCreateDeposit(built).catch(err => {
setForm(built);
setErr(err.message);
setModalOpen(true);
});
} else {
setForm(built);
setErr(null);
setModalOpen(true);
}
}, [plats]); // eslint-disable-line
/* CRUD */
@@ -762,54 +832,71 @@ export default function DepotsRetraits() {
const close = () => { setModalOpen(false); setEditingId(null); setForm(emptyMouvementForm); setErr(null); };
/** Crée un dépôt/retrait à partir d'un objet form donné. Extrait de submit() pour être
* réutilisable par la création automatique déclenchée depuis ?new=1&autoCreate=1 (cf.
* effets ci-dessus) — l'utilisateur y a déjà validé/rectifié le montant en amont, donc pas
* besoin de repasser par la modale. Les erreurs remontent à l'appelant (try/catch). */
const doCreateDeposit = async (f) => {
const payload = {
investisseur_id: Number(f.investisseur_id) || undefined,
plateforme_id: Number(f.plateforme_id),
date_operation: f.date_operation,
type: f.type,
montant: Number(f.montant),
libelle: f.libelle || undefined,
reference: f.reference || undefined,
notes: f.notes || undefined,
};
await api.post('/depots-retraits', payload);
close();
// Vérifier si la plateforme est flat_tax → proposer correction de solde
const plat = plats.find(p => String(p.id) === String(f.plateforme_id));
if (plat?.fiscalite === 'flat_tax') {
// Solde attendu = solde actuel ± montant de l'opération (avant reload)
const currentPlatData = dashData?.cashByPlatform?.find(
p => String(p.plateforme_id) === String(f.plateforme_id)
);
const currentBalance = currentPlatData?.solde_portefeuille ?? 0;
const montant = Number(f.montant);
const expectedBalance = Math.round(
(f.type === 'retrait' ? currentBalance - montant : currentBalance + montant) * 100
) / 100;
setCorrModal({
open: true,
plateforme_nom: plat.nom,
type_operation: f.type,
computedBalance: expectedBalance,
date: f.date_operation,
platId: Number(f.plateforme_id),
investisseurId: Number(f.investisseur_id) || null,
});
} else {
await Promise.all([load(), loadDashDataAsOf()]);
maybeReturnToInvestissement();
}
};
const submit = async (e) => {
e?.preventDefault?.(); setErr(null);
try {
const payload = {
investisseur_id: Number(form.investisseur_id) || undefined,
plateforme_id: Number(form.plateforme_id),
date_operation: form.date_operation,
type: form.type,
montant: Number(form.montant),
libelle: form.libelle || undefined,
reference: form.reference || undefined,
notes: form.notes || undefined,
};
if (editingId) {
const payload = {
investisseur_id: Number(form.investisseur_id) || undefined,
plateforme_id: Number(form.plateforme_id),
date_operation: form.date_operation,
type: form.type,
montant: Number(form.montant),
libelle: form.libelle || undefined,
reference: form.reference || undefined,
notes: form.notes || undefined,
};
await api.put(`/depots-retraits/${editingId}`, payload);
close(); await Promise.all([load(), loadDashDataAsOf()]);
return;
}
// Nouvelle opération
await api.post('/depots-retraits', payload);
close();
// Vérifier si la plateforme est flat_tax → proposer correction de solde
const plat = plats.find(p => String(p.id) === String(form.plateforme_id));
if (plat?.fiscalite === 'flat_tax') {
// Solde attendu = solde actuel ± montant de l'opération (avant reload)
const currentPlatData = dashData?.cashByPlatform?.find(
p => String(p.plateforme_id) === String(form.plateforme_id)
);
const currentBalance = currentPlatData?.solde_portefeuille ?? 0;
const montant = Number(form.montant);
const expectedBalance = Math.round(
(form.type === 'retrait' ? currentBalance - montant : currentBalance + montant) * 100
) / 100;
setCorrModal({
open: true,
plateforme_nom: plat.nom,
type_operation: form.type,
computedBalance: expectedBalance,
date: form.date_operation,
platId: Number(form.plateforme_id),
investisseurId: Number(form.investisseur_id) || null,
});
} else {
await Promise.all([load(), loadDashDataAsOf()]);
}
await doCreateDeposit(form);
} catch (e) { setErr(e.message); }
};
@@ -831,6 +918,7 @@ export default function DepotsRetraits() {
}
setCorrModal(m => ({ ...m, open: false }));
await Promise.all([load(), loadDashDataAsOf()]);
maybeReturnToInvestissement();
};
const openRowMenu = (e, row) => {
@@ -1623,7 +1711,7 @@ export default function DepotsRetraits() {
type_operation={corrModal.type_operation}
computedBalance={corrModal.computedBalance}
date={corrModal.date}
onClose={() => { setCorrModal(m => ({ ...m, open: false })); load(); }}
onClose={() => { setCorrModal(m => ({ ...m, open: false })); load(); maybeReturnToInvestissement(); }}
onConfirm={handleCorrectionConfirm}
/>