# Task D.3: Lead / Enquiry Ownership Refinement ## 1. Files Added - `drizzle/0012_odd_fat_cobra.sql` - `docs/adr/0011-lead-enquiry-ownership-model.md` - `docs/implementation/task-d3-lead-enquiry-ownership-refinement.md` ## 2. Files Modified - `src/db/schema.ts` - `src/lib/auth/rbac.ts` - `src/features/users/components/user-form-sheet.tsx` - `src/features/crm/enquiries/api/types.ts` - `src/features/crm/enquiries/server/service.ts` - `src/features/crm/quotations/server/service.ts` - `src/app/api/crm/enquiries/route.ts` - `src/app/api/crm/enquiries/[id]/route.ts` - `src/app/api/crm/enquiries/[id]/assign/route.ts` - `src/app/api/crm/enquiries/[id]/reassign/route.ts` - `src/app/api/crm/enquiries/[id]/customers/route.ts` - `src/app/api/crm/enquiries/[id]/followups/route.ts` - `src/app/api/crm/enquiries/[id]/followups/[followupId]/route.ts` - `src/app/api/crm/dashboard/route.ts` - `src/app/api/crm/dashboard/export/route.ts` - `src/app/dashboard/crm/enquiries/page.tsx` - `src/app/dashboard/crm/enquiries/[id]/page.tsx` - `src/config/nav-config.ts` - `src/features/crm/enquiries/components/enquiry-form-sheet.tsx` - `src/features/crm/enquiries/components/enquiry-assignment-dialog.tsx` - `src/features/crm/enquiries/components/enquiry-columns.tsx` - `src/features/crm/enquiries/components/enquiry-detail.tsx` - `src/features/crm/enquiries/components/enquiry-cell-action.tsx` - `src/features/crm/dashboard/api/types.ts` - `src/features/crm/dashboard/server/service.ts` - `src/features/crm/dashboard/components/crm-dashboard.tsx` - `src/features/crm/dashboard/components/dashboard-summary-cards.tsx` - `src/features/crm/dashboard/components/dashboard-funnel.tsx` - `src/features/crm/dashboard/components/dashboard-sales-ranking.tsx` - `src/features/crm/dashboard/components/dashboard-followups.tsx` - `src/features/crm/dashboard/components/dashboard-hot-projects.tsx` ## 3. Pipeline Changes - Added `crm_enquiries.pipeline_stage` - Allowed values: - `lead` - `enquiry` - `closed_won` - `closed_lost` - Backfilled legacy enquiry rows: - assigned rows -> `enquiry` - unassigned rows -> `lead` - closed-lost/cancelled status rows -> `closed_lost` - Assign flow now converts `lead -> enquiry` - Quotation accepted/lost/rejected outcomes can synchronize linked enquiry lifecycle to won/lost stages ## 4. Ownership Changes - Added business role `marketing` - Marketing-created records default to `lead` - Sales-created records default to `enquiry` - `sales` and `sales_support` now use server-enforced own-or-assigned enquiry visibility - managers, admins, and marketing retain broader monitoring visibility ## 5. Visibility Rules Added - Marketing can read CRM lead/enquiry monitoring surfaces - Marketing cannot see quotation pricing or approval analytics - Enquiry detail hides related quotation links when the user lacks quotation read access - CRM dashboard hides quotation, revenue, hot-enquiry, sales-ranking, and approval sections when the user lacks those permissions ## 6. KPI Changes - Dashboard summary now uses `pipelineStage` for: - lead count - enquiry count - won count - lost count - User-facing KPI language changed from `Opportunity` to `Enquiry` - Funnel wording now follows lead -> enquiry -> quotation flow - Follow-up and ownership labels now use `Enquiry Owner` ## 7. ADR Added - Added `docs/adr/0011-lead-enquiry-ownership-model.md` - This freezes: - lead ownership - enquiry ownership - assignment conversion behavior - marketing visibility restriction boundaries - won/lost lifecycle naming ## 8. Migration Notes - Apply `drizzle/0012_odd_fat_cobra.sql` - Existing `crm_enquiries` rows are backfilled in-place; no new lead table is introduced - Existing forms and APIs remain shared across lead and enquiry flows - Existing quotation routes remain intact; only linked enquiry lifecycle sync was added ## 9. Remaining Risks - `closed_won` still has no dedicated timestamp field; lifecycle timing remains coarse - dashboard/export visibility now follows permission boundaries, but any future custom report endpoints must preserve the same commercial-data guardrails - quotation-to-enquiry lifecycle sync currently depends on quotation status transitions and does not yet model reopen scenarios explicitly ## Verification - `npx tsc --noEmit`