Location (LLD/LCD)

Référence technique du domaine Location — affaires, contrats, financement PMT, agréments bailleurs, cessions, assurance, maintenance, prélèvements et engagements bancaires.

Location (LLD/LCD)

Périmètre : cycle de vie complet du dossier de location longue durée (LLD) et courte durée (LCD) — qualification, équipements, devis fournisseurs, financement, propositions, agréments bailleurs, signature, mise en service, loyers, assurance, maintenance, suivi des prélèvements bailleur, engagements bancaires, cession de contrat à bailleur, restitution. C'est le cœur du métier Pulse.

Tous les écrans vivent sous /workspace/lease/* et les routes API sous /api/workspace/lease/*. Source : docs/11-domain-lease-satelease.md. Les contrats repris de Sat&Lease conservent leur référence historique (legacyExternalRef, ex. 201805167).

Vocabulaire & entités

TermeEntitéDéfinition
AffaireLeaseAffairDossier commercial pré-signature. Étapes QUALIFICATION → … → CEDED.
ContratLeaseContractEngagement signé. Numéro CLLD-YY-MM-NNN généré sous advisory lock PostgreSQL.
BailleurLenderOrganisme financier (LIXXBAIL, GRENKE, CM LEASING, LOCAM, SOFIBRA, BNP…).
Demande d'accordLenderApprovalRequestDossier soumis au bailleur pour validation (6 statuts).
CessionCessionTransfert du contrat de Pulse vers un bailleur ; irréversible une fois COMPLETED.
Type de refinancementRefinancingTypeMANAGEMENT_MANDATE / BACKED_LOAN / SELF_CARRIED.
Contrat miroirLeaseContract (isMirror)Jumeau créé lors d'une cession, relié via mirrorContractId.
PMTMensualité : PMT = C × i / (1 − (1+i)^(−n)).
Marge loueur% ajouté au taux bailleur pour rémunération Pulse.
Loyer intercalaireLeaseRentSchedule (isIntercalary)Pro-rata entre signature et 1er loyer ordinaire.
AssuranceLeaseInsuranceCouverture matériels loués ; facturation annuelle au prorata.
MaintenanceLeaseMaintenancePrestation périodique ; reversement au prestataire suivi.
Prélèvement bailleurLenderPaymentScheduleÉchéance prélevée par le bailleur, pointée sur relevé bancaire.
Engagement bancaireLeaseBankCommitmentEncours financier restant vis-à-vis d'un bailleur, par business unit.
AvenantLeaseAmendmentModification post-signature (durée, lignes, taux) ; numérotés 1..N.
PartenairePartnerApporteur d'affaires / courtier ; perçoit une commission.
Type d'annéeScheduleYearTypeFIRST / INTERMEDIATE / LAST — pilote le prorata temporis (assurance, maintenance).

Le modèle complet est dans docs/04-data-model.md §9.

Écrans

ÉcranRouteContenu
Dashboard Location/workspace/lease8 KPI dynamiques, funnel pipeline 10 statuts, exposition par bailleur, horizon 6 mois, alertes.
Liste affaires/workspace/lease/affairsDataTable 10 colonnes + 7 filtres URL-bound + vue Kanban 10 statuts.
Création affaire/workspace/lease/affairs/newWizard 8 champs (type, client/SIREN, établissement, owner, date, montant, bailleur pressenti, opportunité CRM).
Fiche affaire/workspace/lease/affairs/[id]Stepper 10 statuts + 8 onglets : Synthèse, Équipements, Enveloppe, Financement, Proposition, Agréments, Documents, Audit.
Simulateur financement(composant dans fiche affaire)Capital / durée / taux → PMT, tableau d'amortissement, comparaison scénarios.
Liste contrats/workspace/lease/contractsDataTable : N°, client, loyer, statut, bailleur ; filtres statut/période/montant.
Fiche contrat/workspace/lease/contracts/[id]Onglets : Synthèse, Équipements, Financement, Loyers, Assurance, Maintenance, Prélèvements bailleur, Engagements, Documents, Avenants, Cession, Audit.
Bailleurs/workspace/lease/lendersCRUD + fiche 360° (conditions, agréments, contrats cédés, encours, performance).
Agréments/workspace/lease/approval-requestsListe cross-affaires LenderApprovalRequest + fiche (conditions, échanges, historique).
Cessions/workspace/lease/cessionsListe cross-contrats (statut, montant, date effective) + fiche.
Échéancier global/workspace/lease/rent-scheduleTous les loyers à venir ; vue liste + toggle calendaire CSS 4×3 ; KPI M+1/M+3/M+6.
Assurances/workspace/lease/insuranceLeaseInsurance avec alertes attestation manquante/expirée + échéancier par exercice.
Maintenance/workspace/lease/maintenanceLeaseMaintenance + échéancier + suivi reversement prestataire.
Engagements bancaires/workspace/lease/bank-commitmentsConsolidation encours par bailleur et BU + pointage LenderPaymentSchedule.
Partenaires/workspace/lease/partnersCRUD + commissions dues/payées.

Modèle de données (points clés)

  • LeaseContract.number : séquence CLLD-YY-MM-NNN générée sous advisory lock PostgreSQL — immuable à la création.
  • LeaseRentSchedule : tableau d'amortissement figé après SIGNED (sauf via avenant). La régénération est refusée si un échéancier existe déjà.
  • LeaseFinancing : stocke capital, taux, PMT, VR, frais de dossier et la décomposition de marge (brute/nette/commissions partenaire/Pulse).
  • LeaseRentSchedule.isIntercalary : ligne intercalaire distincte dans la 1ère facture (PMT × jours / 30).
  • Cession : machine à états PROPOSED → ACCEPTED → COMPLETED. COMPLETED est irréversible. Si refinancingType = MANAGEMENT_MANDATE, un contrat miroir (isMirror = true, mirrorContractId croisé) est créé en transaction.
  • LeaseInsuranceSchedule / LeaseMaintenanceSchedule : une ligne par exercice civil, prorata via prorataTemporis() (FIRST/INTERMEDIATE/LAST). Une ligne SENT ou SETTLED est immuable.
  • LeaseBankCommitment.outstandingHt : recalculé chaque 1er du mois (periodicAmount × remainingRentCount) par le job lease:bank-commitment-refresh.
  • LeaseContract.legacyExternalRef : référence Sat&Lease conservée pour les contrats repris. replacedByContractId trace la chaîne de remplacement.
  • LenderApprovalRequest soumise (SUBMITTED) ne peut pas être supprimée (audit).

API

Toutes les routes exigent une permission lease.* (vérifiée côté API et UI).

MéthodeRoutePermission
GET/api/workspace/lease/affairslease.affair:read
POST/api/workspace/lease/affairslease.affair:create
GET / PATCH/api/workspace/lease/affairs/[id]lease.affair:read / :update
POST/api/workspace/lease/affairs/[id]/transitionlease.affair:transition
POST/api/workspace/lease/affairs/[id]/simulate-financinglease.financing:simulate
GET / POST / DELETE/api/workspace/lease/affairs/[id]/financings/[fid]lease.financing:simulate
POST/api/workspace/lease/affairs/[id]/generate-proposallease.affair:update
POST/api/workspace/lease/affairs/[id]/approval-requestslease.approval_request:create
PATCH/api/workspace/lease/approval-requests/[id]/submitlease.approval_request:submit
PATCH/api/workspace/lease/approval-requests/[id]/answerlease.approval_request:submit
GET/api/workspace/lease/contractslease.contract:read
POST/api/workspace/lease/contractslease.contract:create
GET / PATCH/api/workspace/lease/contracts/[id]lease.contract:read / :update
POST/api/workspace/lease/contracts/[id]/signlease.contract:sign
POST/api/workspace/lease/contracts/[id]/unlocklease.contract:unlock
POST/api/workspace/lease/contracts/[id]/amendmentslease.contract:update
GET / POST / DELETE/api/workspace/lease/contracts/[id]/lines/*lease.contract:update
GET/api/workspace/lease/contracts/[id]/mirrorlease.contract:read
POST/api/workspace/lease/contracts/[id]/propose-cessionlease.cession:propose
POST/api/workspace/lease/cessions/[id]/completelease.cession:complete
POST/api/workspace/lease/billing/runlease.contract:update
GET / POST/api/workspace/lease/contracts/[id]/insurancelease.contract:read / :update
GET / POST/api/workspace/lease/contracts/[id]/maintenancelease.contract:read / :update
GET/api/workspace/lease/contracts/[id]/lender-paymentslease.contract:read
PATCH/api/workspace/lease/lender-payments/[id]/reconcilelease.contract:update
POST/api/workspace/lease/lease-bank-commitments/refreshlease.contract:update
GET/api/workspace/lease/bank-commitmentslease.lender:read
GET/api/workspace/lease/lenderslease.lender:read
POST/api/workspace/lease/lenderslease.lender:create
GET/api/workspace/lease/lenders/[id]/performancelease.lender:read
GET/api/workspace/lease/rent-schedulelease.contract:read
GET/api/workspace/lease/exposure-by-lenderlease.lender:read
GET/api/workspace/lease/pipelinelease.affair:read
GET/api/workspace/lease/rent-horizonlease.contract:read
GET/api/workspace/lease/alertslease.contract:read
POST/api/webhooks/yousignsignature HMAC (prod)

Exemple de référence d'endpoint :

POST/api/workspace/lease/contracts/[id]/unlockAuth

Déverrouille temporairement un contrat signé pour permettre des corrections. Réservé aux utilisateurs ADMIN ayant la permission lease.contract:unlock. Re-verrouillage automatique après 24 h.

Corps (JSON)

reason
string required
Raison du déverrouillage (minimum 20 caractères). Obligatoire.

Requête

curl -s -X POST "$API/api/workspace/lease/contracts/$ID/unlock" \
  -H "Content-Type: application/json" -H "Cookie: $SESSION" \
  -d '{"reason":"Erreur de saisie du taux bailleur validée par direction"}'

Réponse

{ "id": "ctr_…", "isLocked": false, "unlockedAt": "2026-06-16T10:00:00Z" }

Workflows

Cycle de vie d'une affaire LLD

QUALIFICATION
   │  (proposition financière prête)
   ▼
PROPOSAL
   │  (accord client sur principes)
   ▼
NEGOTIATION
   │  (lancement demande agrément bailleur)
   ▼
LENDER_REVIEW  ←──(refus → retour NEGOTIATION ou autre bailleur)
   │  (accord bailleur)
   ▼
CONTRACT_DRAFT
   │  (signature client + bailleur)
   ▼
SIGNED
   │  (mise en service)
   ▼
IN_USE
   ├──→ TERMINATED  (fin contrat normale ou anticipée + restitution)
   └──→ CEDED       (cédé à un bailleur)

Transitions contrôlées par la state machine lease.workflow.ts. Une affaire IN_USE ne peut plus être annulée, seulement résiliée.

Facturation des loyers

Job Nitro lease:billing (30 2 * * *) : pour chaque contrat ACTIVE ou CEDED (mandat de gestion inclus), génère 1 Invoice par LeaseRentSchedule dont dueDate ≤ today + 15j et status = PLANNED. Idempotent (invoiceId non-null → skip). 1ère facture = loyer intercalaire + frais de dossier + 1er loyer ordinaire.

Unlock & re-lock contrat

Réservé ADMIN. Raison obligatoire (≥ 20 chars) + confirmation 2-step + audit log renforcé + email automatique à la direction Pulse + re-verrouillage auto après 24 h. Task Nitro lease:contract-auto-relock schedulée 15 * * * *.

Avenant

Création avenant numéroté → PDF → signature (identique au contrat initial) → recalcul tableau d'amortissement si durée ou taux modifiés (nouveaux loyers à partir de la date effective). C'est le seul mécanisme permettant de modifier l'échéancier après SIGNED.

Cession à un bailleur

Proposition → acceptation bailleur (ACCEPTED) → émission facture de cession → création éventuelle contrat miroir → date effective → COMPLETED. Si MANAGEMENT_MANDATE : Pulse reste émetteur des facturations et responsable du suivi des prélèvements (LenderPaymentSchedule).

Facturation assurance & maintenance

Jobs lease-insurance-billing (0 3 * * *) et lease-maintenance-billing (0 4 * * *) : itèrent sur les échéances PLANNED dues → génèrent les factures → passent SENT puis SETTLED. Prorata temporis 1ère/dernière année géré par computeYearlySlices dans server/lib/finance.ts.

Pointage bancaire (3-voies)

PATCH /lender-payments/[id]/reconcile : transition ACTIVE → SETTLED ou REJECTED. En cas de rejet : statut REJECTED + notification gestionnaire. Job lease:bank-commitment-refresh (0 5 1 * *) : recalcule outstandingHt sur tous les engagements.

Signature électronique

Provider abstrait (FIXTURES / YOUSIGN / DOCUSEAL). Webhook HMAC Yousign → handleSignatureWebhook → transitions PENDING_CUSTOMER_SIGNATURE → PENDING_LENDER_SIGNATURE → ACTIVE. PDF signé + certificat eIDAS archivés dans bucket SIGNATURES_EVIDENCE (Object Lock COMPLIANCE 10 ans). Cron de rattrapage signature:archival-reconcile (45 * * * *) ré-essaie les enveloppes COMPLETED sans signedFileId.

Règles métier

Garde-fous non négociables
  • Tableau d'amortissement immuable après SIGNED : sauf via avenant (seul vecteur légal de recalcul).
  • lease.contract:unlock : permission spéciale réservée ADMIN, raison ≥ 20 chars, audit renforcé, email direction, re-lock automatique 24 h.
  • Cession COMPLETED irréversible : tout changement de bailleur post-cession est une novation complexe hors scope.
  • Numérotation contrat : séquence CLLD-YY-MM-NNN sous advisory lock PostgreSQL, immuable.
  • Fichiers contrats/signatures : bucket SIGNATURES_EVIDENCE Object Lock COMPLIANCE 10 ans (jamais supprimable).
  • Un contrat ne peut être cédé qu'une seule fois (mirrorContractId anti-double cession).
  • Demande d'agrément SUBMITTED non supprimable (audit trail).
  • Une échéance d'assurance ou maintenance SENT/SETTLED est immuable.
  • Affaire IN_USE : annulation interdite (résiliation uniquement).
  • Statut affaire et statut contrat sont distincts : une affaire peut être SIGNED (workflow) pendant que le contrat est ACTIVE (lifecycle technique).
  • Mandat de gestion : Pulse reste émetteur des facturations tant que ce type de refinancement est actif.
  • Contrats repris de Sat&Lease : legacyExternalRef conservé, chaîne de remplacement tracée via replacedByContractId.

KPIs

Nb affaires par statut · cycle moyen Qualification → Signature · taux d'aboutissement · taux d'accord par bailleur · délai moyen agrément · exposition (capital restant dû) par bailleur · encours engagements bancaires par bailleur et BU · ratio cession / auto-portage · marge brute/nette/cession par contrat · loyers attendus M+1/M+3/M+6 · taux attestations assurance reçues · taux de rejet des prélèvements.

Notifications

ÉvénementCanalDestinataire
Affaire crééeIn-appOwner + manager
Proposition acceptéeIn-app + emailOwner
Réponse bailleur reçueIn-app + emailOwner + FINANCE_OFFICER
Contrat prêt à signatureIn-app + email + SMSClient (signataires)
Contrat signéIn-app + emailClient + owner + accounting
Loyer en retardEmailClient + accounting
Restitution J-30EmailClient + owner
Cession finaliséeEmailClient (changement créancier)
Contrat unlockéEmailDirection Pulse
Prélèvement rejetéIn-appGestionnaire