3 Ekim 2020 Cumartesi

AX 2012 - Using Rest Api with AX

One of the our customers of AX partner which I work AGC Software applied a service requires using Rest Api. I found different samples on forums and blogs about AX 2012 but succeeded none of them. At that point I dediced to start from beginning.

At first I installed Postman to my computer. Succeeded to run service with Postman

Now I have C# RestSharp library code written by Postman's code generator:





I needed import RestSharp library into my project before have a code deployable by AX. When tried to import by command written in Visual Studio 2013 Package Manager Console :

Install-Package RestSharp 





I got "The underlying connection was closed: An unexpected error occurred on a send." error. Found solution on a forum page. When i wrote code below to a .REG file and run than I could install  Nuget packages:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v4.0.30319]
"SchUseStrongCrypto"=dword:00000001

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319]
"SchUseStrongCrypto"=dword:00000001


But than I see the last version of RestSharp doesn't compatible with Visual Studio 2013 :





Learned last RestSharp version which works with Visual Studio 2013 from another forum page  than I could import:

Install-Package RestSharp -Version 103.1.0

Now I had a working and deployable code with AX:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RestSharp;

namespace Ingenico
{
    public class Ingenico
    {
        public string StartDate { get; set; }
        public string EndDate { get; set; }
        public string Body { get; set; }
        public string Idx { get; set; }
        public string getPaymentSummaryReport()
        {
            var client = new RestClient("https://XXXXXXXXXXXXXXX/GetPaymentSummaryReport");
            client.Timeout = -1;
            var request = new RestRequest(Method.POST);
            request.AddHeader("Operator", "XXXXXXX");
            request.AddHeader("Authorization", "Basic XXXXXXX");
            request.AddHeader("Content-Type", "application/json");
            Body = "{\r\n\"ReportId\": "+Idx+",\r\n\"SerialList\": [\r\n\"\"\r\n],\r\n\"StartDate\": \""+StartDate+
            "\",\r\n\"EndDate\": \""+EndDate+
            "\",\r\n\"ExternalField\": \"\",\r\n\"ReceiptNo\": 0,\r\n\"ZNo\": 0,\r\n\"GetSummary\": true,\r\n\"SaleFlags\": 0,\r\n\"DeviceGroupName\": \"\"\r\n}\r\n       \r\n";
            request.AddParameter("application/json",Body, ParameterType.RequestBody);
            IRestResponse response = client.Execute(request);
            return response.Content;
        }
    }
}


When I deployed code, I saw it looks for  RestSharp.DLL and RestSharp.XML files reside in project folder and AX doesn't copy them to bin folder. Copied these files to server and client bin folders:



