# Phase 2 — v1 CRM End-to-End

> **Goal:** Full CRUD for Lead + Deal, Kanban board, read-only Pipeline endpoint,
> CRM Contact view, Activity timeline, authorization, label integration, sidebar menu.
> **Done-when:** All 9 feature tests pass. 5 user journeys verified in browser per profile.

---

## Implementation Order

| Step | Module | Spec |
|------|--------|------|
| 2.1 | Lead module | [`modules/lead.md`](../modules/lead.md) |
| 2.2 | Deal module | [`modules/deal.md`](../modules/deal.md) |
| 2.3 | Pipeline endpoint | [`modules/pipeline.md`](../modules/pipeline.md) |
| 2.4 | CRM Contact view | [`modules/contact.md`](../modules/contact.md) |
| 2.5 | Activity timeline | [`modules/activity.md`](../modules/activity.md) |
| 2.6 | DynamicFields component | [`engines/field-engine.md`](../engines/field-engine.md) — already spec'd |
| 2.7 | LeadPolicy + DealPolicy | [`modules/lead.md`](../modules/lead.md) |
| 2.8 | Label integration | Mount `LabelProvider` in AppLayout; use `useLabel()` in CRM pages |
| 2.9 | Registry + sidebar | [`setup.md`](../setup.md) AppRegistry + `application-menu-items.tsx` |
| 2.10 | `HandleInertiaRequests` | Add `labels` share — [`engines/label-engine.md`](../engines/label-engine.md) |
| 2.11 | ProductProfileSeeder hook | [`setup.md`](../setup.md) |
| 2.12 | 9 Feature tests | See below |
| 2.13 | 5 User journeys | See below |
| 2.14 | CLAUDE.md audit | Verify all pages comply — wrapper, layout, breadcrumbs, subtitle |

---

## Sidebar Menu Entry (`application-menu-items.tsx`)

```tsx
{
    title: 'CRM',
    icon: Users,
    tenantAccess: ['apps', 'CrmApp'],
    children: [
        { title: 'Leads',    href: route('leads.index'),    tenantAccess: ['modules', 'Lead'] },
        { title: 'Deals',    href: route('deals.index'),    tenantAccess: ['modules', 'Deal'] },
        { title: 'Contacts', href: route('crm.contacts.index'), tenantAccess: ['modules', 'Contact'] },
        { title: 'Activity', href: route('crm.activity.index'), tenantAccess: ['modules', 'Activity'] },
    ],
}
```

---

## 9 Required Feature Tests (CI Gate)

```
tests/Feature/CRM/LeadCrudTest.php          — create, read, update, delete
tests/Feature/CRM/LeadFilterTest.php        — search, status, preferred_country, sort
tests/Feature/CRM/LeadConversionTest.php    — convert resolves target via EntityRegistry, creates contact + deal, AlreadyConvertedException, transaction rollback on mid-flow failure
tests/Feature/CRM/LeadAuthorizationTest.php — manager sees only branch, sales sees only own
tests/Feature/CRM/WorkflowFiringTest.php    — lead.created triggers rule, WorkflowRun created
tests/Feature/CRM/DealKanbanTest.php        — board groups deals by stage, stage move
tests/Feature/CRM/DealAuthorizationTest.php — same scope rules as Lead
tests/Feature/CRM/LabelResolverTest.php     — education profile returns Application/Enrollment/Student
tests/Feature/CRM/ProductProfileActivationTest.php — setup:crm seeds correct pack for each of 5 profiles
```

---

## 5 User Journeys (Browser Verification per Profile)

| # | Profile | Journey |
|---|---------|---------|
| J1 | General | Create lead → qualified → convert → deal appears in board |
| J2 | Education | Create application → assign counselor (auto via workflow) → move to Offer Received |
| J3 | Real Estate | Create enquiry → schedule site visit → make offer → convert to property deal |
| J4 | Pharma | Create doctor lead → auto-assign MR → progress to Key Account |
| J5 | Garments | Create buyer lead → sample approved → price negotiation → order placed |

Cross-profile check: verify sidebar shows "Applications" on education profile, "Buyers" on garments profile.

---

## Authorization Rules

| Role | Lead scope | Deal scope |
|------|------------|------------|
| admin | all | all |
| manager | branch (`branch_id` match) | branch |
| sales / counselor / agent | own (`owner_id` match) | own |

Implemented in `LeadPolicy` + `DealPolicy`. `LeadService::baseQuery()` applies scope automatically.

---

## Done-When Checklist

- [ ] All 9 feature tests pass (`php artisan test --filter=CRM`)
- [ ] `php artisan route:list --name=leads` shows all 7 routes + bulk-action + convert
- [ ] Kanban board groups deals correctly, drag-to-stage works
- [ ] Education profile: sidebar shows "Applications" not "Leads"
- [ ] Dynamic custom fields render in Create/Edit forms for each profile
- [ ] `LeadService::convert()` is wrapped in DB::transaction (code review)
- [ ] `LeadService::convert()` resolves target class via `EntityRegistry::convertsTo('lead')` — no hardcoded `Deal::create()` call (grep check)
- [ ] `Deal::fromLead()` factory method exists and implements `ConvertibleTargetInterface`
- [ ] No `response()->json()` in any Inertia-consumed route handler (grep check)
- [ ] All pages have correct breadcrumbs + content wrapper (CLAUDE.md audit)
