The campaigns module tracks members, responses, and ROI metrics with signals updating stats when leads and opportunities change.
This post is Part 20 of 28 in the Horilla CRM Technical Blog series.
What are marketing campaigns in Horilla CRM?
Marketing campaigns in Horilla CRM enable you to:
- Campaign and CampaignMember models
- Automatic metric recalculation on related changes
- Dashboard charts for campaign performance
- Money fields with currency cascade
Opportunity attribution
An Opportunity has primary_campaign_source for its principal marketing attribution. This relationship creates the bridge that lets opportunity signals refresh campaign metrics and lets reports connect pipeline revenue to marketing work. It is deliberately primary: if several touches influenced an opportunity, this field identifies the CRM’s selected primary source rather than attempting to implement a multi-touch attribution engine in a single foreign key.
opportunity.primary_campaign_source = campaign
opportunity.save(update_fields=["primary_campaign_source"])
Set attribution using valid campaign records from the same company and preserve it through imports and API serializers. Replacing the source can affect both the old and new campaign’s metrics, so use a normal save path that allows the campaign signal logic to observe the change. Direct SQL or QuerySet.update() skips model signals and can leave cached campaign values incorrect.
Signal-safe operational patterns
The updater is invoked from model lifecycle signals, so receivers must avoid creating an infinite loop. Updating denormalized fields should be scoped to the campaign and should not perform a recursive member/opportunity/lead save. It should also account for old relationships when a member or opportunity moves between campaigns; otherwise, only the new campaign gets refreshed.
For batch administration, identify the impacted campaign IDs and invoke the application’s supported refresh logic once per campaign after the batch. This both controls query volume and makes the eventual values deterministic. Templates, dashboards, and API response code should read the stored metric fields. They should not count related rows on every render, which would erase the performance value of denormalization and could disagree with the application’s definitions of qualified responses or attributable revenue.
Extending campaign analytics
Add a new metric by defining its precise source rows, lifecycle events, and company boundary first. Then extend the central metric computation and make the relevant member, lead, or opportunity events trigger it. Avoid placing calculation fragments in each view. A central definition gives list screens, exports, and scheduled reporting one result and makes a full rebuild possible when historical data is imported or repaired.
Benefits of Campaigns in Horilla CRM
- Tie marketing spend to pipeline outcomes
- Live metrics without manual spreadsheet updates
- Member status tracking across channels
Campaigns close the loop between marketing and sales. Register models and rely on built-in signal handlers for metric freshness.
Continue the series
Previous: Part 19 — Opportunities and Pipeline Stages in Horilla CRM
Next: Part 21 — Sales Forecasting in Horilla CRM: Types, Conditions, and ECharts
More posts are on the Horilla Blogs; share feedback on GitHub.