Skip to content

Commit

Permalink
feat: Add subscription to Hubspot tracker (#3676)
Browse files Browse the repository at this point in the history
  • Loading branch information
zachaysan authored Mar 27, 2024
1 parent b872a6c commit 44ed1bf
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 6 deletions.
29 changes: 26 additions & 3 deletions api/integrations/lead_tracking/hubspot/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

import hubspot
from django.conf import settings
from hubspot.crm.companies import SimplePublicObjectInputForCreate
from hubspot.crm.companies import (
SimplePublicObjectInput,
SimplePublicObjectInputForCreate,
)
from hubspot.crm.contacts import BatchReadInputSimplePublicObjectId

from users.models import FFAdminUser
Expand Down Expand Up @@ -64,8 +67,15 @@ def create_contact(self, user: FFAdminUser, hubspot_company_id: str) -> dict:
)
return response.to_dict()

def create_company(self, name: str, organisation_id: int) -> dict:
properties = {"name": name, "orgid": str(organisation_id)}
def create_company(
self, name: str, active_subscription: str, organisation_id: int
) -> dict:
properties = {
"name": name,
"active_subscription": active_subscription,
"orgid": str(organisation_id),
}

simple_public_object_input_for_create = SimplePublicObjectInputForCreate(
properties=properties,
)
Expand All @@ -75,3 +85,16 @@ def create_company(self, name: str, organisation_id: int) -> dict:
)

return response.to_dict()

def update_company(self, active_subscription: str, hubspot_company_id: str) -> dict:
properties = {
"active_subscription": active_subscription,
}
simple_public_object_input = SimplePublicObjectInput(properties=properties)

response = self.client.crm.companies.basic_api.update(
company_id=hubspot_company_id,
simple_public_object_input=simple_public_object_input,
)

return response.to_dict()
28 changes: 27 additions & 1 deletion api/integrations/lead_tracking/hubspot/lead_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
from django.conf import settings

from integrations.lead_tracking.lead_tracking import LeadTracker
from organisations.models import HubspotOrganisation, Organisation
from organisations.models import (
HubspotOrganisation,
Organisation,
Subscription,
)
from users.models import FFAdminUser

from .client import HubspotClient
Expand Down Expand Up @@ -67,15 +71,37 @@ def get_or_create_organisation_hubspot_id(self, organisation: Organisation) -> s

response = self.client.create_company(
name=organisation.name,
active_subscription=organisation.subscription.plan,
organisation_id=organisation.id,
)

# Store the organisation data in the database since we are
# unable to look them up via a unique identifier.
HubspotOrganisation.objects.create(
organisation=organisation,
hubspot_id=response["id"],
)

return response["id"]

def update_company_active_subscription(
self, subscription: Subscription
) -> dict | None:
if not subscription.plan:
return

organisation = subscription.organisation

# Check if we're missing the associated hubspot id.
if not getattr(organisation, "hubspot_organisation", None):
return

response = self.client.update_company(
active_subscription=subscription.plan,
hubspot_company_id=organisation.hubspot_organisation.hubspot_id,
)

return response

def _get_client(self) -> HubspotClient:
return HubspotClient()
13 changes: 13 additions & 0 deletions api/integrations/lead_tracking/hubspot/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,16 @@ def track_hubspot_lead(user_id: int, organisation_id: int) -> None:

hubspot_lead_tracker = HubspotLeadTracker()
hubspot_lead_tracker.create_lead(user=user, organisation=organisation)


@register_task_handler()
def update_hubspot_active_subscription(subscription_id: int) -> None:
assert settings.ENABLE_HUBSPOT_LEAD_TRACKING

from organisations.models import Subscription

from .lead_tracker import HubspotLeadTracker

subscription = Subscription.objects.get(id=subscription_id)
hubspot_lead_tracker = HubspotLeadTracker()
hubspot_lead_tracker.update_company_active_subscription(subscription)
12 changes: 11 additions & 1 deletion api/organisations/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
from simple_history.models import HistoricalRecords

