Friday, April 25, 2014

AIF Document Services: Handling header-line relationships and custom operations

Using AIF document services to create data in AX is not a new thing. Its been around for a while now. Document services component of AX is easily the most used aspect when it comes to using AIF services with AX. For most of the basic AX tables, document services are shipped out of the box. For the custom tables that you create, you can easily create document services exposing the tables to the outside world. Create, Read, Update are commonly used service operations. Today i want to discuss how we can use these document services to create some data maintaining the header-line relations of the tables involved. We have two tables, MMSStagingImportTable the parent and MMSStagingStockImport the child and lets assume that we have a query with this relationship and all the Axd* and Ax* objects are already created in AX, port containing the service operations is activated and service reference is added in a .NET console application. For more info refer
http://technet.microsoft.com/en-us/library/aa856656.aspx

Below code is a function loadStock() in the .NET console application that shows how i am creating a header with 2 lines using the create service operation.

     static void loadStock()  
     {  
       // Instantiate an instance of the service client class.  
       MMSIntellipharmServiceClient client = new MMSIntellipharmServiceClient();  
       // Initizlise parameters used for the create method  
       CallContext context = new CallContext() { Company = "mms" };  
       // Create an instance of the document class.  
       AxdMMSIntellipharm stagingForm = new AxdMMSIntellipharm();  
       // Create instances of the entities that are used in the service and set the needed fields on those entities.  
       AxdEntity_MMSStagingImportTable stagingImportTable = new AxdEntity_MMSStagingImportTable();  
       stagingImportTable.ImportId = "IPH_1002";  
       stagingImportTable.ImportDate = new DateTime(2014, 4, 4);  
       stagingImportTable.Datasource = AxdEnum_MMSImportDatasource.IPh;  
       stagingImportTable.Type = AxdEnum_MMSImportType.Stock;  
       AxdEntity_MMSStagingStockImport stagingStockImport = new AxdEntity_MMSStagingStockImport();  
       stagingStockImport.Barcode = "535353";  
       stagingStockImport.Cost = 14;  
       stagingStockImport.IsGST = AxdExtType_MMSImportIsGST.Yes;  
       stagingStockImport.ListPrice = 23;  
       stagingStockImport.SellPrice = 24;  
       stagingStockImport.SOH = 50;  
       stagingStockImport.StockDate = new DateTime(2014, 3, 25);  
       stagingStockImport.StoreId = "1300";  
       stagingStockImport.StoreItemDesc = "desc";  
       stagingStockImport.StoreItemId = "555555";  
       AxdEntity_MMSStagingStockImport stagingStockImport2 = new AxdEntity_MMSStagingStockImport();  
       stagingStockImport2.Barcode = "768756";  
       stagingStockImport2.Cost = 13;  
       stagingStockImport2.IsGST = AxdExtType_MMSImportIsGST.Yes;  
       stagingStockImport2.ListPrice = 27;  
       stagingStockImport2.SellPrice = 26;  
       stagingStockImport2.SOH = 40;  
       stagingStockImport2.StockDate = new DateTime(2014, 3, 24);  
       stagingStockImport2.StoreId = "1301";  
       stagingStockImport2.StoreItemDesc = "desc";  
       stagingStockImport2.StoreItemId = "444444";  
       stagingImportTable.MMSStagingStockImport = new AxdEntity_MMSStagingStockImport[2] { stagingStockImport, stagingStockImport2 };  
       stagingForm.MMSStagingImportTable = new AxdEntity_MMSStagingImportTable[1] { stagingImportTable };  
       // Call the create method on the service passing in the document.  
       EntityKey[] keys = client.create(context, stagingForm);  
       // The create method returns an EntityKey which contains the RecID of the header.  
       EntityKey returnedRecord = (EntityKey)keys.GetValue(0);  
       Console.WriteLine("New stock header created " + returnedRecord.KeyData[0].Value);  
       Console.ReadLine();  
     }  

This function can be called from the main() method like below.

     static void Main(string[] args)  
     {  
       Console.WriteLine("Choose the action you want to perform:");  
       Console.WriteLine("1: Load purchase data");  
       Console.WriteLine("2: Load sales data");  
       Console.WriteLine("3: Load stock data");  
       Console.WriteLine("4: Load queue");  
       Console.WriteLine("5: Load stock data batches");  
       Console.WriteLine("6: Make import Id ready");  
       Console.WriteLine("7: Add ready import Ids to queue");  
       string userChoice = Console.ReadLine();  
       switch (userChoice)  
       {  
         case "1":  
           Program.loadPurchase();  
           break;  
         case "2":  
           Program.loadSales();  
           break;  
         case "3":  
           Program.loadStock();  
           break;  
         case "4":  
           Program.loadQueue();  
           break;  
         case "5":  
           Program.loadStockMultiple();  
           break;  
         case "6":  
           Program.makeReady();  
           break;  
         case "7":  
           Program.addToQueue();  
           break;  
       }  
       Console.WriteLine("Press any key to exit");  
       Console.ReadLine();  
     }  

