Skip to content

Proposals

StatusShipped
LinearPIN-134 ↗
ModuleBids ↗

One proposal per bid, versioned append-only. Each Save & Generate PDF freezes a snapshot of the active estimate revision (contract total, CSI groups) and renders a deterministic PDF — regenerating an old version always reproduces the numbers captured at its creation, never today's estimate. No AI in the generation path.

Prerequisites

  • Role: Estimator, Chief Estimator, Senior PM, Executive, or Owner. (PM and below are read-only — but note the button isn't hidden for them, see Known Issues.)
  • Bid state: a bid whose Estimate has an active revision — the Proposal toolbar button is disabled with "Generate an estimate revision first" otherwise.
  • Environment: no special setup needed — PDF generation runs on a pure-Go renderer (PIN-504), so it works the same on staging as anywhere else.

1. Open the drawer

Bid → Estimate tab. In the toolbar's total strip, click Proposal (tooltip "Generate Proposal"). The Generate Proposal drawer opens.

2. Fill the proposal fields

Editable fields: Cover Letter, Scope Summary, Inclusions (one per line), Exclusions (one per line), Payment Terms (default Net 30), Valid for (days) (default 30), Terms & Conditions.

Under Previous Versions, existing revisions list as v{N} + date with a Download PDF pill (or "No PDF yet").

3. Generate

Click Save & Generate PDF (busy state "Generating PDF..."). On success: toast "Proposal PDF generated successfully", a new v{N} row appears with Download PDF, and the file also lands in the bid's Documents → 08 Proposal folder as proposal-v{N}.pdf.

Branding on the PDF (company name/logo/address/phone/email) comes from Settings → AppSettings, not from any Organization — default company name is "HVAC Construction Inc." A missing logo degrades to no logo, not an error.

4. Verify snapshot semantics

This is the core invariant worth testing:

  1. Generate v1. Note the contract total on the PDF.
  2. Change the estimate (add a line item, push takeoff, or activate a different estimate revision).
  3. Generate v2 — it must reflect the new numbers.
  4. Re-download v1 — it must still show the old numbers. Old versions never re-read live estimate data.

Verification Checklist

  • Proposal button disabled (with tooltip) until an estimate revision is active
  • Generate creates v1, toast fires, Download PDF works
  • PDF appears in Documents → 08 Proposal as proposal-v{N}.pdf
  • PDF shows AppSettings branding and the estimate's CSI table / inclusions / exclusions
  • Snapshot test above passes (v1 keeps old numbers after the estimate changes)
  • Versions only ever grow — no edit/delete of an existing version anywhere
  • No console errors throughout

Known Issues / Current Gaps

  • ⚠ The Proposal button is not permission-gated client-side — a read-only role can open the drawer and only fails server-side on generate. Check the rejection is a clean error, not a broken state.
  • Budget masking (PIN-432): roles without budget visibility get materials/labor/CSI cost breakdowns zeroed when reading proposal data; the client-facing contract total always shows. Masked zeros for a Financial-restricted role are intended.

Updating this walkthrough

Run the full flow yourself end-to-end. If it passes, bump last_verified: in the frontmatter to today's date. If any step fails, fix the doc or the code in the same PR — drift is the enemy.