M10 Quotes — fee proposals from draft to accepted
A quote can originate from a lead OR a client (exactly one required). Full line-item pipeline mirroring M11 — bcmath scale 3, VAT aggregation, currency + FX, per-line discount. 7-state machine. Auto-expire cron. Branded teal-accented PDF via dompdf.
The 7-state machine
Quote header fields
Atomic via NumberSequenceService. Once issued, never re-used.
Quote always anchored to one prospect. Lead-anchored cannot convert to invoice until lead is converted to client.
Validity drives the auto-expire cron. Default 30 days.
FX rate captured if non-OMR; OMR-equivalent computed for reporting.
Header subject line on the PDF. e.g. "Annual Audit FY 2025 — fee proposal".
Renders as a callout on the PDF. Describes deliverables + assumptions.
Default from firm settings; per-quote override allowed.
Pricing rationale, partner approvals, internal reminders. Never on the PDF.
Line items
| Field | Type | Behaviour |
|---|---|---|
| description | Text | Datalist autocomplete: pulls from job titles, template descriptions, recent invoice lines |
| quantity | DECIMAL(18,3) | Hours / units / fixed=1 |
| unit_price | DECIMAL(18,3) | Per unit, OMR |
| discount_percent | DECIMAL(5,2) | 0-100, applied per line |
| vat_category | standard / zero / exempt | Standard = 5%, others = 0% but tracked separately |
| line_subtotal | Computed | qty × price × (1 - discount/100) |
| line_vat | Computed | subtotal × vat_rate |
| line_total | Computed | subtotal + line_vat |
All math via bcmath scale 3. Header totals roll up: subtotal + vat - header_discount = grand_total.
Step-by-step — issue a quote
From a lead OR client
Lead detail → Create quote (inherits lead's contact). Or client detail → New quote. Auto-numbered draft created.
Fill header
Subject · scope · valid-until · currency. Defaults pre-filled where firm settings have them.
Add line items
Description (with autocomplete) · qty · unit_price · discount % · VAT category. Repeat for OOP, travel, etc. Header rolls up live as you type.
Preview PDF
Click Preview. Branded teal-accented A4 layout: firm letterhead, scope-of-work callout, line table, totals, T&Cs, two-signature block.
Send
Click Send. M17 emails the PDF to the lead/client primary contact via firm SMTP. Template
quote.sent. Status flips draft → sent.Track + chase
Quote pipeline shows pending quotes by stage. Stuck filter (no activity 14d+) surfaces dead quotes. Follow up.
Accept
Client emails confirmation. From quote detail → Mark accepted. Status sent → accepted. Lineage stamps
accepted_at+ by.Convert
From accepted quote → Convert to invoice (if client; lead-anchored quotes need lead-converted-to-client first). Lines mirror to a draft tax invoice.
quote.converted_to_invoice_id+invoice.parent_quote_idstamped.
Auto-expire cron
QuoteStateService::markExpired() runs nightly. For any quote where status='sent' AND valid_until < CURDATE(), status auto-flips to expired. Notification fires to the lead-owner. Audit-log entry. The original PDF + lineage are preserved; only the status changes.
Revising a quote
Sometimes a client negotiates. Two paths:
- Edit the draft — only allowed while status=draft
- Revise sent quote — click Revise on a sent quote. Original flips to revised (immutable). New draft auto-created with cloned lines (auto-numbered next QU sequence). Revision lineage captured:
quote.revised_from_id
Open any active lead → Create quote → 3 line items. Watch line totals + VAT + header total compute live as you type. Save as draft. Click Preview PDF. The branded teal layout renders in < 1 second from dompdf cache.
Lead-anchored quotes can't convert to invoice until the lead is first converted to a client. The system rejects with a clear message. Convert the lead first (preserves the lineage), then convert the quote. Don't try to manually swap lead_id for client_id in the DB.
Reports → Quote Conversion shows acceptance % by fee bracket. If 80% of OMR 5,000 quotes accept but only 20% of OMR 30,000 accept, your large-fee positioning needs work. Tune scope or pricing accordingly. This is the single best lever for revenue growth.