Skip to main content

Adding Dimensions

Konsolidat's dimension system is defined once in dbt_project.yml and auto-propagates through all Gold models via Jinja macros.

How It Works

1. Define in dbt_project.yml

vars:
dimensions:
- name: dim_cost_center # Column name in Gold models
source_column: CostCenter # D365 OData field name
label: "Cost Center" # Display name
cube_type: string # Analytical type
in_budget: true # Include in budget models
allocation_role: cost_center # Used by allocation engine
- name: dim_department
source_column: Department
label: "Department"
cube_type: string
in_budget: true
- name: dim_business_unit
source_column: BusinessUnit
label: "Business Unit"
cube_type: string
in_budget: false

2. Macros Read the Config

The dimension_helpers.sql macros read var('dimensions') and generate SQL fragments:

-- In a Gold model:
select
data_area_id,
{{ dim_select('gl.') }}, -- gl.dim_cost_center, gl.dim_department, gl.dim_business_unit
{{ measure_select() }}
from {{ ref('silver_gl_entries') }} as gl
group by
data_area_id,
{{ dim_group_by('gl.') }} -- gl.dim_cost_center, gl.dim_department, gl.dim_business_unit

3. API Filters Automatically

The Frappe API's _batch_query_clickhouse() function accepts cost_center and department as optional filters. These map to dim_cost_center and dim_department in the SQL WHERE clause.

Adding a New Dimension

Step 1: Add to dbt_project.yml

vars:
dimensions:
# ... existing dimensions ...
- name: dim_project
source_column: Project
label: "Project"
cube_type: string
in_budget: false

Step 2: Verify Source Data

Ensure the D365 OData entity exposes the field. Check the Bronze model:

select distinct Project from epm_bronze.bronze_general_journal_account_entries limit 10

Step 3: Rebuild

dbt build --full-refresh

The dimension macros will automatically include dim_project in all models that use dim_select(), dim_group_by(), etc.

Step 4: Update API (Optional)

If you want the new dimension to be filterable via =EPM():

  1. Add a parameter to epm_value() and epm_batch() in konsol/api.py
  2. Add it to the SQL WHERE clause generation in _batch_query_clickhouse()
  3. Add it to the VBA ResolveEpmArgs() function if needed

Dimension Properties

PropertyRequiredDescription
nameYesColumn name in Gold models (convention: dim_*)
source_columnYesField name in D365 OData / Bronze source
labelYesHuman-readable display name
cube_typeYesAnalytical type (currently: string)
in_budgetYestrue to include in budget models (get_budget_dimensions())
allocation_roleNoSpecial role: cost_center is used by the allocation engine

Which Macros Use Dimensions

MacroWhere UsedEffect
dim_select()SELECT clausesAdds dimension columns
dim_group_by()GROUP BY clausesGroups by dimensions
dim_join_on()JOIN conditionsMatches dimensions across tables
dim_coalesce()FULL OUTER JOIN resultsPicks non-null dimension value
dim_partition_by()Window functionsPartitions by dimensions
dim_empty_strings()Non-entity consolidation layersFills '' for IC/CTA rows
dim_select_from_source()Bronze modelsMaps OData source_column to dim_name

Budget Dimensions

get_budget_dimensions() filters to dimensions with in_budget: true. These are the only dimensions included in the budget spread model. Currently:

  • dim_cost_center (in_budget: true)
  • dim_department (in_budget: true)
  • dim_business_unit (in_budget: false — excluded from budget)

Set in_budget: true on your new dimension if budget data should be tracked at that level.

Allocation Cost Center Dimension

get_allocation_cost_center_dim() returns the dimension with allocation_role: 'cost_center'. The allocation engine uses this to match source/target cost centers. Only one dimension should have this role.

Next Steps