Fiche Express Lot — Génération PDF
Summary
A Pro (or Admin) requests a formatted PDF sheet for a single Lot within a Programme. The system fetches lot and programme data, resolves an optional floor-plan image (embedded as a base64 data URI to bypass Puppeteer’s AppArmor sandbox), renders an HTML template, and converts it to PDF via Puppeteer. The resulting PDF is either streamed directly to the browser as a download, or attached to a transactional email sent on behalf of the Pro to a prospect’s address. A per-day email rate limit guards against abuse.
State Diagram
stateDiagram-v2 state "Requête reçue" as RequestReceived state "Chargement données" as DataFetch state "Lot introuvable" as LotNotFound state "Résolution du plan" as PlanResolution state "Rendu HTML" as HtmlRender state "Génération PDF" as PdfGeneration state "Mode téléchargement" as DownloadMode state "Mode email" as EmailMode state "Vérification taille" as SizeCheck state "PDF trop lourd" as TooLarge state "Vérification quota" as RateLimitCheck state "Quota dépassé" as RateLimitExceeded state "Email envoyé" as EmailSent [*] --> RequestReceived RequestReceived --> DataFetch: request received DataFetch --> LotNotFound: lot missing LotNotFound --> [*] DataFetch --> PlanResolution: lot found PlanResolution --> HtmlRender: plan resolved or skipped HtmlRender --> PdfGeneration PdfGeneration --> DownloadMode: mode = download PdfGeneration --> EmailMode: mode = email DownloadMode --> [*]: PDF streamed to browser EmailMode --> SizeCheck SizeCheck --> TooLarge: PDF > 4 MB TooLarge --> [*] SizeCheck --> RateLimitCheck: PDF <= 4 MB RateLimitCheck --> RateLimitExceeded: daily limit hit RateLimitExceeded --> [*] RateLimitCheck --> EmailSent: limit OK EmailSent --> [*]
Steps
1. Authentication (Actor: system)
Every request passes through authenticateProOrAdmin. A valid Pro or Admin JWT is required.
Triggers: Incoming POST request to /:lotId/generate
Outcome: request.proId or request.adminId populated; 401 if missing
2. Body Validation (Actor: system)
The request body is validated against generateFicheBodySchema. Required fields: selectedDispositifs (array of financing scheme keys), mode (download | email). When mode = email, email field is required.
Triggers: Auth passed
Outcome: Typed body available; 400 EMAIL_REQUIRED if mode is email but email is absent
3. Data Fetch (Actor: system)
buildFicheExpressData({ lotId, proId }) queries the database for the lot, its parent programme, and the Pro’s profile (for branding). Admin callers pass proId = null and receive default SeaLion branding.
Triggers: Validation passed
Outcome: Structured data object with lot, programme, and pro; 404 LOT_NOT_FOUND on failure
4. Floor Plan Resolution (Actor: system)
resolveLotPlan searches for a PNG floor plan matching the lot number, looking first in lot-level documents then in programme-level documentsVente. If found, the PNG is read from disk and base64-encoded into a data URI so Puppeteer can render it without filesystem access issues.
Triggers: Data fetch succeeded
Outcome: planMatch object with pngDataUri, or null if no plan exists (non-blocking)
5. HTML Rendering (Actor: system)
renderFicheExpressHtml({ data, selectedDispositifs, planMatch }) produces a complete HTML page containing lot details, programme information, selected financing schemes, Pro branding, and the optional floor plan.
Triggers: Plan resolution complete Outcome: HTML string ready for PDF conversion
6. PDF Generation (Actor: system)
renderHtmlToPdf(html) launches Puppeteer, renders the HTML in a headless browser, and prints to PDF.
Triggers: HTML rendered
Outcome: Buffer containing the PDF bytes
7a. Download Mode (Actor: pro)
The PDF buffer is sent directly in the HTTP response with Content-Type: application/pdf and Content-Disposition: attachment. Filename format: SeaLion_{Programme}_{LotN}_{YYYY-MM-DD}.pdf.
Triggers: mode = download
Outcome: Browser downloads the PDF
7b. Email Mode (Actor: system)
The PDF size is checked against the 4 MB limit. Then the Pro’s daily email counter is checked and incremented via checkAndIncrementEmailLimit. sendEmail sends the PDF as an attachment from noreply@sealion.cacio.fr with Reply-To set to the Pro’s own email address. An optional custom message from the Pro is included in the email body.
Triggers: mode = email
Outcome: Email delivered to prospect; { data: { sent: true } } returned
Error States
- Lot not found → 404,
LOT_NOT_FOUND - Email missing in email mode → 400,
EMAIL_REQUIRED - PDF exceeds 4 MB in email mode → 413,
PDF_TOO_LARGE— “Ce PDF est trop lourd pour être envoyé par email, téléchargez-le et envoyez-le manuellement” - Daily email rate limit reached → 429,
RATE_LIMIT_EXCEEDED— “Limite d’envois journalière atteinte” - Floor plan PNG unreadable → non-fatal, logged, fiche rendered without plan
Related Processes
- fiche-express-programme — same PDF pipeline applied to a full Programme instead of a single Lot
- registration — prerequisite: Pro must be registered and active to access this route