from app.utils import is_enterprise, is_saas
from integrations.lead_tracking.hubspot.tasks import track_hubspot_lead
from integrations.lead_tracking.hubspot.tasks import (
track_hubspot_lead,
update_hubspot_active_subscription,
)
from organisations.chargebee import (
get_customer_id_from_subscription_id,
get_max_api_calls_for_plan,
Expand Down Expand Up @@ -251,6 +254,13 @@ def can_auto_upgrade_seats(self) -> bool:
def is_free_plan(self) -> bool:
return self.plan == FREE_PLAN_ID

@hook(AFTER_SAVE, when="plan", has_changed=True)
def update_hubspot_active_subscription(self):
if not settings.ENABLE_HUBSPOT_LEAD_TRACKING:
return

update_hubspot_active_subscription.delay(args=(self.id,))

@hook(AFTER_SAVE, when="cancellation_date", has_changed=True)
@hook(AFTER_SAVE, when="subscription_id", has_changed=True)
def update_mailer_lite_subscribers(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
Organisation,
OrganisationRole,
)
from task_processor.task_run_method import TaskRunMethod
from users.models import FFAdminUser


Expand Down Expand Up @@ -89,7 +90,9 @@ def test_hubspot_with_new_contact_and_new_organisation(
organisation.refresh_from_db()
assert organisation.hubspot_organisation.hubspot_id == future_hubspot_id
mock_create_company.assert_called_once_with(
name=organisation.name, organisation_id=organisation.id
name=organisation.name,
active_subscription="free",
organisation_id=organisation.id,
)
mock_create_contact.assert_called_once_with(user, future_hubspot_id)
mock_get_contact.assert_called_once_with(user)
Expand Down Expand Up @@ -214,3 +217,65 @@ def test_hubspot_with_existing_contact_and_new_organisation(
# further hubspot resources.
mock_create_company.assert_not_called()
mock_create_contact.assert_not_called()


def test_update_company_active_subscription(
organisation: Organisation,
settings: SettingsWrapper,
mocker: MockerFixture,
) -> None:
settings.ENABLE_HUBSPOT_LEAD_TRACKING = True
settings.TASK_RUN_METHOD = TaskRunMethod.SYNCHRONOUSLY

mock_update_company = mocker.patch(
"integrations.lead_tracking.hubspot.client.HubspotClient.update_company"
)
hubspot_id = "12345"
# Create an existing hubspot organisation to mimic a previous
# successful API call.
HubspotOrganisation.objects.create(
organisation=organisation,
hubspot_id=hubspot_id,
)

assert organisation.subscription.plan == "free"

# When
organisation.subscription.plan = "scale-up-v2"
organisation.subscription.save()

# Then
mock_update_company.assert_called_once_with(
active_subscription=organisation.subscription.plan,
hubspot_company_id=hubspot_id,
)


def test_update_company_active_subscription_not_called(
organisation: Organisation,
settings: SettingsWrapper,
mocker: MockerFixture,
) -> None:
# Set to False to ensure update doesn't happen.
settings.ENABLE_HUBSPOT_LEAD_TRACKING = False
settings.TASK_RUN_METHOD = TaskRunMethod.SYNCHRONOUSLY

mock_update_company = mocker.patch(
"integrations.lead_tracking.hubspot.client.HubspotClient.update_company"
)
hubspot_id = "12345"
# Create an existing hubspot organisation to mimic a previous
# successful API call.
HubspotOrganisation.objects.create(
organisation=organisation,
hubspot_id=hubspot_id,
)

assert organisation.subscription.plan == "free"

# When
organisation.subscription.plan = "scale-up-v2"
organisation.subscription.save()

# Then
mock_update_company.assert_not_called()

0 comments on commit 44ed1bf

Please sign in to comment.