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!

  • There is a maximum of one Primary Key per table, whereas a table can have several alternate keys. The primary key is usually the type of key that other tables, called child tables, refer to when a foreign key field in those other tables need a relational identifier.

  • For new tables the default is a primary key based on the RecId field , incremented number or a completely meaningless number that is generated by the system surrogate key.

  • Alternate key can be chosen as the Replacement Key of a table that can display on forms instead of a meaningless numeric primary key value. Each table can have a maximum of one replacement key.

  • Natural key has meaning to people. Most replacement keys are natural keys.

  • Relations represents a foreign key.

Thank you for reading!

In Dynamics AX, there is a class called SysQueryRangeUtil that can be utilized in both query ranges and table filters. Using methods from this class allow you to be very precise about what dates you want to use in reports or for filtering your data.

Let’s say you have a report that you always want to run to see orders with shipping dates of the next day. It is possible to do so by using one of the methods from the SysQueryRangeUtil. The use of the letter ‘t’ will work for today’s date, but when you try to add days to it, it doesn’t work in reports. Instead, I will use the currentdate() method and add 1 to it.

All methods & expressions must be surrounded by parentheses as shown below.

Figure 1 – Filtering the requested ship dates in an AX query for tomorrow

Filtering the requested ship dates in an AX query for tomorrow (current day() + 1)

On any form with a grid, you filter your data by pressing Ctrl+G.

If I were to want to see open customer invoices from the last 90 days, I would filter my open customer invoices form and use the method (dayRange(-90,0)). The first number represents how many months backward from this month, and the second represents how many months forward. The same sorts of things can be done for the monthRange(), yearRange(), and dateRange() methods. The best part about this is that you can of course save these filters to create views that you might use on a daily basis.

Figure 2 – Searching the grid for the past 90 days

If you are creating your query ranges in code, these methods can also be utilized whenever you are setting them. Definitely be sure to check out the SysQueryRangeUtil class as there are many more methods to use.

Here is some methods you can use:

currentCustomerAccount()

currentVendorAccount()

currentUserId()

currentDate()

dateRange()

day()

dayRange()

greaterThanDate()

greaterThanUtcDate()

greaterThanUtcNow()

lessthanDate()

lessthanUtcDate()

lessthanUtcNow()

monthRange()

yearRange()

for example:

(dayRange(-30,0)) – Results in a date range for the last 30 days: "26-01-2017".."25-02-2017"

(day(-1)) – Results in yesterday's date: 24-02-2017

(day(0)) – Results in today's date: 25-02-2017

(day(1)) – Results in tomorrow's date: 26-02-2017

(greaterThanDate(2)) – Results in every date after today plus 2: > 27-02-2017

(lessThanDate(-1)) – Results in every date of today minus 1: < 24-02-2017

(monthRange(0,2))  Results in first day till the last day of the month's choosen (0 = current month): "01-02-2017".."30-04-2017"

(yearRange(-1,-1))  Results in first day till the last day of the chosen year: "01-01-2017".."31-12-2017"

Thank you for reading!

  • There is a maximum of one Primary Key per table, whereas a table can have several alternate keys. The primary key is usually the type of key that other tables, called child tables, refer to when a foreign key field in those other tables need a relational identifier.

  • For new tables the default is a primary key based on the RecId field , incremented number or a completely meaningless number that is generated by the system surrogate key.

  • As The RecId data type, surrogate keys exist on a primary key table. As The RefRecId ETD, surrogate foreign keys exist on a foreign key table (Ex: Party field is on CustTable Table).

  • Alternate key can be chosen as the Replacement Key of a table that can display on forms instead of a meaningless numeric primary key value. Each table can have a maximum of one replacement key.

  • Natural key has meaning to people. A set of fields that uniquely identify a record and would have formed the primary key of the table, if not for the existence of a surrogate key.

  • Relations represents a foreign key.

Thank you for reading!

InventSum is needed to recalculate sometimes. We should use InventSumRecalcItem class in Dynamics AX.

InventSumRecalcItem InventSumRecalcItem;
;
InventSumRecalcItem = new InventSumRecalcItem("ITEM001", true, checkfix::fix);
InventSumRecalcItem.updatenow();

First parameter : ItemId

Second parameter : Show errors

Third parameter : Fix or only check

What if you want to calculate for all items:

InventTable InventTable;
InventSumRecalcItem InventSumRecalcItem;

WHILE SELECT InventTable
	WHERE (InventTable.ItemType == ItemType::Item) || (InventTable.ItemType == ItemType::BOM)
	{
		InventSumRecalcItem = new InventSumRecalcItem(InventTable.ItemId, true, checkfix::fix);
		InventSumRecalcItem.updatenow();
	}

Thank you for reading!