Thursday, January 3, 2013

Create Product / Product Masters AX 2012

 

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!




8 comments:

  1. 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):

    [SysEntryPointAttribute(true)]
    public EcoResProductNumber getNextProductNumber()
    {
    EcoResProductNumber nextNumber;
    NumberSequenceTable numberSequenceTable;

    numberSequenceTable = EcoResProductParameters::numRefProductNumber().numberSequenceTable();
    nextNumber = NumberSeq::newGetNumFromId(numberSequenceTable.RecId).num();

    return nextNumber;
    }

    ReplyDelete
    Replies
    1. Your code looks ok.

      Two 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.

      Delete
    2. 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.

      My 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.

      Delete
    3. First, where are you calling this service from? Is it a C# application?

      Second, 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

      Delete
    4. 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.

      Delete
    5. Thanks for sharing the answer to your problem. And I'm glad you got it!

      Delete
  2. How can I retrieve the product details(color, attribute, price) using c#?

    ReplyDelete
    Replies
    1. That 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#.

      As 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!

      Delete

Thank you for your thoughts. Your comment will appear in my blog shortly after review.

Have a great day!