This commit is contained in:
ocroguennec committed 2026-09-18 08:16:01 +02:00
1 parent c2dbe506c5
commit 337d0c1a2c
2 files changed
+26 -20

No files matched your search

@@ -11,7 +11,7 @@ import FraisFormModal from '../components/FraisFormModal.jsx';
import { FRAIS_CATEGORIE_LABELS } from '../components/MouvementsShared.jsx'; import { FRAIS_CATEGORIE_LABELS } from '../components/MouvementsShared.jsx';
import { useAsyncAction } from '../hooks/useAsyncAction.js'; import { useAsyncAction } from '../hooks/useAsyncAction.js';
import { fmtEUR, fmtDate, fmtOctets, today } from '../utils/format.js'; import { fmtEUR, fmtDate, fmtOctets, today } from '../utils/format.js';
import { emptyDealForm, AUTO_VAL_NOTE } from './InvestissementsPe.jsx'; import { emptyDealForm, AUTO_VAL_NOTE, AUTO_VAL_NOTE_LEGACY } from './InvestissementsPe.jsx';
/** /**
* Fiche détaillée d'un deal Private Equity (chantier "fiche détail PE", 17/09/26 — demande * Fiche détaillée d'un deal Private Equity (chantier "fiche détail PE", 17/09/26 — demande
@@ -117,7 +117,7 @@ export default function InvestissementPeDetail() {
if (!deal) return; if (!deal) return;
const entree = fraisOps.find(f => f.categorie === 'souscription'); const entree = fraisOps.find(f => f.categorie === 'souscription');
const gestion = fraisOps.find(f => f.categorie === 'gestion' && f.origine === 'souscription'); const gestion = fraisOps.find(f => f.categorie === 'gestion' && f.origine === 'souscription');
const autoVal = valorisations.find(v => v.notes === AUTO_VAL_NOTE); const autoVal = valorisations.find(v => v.notes === AUTO_VAL_NOTE || v.notes === AUTO_VAL_NOTE_LEGACY);
setForm({ setForm({
...emptyDealForm, ...emptyDealForm,
plateforme_id: String(deal.plateforme_id || ''), plateforme_id: String(deal.plateforme_id || ''),
@@ -178,15 +178,16 @@ export default function InvestissementPeDetail() {
} catch { /* non bloquant */ } } catch { /* non bloquant */ }
}; };
// Toujours créée, même sans frais (= montant investi tel quel) — décision Olivier 18/09/26,
// cf. InvestissementsPe.jsx::syncValorisationInitiale (même logique, dupliquée ici pour la
// modale d'édition de la fiche détail).
const syncValorisationInitiale = async (totalFraisSource, valorisationInitialeId) => { const syncValorisationInitiale = async (totalFraisSource, valorisationInitialeId) => {
try { try {
if (valorisationInitialeId) await api.del(`/investissements-pe/${id}/valorisations/${valorisationInitialeId}`); if (valorisationInitialeId) await api.del(`/investissements-pe/${id}/valorisations/${valorisationInitialeId}`);
if (totalFraisSource > 0) {
const initiale = Math.max(0, Math.round((Number(form.montant_investi) - totalFraisSource) * 100) / 100); const initiale = Math.max(0, Math.round((Number(form.montant_investi) - totalFraisSource) * 100) / 100);
await api.post(`/investissements-pe/${id}/valorisations`, { await api.post(`/investissements-pe/${id}/valorisations`, {
date_valorisation: form.date_souscription, valorisation_nette: initiale, notes: AUTO_VAL_NOTE, date_valorisation: form.date_souscription, valorisation_nette: initiale, notes: AUTO_VAL_NOTE,
}); });
}
} catch { /* non bloquant */ } } catch { /* non bloquant */ }
}; };
+21 -16
View File
@@ -41,19 +41,23 @@ export const emptyDealForm = {
// tard depuis la page Frais PE ou la liste des investissements). // tard depuis la page Frais PE ou la liste des investissements).
frais_entree_mode_saisie: 'montant', frais_entree_valeur: '', frais_entree_mode_reglement: 'source', frais_entree_compte_id: '', frais_entree_id: null, frais_entree_mode_saisie: 'montant', frais_entree_valeur: '', frais_entree_mode_reglement: 'source', frais_entree_compte_id: '', frais_entree_id: null,
frais_gestion_mode_saisie: 'montant', frais_gestion_valeur: '', frais_gestion_mode_reglement: 'source', frais_gestion_compte_id: '', frais_gestion_id: null, frais_gestion_mode_saisie: 'montant', frais_gestion_valeur: '', frais_gestion_mode_reglement: 'source', frais_gestion_compte_id: '', frais_gestion_id: null,
// Id de la valorisation "initiale" auto-générée à la date de souscription pour refléter les // Id de la valorisation "initiale" auto-générée à la date de souscription — null si aucune
// frais réglés "à la source" (cf. AUTO_VAL_NOTE / submit ci-dessous) — null si aucune n'existe // n'existe encore pour ce deal. Décision Olivier 17/09/26 (puis 18/09/26 — systématique, même
// encore pour ce deal. Décision Olivier 17/09/26 : le montant investi (souscrit) ne bouge // sans frais) : le montant investi (souscrit) ne bouge JAMAIS, c'est cette valorisation qui
// JAMAIS, c'est la valorisation qui encaisse immédiatement les frais d'entrée/de gestion à la // encaisse immédiatement les frais d'entrée/de gestion éventuels à la date de souscription (ou
// date de souscription — jusqu'à ce qu'une vraie valorisation du fonds vienne la remplacer. // simplement reprend le montant investi tel quel en l'absence de frais) — jusqu'à ce qu'une
// vraie valorisation du fonds vienne la remplacer.
valorisation_initiale_id: null, valorisation_initiale_id: null,
}; };
// Marqueur de la valorisation "initiale" auto-générée par l'étape "Frais" de la modale (cf. // Marqueur de la valorisation "initiale" auto-générée à la souscription (cf. submit ci-dessous)
// submit ci-dessous) — permet de la retrouver/remplacer sans toucher aux valorisations saisies // — permet de la retrouver/remplacer sans toucher aux valorisations saisies manuellement par
// manuellement par ailleurs (modale "Valorisations" annexe). // ailleurs (modale "Valorisations" annexe). Créée systématiquement depuis le 18/09/26 (avant
export const AUTO_VAL_NOTE = 'Valorisation initiale (frais de souscription)'; // cette date, uniquement quand le deal avait des frais réglés "à la source" — cf.
// AUTO_VAL_NOTE_LEGACY pour reconnaître ces anciennes lignes lors du pré-remplissage).
export const AUTO_VAL_NOTE = 'Valorisation initiale (souscription)';
export const AUTO_VAL_NOTE_LEGACY = 'Valorisation initiale (frais de souscription)';
// Accès rapide "+ frais" depuis une ligne de la liste des investissements (chantier "frais PE", // Accès rapide "+ frais" depuis une ligne de la liste des investissements (chantier "frais PE",
// 17/09/26, demande Olivier) — permet d'ajouter un frais complémentaire (gestion récurrente, // 17/09/26, demande Olivier) — permet d'ajouter un frais complémentaire (gestion récurrente,
@@ -309,7 +313,7 @@ export default function InvestissementsPe() {
]); ]);
const entree = fraisOps.find(f => f.categorie === 'souscription'); const entree = fraisOps.find(f => f.categorie === 'souscription');
const gestion = fraisOps.find(f => f.categorie === 'gestion' && f.origine === 'souscription'); const gestion = fraisOps.find(f => f.categorie === 'gestion' && f.origine === 'souscription');
const autoVal = valRows.find(v => v.notes === AUTO_VAL_NOTE); const autoVal = valRows.find(v => v.notes === AUTO_VAL_NOTE || v.notes === AUTO_VAL_NOTE_LEGACY);
setForm(f => ({ setForm(f => ({
...f, ...f,
valorisation_initiale_id: autoVal ? autoVal.id : null, valorisation_initiale_id: autoVal ? autoVal.id : null,
@@ -361,23 +365,24 @@ export default function InvestissementsPe() {
// Gère la valorisation "initiale" auto-générée à la date de souscription (cf. AUTO_VAL_NOTE) : // Gère la valorisation "initiale" auto-générée à la date de souscription (cf. AUTO_VAL_NOTE) :
// le montant investi (souscrit) ne bouge jamais, c'est cette valorisation qui encaisse // le montant investi (souscrit) ne bouge jamais, c'est cette valorisation qui encaisse
// immédiatement les frais réglés "à la source" — décision Olivier 17/09/26. Comme l'API ne // immédiatement les frais réglés "à la source" quand il y en a. Créée systématiquement,
// propose pas de PUT sur une valorisation, on supprime l'éventuelle ligne existante puis on // même sans frais (= montant investi tel quel) — décision Olivier 18/09/26, pour que la courbe
// recrée à neuf (couvre aussi le cas où la date de souscription a changé). Best-effort, jamais // de valorisation et le TVPI démarrent dès la souscription plutôt que rester sans point tant
// bloquant pour l'enregistrement principal du deal. // qu'aucune valorisation manuelle n'a été saisie. Comme l'API ne propose pas de PUT sur une
// valorisation, on supprime l'éventuelle ligne existante puis on recrée à neuf (couvre aussi le
// cas où le montant investi, les frais ou la date de souscription ont changé). Best-effort,
// jamais bloquant pour l'enregistrement principal du deal.
const syncValorisationInitiale = async (dealId, totalFraisSource, valorisationInitialeId) => { const syncValorisationInitiale = async (dealId, totalFraisSource, valorisationInitialeId) => {
try { try {
if (valorisationInitialeId) { if (valorisationInitialeId) {
await api.del(`/investissements-pe/${dealId}/valorisations/${valorisationInitialeId}`); await api.del(`/investissements-pe/${dealId}/valorisations/${valorisationInitialeId}`);
} }
if (totalFraisSource > 0) {
const initiale = Math.max(0, Math.round((Number(form.montant_investi) - totalFraisSource) * 100) / 100); const initiale = Math.max(0, Math.round((Number(form.montant_investi) - totalFraisSource) * 100) / 100);
await api.post(`/investissements-pe/${dealId}/valorisations`, { await api.post(`/investissements-pe/${dealId}/valorisations`, {
date_valorisation: form.date_souscription, date_valorisation: form.date_souscription,
valorisation_nette: initiale, valorisation_nette: initiale,
notes: AUTO_VAL_NOTE, notes: AUTO_VAL_NOTE,
}); });
}
} catch { /* non bloquant — rattrapable depuis la modale Valorisations */ } } catch { /* non bloquant — rattrapable depuis la modale Valorisations */ }
}; };