# CRM Core Foundation Design

Date: 2026-05-21

Status: proposed bounded Phase 4 design based on approved scope.

## Goal

Build a usable CRM Core for ECOLE ECOIN that operations staff can use immediately for lead management, assignments, stages, tasks, and follow-up tracking, while integrating only with currently safe lifecycle events.

This phase does not build marketing automation. It builds the operational sales workspace and prepares the system for broader event-driven CRM wiring later.

## Scope Decision

Phase 4 CRM Core uses the same philosophy as the EventBus phase:

- Safety over coverage.
- Manual usability first.
- Event-driven integration only from safe, already wired events.
- No broad rewrites of enrollment, finance, attendance, or legacy WhatsApp/payment flows.

This phase will implement:

- CRM module under `app/Domain/CRM/`
- CRM leads
- pipelines and stage tracking
- manual lead creation
- assignment and reassignment
- follow-up tasks
- CRM activities and stage history
- basic CRM dashboard
- admin CRM lead detail
- event listeners for:
  - `WhatsAppLeadCreated`
  - `B2bLeadConverted`
  - `WhatsAppReplySent` as CRM activity only

This phase will not implement:

- Marketing automation campaigns
- auto-assignment
- CRM-owned payment or enrollment mutations
- CRM-owned WhatsApp state changes
- lifecycle rewiring of legacy enrollment/payment/no-show flows

## CRM Philosophy

CRM is an operational pipeline system, not just a contact database.

It must help staff:

- receive leads
- qualify them
- assign ownership
- plan next actions
- track stage progression
- keep a readable timeline
- recover stalled opportunities manually

The CRM should remain operational even when only some event integrations are active. That is why manual lead entry and manual task handling are part of Phase 4 core, not optional extras.

## Module Ownership

New module:

```text
app/Domain/CRM/
```

CRM owns:

- `crm_leads`
- `crm_tasks`
- `crm_activities`
- `crm_stage_history`
- pipeline definitions and stage transitions
- assignment metadata
- follow-up reminders metadata

CRM does not own:

- enrollments
- payments
- attendance
- WhatsApp conversations
- B2B lead workflow
- organizations

CRM may link to those entities and react to their lifecycle facts, but it must not become the owner of their state.

## Pipelines

Phase 4 defines three canonical pipelines in code, not in a dynamic builder yet.

### Student Enrollment Pipeline

Stages:

- `new`
- `interested`
- `qualified`
- `course_recommended`
- `enrollment_started`
- `deposit_pending`
- `confirmed`
- `attended`
- `completed`
- `lost`

### Corporate B2B Pipeline

Stages:

- `new`
- `contacted`
- `qualified`
- `proposal_sent`
- `negotiation`
- `agreement_pending`
- `active`
- `won`
- `lost`

### Webinar/Event Pipeline

Stages:

- `registered`
- `reminded`
- `attended`
- `followup_pending`
- `converted`
- `lost`

These pipelines are represented by CRM-owned configuration/constants so validation, UI badges, and stage movement all use one source of truth.

## Data Model

### crm_leads

- `id`
- `pipeline_type`
- `stage`
- `source`
- `assigned_to_user_id` nullable
- `linked_user_id` nullable
- `linked_enrollment_id` nullable
- `linked_b2b_lead_id` nullable
- `linked_organization_id` nullable
- `linked_event_registration_id` nullable
- `linked_whatsapp_conversation_id` nullable
- `full_name` nullable
- `phone_e164` nullable
- `email` nullable
- `score` nullable
- `lost_reason` nullable
- `converted_at` nullable
- `last_activity_at` nullable
- `next_followup_at` nullable
- `metadata` json/jsonb
- timestamps

Rules:

- Each CRM lead belongs to exactly one pipeline.
- Each CRM lead has one current stage.
- Linked entities are optional because manual leads may exist before any enrollment or B2B record exists.

### crm_tasks

