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.
Hi Sumit.
May be I have found a “simpler” way to set/change the value.
In the followinf code I set a new dimension value (Subchannel) that I have mapped on Subsegment table (i.e. I use Subchannel in spite of worker);
The code seems to me simpler because I use the AxdDimensionUtil class
static void Giancarlo_SettaDimSuClienti(Args _args)
{
void ReplaceSubchannelOnCustomer(AccountNum numCliente) {
CustTable custTable;
Struct struct = new Struct();
container ledgerDimension;
DimensionAttribute dimSubchannelName; // fine the name of the dimension to set
DimAttributeSubchannel subChannelValue; // used only
DimensionDefault newDimDefault;
DimensionAttributeValueSet valSet;
DimensionAttributeValueSetItem valSetItem;
DimensionAttributeValue value;
DimensionAttribute nomeDim;
int i;
;
custTable = custTable::find(numCliente, true);
select firstOnly * from dimSubchannelName where dimSubchannelName.BackingEntityType == tableNum(DimAttributeSubchannel);
select firstOnly * from subChannelValue where subChannelValue.Value == custTable.SubsegmentId;
if (custTable.SubsegmentId == “” ) {
throw error(strFmt(“Error: Subsegment/Subchannel not setted on Customer %1”,custTable.AccountNum));
} else if (subChannelValue.RecId == 0) {
throw error(strFmt(“Error: Subsegment/Subchannel %1 on Customer %2 does not exists”,custTable.SubsegmentId,custTable.AccountNum));
return;
}
select firstOnly * from valSet where valSet.RecId == custTable.DefaultDimension; // Get Customer dimension
// deposit on struct the dimensions values
while select * from valSetItem where valSetItem.DimensionAttributeValueSet == valSet.RecId
join value where value.RecId == valSetItem.DimensionAttributeValue
join nomeDim where nomeDim.RecId == value.DimensionAttribute
struct.add(nomeDim.Name,valSetItem.DisplayValue); // Accumulate Values
//
// Replace or add the dimension value
//
if(struct.exists(dimSubchannelName.Name))
struct.remove(dimSubchannelName.Name); // if exist, replace (remove + add
struct.add(dimSubchannelName.Name, custTable.SubsegmentId);
//
// Fill the container structure
//
i = struct.fields();
ledgerDimension += struct.fields();
while (i >0) {
ledgerDimension += struct.fieldName(i);
ledgerDimension += struct.valueIndex(i);
i–;
}
ttsBegin;
//replace the values
newDimDefault = AxdDimensionUtil::getDimensionAttributeValueSetId(ledgerDimension);
if (custTable.DefaultDimension != newDimDefault) {
custTable.DefaultDimension = newDimDefault;
custTable.update();
ttsCommit;
} else
ttsAbort;
}
//ReplaceSubchannelOnCustomer(‘CE000115-ESTE’);
//ReplaceSubchannelOnCustomer(‘CI003708’);
ReplaceSubchannelOnCustomer(‘CI016269-ITAL’);
}
LikeLike
I have read your post, but I am rather surprised that Microsoft has chosen such an inferior replacement for dimensions in AX2009. From what I can tell, the following AX 2009 lines
ProjJournalTrans.clear();
ProjJournalTrans.dimension[2] = “Dept01”;
need to be replaced by nearly 100 lines of code in AX 2012, including 5 database reads/writes and two transactions. Or am I missing something?
LikeLike
Well I have checked some the service classes related to dimensions and other framework classes. I could find these methods only. So yes to replace or default one value, I see we need to write some good amount of code.
LikeLike
Thanks a lot for this very helpful code.
Regards,
Vishal Kohli
LikeLike
Hi,
Iam going to prepare production environment, from test environment i have to delete all transactions and have to make Main Accounts values to zero.
I am facing challenge making Main account values to zero, please guide me how to make Accounts value to zero. Even after deleting all the transactions and data still accounts are holding values. Please guide.
LikeLike
i got solution below is the link
https://community.dynamics.com/product/ax/f/33/t/76118.aspx?wa=wsignin1.0
LikeLike
Hi guys
Here is a more simple code sample of how this can be done.
///
/// Sets a value in a default dimension field of a given record, i.e. salesLine, CustTable etc.
///
///
/// The defaultDimension field ffrom the existing record, i.e. SalesLine.DefaultDimension
///
///
/// The name of the dimension attribute to set the value for, i.e. if we want to set the value for Cost center it will be “CUST’
/// or the name of whatever the dimension attribute that is using CostCenter table as backing entity
///
///
/// The value we want to set the default dimension field to. Note that this
///
///
///
///
///
///
///
static public DimensionDefault setDefaultDimensionValue(DimensionDefault _fromDefaultDimension, Name _attributeName, Str _newDimAttributeValue)
{
DimensionAttributeValueSetStorage davsStorage = new DimensionAttributeValueSetStorage();
DimensionAttributeValue dav;
DimensionDefault toDefaultDimension;
Map dimensionSpecifiers = DimensionDefaultingEngine::getDefaultDimensionSpecifiers(_fromDefaultDimension);
MapEnumerator enumerator;
DimensionAttribute DimensionAttribute;
//Add current dimension attribute values
enumerator = dimensionSpecifiers.getEnumerator();
while (enumerator.moveNext())
{
DimensionAttribute = DimensionAttribute::find(enumerator.currentKey());
dav = DimensionAttributeValue::findByDimensionAttributeAndValue(DimensionAttribute::findByName(DimensionAttribute.Name), enumerator.currentValue());
davsStorage.addItem(dav);
}
//Add the extra dimension attribute values
dav = DimensionAttributeValue::findByDimensionAttributeAndValue(DimensionAttribute::findByName(_attributeName), _newDimAttributeValue, true,true);
davsStorage.addItem(dav); // Replaces since attribute already exists with different value
toDefaultDimension = davsStorage.save();
return toDefaultDimension;
}
LikeLike
Hi Carsten, very useful, many thanks, Martin
LikeLike