Next we will enhance the loadStock() method by inserting 2 more lines to the same header. I will be using service operations read and update. I haven't seen any example of this kind yet, which is the primary reason for this blog post. This update is actually not updating anything in our case. It merely reads the returned header from the EntityKey[] and adds two more lines to it. Can you notice the differences between loadStockMultiple() and loadStock()?

     static void loadStockMultiple()  
     {  
       // Instantiate an instance of the service client class.  
       MMSIntellipharmServiceClient client = new MMSIntellipharmServiceClient();  
       // Initizlise parameters used for the create method  
       CallContext context = new CallContext() { Company = "mms" };  
       // Create an instance of the document class.  
       AxdMMSIntellipharm stagingForm = new AxdMMSIntellipharm();  
       // Create instances of the entities that are used in the service and set the needed fields on those entities.  
       AxdEntity_MMSStagingImportTable stagingImportTable = new AxdEntity_MMSStagingImportTable();  
       stagingImportTable.ImportId = "IPH_1005";  
       stagingImportTable.ImportDate = new DateTime(2014, 4, 4);  
       stagingImportTable.Datasource = AxdEnum_MMSImportDatasource.IPh;  
       stagingImportTable.Status = AxdEnum_MMSImportStatus.New;  
       stagingImportTable.Type = AxdEnum_MMSImportType.Stock;  
       AxdEntity_MMSStagingStockImport stagingStockImport = new AxdEntity_MMSStagingStockImport();  
       stagingStockImport.Barcode = "535353";  
       stagingStockImport.Cost = 14;  
       stagingStockImport.IsGST = AxdExtType_MMSImportIsGST.Yes;  
       stagingStockImport.ListPrice = 23;  
       stagingStockImport.SellPrice = 24;  
       stagingStockImport.SOH = 50;  
       stagingStockImport.StockDate = new DateTime(2014, 3, 25);  
       stagingStockImport.StoreId = "1300";  
       stagingStockImport.StoreItemDesc = "desc";  
       stagingStockImport.StoreItemId = "555555";  
       AxdEntity_MMSStagingStockImport stagingStockImport2 = new AxdEntity_MMSStagingStockImport();  
       stagingStockImport2.Barcode = "768756";  
       stagingStockImport2.Cost = 13;  
       stagingStockImport2.IsGST = AxdExtType_MMSImportIsGST.Yes;  
       stagingStockImport2.ListPrice = 27;  
       stagingStockImport2.SellPrice = 26;  
       stagingStockImport2.SOH = 40;  
       stagingStockImport2.StockDate = new DateTime(2014, 3, 24);  
       stagingStockImport2.StoreId = "1301";  
       stagingStockImport2.StoreItemDesc = "desc";  
       stagingStockImport2.StoreItemId = "444444";  
       stagingImportTable.MMSStagingStockImport = new AxdEntity_MMSStagingStockImport[2] { stagingStockImport, stagingStockImport2 };  
       stagingForm.MMSStagingImportTable = new AxdEntity_MMSStagingImportTable[1] { stagingImportTable };  
       // Call the create method on the service passing in the document.  
       EntityKey[] keys = client.create(context, stagingForm);  
       //update >  
       // Use the keys to read   
       AxdMMSIntellipharm stagingFormUpd = client.read(context, keys);  
       // Create instances of the entities that are used in the service and set the needed fields on those entities.  
       AxdEntity_MMSStagingImportTable stagingImportTableUpd = stagingFormUpd.MMSStagingImportTable.First();  
       stagingImportTableUpd.action = AxdEnum_AxdEntityAction.update;  
       stagingImportTableUpd.actionSpecified = true;  
       AxdEntity_MMSStagingStockImport stagingStockImportUpd1 = new AxdEntity_MMSStagingStockImport();  
       stagingStockImportUpd1.action = AxdEnum_AxdEntityAction.create;  
       stagingStockImportUpd1.actionSpecified = true;  
       stagingStockImportUpd1.Barcode = "53535";  
       stagingStockImportUpd1.Cost = 14;  
       stagingStockImportUpd1.IsGST = AxdExtType_MMSImportIsGST.Yes;  
       stagingStockImportUpd1.ListPrice = 23;  
       stagingStockImportUpd1.SellPrice = 24;  
       stagingStockImportUpd1.SOH = 50;  
       stagingStockImportUpd1.StockDate = new DateTime(2014, 3, 25);  
       stagingStockImportUpd1.StoreId = "1300";  
       stagingStockImportUpd1.StoreItemDesc = "desc";  
       stagingStockImportUpd1.StoreItemId = "555555";  
       AxdEntity_MMSStagingStockImport stagingStockImportUpd2 = new AxdEntity_MMSStagingStockImport();  
       stagingStockImportUpd2.action = AxdEnum_AxdEntityAction.create;  
       stagingStockImportUpd2.actionSpecified = true;  
       stagingStockImportUpd2.Barcode = "76875";  
       stagingStockImportUpd2.Cost = 13;  
       stagingStockImportUpd2.IsGST = AxdExtType_MMSImportIsGST.Yes;  
       stagingStockImportUpd2.ListPrice = 27;  
       stagingStockImportUpd2.SellPrice = 26;  
       stagingStockImportUpd2.SOH = 40;  
       stagingStockImportUpd2.StockDate = new DateTime(2014, 3, 24);  
       stagingStockImportUpd2.StoreId = "1301";  
       stagingStockImportUpd2.StoreItemDesc = "desc";  
       stagingStockImportUpd2.StoreItemId = "444444";  
       stagingImportTableUpd.MMSStagingStockImport = new AxdEntity_MMSStagingStockImport[2] { stagingStockImportUpd1, stagingStockImportUpd2 };  
       stagingFormUpd.MMSStagingImportTable = new AxdEntity_MMSStagingImportTable[1] { stagingImportTableUpd };  
       client.update(context, keys, stagingFormUpd);  
       EntityKey returnedRecord = (EntityKey)keys.GetValue(0);  
       Console.WriteLine("New stock header created & updated " + returnedRecord.KeyData[0].Value);  
       Console.ReadLine();  
       //update <  
     }  

