Thursday, December 15, 2011

AX 2012: Reference To the VS Projects not updated in AOT



Common error when using a class that runs on server and references a Visual studio DLL file.
I am trying to consume web service from external systems in AX. To do this, I have added the references in VS 2010 solution and that in turn is added to the AOT. I have set the deploy on client and server to yes.





When you add the solution to the AOT, the AX server will store the relevant DLL and its configuration file to the folder under the application as below. The highlighted part is the instance name.
C:\Program Files\Microsoft Dynamics AX\60\Server\Lexjet_DEV_AX\bin\VSAssemblies
When you use the reference to the web services in a class, which is very much simplified in AX 2012 as it gets stored under the VS references, you may want to execute the class always from server. In my case it is a batch job so it becomes mandatory for me.


But when my code tries to create a service client, it points out the configuration path to the client rather than the destination pointed above.


Second Problem with this path reference is that when you try updating a service in VS 2010 and update the project to AOT, only the server folder gets the latest artifacts of the project. Below is a screen that shows that the last modified date of the client folder is older than the one in server.

 
The deploy to client did not work and even though you run the class from server it takes the client’s path reference in the createServiceClient method.
Workaround:
Every time you add or update the service reference, you need to copy the configuration and DLL from server to the client folder.
This is a big overhead when you are developing integration scenarios and you tend to update the service reference again and again.
You also do not want to refer to the client’s directory in case of a batch job. This is a big pain when you are migrate your code to other environments.

Wednesday, September 14, 2011

Importing Vendor Into Ax 2012

Having all the relations of GAB structure changed, it became mandatory to write a script to import vendor data into Ax 2012. I will create a DirParty record and then associate it to vendor fields. The addresses can be attached to the vendor's PartyRecId. The use of views to record all the address fields help to initialize the linked tables.



