Saturday, 15 June 2013

AX2012 R2 : Creating product variants based on generic product model using X++

Hi Friends,
In one of my recent tasks, I was required to automatically create the product variants for a product master as per all the suggested product variants by the system based on generic product model. So I need to automate the below shown process which if done manually for all items will consume lot of time:

These variant suggestions are based on the defined product variants ( refer to my previous post to get technical understanding of tables storing this information here).

So I did some technical analysis and found the following:
  • The class used by standard AX to create variant suggestion is EcoResProductVariantCreationMgr.
  • It populates a temperory table to store all the variant suggestions, but by default is keeps the selected field as false.
  • The function filling this temperoary table is a buildVariantSuggestion() is a protected function, so I cannot call it directly from my code.
So there were few challenges in directly using this class from my job but with few simple customizations in this class, I was able to utilize this class for creating the product variants in a much simple way rather then re writing all the logic inside the class in my job. So I did the following changes:
1. Added a new boolean variable in class declartion and created a parm method for it. This is to initialize this variable when it is called from my custom code and tweak the way temperory table is created:
2. Changed to access modifier of the buildVariantSuggestion() 

3. In case the class is called from custom code then we need to mark the records as selected:

So with the above three modifications in standard AX class, I was ready to use this class in my job and I created the below sampe piece of job:

We assigned the EcoResProductMaster record in the args and used that to initialize the class, used the RecId of the productMaster to find the record.
Then we called the parmCalledFromJob method and set it as true to tell system that this class is trigerred from my custom code.
We called the buildVariantSuggestions() , as this is no longer protected I can directly call from my job [This design can be improved by creating a child class instead of changing the access modifier].

So now I ran the job  for the same product which I have shown in the first screenshot:

and then when we look at product variants, we see that system has created the product variants :)

P.S --> Before modifying EcoResProductVariantCreationMgr class, please check if this is not conflicting with any other modifications in your system. Especially modifiyng the access modifier is not a recommened thing to do always, however I did this as it was a one time job which we need to run and then reverted back the changes.

Sometimes it is much easy to tweak the standard AX objects and leverage the existing code instead of rewriting the whole logic.

Till my next post.....take care friends and keep sharing.

Friday, 7 June 2013

AX2012 R2 : Understanding technical design of product dimensions data


In this blog we will take a dive into tables storing product dimension data for product master and also look at the code which we can use to generate this data automatically.
Standard Microsoft Dynamics AX 2012 R2 provide the following product dimensions: Configuration, Size, Color, Style. For each dimension there is a master table as shown below:

Notice that each master table has only one field called "Name" and each master table has unique index defined on this field.

So the above table hold the master data for the product dimensions in the system.

Now there are another set of 4 table which store the product dimension data for a product master. Shown below:

The above 8 tables can be seen as datasources on EcoResProductMasterDimension form where we define the product dimension data for product master.

For understanding purposes let us focus on configuration dimension and try to explore EcoResProductMasterConfiguration table.

If we notice the fields and relations on this table, we see it is storing the following references in each record:

  • ConfigProductMaster --> Stores RecId of product master
  • Configuration --> Stores the RecId of EcoResConfiguration ( This is the master table configurations)
  • ConfigProductDimensionAttribute --> Stores RecId of dimension attribute [Let's park this field for now , we'll come to it later]
So now once we have understood how the tables are related, let's see the X++ code which we can create product dimensions for a product master by pulling data from the master tables.
[P.S --> Currently we are focussing only on product dimension configuration]
So now let's understand the above code: The logic is pretty simple, we just need to know the recId of product master and the dimension for which we are going to create the product dimensions [In this case Configuration]. Loop through the ecoResConfiguration table and insert record for Product master.
But the interesting thing to notice is, the way we have initialized ConfigProductDimensionAttribute. This field stores refRecId.  The same code can be found on the standard AX form where we create product dimensions for product master, shown below:
So to understand this field ( which we parked earlier), let's take a closer look at the table EcoResProductDimensionAttribute
We will see this table only holds a single field DimensionTableId which is actually a RefTableId.
On closer look if we look in the table browser, the records in this table store the tableId of the dimension master tables [EcoResSize, EcoResConfiguration, EcoResColor, EcoResStyle] , see below the example for EcoResConfiguration:

Well doesn't it looks odd to have table Id stored in a table, and in order to get the recId of that record we are using a static function
EcoResProductDimensionAttribute::inventDimFieldId2DimensionAttributeRecId(fieldNum(InventDim, ConfigId));
in which we pass the fieldNum of ConfigId field of InventDim Table !!!!!!! 
The answer lies in the static function itself, see below the function:
The tableId is being determined by the configId fieldNum of InventDim table. What I think is that Microsoft wants to make sure that we do not use these tables for storing incorrect data and we can refer to them from dimension fieldID's itself. I see this might provide a tight binding of data model from code for storing dimensions. No one really wants to mess them ;)
So now show time, let's create a product master with attach a product dimension with only color enabled in it.

So now once the product master is created, let's see what are the colors stored in EcoResColor table:
To do this we can view details of colors available from the form where we define color dimensions for product master
So now we need a programme define all these colors for this product master, let's use the code I mentioned above but with the tables of color dimensions this time: so let's execute the code: [ notice this time we have used tables storing colorId]
Once the code is executed and now let go to product dimension form for the product master and see the color are defined for the product master:
So the above code can be useful in implementing product master for companies which have a common dimension master data which they want to assign to all products.
In my next blog I'll continue to explain on how we can create released product variants from the product dimension data defined for product master.
Till then take care friends and keep sharing!!!!