Aller au contenu

Achat primaire — subtilités et clôture du funding

Extrait du domain map API, qui était chargé dans chaque session agent. La rule garde l'index module → contexte ; le détail vit ici, à côté du code.

Les trois flux d'achat et le diagramme d'états sont dans la vue d'ensemble du module. Cette page ne garde que les règles qui ne se lisent pas dans le flux.

Echecs possibles (tous flows) : declined (expired, no-bricks-left) ou refunded

Investissement automatique : - Vagues auto : 10 min apres display, plus J+1 16h si cette vague tourne strictement avant le cutoff (funding.startedAt - startDelayBeforeFundingStartInHours). Pas de vague auto si earlyFundingAccess (un job restant au cutoff no-op). Distribution round-robin si plus de demandes que de briques disponibles. - Relance admin manuelle (POST .../funding/trigger-automatic-investment) autorisee apres ouverture / apres le cutoff ; chaque achat a son propre plafond minConfirmationDelayHours. - Expiration non confirme : a la vague, refundableUntil est ecrit a max(funding.startedAt - startDelayBeforeFundingStartInHours, createdAt + minConfirmationDelayHours) et n'est plus recalcule si l'ouverture bouge. Un job Graphile investor-automatic-funding-cancel-expired-purchase est enqueue en fin de vague a cette deadline + 2 min (expireJobDelayAfterRefundableUntilInMinutes). Un deplacement d'ouverture ne recale ni la colonne ni ce job. Job / GET / wallet / rappel H-3 lisent cette colonne. Confirm / cancel refuses apres refundableUntil. Retractation legale 4 jours a partir de la confirmation. GET /investor/investment-plan expose cette deadline dans refundableUntil (pas la retractation 4 jours). - Rappel push H-3 : job Graphile unconfirmed-auto-invest-reminder a funding.startedAt - reminderHoursBeforeFundingStart, file unconfirmed-auto-invest-reminder-queue (pas la file d'achat). Schedule a la publication et a chaque changement de funding.startedAt ; skip si l'instant est deja passe. Un patch d'ouverture ne recree que ce rappel, pas les vagues. Recipients : waiting, attribution >= 3 h, encore confirmables, refundableUntil au plus a l'ouverture (le copy CIO dit expire dans 2 h ; un plancher 12 h apres l'ouverture est exclus). Broadcast CIO UnconfirmedAutoInvestReminder (dev/staging 46, prod 103).

Subtilites : - Quota 70% par investisseur : calcule sur le total de briques du bien, pas sur les briques restantes - Retractation 4 jours : calcule en end-of-day timezone Europe/Paris, converti en UTC - L'assignation des briques est une etape separee (batch) : les purchases sont creees sans briques, puis un job assigne les briques libres aux purchases en attente - Oversell bricks physiques : le worker d'assignation pose funding.blocked = { reason: 'bricks-oversell', at } (idempotent + alerte Slack) et decline l'exces en no-bricks-left (WT declined, pas de refund WT ; Redis non decremente sur ces declines) - Tant que funding.blocked est set (bricks-oversell ou counter-reconciliation) : mutations d'achat refusees ; watchers expiration/confirmation exclus via SQL ; assignation continue (drain). Affichage 100 % Redis = overlay lecture quand blocked - Cron funding-counter-reconciliation : block → drain waiting_for_assignation → stabilize → Redis=min(PG, capacity) → unblock (refuse si PG > capacity, ou si skipRedisCounterWrite et écart à corriger). Detail : funding-counter-reconciliation.md - Si le nombre total de briques achetees depasse le total apres assignation (race condition), la transaction est rollback - context.refund.at sur la WT d'achat = date de remboursement effectif, pas de demande : il reste null tant que le P2P de remboursement n'est pas confirme (le front affiche alors "en cours de restitution"). Tout nouveau kind de refund doit donc passer par PrimaryPurchaseRefundService.handleSucceededLemonwayP2PRefund dans le handler de succes P2P — sinon la ligne reste "en cours" a vie

Workflow funding : notStarted -> inProgress -> maxEndDateReached -> closed (success | failed)

Subtilites cloture funding : - Success requiert que le refund window du dernier achat soit expire (pas de tous les achats) - Apres cloture success, les refunds customer sont bloques meme si encore dans la fenetre de retractation - Seuls les refunds admin avec raison propertyNotFinanced restent possibles apres cloture - Si aucun achat n'existe, la cloture success est bloquee (pas de latestRefundableUntilDate)