Class: Marketing::CampaignInfluence
- Inherits:
-
Object
- Object
- Marketing::CampaignInfluence
- Defined in:
- app/queries/marketing/campaign_influence.rb
Overview
Which campaigns TOUCHED the deals we invoiced in a period — answered by a
join, without rewriting a single source_id.
This is the read that lets Campaign#synchronize_source's mutation be
retired. That method records influence by overwriting the operational
source on a customer's opportunities, orders and invoices, which destroys
acquisition to record a touch. Everything it approximates is already stored
(see the touch table below); it only ever needed to be joined, not written.
Sourced vs influenced, and why they are different columns. Sourced means
the campaign's source IS the deal's acquisition source — first touch, the
original_source_id stamped once at creation and never rewritten.
Influenced means a touch landed inside the window but the deal was acquired
elsewhere. Both are legitimate; collapsing them into one number is how the
July 2026 attribution mess started, with 20k Costco orders reading as
"Trade Show > 2011 Toronto".
The influence column does not sum. Deliberately many-to-many: a deal
touched by three campaigns appears under all three at full value, because
first-touch acquisition plus influence lists is the model — not U-shaped
or data-driven fractional splitting. Adding the column across campaigns
double-counts revenue. Result#revenue_is_summable? says so in the data so
a report surface (or Sunny) cannot quietly total it.
Window. 90 days by default, and deliberately NOT
Campaign#auto_assign_window_days (30). That window governs tagging — how
long a campaign may claim the operational source of new records. This one
governs reporting reach, and the common B2B default for campaign influence
is 90 days (Salesforce Campaign Influence, HubSpot). Tying reporting to the
tagging window would make a campaign's influence shrink because someone
tightened its auto-assign rule.
Revenue is invoice revenue in CONSOLIDATED_CURRENCY — invoices are the
final number for sales data; orders are for analyzing state.
Defined Under Namespace
Constant Summary collapse
- DEFAULT_WINDOW_DAYS =
Reporting reach, in days before the invoice. Industry default for B2B
campaign influence; independent of any campaign's tagging window. 90- NET_NEW_DAYS =
"Net new" means the customer was acquired this recently before the invoice.
A SEPARATE 90 from the influence window on purpose: narrowing the influence
window must not silently redefine what counts as a new customer. Matches
the existing rule in Report::SourcesReport::Result#net_new_orders_count.
Lives in the view, which is why nothing here interpolates it. 90- MAX_WINDOW_DAYS =
The furthest back the view's join reaches. A window beyond this can't be
answered from it, so asking is an error rather than a quiet short report. 365- ALWAYS_ON_COVERAGE =
Share of period revenue past which a campaign's reach stops telling you
anything comparative. Q3 2026's "Nurturing Campaign" sat at 38% — 520 of
1,035 invoices — on 7,612 standing audience memberships and zero email
deliveries. It will win any influence ranking it appears in, forever. 0.25
Instance Method Summary collapse
- #call ⇒ Result
-
#initialize(start_date:, end_date:, window_days: DEFAULT_WINDOW_DAYS) ⇒ CampaignInfluence
constructor
A new instance of CampaignInfluence.
Constructor Details
#initialize(start_date:, end_date:, window_days: DEFAULT_WINDOW_DAYS) ⇒ CampaignInfluence
Returns a new instance of CampaignInfluence.
125 126 127 128 129 |
# File 'app/queries/marketing/campaign_influence.rb', line 125 def initialize(start_date:, end_date:, window_days: DEFAULT_WINDOW_DAYS) @start_date = start_date @end_date = end_date @window_days = window_days end |
Instance Method Details
#call ⇒ Result
132 133 134 135 136 137 138 139 140 141 142 |
# File 'app/queries/marketing/campaign_influence.rb', line 132 def call totals = period_totals Result.new( rows: rows(totals['period_revenue']), start_date: @start_date, end_date: @end_date, window_days: @window_days, period_invoices: totals['period_invoices'], period_revenue: totals['period_revenue'], distinct_influenced_invoices: totals['distinct_influenced_invoices'], distinct_influenced_revenue: totals['distinct_influenced_revenue'] ) end |