Financial Dimensions in D365 Finance: How They Really Work
Financial dimensions are the part of Dynamics 365 Finance that everyone uses and few truly understand. Cost center, department, business unit, project — they tag every transaction and drive every meaningful report. Then a developer opens the data model, sees DimensionAttributeValueCombination joined to four other Dimension* tables, and quietly backs away. Let us make this concrete.
Two kinds of dimensions
There are exactly two flavours, and confusing them causes most dimension bugs.
- Default dimensions are a set of dimension values attached to a master record or line — a vendor, a fixed asset, a purchase line. They answer "what tags should transactions inherit from here?"
- Ledger dimensions are a main account plus its dimensions, combined into one accounting string. They answer "exactly which account did this voucher hit?"
A purchase order line carries a default dimension. When it posts, the framework merges that default dimension with the main account to produce the ledger dimension on the voucher. Same values, different purpose, different storage.
Where the values actually live
Every unique combination of dimension values is stored once in DimensionAttributeValueCombination (DAVC) and referenced by RecId. The supporting cast:
| Table | Holds |
|---|---|
DimensionAttribute | The dimension definitions (Department, CostCenter…) |
DimensionAttributeValue | A single value for one attribute (CostCenter "CC010") |
DimensionAttributeValueCombination | A full combination (the accounting string) |
DimensionAttributeValueSet | A set of values for default dimensions |
DimensionAttributeLevelValue | The exploded per-attribute rows for a combination |
The LedgerDimension and DefaultDimension fields you see on tables are just RecId pointers into this structure. That indirection is why you cannot read a cost center with a simple field access.
Reading dimensions in X++
To get a human-readable value out of a default dimension, walk the value set:
public static str getDimensionValue(
DimensionDefault _defaultDimension, str _dimensionName)
{
DimensionAttributeValueSetItem setItem;
DimensionAttributeValue dimValue;
DimensionAttribute dimAttr;
select firstonly Value from setItem
where setItem.DimensionAttributeValueSet == _defaultDimension
join RecId from dimValue
where dimValue.RecId == setItem.DimensionAttributeValue
join Name from dimAttr
where dimAttr.RecId == dimValue.DimensionAttribute
&& dimAttr.Name == _dimensionName;
return setItem.DisplayValue;
}
Building a default dimension in code
The supported way to create a combination is the DimensionDefaultingService / DimensionAttributeValueSetStorage helper, never hand-inserting into DAVC:
public static DimensionDefault buildDefaultDimension(
container _attrNames, container _values)
{
DimensionAttributeValueSetStorage storage =
new DimensionAttributeValueSetStorage();
for (int i = 1; i <= conLen(_attrNames); i++)
{
DimensionAttribute attr =
DimensionAttribute::findByName(conPeek(_attrNames, i));
if (attr.RecId)
{
DimensionAttributeValue dimValue =
DimensionAttributeValue::findByDimensionAttributeAndValue(
attr, conPeek(_values, i), false, true);
storage.addItem(dimValue);
}
}
return storage.save();
}
addItem plus save() deduplicates against existing combinations for you, so you never create orphan rows.
Account structures and advanced rules
What combinations are valid is governed by account structures and advanced rules, configured under General ledger > Chart of accounts > Structures. An account structure says "for main accounts 6000–6999, Department and CostCenter are required, BusinessUnit is blank." Advanced rules layer conditional dimensions on top — "if Department is Sales, also require SalesRegion."
This is why a voucher posting can fail with "the dimension combination is not valid": the values exist, but the account structure does not permit that shape for that main account. When you build ledger dimensions in code, validate against the active structure or the posting will reject them.
Reporting reality
Financial reporting (the Management Reporter successor) and Power BI both read the exploded DimensionAttributeLevelValue rows, which is why dimensions you populate correctly "just appear" in reports. If a dimension shows blank in reports but looks fine on the form, the usual culprit is a default dimension that was never merged into the ledger dimension at posting time — the report reads the voucher, and the voucher never got the tag.
Once you internalize default vs ledger and combinations stored once by RecId, the rest of the model stops being intimidating and starts being predictable.
Keep reading
Extending Data Entities in D365 Finance & Operations Without Breaking Upgrades
Add fields, computed columns, and validation to standard D365 Finance & Operations data entities the upgrade-safe way — with X++ examples and the staging-table traps to avoid.
Chain of Command vs Event Handlers: Extending D365 F&O the Right Way
When to use Chain of Command and when to use pre/post event handlers in Dynamics 365 Finance & Operations — with X++ examples, a decision table, and the gotchas that trip up teams.
Electronic Reporting in D365 Finance: Building Custom Formats Without Code
A practical guide to the Electronic Reporting (ER) framework in D365 Finance — data models, model mappings, and format configurations to produce custom files without X++.
Newsletter
New posts, straight to your inbox
One email per post. No spam, no tracking pixels, unsubscribe anytime.
Comments
No comments yet. Be the first.