Then I could use the service:

    Ingenico.Ingenico o = new Ingenico.Ingenico();
    str a;

    try
    {
        o.set_StartDate("2020-10-01");
        o.set_EndDate("2020-10-01");
        o.set_Idx("111111111111");
        a = o.getPaymentSummaryReport();

    info(a);


I used  another blog for parse Json string which came from service.

 

class AGCingenico
{
    Array           jsonArray;
 
}

At first parse Json string with this method:

void parseJson(str  _json)

{
   Array           jsonArray;
   container       con;

   int             jsonSubstrNo, i;

   TextBuffer      textBuffer;

   Array           returnArray = new Array(Types::Class);

   #Define.LBracket('{')

   #Define.RBracket('}')
    str st;
    int fnd;



    fnd = strScan(_json,"]",1,strLen(_json));

    st="{"+subStr(_json,fnd+2,strlen(_json) - fnd -1);
    con+=st;



   // extract the json substrings and add them to a Array

   textBuffer = new TextBuffer();

   textBuffer.setText(_json);

   while (textBuffer.nextToken(0, #LBracket+#RBracket))

   {

       if ((jsonSubstrNo mod 2) > 0)
       {
           st =#LBracket + textBuffer.token() + #RBracket;
           con += st;
       }
       jsonSubstrNo++;

   }


   for (i = 1; i <= conLen(con); i++)

   {

       returnArray.value(i, RetailCommonWebAPI::getMapFromJsonString(conPeek(con, i)));

   }

   jsonArray = returnArray;

}

Than insert values a table with this method:

void jsonToTable()
{
   MapIterator                 mi;
   Map                         data;
   int                         i;
   str                         value;
   

   for (i = 1; i <= jsonArray.lastIndex(); i++)
   {
       data = jsonArray.value(i);
       mi = new MapIterator(data);
       tmpTable.clear();

       while (mi.more())
       {
           value = data.lookup(mi.key());
     
           switch (mi.key())
           {
               case "ErrCode":
                this.parmErrCode(str2int(value));
                break;
               case "ErrDesc":
                this.parmErrStr(value);
                break;
               case "Amount":
                tmpTable.Amount = str2num_RU(value);
                break;
               case "AmountCredit":
                tmpTable.AmountCredit = str2num_RU(value);
                break;

...

...

...


When I was thinking all well done there was another surprise. Code was working good with client side but not in CIL was giving an error about type convert. I found solution from a  forum page again. Martin Drab described pinpoint solution. Modified RetailCommonWebAPI class like this and all errors was fixed:

//AGC memre 29.9.20
//private static Map getMap(CLRObject _dict)
private static Map getMap(System.Collections.IDictionary _dict)
//http://dev.goshoom.net/en/2016/01/x-to-cil-object-must-implement-iconvertible/#comment-135186
//AGC memre
{


At last it's easy to use Rest Api with AX 2012 but there're lots of issues need to touch, fortunately there're lots of forums pages and blogs. :)

4 Eylül 2020 Cuma

AX 2012 - Workflow form open error

 

At one of our customers Workflow wasn't opening. Also error message wasn't so much explaining:





After a review of Event log, couldn't find any log about this error. Tried all suggested solutions at web, still no success:

-Incremental CIL

-Full CL

-XppIL klasörünü silip AOS restart

-Full compile ve Full CIL

-Client reinstall


At a blog says there isn't inner CLR catch at try/catch in WorkflowEditorHost form. Like blogger did I decided add include these red lines to form:

 

private void build()
{
    #Admin
    #AOT
     //AGC memre
    #OCCRetryCount
    System.Exception ex;
    //AGC memre



    WorkflowVersionTable        versionTable;
    WorkflowTypeName            templateName;
    str                         domainUser;
    UserInfo                    userInfo;
    NumberSeq                   num;
    SysInfoAction_MenuFunction  sysInfoAction;
    TreeNode                    treeNode;
    SysDictWorkflowType            workflowType;
    int                         classId;

    num = NumberSeq::newGetNum(SysWorkflowParameters::numRefSequenceId(), false, true);
    if (num == null)
    {
        sysInfoAction = SysInfoAction_MenuFunction::newMenuItem(menuitemDisplayStr(SystemParameters), MenuItemType::Display);
        throw error("@SYS108268", '', sysInfoAction);
    }

    versionTable = element.args().record();

    //BP Deviation Documented
    select userInfo where userInfo.Id == curUserId();
    domainUser = userInfo.NetworkDomain + '\\' + userInfo.NetworkAlias;

    if (element.args().parmEnumType() == enumNum(WorkflowConfigurationActionType) &&
        element.args().parmEnum() == WorkflowConfigurationActionType::New)
    {
        templateName = element.args().parm();
        treeNode = TreeNode::findNode(#WorkflowTypesPath + #AOTDelimiter + templateName);

        if (treeNode)
        {
            workflowType = new SysDictWorkflowType(templateName);
            classId = className2Id(workflowType.document());
            if(classId == 0)
            {
                throw error("@SYS108554" + '. ' + "@SYS113219");
            }

            try
            {
                workflowConfiguration = Microsoft.Dynamics.AX.Framework.Workflow.Model.WorkflowModel::
                                            Create(templateName, curext(), curUserId(), domainUser);
            }
            catch (Exception::CLRError)
            {
            //AGC memre
            ex = ClrInterop::getLastException();
            if (ex != null)
            {
                ex = ex.get_InnerException();
                if (ex != null)
                {
                    error(ex.ToString());
                }
                else
                    error(AifUtil::getClrErrorMessage());
            }
            else
                    error(AifUtil::getClrErrorMessage());
            //AGC memre           
               
throw error("@SYS327400");
            }
        }
        else
        {
            throw error(strFmt("@SYS106830", templateName));
        }
    }
    else
    {
        try
        {
            workflowConfiguration = Microsoft.Dynamics.AX.Framework.Workflow.Model.WorkflowModel::Create(versionTable.ConfigurationId, curext(), domainUser);
        }
        catch (Exception::CLRError)
        {
            //AGC memre
            ex = ClrInterop::getLastException();
            if (ex != null)
            {
                ex = ex.get_InnerException();
                if (ex != null)
                {
                    error(ex.ToString());
                }
                else
                    error(AifUtil::getClrErrorMessage());
            }
            else
                    error(AifUtil::getClrErrorMessage());
            //AGC memre           
           
throw error("@SYS327400");
        }
    }

}





public void run()
{
    //AGC memre
    #OCCRetryCount
    System.Exception ex;
    //AGC memre

    try
    {
        modelEditorControl.Load(workflowConfiguration, userSettings);
        workflowEditorPane.initializeActionPane();
        if (element.args().openMode() == OpenMode::View)
        {
            isReadOnly = true;
            modelEditorControl.set_IsReadOnly(true);
            modifyElementButtonGroup.caption("@SYS322934");
            modifyWorkflow.caption("@SYS322935");
            viewToolboxButton.visible(false);
        }
    }
    catch (Exception::CLRError)
    {
    //AGC memre
        ex = ClrInterop::getLastException();
        if (ex != null)
        {
            ex = ex.get_InnerException();
            if (ex != null)
            {
                error(ex.ToString());
            }
            else
                error(AifUtil::getClrErrorMessage());
        }
        else
                error(AifUtil::getClrErrorMessage());
    //AGC memre
        throw error("@SYS327400");

    }

    super();
}

 

Then I could get a clear error message:

 



Looked inbound ports, reviewed Windows firewall, reviewed Ax32Serv.exe.config file, seem everything good. But client configuration tool still couldn't WCF refresh:

 


 

In spite of there is just one AOS at the computer, tried to write Instance Name:


 

Problem was solved: