Skip to content

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 partners table). 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_users with type = 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:

  1. The KPI metric is total referral revenue rather than account value.
  2. The Kanban columns come from catalog_follow_up_statuses with source = 'partner', not source = 'company'.
  3. Registered partners require dedicated backend queries that join the partners, companies, and users tables — they are not prospection_users rows.

The module is marked Beta in the UI.

Key Concepts

ConceptDescription
PartnerA registered partner on the platform. Identified by a row in the partners table. Shown with a "Platform" badge in the CRM.
Partner ProspectA recruitment lead not yet registered. Stored in prospection_users with type = 3 and company_id = NULL. Shown with a "New" badge.
Follow-up CategoryA pipeline stage (catalog_follow_up_statuses with source = 'partner'). Drives Kanban columns.
ActivityA scheduled follow-up note (call, WhatsApp, email, meeting, SMS, other) with an optional date. Shown in the Calendar view.
NoteA timestamped comment attached to a partner or partner prospect that records a status change and/or interaction.
ContactAn additional person associated with a partner or partner prospect beyond the primary contact. Stored in extra_contacts.
Revenue KPITotal referral revenue in USD contributed by partners in each pipeline column. Surfaced via GET /partner/kpis.
crm-partners permissionNamed 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.

OperationPartner (registered)Partner Prospect
Kanban / List / KPIs / CalendarGET /partner/grouped, /list, /kpis, /activitiesIncluded via UNION in the same query
DetailGET /partner/{id}/detailsGET /prospects/{id}?source=partner
NotesPOST /partner/{id}/notesPOST /prospects/{id}/notes
Partner data (Details form)PATCH /partner/{id}/dataPATCH /prospects/{id}/partner-data
ContactsPOST /partner/{id}/contactsVia /prospects/{id}/contacts
CreateN/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 form

Data Flow

Partner Prospect Creation

Kanban Drag → Note → Pipeline Update

Side Panel Detail Load

Database

Tables

TablePartners CRM Usage
partnersRegistered partners; follow_up_id tracks pipeline column; admin_id and support_id for assignments
prospection_usersPartner prospects (type = 3, company_id = NULL)
partner_follow_up_dataExtended CRM data for both entity types (model = 'partner' or 'prospect', model_id) — URL, type, origin, rating, social network
follow_up_commentsNotes for registered partners (linked by company_id)
prospect_follow_up_commentsNotes for partner prospects (linked by prospect_id)
extra_contactsAdditional contacts for partners (company_id) and partner prospects (prospect_id)
catalog_follow_up_statusesPipeline columns (source = 'partner'); drives both Kanban and note status picker
catalog_partner_typesPartner type catalog (e.g., agency, reseller)
catalog_partner_originsPartner origin/acquisition channel catalog
companiesPartner identity and referral data; joined to compute revenue KPIs
usersPartner contact information (email, phone)
partner_payments, partner_payments_detailsSource for total referral revenue shown in KPI cards per column

Entity Relationship

Key Fields

partner_follow_up_data

ColumnTypeDescription
modelENUM'partner' for registered partners, 'prospect' for partner prospects
model_idINTID of the corresponding entity in partners or prospection_users
urlVARCHARPartner website or landing page URL
partner_type_idINT (FK)Reference to catalog_partner_types
partner_origin_idINT (FK)Reference to catalog_partner_origins
ratingTINYINT (1–5)Internal quality rating
locale_idINT (FK)Country/locale
social_networkVARCHARSocial media handle or link

catalog_follow_up_statuses (partner pipeline columns)

ColumnTypeDescription
idINTStatus ID
nameVARCHARColumn label shown in Kanban
sourceVARCHAR'partner' to scope to Partners CRM
orderINTDisplay order of Kanban columns

Role-Based Access

PermissionEffect
crm-partnersRequired for all Partners CRM endpoints (list, kanban, kpis, calendar, detail, notes, contacts, data patch, create prospect)
update-partnerEnables the "Assign Administrator" modal in the List view
view-partnersGrants access to the operational Partners admin table (/partners) — separate from the CRM pipeline
crm-view-allExpands calendar and list visibility; enables salesman filter on activities

Key Decisions

DecisionReasoningAlternative Considered
UNION query for Kanban/List/CalendarPartners (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 immediatelyDropping 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 exposedThe 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 importPartners 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=partner for follow-up categories, partner types, origins, locales), Tasks module (TodoForm for reminders linked by partner ID), Mailing module (email tab in side panel using modules partners / 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
  • 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

Envia Admin