Broker Assignment and Application Review

Summary

When a mortgage application is submitted, a BrokerAssignment record is created pointing to the first active Pro in the system. The assigned Pro can then list all their assignments, view full application detail, update the assignment status (which mirrors to the application status), and export a ZIP archive of the full dossier. Status transitions are kept in sync between BrokerAssignment and MortgageApplication via a Prisma transaction to guarantee consistency.

State Diagram

stateDiagram-v2
    state "Dossier assigné" as Assigned
    state "Traitement en cours" as InProgress
    state "Dossier approuvé" as Approved
    state "Dossier refusé" as Refused
    [*] --> Assigned: BrokerAssignment created on application submit
    Assigned --> InProgress: Pro updates status to in_progress
    InProgress --> Approved: Pro approves → MortgageApplication.status = complete
    InProgress --> Refused: Pro refuses → MortgageApplication.status = rejected
    Assigned --> Approved: Direct approval without in_progress step
    Assigned --> Refused: Direct refusal
    Approved --> [*]
    Refused --> [*]

Steps

1. Assignment Creation (Actor: system)

Triggered automatically at the end of POST /mortgage/applications/:id/submit (see wizard-steps).

  • prisma.pro.findFirst({ where: { isActive: true } }) — first active Pro in creation order.
  • prisma.brokerAssignment.create({ data: { applicationId, proId, status: "assigned" } }) — atomic with the application status update in a $transaction.
  • notifyMortgageSubmission(buyerName, applicationId) — fire-and-forget Slack notification.

Triggers: Application successfully submitted Outcome: BrokerAssignment with status: "assigned", Slack message posted

2. List Assigned Applications (Actor: pro)

GET /pro/applications — returns all BrokerAssignment records where proId = request.proId, ordered by most recently updated. Includes nested application summary with buyer info, property, and document analysis statuses.

Triggers: Pro opens their back-office dashboard Outcome: Array of assignments with lightweight application summaries

3. View Application Detail (Actor: pro)

GET /pro/applications/:id — finds the BrokerAssignment scoped to { applicationId: id, proId: request.proId }. Returns the full application with acheteur, property, all documents, and co-borrower data.

Cross-Pro access is prevented: a Pro can only retrieve applications assigned to them.

Triggers: Pro selects a specific application Outcome: Full application + assignment detail

4. Update Assignment Status (Actor: pro)

PUT /pro/applications/:id/status with body { status, notes? }.

Valid assignment statuses: assigned, in_progress, approved, refused.

Status mapping to application status (atomic Prisma transaction):

Assignment statusApplication status
approvedcomplete
refusedrejected
any othersent

Both BrokerAssignment and MortgageApplication are updated in the same $transaction — neither can succeed alone.

Triggers: Pro makes a decision or marks work in progress Outcome: Both assignment and application statuses updated atomically

5. Export Application as ZIP (Actor: pro)

GET /pro/applications/:id/export — generates a ZIP archive via generateMortgageExportZip() from lib/mortgage-export.ts.

Archive contents:

  • synthese.json — structured application summary (profile, financial, project, co-borrower, property, metadata)
  • documents/{category}/{fileName} — all uploaded document files from disk

Response headers set to application/zip with Content-Disposition: attachment; filename="dossier-{id}.zip".

Triggers: Pro requests to download the complete dossier Outcome: ZIP buffer streamed as a file download

Error States

  • No active Pro available at submission time → 503 NO_PRO_AVAILABLE (submit blocked)
  • Assignment not found or wrong Pro → 404 NOT_FOUND
  • Invalid status value → 400 (schema validation)
  • Archive directory or files missing during export → 404 or partial ZIP