Dimensions are stored as a numbered set, not as fields, and everything that creates a document or journal line has to build the right set. How dimension sets work, how to read and write them in code, applying default dimensions, validating combinations, and the mistakes that leave entries with blank dimensions.
Dimensions look like fields on a document, and two of them (the global ones) are. The rest, and the whole set, are stored differently, and most extension code that creates documents or journal lines gets them subtly wrong. This is how they actually work.
Every unique combination of dimension values is stored once in Dimension Set Entry under a Dimension Set ID. A document header, line, journal line or ledger entry carries that ID in "Dimension Set ID", plus the two global dimensions copied into "Shortcut Dimension 1 Code" and "Shortcut Dimension 2 Code" for filtering. Changing a dimension on a line does not edit the set; it finds or creates a new set and points the line at it. Sets are immutable and shared.
var
DimMgt: Codeunit DimensionManagement;
TempDimSetEntry: Record "Dimension Set Entry" temporary;
NewDimSetID: Integer;
begin
// read the current set into a temp table
DimMgt.GetDimensionSet(TempDimSetEntry, SalesLine."Dimension Set ID");
// add or change a value
TempDimSetEntry.Init();
TempDimSetEntry."Dimension Code" := ProjectDimCodeTok;
TempDimSetEntry."Dimension Value Code" := ProjectCode;
if not TempDimSetEntry.Insert() then
TempDimSetEntry.Modify();
// get (or create) the set for the new combination
NewDimSetID := DimMgt.GetDimensionSetID(TempDimSetEntry);
SalesLine."Dimension Set ID" := NewDimSetID;
DimMgt.UpdateGlobalDimFromDimSetID(NewDimSetID, SalesLine."Shortcut Dimension 1 Code", SalesLine."Shortcut Dimension 2 Code");
end;
Simpler, and preferred for setting one value: SalesLine.ValidateShortcutDimCode(3, Value) for a shortcut dimension by its position, which does all of the above. For the two globals, validating the field does it.
When a document line is created for an item, customer or G/L account, the base application calls DimMgt.GetDefaultDimID with the list of source records (customer, salesperson, item, and so on) and builds the set from their default dimensions, honouring priority. If your code creates lines without going through the standard Validate of the "No." field, the defaults are never applied, and the line posts with blank dimensions. Always validate Type then "No." on a new line; or call CreateDim on the line, which is what validation does.
DimMgt.CheckDimValuePosting (and CheckDimIDComb) enforce Code Mandatory, Same Code, blocked values and dimension combinations. Posting calls them; your pre-checks should too, so the user gets the error on the line, not at posting.
"Shortcut Dimension 1 Code" directly without updating the set ID. The global field and the set disagree; reports by set are wrong.