Mortgage Application Wizard Steps

Summary

After a MortgageApplication is created in draft status, the applicant (or their Pro agent) fills in the application through a 5-step wizard. Each step is saved individually via PUT /mortgage/applications/:id/step/:step. Steps 1, 2, and 4 persist JSON data to dedicated columns on the application. Steps 3 and 5 advance the currentStep counter only. Step 1 also handles an optional co-borrower (upsert or delete). After all steps and at least one document, POST /mortgage/applications/:id/submit transitions the application to pending_review and creates a BrokerAssignment.

State Diagram

stateDiagram-v2
    state "Brouillon" as Draft
    state "Étape 1 — Profil sauvegardé" as Step1Saved
    state "Étape 2 — Finances sauvegardées" as Step2Saved
    state "Étape 3 — Avancée" as Step3Advanced
    state "Étape 4 — Projet sauvegardé" as Step4Saved
    state "Étape 5 — Avancée" as Step5Advanced
    state "En attente de revue" as PendingReview
    [*] --> Draft
    Draft --> Step1Saved: applicant saves step 1 (profileData + optional coBorrower)
    Step1Saved --> Step2Saved: applicant saves step 2 (financialData)
    Step2Saved --> Step3Advanced: applicant completes step 3
    Step3Advanced --> Step4Saved: applicant saves step 4 (projectData)
    Step4Saved --> Step5Advanced: applicant completes step 5
    Step5Advanced --> PendingReview: applicant submits (all sections + at least 1 doc)
    Draft --> PendingReview: applicant submits (all sections already complete)
    PendingReview --> [*]

Steps

1. Save Step 1 — Profile Data (Actor: acheteur or pro)

PUT /mortgage/applications/:id/step/1 with body { data: {...}, coBorrower?: {...} }.

  • Writes profileData JSON to the application.
  • If callerRole === "pro": also updates the placeholder Acheteur record with real name/email/phone from data.
  • If coBorrower is a non-empty object: upserts a CoBorrower record linked to the application.
  • If coBorrower is an explicit empty object {}: deletes any existing CoBorrower.
  • currentStep advances to Math.max(currentStep, 1) — never goes backward.
  • completionRate is recalculated: each of profileData, financialData, projectData, and docs present contributes 25%.

Triggers: User completes the personal profile form Outcome: profileData persisted, optional co-borrower record upserted or deleted, completionRate updated

2. Save Step 2 — Financial Data (Actor: acheteur or pro)

PUT /mortgage/applications/:id/step/2 with body { data: {...} }.

  • Writes financialData JSON column.
  • Advances currentStep to at least 2.
  • Recalculates completionRate.

Triggers: User completes the financial situation form Outcome: financialData persisted

3. Save Step 3 — Advance Only (Actor: acheteur or pro)

PUT /mortgage/applications/:id/step/3 with body { data: {...} }.

  • No dedicated JSON column — data is accepted but not stored.
  • Advances currentStep to at least 3.
  • Recalculates completionRate based on previously saved data.

Triggers: User completes step 3 UI (e.g. document review or declaration page) Outcome: currentStep advanced

4. Save Step 4 — Project Data (Actor: acheteur or pro)

PUT /mortgage/applications/:id/step/4 with body { data: {...} }.

  • Writes projectData JSON column.
  • Advances currentStep to at least 4.
  • Recalculates completionRate.

Triggers: User completes the property project form Outcome: projectData persisted

5. Save Step 5 — Advance Only (Actor: acheteur or pro)

PUT /mortgage/applications/:id/step/5 with body { data: {...} }.

  • No dedicated JSON column.
  • Advances currentStep to at least 5.
  • Recalculates completionRate.

Triggers: User completes final review step Outcome: currentStep advanced to 5

6. Submit Application (Actor: acheteur or pro)

POST /mortgage/applications/:id/submit.

Pre-conditions validated before submission:

  • profileData, financialData, projectData must all be non-null.
  • At least one MortgageDocument must be linked to the application.
  • At least one active Pro must exist in the system to receive the assignment.

On success (Prisma transaction, atomic):

  • MortgageApplication.statuspending_review
  • MortgageApplication.completionRate → 100
  • MortgageApplication.sentAt → now
  • BrokerAssignment created with status: "assigned" linked to the first active Pro.

Fire-and-forget Slack notification via notifyMortgageSubmission.

Triggers: User clicks “Submit” after completing all steps and uploading documents Outcome: Application transitions to pending_review, broker is assigned, Slack notified

Error States

  • Ownership violation (wrong acheteurId or proId) → 404, application not found
  • Invalid step number → no dedicated column but currentStep still advances
  • Submit with missing sections → 422, INCOMPLETE_APPLICATION
  • Submit with no documents → 422, NO_DOCUMENTS
  • Submit with no active Pro → 503, NO_PRO_AVAILABLE