Wednesday, November 16, 2011

How Alerts in AX work with templates and emails

Alerts in AX can be very useful, and I wanted to touch on how they work, along with some explanations, tips, and tricks.  Much of this post is basic, but there are some caveats explained as well.  I mainly use alerts to notify a user when a field or record is created, modified, deleted, etc.  A user specifically wanted to know whenever a credit limit changed for a this is the scenario I will be covering.

You first need to make sure you have an alert email template setup.  This can be done from Basic>Setup>Email Templates.  Here you will create your header alert record, and your line records are for different languages should you need them.

After you create the basic one as pictured, you click the "Template" button where you define your template.  I suggest putting in the following HTML code under the HTML tab from

You then need to configure the alerts parameters at Basic>Setup>Alerts.  You want to choose the "Alerts" email id template you just created.

The "Drill-down target" can be confusing...but here is my interpretation of it.  You basically put something different for each of your AX environments (Dev/Test/Prod).  When you have an alert email sent automatically with hyperlinks, it will be something like Dynamics://DEV... (instead of http://DEV...) that will be clickable, and open an AX client and go directly to the alert or the alert's origin.

When you click the link, it basically tries to go to your default client config, and then it confirms that the "DEV" found in the URL also is found in the alerts setup.  If it is not, it just doesn't open the client.  So if you are getting alerts from Dev/Test/Prod, only the ones that have the correct Dynamics://[this part] will work.  This obviously means you must have a local client installed.

The types of alerts:
  • Changed based alerts
    • Alerts that occur when something changes, like a customer name, or customer credit limit
  • Due date alerts
    • Alerts that occur when something is due, typically workflow related

These both have batch jobs, located at Basic>Periodic>Alerts that should be running in order to actually create the alerts that meet the rule criteria.  If you aren't getting the alert bell pictures in the lower corner of your session, then one of these is probably not running correctly.

Now let's setup the alert rule to notify us when the credit limit has changed for a customer.  You can set alerts on anything pretty much you right click on.  Go to your customer details form, then the general tab, then right click on the credit limit field and click "Create alert rule" as pictured:

Now base AX only lets you alert one user.  You can create a user that has a distribution list for an email account, or you can do some simple modifications in X++ to support overwriting the alert email, or alerting multiple emails/users.  For now, you can just click OK here and then close the window behind it.

If we go to a customer and change their credit rating, an alert will be generated (provided the batch is running as stated earlier or it is run manually).

For the email portion, the way alerts are setup is to have traceable emails.  So what it does is dump it into an email sending queue that can be found at Administration>Periodic>E-mail processing>E-mail sending status.

This emailing subsystem is very handy, and open for many useful modifications if you're feeling creative.  Emails in this queue are picked up for delivery by the e-mail processing batch, which is located at Administration>Periodic>E-mail processing>Batch.  This must be running for the emails to go out.

You can view sent emails with the body of the message, and also resend them should you choose to do so.

There is also a retry schedule that is located in the same place where you can set your retry intervals for failed email attempts.

The framework is here behind the scenes to open it up for some very useful modifications.  For example, if you periodically email customers a survey, and you want to send them reminders to fill it out, or you are waiting for any other sort of user input and you want to send emails until they perform their action.

This just briefly outlines and touches on alerts and emails.  If you have any questions, feel free to comment.  I will be elaborating on email templates later on.

Tuesday, November 15, 2011

How to call a form from code and make it modal

This is how to open a form from X++ and set it modal.  Modal means it is on top of the other windows and you can not click on the ones behind it.  I have this just in a clicked event on a button.

void clicked()
    CustTable   custTable;
    FormRun     formRun;
    Args args = new Args();
    // Just selecting a random record in CustTable
    select firstonly custTable;

    // This is the name of the form you are opening;
    // This is the optional record you are passing it

    // This builds the formrun
    formRun = ClassFactory.formRunClass(args);

    // This waits until the form closes
    if (!formRun.closed())

Monday, November 7, 2011

Changing dimensions on an item the easy way using xRecord.merge?

Many of us have hit the same issue at some point, where a dimension needs to be changed on an item, and going about doing it can be a very big hassle.

This post is purely experimental, and I am currently assessing the feasibility of it, so please be very critical of it.  I'm looking for feedback on issues I've not yet thought of.

A project I'm working on is changing/removing the serialization dimension on many different items. The customer has gone from AX 3.0 to 4.0 to 2009, so there is a good share of old/bad data, old open transactions, etc.

Our goal is to turn on "blank issue allowed" and "blank receipt allowed" to make transfers/movements easier.

There are two basic ways that I can think of to change the dimensions on the items, and these are very brief steps that would actually require more thought/elaboration in a real scenario:

  • Scrap out all inventory, close all open transactions, then change the dimension
    • Difficulty: High
  • Create a new item with desired dimension, transfer old item to new item, then rename primary key to get the ItemId back to old value
    • Difficulty: Low, but you lose item history, so this often won't work.

I tried to get creative and think out of the box on this one a bit because the amount of work required to close all open transactions is huge, plus could mean several days, maybe even weeks of downtime because of the amount of transactions constantly going on.  This customer can not be shut down for more than a few days too.

My idea is to create a new item with the desired dimension, then use the Kernel xRecord.Merge functionality to merge the item with the old dimension into the newly configured item, then use the primary key functionality to rename the put the ItemId back.  I just tried this yesterday, so I've barely looked at system functionality beyond some simple tests.  The transactions came over, the BOMs came, it has some promise.

Some initial thoughts where this might have some holes...since we are moving the old item into the new item, the new item will have a new recId.  So if there are any tag tables that reference by RecId, this would clearly cause an issue.  I believe if the correct relation is setup though, that it might propagate correctly.  Tables like the Address or DocuRef (document handling) however that use commons and reference by Table and RecId might have some issues.  I have yet to test this.

There are some other minor steps that will need to be done in this code, such as confirming that those tables I delete have the previous values passed over...things such as item price.  If you have other custom code, you may need to add/remove some of the tables I deleted.  The discovery process on this was pretty simple, I ran it, then the validate would fail and I'd delete records out of the table, and rerun until it worked.

The "Master" item here is the new item with the new dimension.  The "child" item is the original item with the dimension we want to change.

Anyway, here is the proof of concept code you've been waiting for.  Please comment with thoughts/concerns/experience:

static void ItemMerge(Args _args)
    ItemId      iMaster = '26101A'; // New item with newly configured dimension
    ItemId      iChild  = '26101';  // Original item with old dimension

    InventTable master;
    InventTable child;
    CUSInventTable          t1;
    InventTableModule       t2;
    InventItemLocation      t3;
    InventItemPurchSetup    t4;
    InventItemSalesSetup    t5;
    ConfigTable             t6;

    delete_from t1
        where t1.ItemId == iMaster;

    delete_from t2
        where t2.ItemId == iMaster;

    delete_from t3
        where t3.ItemId == iMaster;

    delete_from t4
        where t4.ItemId == iMaster;

    delete_from t5
        where t5.ItemId == iMaster;

    delete_from t6
        where t6.ItemId == iMaster;

    child = InventTable::find(iChild, true);
    master = InventTable::find(iMaster, true);