From 06219babdc1fe81957358bfafc3d9303d83e003c Mon Sep 17 00:00:00 2001 From: fivetran-catfritz <111930712+fivetran-catfritz@users.noreply.github.com> Date: Tue, 4 Mar 2025 09:57:52 -0600 Subject: [PATCH 1/9] feature/cash-flow-report --- models/netsuite2/netsuite2__balance_sheet.sql | 1 + models/netsuite2/netsuite2__cash_flow.sql | 137 ++++++++++++++++++ .../netsuite2/netsuite2__income_statement.sql | 1 + 3 files changed, 139 insertions(+) create mode 100644 models/netsuite2/netsuite2__cash_flow.sql diff --git a/models/netsuite2/netsuite2__balance_sheet.sql b/models/netsuite2/netsuite2__balance_sheet.sql index 435499f2..2fc768ee 100644 --- a/models/netsuite2/netsuite2__balance_sheet.sql +++ b/models/netsuite2/netsuite2__balance_sheet.sql @@ -318,3 +318,4 @@ balance_sheet as ( select * from surrogate_key +where accounting_period_ending >= '2025-01-31' \ No newline at end of file diff --git a/models/netsuite2/netsuite2__cash_flow.sql b/models/netsuite2/netsuite2__cash_flow.sql new file mode 100644 index 00000000..27ea0e2b --- /dev/null +++ b/models/netsuite2/netsuite2__cash_flow.sql @@ -0,0 +1,137 @@ +{{ config(enabled=var('netsuite_data_model', 'netsuite') == var('netsuite_data_model_override','netsuite2'))}} + +{%- set multibook_enabled = var('netsuite2__multibook_accounting_enabled', false) -%} +{%- set to_subsidiary_enabled = var('netsuite2__using_to_subsidiary', false) and var('netsuite2__using_exchange_in_rate', true) -%} +{%- set multibook_cols = ['accounting_book_id', 'accounting_book_name'] -%} +{%- set to_subsidiary_cols = ['to_subsidiary_id', 'to_subsidiary_name', 'to_subsidiary_currency_symbol'] -%} + +{%- set grain_cols_list = ['accounting_period_ending', 'subsidiary_id', 'subsidiary_name'] -%} +{%- do grain_cols_list.extend(multibook_cols) if multibook_enabled -%} +{%- do grain_cols_list.extend(to_subsidiary_cols) if to_subsidiary_enabled -%} +{%- set grain_cols_sql = grain_cols_list | join(',\n') -%} + +{%- set helper_cols_list = ['account_type_name','account_type_id','account_name','account_id'] -%} +{%- set helper_cols_sql = helper_cols_list | join(',\n') -%} + +with income_statement as ( + select + {{ grain_cols_sql }}, + sum(case when account_category in ('Income', 'Expense') then transaction_amount else 0 end) as net_income, + sum(case when lower(account_name) like '%depreciation%' or lower(account_name) like '%amortization%' then transaction_amount else 0 end) as non_cash_expenses + from {{ ref('netsuite2__income_statement') }} + {{ dbt_utils.group_by(grain_cols_list|length) }} +), + +balance_sheet as ( + select + {{ grain_cols_sql }}, + {{ helper_cols_sql }}, + sum(transaction_amount) as transaction_amount + from {{ ref('netsuite2__balance_sheet') }} + where lower(account_type_id) in ('acctpay', 'acctrec', 'bank', 'credcard', + 'deferexpense', 'deferrevenue', 'equity', 'fixedasset', 'longtermliab', + 'othasset', 'othcurrasset', 'othcurrliab', 'unbilledrec') + and not is_accounting_period_adjustment + {{ dbt_utils.group_by(grain_cols_list|length + helper_cols_list|length) }} +), + +balance_sheet_prev_transaction as ( + select + *, + lag(transaction_amount) over ( + partition by -- need to make these dynamic + subsidiary_id, + accounting_book_id, + to_subsidiary_id, + account_id + order by accounting_period_ending + ) as prev_transaction_amount + from balance_sheet +), + +balance_sheet_changes as ( + select + *, + transaction_amount - coalesce(prev_transaction_amount, 0) as change_since_prev_period + from balance_sheet_prev_transaction +), + +category_changes as ( + select + {{ grain_cols_sql }}, + -- working capital changes + sum(case when lower(account_type_id) = 'acctrec' + then change_since_prev_period else 0 end) as change_in_ar, + sum(case when lower(account_type_id) = 'acctpay' + then change_since_prev_period else 0 end) as change_in_ap, + sum(case when lower(account_type_id) = 'othcurrasset' + then change_since_prev_period else 0 end) as change_in_inventory, + + -- investment changes + sum(case when lower(account_type_id) in ('deferexpense', 'fixedasset', 'othasset', 'unbilledrec') + then change_since_prev_period else 0 end) as change_in_investments, + + -- financing changes + sum(case when lower(account_type_id) = 'equity' and lower(account_name) not like '%stock%' -- stocks not treated as cash + then change_since_prev_period else 0 end) as change_in_equity, + sum(case when lower(account_type_id) = 'deferrevenue' + then change_since_prev_period else 0 end) as change_in_deferred_revenue, + sum(case when lower(account_type_id) in ('longtermliab', 'othcurrliab', 'credcard') + then change_since_prev_period else 0 end) as change_in_debt, + + -- bank changes + sum(case when lower(account_type_id) = 'bank' + then change_since_prev_period else 0 end) as change_in_cash + from balance_sheet_changes + {{ dbt_utils.group_by(grain_cols_list|length) }} +), + +cash_flow as ( + select + {% for col in grain_cols_list %} + income_statement.{{ col }}, + {% endfor %} + + sum(coalesce(income_statement.net_income, 0)) as net_income, + sum(coalesce(income_statement.non_cash_expenses, 0)) as non_cash_expenses, + + -- Operating Cash Flow + sum(coalesce(category_changes.change_in_ar, 0)) as change_in_ar, + sum(coalesce(category_changes.change_in_ap, 0)) as change_in_ap, + sum(coalesce(category_changes.change_in_inventory, 0)) as change_in_inventory, + sum((income_statement.net_income + + income_statement.non_cash_expenses + - coalesce(category_changes.change_in_ar, 0) + - coalesce(category_changes.change_in_inventory, 0) + + coalesce(category_changes.change_in_ap, 0))) as cash_from_operations, + + -- Investing Cash Flow + sum(coalesce(category_changes.change_in_investments, 0)) as cash_from_investing, + + -- Financing Cash Flow + sum(coalesce(category_changes.change_in_equity, 0) + + coalesce(category_changes.change_in_deferred_revenue, 0) + + coalesce(category_changes.change_in_debt, 0)) as cash_from_financing, + + -- Bank Change in Cash + sum(coalesce(category_changes.change_in_cash, 0)) as reported_change_in_cash + + from income_statement + left join category_changes + on + {% for col in grain_cols_list %} + {{ 'and' if not loop.first}} income_statement.{{ col }} = category_changes. {{ col }} + {% endfor %} + {{ dbt_utils.group_by(grain_cols_list|length) }} +), + +final as ( + select + *, + -- Net Cash Flow + cash_from_operations + cash_from_investing + cash_from_financing as net_cash_flow + from cash_flow +) + +select * +from final \ No newline at end of file diff --git a/models/netsuite2/netsuite2__income_statement.sql b/models/netsuite2/netsuite2__income_statement.sql index 90f05158..65309043 100644 --- a/models/netsuite2/netsuite2__income_statement.sql +++ b/models/netsuite2/netsuite2__income_statement.sql @@ -204,3 +204,4 @@ surrogate_key as ( select * from surrogate_key +where accounting_period_ending >= '2025-01-31' From 32d155e99ceb189d47aa22795f87e9178b82394a Mon Sep 17 00:00:00 2001 From: fivetran-catfritz <111930712+fivetran-catfritz@users.noreply.github.com> Date: Tue, 4 Mar 2025 10:07:28 -0600 Subject: [PATCH 2/9] revise --- models/netsuite2/netsuite2__balance_sheet.sql | 3 +- models/netsuite2/netsuite2__cash_flow.sql | 43 ++++++++++--------- .../netsuite2/netsuite2__income_statement.sql | 1 + 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/models/netsuite2/netsuite2__balance_sheet.sql b/models/netsuite2/netsuite2__balance_sheet.sql index 2fc768ee..1cf7fd63 100644 --- a/models/netsuite2/netsuite2__balance_sheet.sql +++ b/models/netsuite2/netsuite2__balance_sheet.sql @@ -318,4 +318,5 @@ balance_sheet as ( select * from surrogate_key -where accounting_period_ending >= '2025-01-31' \ No newline at end of file +where accounting_period_ending >= '2025-01-31' +and accounting_period_ending < '2025-03-01' \ No newline at end of file diff --git a/models/netsuite2/netsuite2__cash_flow.sql b/models/netsuite2/netsuite2__cash_flow.sql index 27ea0e2b..2dda9b71 100644 --- a/models/netsuite2/netsuite2__cash_flow.sql +++ b/models/netsuite2/netsuite2__cash_flow.sql @@ -1,38 +1,39 @@ {{ config(enabled=var('netsuite_data_model', 'netsuite') == var('netsuite_data_model_override','netsuite2'))}} {%- set multibook_enabled = var('netsuite2__multibook_accounting_enabled', false) -%} -{%- set to_subsidiary_enabled = var('netsuite2__using_to_subsidiary', false) and var('netsuite2__using_exchange_in_rate', true) -%} {%- set multibook_cols = ['accounting_book_id', 'accounting_book_name'] -%} -{%- set to_subsidiary_cols = ['to_subsidiary_id', 'to_subsidiary_name', 'to_subsidiary_currency_symbol'] -%} -{%- set grain_cols_list = ['accounting_period_ending', 'subsidiary_id', 'subsidiary_name'] -%} -{%- do grain_cols_list.extend(multibook_cols) if multibook_enabled -%} -{%- do grain_cols_list.extend(to_subsidiary_cols) if to_subsidiary_enabled -%} -{%- set grain_cols_sql = grain_cols_list | join(',\n') -%} +{%- set to_subsidiary_enabled = var('netsuite2__using_to_subsidiary', false) and var('netsuite2__using_exchange_in_rate', true) -%} +{%- set to_subsidiary_cols = ['to_subsidiary_id', 'to_subsidiary_name', 'to_subsidiary_currency_symbol'] -%} -{%- set helper_cols_list = ['account_type_name','account_type_id','account_name','account_id'] -%} -{%- set helper_cols_sql = helper_cols_list | join(',\n') -%} +{%- set base_cols_list = ['accounting_period_ending', 'subsidiary_id', 'subsidiary_name'] -%} +{%- do base_cols_list.extend(multibook_cols) if multibook_enabled -%} +{%- do base_cols_list.extend(to_subsidiary_cols) if to_subsidiary_enabled -%} +{%- set base_cols_sql = base_cols_list | join(',\n') -%} with income_statement as ( select - {{ grain_cols_sql }}, + {{ base_cols_sql }}, sum(case when account_category in ('Income', 'Expense') then transaction_amount else 0 end) as net_income, sum(case when lower(account_name) like '%depreciation%' or lower(account_name) like '%amortization%' then transaction_amount else 0 end) as non_cash_expenses from {{ ref('netsuite2__income_statement') }} - {{ dbt_utils.group_by(grain_cols_list|length) }} + {{ dbt_utils.group_by(base_cols_list|length) }} ), balance_sheet as ( select - {{ grain_cols_sql }}, - {{ helper_cols_sql }}, + {{ base_cols_sql }}, + account_type_name, + account_type_id, + account_name, + account_id, sum(transaction_amount) as transaction_amount from {{ ref('netsuite2__balance_sheet') }} where lower(account_type_id) in ('acctpay', 'acctrec', 'bank', 'credcard', 'deferexpense', 'deferrevenue', 'equity', 'fixedasset', 'longtermliab', 'othasset', 'othcurrasset', 'othcurrliab', 'unbilledrec') and not is_accounting_period_adjustment - {{ dbt_utils.group_by(grain_cols_list|length + helper_cols_list|length) }} + {{ dbt_utils.group_by(base_cols_list|length + 4) }} ), balance_sheet_prev_transaction as ( @@ -40,9 +41,9 @@ balance_sheet_prev_transaction as ( *, lag(transaction_amount) over ( partition by -- need to make these dynamic + {{ 'accounting_book_id,' if multibook_enabled }} + {{ 'to_subsidiary_id,' if to_subsidiary_enabled }} subsidiary_id, - accounting_book_id, - to_subsidiary_id, account_id order by accounting_period_ending ) as prev_transaction_amount @@ -58,7 +59,7 @@ balance_sheet_changes as ( category_changes as ( select - {{ grain_cols_sql }}, + {{ base_cols_sql }}, -- working capital changes sum(case when lower(account_type_id) = 'acctrec' then change_since_prev_period else 0 end) as change_in_ar, @@ -83,12 +84,12 @@ category_changes as ( sum(case when lower(account_type_id) = 'bank' then change_since_prev_period else 0 end) as change_in_cash from balance_sheet_changes - {{ dbt_utils.group_by(grain_cols_list|length) }} + {{ dbt_utils.group_by(base_cols_list|length) }} ), cash_flow as ( select - {% for col in grain_cols_list %} + {% for col in base_cols_list %} income_statement.{{ col }}, {% endfor %} @@ -119,10 +120,10 @@ cash_flow as ( from income_statement left join category_changes on - {% for col in grain_cols_list %} - {{ 'and' if not loop.first}} income_statement.{{ col }} = category_changes. {{ col }} + {% for col in base_cols_list %} + {{ 'and' if not loop.first}} income_statement.{{ col }} = category_changes.{{ col }} {% endfor %} - {{ dbt_utils.group_by(grain_cols_list|length) }} + {{ dbt_utils.group_by(base_cols_list|length) }} ), final as ( diff --git a/models/netsuite2/netsuite2__income_statement.sql b/models/netsuite2/netsuite2__income_statement.sql index 65309043..d2c1f11d 100644 --- a/models/netsuite2/netsuite2__income_statement.sql +++ b/models/netsuite2/netsuite2__income_statement.sql @@ -205,3 +205,4 @@ surrogate_key as ( select * from surrogate_key where accounting_period_ending >= '2025-01-31' +and accounting_period_ending < '2025-03-01' \ No newline at end of file From 3343e5d7cf26f4be075d26416b73375a661f3ade Mon Sep 17 00:00:00 2001 From: fivetran-catfritz <111930712+fivetran-catfritz@users.noreply.github.com> Date: Tue, 4 Mar 2025 10:51:56 -0600 Subject: [PATCH 3/9] revision --- integration_tests/dbt_project.yml | 5 +++-- models/netsuite2/netsuite2__cash_flow.sql | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/integration_tests/dbt_project.yml b/integration_tests/dbt_project.yml index d32bb0b6..4fdc8038 100644 --- a/integration_tests/dbt_project.yml +++ b/integration_tests/dbt_project.yml @@ -12,8 +12,9 @@ vars: netsuite_data_model_override: netsuite # Enable below when generating docs - # netsuite2__multibook_accounting_enabled: true - # netsuite2__using_to_subsidiary: true + netsuite2__multibook_accounting_enabled: true + netsuite2__using_to_subsidiary: true + netsuite2__using_exchange_rate: false netsuite_source: # Netsuite Seed Data diff --git a/models/netsuite2/netsuite2__cash_flow.sql b/models/netsuite2/netsuite2__cash_flow.sql index 2dda9b71..a7911503 100644 --- a/models/netsuite2/netsuite2__cash_flow.sql +++ b/models/netsuite2/netsuite2__cash_flow.sql @@ -1,9 +1,10 @@ {{ config(enabled=var('netsuite_data_model', 'netsuite') == var('netsuite_data_model_override','netsuite2'))}} -{%- set multibook_enabled = var('netsuite2__multibook_accounting_enabled', false) -%} +{%- set multibook_enabled = true if var('netsuite2__multibook_accounting_enabled', false) -%} {%- set multibook_cols = ['accounting_book_id', 'accounting_book_name'] -%} -{%- set to_subsidiary_enabled = var('netsuite2__using_to_subsidiary', false) and var('netsuite2__using_exchange_in_rate', true) -%} +{%- set to_subsidiary_enabled = (var('netsuite2__using_to_subsidiary', false) and var('netsuite2__using_exchange_rate', true)) -%} +{{ print('to sub ' ~ to_subsidiary_enabled)}} {%- set to_subsidiary_cols = ['to_subsidiary_id', 'to_subsidiary_name', 'to_subsidiary_currency_symbol'] -%} {%- set base_cols_list = ['accounting_period_ending', 'subsidiary_id', 'subsidiary_name'] -%} @@ -14,7 +15,7 @@ with income_statement as ( select {{ base_cols_sql }}, - sum(case when account_category in ('Income', 'Expense') then transaction_amount else 0 end) as net_income, + sum(case when lower(account_category) in ('income', 'expense') then transaction_amount else 0 end) as net_income, sum(case when lower(account_name) like '%depreciation%' or lower(account_name) like '%amortization%' then transaction_amount else 0 end) as non_cash_expenses from {{ ref('netsuite2__income_statement') }} {{ dbt_utils.group_by(base_cols_list|length) }} From 4fd5fb4b87cb3d8be96093048deed7f5a88d101e Mon Sep 17 00:00:00 2001 From: fivetran-catfritz <111930712+fivetran-catfritz@users.noreply.github.com> Date: Tue, 4 Mar 2025 10:52:43 -0600 Subject: [PATCH 4/9] revision --- integration_tests/dbt_project.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/integration_tests/dbt_project.yml b/integration_tests/dbt_project.yml index 4fdc8038..b3b7c6ff 100644 --- a/integration_tests/dbt_project.yml +++ b/integration_tests/dbt_project.yml @@ -11,10 +11,9 @@ vars: netsuite_schema: netsuite_integration_tests_8 netsuite_data_model_override: netsuite - # Enable below when generating docs - netsuite2__multibook_accounting_enabled: true - netsuite2__using_to_subsidiary: true - netsuite2__using_exchange_rate: false + ## Enable below when generating docs + # netsuite2__multibook_accounting_enabled: true + # netsuite2__using_to_subsidiary: true netsuite_source: # Netsuite Seed Data From 900f5540c650538403c90a5c151e06c5ee77c969 Mon Sep 17 00:00:00 2001 From: fivetran-catfritz <111930712+fivetran-catfritz@users.noreply.github.com> Date: Tue, 4 Mar 2025 11:01:09 -0600 Subject: [PATCH 5/9] revision --- models/netsuite2/netsuite2__cash_flow.sql | 1 - 1 file changed, 1 deletion(-) diff --git a/models/netsuite2/netsuite2__cash_flow.sql b/models/netsuite2/netsuite2__cash_flow.sql index a7911503..9eb0be99 100644 --- a/models/netsuite2/netsuite2__cash_flow.sql +++ b/models/netsuite2/netsuite2__cash_flow.sql @@ -4,7 +4,6 @@ {%- set multibook_cols = ['accounting_book_id', 'accounting_book_name'] -%} {%- set to_subsidiary_enabled = (var('netsuite2__using_to_subsidiary', false) and var('netsuite2__using_exchange_rate', true)) -%} -{{ print('to sub ' ~ to_subsidiary_enabled)}} {%- set to_subsidiary_cols = ['to_subsidiary_id', 'to_subsidiary_name', 'to_subsidiary_currency_symbol'] -%} {%- set base_cols_list = ['accounting_period_ending', 'subsidiary_id', 'subsidiary_name'] -%} From 54aafdd1af4b3d423bf2bc108168acc2de97476c Mon Sep 17 00:00:00 2001 From: fivetran-catfritz <111930712+fivetran-catfritz@users.noreply.github.com> Date: Wed, 2 Apr 2025 20:50:34 -0500 Subject: [PATCH 6/9] feature/cash-flow-report --- dbt_project.yml | 23 +++ ...t_netsuite2__cash_flow_classifications.sql | 49 +++++++ models/netsuite2/netsuite2__cash_flow.sql | 138 ++++-------------- .../netsuite2__transaction_details.sql | 2 + 4 files changed, 101 insertions(+), 111 deletions(-) create mode 100644 models/netsuite2/intermediate/int_netsuite2__cash_flow_classifications.sql diff --git a/dbt_project.yml b/dbt_project.yml index f3caefcf..c4517afa 100644 --- a/dbt_project.yml +++ b/dbt_project.yml @@ -13,8 +13,31 @@ models: netsuite2: intermediate: +materialized: ephemeral + # int_netsuite2__cash_flow_classifications: + # +materialized: table vars: + cash_flow_defaults: + operations_transactions: + - condition: "lower(account_type_id) = 'acctrec'" + subcategory: "cash_from_customers" + - condition: "lower(account_type_id) = 'acctpay'" + subcategory: "cash_to_suppliers" + - condition: "lower(account_type_id) in ('expense', 'othcurrliab')" + subcategory: "cash_for_expenses" + - condition: "lower(account_name) like '%good%will%'" + exclude: true + + investments_transactions: + - condition: "lower(account_type_id) in ('fixedasset', 'othasset', 'deferexpense')" + subcategory: "cash_spent_on_assets" + - condition: "lower(account_type_id) = 'unbilledrec'" + subcategory: "cash_spent_on_investments" + + finances_transactions: + - condition: "lower(account_type_id) in ('equity', 'longtermliab')" + subcategory: "cash_from_financing" + netsuite: ## Netsuite staging models netsuite_accounting_books: "{{ ref('stg_netsuite__accounting_books') }}" diff --git a/models/netsuite2/intermediate/int_netsuite2__cash_flow_classifications.sql b/models/netsuite2/intermediate/int_netsuite2__cash_flow_classifications.sql new file mode 100644 index 00000000..cd29e824 --- /dev/null +++ b/models/netsuite2/intermediate/int_netsuite2__cash_flow_classifications.sql @@ -0,0 +1,49 @@ +{{ config( + enabled = var('netsuite_data_model', 'netsuite') == var('netsuite_data_model_override', 'netsuite2') +) }} + +-- load the dictionary with the classification filters +{% set classifications = var('cash_flow_classifications', var('cash_flow_defaults', {})) %} + +with transaction_details as ( + select * + from {{ ref('netsuite2__transaction_details') }} +), + +transaction_classifications as ( + select + *, + -- iterate through the categories and filters in classifications + case + {% for category, filters in classifications.items() %} + {% for filter in filters %} + when {{ filter.condition }} + {% if filter.exclude is defined and filter.exclude %} + then null + {% else %} + then '{{ category }}' + {% endif %} + {% endfor %} + {% endfor %} + else null + end as cash_flow_category, + + case + {% for category, filters in classifications.items() %} + {% for filter in filters %} + when {{ filter.condition }} + {% if filter.subcategory is defined %} + then '{{ filter.subcategory }}' + {% else %} + then null + {% endif %} + {% endfor %} + {% endfor %} + else null + end as cash_flow_subcategory + from transaction_details +) + +select * +from transaction_classifications +where cash_flow_category is not null \ No newline at end of file diff --git a/models/netsuite2/netsuite2__cash_flow.sql b/models/netsuite2/netsuite2__cash_flow.sql index 9eb0be99..0154e7c9 100644 --- a/models/netsuite2/netsuite2__cash_flow.sql +++ b/models/netsuite2/netsuite2__cash_flow.sql @@ -1,6 +1,8 @@ -{{ config(enabled=var('netsuite_data_model', 'netsuite') == var('netsuite_data_model_override','netsuite2'))}} +{{ config( + enabled = var('netsuite_data_model', 'netsuite') == var('netsuite_data_model_override', 'netsuite2') +) }} -{%- set multibook_enabled = true if var('netsuite2__multibook_accounting_enabled', false) -%} +{%- set multibook_enabled = var('netsuite2__multibook_accounting_enabled', false) -%} {%- set multibook_cols = ['accounting_book_id', 'accounting_book_name'] -%} {%- set to_subsidiary_enabled = (var('netsuite2__using_to_subsidiary', false) and var('netsuite2__using_exchange_rate', true)) -%} @@ -11,127 +13,41 @@ {%- do base_cols_list.extend(to_subsidiary_cols) if to_subsidiary_enabled -%} {%- set base_cols_sql = base_cols_list | join(',\n') -%} -with income_statement as ( - select - {{ base_cols_sql }}, - sum(case when lower(account_category) in ('income', 'expense') then transaction_amount else 0 end) as net_income, - sum(case when lower(account_name) like '%depreciation%' or lower(account_name) like '%amortization%' then transaction_amount else 0 end) as non_cash_expenses - from {{ ref('netsuite2__income_statement') }} - {{ dbt_utils.group_by(base_cols_list|length) }} +with cash_flow_classifications as ( + select * + from {{ ref('int_netsuite2__cash_flow_classifications') }} ), -balance_sheet as ( +aggregated_transactions as ( select {{ base_cols_sql }}, - account_type_name, - account_type_id, - account_name, - account_id, - sum(transaction_amount) as transaction_amount - from {{ ref('netsuite2__balance_sheet') }} - where lower(account_type_id) in ('acctpay', 'acctrec', 'bank', 'credcard', - 'deferexpense', 'deferrevenue', 'equity', 'fixedasset', 'longtermliab', - 'othasset', 'othcurrasset', 'othcurrliab', 'unbilledrec') - and not is_accounting_period_adjustment - {{ dbt_utils.group_by(base_cols_list|length + 4) }} -), + cash_flow_category, + sum(transaction_amount) as cash_ending_period + from cash_flow_classifications + {{ dbt_utils.group_by(base_cols_list|length + 1) }} +), -balance_sheet_prev_transaction as ( +with_lag as ( select *, - lag(transaction_amount) over ( - partition by -- need to make these dynamic - {{ 'accounting_book_id,' if multibook_enabled }} - {{ 'to_subsidiary_id,' if to_subsidiary_enabled }} - subsidiary_id, - account_id + lag(cash_ending_period) over ( + partition by + {{ base_cols_sql }}, + cash_flow_category order by accounting_period_ending - ) as prev_transaction_amount - from balance_sheet -), - -balance_sheet_changes as ( - select - *, - transaction_amount - coalesce(prev_transaction_amount, 0) as change_since_prev_period - from balance_sheet_prev_transaction -), - -category_changes as ( - select - {{ base_cols_sql }}, - -- working capital changes - sum(case when lower(account_type_id) = 'acctrec' - then change_since_prev_period else 0 end) as change_in_ar, - sum(case when lower(account_type_id) = 'acctpay' - then change_since_prev_period else 0 end) as change_in_ap, - sum(case when lower(account_type_id) = 'othcurrasset' - then change_since_prev_period else 0 end) as change_in_inventory, - - -- investment changes - sum(case when lower(account_type_id) in ('deferexpense', 'fixedasset', 'othasset', 'unbilledrec') - then change_since_prev_period else 0 end) as change_in_investments, - - -- financing changes - sum(case when lower(account_type_id) = 'equity' and lower(account_name) not like '%stock%' -- stocks not treated as cash - then change_since_prev_period else 0 end) as change_in_equity, - sum(case when lower(account_type_id) = 'deferrevenue' - then change_since_prev_period else 0 end) as change_in_deferred_revenue, - sum(case when lower(account_type_id) in ('longtermliab', 'othcurrliab', 'credcard') - then change_since_prev_period else 0 end) as change_in_debt, - - -- bank changes - sum(case when lower(account_type_id) = 'bank' - then change_since_prev_period else 0 end) as change_in_cash - from balance_sheet_changes - {{ dbt_utils.group_by(base_cols_list|length) }} -), - -cash_flow as ( - select - {% for col in base_cols_list %} - income_statement.{{ col }}, - {% endfor %} - - sum(coalesce(income_statement.net_income, 0)) as net_income, - sum(coalesce(income_statement.non_cash_expenses, 0)) as non_cash_expenses, - - -- Operating Cash Flow - sum(coalesce(category_changes.change_in_ar, 0)) as change_in_ar, - sum(coalesce(category_changes.change_in_ap, 0)) as change_in_ap, - sum(coalesce(category_changes.change_in_inventory, 0)) as change_in_inventory, - sum((income_statement.net_income - + income_statement.non_cash_expenses - - coalesce(category_changes.change_in_ar, 0) - - coalesce(category_changes.change_in_inventory, 0) - + coalesce(category_changes.change_in_ap, 0))) as cash_from_operations, - - -- Investing Cash Flow - sum(coalesce(category_changes.change_in_investments, 0)) as cash_from_investing, - - -- Financing Cash Flow - sum(coalesce(category_changes.change_in_equity, 0) - + coalesce(category_changes.change_in_deferred_revenue, 0) - + coalesce(category_changes.change_in_debt, 0)) as cash_from_financing, - - -- Bank Change in Cash - sum(coalesce(category_changes.change_in_cash, 0)) as reported_change_in_cash - - from income_statement - left join category_changes - on - {% for col in base_cols_list %} - {{ 'and' if not loop.first}} income_statement.{{ col }} = category_changes.{{ col }} - {% endfor %} - {{ dbt_utils.group_by(base_cols_list|length) }} + ) as cash_beginning_period + from aggregated_transactions ), final as ( select - *, - -- Net Cash Flow - cash_from_operations + cash_from_investing + cash_from_financing as net_cash_flow - from cash_flow + {{ base_cols_sql }}, + accounting_period_ending, + cash_flow_category, + cash_beginning_period, + cash_ending_period, + cash_ending_period - coalesce(cash_beginning_period, 0) as cash_net_period + from with_lag ) select * diff --git a/models/netsuite2/netsuite2__transaction_details.sql b/models/netsuite2/netsuite2__transaction_details.sql index f904c1e4..fc23091a 100644 --- a/models/netsuite2/netsuite2__transaction_details.sql +++ b/models/netsuite2/netsuite2__transaction_details.sql @@ -345,3 +345,5 @@ surrogate_key as ( select * from surrogate_key +where accounting_period_ending >= '2025-01-31' +and accounting_period_ending < '2025-03-01' \ No newline at end of file From eb15afd2e32b9d10ac5dd715e4ec91c72c537b04 Mon Sep 17 00:00:00 2001 From: fivetran-catfritz <111930712+fivetran-catfritz@users.noreply.github.com> Date: Wed, 2 Apr 2025 20:53:34 -0500 Subject: [PATCH 7/9] feature/cash-flow-report --- models/netsuite2/netsuite2__cash_flow.sql | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/models/netsuite2/netsuite2__cash_flow.sql b/models/netsuite2/netsuite2__cash_flow.sql index 0154e7c9..194b146b 100644 --- a/models/netsuite2/netsuite2__cash_flow.sql +++ b/models/netsuite2/netsuite2__cash_flow.sql @@ -22,9 +22,10 @@ aggregated_transactions as ( select {{ base_cols_sql }}, cash_flow_category, + cash_flow_subcategory, sum(transaction_amount) as cash_ending_period from cash_flow_classifications - {{ dbt_utils.group_by(base_cols_list|length + 1) }} + {{ dbt_utils.group_by(base_cols_list|length + 2) }} ), with_lag as ( @@ -33,7 +34,8 @@ with_lag as ( lag(cash_ending_period) over ( partition by {{ base_cols_sql }}, - cash_flow_category + cash_flow_category, + cash_flow_subcategory order by accounting_period_ending ) as cash_beginning_period from aggregated_transactions @@ -44,6 +46,7 @@ final as ( {{ base_cols_sql }}, accounting_period_ending, cash_flow_category, + cash_flow_subcategory, cash_beginning_period, cash_ending_period, cash_ending_period - coalesce(cash_beginning_period, 0) as cash_net_period From bcfd2e44af25fa2e50d57a3a73faabe93261864e Mon Sep 17 00:00:00 2001 From: fivetran-catfritz <111930712+fivetran-catfritz@users.noreply.github.com> Date: Wed, 2 Apr 2025 20:54:14 -0500 Subject: [PATCH 8/9] feature/cash-flow-report --- models/netsuite2/netsuite2__balance_sheet.sql | 4 +--- models/netsuite2/netsuite2__income_statement.sql | 4 +--- models/netsuite2/netsuite2__transaction_details.sql | 4 +--- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/models/netsuite2/netsuite2__balance_sheet.sql b/models/netsuite2/netsuite2__balance_sheet.sql index 1cf7fd63..a815f4de 100644 --- a/models/netsuite2/netsuite2__balance_sheet.sql +++ b/models/netsuite2/netsuite2__balance_sheet.sql @@ -317,6 +317,4 @@ balance_sheet as ( ) select * -from surrogate_key -where accounting_period_ending >= '2025-01-31' -and accounting_period_ending < '2025-03-01' \ No newline at end of file +from surrogate_key \ No newline at end of file diff --git a/models/netsuite2/netsuite2__income_statement.sql b/models/netsuite2/netsuite2__income_statement.sql index d2c1f11d..97f3f9f7 100644 --- a/models/netsuite2/netsuite2__income_statement.sql +++ b/models/netsuite2/netsuite2__income_statement.sql @@ -203,6 +203,4 @@ surrogate_key as ( ) select * -from surrogate_key -where accounting_period_ending >= '2025-01-31' -and accounting_period_ending < '2025-03-01' \ No newline at end of file +from surrogate_key \ No newline at end of file diff --git a/models/netsuite2/netsuite2__transaction_details.sql b/models/netsuite2/netsuite2__transaction_details.sql index fc23091a..d4d4656f 100644 --- a/models/netsuite2/netsuite2__transaction_details.sql +++ b/models/netsuite2/netsuite2__transaction_details.sql @@ -344,6 +344,4 @@ surrogate_key as ( ) select * -from surrogate_key -where accounting_period_ending >= '2025-01-31' -and accounting_period_ending < '2025-03-01' \ No newline at end of file +from surrogate_key \ No newline at end of file From b462272a9bc2252cdacf1b12155baabaa7ba5e39 Mon Sep 17 00:00:00 2001 From: fivetran-catfritz <111930712+fivetran-catfritz@users.noreply.github.com> Date: Wed, 2 Apr 2025 22:29:05 -0500 Subject: [PATCH 9/9] feature/cash-flow-report --- dbt_project.yml | 6 +- ...t_netsuite2__cash_flow_classifications.sql | 2 +- models/netsuite2/netsuite2__cash_flow.sql | 102 ++++++++++++++---- 3 files changed, 83 insertions(+), 27 deletions(-) diff --git a/dbt_project.yml b/dbt_project.yml index c4517afa..dc6ec363 100644 --- a/dbt_project.yml +++ b/dbt_project.yml @@ -18,7 +18,7 @@ models: vars: cash_flow_defaults: - operations_transactions: + operations: - condition: "lower(account_type_id) = 'acctrec'" subcategory: "cash_from_customers" - condition: "lower(account_type_id) = 'acctpay'" @@ -28,13 +28,13 @@ vars: - condition: "lower(account_name) like '%good%will%'" exclude: true - investments_transactions: + investment: - condition: "lower(account_type_id) in ('fixedasset', 'othasset', 'deferexpense')" subcategory: "cash_spent_on_assets" - condition: "lower(account_type_id) = 'unbilledrec'" subcategory: "cash_spent_on_investments" - finances_transactions: + finance: - condition: "lower(account_type_id) in ('equity', 'longtermliab')" subcategory: "cash_from_financing" diff --git a/models/netsuite2/intermediate/int_netsuite2__cash_flow_classifications.sql b/models/netsuite2/intermediate/int_netsuite2__cash_flow_classifications.sql index cd29e824..8419561f 100644 --- a/models/netsuite2/intermediate/int_netsuite2__cash_flow_classifications.sql +++ b/models/netsuite2/intermediate/int_netsuite2__cash_flow_classifications.sql @@ -21,7 +21,7 @@ transaction_classifications as ( {% if filter.exclude is defined and filter.exclude %} then null {% else %} - then '{{ category }}' + then '{{ category }}_transactions' {% endif %} {% endfor %} {% endfor %} diff --git a/models/netsuite2/netsuite2__cash_flow.sql b/models/netsuite2/netsuite2__cash_flow.sql index 194b146b..12384a52 100644 --- a/models/netsuite2/netsuite2__cash_flow.sql +++ b/models/netsuite2/netsuite2__cash_flow.sql @@ -8,50 +8,106 @@ {%- set to_subsidiary_enabled = (var('netsuite2__using_to_subsidiary', false) and var('netsuite2__using_exchange_rate', true)) -%} {%- set to_subsidiary_cols = ['to_subsidiary_id', 'to_subsidiary_name', 'to_subsidiary_currency_symbol'] -%} -{%- set base_cols_list = ['accounting_period_ending', 'subsidiary_id', 'subsidiary_name'] -%} +{%- set base_cols_list = ['subsidiary_id', 'subsidiary_name'] -%} {%- do base_cols_list.extend(multibook_cols) if multibook_enabled -%} {%- do base_cols_list.extend(to_subsidiary_cols) if to_subsidiary_enabled -%} -{%- set base_cols_sql = base_cols_list | join(',\n') -%} +{%- set base_cols_sql = base_cols_list | join(', ') -%} + +{%- set categories = var('cash_flow_classifications', var('cash_flow_defaults', {})).keys() -%} with cash_flow_classifications as ( select * from {{ ref('int_netsuite2__cash_flow_classifications') }} ), -aggregated_transactions as ( +aggregated_by_category as ( select {{ base_cols_sql }}, + accounting_period_ending, cash_flow_category, - cash_flow_subcategory, - sum(transaction_amount) as cash_ending_period + sum(transaction_amount) as cash_net_period from cash_flow_classifications - {{ dbt_utils.group_by(base_cols_list|length + 2) }} -), + {{ dbt_utils.group_by(base_cols_list | length + 2) }} +), + +pivoted_cash_flow as ( + select + {{ base_cols_sql }}, + accounting_period_ending, + + + {%- for category in categories %} + sum( + case when cash_flow_category = '{{ category }}_transactions' + then cash_net_period + else 0 end) + as {{ category }}_cash_flow {{ ',' if not loop.last }} + {%- endfor %} + + from aggregated_by_category + {{ dbt_utils.group_by(base_cols_list | length + 1) }} +), + +total_cash as ( + select + *, + {%- for category in categories %} + {{ category }}_cash_flow {{ '+' if not loop.last }} + {%- endfor %} + as net_cash_flow + from pivoted_cash_flow +), -with_lag as ( +with_beginning_cash as ( select *, - lag(cash_ending_period) over ( - partition by - {{ base_cols_sql }}, - cash_flow_category, - cash_flow_subcategory + lag(net_cash_flow) over ( + partition by {{ base_cols_sql }} order by accounting_period_ending - ) as cash_beginning_period - from aggregated_transactions + ) as beginning_cash + from total_cash ), -final as ( +income_statement as ( + select * + from {{ ref('netsuite2__income_statement') }} +), + +income_statement_classifications as ( select {{ base_cols_sql }}, accounting_period_ending, - cash_flow_category, - cash_flow_subcategory, - cash_beginning_period, - cash_ending_period, - cash_ending_period - coalesce(cash_beginning_period, 0) as cash_net_period - from with_lag + sum(case when lower(account_category) in ('income', 'expense') then transaction_amount else 0 end) as net_income, + sum(case when lower(account_name) like '%depreciation%' or lower(account_name) like '%amortization%' then transaction_amount else 0 end) as non_cash_expenses + from income_statement + {{ dbt_utils.group_by(base_cols_list | length + 1) }} +), + +final as ( + select + with_beginning_cash.accounting_period_ending, + {% for col in base_cols_list %} + with_beginning_cash.{{ col }}, + {% endfor %} + + {%- for category in categories %} + with_beginning_cash.{{ category }}_cash_flow , + {%- endfor %} + + with_beginning_cash.net_cash_flow, + with_beginning_cash.beginning_cash, + coalesce(with_beginning_cash.beginning_cash, 0) + with_beginning_cash.net_cash_flow as ending_cash, + income_statement_classifications.net_income, + income_statement_classifications.non_cash_expenses + from with_beginning_cash + left join income_statement_classifications + on with_beginning_cash.accounting_period_ending = income_statement_classifications.accounting_period_ending + and {% for col in base_cols_list %} + with_beginning_cash.{{ col }} = income_statement_classifications.{{ col }} + {% if not loop.last %} and {% endif %} + {% endfor %} ) select * -from final \ No newline at end of file +from final +order by accounting_period_ending