Partners CRM
Partner-channel pipeline management module for tracking registered partners and partner-channel leads through a shared Kanban, List, and Calendar workspace.
Overview
The Partners CRM module gives the partner operations team a dedicated pipeline for two types of entities that share the same follow-up workflow:
- Registered partners — companies already enrolled on the platform (rows in the
partnerstable). They appear in the pipeline after joining so the team can track referral activity, manage their progress, and coordinate follow-ups. - Partner prospects — leads being recruited as future partners before they register (
prospection_userswithtype = 3,company_id IS NULL). They move through the same pipeline columns as registered partners until they sign up.
This module is separate from the main CRM (/crm) for three reasons:
- The KPI metric is total referral revenue rather than account value.
- The Kanban columns come from
catalog_follow_up_statuseswithsource = 'partner', notsource = 'company'. - Registered partners require dedicated backend queries that join the
partners,companies, anduserstables — they are notprospection_usersrows.
The module is marked Beta in the UI.
Key Concepts
| Concept | Description |
|---|---|
| Partner | A registered partner on the platform. Identified by a row in the partners table. Shown with a "Platform" badge in the CRM. |
| Partner Prospect | A recruitment lead not yet registered. Stored in prospection_users with type = 3 and company_id = NULL. Shown with a "New" badge. |
| Follow-up Category | A pipeline stage (catalog_follow_up_statuses with source = 'partner'). Drives Kanban columns. |
| Activity | A scheduled follow-up note (call, WhatsApp, email, meeting, SMS, other) with an optional date. Shown in the Calendar view. |
| Note | A timestamped comment attached to a partner or partner prospect that records a status change and/or interaction. |
| Contact | An additional person associated with a partner or partner prospect beyond the primary contact. Stored in extra_contacts. |
| Revenue KPI | Total referral revenue in USD contributed by partners in each pipeline column. Surfaced via GET /partner/kpis. |
crm-partners permission | Named permission required for every Partners CRM endpoint. Controls visibility of the module and all CRM actions. |
Architecture
System Overview
Dual API Design
The module manages two entity types through two different API paths. The side panel dispatches to the correct path based on the type field on each card.
| Operation | Partner (registered) | Partner Prospect |
|---|---|---|
| Kanban / List / KPIs / Calendar | GET /partner/grouped, /list, /kpis, /activities | Included via UNION in the same query |
| Detail | GET /partner/{id}/details | GET /prospects/{id}?source=partner |
| Notes | POST /partner/{id}/notes | POST /prospects/{id}/notes |
| Partner data (Details form) | PATCH /partner/{id}/data | PATCH /prospects/{id}/partner-data |
| Contacts | POST /partner/{id}/contacts | Via /prospects/{id}/contacts |
| Create | N/A (on platform) | POST /partner/prospect |
Backend Structure
backend/
├── routes/
│ ├── partner.routes.js # CRM route definitions (~lines 340–716)
│ └── prospects.routes.js # Shared prospect endpoints (partner-data patch, detail)
├── controllers/
│ ├── partner.controller.js # CRM handlers; delegates list/kanban/kpis to util
│ └── prospects.controller.js # Partner prospect detail, notes, partner-data
└── libraries/
├── partners.util.js # Kanban/list/KPI SQL (UNION queries), registerProspect
└── prospects.util.js # General CRM util; type=3 becomes source='partner'Frontend Structure
frontend/client/src/
├── views/partners/crm/
│ └── index.vue # Entry point: view switcher, provide injections
└── views/partners/components/crm/
├── Kamban.vue # Kanban board with infinite scroll
├── ListView.vue # DataTable view
├── CalendarView.vue # FullCalendar view
├── Sidepanel.vue # Offcanvas detail panel
├── AddProspect.modal.vue # Create partner prospect modal
├── Skeleton.vue # Kanban loading skeleton
├── table.defaults.js # Default columns and filters
└── Follow/
├── Note.form.vue # Follow-up note + schedule form
├── Details.form.vue # Partner metadata (URL, type, origin, rating)
└── Contact.form.vue # Contact CRUD formData Flow
Partner Prospect Creation
Kanban Drag → Note → Pipeline Update
Side Panel Detail Load
Database
Tables
| Table | Partners CRM Usage |
|---|---|
partners | Registered partners; follow_up_id tracks pipeline column; admin_id and support_id for assignments |
prospection_users | Partner prospects (type = 3, company_id = NULL) |
partner_follow_up_data | Extended CRM data for both entity types (model = 'partner' or 'prospect', model_id) — URL, type, origin, rating, social network |
follow_up_comments | Notes for registered partners (linked by company_id) |
prospect_follow_up_comments | Notes for partner prospects (linked by prospect_id) |
extra_contacts | Additional contacts for partners (company_id) and partner prospects (prospect_id) |
catalog_follow_up_statuses | Pipeline columns (source = 'partner'); drives both Kanban and note status picker |
catalog_partner_types | Partner type catalog (e.g., agency, reseller) |
catalog_partner_origins | Partner origin/acquisition channel catalog |
companies | Partner identity and referral data; joined to compute revenue KPIs |
users | Partner contact information (email, phone) |
partner_payments, partner_payments_details | Source for total referral revenue shown in KPI cards per column |
Entity Relationship
Key Fields
partner_follow_up_data
| Column | Type | Description |
|---|---|---|
model | ENUM | 'partner' for registered partners, 'prospect' for partner prospects |
model_id | INT | ID of the corresponding entity in partners or prospection_users |
url | VARCHAR | Partner website or landing page URL |
partner_type_id | INT (FK) | Reference to catalog_partner_types |
partner_origin_id | INT (FK) | Reference to catalog_partner_origins |
rating | TINYINT (1–5) | Internal quality rating |
locale_id | INT (FK) | Country/locale |
social_network | VARCHAR | Social media handle or link |
catalog_follow_up_statuses (partner pipeline columns)
| Column | Type | Description |
|---|---|---|
id | INT | Status ID |
name | VARCHAR | Column label shown in Kanban |
source | VARCHAR | 'partner' to scope to Partners CRM |
order | INT | Display order of Kanban columns |
Role-Based Access
| Permission | Effect |
|---|---|
crm-partners | Required for all Partners CRM endpoints (list, kanban, kpis, calendar, detail, notes, contacts, data patch, create prospect) |
update-partner | Enables the "Assign Administrator" modal in the List view |
view-partners | Grants access to the operational Partners admin table (/partners) — separate from the CRM pipeline |
crm-view-all | Expands calendar and list visibility; enables salesman filter on activities |
Key Decisions
| Decision | Reasoning | Alternative Considered |
|---|---|---|
| UNION query for Kanban/List/Calendar | Partners (from partners + companies + users) and partner prospects (from prospection_users) are two structurally different rows. A UNION in partners.util.js (createGeneralQuery) exposes them as a unified result set without duplicating views. | Separate Kanban columns per entity type (confusing UX, doubled API surface) |
| Drag does not commit immediately | Dropping a card opens the side panel with the target column pre-selected. The status change only persists when the user saves a note. This prevents accidental pipeline mutations and enforces a note-with-every-stage-change discipline. | PATCH on drop + optional note (risks silent stage changes with no audit trail) |
Separate from main CRM (/crm) | Partners use a dedicated status catalog (source = 'partner'), different KPIs (revenue vs account value), and registered partners require joins against the partners table rather than prospection_users. Merging would add conditional branching throughout the shared code. | Single CRM with entity type toggle (increased complexity in shared queries and permissions) |
putContact not exposed | The putContact controller method exists but no route is registered in partner.routes.js. Contact edits for partner prospects are routed through /prospects/{id}/contacts. This was intentional — registered partner contacts are managed via the company contact API, not the CRM-specific route. | Adding a /partner/{id}/contacts/{contactId} route (low ROI, inconsistent with how partner company contacts are managed) |
| No CSV import | Partners CRM has no bulk-import flow (unlike main CRM). Partner prospects are recruited individually, making bulk import a low-priority feature. | CSV import matching main CRM (deferred to future iteration) |
Dependencies
- Internal: Auth module (permissions, user context), Catalogs module (
GET /catalog/tours?type=partnerfor follow-up categories, partner types, origins, locales), Tasks module (TodoFormfor reminders linked by partner ID), Mailing module (email tab in side panel using modulespartners/partner_prospects) - External: FullCalendar (calendar view), vuedraggable (Kanban drag-and-drop), Vuelidate (form validation)
- Deep links: Tasks and mailing modules reference
/partners/crm?type=partner|prospect&id=for entity permalinks
Related Documentation
- API Endpoints — Complete endpoint reference for all Partners CRM routes
- UI Screens & Flows — Frontend screens, component tree, and user interaction flows
- User Guide — End-user instructions for Partners CRM
- CRM Module — Main CRM (prospects/clients); shares patterns for notes, contacts, and Kanban behavior
