Programmatically inserting into financial/default dimensions in X++ (D365O/AX7)
-
Programmatically inserting into financial/default dimensions in X++ (D365O/AX7)
Posted by bparker@czarnowski.com on March 16, 2017 at 4:17 pm-
Hi everybody. Google has failed me, so I thought I’d just ask somewhere. I am new (a couple of months) to D365O/AX, currently implementing (pre-rollout), learning X++ by trying to (re)write the integrations we have in our old ERP system. We are implementing Dynamics 365 For Operations, aka AX7. I have no experience with AX 2012, and limited experience with object oriented programming in general. So if anything sounds like I don’t know what I’m talking about… I probably don’t, please correct me!
One integration is a human resources system, and I’m trying to sync changes made there to Workers in D365O. (And the related Person/Party/Employment/etc records, it’s crazy, I’m touching 20-30 tables so far and I’m not done.) There are two pieces of data that are going to be stored in Default Dimensions or Financial Dimensions (not sure if those are synonyms, it seems they are) on the HcmWorker record.
Although there are a ton of blog posts with sample code about READING data from default dimensions, I have yet to find one that covers WRITING to them. I’ll need to be able to insert and update them. I’ve literally spent a couple of days trying to reverse engineer it or find utility methods, and it seems like I’d have to manually insert into at least four tables, with some hash value columns that I still don’t understand how to populate.
Are there any resources out there? I can’t believe there isn’t an easier way.
——————————
Brian Parker
Developer
Czarnowski
Pittsburgh PA
—————————— -
Matthew Hill
MemberMarch 17, 2017 at 10:46 AM
Brian,I would look here for any information related to D365fo / AX7.
Dynamics 365 for Operations Help Wiki – The source for help for Dynamics 365 for Operations
It is important to note that Microsoft is still locking down the platform so make sure you Extend instead of Overlay base objects. If you are just looking to integrate external systems with AX, I recommend checking out the Recurring Integrations in the Wiki.
Hope that helps!
Matt
——————————
Matthew Hill
Director of Business Solutions
CSG, a Columbus Company
Cleveland OH
——————————
——————————————- -
Hello Brian,
There is help available. Try msdn.microsoft.com. Search for Dynamics 365 and see if you can locate what you are looking for. In older versions, tablename.insert(), tablename.update(), etc. works. These are methods for the table to do specific things.
Look for best practices in coding as it will save you from a host of problems and headaches. (https://msdn.microsoft.com/en-us/library/gg509027.aspx – Best practices for developing with Microsoft Dynamics 365). Also, if you have a partner, see if they have training or search for training related to D365.
Be patient. And good luck. Keep us updated on how you are doing…
Dave Coombs
Development Services Manager
Harman Management Corp.
——Original Message——
Hi everybody. Google has failed me, so I thought I’d just ask somewhere. I am new (a couple of months) to D365O/AX, currently implementing (pre-rollout), learning X++ by trying to (re)write the integrations we have in our old ERP system. We are implementing Dynamics 365 For Operations, aka AX7. I have no experience with AX 2012, and limited experience with object oriented programming in general. So if anything sounds like I don’t know what I’m talking about… I probably don’t, please correct me!
One integration is a human resources system, and I’m trying to sync changes made there to Workers in D365O. (And the related Person/Party/Employment/etc records, it’s crazy, I’m touching 20-30 tables so far and I’m not done.) There are two pieces of data that are going to be stored in Default Dimensions or Financial Dimensions (not sure if those are synonyms, it seems they are) on the HcmWorker record.
Although there are a ton of blog posts with sample code about READING data from default dimensions, I have yet to find one that covers WRITING to them. I’ll need to be able to insert and update them. I’ve literally spent a couple of days trying to reverse engineer it or find utility methods, and it seems like I’d have to manually insert into at least four tables, with some hash value columns that I still don’t understand how to populate.
Are there any resources out there? I can’t believe there isn’t an easier way.
——————————
Brian Parker
Developer
Czarnowski
Pittsburgh PA
—————————— -
THANK YOU!!! to everybody for your help, especially Steeve and Denis for the specific code samples. I’ve been busy with other parts of my job but will be circling back to this in the next day or so. I will try them out to make sure that I communicated what I am trying to do, and that these work as I need them to.
The way I was trying to do it was WAY worse and involved manually touching several tables. I am sure that I have written some other far less efficient code for this import routine! I have plenty to learn.
Thanks also for the best practices & wiki links… I was aware of them but hadn’t been able to find what I was looking for there. We have a partner (I pity anybody trying to implement without one) but although they were comfortable reading dimensions, writing them was novel to them.
Good advice on extending rather than customizing. We’ve been forced to do a couple of things as customizations, but I’m avoiding it as much as possible.
——————————
Brian Parker
Developer
Czarnowski
Pittsburgh PA
——————————
——————————————- -
Steeve Gilbert
MemberMarch 17, 2017 at 7:43 AM
Hi Brian,Don’t worry, I’m a long time Ax developer and that also took me a while to find/understand how to update this new Default Dimension. The first time I work with it was to update InventTable.DefaultDimension so I’ll use that as my example below. Here’s a static method I’ve build (with help of some blog can’t remember where) to simplify DefaultDimension modification.
/// <summary>
/// Modify a default dimension
/// </summary>
/// <param name = “defaultDimension”> RecId of an existing DefaultDimension (ex : InventTable.DefaultDimension) so
/// you can add or remove value to that DefaultDimension defaultDimension can be 0 if you want to start
/// a DefaultDimension from scratch</param>
/// <param name = “dimAttr”>RecId from DimensionAttribute.RecId of the attribute you want to change</param>
/// <param name = “dimValue”>Value to set for that DimensionAttribute</param>
/// <returns>RecId that you can save in your DefaultDimension field (like InventTable.DefaultDimension)</returns>
static RecId Write(RecId defaultDimension, RecId dimAttr, str dimValue)
{DimensionAttributeValueSetStorage dimStorage;DimensionAttribute dimAttribute;DimensionAttributeValue dimAttributeValue;//Find or create a storage dimension that will contain each dimension attribute and their value//”defaultDimension” is the RecId of an existing DefaultDimension (ex : InventTable.DefaultDimension) so// you can add or remove value to that DefaultDimension. defaultDimension can be 0 if you want to// create a DefaultDimension from scratchdimStorage = DimensionAttributeValueSetStorage::find(defaultDimension);if (dimValue){dimAttribute = DimensionAttribute::find(dimAttr);dimAttributeValue = DimensionAttributeValue::findByDimensionAttributeAndValue(dimAttribute, dimValue, true, true);dimStorage.addItem(dimAttributeValue);}else{//If there’s no value, that means we want to remove that Dimension Attribute
dimStorage.removeDimensionAttribute(dimAttr);}return dimStorage.save();}
So you can add multiple Dimension Attribute to DefaultDimension by calling this method for each Dimension Attribute and passing the DefaultDimension return from the first call onto the next call. The final DefaultDimension will contain all your DimensionAttribute. Or you can create a better method that handle that all for you. š
As for Financial Dimension, I think you refer to LedgerDimension. I haven’t played much with that. The only place i used that was to set a Ledger Account on a record like InventPosting.LedgerDimension. The code I used was that :
LedgerDefaultAccountHelper::getDefaultAccountFromMainAccountRecId(MainAccount::findByMainAccountId(mainAcc).RecId)
It returns the RecId you need to put in LedgerDimension if you want it to refer to the Main Account “mainAcc”.Hope that helps. Let us know if you have more questions.
——————————
Steeve Gilbert
Analyst Programmer
Boa-Franc S.E.N.C.
QC, Canada
——————————
——————————————- -
Turns out Steeve’s was really close to what I wanted, close enough for me to make the final leap. Presented below is my version with two small changes:
- Note in the “summary” about calling it, with sample syntax. You need to follow the call with an update to your table (as far as I could tell) for the change to stick. That threw me for a little while… please correct me if I’m mistaken or could handle it better.
- I wanted to be able to just feed the attribute name as a string, instead of looking up a RecID; that’s easier to use (for my purposes, at least).
/// <summary>
/// Generic dimension updater. Call this with the DefaultDimension of any table, and update that table's DefaultDimension with the returned value.
///
/// Sample usage:
/// hcmEmployment.DefaultDimension = xczDimensionHandler::PutDimension(hcmEmployment.DefaultDimension, 'BusinessUnit', '004');
/// hcmEmployment.validTimeStateUpdateMode(ValidTimeStateUpdate::Correction); // This may or may not be appropriate for your circumstances
/// hcmEmployment.update();
///
/// Based on sample code courtesy Steeve Gilbert.
/// </summary>
/// <param name = "_DimensionAttributeValueSet">Value in the "DefaultDimension" column of the linked table (i.e. HcmEmployee, etc.). Links to DimensionAttributeValueSet.RecId. You will need to update the calling table this came from.</param>
/// <param name="_attribute">Attribute (e.g. "Department").</param>
/// <param name="_value">Potential value for the Attribute (e.g. "950").</param>
public static RecId PutDimension (RecId _defaultDimension, str _attribute, str _value)
{
DimensionAttributeValueSetStorage dimStorage;
DimensionAttribute dimAttribute;
DimensionAttributeValue dimAttributeValue;
RecId dimAttr;
// Verify that the value passed for _attribute identifies a real dimension.
select firstonly dimAttribute
where (dimAttribute.Name == _attribute);
if (!dimAttribute)
{
throw error("Invalid _attribute passed to PutDimension: " + _attribute);
}
//Find or create a storage dimension that will contain each dimension attribute and their value
//"defaultDimension" is the RecId of an existing DefaultDimension (ex : InventTable.DefaultDimension) so
// you can add or remove value to that DefaultDimension. defaultDimension can be 0 if you want to
// create a DefaultDimension from scratch
dimStorage = DimensionAttributeValueSetStorage::find(_defaultDimension);
if (_value)
{
dimAttributeValue = DimensionAttributeValue::findByDimensionAttributeAndValue(dimAttribute, _value, true, true);
dimStorage.addItem(dimAttributeValue);
}
else
{
//If there's no value, that means we want to remove that Dimension Attribute
dimStorage.removeDimensionAttribute(dimAttribute.RecId);
}
return dimStorage.save();
}——————————
Brian Parker
Developer
Czarnowski
Pittsburgh PA
——————————
——————————————- -
Hmm. Clearly I could also use tips on formatting code for this forum.
——————————
Brian Parker
Developer
Czarnowski
Pittsburgh PA
——————————
——————————————- -
Denis Marcoux
MemberMarch 17, 2017 at 8:05 AM
Hi Brian,Creating a defautltDimension looks something like this. Parameter _value i a container that passes the dimension values into the method.
public static DimensionDefault createDefaultDimension(container _value, boolean _createIfNotFound = true)
#{
# DimensionAttributeValueSetStorage valueSetStorage = new DimensionAttributeValueSetStorage();
# DimensionDefault result;
# int i;
# DimensionAttribute dimensionAttribute;
# DimensionAttributeValue dimensionAttributeValue;
#
# container conAttr;
# container conValue = _value;
# str dimValue;
#
# String255 Dimension1Name = ‘Department’;
# String255 Dimension2Name = ‘BusinessUnit’;
# String255 Dimension3Name = ‘Project’;
#
# // Créer un container avec les noms de dimension
# conAttr = [strFmt(“%1”,Dimension1Name),
# strFmt(“%1”,Dimension2Name),
# strFmt(“%1”,Dimension3Name)
# ];
#
# for (i = 1; i <= conLen(conAttr); i++)
# {
# dimensionAttribute = dimensionAttribute::findByName(conPeek(conAttr,i));
#
# if (dimensionAttribute.RecId == 0)
# {
# continue;
# }
#
# dimValue = conPeek(conValue,i);
#
# if (dimValue != “”)
# {
# // _createIfNotFound is “true”. A dimensionAttributeValue record will be created if not found.
# dimensionAttributeValue =
# dimensionAttributeValue::findByDimensionAttributeAndValue(dimensionAttribute,dimValue,false,_createIfNotFound);
#
# // Add the dimensionAttibuteValue to the default dimension
# valueSetStorage.addItem(dimensionAttributeValue);
# }
# }
# result = valueSetStorage.save();
# return result;
#}——————————
Denis Marcoux
Developer
Grics
Montreal QC
——————————
——————————————-
bparker@czarnowski.com replied 8 years, 6 months ago 1 Member · 0 Replies -
-
0 Replies
Sorry, there were no replies found.
The discussion ‘Programmatically inserting into financial/default dimensions in X++ (D365O/AX7)’ is closed to new replies.