Replacing Financial Dimension in Ledger Dimension

Replacing a financial dimension in a Ledger dimension is not straight forward as the replacing of a dimension in Default dimensions. For default dimensions, we had a method serviceReplaceAttributeValue to replace one particular dimension. But for a ledger dimension, we need to do the following:

  1. Get the target ledger dimension
  2. Break it into its component parts (Main account and active dimensions)
  3. Prepare the hash key array for each of them except main account (for only financial dimensions)
  4. In this hash key array replace the hash key of the required financial dimension with the hash key of attribute value to be replaced
  5. Find or create the DimensionAttributeValueSet table
  6. Then use the “serviceCreateLedgerDimension” method of DimensionDefaultingService to get the new ledger dimension and use it

If anybody finds a better solution, please share.

Here is the job that does the steps explained above:

static void replaceLedgerDimensions(Args _args)



    DimensionSHA1Hash               hash; //To store the calculated hash for DimensionAttributeValueSet

    HashKey                         valueKeyHashArray[]; //To store the has key of dimension in question

    Map                             dimAttrIdx, dimAttrRecId; //to store the dimension index and backing entity type

    DimensionAttributeSetItem       dimAttrSetItem; // Contains the number of dimensions active for a account structure ledger

    DimensionAttribute              dimAttr; // Contains the financial dimensions records

    DimensionAttributeValue         dimAttrValue; // Contains used financial dimension values

    DimensionAttributeValueSet      dimAttrValueSet; //Contains default dimension records

    DimensionAttributeValueSetItem  dimAttrValueSetItem; //Contains individual records for default dimensions

    DimAttributeHcmWorker           dimAttrWorker; //Backing entity view for Employee type dimension

    DimensionEnumeration            dimensionSetId; //Record id for table that contains active dimensions for current ledger

    LedgerDimensionAccount          targetDimension = 5637145829; //Record Id DimensionAttributeValueCombination table in which attribute value is to be replaced

    LedgerDimensionAccount          ledgerDimension; // To hold the resultant DimensionAttributeValueCombination record id

    LedgerDimensionAccount          mainAccDimension; // Record id for LedgerDimension(DimensionAttributeValueCombination) containing default account for main account RecId

    DimensionStorage                dimensionStorage; // Class Dimension storage is used to store and manipulate the values of combination

    DimensionStorageSegment         segment; // Class DimensionStorageSegment will get specfic segments based on hierarchies


    int     segmentCount, segmentIndex;

    int     hierarchyCount, hierarchyIndex;

    SysDim  segmentValue, mainAccountValue;

    int     dimAttrCount, i;

    int     emplBackEntityType; //Stores the backing entity type for Employee type dimension

    str     valueStrArray[]; //To store the dimension attribute values

    int64   valueKeyArray[]; //To store the record ids of DimensionAtrributeValue table

    int     backingEntityInstance;



    //The employee backing entity will be the view DimAttributeHcmWorker

    emplBackEntityType = tableNum(DimAttributeHcmWorker);


    //Initialize the map to store the backing entity types

    dimAttrIdx = new Map(Types::Integer, Types::Integer);

    dimAttrRecId = new Map(Types::Integer, Types::Int64);


    //Get the record Id (dimension set id) for current ledger to find active dimensions

    dimensionSetId = DimensionCache::getDimensionAttributeSetForLedger();


    //Find all the active dimensions for current ledger except main account and store there

    //backing entity type in the map

    while select * from dimAttr

            order by Name

            where dimAttr.Type != DimensionAttributeType::MainAccount

        join RecId from dimAttrSetItem

            where dimAttrSetItem.DimensionAttribute == dimAttr.RecId &&

                dimAttrSetItem.DimensionAttributeSet == dimensionSetId



        dimAttrIdx.insert(dimAttr.BackingEntityType, dimAttrCount);

        dimAttrRecId.insert(dimAttr.BackingEntityType, dimAttr.RecId);



    //initialize hash key array to null

    for (i = 1; i<= dimAttrCount; i++)


        valueKeyHashArray[i] = emptyGuid();

        valueStrArray[i] = ;

        valueKeyArray[i] = 0;



    // Default hash key array with required dimension value

    // Get dimension storage

    dimensionStorage = DimensionStorage::findById(targetDimension);

    if (dimensionStorage == null)


        throw error("@SYS83964");



    // Get hierarchy count

    hierarchyCount = dimensionStorage.hierarchyCount();

    //Loop through hierarchies to get individual segments

    for(hierarchyIndex = 1; hierarchyIndex <= hierarchyCount; hierarchyIndex++)


        //Get segment count for hierarchy

        segmentCount = dimensionStorage.segmentCountForHierarchy(hierarchyIndex);


        //Loop through segments and display required values

        for (segmentIndex = 1; segmentIndex <= segmentCount; segmentIndex++)


            // Get segment

            segment = dimensionStorage.getSegmentForHierarchy(hierarchyIndex, segmentIndex);


            // Get the segment information

            if (segment.parmDimensionAttributeValueId() != 0)


                //Get the backing entity type;

                backingEntityInstance = DimensionAttribute::find(DimensionAttributeValue::find(segment.parmDimensionAttributeValueId()).DimensionAttribute).BackingEntityType;


                if (backingEntityInstance == tableNum(DimAttributeMainAccount))

                    mainAccountValue = segment.parmDisplayValue();


                //Default the required arrays

                if (dimAttrIdx.exists(backingEntityInstance))


                    i = dimAttrIdx.lookup(backingEntityInstance);

                    valueKeyHashArray[i]    = segment.parmHashKey();

                    valueKeyArray[i]        = segment.parmDimensionAttributeValueId();

                    valueStrArray[i]        = segment.parmDisplayValue();






    //Find the Dimension attribute record for the dimension to work on, in our case it is HcmWorker


    dimAttr = DimensionAttribute::find(dimAttrRecId.lookup(emplBackEntityType));


    //Get the backing entity type for the dimension value to process

    select firstOnly dimAttrWorker

        where dimAttrWorker.Value == "51011";


    //Find the required Dimension Attribute Value record

    //Create if necessary

    dimAttrValue = DimensionAttributeValue::findByDimensionAttributeAndEntityInst(dimAttr.RecId, dimAttrWorker.RecId, false, true);


    //Replace the required combination hash keys and other value arrays

    i = dimAttrIdx.lookup(emplBackEntityType);

    valueKeyHashArray[i]    = dimAttrValue.HashKey;

    valueStrArray[i]        = dimAttrWorker.Value;

    valueKeyArray[i]        = dimAttrValue.RecId;


    //Calculate the hash for the current values

    hash = DimensionAttributeValueSetStorage::getHashFromArray(valueKeyHashArray, dimAttrCount);


    //Null hash indicates no values exist, which may occur if the user entered an invalid value for one dimension attribute

    if (hash == conNull())


        throw error("Wrong value for Employee Dimension");



    // Search for existing value set

    dimAttrValueSet = DimensionAttributeValueSet::findByHash(hash);


    // This value set does not exist, so it must be persisted

    if (!dimAttrValueSet)




        // Insert the value set with appropriate hash

        dimAttrValueSet.Hash = hash;



        // Insert only specified set items use this

        for (i = 1; i <= dimAttrCount; i++)


            if (valueKeyArray[i] != 0)



                dimAttrValueSetItem.DimensionAttributeValueSet = dimAttrValueSet.RecId;

                dimAttrValueSetItem.DimensionAttributeValue = valueKeyArray[i];

                dimAttrValueSetItem.DisplayValue = valueStrArray[i];








    // Get the default account for main account

    mainAccDimension = DimensionStorage::getDefaultAccountForMainAccountNum(mainAccountValue);


    //Find or create the LedgerDimension record for required combination

    ledgerDimension = DimensionDefaultingService::serviceCreateLedgerDimension(




    info(strFmt("Original %1: %2", targetDimension, DimensionAttributeValueCombination::find(targetDimension).DisplayValue));

    info(strFmt("Replaced %1: %2", ledgerDimension, DimensionAttributeValueCombination::find(ledgerDimension).DisplayValue));




Here is the output:


8 thoughts on “Replacing Financial Dimension in Ledger Dimension

  1. Thanks Sumit for this information!
    Just a quick answer: is there any possibilities, when using system dimensions like Customer,ItemGroup…, to automatically populate the corresponding financial dimension when creating or modifying the Customer or the Item GRoup?
    For example:
    Creating Customer “C1”; the corresponding Finacial Dimension “Customer”, alread previously created, should be automatically populated with the default “C1” customer code.
    Is there any special setup to perform before? It is a standard AX behavior?
    Thanks and kind regards

    1. Hi Michele,

      We also have this scenario where we made customer as one of the financial dimensions, the users wanted this to be defaulted on customer once the customer is created.

      We didnt have any other code but in the CustTable’s write method, we wrote the code to default the value.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s