Replace a Financial Dimension in Default Dimensions–Condensed [AX 2012]

In my previous post on Replacing default dimensions, I had provided a job to replace one financial dimension within a default dimension. The job was pretty big and I always thought that Microsoft should have provided some way to do these operations easily. Luckily I found a class that has helped me to condense that job and make it pretty small. I am sharing that job here:

The dimensions for Customer record looks like this before running the job:

image

Here is the job to change the values. We will change the values for Business Unit, Department and Worker all with this simple job:

static void replaceDefaultDimensionsCondense(Args _args)

{

    /*

     * In this job, we will replace the Business Unit Value from BU-001 to BU-002

     * and fill in the values for Department as Dep-001 and Worker as 114

     */

    CustTable                       custTable = CustTable::find(‘CUS-00004’); //Customer Record containing Financial Dimension

    Struct                          struct = new Struct(); //Structure to hold the dimension values to replace

    container                       defDimensionCon; //Container to prepare the required values and dimension attribute combination

    DimensionDefault                dimensionDefault; //Get the replaced dimension recid

    DimensionAttributeSetItem       dimAttrSetItem; //Table to get active dimensions for the legal entity

    DimensionAttribute              dimAttribute; //Table to get the Financial dimensions

    int i; //For looping

 

    //Loop for required dimensions

    while select Name, BackingEntityType from dimAttribute

        where dimAttribute.BackingEntityType == tableNum(DimAttributeOMBusinessUnit) ||

              dimAttribute.BackingEntityType == tableNum(DimAttributeOMDepartment) ||

              dimAttribute.BackingEntityType == tableNum(DimAttributeHcmWorker) &&

              dimAttribute.Type              != DimensionAttributeType::DynamicAccount

              join dimAttrSetItem

                where dimAttrSetItem.DimensionAttribute == dimAttribute.RecId &&

                      dimAttrSetItem.DimensionAttributeSet == DimensionCache::getDimensionAttributeSetForLedger()

    {

        //Add the Dimension name and display value to struct

        if (dimAttribute.BackingEntityType == tableNum(DimAttributeOMBusinessUnit))

        {

            struct.add(dimAttribute.Name, ‘BU-002’);

        }

        else if (dimAttribute.BackingEntityType == tableNum(DimAttributeOMDepartment))

        {

            struct.add(dimAttribute.Name, ‘DEP-002’);

        }

        else if (dimAttribute.BackingEntityType == tableNum(DimAttributeHcmWorker))

        {

            struct.add(dimAttribute.Name, ‘114’);

        }

    }

 

    //Prepare the container

    defDimensionCon += struct.fields();

 

    for (i = 1; i <= struct.fields(); i++)

    {

        defDimensionCon += struct.fieldName(i);

        defDimensionCon += struct.valueIndex(i);

    }

 

    //if there are fields in struct

    if (struct.fields())

    {

        //Get the DimensionAttributeValueSet table’s Record ID

        dimensionDefault = AxdDimensionUtil::getDimensionAttributeValueSetId(defDimensionCon);

       

        //Update to Customer

        ttsBegin;

        custTable.selectForUpdate(true);

        if (custTable.DefaultDimension)

        {

            custTable.DefaultDimension = DimensionDefaultingService::serviceMergeDefaultDimensions(dimensionDefault, custTable.DefaultDimension);

        }

        else

        {

            custTable.DefaultDimension = dimensionDefault;

        }

        custTable.doUpdate();

        ttsCommit;

    }

}

 

Dimensions after running the job:

image

Pretty Neat!

The class AxdDimensionUtil is pretty handy to do all these stuff.

Building a simple report – Using Report Data Provider

In my previous post, I explained how we can build a simple report using just the AOT queries. Now what if we have some logic that needs to be implemented and cannot be achieved using AOT queries?

This is where Report Data Providers plays a significant roles. Let us take the same example of displaying the Customer Id, Name and Balance that was used in post “Build and Deploy Simple Report–Queries”.

We can have this report using Report Data Providers as well. For this, we will keep the query and create three more artifacts, RDP, Report and a new Output menu item.

First we create a Report Data Provider class named “SKL_SampleReportDP”. Do the following:

To create an RDP for a report, we also need a temporary table (if it is Base Provider) or a permanent table (if it is pre process provider).

For this sample, we will use CustTmpAccountSum table that is present in base product.

Here are the class methods

