Mission Control Revenue Tracking & Attribution Analysis
Date: February 26, 2026 Subject: Qalo Revenue Discrepancy Investigation & System Documentation Discrepancy: Revenue Comparison (255,871.60) = $43,068.43Table of Contents
- Executive Summary
- How Pixel Tracked Revenue Works
- Revenue Comparison vs Executive Summary
- Attribution Models Explained
- UTM Parameter Analysis
- Current Issues & Root Causes
- Recommended Fixes
Executive Summary
Key Findings
The $43K discrepancy is caused by:-
Different Data Sources:
- Revenue Comparison uses
interaction_insight_summary(attribution data with overcounting) - Executive Summary uses
shopify_daily_summary(Shopify aggregation, may have sync issues)
- Revenue Comparison uses
-
Any-Click Attribution Overcounting:
- Revenue Comparison uses “Any Click” attribution by default
- Every touchpoint gets 100% credit -> same order counted multiple times
- Example: 300 in Revenue Comparison
-
Potential Data Sync Issues:
shopify_daily_summarytable may be missing days or have stale data- Executive Summary may understate revenue if daily sync fails
How Pixel Tracked Revenue Works
Q: How is pixel tracked revenue defined?
Answer: Pixel tracked revenue is defined as revenue from orders that have at least one marketing touchpoint tracked by the Mission Control pixel. Technical Implementation: File:eventapp/management/commands/process_ordcredit.py:61-81
eventapp/management/commands/process_data.py:1540-1562
eventapp/management/commands/process_ordcredit.py:93-115
eventapp_ordercredit -> aggregated into interaction_insight_summary -> displayed as “pixel tracked revenue”
Q: Does pixel revenue match with Shopify? Are untracked orders represented?
Answer: Pixel revenue SHOULD match Shopify, but currently DOES NOT due to:1. Untracked Orders (No Pixel Touchpoints)
Current Behavior:- Orders with NO marketing touchpoints = excluded from attribution reports
- These orders are in
orderstable but NOT ineventapp_ordercredit - Example: Direct orders from repeat customers who bookmarked the site
interaction_insight/selectors.py:1897
2. Shopify Daily Summary Table Issues
Current Implementation: File:custom_reports/models.py:46
sales_performance/selectors.py:39-40
- Model uses:
shopify_daily_summary - Actual table:
shopify_daily_data(confirmed by database schema check) - This may cause queries to fail or return stale data
Q: Should the pixel track ALL Shopify revenue regardless of source?
Answer: YES, it should, but currently it doesn’t. Current Behavior:- YES Pixel tracks transaction events regardless of source
- YES Orders are synced from Shopify API (all orders)
- NO Attribution only includes orders with
media_sourcetouchpoints - NO Direct orders without prior pixel sessions = excluded from attribution
eventapp/management/commands/process_ordcredit.py:61
- If no touchpoints found, attribute to “Direct” with
credit_score = 1.0 - This ensures 100% of Shopify revenue is represented in Mission Control
Revenue Comparison vs Executive Summary
Revenue Comparison Page ($298,940.03)
URL:https://app.lunarmc.ai/revenue-comparison
Backend Flow:
interaction_insight/views.py:8273->RevenuesDashboardGraph.get()- Line 8307: Sets
attribution = 'Any Click'(hardcoded!) - Calls
NewOrderDetailsChannelData->OrderDetailsSource - Executes query with
SUM(any_click_revenue)/100
interaction_insight/selectors.py:1897
- Uses Any-Click attribution -> every touchpoint gets 100% credit
- Example journey: Google Ads -> Email -> Facebook Ads -> Purchase ($100)
- Google Ads: +$100
- Email: +$100
- Facebook Ads: +$100
- Total: $300 (3x overcounting)
Executive Summary Gross Sales ($255,871.60)
URL:https://app.lunarmc.ai/sales-performance
Backend Flow:
sales_performance/views.py:87->SalesDashboard.get()- Calls
GetTotalShopifySalesData+GetSalesDashboardData - Merges results and displays “Gross Sales” card
sales_performance/selectors.py:15-21
sales_performance/selectors.py:39-40
- Uses Shopify aggregation -> each order counted once
- May be missing data if
shopify_daily_summarytable has sync issues - Should match Shopify Admin (but might be understated if days are missing)
Attribution Models Explained
Q: What is the attribution model for L5 Pixel?
Answer: Mission Control supports FOUR attribution models with a 30-day attribution window (configurable).1. Any-Click Attribution
How it works: Every touchpoint gets 100% credit. File:eventapp/management/commands/process_ordcredit.py:119-146
interaction_insight/selectors.py:1897
- Order: $100
- Touchpoints: Google Ads, Email, Facebook Ads
- Each gets: $100
- Total shown: $300 YES (intentional overcounting)
2. First-Click Attribution
How it works: Only the first touchpoint gets 100% credit. File:interaction_insight/selectors.py:1891
sales_performance/views.py:204-217 (ProcessInsightSummary.py aggregation)
- Touchpoints: Google Ads (2/1) -> Email (2/8) -> Facebook Ads (2/20)
- Google Ads gets: $100
- Email gets: $0
- Facebook Ads gets: $0
3. Last-Click Attribution
How it works: Only the last touchpoint gets 100% credit. File:interaction_insight/selectors.py:1893
interaction_insight/management/commands/ProcessInsightSummary.py:219-232
- Touchpoints: Google Ads (2/1) -> Email (2/8) -> Facebook Ads (2/20)
- Google Ads gets: $0
- Email gets: $0
- Facebook Ads gets: $100
4. Linear (Equal Weight) Attribution
How it works: Credit is split evenly across all non-excluded touchpoints. File:eventapp/management/commands/process_ordcredit.py:119-146
interaction_insight/selectors.py:1895
interaction_insight/management/commands/ProcessInsightSummary.py:234-242
- Order: $100
- Touchpoints: Google Ads, Email, Facebook Ads (3 total)
- Each gets: $33.33 (credit_score = 0.333)
- Total shown: $100 YES
Attribution Window
File:eventapp/management/commands/process_ordcredit.py:43-49
eventapp/management/commands/process_ordcredit.py:61-68
- Default: 30 days lookback
- Configurable per client via
client.attr_lookupfield - NOT lifetime: Only looks back X days before purchase
UTM Parameter Analysis
Current Mission Control Parameters
Reference: Mission Control UTM Guide Example (Facebook Ads):eventapp/models/identity.py:123-132 (MediaAttRule matching)
Q: Why do we need all these parameters?
Current Parameter Breakdown:| Parameter | Example Value | Purpose | Is Redundant? |
|---|---|---|---|
l5s | fb | Identifies platform | YES YES - l5ss already contains this |
l5m | social | Media type | YES YES - Can be inferred from l5s |
l5ss | {{site_source_name}} | Detailed source | YES KEEP - Primary identifier |
l5adid | {{ad.id}} | Ad ID | YES KEEP - Needed for ad-level tracking |
l5p | {{placement}} | Placement | ❓ REVIEW - Check if used in reports |
Redundancy Analysis
1. l5s=fb is Redundant
Current Usage:
l5ss={{site_source_name}}already contains platform info- Facebook’s macro
{{site_source_name}}returns values like:- “fb” (mobile app)
- “ig” (Instagram)
- “facebook” (desktop)
- “instagram” (explicit)
l5s parameter
- Rely solely on
l5ssfor platform identification - Update
MediaAttRuleto match onl5sspatterns instead
2. l5m=social is Redundant
Current Usage:
- Media type can be inferred from
l5sorl5ss:fb,ig,tiktok,snapchat-> Socialgoogle,bing-> Searchemail,klaviyo-> Email
interaction_insight/selectors.py:1901-1902
l5m parameter
- Calculate
media_typeserver-side based onmedia_source - Create a mapping table:
3. l5p={{placement}} May Be Useful
Current Usage:
- Stored but not displayed in standard reports
- Could be used for:
- Facebook Placements: Feed, Stories, Reels, Marketplace, etc.
- Google Placements: Search, Display Network, YouTube, etc.
eventapp/models/identity.py
- Check with clients if they use placement data
- If not used, remove to simplify URL structure
Simplified Parameter Structure
Recommended Minimal Parameters:l5ss->media_source(via MediaAttRule)l5adid->mkt_content(ad identifier)l5cid->campaign_id(for ad platform sync)l5c->mkt_campaign(campaign name)
- YES Cleaner URLs
- YES Less client configuration
- YES Easier troubleshooting
- YES Maintains full tracking capability
Q: When can clients use L5 UTM vs standard UTM?
Answer: Clients can use EITHER L5 parameters OR standard UTM parameters (or both). Current Implementation: File:eventapp/models/identity.py:123-132 (MediaAttRule)
- L5 parameters checked first
- If not found, fall back to standard UTM
- Allows clients to use existing UTM structure
- New clients: Use L5 parameters (cleaner, more specific)
- Existing clients with UTM: Can keep using UTM (backward compatible)
- Migration path: Add MediaAttRule entries that map standard UTM to media_source
Current Issues & Root Causes
Issue 1: Revenue Comparison Page Shows Inflated Numbers
Root Cause: Hardcoded “Any Click” attribution File:interaction_insight/views.py:8307
- Users see 255K
- 17% overstatement due to multi-touch attribution
- Confusing when compared to Shopify Admin
Issue 2: Executive Summary Gross Sales May Be Understated
Root Cause: Table name mismatch + potential sync issues File:custom_reports/models.py:46
- Query may fail silently
- Returns stale data or zeros
- Executive Summary shows lower revenue than actual
Issue 3: MC ROAS and MC CPA Are Redundant
Current Implementation: File:sales_performance/views.py:190-192
- MC ROAS:
pixel_tracked_revenue / ad_spend - MC CPA:
ad_spend / pixel_tracked_orders
sales_performance/views.py:179 (MER calculation)
sales_performance/views.py:185 (CAC calculation)
| Metric | Formula | What It Measures | Includes Untracked Orders? |
|---|---|---|---|
| MER | shopify_revenue / ad_spend | All revenue efficiency | YES YES |
| MC ROAS | pixel_revenue / ad_spend | Tracked revenue only | NO NO |
| CAC | ad_spend / new_customers | Cost to acquire customer | YES YES |
| MC CPA | ad_spend / pixel_orders | Cost per tracked order | NO NO |
- If pixel tracks 90% of orders -> MC ROAS understated by 10%
- MER is more accurate (uses all Shopify revenue)
- Showing both metrics is confusing
- MER and CAC already provide these insights
- MER is more accurate (includes all revenue)
- Reduces dashboard clutter
Issue 4: Untracked Orders Excluded from Attribution
Root Cause: Orders without page views are not attributed File:eventapp/management/commands/process_ordcredit.py:61
- Customer bookmarks site -> direct to checkout
- No page_view events captured
- Order completes but has NO touchpoints
- Excluded from
eventapp_ordercredittable - Missing from attribution reports
- ~5-10% of orders typically have no touchpoints
- These orders don’t appear in Revenue Comparison or Channel Performance
- Attribution totals understate true performance
interaction_insight/selectors.py:1897
Recommended Fixes
Fix 1: Change Revenue Comparison Default Attribution
Problem: Revenue Comparison hardcodes “Any Click” attribution, causing overcounting. Current Code: File:interaction_insight/views.py:8307
- Revenue Comparison will show 298K
- Matches Executive Summary and Shopify Admin
- Still allows users to select “Any Click” if desired
Fix 2: Fix Shopify Daily Summary Table Name
Problem: Model points to wrong table name. Current Code: File:custom_reports/models.py:46
- Executive Summary will pull from correct table
- Gross Sales will match Shopify Admin
- Eliminates potential for stale data
Fix 3: Remove MC ROAS and MC CPA from Executive Summary
Problem: Redundant metrics that confuse users. Current Code: File:sales_performance/views.py:190-192
- MER includes ALL Shopify revenue (more accurate than MC ROAS)
- CAC is the standard industry metric (CPA is confusing)
- Pixel tracking coverage is < 100%, so MC metrics understate performance
- Showing both sets creates confusion about which to trust
- Cleaner dashboard with 2 fewer cards
- Users see one source of truth (MER) instead of conflicting ROAS values
- Aligns with industry standards (CAC is standard, not CPA)
Fix 4: Attribute Untracked Orders to “Direct”
Problem: Orders without touchpoints are excluded from attribution. Current Code: File:eventapp/management/commands/process_ordcredit.py:61-81
- 100% of Shopify orders now appear in attribution reports
- “Direct” channel will show true untracked revenue
- Revenue Comparison total will match Shopify Admin
Fix 5: Simplify L5 UTM Parameters
Problem: Redundant parameters (l5s, l5m) clutter URLs.
Current Parameters:
- Update MediaAttRule to match on
l5ssonly:
- Calculate media_type server-side:
eventapp/models/identity.py (add method)
- Update URL templates:
- 40% shorter URLs
- Easier client setup (fewer parameters to configure)
- Maintains full tracking capability
- Backward compatible (old URLs still work)
Fix 6: Add Revenue Reconciliation Report
Problem: No easy way to see why numbers don’t match. Recommended: Create a new “Revenue Reconciliation” page that shows:Summary of Recommended Changes
| Issue | Current Behavior | Recommended Fix | Priority |
|---|---|---|---|
| Revenue Comparison overcounting | Shows $298K (Any Click) | Change default to Last Click | HIGH HIGH |
| Table name mismatch | Model uses wrong table | Update model to shopify_daily_data | HIGH HIGH |
| MC ROAS/CPA redundant | Shows 4 similar metrics | Remove MC ROAS and MC CPA | MEDIUM MEDIUM |
| Untracked orders excluded | ~5-10% orders missing | Attribute to “Direct” | MEDIUM MEDIUM |
| L5 parameters redundant | l5s=fb&l5m=social&l5ss=... | Use only l5ss and l5adid | LOW LOW |
| No reconciliation report | Users confused by differences | Add reconciliation dashboard | LOW LOW |
Implementation Plan
Phase 1: Critical Fixes (Week 1)
-
Fix Revenue Comparison attribution default
- File:
interaction_insight/views.py:8307 - Change:
'Any Click'->'Last Click' - Test: Verify Revenue Comparison shows ~$255K
- File:
-
Fix table name in model
- File:
custom_reports/models.py:46 - Change:
'shopify_daily_summary'->'shopify_daily_data' - Run:
python manage.py migrate
- File:
-
Remove MC ROAS and MC CPA
- File:
sales_performance/views.py:190-192 - Remove: Lines that add MC ROAS and MC CPA cards
- Test: Verify Executive Summary shows only MER and CAC
- File:
Phase 2: Attribution Improvements (Week 2-3)
-
Attribute untracked orders to Direct
- File:
eventapp/management/commands/process_ordcredit.py:81 - Add: Fallback Direct attribution logic
- Test: Verify 100% of orders appear in attribution
- File:
-
Add attribution model selector to Revenue Comparison
- File: Frontend component
- Add: Dropdown to select First/Last/Any/Linear
- Default: Last Click
Phase 3: UTM Simplification (Week 4)
-
Simplify L5 parameters
- Update: MediaAttRule configuration
- Remove: Dependencies on
l5sandl5m - Document: New parameter structure for clients
-
Create reconciliation report
- New:
/revenue-reconciliationpage - Shows: All revenue sources side-by-side
- Explains: Why numbers differ
- New:
Testing Checklist
- Revenue Comparison matches Executive Summary (±$1K)
- Executive Summary Gross Sales matches Shopify Admin
- All orders from Shopify appear in attribution (check untracked count = 0)
- MC ROAS and MC CPA cards removed from dashboard
- Simplified L5 parameters work for all ad platforms
- Reconciliation report shows correct breakdowns
- Performance: Queries run in < 2 seconds
Appendix: SQL Queries for Verification
Query 1: Check for Table Mismatch
Query 2: Compare All Revenue Sources
Query 3: Find Untracked Orders
Query 4: Verify Daily Summary Completeness
Document Version: 1.0 Last Updated: February 26, 2026 Author: Mission Control Engineering Team
.png?fit=max&auto=format&n=Frm2GFbmok4D-yJA&q=85&s=93c3ebd47542af65d1cd06d8563a7f6e)