Showing posts with label Dynamics AX. Show all posts
Showing posts with label Dynamics AX. Show all posts

Monday, September 22, 2014

Consuming WCF web service from .NET assembly in Dynamics AX 2012

X++ code for consuming a WCF web service:
 static server str serverSendToEndPoint(str _endPoint, str _soapAction, str _serverThumbprint, str _clientThumbprint, str _xmlString)  
 {  
   str                 ret;  
   System.String       clrEndPoint;  
   System.String       clrSoapAction;  
   System.String       clrServerThumbprint, clrClientThumbprint;  
   System.String       clrXmlString;  
   System.String       clrResponse;  
   System.Exception     ex;  
   Namespace.WebService webService;  
   try  
   {  
     webService = new Namespace.WebService();  
     clrEndPoint         = CLRInterop::getObjectForAnyType(_endPoint);  
     clrSoapAction        = CLRInterop::getObjectForAnyType(_soapAction);  
     clrServerThumbprint     = CLRInterop::getObjectForAnyType(_serverThumbprint);  
     clrClientThumbprint     = CLRInterop::getObjectForAnyType(_clientThumbprint);  
     clrXmlString        = CLRInterop::getObjectForAnyType(_xmlString);  
     if (VendParameters::find().AVAMutualAuthentication == NoYes::Yes)  
     {  
       clrResponse = webService.callWCFWebService(clrEndPoint, clrSoapAction, clrServerThumbprint, clrClientThumbprint, clrXmlString);  
     }  
     else  
     {  
       clrResponse = webService.callWCFWebService(clrEndPoint, clrSoapAction, clrServerThumbprint, clrXmlString);  
     }  
     return CLRInterop::getAnyTypeForObject(clrResponse);  
   }  
   catch (Exception::CLRError)  
   {  
     ex = CLRInterop::getLastException();  
     throw error(strFmt("The request failed with the following response %1", CLRInterop::getAnyTypeForObject(ex.ToString())));  
   }  
 }  


.NET Assembly
namespace Namespace
{
    public class WebService
    {
      public String callWCFWebService(string url = "", string soapAction = "", string thumbPrint = "", string thumbPrint2 = "", string xmlString = "")  
     {  
       ServicePointManager.ServerCertificateValidationCallback = delegate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return true; };  
       //Validate parameters  
       if (url == "")  
       {  
         throw new System.ArgumentException("Url missing");  
       }  
       if (soapAction == "")  
       {  
         throw new System.ArgumentException("Soap Action missing");  
       }  
       if (thumbPrint == "")  
       {  
         throw new System.ArgumentException("Server Thumbprint missing");  
       }  
       if (thumbPrint2 == "")  
       {  
         throw new System.ArgumentException("Client Thumbprint missing");  
       }  
       if (xmlString == "")  
       {  
         throw new System.ArgumentException("xmlString missing");  
       }  
       // Create a new HttpWebRequest object for the specified resource.  
       HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);  
       // Request mutual authentication.  
       request.AuthenticationLevel = AuthenticationLevel.MutualAuthRequired;  
       // Supply client credentials.  
       X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);  
       store.Open(OpenFlags.ReadOnly | OpenFlags.IncludeArchived);  
       // Find Server Certificate by thumbprint  
       X509Certificate2Collection col =  
       store.Certificates.Find(X509FindType.FindByThumbprint, thumbPrint.Replace(" ", ""), false);  
       X509Certificate2 cert = col.OfType<X509Certificate2>().FirstOrDefault();  
       if (cert == null)  
       {  
         throw new System.ArgumentException("Server Certificate not found in store ");  
       }  
       request.ClientCertificates.Add(cert);  
       // Find Client Certificate by thumbprint  
       col = store.Certificates.Find(X509FindType.FindByThumbprint, thumbPrint2.Replace(" ", ""), false);  
       cert = col.OfType<X509Certificate2>().FirstOrDefault();  
       store.Close();  
       if (cert == null)  
       {  
         throw new System.ArgumentException("Client Certificate not found in store");  
       }  
       request.ClientCertificates.Add(cert);  
       ASCIIEncoding encoding = new ASCIIEncoding();  
       byte[] bytesToWrite = encoding.GetBytes(xmlString);  
       request.Method = "POST";  
       request.ContentLength = bytesToWrite.Length;  
       request.Headers.Add("SOAPAction: \"" + soapAction + "\"");  
       request.ContentType = "text/xml; charset=utf-8";  
       request.KeepAlive = false;  
       request.ProtocolVersion = HttpVersion.Version10;  
       request.PreAuthenticate = true;  
       //Send Request  
       Stream dataStream = request.GetRequestStream();  
       dataStream.Write(bytesToWrite, 0, bytesToWrite.Length);  
       dataStream.Close();  
       //Get Response  
       string responseString = "";  
       try  
       {  
         HttpWebResponse response = (HttpWebResponse)request.GetResponse();  
         // Read and display the response.  
         Stream streamResponse = response.GetResponseStream();  
         StreamReader streamRead = new StreamReader(streamResponse);  
         responseString = streamRead.ReadToEnd();  
         //Console.WriteLine(responseString);  
         // Close the stream objects.  
         streamResponse.Close();  
         streamRead.Close();  
         //Release the HttpWebResponse.  
         response.Close();  
       }  
       catch (Exception e)  
       {  
         if (e is WebException)  
         {  
           WebResponse errResp = ((WebException)e).Response;  
           if (errResp != null)  
           {  
             using (Stream streamResponse = errResp.GetResponseStream())  
             {  
               StreamReader streamRead = new StreamReader(streamResponse);  
               responseString = streamRead.ReadToEnd();  
               //Console.WriteLine(responseString);  
               // Close the stream objects.  
               streamResponse.Close();  
               streamRead.Close();  
             }  
           }  
           else  
           {  
             responseString = e.Message;  
             //Console.WriteLine(e.Message);  
           }  
         }  
       }  
       return responseString;  
     }
}  