/// <summary>

/// The <c>SKL_SampleReportDP</c> class is the report data provider class for the

/// SKL_SampleSimpleReportQuery report.

/// </summary>

/// <remarks>

/// This is a sample class. Author: Sumit Loya

/// </remarks>

[ SRSReportQueryAttribute (querystr(SKL_SampleCustomer))]

class SKL_SampleReportDP extends SRSReportDataProviderBase

{

    CustTmpAccountSum   tmpAccountSum;

}

 

The class declaration contains one attribute “SRSReportQueryAttribute”. This attribute specifies the query that will be used for this report. In case no query is required, this attribute can be removed.

There is one other attribute that can be specified on the RDP class and that is SRSReportParameterAttribute. This attribute defines the contract class that will be used to display report parameters.

processReport method

The processReport method is the entry point for calculating the report data for dataset. Here is the method for our sample class

[SysEntryPointAttribute(false)]

public void processReport()

{

    this.insertTmpAccountSum();

}

 

insertTmpAccountSum method

This is a private method that uses the report query to insert data into the temporary table

/// <summary>

/// This method processes the report query and inserts data into the CustTmpAccountSum temporary table

/// </summary>

private void insertTmpAccountSum()

{

    QueryRun            queryRun = new QueryRun(this.parmQuery());

    CustTable           custTable;

   

    while (queryRun.next())

    {

        custTable = queryRun.get(tableNum(custTable));

       

        tmpAccountSum.AccountNum    = custTable.AccountNum;

        tmpAccountSum.Txt           = custTable.name();

        tmpAccountSum.Balance01     = custTable.openBalanceMST();

        tmpAccountSum.insert();

    }

}

 

getCustTmpAccountSum method

This method is mandatory as it returns the table buffer that contains the processed report data. The Dataset uses this buffer to bind the table to the dataset.

/// <summary>

/// This method returns the table buffer that contains processed data

/// </summary>

[SRSReportDataSetAttribute(tableStr(CustTmpAccountSum))]

public CustTmpAccountSum getCustTmpAccountSum()

{

    select * from tmpAccountSum;

 

    return tmpAccountSum;

}

 

After creating the class, go ahead and add a new report the existing report model.

  • Open the previously created Report Model

image

  • Right click on report model, select Add –> Report. Name the report SKL_SampleReportDP
  • Now in the report, go to the Datasets section and create new Dataset using the name CustomerDP
  • The parameters for the new Dataset should be as shown below. The Data Source Type is “Report Data Provider”

image

  • In the query property, click the button image_thumb6
  • This opens a box to select a data provider class
  • Select the class we created before, SKL_SampleReportDP and click “Next”

image

  • In the next tab, Deselect all fields and just select “AccountNum, Balance01 and Txt” fields
  • Click Ok

image

  • This is how the data set looks like

image

  • Now create a new Auto Design as before and name it as CustDP, Select a Layout Template for the report design in Parameters window
  • Now right click on newly created design select “Add” and “Table”
    • Set following properties
      • Name – CustDP
      • Dataset – CustomerDP
      • Style Template – TableStyleAlternatingRowsTemplate
  • You will also notice that the fields are included automatically from Dataset to the “Data” node of the table

image

  • Now right click on the report model and select option “Add SKL_SampleReportProject to AOT”
  • Once the report is created back to AOT, go to AX, find the report in AOT –> SSRS Reports –> Reports, right click and deploy using “Deploy Element” option
  • Once the deployment is successful, create an output type menu item for the report
  • Go to AOT –> Menu Items –> Output, Right click and select “New Menu Item”. Set following properties

image

Now using menu item open and run report. You will notice the same dialog

image

The report looks like as shown below:

image

Getting Individual Dimension Combination Values–Using Views [AX 2012]

In my previous post, I had illustrated how we can get individual dimension combination values using DimensionStorage class. In this post, I will provide another job that utilizes a view to get individual values for a ledger dimension.

The view that has been used in DimensionAttributeLevelValueAllView. Here is the job

static void sgxIndividualDimensionValuesUsingView(Args _args)

