At the previous post I already show how to customize Response Value list in AIF Document standard service, today we will talk about response in Custom AIF service class.

We already know for Custom AIF service we actually need 2 classes, one is contract for data input and one is service to process a logic. With Response class, it’s literally same with contract class. They both hold parm value.

  • contract class gets parametters.

  • Response class sets return values.

Scenario

I want to get HcmPersonnelNumberId and HcmWorkerName of current userID on C#.NET application.

Continue Reading ...

We normally use NETTCP adapter for .NET system, It going to very easy to consume by.

As my experience, if we use NETTCP adapter for consuming by Java system, we will need add some extension or 3rd party due to binding and mismatch schema and of course client doesn’t want to do that either me.

So, my solution is creating HTTP adapter for Java, you can also use .NET system to consume HTTP adapter without any problems

Continue Reading ...

Normally, when we consume AIF Service, we use this code like below to handle Error messages

try
{
	client.register(ctx, contract);
	Console.WriteLine("items registed on Trans Id: " + contract.InventTransId + " with " + contract.Qty + " quantities.");
	Console.ReadLine();
}
catch (Exception ex)
{
	Console.WriteLine(string.Format("Ex: {0}", ex.Message));
	Console.ReadLine();
}

If it cause error, message would return like this

Continue Reading ...

For this demonstration, I use AIF service to create Sales order with SalesSalesOrderService and I gonna consume AIF using C#.NET.

Normally, in consume service application we handle return value by using EntityKeyList, EntityKey, KeyData[0].Value. And for Sales Order It will return Created SalesID.

Let’s take a look on AxdSalesOrder class. More about Axd class please prefer this.

Then go to createList method, this method will handle response value

public AifEntityKeyList createList(
    AifDocumentXml                  _xml,
    AifEndpointActionPolicyInfo     _actionPolicyInfo,
    AifConstraintListCollection     _constraintListCollection)
{
    AifEntityKeyList aifEntityKeyList;

    aifEntityKeyList = super(_xml, _actionPolicyInfo, _constraintListCollection);

    // Sales orders are committed - master planning explosion can be executed and confirmed dates be set
    this.postSalesOrderCreation(aifEntityKeyList);

    return aifEntityKeyList;
}

So, how about customer wants another meaning value beside SalesID likes InventTransId information in SalesLine table or another tables base on your requirement. To do that, we need to customize this method.

Continue Reading ...

Main requirements is Using batch to find and block vendor base on last transaction condition and notify for them by emails.

Set up E-mail parameters

For set up email, we need Go to AX System administrator > Setup > E-mail parameters

Vendor emails

Vendor emails locate on LogisticsElectronicAddress.Locator, partyTable.PrimaryContactEmail, partyLocation.Location, please take a look on this job to find how to update Vendor emails and you also could see the relations more clearly.

static void UpdateVendorEmail(Args _args)
{
    VendTable                   vendTable;
    LogisticsElectronicAddress  electronicAddress;
    DirPartyTable               partyTable;
    DirPartyLocation            partyLocation;

    electronicAddress.initValue();
    electronicAddress.Type = LogisticsElectronicAddressMethodType::Email;
    electronicAddress.Description = "Max Nguyen";
    electronicAddress.Locator     = "luan52@outlook.com";
    electronicAddress.IsPrimary   = NoYes::Yes;
    electronicAddress.insert();

    while select forUpdate partyTable
        exists join vendTable
            where vendTable.Party == partyTable.RecId
    {
        ttsBegin;
        partyTable.PrimaryContactEmail = electronicAddress.RecId;
        partyTable.update();
        ttsCommit;

        select firstOnly forupdate partyLocation
            where partyLocation.Party == partyTable.RecId;

        if (partyLocation)
        {
            ttsBegin;
            partyLocation.Location = electronicAddress.Location;
            partyLocation.update();
            ttsCommit;
        }
        else
        {
            partyLocation.initValue();
            partyLocation.Location = electronicAddress.Location;
            partyLocation.Party     = partyTable.RecId;
            partyLocation.insert();
        }
    }
}

Batch class

Main logic here is find Vend accounts are not exist in VendTrans table with condition endTrans.TransDate >= beginDate, and beginDate count from today systemDateGet().

public class Max_VendorBlockedBatch extends RunBaseBatch
{
}

Get the date before 6 months from today