Wednesday, May 9, 2012

Filtered lookups on SysQueryForm

I was asked to filter the lookup on the JournalNum field in the Select Query form. Nornally the lookup on this field would display all JournalNums, but the client only wanted to see Vendor Payment journals.

To achieve this I modified the SysQueryForm, specifically the lookup method on the RangeValue field of the Range dataset.

What my code does is use a custom lookup filtered by JournalType.

 public void lookup(FormControl _formControl, str _filterStr)  
 {  
   SysTableLookup   sysTableLookup;  
   SysDictField    sysDictField;  
   QueryBuildRange   rangeJournalType;  
   Query        query;  
   TmpSysQuery     tmpSysQuery;  
   ;  
   sysDictField = new SysDictField(Range.Table_Id, Range.Field_Id);  
   if (sysDictField.typeId() == extendedTypeNum(ledgerJournalId))  
   {  
     //Search range records for LedgerJournalTable.JournalType so that we can use this to filter the lookup for JournalNum  
     for (tmpSysQuery = Range_DS.getFirst(); tmpSysQuery; tmpSysQuery = Range_DS.getNext())  
     {  
       if (tmpSysQuery.Table_Id == tableNum(LedgerJournalTable) &&  
         tmpSysQuery.Field_Id == fieldId2Ext(fieldNum(LedgerJournalTable, JournalType), 1))  
       {  
         break;  
       }  
     }  
     // If a range value exists for JournalType then perform a filtered lookup of JournalNum  
     if (tmpSysQuery.RangeValue)  
     {  
       sysTableLookup = SysTableLookup::newParameters(tableNum(LedgerJournalTable), _formControl);  
       sysTableLookup.addLookupfield(fieldNum(LedgerJournalTable, JournalNum));  
       sysTableLookup.addLookupfield(fieldNum(LedgerJournalTable, JournalName));  
       sysTableLookup.addLookupfield(fieldNum(LedgerJournalTable, Name));  
       sysTableLookup.addLookupfield(fieldNum(LedgerJournalTable, JournalType));  
       query = new Query();  
       SysQuery::findOrCreateDataSource(query, tableNum(LedgerJournalTable)).addRange(fieldnum(LedgerJournalTable, JournalType)).value(tmpSysQuery.RangeValue);  
       sysTableLookup.parmQuery(query);  
       sysTableLookup.performFormLookup();  
     }  
     else  
     {  
       SysLookup::lookupRange(_formControl, range, sysQueryForm.query());  
     }  
   }  
   else  
   {  
     SysLookup::lookupRange(_formControl, range, sysQueryForm.query());  
   }  
 }  

Tuesday, April 19, 2011

Deleting duplicate records via SQL

 WITH DUP_BARCODE (ITEMBARCODE, DUPCOUNT) AS  
 (SELECT ITEMBARCODE, COUNT(RECID) AS DUPCOUNT  
  FROM INVENTITEMBARCODE   
  GROUP BY ITEMBARCODE  
  HAVING COUNT(RECID) > 1)  
    
  DELETE FROM INVENTITEMBARCODE  
  FROM INVENTITEMBARCODE AS I1  
  JOIN DUP_BARCODE ON I1.ITEMBARCODE = DUP_BARCODE.ITEMBARCODE  
  WHERE I1.RECID != (SELECT TOP 1 RECID FROM INVENTITEMBARCODE AS I2 WHERE I2.ITEMBARCODE = I1.ITEMBARCODE ORDER BY I1.CREATEDDATETIME ASC)  

Monday, April 4, 2011

Master scheduling run for All Items not picking up level 0 items