static void importVendor(Args _args)
{
    CommaIO                     csvFile;
    container                   readCon;
    counter                     icount,inserted;
    Dialog                      dialog;
    DialogField                 dfFileName;
    DirPartyRecId               partyRecId,contactPartyRecid;
    Name                        name,contactName;
    VendTable                   vendtable;
    FileName                    fileName;
    str                         contactperson;
    DirPartyPostalAddressView   addressView;
    DirPartyContactInfoView     contactView;
    ContactPerson               contactpersonTable;
    LogisticsElectronicAddressMethodType enumType;


    DirParty                    dirParty;
    LogisticsPostalAddress      address;
    LogisticsElectronicAddress  logisticsElectronicAddress;
    BinData binData;
    str stringImage;
    inserted =0;
    #File


    dialog = new Dialog("Pick the file");
    dfFileName = dialog.addField(extendedTypeStr(FileNameOpen));
    dialog.filenameLookupFilter(["All files", #AllFiles]);


        if (dialog.run())
        {
             filename =  dfFileName.value();
        }




    csvFile = new CommaIO(filename, 'r');
    
   
    if (csvFile)
    {
        readCon = csvFile.read();
        ttsbegin;
        while (csvFile.status() == IO_Status::OK)
        {
            readCon = csvFile.read();
            icount++;
            if (readCon)
            {
                name = conPeek(readCon,2);
                partyRecId = DirPartyTable::createNew( DirPartyType::Organization, name).RecId;


                vendtable.clear();
                vendtable.initValue();
                vendtable.Party = partyRecId;
                vendtable.AccountNum = conPeek(readCon,1);
                vendtable.VendGroup  = conPeek(readCon,5);
                vendtable.Currency   = conPeek(readCon,6);
                //vendtable.Blocked    =
                vendtable.DlvMode    = conPeek(readCon,8);
                vendtable.PaymMode   = conPeek(readCon,9);
                ContactPerson = conPeek(readCon,12);
                if(contactperson != '')
                {
                    contactname = conPeek(readCon,12);
                    ContactPerson::findOrCreateNameParty(partyRecId,contactname);
                }
        
                vendtable.insert();


                address.CountryRegionId = strLRTrim(conPeek(readCon,14));
                //addressView.State = strLRTrim(conPeek(readCon,16));
                address.ZipCode = strLRTrim(conPeek(readCon,15));
                address.Street  = strLRTrim(conPeek(readCon,19));
               // address.county = strLRTrim(conPeek(readCon,17));
               address.City    = strLRTrim(conPeek(readCon,18));
                addressView.LocationName = name;
                addressView.IsPrimary = NoYes::Yes;
                addressView.Party = partyRecId;
                addressview.initFromPostalAddress(address);


                DirParty = DirParty::constructFromPartyRecId(addressView.Party );
                DirParty.createOrUpdatePostalAddress(addressView);


                contactView.LocationName = "Delivery email";
                contactView.Locator      = strLRTrim(conPeek(readCon,10));
                contactView.Type         = LogisticsElectronicAddressMethodType::Email;
                contactView.Party        = partyRecId;
                contactView.IsPrimary    = NoYes::Yes;
                dirParty.createOrUpdateContactInfo(contactView);


                contactView.LocationName = "Delivery Cellular Phone";
                contactView.Locator      = strLRTrim(conPeek(readCon,11));
                contactView.Type         = LogisticsElectronicAddressMethodType::Phone;
                contactView.Party        = partyRecId;
                contactView.IsPrimary    = NoYes::Yes;
                dirParty.createOrUpdateContactInfo(contactView);


                contactView.LocationName = "Delivery Phone";
                contactView.Locator      = strLRTrim(conPeek(readCon,3));
                contactView.Type         = LogisticsElectronicAddressMethodType::Phone;
                contactView.Party        = partyRecId;
                contactView.IsPrimary    = NoYes::Yes;
                dirParty.createOrUpdateContactInfo(contactView);


                contactView.LocationName = "Delivery Fax";
                contactView.Locator      = strLRTrim(conPeek(readCon,4));
                contactView.Type         = LogisticsElectronicAddressMethodType::Fax;
                contactView.Party        = partyRecId;
                contactView.IsPrimary    = NoYes::Yes;
                dirParty.createOrUpdateContactInfo(contactView);
                inserted++;
            }
        }
        ttsCommit;
    }
    info(strfmt("%1 records inserted out of %2",inserted,icount));
}



Tuesday, October 26, 2010

Ax2009 Reporting services installation Error out on iis

On the Box:
Server: windows 2008 server R2.
SQL 2008.

When installing Reporting Extensions for Ax 2009,the pre-requisite page shows that IIS is not installed even thou you may have installed it. In the log file,you will find that the installation fails at IIS.

For the workaround, install the following IIS components from Manage Your server-> Server Roles->role services.

Install the selected components and You wont be asked to install IIS pre-requisite.


Thursday, October 21, 2010

SQL Database Error in Ax2009

When I try to execute a query which uses complex joins in dynamics ax 2009, the sql buffer size is reached depending upon the buffer size of configuration database size. Here is the error in the infolog.
Error:
The total, internal size of the records in your joined SELECT statement is 25666 bytes, but Microsoft Dynamics is by default performance-tuned not to exceed 24576 bytes.
It is strongly recommended that you split your table(s) into smaller units.
Alternatively, you have to specify a 'Buffer size' value of 26 Kbytes or higher in the 'SQL' tab page in the Microsoft Dynamics Configuration Utility. The default value is 24 Kbytes.


 As a solution, you can increase the buffer size in the ax server configuration manager by multiples of 2. By default it has 24kb of buffer.Also note how the page memory use increases from task manager of 'AX32' services.The increase in buffer id proportional to the increase in PF memory.

Thursday, October 14, 2010

Copying Infolog -Sending EMail from ax2009

This is a small post to send and configure mail in ax 2009.I have had a requirement to copy the infolog at the end of some function and send it to the user. This is a code snippet (not the actual one though) how I managed.
First you need to set a template for the mail (Basic-> set up->Email Template). I have created a template as shown.


%name% and %matter% are dynamic macro taken from runtime.You can have your code to take up customer name and description from a failed sales order for an instance.
A job is written, which produces three info message logs and sent it to the email.The template is helpful in forming email body parameters. You can send the mail from the email address of customer.

static void SendEmail(Args _args)
{
    SysInetMail mail;
    Map mappings;
    COM document;
    COM body;
    str ret, subject, matter;
    sysemailid emailid;
    languageId languageId;
    SysEmailMessageTable message;
    Xinfo info;
    int infoLogCounter;
    Log log;
    int i=1;
    container con;


    SysInfologEnumerator enum ;


    #help
    ;
    info( " hello world");
    error('this is error');
    error('this is  error 2 ');
    //info - if you want to copy these info msg to a email in some functionality
     log = Info::infoCon2Str( infolog.copy(1,infolog.num()));
     con = infolog.copy(1,infolog.num());
     enum = SysInfologEnumerator::newData(con );
      while (enum.moveNext())
    {
        matter +=strfmt('
%1 -> %2 ',int2str(enum.currentException()), enum.currentMessage());
    }


    emailId = 'Log';
    languageId = 'en-us';
    mail = new SysInetMail();
    mail.parmForceSendDialog(true); // this will show u a dialog before sending.
    mappings = new Map(Types::String, Types::String);
    mappings.insert('name', 'parthav');
    mappings.insert('matter',matter );
    message = SysEmailMessageTable::find( emailId,languageId);
    ret = SysEmailMessage::stringExpand(message.Mail, mappings);
    ret = WebLet::weblets2Html4Help(ret, '');
    document = new COM(#HTMLDocumentClassName);
    SysEmailTable::insertHTML2Document(document, ret);
    body = document.body();
    subject = SysEmailMessageTable::find(emailId, languageId).Subject;
    mail.sendMail( 'parthav.patel@ignify.com'   ,  'CSV Import Log',    body.OuterText() ,false);
    
}
After hitting f5,you will get a dialog and composition of email. You can avoid this by setting mail.parmForceSendDialog(false) in the above code.


You are now ready to send the mail of your report.