Skip to content

specialorange/django-acquisitions

Repository files navigation

django-acquisitions

Tests codecov Documentation Status PyPI version

A minimal Django 4+ package for customer acquisition pipeline management. Track leads, manage contacts, automate outreach campaigns, and handle customer onboarding.

Features

  • Lead Management - Track potential customers through your acquisition pipeline
  • Contact Management - Multiple contacts per lead (decision makers, influencers, etc.)
  • Touchpoint Tracking - Record all outreach (calls, emails, SMS, meetings)
  • Outreach Campaigns - Automated multi-step sequences with email/SMS
  • Marketing Documents - Manage brochures, case studies, proposals
  • Seller Profiles - Internal profiles for automation preferences
  • Onboarding Handoff - Convert leads to customers with callbacks

Installation

pip install django-acquisitions

With optional dependencies:

# With Celery support for async tasks
pip install django-acquisitions[celery]

# With Twilio for SMS
pip install django-acquisitions[twilio]

# With Django REST Framework for API
pip install django-acquisitions[drf]

# All optional dependencies
pip install django-acquisitions[all]

Quick Start

  1. Add to INSTALLED_APPS:
INSTALLED_APPS = [
    # ...
    'acquisitions',
]
  1. Run migrations:
python manage.py migrate acquisitions
  1. Configure settings (optional):
# settings.py

# For multi-tenant (MST-style with django-organizations)
ACQUISITIONS_TENANT_MODEL = 'accounts.Account'

# For schema-per-tenant (MFT-style with django-tenants)
ACQUISITIONS_TENANT_MODEL = None  # Uses schema isolation

# Communication backends
ACQUISITIONS_EMAIL_BACKEND = 'acquisitions.backends.email.django_email.DjangoEmailBackend'
ACQUISITIONS_SMS_BACKEND = 'acquisitions.backends.sms.twilio.TwilioBackend'

# Twilio settings (if using Twilio backend)
TWILIO_ACCOUNT_SID = 'your-account-sid'
TWILIO_AUTH_TOKEN = 'your-auth-token'
TWILIO_FROM_NUMBER = '+1234567890'

# Celery settings
ACQUISITIONS_USE_CELERY = True

# Onboarding callback (called when lead converts to customer)
ACQUISITIONS_ONBOARDING_CALLBACK = 'myapp.services.create_customer_from_lead'

Models

Lead

The core model representing a potential customer:

from acquisitions.models import Lead

lead = Lead.objects.create(
    company_name='Acme Corp',
    email='contact@acme.com',
    status=Lead.Status.NEW,
    source=Lead.Source.WEBSITE,
)

LeadContact

Multiple contacts per lead:

from acquisitions.models import LeadContact

contact = LeadContact.objects.create(
    lead=lead,
    first_name='John',
    last_name='Doe',
    title='VP of Operations',
    role=LeadContact.Role.DECISION_MAKER,
    email='john@acme.com',
    is_primary=True,
)

Touchpoint

Track all interactions:

from acquisitions.models import Touchpoint
from django.utils import timezone

touchpoint = Touchpoint.objects.create(
    lead=lead,
    touchpoint_type=Touchpoint.TouchpointType.EMAIL,
    direction=Touchpoint.Direction.OUTBOUND,
    subject='Introduction to our services',
    occurred_at=timezone.now(),
    performed_by_id=request.user.id,
)

OutreachCampaign

Automated sequences:

from acquisitions.models import OutreachCampaign, CampaignStep

campaign = OutreachCampaign.objects.create(
    name='New Lead Nurture',
    status=OutreachCampaign.Status.ACTIVE,
)

CampaignStep.objects.create(
    campaign=campaign,
    step_order=1,
    step_type=CampaignStep.StepType.EMAIL,
    delay_days=0,
    subject_template='Welcome to {{ company_name }}!',
    body_template='Hi {{ contact.first_name }}, ...',
)

API Endpoints

When using with Django REST Framework:

# urls.py
from django.urls import path, include

urlpatterns = [
    path('api/acquisitions/', include('acquisitions.api.urls')),
]

Available endpoints:

  • GET/POST /api/acquisitions/leads/ - List/create leads
  • GET/PUT/DELETE /api/acquisitions/leads/{uuid}/ - Lead detail
  • POST /api/acquisitions/leads/{uuid}/convert/ - Convert to customer
  • POST /api/acquisitions/leads/{uuid}/enroll_campaign/ - Enroll in campaign
  • GET/POST /api/acquisitions/leads/{uuid}/contacts/ - Lead contacts
  • GET/POST /api/acquisitions/leads/{uuid}/touchpoints/ - Lead touchpoints
  • GET/POST /api/acquisitions/campaigns/ - Outreach campaigns
  • GET/POST /api/acquisitions/documents/ - Marketing documents

Multi-Tenancy Support

django-acquisitions supports multi-tenancy out of the box with two approaches:

Schema-per-tenant (django-tenants)

For django-tenants with PostgreSQL schema isolation:

# settings.py
SHARED_APPS = [
    'django_tenants',
    'django.contrib.contenttypes',
    'django.contrib.auth',
    # ... other shared apps
]

TENANT_APPS = [
    'django.contrib.sessions',
    'django.contrib.messages',
    'acquisitions',  # Include in tenant apps
    # ... other tenant-specific apps
]

INSTALLED_APPS = SHARED_APPS + [app for app in TENANT_APPS if app not in SHARED_APPS]

# Leave ACQUISITIONS_TENANT_MODEL unset (defaults to None)
# Schema isolation handles tenant separation automatically

Run migrations for each tenant as per django-tenants documentation.

FK-based tenancy (django-organizations style)

For shared-schema multi-tenancy with a foreign key to your tenant/account model:

# settings.py
ACQUISITIONS_TENANT_MODEL = 'accounts.Account'  # Your tenant model
ACQUISITIONS_TENANT_FIELD_NAME = 'account'  # FK field name (default)

Then use the TenantFilterMixin in your views:

from acquisitions.mixins import TenantFilterMixin
from acquisitions.models import ProspectiveClient

class ProspectiveClientListView(TenantFilterMixin, ListView):
    model = ProspectiveClient

    def get_queryset(self):
        return self.filter_by_tenant(super().get_queryset())

The mixin automatically detects tenants from request.tenant (django-tenants) or request.user.account.

Celery Tasks

For automated outreach, add to your Celery beat schedule:

# celery.py or settings.py
CELERY_BEAT_SCHEDULE = {
    'process_acquisitions_outreach': {
        'task': 'acquisitions.tasks.outreach_tasks.process_scheduled_outreach',
        'schedule': crontab(minute='*/5'),  # Every 5 minutes
    },
}

Custom Models

Use abstract models to add custom fields:

# myapp/models.py
from acquisitions.abstract_models import AbstractLead
from myapp.models import Account

class Lead(AbstractLead):
    account = models.ForeignKey(Account, on_delete=models.CASCADE)
    custom_field = models.CharField(max_length=100)

    class Meta(AbstractLead.Meta):
        abstract = False

Then configure:

ACQUISITIONS_LEAD_MODEL = 'myapp.Lead'

License

BSD-3-Clause

About

Django 4+ customer acquisition pipeline

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors