Location (LLD/LCD)
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.
/workspace/lease/*. Côté API, deux familles de
préfixes coexistent : les ressources « affaire » et « contrat » sont des
collections REST à part entière (/api/workspace/lease-affairs/* et
/api/workspace/lease-contracts/*, avec tiret), tandis que les vues
agrégées et le simulateur restent sous /api/workspace/lease/* (dashboard,
pipeline, simulate-financing, billing/run…). 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
| Terme | Entité | Définition |
|---|---|---|
| Affaire | LeaseAffair | Dossier commercial pré-signature. Étapes QUALIFICATION → … → CEDED. |
| Contrat | LeaseContract | Engagement signé. Numéro CLLD-YY-MM-NNN généré sous advisory lock PostgreSQL. |
| Bailleur | Lender | Organisme financier (LIXXBAIL, GRENKE, CM LEASING, LOCAM, SOFIBRA, BNP…). |
| Demande d'accord | LenderApprovalRequest | Dossier soumis au bailleur pour validation (6 statuts). |
| Cession | Cession | Transfert du contrat de Pulse vers un bailleur ; irréversible une fois COMPLETED. |
| Type de refinancement | RefinancingType | MANAGEMENT_MANDATE / BACKED_LOAN / SELF_CARRIED. |
| Contrat miroir | LeaseContract (isMirror) | Jumeau créé lors d'une cession, relié via mirrorContractId. |
| PMT | — | Mensualité : PMT = C × i / (1 − (1+i)^(−n)). |
| Marge loueur | — | % ajouté au taux bailleur pour rémunération Pulse. |
| Loyer intercalaire | LeaseRentSchedule (isIntercalary) | Pro-rata entre signature et 1er loyer ordinaire. |
| Assurance | LeaseInsurance | Couverture matériels loués ; facturation annuelle au prorata. |
| Maintenance | LeaseMaintenance | Prestation périodique ; reversement au prestataire suivi. |
| Prélèvement bailleur | LenderPaymentSchedule | Échéance prélevée par le bailleur, pointée sur relevé bancaire. |
| Engagement bancaire | LeaseBankCommitment | Encours financier restant vis-à-vis d'un bailleur, par business unit. |
| Avenant | LeaseAmendment | Modification post-signature (durée, lignes, taux) ; numérotés 1..N. |
| Partenaire | Partner | Apporteur d'affaires / courtier ; perçoit une commission. |
| Type d'année | ScheduleYearType | FIRST / INTERMEDIATE / LAST — pilote le prorata temporis (assurance, maintenance). |
Le modèle complet est dans
docs/04-data-model.md§9.
Écrans
| Écran | Route | Contenu |
|---|---|---|
| Dashboard Location | /workspace/lease | 8 KPI dynamiques, funnel pipeline 10 statuts, exposition par bailleur, horizon 6 mois, alertes. |
| Liste affaires | /workspace/lease-affairs | DataTable 10 colonnes + 7 filtres URL-bound + vue Kanban 10 statuts. |
| Création affaire | /workspace/lease-affairs/new | Wizard 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-contracts | DataTable : 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/lenders | CRUD + fiche 360° (conditions, agréments, contrats cédés, encours, performance). |
| Agréments | /workspace/lease-approval-requests | Liste cross-affaires LenderApprovalRequest + fiche (conditions, échanges, historique). |
| Cessions | /workspace/lease-cessions | Liste cross-contrats (statut, montant, date effective) + fiche. |
| Échéancier global | /workspace/lease-rent-schedule | Tous les loyers à venir ; vue liste + toggle calendaire CSS 4×3 ; KPI M+1/M+3/M+6. |
| Assurances | /workspace/lease-insurance | LeaseInsurance avec alertes attestation manquante/expirée + échéancier par exercice. |
| Maintenance | /workspace/lease-maintenance | LeaseMaintenance + échéancier + suivi reversement prestataire. |
| Engagements bancaires | /workspace/lease-bank-commitments | Consolidation encours par bailleur et BU + pointage LenderPaymentSchedule. |
| Partenaires | /workspace/partners | CRUD + commissions dues/payées. |
Modèle de données (points clés)
LeaseContract.number: séquenceCLLD-YY-MM-NNNgénérée sous advisory lock PostgreSQL — immuable à la création.LeaseRentSchedule: tableau d'amortissement figé aprèsSIGNED(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 à étatsPROPOSED → ACCEPTED → COMPLETED, avec sortiesREFUSED/CANCELED(PROPOSEDpeut aller versACCEPTED/REFUSED/CANCELED;ACCEPTEDversCOMPLETED/CANCELED).COMPLETEDest irréversible. SirefinancingType = MANAGEMENT_MANDATE, un contrat miroir (isMirror = true,mirrorContractIdcroisé) est créé en transaction et le contrat d'origine passeCEDED.LeaseInsuranceSchedule/LeaseMaintenanceSchedule: une ligne par exercice civil, prorata viaprorataTemporis()(FIRST/INTERMEDIATE/LAST). Une ligneSENTouSETTLEDest immuable.LeaseBankCommitment.outstandingHt: recalculé chaque 1er du mois (periodicAmount × remainingRentCount) par le joblease:bank-commitment-refresh.LeaseContract.legacyExternalRef: référence Sat&Lease conservée pour les contrats repris.replacedByContractIdtrace la chaîne de remplacement.LenderApprovalRequestsoumise (SUBMITTED) ne peut pas être supprimée (audit).
API
Toutes les routes exigent une permission lease.* (vérifiée côté API et UI).
Les ressources « affaire » / « contrat » sont des collections REST à tiret
(lease-affairs, lease-contracts) ; les vues agrégées et le simulateur
restent sous lease/.
| Méthode | Route | Permission |
|---|---|---|
GET | /api/workspace/lease-affairs | lease.affair:read |
POST | /api/workspace/lease-affairs | lease.affair:create |
GET / PATCH | /api/workspace/lease-affairs/[id] | lease.affair:read / :update |
DELETE | /api/workspace/lease-affairs/[id] | lease.affair:delete |
GET / POST | /api/workspace/lease-affairs/[id]/financings | lease.affair:read / :update |
DELETE | /api/workspace/lease-affairs/[id]/financings/[financingId] | lease.affair:update |
GET / POST | /api/workspace/lease-affairs/[id]/approval-requests | lease.affair:read / :update |
POST | /api/workspace/lease/simulate-financing | lease.affair:read |
POST | /api/workspace/lease/simulate-financing/two-rates | lease.affair:read |
GET | /api/workspace/lease-approval-requests | lease.contract:read |
PATCH | /api/workspace/lease-approval-requests/[id] | lease.affair:update |
GET | /api/workspace/lease-contracts | lease.contract:read |
POST | /api/workspace/lease-contracts | lease.contract:create |
GET / PATCH | /api/workspace/lease-contracts/[id] | lease.contract:read / :update |
POST | /api/workspace/lease-contracts/[id]/sign | lease.contract:update |
POST | /api/workspace/lease-contracts/[id]/transition | lease.contract:update |
POST | /api/workspace/lease-contracts/[id]/lock | lease.contract:update |
POST | /api/workspace/lease-contracts/[id]/unlock | lease.contract:unlock |
GET / POST / DELETE | /api/workspace/lease-contracts/[id]/amendments/* | lease.contract:update |
GET / POST / DELETE | /api/workspace/lease-contracts/[id]/lines/* | lease.contract:update |
GET | /api/workspace/lease-contracts/[id]/rent-schedule | lease.contract:read |
POST | /api/workspace/lease-contracts/[id]/rent-schedule | lease.contract:update |
GET | /api/workspace/lease-contracts/[id]/mirror | lease.contract:read |
POST | /api/workspace/lease-contracts/[id]/cession | lease.contract:update |
GET / PATCH | /api/workspace/lease-contracts/[id]/cession | lease.contract:read / :update |
POST | /api/workspace/lease/billing/run | lease.contract:update |
GET / POST | /api/workspace/lease-contracts/[id]/insurance | lease.contract:read / :update |
GET / POST | /api/workspace/lease-contracts/[id]/maintenance | lease.contract:read / :update |
GET / POST | /api/workspace/lease-contracts/[id]/lender-payments | lease.contract:read / :update |
GET / POST | /api/workspace/lease-contracts/[id]/bank-commitments | lease.contract:read / :update |
PATCH | /api/workspace/lender-payments/[id]/reconcile | lease.contract:update |
PATCH | /api/workspace/lender-payments/[id]/reactivate | lease.contract:update |
POST | /api/workspace/lease-bank-commitments/refresh | lease.lender:read |
GET | /api/workspace/lease-bank-commitments | lease.contract:read |
GET | /api/workspace/lenders | lease.lender:read |
POST | /api/workspace/lenders | lease.lender:manage |
GET | /api/workspace/lenders/[id]/performance | lease.lender:read |
GET | /api/workspace/lease-rent-schedule | lease.contract:read |
GET | /api/workspace/lease/exposure-by-lender | lease.affair:read |
GET | /api/workspace/lease/pipeline | lease.affair:read |
GET | /api/workspace/lease/rent-horizon | lease.affair:read |
GET | /api/workspace/lease/alerts | lease.affair:read |
POST | /api/webhooks/yousign | signature HMAC (prod) |
propose-cession /
complete ni de permissions lease.cession:*. La cession se pilote sur le
contrat : POST …/lease-contracts/[id]/cession crée la proposition (PROPOSED),
puis PATCH …/lease-contracts/[id]/cession transitionne le statut (jusqu'à
COMPLETED). Les deux exigent lease.contract:update.Financement. La simulation est stateless (POST /api/workspace/lease/simulate-financing,
lease.affair:read). La sauvegarde de scénarios se fait sur l'affaire via
POST/GET/DELETE …/lease-affairs/[id]/financings[/financingId]
(lease.affair:update en écriture, lease.affair:read en lecture).Agréments & transitions d'affaire. La demande d'agrément se crée via
POST …/lease-affairs/[id]/approval-requests (lease.affair:update) ; la
soumission au bailleur et sa réponse passent par le mêmePATCH /api/workspace/lease-approval-requests/[id]. Le statut d'une affaire évolue par
le PATCH …/lease-affairs/[id] (champ status), pas par un endpoint
transition dédié.Exemple de référence d'endpoint :
/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)
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.
Jalons d'affaire (AffairCheckpoint)
Les jalons matérialisent les étapes de validation d'une affaire (CE1,
signature, etc.). Le mécanisme est partagé entre la Location et le CRM : un
jalon (AffairCheckpoint) est polymorphe et pointe via affairModel sur une
LeaseAffair ou une Opportunity (service
server/services/commercial/affair-checkpoint.service.ts).
commercial et non lease. Les
endpoints d'instances sont gardés par sales.quote:read / sales.quote:update ;
les templates sont gérés sous crm (crm.opportunity:read en lecture,
crm.settings:update en écriture). Aucune permission lease.* n'est requise.Modèle
AffairCheckpointTemplate: étape standard configurable par organisation (codeunique par org,label,position,isActive). Ciblage double :affairTypes(SALE/LEASE_LLD/LEASE_LCD/SERVICE) etnatureCodes(codesOpportunityNature). Vide = template « commun » posé sur toute affaire.AffairCheckpoint: instance posée sur une affaire (@@unique([affairModel, affairId, templateId])), portant unstatus,doneAt/doneById,notesetresultRef(référence vers l'entité produite —Invoice.id,File.id…).
AffairCheckpointStatus : PENDING · IN_PROGRESS · DONE · NA · BLOCKED.
Logique
- Bootstrap incrémental & idempotent :
bootstrapCheckpointsne pose que les templatesisActiveapplicables au périmètre résolu de l'affaire (type d'affaire + natures), sans dupliquer ni écraser les jalons déjà posés. Le périmètre d'uneLeaseAffairest dérivé de sonLeaseType(LLD/LCD) via le mappingserver/lib/commercial/affair-type-mapping.ts; celui d'uneOpportunityde l'union des natures portées par ses lignes. - Validation :
markDoneest le seul chemin versDONE(horodate et trace le validateur) ; il refuse un jalon déjàDONE(409). - Réaffectation :
setCheckpointStatusn'autorise quePENDING/NA/BLOCKED(DONEexclu) ; quitterDONEeffacedoneAt/doneByIdpour la cohérence des compteurs. - Toutes les mutations sont auditées (
commercial.checkpoints.bootstrap,commercial.checkpoint.done,commercial.checkpoint.status).
API
| Méthode | Route | Permission |
|---|---|---|
GET | /api/workspace/commercial/affair-checkpoints?affairModel=&affairId= | sales.quote:read |
POST | /api/workspace/commercial/affair-checkpoints (bootstrap) | sales.quote:update |
POST | /api/workspace/commercial/affair-checkpoints/[id]/done | sales.quote:update |
POST | /api/workspace/commercial/affair-checkpoints/[id]/status | sales.quote:update |
GET / POST | /api/workspace/crm/checkpoint-templates | crm.opportunity:read / crm.settings:update |
GET / PATCH / DELETE | /api/workspace/crm/checkpoint-templates/[id] | crm.opportunity:read / crm.settings:update |
Règles métier
- Tableau d'amortissement immuable après
SIGNED: sauf via avenant (seul vecteur légal de recalcul). lease.contract:unlock: permission spéciale réservéeADMIN, raison ≥ 20 chars, audit renforcé, email direction, re-lock automatique 24 h.- Cession
COMPLETEDirréversible : tout changement de bailleur post-cession est une novation complexe hors scope. - Numérotation contrat : séquence
CLLD-YY-MM-NNNsous advisory lock PostgreSQL, immuable. - Fichiers contrats/signatures : bucket
SIGNATURES_EVIDENCEObject Lock COMPLIANCE 10 ans (jamais supprimable).
- Un contrat ne peut être cédé qu'une seule fois (
mirrorContractIdanti-double cession). - Demande d'agrément
SUBMITTEDnon supprimable (audit trail). - Une échéance d'assurance ou maintenance
SENT/SETTLEDest 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 estACTIVE(lifecycle technique). - Mandat de gestion : Pulse reste émetteur des facturations tant que ce type de refinancement est actif.
- Contrats repris de Sat&Lease :
legacyExternalRefconservé, chaîne de remplacement tracée viareplacedByContractId.
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énement | Canal | Destinataire |
|---|---|---|
| Affaire créée | In-app | Owner + manager |
| Proposition acceptée | In-app + email | Owner |
| Réponse bailleur reçue | In-app + email | Owner + FINANCE_OFFICER |
| Contrat prêt à signature | In-app + email + SMS | Client (signataires) |
| Contrat signé | In-app + email | Client + owner + accounting |
| Loyer en retard | Client + accounting | |
| Restitution J-30 | Client + owner | |
| Cession finalisée | Client (changement créancier) | |
| Contrat unlocké | Direction Pulse | |
| Prélèvement rejeté | In-app | Gestionnaire |
Ventes & Facturation
Référence technique du domaine Ventes & Facturation — devis, commandes, factures, encaissements, achats fournisseur, facturation électronique B2B (réforme FR 2026).
Assets & Maintenance
Référence technique du domaine Assets & Maintenance — cycle de vie équipements, OT, pièces détachées, transport, SAV public.