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
@@ -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}
|
||||
/>
|
||||
|
||||
|
||||
Reference in new issue
Block a user