Hi There,
I hope everyone had a great new year and that you are ready for a new one full of challenges, and good things.
In this post I would like to discuss how to create product and product masters in AX 2012. Sometimes our requirements vary depending on the customer, and more often than not, we need to provide a dynamic option to create either a product or a product master.
So, what is the definition of these two terms?
Product: This is the simpler of the two. They do not need product dimension groups when created.
Product Master: This type must contain product dimension groups; otherwise you’ll get a run-time error at the time the EcoResService is called.
The product dimension groups can be Color, Size, Style and Configuration. In addition, you can create any other dimension group. In my example, a user created a dimension group called “Revision”, which is the chosen one for this example. This shows how flexible AX2012 is around product dimensions.
The following code takes two parameters. One is the product name (which has a custom EDT), and the product sub type (is it product or product master). Further, to achieve this we will use the following classes:
EcoResEcoResProduct_TrackingDimGroup
EcoResEcoResProduct_Product_Master
EcoResEcoResProduct_Product_Distinct
EcoResEcoResProduct_ProductDimGroup
EcoResEcoResProduct_StorageDimGroup
EcoResEcoResProduct_translation
EcoResEcoResProduct_Identifier
EcoResProductService
The EcoResEcoResProduct_Product_Master (http://msdn.microsoft.com/en-us/library/ecoresecoresproduct_product_master.aspx) class is in charge of creating the new Product Master, and the EcoResEcoResProduct_Product_Distinct (http://msdn.microsoft.com/en-us/library/ecoresecoresproduct_product_distinct.aspx) class is in charge of creating the Product. These records are going to be created in the EcoResProduct Table.
As you probably know by now, even these records exist in the EcoResProduct table, they haven’t been release to AX just yet.
Moving right along, we will be using the EcoResProductService class (http://msdn.microsoft.com/en-us/library/ecoresproductservice.aspx) to actually create the product in the EcoResProduct Table.
/// <summary>
/// Creates a new product in EcoResProduct
/// </summary>
/// <returns>
///
/// </returns>
/// <remarks>
///
/// </remarks>
/// <exception cref="Exception::Error">
/// Throws error upon Exception.
/// </exception>
public static void CreateEcoResProduct(ProductName _ecmProductName, EcoResProductSubtype prodSubType)
{
EcoResEcoResProduct_TrackingDimGroup tracDimGroup;
EcoResEcoResProduct_Product_Master productMaster;
EcoResEcoResProduct_Product_Distinct distMaster;
EcoResEcoResProduct_ProductDimGroup prodDimGroup;
EcoResEcoResProduct_StorageDimGroup storDimGroup;
EcoResEcoResProduct_translation translation;
EcoResEcoResProduct_Identifier identifier;
EcoResProductService ecoProdSvc;
EcoResProductNumber lEcoResProductNumber;
NumberSequenceTable numberSequenceTable;
EcoResEcoResProduct ecoResProd;
EcoResProductType ecoResProductType;
boolean isMaster = false;
;
try
{
// create product by initializing the Service object
ecoProdSvc = EcoResProductService::construct();
// initialize the EcoResEcoResProduct object
ecoResProd = new EcoResEcoResProduct();
numberSequenceTable = EcoResProductParameters::numRefProductNumber().numberSequenceTable();
lEcoResProductNumber = NumberSeq::newGetNumFromId(numberSequenceTable.RecId).num();
ecoResProductType = EcoResProductType::Item;
if(prodSubType == EcoResProductSubtype::ProductMaster)
{
isMaster = true;
//Create a new product master
productMaster = new EcoResEcoResProduct_Product_Master();
//initialize product master
productMaster.parmDisplayProductNumber(lEcoResProductNumber);
productMaster.parmProductType(ecoResProductType);
productMaster.parmSearchName(_ecmProductName);
productMaster.parmVariantConfigurationTechnology(EcoResVariantConfigurationTechnologyType::PredefinedVariants);
//create a product master Translation Object
translation = productMaster.createTranslation().addNew();
// create a new identifier object
Identifier = productMaster.createIdentifier().AddNew();
// Create the ProductDimensionGroup
prodDimGroup = productMaster.createProductDimGroup().addNew();
prodDimGroup.parmProduct(lEcoResProductNumber);
prodDimGroup.parmProductDimensionGroup('Revision');
// Create the StorageDimgroup object
storDimGroup = productMaster.createStorageDimGroup().addNew();
storDimGroup.parmProduct(lEcoResProductNumber);
storDimGroup.parmStorageDimensionGroup("S-W-L");
// Create the TrackingDimGroup object
tracDimGroup = productMaster.createTrackingDimGroup().addNew();
tracDimGroup.parmProduct(lEcoResProductNumber);
tracDimGroup.parmTrackingDimensionGroup("none");
}
else
{
// Create a new product distinct master
distMaster = new EcoResEcoResProduct_Product_Distinct();
// Take the newly created and initialize ProdMaster - variable and fill with product data
distMaster.parmDisplayProductNumber(lEcoResProductNumber);
distMaster.parmProductType(ecoResProductType);
distMaster.parmSearchName(_ecmProductName);
// Create a translation object
translation = distMaster.createTranslation().addNew();
// Create a new identifier object
Identifier = distMaster.createIdentifier().addNew();
// Create the StorageDimgroup object
storDimGroup = distMaster.createStorageDimGroup().addNew();
storDimGroup.parmProduct(lEcoResProductNumber);
storDimGroup.parmStorageDimensionGroup("S-W-L");
// Create the TrackingDimGroup object
tracDimGroup = distMaster.createTrackingDimGroup().addNew();
tracDimGroup.parmProduct(lEcoResProductNumber);
tracDimGroup.parmTrackingDimensionGroup("None");
}
// fill the translation object
translation.parmDescription(_ecmProductName);
translation.parmLanguageId('en-us');
//translati
translation.parmName(_ecmProductName);
// fill the identifier
identifier.parmProductNumber(lEcoResProductNumber);
// add the product to ecoResProd
if(isMaster)
ecoResProd.createProduct().add(productMaster);
else
ecoResProd.createProduct().add(distMaster);
// create the product using service
ecoProdSvc.create(ecoResProd);
}
catch(Exception::Error)
{
throw Exception::Error;
}
catch(Exception::Deadlock)
{
retry;
}
catch(Exception::UpdateConflict)
{
if(appl.ttsLevel() == 0)
{
if(xSession::currentRetryCount() >= 4)
{
throw Exception::UpdateConflictNotRecovered;
}
else
{
retry;
}
}
else
{
throw Exception::UpdateConflict;
}
}
}
Happy New Year and until the next time!
Thanks for the great information! I need to use a subset of this functionality -- basically I just need to retrieve the next product number from the number sequence and then return that number via an AIF service. I've got the service setup correctly, but whenever I call the service I get an AIFFault with the message "Number selection is canceled". Here is my code (mostly copied from yours):
ReplyDelete[SysEntryPointAttribute(true)]
public EcoResProductNumber getNextProductNumber()
{
EcoResProductNumber nextNumber;
NumberSequenceTable numberSequenceTable;
numberSequenceTable = EcoResProductParameters::numRefProductNumber().numberSequenceTable();
nextNumber = NumberSeq::newGetNumFromId(numberSequenceTable.RecId).num();
return nextNumber;
}
Your code looks ok.
DeleteTwo things:
1- Is your Product Sequence Number set correctly, you can check if the "Manual" check box is checked. If it is, uncheck it.
2- Does the service has enough rights to do this?
I hope this helps.
Thanks for the quick response! I verified that "Manual" is unchecked for my product sequence, but I'm not sure how to verify the permissions that my service has.
DeleteMy understanding is that if I'm not using a trusted intermediary and passing a context into my service call, the service will run in the context of the Windows user that makes the service call (please correct if I'm wrong). The user I'm currently using to make the service call is in the "System administrator" role, so my assumption is that I would have rights to do everything. Is there a specific permission that I would need to set in order to generate product numbers through code?
Thanks again for your help. I've been working on this on and off for several days and was very happy to find your blog and see that you're doing exactly what I need to do.
First, where are you calling this service from? Is it a C# application?
DeleteSecond, you said that the user you are using inherits the Sys Admin role, but have you tested this by you login in as admin and see what happens?
Third, the service will have to go through the AOS to authenticate you, if you exists as an user in AX can you double check that your user is admin?
Thanks
Finally figured it out. Since the number sequence was setup as Continuous, I apparently have to put a ttsbegin/ttscommit statement around my number generation code.
DeleteThanks for sharing the answer to your problem. And I'm glad you got it!
DeleteHow can I retrieve the product details(color, attribute, price) using c#?
ReplyDeleteThat is a good question. I have never done it, but you could do it by (1) creating a query service, (2) a service class that gets a product ID as parameter, and (3) that returns a data set to C#.
DeleteAs far as I know, the only service that will allow you to do this is a query service because it can be translated as a data set in C#. I hope this helps. It would be great if you could post your code here for us to learn ...
Thanks for reading my blog!