Hi Friends,
Solugenix Corporation is involved in developing some easy to use mobile applications for AX. For more details, check this link out:
Hi Friends,
Solugenix Corporation is involved in developing some easy to use mobile applications for AX. For more details, check this link out:
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));
}
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:
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:
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.
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.
In my earlier post, I had described a way to default financial dimensions through code. In this post, I will describe about the way we can default Ledger Dimensions (DimensionAttributeValueCombination) through code.
We have seen how we can fetch the values from a dimension combination in my post here. But what if we have to default them using code.
Say you are creating a general journal and want to default the Account and Offset account fields. These fields have been changed to segmented controls and now store RecIds for DimensionAttributeValueCombination table.
The job below will help you in doing that.
Below are the screen shots of dimensions for the two record ids used below:
static void setLedgerDimensions(Args _args)
{
LedgerDimensionAccount ledgerDimension; // Record id for LedgerDimension(DimensionAttributeValueCombination) containing combination of dimensions
LedgerDimensionAccount mainAccDimension; // Record id for LedgerDimension(DimensionAttributeValueCombination) containing default account for main account RecId
RefRecId emplDimAttrRecId = 5637147951; // For ex. purpose defaulting it to required DimensionAttributeValueSet RecordId
RefRecId dimensionRecId = 5637145941; // For ex. purpose defaulting it to required DimensionAttributeValueSet RecordId
;
/*
* For information on finding the record ids for required dimension combinations
* Go through the following blog
* http://sumitsaxfactor.wordpress.com/2011/12/28/defaulting-financial-dimensions-ax-2012/
*/
// Get the default account for main account 110154
mainAccDimension = DimensionStorage::getDefaultAccountForMainAccountNum("110154");
//Find or create the LedgerDimension record for required combination
//Param1 – Ledger Dimension record id, in our case Default account for main account
//Param2 – Default Dimension Record Id for 1st Dimension Combination
//Param3 – Default Dimension Record Id for 2nd Dimension Combination
//Param4 – Default Dimension Record Id for 3rd Dimension Combination
ledgerDimension = DimensionDefaultingService::serviceCreateLedgerDimension(
mainAccDimension,
dimensionRecId,
emplDimAttrRecId);
info(strFmt("%1: %2", ledgerDimension, DimensionAttributeValueCombination::find(ledgerDimension).DisplayValue));
}
This is the output
Friends I am updating the post to illustrate how we can default dimensions in case the account is not a ledger account.
In the job above, you can use the method getDynamicAccount instead of getDefaultAccountForMainAccount and get non-ledger account ledger dimensions. Here is a small job that shows the same.
static void getNonLedgerAccounts(Args _args)
{
LedgerDimensionAccount ledgerDim;
ledgerDim = DimensionStorage::getDynamicAccount(‘VEN-00273′, LedgerJournalACType::Vend);
info(strFmt("%1 -%2", ledgerDim, DimensionAttributeValueCombination::find(ledgerDim).DisplayValue));
}
Next in the series of posts on Ledger dimensions and Financial dimensions is defaulting or setting the financial dimensions for any record. Suppose you have a requirement wherein you need to create a customer via code and default specific dimension (say Employee) to this record.
In AX 2012 dimensions are not directly attached but the combination Record Id is stored. The name generally is DefaultDimension.
This field points to a record in DimensionAttributeValueSet table. This table holds the combination of financial dimensions that a particular record is attached to. The combination is stored in DimensionAttributeValueSetItem table.
The job below will help you in defaulting a dimension: I have put in enough comments to make the job self explanatory. This job will help you find / create a dimension combination record and get the record id to set.
static void setDefaultFinancialDimension(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; //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
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);
//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();
//Find the Dimension attribute record for the dimension to work on
dimAttr.clear();
select firstonly dimAttr
where dimAttr.BackingEntityType == emplBackEntityType;
//Get the backing entity type for the dimension value to process
select firstOnly dimAttrWorker
where dimAttrWorker.Value == ’000038′;
//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();
/*
* This Piece of code is only meant for better understanding hence commented
* Use this code in case you have to handle more than one dimension
* For our example we have only employee type dimension hence we will not use this for loop
* Value key array would be the array of different dimension values
*/
// Insert only specified set items use this
/*for (i = 1; i <= dimAttrCount; i++)
{
if (valueKeyArray[i] != 0)
{
dimAttrValueSetItem.clear();
dimAttrValueSetItem.DimensionAttributeValueSet = valueSet.RecId;
dimAttrValueSetItem.DimensionAttributeValue = valueKeyArray[i];
dimAttrValueSetItem.DisplayValue = valueStrArray[i];
dimAttrValueSetItem.insert();
}
}*/
//Insert Employee dimension set item
dimAttrValueSetItem.clear();
dimAttrValueSetItem.DimensionAttributeValueSet = dimAttrValueSet.RecId;
dimAttrValueSetItem.DimensionAttributeValue = dimAttrValue.RecId;
dimAttrValueSetItem.DisplayValue = dimAttrWorker.Value;
dimAttrValueSetItem.insert();
ttscommit;
}
info(strFmt("%1", dimAttrValueSet.RecId));
}
In this post, I will be explaining the method to get individual values for each dimension combination that is created and stored.
Dimension combinations are stored are DimensionAttributeValueCombination class. But they are stored as a combination ex: (100010-AX-00001- – - -). How would you know the value in each combination belongs to what dimension?
The answer is through dimension storage class. This class is used to manipulate these combinations.
The job below helps you in finding out the required values. The job has lots of self explanatory comments.
static void getDimensionCombinationValues(Args _args)
{
// DimensionAttributeValueCombination stores the combinations of dimension values
// Any tables that uses dimension combinations for main account and dimensions
// Has a reference to this table’s recid
DimensionAttributeValueCombination dimAttrValueComb;
//GeneralJournalAccountEntry is one such tables that refrences DimensionAttributeValueCombination
GeneralJournalAccountEntry gjAccEntry;
// Class Dimension storage is used to store and manipulate the values of combination
DimensionStorage dimensionStorage;
// Class DimensionStorageSegment will get specfic segments based on hierarchies
DimensionStorageSegment segment;
int segmentCount, segmentIndex;
int hierarchyCount, hierarchyIndex;
str segmentName, segmentDescription;
SysDim segmentValue;
;
//Get one record for demo purpose
gjAccEntry = GeneralJournalAccountEntry::find(5637765403);
setPrefix("Dimension values fetching");
//Fetch the Value combination record
dimAttrValueComb = DimensionAttributeValueCombination::find(gjAccEntry.LedgerDimension);
setPrefix("Breakup for " + dimAttrValueComb.DisplayValue);
// Get dimension storage
dimensionStorage = DimensionStorage::findById(gjAccEntry.LedgerDimension);
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++)
{
setPrefix(strFmt("Hierarchy: %1", DimensionHierarchy::find(dimensionStorage.getHierarchyId(hierarchyIndex)).Name));
//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 segment name
segmentName = DimensionAttribute::find(DimensionAttributeValue::find(segment.parmDimensionAttributeValueId()).DimensionAttribute).Name;
//Get segment value (id of the dimension)
segmentValue = segment.parmDisplayValue();
//Get segment value name (Description for dimension)
segmentDescription = segment.getName();
info(strFmt("%1: %2, %3", segmentName, segmentValue, segmentDescription));
}
}
}
}
Here is a sample output after running the code:
Note: Hiearchies: CEEBD_Dept-CostCenter-Purpose and CorpShared_Dept-CostCenter-Purpose are child hierarchies of “Account structure”.
While working on a requirement for ledger amounts, I had to find out a way to filter the transactions for a specific dimension value of a specific dimension type; from ledger transactions of a main account.
Now had it been Ax 2009, it was pretty simple where you could provide a range on Dimensions[arrayIndex] field. But in Ax 2012 the dimensions on a transaction are always stored a combination value rather than a separate value.
While running the query, I found a support provided by MS where-in all the dimensions will be added as fields for a table having Ledger dimensions on run-time. Now if we were running a query manually, we will be able to specify the range manually as shown below:
For demo purpose, I am using GeneralJournalAccountEntry table that holds the amounts for transactions posted to a main account
Now try and add a range, when you select the drop-down on the Field column, you will notice new fields added to the drop-down
The concept behind is that, these fields are added at run-time and when you add a range to any of these fields, a view (DimensionAttributeLevelValueView) will be dynamically added for each field range record you create, as a child DS for GeneralJournalAccountEntry
This is what the SQL statement looks like at the backend
SELECT * FROM GeneralJournalAccountEntry(GeneralJournalAccountEntry_1) JOIN * FROM DimensionAttributeLevelValueView(DimAttCol_GeneralJournalAccountEntry_1_LedgerDimension_5637145354) ON GeneralJournalAccountEntry.LedgerDimension = DimensionAttributeLevelValueView.ValueCombinationRecId AND ((DimensionAttribute = 5637145354)) AND ((DisplayValue = N’000001′))
Now when we are running queries manually, we can specify such kind of a range, what if we have to do the same thing while running a query at a backend or through a code?
Here comes the DimensionProvider class to our rescue. This class will help us add such ranges as required. Look at the sample job below for some guidance.
static void addDimensionRange(Args _args)
{
Query query = new Query();
QueryRun queryRun;
QueryBuildDataSource qbds;
DimensionProvider dimensionProvider = new DimensionProvider();
GeneralJournalAccountEntry accEntry;
DimensionAttribute dimAttr;
Name dimAttrNameEmpl, dimAttrNameCostCenter;
int i;
;
select firstOnly dimAttr where dimAttr.BackingEntityType == tableNum(DimAttributeHcmWorker);
dimAttrNameEmpl = dimAttr.Name;
select firstOnly dimAttr where dimAttr.BackingEntityType == tableNum(DimAttributeOMCostCenter);
dimAttrNameCostCenter = dimAttr.Name;
qbds = query.addDataSource(tableNum(GeneralJournalAccountEntry));
dimensionProvider.addAttributeRangeToQuery(query, qbds.name(), fieldStr(GeneralJournalAccountEntry, LedgerDimension), DimensionComponent::DimensionAttribute, SysQuery::valueNotEmptyString(), dimAttrNameEmpl, true);
dimensionProvider.addAttributeRangeToQuery(query, qbds.name(), fieldStr(GeneralJournalAccountEntry, LedgerDimension), DimensionComponent::DimensionAttribute, SysQuery::valueNotEmptyString(), dimAttrNameCostCenter, true);
queryRun = new QueryRun(query);
queryRun.prompt();
while(queryRun.next())
{
accEntry = queryRun.get(tableNum(GeneralJournalAccountEntry));
info(strFmt("%1 <–> %2", DimensionAttributeValueCombination::find(accEntry.LedgerDimension).DisplayValue, accEntry.AccountingCurrencyAmount));
}
}
Here is the sample output log
This is how the query will be if you prompt the query
This article focuses on getting the active dimensions for a Legal Entity. In Ax 2009, we could get the number of dimensions by using the enumCnt method on SysDimension enum and get the count. Here it is not that straight forward.
Following job will help you in getting the count and display their names;
static void getActiveFinancialDimensions(Args _args)
{
DimensionAttributeSetItem dimAttrSetItem; // Contains the number of dimensions active for a account structure ledger
DimensionAttribute dimAttr; // Contains the financial dimensions records
DimensionEnumeration dimensionSetId; //Record id for table that contains active dimensions for current ledger
int dimAttrCount;
//Get the record Id (dimension set id) for current ledger to find active dimensions
dimensionSetId = DimensionCache::getDimensionAttributeSetForLedger();
//Find the count of active dimensions for current ledger except main account
select count(RecId) from dimAttr
where dimAttr.Type != DimensionAttributeType::MainAccount
join RecId from dimAttrSetItem
where dimAttrSetItem.DimensionAttribute == dimAttr.RecId &&
dimAttrSetItem.DimensionAttributeSet == dimensionSetId;
info(strFmt("Total active financial dimensions for current legal entity: %1", dimAttr.RecId));
//Find all the active dimensions for current ledger except main account and display them
while select * from dimAttr
order by Name
where dimAttr.Type != DimensionAttributeType::MainAccount
join RecId from dimAttrSetItem
where dimAttrSetItem.DimensionAttribute == dimAttr.RecId &&
dimAttrSetItem.DimensionAttributeSet == dimensionSetId
{
info(dimAttr.Name);
}
}