The RecVersion value in AX for the header remains 1, which shows that there is no update to the record. Also notice the use of action and actionSpecified with each buffer, in case of header we use AxdEnum_AxdEntityAction.update and in case of lines we use AxdEnum_AxdEntityAction.create.

We can also add custom service operations along with the standard ones as you can see in my main method, addToQueue() and makeReady(). makeReady() expects a parameter.
Both the methods are added to the DocumentService class in AX with attribute [SysEntryPointAttribute(true)].

.NET code:
     static void makeReady()  
     {  
       try  
       {  
         Console.WriteLine("Enter an Import Id to make ready :");  
         string importId = Console.ReadLine();  
         // Instantiate an instance of the service client class.  
         MMSIntellipharmServiceClient client = new MMSIntellipharmServiceClient();  
         // Initizlise parameters used for the create method  
         CallContext context = new CallContext() { Company = "mms" };  
         // Create an instance of the document class.  
         AxdMMSIntellipharm staging = new AxdMMSIntellipharm();  
         client.makeReady(context, importId);  
         Console.WriteLine("Success!");  
       }  
       catch (Exception e)  
       {  
         Console.WriteLine(e.InnerException.Message);  
       }  
       Console.ReadLine();  
     }  

     static void addToQueue()  
     {  
       try  
       {  
         // Instantiate an instance of the service client class.  
         MMSIntellipharmServiceClient client = new MMSIntellipharmServiceClient();  
         // Initizlise parameters used for the create method  
         CallContext context = new CallContext() { Company = "mms" };  
         // Create an instance of the document class.  
         AxdMMSIntellipharm staging = new AxdMMSIntellipharm();  
         client.addToQueue(context);  
         Console.WriteLine("Success!");  
       }  
       catch (Exception e)  
       {  
         Console.WriteLine(e.InnerException.Message);  
       }  
       Console.ReadLine();  
     }  

AX code:
 [SysEntryPointAttribute(true)]  
 public void makeReady(MMSImportID _importId)  
 {  
   MMSStagingImportTable  importTable;  
   ttsbegin;  
   select firstOnly forupdate importTable where importTable.ImportId == _importId;  
   if (importTable)  
   {  
     importTable.Status = MMSImportStatus::Ready;  
     importTable.update();  
   }  
   ttscommit;  
 }  

 [SysEntryPointAttribute(true)]  
 public void addToQueue()  
 {  
   MMSStagingImportTable  importTable;  
   MMSStagingImportQueue  queueTable, queueTableNot;  
   MMSSeqId        seqId;  
   NumberSeq        numberSeq;  
   numberSeq = NumberSeq::newGetNum(MMSStagingImportQueue::numRefSeqId());  
   numberSeq.used();  
   seqId = numberSeq.num();  
   ttsBegin;  
   insert_recordset queueTable(ImportID, SeqID)  
   select ImportID, seqId  
     from importTable  
     where importTable.Status == MMSImportStatus::Ready  
     notexists join queueTableNot  
     where queueTableNot.ImportID == importTable.ImportId;  
   ttsCommit;  
 }  

Thats a lot of code for one post. I hope you learned something new today.

1 comment:

  1. Good article. I read barcode parameter in your article, but I don't know what's the meaning of barcode parameter, it's a little difficult for me.

    ReplyDelete