Chinh (lelouvincx) / [Internal] Customers' MRR (Exchange Rate)

Created Mon, 25 May 2026 00:00:00 +0000 Modified Mon, 25 May 2026 06:02:25 +0000
726 Words
  • Goal

    • Resolve discrepancies between MRR (Monthly Recurring Revenue) numbers on bi.holistics.io and Zoho Billing, and establish a single source of truth for customer MRR.
    • Stakeholders: Data Team, RevOps (Quinn, Arden, Vincent), Finance (Sriram).
    • Why it matters: MRR is the core metric for revenue planning (see [[Lead Funnel by Sales Motion]]’s $330K target). If the number is wrong, every downstream decision — pricing analysis, churn measurement, forecasting — is wrong.
    • Decision-making questions:
      • Why does MRR on bi.holistics.io differ from Zoho Billing?
      • Which exchange rate methodology should we use for multi-currency subscriptions?
      • How do we handle customers with multiple tenants or multi-region setups?
  • MRR Definition

    • MRR = sum of all active subscriptions’ monthly recurring revenue.
    • Zoho’s base currency: Singapore Dollar (SGD).
    • Per-subscription calculation: subscription.mrr / subscription.exchange_rate
    • Customer = a HubSpot Company (one company can have multiple Holistics tenants and multiple Zoho subscriptions).
    • Primary Tenant: for customers with multi-region or multi-tenant setups, the pipeline must select one primary tenant. This is the core identity resolution challenge.
  • Data Architecture

    • flowchart TD
          subgraph Sources["Data Sources"]
              Holistics["Holistics Backend DB\n(Tenants, Users, Trial Submissions)"]
              Zoho["Zoho Billing\n(Subscriptions, Invoices, Exchange Rates)"]
              HubSpot["HubSpot CRM\n(Companies)"]
          end
      
        subgraph Pipeline["dbt Pipeline"]
            stg_tenants["stg_holistics__tenants"]
            stg_users["stg_holistics__users"]
            stg_trials["stg_holistics__trial_submissions"]
            stg_zoho["stg_zoho__subscriptions"]
            stg_hs["stg_hubspot__companies"]
      
            domain["Domain Mapping\n(tenant → domain_name)"]
            customer_id["itg_mappings__customer_identities\n(Holistics ↔ Zoho ↔ HubSpot)"]
            dim_customer["dim_customers\n(primary tenant selection)"]
            fct_mrr["fct_mrr\n(MRR calculation)"]
        end
      
        subgraph Issues["🔴 Known Issues (9 problems)"]
            I1["1.1/1.2: Exchange Rate Errors\n(static rate, multi-step conversion)"]
            I2["2.1-2.4: Wrong Tenant↔Company Mapping\n(region bug, domain mismatch,\nduplicates, multi-domain)"]
            I3["3.1/3.2: Wrong Tenant↔Zoho Mapping\n(manual errors, multi-tenant sub)"]
            I4["4: Active but Unpaid Customers\n(Zoho subscription logic bug)"]
        end
      
        subgraph Output["Reports"]
            bi_h["bi.holistics.io\nMRR Overview Dashboard"]
            monitor["Customer Identity\nMonitoring Dashboard"]
        end
      
        Holistics --> stg_tenants & stg_users & stg_trials
        Zoho --> stg_zoho
        HubSpot --> stg_hs
      
        stg_tenants & stg_users & stg_trials --> domain
        domain --> customer_id
        stg_hs --> customer_id
        stg_zoho --> customer_id
        customer_id --> dim_customer
        dim_customer --> fct_mrr
        fct_mrr --> bi_h
        customer_id --> monitor
      
          I1 -.->|affects| fct_mrr
          I2 -.->|affects| customer_id
          I3 -.->|affects| customer_id
          I4 -.->|affects| stg_zoho
      
    • Data Sources

      • HubSpot CRM: Companies.
      • Holistics Backend DB: Tenants, Users, Trial Submissions.
      • Zoho Billing: Zoho Customers, Subscriptions, Payments, Invoices, Exchange Rates.
    • dbt Pipeline Flow

      • Staging: stg_holistics__tenants, stg_holistics__users, stg_zoho__subscriptions, stg_hubspot__companies
      • Mapping: itg_mappings__customer_identities (Holistics ↔ Zoho ↔ HubSpot)
      • Dimensions: dim_customers (primary tenant selection logic)
      • Facts: fct_mrr (final MRR calculation)
    • Identity Resolution

      • Currently relies on domain_name (extracted from tenant uname or email). This is fragile.
      • Proposed long-term fix: a canonical customer_skey shared across HubSpot and Zoho.
    • ER Diagram

    • Key Datasets & Dashboards

  • Exchange Rate Decision

    • Base currency: SGD (Zoho uses only 1).
    • Reporting currency: USD (agreed upon, >40% of revenue is in USD, USD is more recognizable and comparable).
    • Problem: pipeline previously used static rates; Zoho uses per-transaction rates set at plan creation time.
    • Agreed approach (Option 2): use live rates (e.g., Google Finance / ECB) — specifically the current/today’s rate for a “what is it worth now” view.
    • Caveat: requires careful handling to avoid FX noise in historical growth metrics.
  • Identified Problems & Root Causes

    • CategoryProblemRoot Cause
      Exchange Rate1.1/1.2: InaccuraciesPipeline uses static rates; Zoho uses per-transaction rates set at plan creation
      HubSpot Mapping2.1–2.4: Mapping BugsMissing regions, domain mismatches (e.g., raft.ai vs vector.ai), duplicate HubSpot companies
      Zoho Mapping3.1/3.2: Link ErrorsManual errors and system inability to handle multi-tenant subscriptions
      Subscription Logic4: Unpaid ActivesZoho fails to re-activate tenants after past-due payments are settled
  • Edge Cases

    • Multi-tenant customers: Datacubed — one Zoho account for two tenants.
    • Multi-region conflicts: Kognity — SG database shows expired trial while US database shows active paying status.
    • Domain mismatches: Vector AI — HubSpot uses vector.ai, app uses raft.ai.
  • Technical Fixes

    • PR #812: Fixed missing regions in manual mapping.
    • PR #852: Implemented hard-coded patches for edge cases:
      • map_holistics_zoho.csv: maps specific tenants (e.g., Datacubed) to shared Zoho accounts.
      • partner_programs.csv: excludes freemium plan IDs from MRR.
      • dom_holistics__internal_tenants.sql: filters out internal Holistics testing tenants.
  • Progress

    • DONE Identify root causes of MRR discrepancies
    • DONE Agree on exchange rate methodology (Option 2 — live rates)
    • DONE Fix missing regions in manual mapping (PR #812)
    • DONE Implement hard-coded patches for edge cases (PR #852)
    • DONE Set up Customer Identity Monitoring dashboard
    • TODO Replace static exchange rates with live rate ingestion — DAT-576 (for quick win)
    • TODO Implement canonical customer_skey for long-term identity resolution
    • TODO Handle Zoho re-activation bug for past-due subscriptions
  • References