{

    GeneralJournalAccountEntry          generalJournalAccountEntry; //Table that stores ledger transactions

    DimensionAttributeLevelValueAllView dimAttrView; //View that will display all values for ledger dimensions

    DimensionAttribute                  dimAttr; //Main dimension attribute table

    int i;

   

    setPrefix(‘Ledger dimension breakup’);

    while select generalJournalAccountEntry

    {

        i++;

        if (i > 100)

            break;

       

        setPrefix(int2str(i) + “. “ + DimensionAttributeValueCombination::find(generalJournalAccountEntry.LedgerDimension).DisplayValue);

        while select DisplayValue from dimAttrView   

            where dimAttrView.ValueCombinationRecId == generalJournalAccountEntry.LedgerDimension

            join BackingEntityType from dimAttr

                where dimAttr.RecId == dimAttrView.DimensionAttribute

        {

            switch (dimAttr.BackingEntityType)

            {

                case tableNum(DimAttributeMainAccount):

                    info(strFmt(“Main Account: %1”, dimAttrView.DisplayValue));

                    break;

               

                case tableNum(DimAttributeOMBusinessUnit):

                    info(strFmt(“Business Unit: %1”, dimAttrView.DisplayValue));

                    break;

 

                case tableNum(DimAttributeCustTable):

                    info(strFmt(“Customer: %1”, dimAttrView.DisplayValue));

                    break;

 

                case tableNum(DimAttributeOMDepartment):

                    info(strFmt(“Department: %1”, dimAttrView.DisplayValue));

                    break;

 

                case tableNum(DimAttributeHcmWorker):

                    info(strFmt(“Worker: %1”, dimAttrView.DisplayValue));

                    break;

            }   

        }

    }

   

}

 

Here is the output.

image

AOT Resources in Image Options [AX 2012]

Till AX 2009 there was only one way for us to use the AOT resources for specifying on menus or menu items or buttons and that was using SysResources class and call while application start to set in various places. But with this small change in Ax 2012, MS has provided a very good small change to use AOT resources in these places.

MS has provided a drop-down ImageLocation and added an option “AOT Resource”. Once we select this option, we can specify the resource name that needs to be used.

Check this out

image

 

image

Nice little change MS.

Replacing Financial Dimension in Ledger Dimension–Version 2 [AX 2012]

Friends, I had posted one article on replacing financial dimensions within a ledger dimension sometime back. The job there was big and endless. I did some more research and was able to condense it a bit.

Here is the modified job:

static void replaceLedgerDimensions1(Args _args)

{

    #LedgerSHA1Hash

    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

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

    DimensionAttributeValue         dimAttrValue; // Contains used financial dimension values

    DimensionAttributeValueSet      dimAttrValueSet; //Contains default dimension records

    DimensionAttributeValueSetItem  dimAttrValueSetItem; //Contains individual records for default dimensions

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

    DimensionDefault                sourceDefDimension, targetDefDimension; //To hold the default dimension combination from a Ledger dimension

    LedgerDimensionDefaultAccount   defaultLedgerAccount;

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

   

    int     dimAttrCount, i;

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

    ;

   

    //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

    {

        dimAttrCount++;

        dimAttrIdx.insert(dimAttr.BackingEntityType, dimAttrCount);

    }

 

    //initialize hash key array to null

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

    {

        valueKeyHashArray[i] = emptyGuid();

    }

   

    // Get the default dimensions from Ledger dimensions

    sourceDefDimension      = DimensionStorage::getDefaultDimensionFromLedgerDimension(sourceDimension);

   

    //Get the default account from Ledger dimensions

    defaultLedgerAccount    = DimensionStorage::getLedgerDefaultAccountFromLedgerDim(sourceDimension);

   

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

    dimAttr.clear();

    select firstOnly dimAttr

        where dimAttr.BackingEntityType == emplBackEntityType

           && dimAttr.Type  != DimensionAttributeType::DynamicAccount;

 

    //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);

   

    //Store the required combination hash keys

    valueKeyHashArray[dimAttrIdx.lookup(emplBackEntityType)] = dimAttrValue.HashKey;

 

    //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)

    {

        ttsbegin;

 

        // Insert the value set with appropriate hash

        dimAttrValueSet.Hash = hash;

        dimAttrValueSet.insert();

 

        //Insert Employee dimension set item

        dimAttrValueSetItem.clear();

        dimAttrValueSetItem.DimensionAttributeValueSet = dimAttrValueSet.RecId;

        dimAttrValueSetItem.DimensionAttributeValue = dimAttrValue.RecId;

        dimAttrValueSetItem.DisplayValue = dimAttrWorker.Value;

        dimAttrValueSetItem.insert();

 

        ttscommit;

    }

   

    //Replace the value in default dimension

    targetDefDimension = DimensionDefaultingService::serviceReplaceAttributeValue(sourceDefDimension, dimAttrValueSet.RecId, dimAttr.RecId);

   

    //Combine the target default dimension, default ledger account to get the resultant ledger dimension

    targetDimension = DimensionDefaultingService::serviceCreateLedgerDimension(defaultLedgerAccount, targetDefDimension);

   

    info(strFmt("Before: %1", DimensionAttributeValueCombination::find(sourceDimension).DisplayValue));

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

}

 

