# django-crmify
A django app for quickly integrating a full-featured 3rd party CRM into an application

## Purpose
To begin, the purpose of crmify is **NOT** to be a full featured CRM. It's utility is as a bridge between a Django application
and a 3rd party CRM. Some sample use cases below.
 
*Basic Lead Management*
1. User comes to site and registers for account, this triggers a crmify event to create a lead in Insightly
2. In Insightly, we have a pipeline set up to automatically email new leads
3. When the user clicks a link in the email, it triggers a change in the status of the User in the backend.
4. This status change is automatically synced by django-crmify to Insightly, which updates the Lead from a status of `NotContacted` to `Contacted`.

*Basic Sales Funnel*
1. User comes to site and registers, triggers crmify to create a lead
2. User makes a purchase, triggering a Conversion event which syncs to Insightly and updates the Lead from a status of `NotContacted` to `Converted`

## Installation
```
pip install django-crmify
```

## Quick Start
To start using django-crmify, you need to setup some basic configuration parameters. While a number of the parameters will likely need tweaking, the most important settings to get started are: `BACKEND`, `LEAD_MODEL`, and `LEAD_MODEL_FIELDMAPPER`(each of these explained in more detail in the settings [section](#settings) below). Additionally, you will likely need to set the authentication parameters of the CRM backend via environment variables. Here is an example configuration for an integration with the Insightly CRM:`

```python
# Backend auth for insightly is API key based, therefore the backend will be looking for an environment variable CRMIFY_BACKEND_AUTH_API_KEY for auth
CRMIFY = {
  'BACKEND': 'crmify.backends.insightly.InsightlyBackend',
  'LEAD_MODEL': 'myapp.models.UserProfile',
  'LEAD_MODEL_FIELDMAPPER': 'myapp.mappers.UserProfileFieldMapper'
}
```

After setting the `LEAD_MODEL`, it is necessary to run `makemigrations` and then `migrate` to finalize setup.

---

The next step is setting the value of the `LEAD_MODEL_FIELDMAPPER` to a dot-separated path of a `LeadModelFieldMapper` subclass, a simple class responsible for mapping fields from your Lead model onto the internal `crmify.models.Lead` model. For example, the default mapper looks as follows:

```python
class DjangoUserFieldMapper(LeadModelFieldMapper):
    field_mapping = {
        'first_name': 'first_name',
        'last_name': 'last_name',
        'email': 'email',
    }
    fallbacks = {
        'username': 'email'
    }
```

To break this down a bit, the `field_mapping` consists of our model's fields on the left and the internal crmify Lead models' fields on the right. The `fallbacks` field defines the mapping to use in the case that the primary `field_mapping` has no value for a specified Lead field. 

## Settings
| Setting                | Default                                       | Description                               |
| ---------------------- | --------------------------------------------- | ----------------------------------------- |
| BACKEND                | 'crmify.backends.insightly.InsightlyBackend'  | dot separated path to a CRM backend       |
| BACKEND_AUTH           | {}                                            | authentication parameters for the CRM backend. Stored in env vars CRMIFY_BACKEND_AUTH_API_KEY, CRMIFY_BACKEND_AUTH_USERNAME, and CRMIFY_BACKEND_AUTH_PASSWORD |     
| LEAD_MODEL             | 'django.contrib.auth.models.User'             | dot separated path to your applications model you'd like treated as a CRM lead |
| LEAD_MODEL_FIELDMAPPER | 'crmify.mappers.DjangoUserFieldMapper'        | dot separated path to a class defining a mapping between LEAD_MODEL and crmify Lead fields |
| LEAD_STATUS_MODEL      | None                                          | dot separated path to a class subclassing the `crmify.mixins.LeadStatusMixin`. In order to track lead status, this class must implement the `lead_status` method. |
| LEAD_NEW_STATUS        | 'NotContacted'                                | the status to use for new leads           |
| LEAD_CONVERTED_STATUS  | 'Converted'                                   | the status to use for converted leads     |
| LEAD_DEAD_STATUS       | 'Disqualified'                                | the status to use for dead leads          |

## Customization
A CRM backend is simply a class implementing two methods:

```
def sync_lead(self, lead):
   """ sync the given lead to the CRM 
   :param lead: `Lead` object to sync to the CRM
   :return: `str` the ID of the lead in the CRM
   """
   pass

def delete_lead(self, lead_id):
    """ delete the lead with the given ID from the CRM 
    :param lead_id: `str` the ID of the lead in the external CRM
    :return: `bool` True if the lead was deleted, False otherwise
    """
    pass
```

After creating this class, you need only point the `BACKEND` setting to the dot separated path to your CRM backend.
