Property Funding¶
Endpoints d'administration pour piloter le cycle de funding d'une property : suivi de l'étape, clôture, transferts de frais (collecte, fiducie) et virement des fonds au porteur. Source : property-funding.controller.ts.
Tous les endpoints requièrent une session admin BetterAuth valide (AdminAuthGuard). Les endpoints muteurs (endPropertyFunding, transferFundingFees, transferFiducieFees, transferFundsToProjectOwner) sont tracés via CreateAdminActionInterceptor.
GET /administration/property/:propertyId/funding¶
Retourne l'état courant du funding (étape admin) : informations de financement, dates clés, montants achetés et bricks encore disponibles.
Décorateur : L51.
Path param¶
propertyId— UUIDv4
Response¶
AdminPropertyFundingStep (cf. property-funding.admin-step.model.ts).
POST /administration/property/:propertyId/funding/end/:endType¶
Clôture le funding d'une property. Seul endType=success est supporté. Met à jour funding.ended, calcule investorCount / purchasedBrickCount, désactive la disponibilité des bricks sur le marché primaire, passe le projet en reception-by-project-owner-pending et invalide les caches Redis. Une AdminAction PROPERTY_END_FUNDING est créée via interceptor.
Décorateur : L61.
Path param¶
propertyId— UUIDv4endType— littéralsuccess
Erreurs¶
| Code | Statut | Cause |
|---|---|---|
property-funding.end.property-not-found |
404 | Property introuvable |
<canCloseFundingWithSuccess error> |
400 | Conditions de clôture non remplies (montant min non atteint, date de remboursement non écoulée…) |
POST /administration/property/:propertyId/funding/transfer-funding-fees¶
Effectue le P2P Lemonway des frais de structuring (HT + TVA) du SPV vers le compte d'origination, génère la facture associée et met à jour companyFees.fundingFees.transfer. Une AdminAction PROPERTY_TRANSFER_FUNDING_FEES est créée via interceptor.
Décorateur : L82.
Path param¶
propertyId— UUIDv4
Erreurs¶
| Code | Statut | Cause |
|---|---|---|
property.funding-fees.not-set |
400 | companyFees.fundingFees non configuré |
property.funding-fees.already-transfered |
400 | Transfert déjà effectué |
p2p-failed-at-provider |
500 | Échec du P2P Lemonway |
POST /administration/property/:propertyId/funding/transfer-fiducie-fees¶
Effectue le P2P Lemonway des frais de fiducie (HT + TVA) du SPV vers le compte principal, génère la facture associée et met à jour companyFees.fiducieFees.transfer. Une AdminAction PROPERTY_TRANSFER_FIDUCIE_FEES est créée via interceptor.
Décorateur : L107.
Path param¶
propertyId— UUIDv4
Erreurs¶
| Code | Statut | Cause |
|---|---|---|
property.fiducie-fees.not-set |
400 | companyFees.fiducieFees non configuré |
property.fiducie-fees.already-transfered |
400 | Transfert déjà effectué |
p2p-failed-at-provider |
500 | Échec du P2P Lemonway |
POST /administration/property/:propertyId/funding/transfer-funds-to-project-owner¶
Vire les fonds collectés au porteur de projet (MoneyOut Lemonway) ou enregistre un transfert externe (bypass manuel). Le montant transférable est plafonné à amountToFund - fundingFeesTotalTtc - fiducieFeesTotalTtc - totalFundEscrow - totalConstructionBudget cumulé sur tous les transferts. Une AdminAction PROPERTY_TRANSFER_FUNDS_TO_PROJECT_OWNER est créée via interceptor.
Décorateur : L132.
Path param¶
propertyId— UUIDv4
Request body¶
Discriminated union sur action :
amountToTransfer en cents (positif). withdraw : flux nominal, MoneyOut exécuté avec l'IBAN du porteur. no-withdraw : bypass manuel (virement effectué hors API, par exemple depuis un compte technique Lemonway), enregistré pour la compta (lemonwayWithdrawBypassed + bypassedBy) et tracé via AdminAction.
Erreurs¶
| Code | Statut | Cause |
|---|---|---|
property.transfer-exceeds-funding-amount |
400 | Total des transferts dépasserait le montant transférable |
transfer-failed-at-provider |
500 | Échec du MoneyOut Lemonway |
POST /:propertyId/funding/trigger-automatic-investment¶
Déclenche manuellement une vague d'investissement automatique pour un bien publié dont le funding est encore ouvert. Trace une PROPERTY_TRIGGER_AUTOMATIC_INVESTMENT. Décorateur : L197.
Path param¶
propertyId— UUIDv4
Request body¶
Quand true, les investisseurs avec un achat auto refunded ou declined restent éligibles ; seuls les investisseurs avec un achat confirmed ou en cours (waiting_* sur primary_purchase) sont exclus. Quand false, tout investisseur ayant déjà une ligne primary_purchase sur le bien est exclu.
Pré-conditions¶
- Feature flag
ENABLE_INVESTMENT_PLANactif (sinoninvestment-plan-disabled) - Bien
published, funding non clôturé - Pas de job Graphile en attente pour ce bien (clé
project-automatic-funding-manual-{propertyId}— sinonautomatic-funding-job-already-pending)
Pas de garde early-access côté endpoint ni worker : décision ops.
Response¶
Corps vide (204 implicite — le controller matche { ok: true } sans valeur de retour).
Erreurs¶
| Code | Statut | Cause |
|---|---|---|
property-not-found |
400 | Bien introuvable |
property-not-published |
400 | Bien non publié |
funding-already-ended |
400 | Funding déjà clôturé |
investment-plan-disabled |
400 | Feature flag ENABLE_INVESTMENT_PLAN désactivé |
automatic-funding-job-already-pending |
400 | Job Graphile déjà en queue pour ce bien |
GET /administration/property/simulate-automatic-investment¶
Lance en arrière-plan une simulation du financement automatique pour un horizon et un taux donnés. La requête répond immédiatement ('Simulation en cours...'), les résultats sont postés sur Slack #financement-automatique-simulation. Le montant à financer est codé en dur (5 M€) et le propertyId simulé est 00000000-0000-0000-0000-000000000000.
Décorateur : L182.
Query params¶
investmentHorizonInMonths— entier positif (numberFromString.then(positiveInteger))returnOnInvestment— pourcentage (numberFromString.then(positiveNumberTagged))
Response¶
Texte brut : 'Simulation en cours... Les résultats seront postés sur Slack. #financement-automatique-simulation'.
Cron funding-auto-close (clôture automatique)¶
Clôture automatique des collectes éligibles et récap quotidien Slack pour l'équipe financement.
Source : funding-auto-close.cron-task.ts · funding-auto-close.service.ts.
Schedule¶
30 7 * * * UTC (08:30 Paris) — déclaré dans graphile-cron.worker.ts.
Comportement¶
Pour chaque funding actif (ClosePropertyFundingRepository.getActiveFundings) :
- Clôture auto — si
status === totallyFinancedetfundingCanBeSuccessfulyEnded: appellePropertyFundingService.endPropertyFunding({ endType: 'success' })(même logique que l'endpoint adminPOST .../funding/end/success) - Stuck — 100 % financé mais fenêtre de rétractation du dernier achat pas encore expirée → listé en alerte (non clôturable)
- Approaching max end date — collecte
inProgressouminToFundReachedà J-7 demaxEndDate(ou dépassée) → alerte avec % confirmé et % confirmé+waiting - To watch — ≥ 100 % avec waiting, date de clôture possible dépassée, mais pas encore
totallyFinanced - Active summaries — toutes les autres collectes en cours, triées par date de clôture possible
Slack¶
Canal prod : #suivi-projet-financement-en-cours (dev/staging : canal dev mappé par env).
Un seul message récap par run, sections :
| Section | Emoji | Contenu |
|---|---|---|
| Clôtures réussies | ✅ | Projets auto-clôturés ce run |
| Stuck | ⚠️ | 100 % mais rétractation en cours |
| Expiration proche | ⏳ | J-7 ou date max dépassée |
| Échecs | ❌ | Erreur à la clôture (message PG/métier) |
| À surveiller | 👀 | 100 % waiting, date clôture passée |
| En cours | — | Liste des autres collectes actives |
Si aucune collecte active : message Aucune collecte active en cours.
Dry run¶
FundingAutoCloseService.closeEligibleFundings({ dryRun: true }) simule les clôtures (logs [DRY RUN]) sans muter la DB — utile pour valider l'éligibilité avant intervention manuelle.
Pièges courants¶
- Stuck vs to watch : stuck =
totallyFinanced+ rétractation ; to watch = pas encoretotallyFinancedmais 100 % avec achatswaiting - % waiting :
fundingPercentageWithWaitinginclut les achatsprimary_purchaseen attente de settlement — peut dépasser 100 % affiché confirmé seul - Échec de clôture : vérifier la section ❌ du Slack puis les conditions
canCloseFundingWithSuccess(dernier achat refundable, montant min, etc.) — clôture manuelle viaPOST .../funding/end/successsi besoin
Liens¶
- Service :
property-funding.service.ts - Service step :
property-funding-step.service.ts - Modèle admin step :
property-funding.admin-step.model.ts - Service simulation auto-funding :
investor-investment-plan.service.ts - Service factures SPV :
special-purpose-vehicule.invoice.service.ts