Master scheduling had been working fine for a number of days following Go-live, but then something happening and alot of items were not being picked up in the master scheduling run. The symptoms reported by the user was that the process was only running for 10 minutes, where initially it was taking 2 hours to run. Also, no errors were being reported. I suspected a data issue and after a several hours of investigation I finally found the issue. Somehow an InventTable record with a blank ItemId had been created in the database. In the Master Scheduling code that builds the item list for each level there's an If statement that checks the ItemId and if it's blank exits the loop and because this blank item was the first item processed for the level, no items were being selected for this level for the master scheduling run.

Following on from the discovery of this issue and the workaround of deleting the blank item from InventTable, the blank item kept reappearing in the database and it was decided to modify the Master Planning code to discard items with a blank ItemId.

This was done by making a simple change to the createListsFromQueryRun method of the ReqTransCache_Periodic class which builds the item lists used by master planning.

   while (runQuery.next())  
   {  
     inventTable = runQuery.get(tablenum(InventTable));  
    
     // Discard any items with a blank ItemId  
     if (inventTable.ItemId == "")  
     {  
       continue;  
     }  
    
     if (!levelItemMap.exists(inventTable.bomLevel))  
     {  
       itemSet = new Set(Types::String);  
       levelItemMap.insert(inventTable.bomLevel, itemSet);  
     }  
     else  
       itemSet = levelItemMap.lookup(inventTable.bomLevel);  
    
     itemSet.add(inventTable.ItemId);  
    
   }  

Saturday, February 26, 2011

X++ code for adjusting tax on journals and free text invoices

 static void ExampleLedgerJournalTaxAdjust(Args _args)  
 {  
   LedgerJOurnalTrans         ledgerJournalTrans;  
   TaxLedgerJournalCalculate  taxLedgerJournalCalculate;  
   TaxLedgerJournal           taxLedgerJournal;  
   TaxRegulation              taxRegulation;  
   ;  
   
   ttsbegin;  
   
   // Find a ledgerJournalTrans record  
   select firstonly LedgerJournalTrans  
   where LedgerJournalTrans.JournalNum == "000185_010";  
   
   
   taxLedgerJournal = TaxLedgerJournal::construct(TaxJournalCall::Journal, ledgerJournalTrans, null);  
   taxRegulation    = new TaxRegulation();  
   taxLedgerJournal.calcAndPost();  
   taxRegulation.setTax(taxLedgerJournal);  
   taxRegulation.createSumsFromTmp();  
   taxRegulation.allocateAmount(6.99);  
   taxRegulation.saveTaxRegulation();  
   
   ttscommit;  
 }  
   
 static void FreeTextTaxAdjust(Args _args)  
 {  
   CustInvoiceTable    custInvoiceTable;  
   CustInvoiceCalcTax  custInvoiceCalcTax;  
   TaxFreeInvoice      taxFreeInvoice;  
   TaxRegulation       taxRegulation;  
   ;  
   
   ttsbegin;  
   
   select firstonly custInvoiceTable  
   where custInvoiceTable.InvoiceId == "Inv135";  
   
   custInvoiceCalcTax = new CustInvoiceCalcTax_Table(custInvoiceTable);  
   taxFreeInvoice     = new TaxFreeInvoice(custInvoiceCalcTax);  
   taxFreeInvoice.calc();  
   taxRegulation = TaxRegulation::newTaxRegulation(taxFreeInvoice);  
   taxRegulation.allocateAmount(35.77);  
   taxRegulation.saveTaxRegulation();  
   
   ttscommit;  
 }  

Checking to see if caller is a form

I had this requirement the other day to set the filter value on the LedgerJournalTable form when it was called from a new form that had been created.

This new form contained a JournalId field and I wanted the users to be able to right-click goto main table to open the LedgerJournalTable showing the journal record.

Because the journal may or may not be posted I needed to set the standard filter on the LedgerJournalTable form to show all journals (by default when the form is opened it shows 'Open' Journals only) So, I added some code to the init method of the LedgerJournalTable form to check the element.args().caller().name for my new form and then set the filter.

This worked a treat, except I managed to break the journal lookups in other areas of the system e.g. Vendor transactions -> Original document.

To cut a long story short the Original Documents function is a class (which does not have a name() method that also calls the LedgerJournalTable form.

To get around this issue I used the code below which checks to see if the caller is a form before calling the name() method.

 if (element.args() && element.args().caller())  
 {  
     // Make sure this was via a form.  
     if(SysDictClass::is(element.args().caller(), classnum(FormRun)))  
     {  
         // Cast the caller and make sure it is the right form.  
         callerForm = element.args().caller();  
   
         if (callerForm.name() == formstr(LIQ_RegisterCashControlDetails))  
         {  
             allOpenPostedField.selection(AllOpenPosted::All);  
         }  
     }  
 }  

Monday, February 21, 2011

Handling AX UTC dates in SQL Server

SELECT *, DATEADD(hour,DATEDIFF(hour,GETUTCDATE(),GETDATE()),MODIFIEDDATETIME) As ModifiedDateTime from SYSRECORDLEVELSECURITY