- `id`
- `crm_lead_id`
- `assigned_to_user_id`
- `type`
- `title`
- `description` nullable
- `due_at`
- `completed_at` nullable
- `status`
- timestamps

Task types:

- `call`
- `whatsapp`
- `followup`
- `payment_reminder`
- `webinar_followup`
- `no_show_recovery`

Task statuses:

- `open`
- `completed`
- `snoozed`
- `cancelled`

### crm_activities

- `id`
- `crm_lead_id`
- `actor_user_id` nullable
- `type`
- `body` nullable
- `meta` json/jsonb
- timestamps

Activities are append-only operational facts for the CRM timeline.

### crm_stage_history

- `id`
- `crm_lead_id`
- `from_stage` nullable
- `to_stage`
- `actor_user_id` nullable
- `reason` nullable
- `created_at`

## Lead Creation Rules

### Manual lead entry

Admin/support can create a CRM lead manually from `/admin/crm/leads/create`.

Required minimum:

- pipeline
- stage
- source
- full name or phone or email

This keeps CRM usable even before all event integrations are wired.

### Safe event integrations

#### WhatsAppLeadCreated

When a safe `WhatsAppLeadCreated` event is emitted:

- create CRM lead if one does not already exist for that linked WhatsApp conversation or linked B2B lead
- default pipeline:
  - `corporate_b2b` if the lead is tied to a B2B flow
  - otherwise `student_enrollment` can remain planned for later integrations
- create CRM activity noting that the lead originated from WhatsApp

For this phase, because the currently safe WhatsApp lead Action creates B2B leads from conversation, the concrete listener will create a Corporate/B2B CRM lead linked to:

- `linked_b2b_lead_id`
- `linked_whatsapp_conversation_id`

#### B2bLeadConverted

When `B2bLeadConverted` fires:

- locate CRM lead by `linked_b2b_lead_id`
- update `linked_organization_id`
- move stage if still within a compatible corporate stage path
  - recommended default: `qualified` stays `qualified` unless no stage existed, otherwise add activity only
- append CRM activity: lead converted to organization

This phase favors activity logging over aggressive stage automation to avoid surprising operators.

#### WhatsAppReplySent

When `WhatsAppReplySent` fires:

- if a CRM lead is linked to the same WhatsApp conversation, append CRM activity
- do not mutate CRM stage automatically in this phase

## Planned Integrations

These remain documented but not wired now:

- `EnrollmentCreated`
- `EnrollmentConfirmed`
- `DepositPaymentApproved`
- `AttendanceMarked`
- `StudentNoShowDetected`
- `WebinarAttended`

They can be wired later only when their owning workflows have safe Action or service seams and focused regression tests.

## Assignment Rules

Phase 4 supports manual assignment only.

Behaviors:

- lead can be assigned to a user
- reassignment updates `assigned_to_user_id`
- reassignment writes CRM activity
- reassignment updates `last_activity_at`

UI views:

- all leads
- unassigned queue
- my leads

Permission behavior:

- managers/admin with broad CRM visibility can see all leads
- support users without broad visibility should see assigned leads by default

## Stage Rules

CRM stages are CRM-owned and do not mutate external workflows.

Allowed in Phase 4:

- manual stage change by authorized CRM users
- stage history entry on every change
- optional `lost_reason` when moved to `lost`
- `converted_at` may be set when the business meaning of the stage indicates conversion for that pipeline

CRM stage changes are internal CRM tracking only. They must not confirm enrollment, approve payments, or change WhatsApp/B2B ownership state.

## Follow-Up Tasks

Tasks are CRM-owned and fully operational in this phase.

Users can:

- create task
- complete task
- snooze task
- reschedule task

Dashboard and lead detail expose:

- overdue tasks
- due today
- my open tasks

Snooze and reschedule both preserve task history through CRM activity entries rather than mutating silently.

## CRM Activity Timeline

