AAuditPro Suite· Finance manual
Finance manual M10 Quotes

The 7-state machine

draft edit lines sent PDF emailed accepted → M07/M11 rejected reason captured expired cron auto-flip revised new version cancel

Quote header fields

1quote_number
Auto · QU/{YYYY}/{NNNN}

Atomic via NumberSequenceService. Once issued, never re-used.

2lead_id OR client_id
XOR — exactly one

Quote always anchored to one prospect. Lead-anchored cannot convert to invoice until lead is converted to client.

3issue_date · valid_until
Required dates

Validity drives the auto-expire cron. Default 30 days.

4currency_code
OMR default

FX rate captured if non-OMR; OMR-equivalent computed for reporting.

5subject
Free text

Header subject line on the PDF. e.g. "Annual Audit FY 2025 — fee proposal".

6scope_of_work
Rich text

Renders as a callout on the PDF. Describes deliverables + assumptions.

7payment_terms
Free text

Default from firm settings; per-quote override allowed.

8internal_notes
Internal only

Pricing rationale, partner approvals, internal reminders. Never on the PDF.

Line items

FieldTypeBehaviour
descriptionTextDatalist autocomplete: pulls from job titles, template descriptions, recent invoice lines
quantityDECIMAL(18,3)Hours / units / fixed=1
unit_priceDECIMAL(18,3)Per unit, OMR
discount_percentDECIMAL(5,2)0-100, applied per line
vat_categorystandard / zero / exemptStandard = 5%, others = 0% but tracked separately
line_subtotalComputedqty × price × (1 - discount/100)
line_vatComputedsubtotal × vat_rate
line_totalComputedsubtotal + line_vat

All math via bcmath scale 3. Header totals roll up: subtotal + vat - header_discount = grand_total.

Step-by-step — issue a quote

  1. From a lead OR client

    Lead detail → Create quote (inherits lead's contact). Or client detail → New quote. Auto-numbered draft created.

  2. Fill header

    Subject · scope · valid-until · currency. Defaults pre-filled where firm settings have them.

  3. Add line items

    Description (with autocomplete) · qty · unit_price · discount % · VAT category. Repeat for OOP, travel, etc. Header rolls up live as you type.

  4. Preview PDF

    Click Preview. Branded teal-accented A4 layout: firm letterhead, scope-of-work callout, line table, totals, T&Cs, two-signature block.

  5. Send

    Click Send. M17 emails the PDF to the lead/client primary contact via firm SMTP. Template quote.sent. Status flips draft → sent.

  6. Track + chase

    Quote pipeline shows pending quotes by stage. Stuck filter (no activity 14d+) surfaces dead quotes. Follow up.

  7. Accept

    Client emails confirmation. From quote detail → Mark accepted. Status sent → accepted. Lineage stamps accepted_at + by.

  8. 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_id stamped.

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:

Try this

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.

Watch out

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.

Tip — quote conversion KPI

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.