# CRM and Quotations

Phase 12 adds permission-scoped contact and lead management plus public and
assisted quotation workflows. Submitted quotations preserve the exact pricing
inputs and outputs used at submission so later catalog, rule, or price changes
cannot rewrite commercial history.

## Contact and lead contract

- Contacts are keyed by a normalized, case-insensitive email address. Public
  requests reuse an existing contact instead of creating duplicates.
- Leads retain their source attribution and use an explicit lifecycle:
  `new`, `qualified`, `contacted`, `proposal_sent`, `won`, or `lost`.
- Lead transitions are allow-listed and optimistic. A lost lead requires a
  reason, and every initial or later state is appended to status history.
- Restrictive foreign keys preserve contacts referenced by leads, quotations,
  and financial history.

## Draft and submission contract

- A draft references one active contact, one price list, and unique typed
  catalog selections with positive decimal quantities. Draft edits use
  `lock_version`; stale and duplicate updates fail atomically.
- Submission captures one UTC calculation time, locks the draft and price list,
  and invokes the Phase 9 pricing service inside the transaction.
- The quotation header stores pricing input, applied-rule, subtotal,
  adjustment, tax, total, currency, and engine-version snapshots. Each line
  stores its label, quantity, unit amount, line total, and source price-entry
  identifier.
- Submitted header financial fields and all submitted lines are immutable.
  Repricing requires a new draft rather than modifying the original result.
- The quotation lifecycle is `draft`, `submitted`, `reviewing`, `contacted`,
  `proposal_sent`, `won`, or `lost`. Transitions are allow-listed, lost requires
  a reason, and every transition is append-only history.

## Access, notifications, and privacy

- Public `GET/POST /quote` exposes only currently effective, active price-list
  options and is throttled. The server derives typed selections and price
  results; browser-supplied totals are never trusted.
- `contacts.manage` and `leads.manage` govern their respective CRM surfaces.
  `quotations.view` grants read-only quotation access, while
  `quotations.manage` is required for draft, submit, and workflow actions.
- Submission queues the after-commit `quotations.submitted` notification through
  the tracked delivery system. Audit records contain identifiers and commercial
  metadata but exclude customer name, email, phone, and notes.

## Operations

- Keep a worker consuming the `notifications` queue and monitor the delivery
  console for the quotation template.
- Run MySQL 8 CI before production certification. SQLite verifies functional
  behavior but cannot certify `FOR UPDATE` serialization under concurrent draft
  submissions.
- Treat contacts, leads, customer notes, and quotation snapshots as personal or
  commercial data. Grant CRM permissions narrowly and apply retention/export
  procedures to a contact and its dependent history as one governed record set.
- Pricing engine changes must increment the engine version and preserve support
  for interpreting every version already stored in submitted quotations.
