1 Temmuz 2021 Perşembe

AX 2012 - Enable default posting profile for sales return

There isn't default posting profile in AX 2012 sales return. So you have to select your ledger account manual:

 

 


 After a few debugging I found an exception; there was a parameter (CustParameters.EnableReturnPostingProfiles) shows at screen with just Brasilian setup. If you remove Brasilian tag from this field at Custparameters than parameter will displaying at screen and after checked up we have a default posting profile for sales returns:

  

 Maybe this's not importing so much when create sales return from screen but if you have to create a sales return from a job like me you might be need this.

 

 

 

 

 

 

27 Nisan 2021 Salı

AX 2012 - SFTP Download

SFTP service of one of our customers had upgraded. Old code had been written with Tamir.SharpSSH.DLL which didn't upgraded since 2007. Unfortunately service won't work with Tamir anymore, given with "Tamir.SharpSsh.jsch.JSchException: Algorithm negotiation fail" error. None of the solutions about I fond at internet worked with me. 

I see SSH.NET adviced ad forums. When I tried to copy SSH.NET's DLL to AX folders and add as reference unfortunately I see an optional paremeter of download method uses  Action<ulong> data type which doesn't supported by X++ and worse X++ makes this optional parameter as mandatory. First tried a solution which I see at a blog suggest overload the method, seem a good solution because of SSH.NET is an open source library. But I got some errors when compiled and didn't messed with these errors, instead of wrote a small C# code and build a small DLL. It was easier and in this case never need code update and recompile with new SSH.NET versions.

Compiled this code with just a few modifications which I found a blog page written for Dynamics 365 F&O:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Renci.SshNet;
using System.IO;

namespace SSHNet2
{
    public class SSHNet2
    {
        public SftpClient sftpClient;
        public void OpenSFTPConnection(string host, int port, string username, string password)
        {
            if (this.sftpClient == null)
            {
                this.sftpClient = new Renci.SshNet.SftpClient(host, port, username, password);
            }
            if (!this.sftpClient.IsConnected)
            {
                this.sftpClient.Connect();
            }
        }
        public List<string> GetDirectories(string path)
        {
            return this.sftpClient.ListDirectory(path).Select(n => n.Name).ToList();
        }
        public void MoveFile(string sourcePath, string destinationPath, bool isPosix)
        {
            this.sftpClient.RenameFile(sourcePath, destinationPath, isPosix);
        }

         public void DeleteFile(string sourcePath)
        {
            this.sftpClient.DeleteFile(sourcePath);
        }
        public Stream DownloadFile(string sourcePath)
        {
            var memoryStream = new MemoryStream();
            this.sftpClient.DownloadFile(sourcePath, memoryStream);
            memoryStream.Position = 0;
            return memoryStream;
        }
    }
}

Copied new DLL and SSH.NET's own DLL to AX folders and my SFTP download problem is solved:

System.IO.StreamReader          streamReader; 

System.IO.StreamWriter          streamWriter; 

 SSHNet2.SSHNet2                 sftp = new SSHNet2.SSHNet2();

System.IO.Stream                stream;

 sftp.OpenSFTPConnection(_RemoteAddress,any2int(_RemotePort), _RemoteUser, _RemotePassword);

            stream = sftp.DownloadFile(_RemoteFileName);

streamReader = new System.IO.StreamReader(stream);

streamWriter = new System.IO.StreamWriter("c:\test\test.txt");

streamWriter.Write(_StreamReader.ReadToEnd());

streamWriter.Flush();

streamWriter.Close();

streamReader.Close();  

9 Mart 2021 Salı

D365 F&O - Add right-click popup menu to form control

 In 2015 I shared a sample  about this issue which run both of AX 2009 and AX 2012. Seem Microsoft decided to change this too with 365, so that code doesn't run on 365 anymore. Microsoft shared a white page as before-after, I learned from that page. It was run with one method, since 365 two methods.

We declare these constants at class declaration. Of course these are not mandatory but good for writing human readable code:

public const int listCredit = 1;

public const int listJournal = 2;

We put this metod into control which we run popup. This method run popup menu. This sample has two popup bars:

        public str getContextMenuOptions()
        {
            str ret;
            ContextMenu menu = new ContextMenu();
            List menuOptions = new List(Types::Class);
            ContextMenuOption option;
            
            option = ContextMenuOption::Create("Credit number", listCredit);
            menuOptions.addEnd(option);

            option = ContextMenuOption::Create("Journal number", listJournal);
            menuOptions.addEnd(option);

            menu.ContextMenuOptions(menuOptions);
            return menu.Serialize();
        }

  This method triggers when select a bar at popup:

 public void selectedMenuOption(int selectedOption)
        {
            switch (selectedOption)
            {
                case -1:
                    break;
                case listCredit:

                    info("Bar 1 selected!..");
                    break;
                case listJournal:
                    info("Bar 2 selected!..");
                    break;
            }
        }

In my tests popup menu sometines doesn't showed up. I never see before at neither 2012 nor 2009. I couldn't find reason. When debugged see getContextMenuOptions doesn't triggered when bars doesn't showed up.

4 Mart 2021 Perşembe

AX 2012/365 F&O - Settle customer/vendor transactions

   public static void settlement(CustVendTransOpen _trans1,CustVendTransOpen _trans2,
        CustVendACType _type = CustVendACType::Vend)
    {
        CustVendOpenTransManager manager;

        if (_type == CustVendACType::Vend)
            manager = CustVendOpenTransManager::construct(VendTable::find(_trans1.AccountNum));
        else
            manager = CustVendOpenTransManager::construct(CustTable::find(_trans1.AccountNum));
        manager.updateTransMarked(_trans1, true);
        manager.updateTransMarked(_trans2, true);
        manager.settleMarkedTrans();
    }

26 Ocak 2021 Salı

AX 2012 - Send filter with args

 

If your filter just one record, you can run filter with args.record(). In the other case if you have to filter more than one record, you can't use record method. You can use InitialQueryParameter for this:

            Args                 args = new Args();
            MenuFunction         MenuFunction = new MenuFunction('LedgerJournalTable', MenuItemType::Display);
            Query                q = new Query();
            QueryBuildDataSource QBDS = q.addDataSource(tableNum(LedgerJournalTable));

            if (ABCRCreditTable.AccualJournalNum != "")
                QBDS.addRange(fieldNum(LedgerJournalTable,JournalNum)).value(ABCRCreditTable.AccualJournalNum);
            if (ABCRCreditTable.AccualJournalNum2 != "")
                QBDS.addRange(fieldNum(LedgerJournalTable,JournalNum)).value(ABCRCreditTable.AccualJournalNum2);

            args.initialQuery(InitialQueryParameter::createByQuery(q));
            args.caller(this);

            MenuFunction.run(args);  



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