Calculating Monthly Recurring Revenue (MRR) Using FiveTran ELT, NetSuite, and Snowflake

Monthly Recurring Revenue (MRR) is a vital financial metric for businesses with subscription-based revenue models. To calculate it, we've established an ELT (Extract, Load, Transform) pipeline using FiveTran and Snowflake, pulling sales and revenue data directly from NetSuite.

Before we delve into the details, you must be aware of two custom fields in NetSuite: CUSTCOL_RRSTART and CUSTCOL_RREND. These fields must be manually updated with the contract start and end dates, respectively, for each sales transaction.

Step 1: Create the Sales Order Details View

To begin with, we create a view in Snowflake named mrr_1_so_detail in the silver.finance schema. This view extracts the essential sales order details, including contract start and end dates, contract length, total contract amount, and amount per contract period. We also convert the status codes to their meaningful equivalents for easy interpretation.

Here's the SQL script for creating this view:

create or replace view silver.finance.mrr_1_so_detail as
select 
    customer.companyname,
    transaction.trandate,
    transaction.tranid,
    transaction.transactionnumber,
    transaction.type,
    transaction.memo,
    line.custcol_rrstart as contract_start,
    date_trunc('month', contract_start) as contract_start_month,
    line.custcol_rrend as contract_end,
    -- determines length of contract and drops partial months
    case
        when day(contract_start) = 1 then 
            datediff('months', date_trunc('month', contract_start), date_trunc('month', contract_end)) + 1
        when day(contract_end) + 1 >= day(contract_start) then 
            datediff('months', date_trunc('month', contract_start), date_trunc('month', contract_end))
        else 
            datediff('months', date_trunc('month', contract_start), date_trunc('month', contract_end)) - 1
    end as contract_months,
    dateadd('months', contract_months, contract_start_month) as contract_end_month,
    classification.name as class,
    classification.fullname as class_full,
    line.netamount * -1 as amount,
    iff(contract_months = 0, 0, amount / contract_months) as amount_per_period,
    -- assigns textual description to status codes
    case 
        when status = 'A' then 'Pending Approval'
        when status = 'B' then 'Pending Fulfillment'
        when status = 'C' then 'Cancelled'
        when status = 'D' then 'Partially Fulfilled'
        when status = 'E' then 'Partially Fulfilled'
        when status = 'F' then 'Pending Billing'
        when status = 'G' then 'Billed'
        when status = 'H' then 'Closed'
        else 'NA'
    end as status,
    month_dim.year,
    month_dim.month
from 
    fivetran.netsuite_suiteanalytics.transactionline as line
left join fivetran.netsuite_suiteanalytics.transaction as transaction 
    on transaction.id = line.transaction
-- creates a separate record for each month in the contract period
left join gold.dates.my_month_dimension as month_dim 
    on month_dim.first_day_month >= contract_start_month 
    and month_dim.first_day_month <= contract_end_month
left join fivetran.netsuite_suiteanalytics.classification 
    on classification.id = line.class
left join fivetran.netsuite_suiteanalytics.customer 
    on customer.id = transaction.entity
where 
    custcol_rrstart is not null
    and type = 'SalesOrd'
order by tranid desc, year desc, month desc

Step 2: Preparing the Legacy Revenue Recognition Details

Next, we prepare the legacy revenue recognition details using another view mrr_2_je_legacy in the silver.finance schema. We handle legacy transactions differently from current ones to ensure accurate MRR calculations.

Here's the SQL script for creating this view:

create or replace view silver.finance.mrr_2_je_legacy as
select
    companyname as companyname,
    elementdate as trandate,
    recordnumber as tranid,
    revenueelement.fullname as transactionnumber,
    sourcerecordtype as type,
    '' as memo,
    revrecstartdate as contract_start,
    date_trunc('month', revrecstartdate) as contract_start_month,
    revrecenddate as contract_end,
    case
        when day(contract_start) = 1 then datediff('months', date_trunc('month', contract_start), date_trunc('month', contract_end)) + 1
        when day(contract_end) + 1 >= day(contract_start) then datediff('months', date_trunc('month', contract_start), date_trunc('month', contract_end)) 
        else datediff('months', date_trunc('month', contract_start), date_trunc('month', contract_end)) - 1
    end as contract_months,
    dateadd('months', contract_months, contract_start_month) as contract_end_month,
    classification.name as class,
    classification.fullname as class_full,
    salesamount as amount,
    iff(contract_months = 0, 0, amount / contract_months) as amount_per_period,
    revenueplanstatus as status,
    month_dim.year,
    month_dim.month
from
    fivetran.netsuite_suiteanalytics.revenueelement
left join fivetran.netsuite_suiteanalytics.customer 
    on (revenueelement.entity = customer.id)
left join fivetran.netsuite_suiteanalytics.classification 
    on classification.id = revenueelement.class
left join gold.dates.my_month_dimension as month_dim 
    on month_dim.first_day_month >= contract_start_month 
    and month_dim.first_day_month <= contract_end_month
where 
  sourcerecordtype = 'journalentry'
order by revrecstartdate asc;

Step 3: Merging Current and Historical Data

In this step, we combine current sales order details with historical revenue recognition details. We accomplish this through a union operation in the view mrr_3_detail, situated within the silver.finance schema.

Here's the SQL script for creating this view:

create or replace view silver.finance.mrr_3_detail as
select * from silver.finance.mrr_1_so_detail
union
select * from silver.finance.mrr_2_je_legacy

Step 4: Creating the Waterfall

Afterward, we create a 'waterfall' view, mrr_waterfall, within the gold.finance schema, which shows the total MRR per month for each company.

Here's the SQL script for creating this view:

create or replace view gold.finance.mrr_waterfall as
select 
    companyname,
    class_full,
    sum(amount_per_period) as sum_amount_per_period,
    year,
    month
from silver.finance.mrr_3_detail
group by companyname, class_full, year, month

Step 5: Final Visualization

Lastly, we utilize Snowflake's pivot function to reshape our data for the final visualization. This can also be achieved using PowerBI for a more graphical representation.

Here's the SQL script for the pivot operation:

select * from (select * from gold.finance.mrr_waterfall where year = '2023')

pivot(sum(sum_amount_per_period) for month in ('1', '2', '3', '4','5','6','7','8','9','10','11','12'))

as p;

This approach presents an efficient way to calculate MRR by taking full advantage of the capabilities provided by FiveTran ELT, NetSuite, and Snowflake. It offers the potential for accurate MRR tracking and useful insights into your business' revenue performance.

Previous
Previous

The Medallion Architecture: A Structured Approach to Business Data

Next
Next

Setup email Support