image

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)

{

    #LedgerSHA1Hash

    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

    {

        dimAttrCount++;

        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.clear();

    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)

    {

        ttsbegin;

 

        // Insert the value set with appropriate hash

        dimAttrValueSet.Hash = hash;

        dimAttrValueSet.insert();

 

        // Insert only specified set items use this

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

        {

            if (valueKeyArray[i] != 0)

            {

                dimAttrValueSetItem.clear();

                dimAttrValueSetItem.DimensionAttributeValueSet = dimAttrValueSet.RecId;

                dimAttrValueSetItem.DimensionAttributeValue = valueKeyArray[i];

                dimAttrValueSetItem.DisplayValue = valueStrArray[i];

                dimAttrValueSetItem.insert();

            }

        }

 

        ttscommit;

    }

 

    // Get the default account for main account

    mainAccDimension = DimensionStorage::getDefaultAccountForMainAccountNum(mainAccountValue);

 

    //Find or create the LedgerDimension record for required combination

    ledgerDimension = DimensionDefaultingService::serviceCreateLedgerDimension(

                                                            mainAccDimension,

                                                            dimAttrValueSet.RecId);

 

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

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

 

}

 

Here is the output:

image

Replace a Financial Dimension in Default Dimensions [AX 2012]

In my earlier post, I had explained how we can set financial dimensions. In this post, I will provide a job by which we can replace one attribute value in a combination of financial dimensions.

The class that we can use is the DimensionDefaultingService and method to use is “serviceReplaceAttributeValue”.

For this purpose, we will use the following combination taken from a customer record.

Fin1

In this financial dimensions, we will replace value if worker from “114” to “51011”.

The job below will be helpful for you to see how this can be achieved.

static void replaceDefaultDimensions(Args _args)

{

    CustTable                       custTable = CustTable::find(‘CUS-00004’); //Customer Record containing Financial Dimension

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

    DimensionAttribute              dimAttr; // Contains the financial dimensions records

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

    DimensionDefault                defaultDimension;

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

    DimensionAttributeValue         dimAttrValue; // Contains used financial dimension values

    DimensionAttributeValueSet      dimAttrValueSet; //Contains default dimension records

    DimensionAttributeValueSetItem  dimAttrValueSetItem; //Contains individual records for default dimensions

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

 

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

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

    int     dimAttrCount, i;

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

 

    //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

    {

        dimAttrCount++;

        dimAttrIdx.insert(dimAttr.BackingEntityType, dimAttrCount);

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

    }

 

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

    select firstOnly dimAttrWorker

        where dimAttrWorker.Value == "51011";

 

    //initialize hash key array to null

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

        valueKeyHashArray[i] = emptyGuid();

 

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

    dimAttr.clear();

    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;

 

    //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)

    {

        ttsbegin;

 

        // Insert the value set with appropriate hash

        dimAttrValueSet.Hash = hash;

        dimAttrValueSet.insert();

 

        // Insert only specified set items use this

        dimAttrValueSetItem.clear();

        dimAttrValueSetItem.DimensionAttributeValueSet = dimAttrValueSet.RecId;

        dimAttrValueSetItem.DimensionAttributeValue = dimAttrValue.RecId;

        dimAttrValueSetItem.DisplayValue = dimAttrWorker.Value;

        dimAttrValueSetItem.insert();

 

        ttscommit;

    }

 

    defaultDimension = DimensionDefaultingService::serviceReplaceAttributeValue(custTable.DefaultDimension, dimAttrValueSet.RecId, dimAttr.RecId);

 

    ttsBegin;

    custTable.selectForUpdate(true);

    custTable.DefaultDimension = defaultDimension;

    custTable.doUpdate();

    ttsCommit;

}

 

This is the output after running this job.

Fin2