public TransDate getBeginDate()
{
    TransDate   beginDate;
    TransDate   currentDate;
    Months      month;
    YearBase    years;
    Day         day;

    currentDate = systemDateGet();
    day         = dayOfMth(currentDate);
    month       = mthOfYr(currentDate);
    years       = year(currentDate);
    if (month < 6)
    {
        beginDate = mkDate(day, 12 - (6 - month) + 1, years - 1);
    }
    else
    {
        beginDate = mkDate(day, month - 6 + 1, years);
    }

    return beginDate;
}

Send E-mail

public void sendEmail(AccountNum _vendor, str _recipient)
{
    str                                     sender  = 'sender@email.com';
    str                                     subject = 'Account blocked';
    str                                     body    = 'Your account is blocked due to last transaction';
    List                                    toList;
    ListEnumerator                          le;
    Set                                     permissionSet;
    System.Exception                        e;
    str                                     mailServer;
    int                                     mailServerPort;
    System.Net.Mail.SmtpClient              mailClient;
    System.Net.Mail.MailMessage             mailMessage;
    System.Net.Mail.MailAddress             mailFrom;
    System.Net.Mail.MailAddress             mailTo;
    System.Net.Mail.MailAddressCollection   mailToCollection;
 
    try
    {
        toList = strSplit(_recipient, ';');
         
        permissionSet = new Set(Types::Class);
        permissionSet.add(new InteropPermission(InteropKind::ClrInterop));
        CodeAccessPermission::assertMultiple(permissionSet);
 
        mailServer      = SysEmaiLParameters::find(false).SMTPRelayServerName;
        mailServerPort  = SysEmaiLParameters::find(false).SMTPPortNumber;
        mailClient      = new System.Net.Mail.SmtpClient(mailServer, mailServerPort);
 
        le = toList.getEnumerator();
        le.moveNext();
         
        mailFrom    = new System.Net.Mail.MailAddress(sender);
        mailTo      = new System.Net.Mail.MailAddress(strLTrim(strRTrim(le.current())));
        mailMessage = new System.Net.Mail.MailMessage(mailFrom, mailTo);     

        mailToCollection = mailMessage.get_To();
        while (le.moveNext())
        {
            mailToCollection.Add(strLTrim(strRTrim(le.current())));
        }
         
        mailMessage.set_Priority(System.Net.Mail.MailPriority::High);
        mailMessage.set_Subject(subject);
        mailMessage.set_Body(body);
 
        mailClient.Send(mailMessage);
        mailMessage.Dispose();
 
        CodeAccessPermission::revertAssert(); 
        info(strFmt('Email was sent to vendor %1.', _vendor));
    }
    catch (Exception::CLRError)
    {
        e = ClrInterop::getLastException();
        while (e)
        {
            info(e.get_Message());
            e = e.get_InnerException();
        }

        CodeAccessPermission::revertAssert();
    }
}

Initializes a new instance of the Batch class.

public static MAX_VendorBlockedBatch construct()
{
    return new MAX_VendorBlockedBatch();
}

Gets description of the dialog.

public static ClassDescription description()
{
    return 'Vendor blocked batch';
}

Find the vendor without transaction and disable, then send email to vendor

public void run()
{
    VendTrans   vendTrans;
    VendTable   vendTable;
    TransDate   beginDate;
    Email       email;
    int         i;

    try
    {
        beginDate = this.getBeginDate();
        while select forUpdate AccountNum, Party from vendTable
            Notexists join vendTrans
            where vendTrans.AccountNum == vendTable.AccountNum
                && vendTrans.TransDate >= beginDate
        {
            //Set the vendor blocked
            ttsBegin;
            vendTable.Blocked = CustVendorBlocked::All;
            vendTable.update();
            ttsCommit;

            //Send E-mail to vendor
            email = vendTable.email();
            if (email)
            {
                this.sendEmail(vendTable.AccountNum, email);
            }
            else
            {
                warning(strFmt('The vendor %1 did not have E-mail address.', vendTable.AccountNum));
            }
        }
    }
    catch (Exception::Deadlock)
    {
        retry;
    }
}

Provides an enter point for the Batch class.

public static void main(Args _args)
{
    MAX_VendorBlockedBatch vendorBlockedBatch = MAX_VendorBlockedBatch::construct();

    if (vendorBlockedBatch.prompt())
    {
        vendorBlockedBatch.run();
    }
}

From here you can run class and set up recurrence for batch job.

Thank you for reading!