The lead detail timeline should merge CRM-owned operational items:

- manual notes
- stage changes
- assignment changes
- task created/completed/snoozed/rescheduled
- safe event-driven items from WhatsApp or B2B conversion

This timeline is CRM’s operational record. It may reference lifecycle facts from other modules, but CRM itself stays append-only and non-owning relative to those external workflows.

## Dashboard

Route:

```text
/admin/crm
```

Widgets for Phase 4:

- total leads
- leads by pipeline
- leads by stage
- unassigned leads
- my leads
- overdue follow-ups
- my open tasks

Keep the dashboard query-driven and intentionally simple. It should be fast and operational, not an analytics warehouse.

## Lead Detail

Route:

```text
/admin/crm/leads/{lead}
```

Contains:

- identity panel
- pipeline and current stage
- assignment panel
- linked entities panel
- tasks list
- stage history
- activity timeline
- note entry

Optional quick links:

- linked WhatsApp conversation
- linked B2B lead
- linked organization

But the CRM page should not embed another module’s full mutation workflow directly.

## Security

New permissions:

- `crm.view`
- `crm.manage`
- `crm.assign`
- `crm.tasks.manage`
- `crm.pipeline.manage`

Rules:

- admin/super_admin can manage all
- support may get CRM access according to granted permissions
- broad visibility versus assigned-only visibility should be enforced in CRM queries or policy helpers

Policy requirements:

- CRM lead viewing
- CRM lead updates
- assignment updates
- task management

## Architecture Rules

CRM implementation must follow:

- CRM reacts to lifecycle events.
- CRM creates its own tasks, activities, and stage history.
- CRM never approves payments.
- CRM never mutates enrollment state directly.
- CRM never mutates attendance state directly.
- CRM never mutates WhatsApp conversation state directly.

Listeners should remain non-mutating toward other modules. If future CRM behavior needs an external workflow mutation, it must call that module’s Action through a documented integration point.

## UI Shape

Keep UI implementation pragmatic:

- simple responsive dashboard cards
- list filters
- lead detail with tabs or stacked sections
- tasks and activity timeline readable on mobile and desktop

Avoid building an over-designed sales suite in Phase 4. This is an operational admin surface first.

## Testing Strategy

Feature tests should cover:

1. manual CRM lead creation works
2. `WhatsAppLeadCreated` creates CRM lead
3. `B2bLeadConverted` appends CRM activity or updates linked organization safely
4. `WhatsAppReplySent` appends CRM activity only
5. reassignment is logged
6. overdue follow-ups appear
7. unauthorized users are blocked
8. stage changes create stage history

Architecture checks should also be extended so CRM listeners are prevented from directly mutating Enrollment or Payment state.

## Proposed Approach

Recommended approach: **bounded operational CRM module with static pipelines and event listeners only for safe events**.

Why this is the best fit now:

- It makes CRM usable immediately through manual lead entry and tasks.
- It respects current domain ownership boundaries.
- It leverages Phase 3 event foundation without pretending the entire platform is already safe for broad automation.
- It avoids binding CRM to unstable legacy workflows too early.

Alternatives considered and rejected for this phase:

1. Fully manual CRM with no event integrations.
   - Safer short-term, but wastes the new EventBus foundation and duplicates lead capture work.
2. Broad CRM integration across enrollment/payment/attendance now.
   - Higher operational value, but violates the approved safety posture and would pull us into legacy rewrites.

## Acceptance Criteria

- CRM Dashboard works.
- Manual lead creation works.
- Lead detail, stages, assignment, tasks, and timeline work.
- `WhatsAppLeadCreated` creates CRM lead.
- `B2bLeadConverted` updates CRM activity and linked context safely.
- `WhatsAppReplySent` appears as CRM activity when linked.
- Remaining lifecycle integrations are documented as planned only.
- CRM does not directly mutate enrollments, payments, attendance, or WhatsApp state.

