Tuesday, 7 August 2012

Creating a batch class in Dynamics AX 2009



For creating a batch class in Dynamics AX we can start from the Tutorial_RunbaseBatch class,
 but here is the explanation for understanding all its methods:

In the classDeclaration, we declare any variable that contains user's selection or preferences
 (for example, the customer account for launch a customer report). Also, we declare other
 variables like dialog fields, auxiliar variables, etc. But, the most important feature here, is the #localmacro.CurrentList declaration. This macro helps you to define which variables you want
 to save for running the class in batch mode (when not user interaction is available).

class Tutorial_RunbaseBatch extends RunBaseBatch
{
      //Packed variables
      TansDate           transDate;
      CustAccount      custAccount;

      // Dialog fields
      DialogField        dlgCustAccount;
      DialogField        dlgTransDate;

      // Current version
      // We can different versions for different purposes
      #define.CurrentVersion(1)
      #define.Version1(1)
      #localmacro.CurrentList        // Our current variable list for save user's selected values
        transDate,
        custAccount
   #endmacro
}

The macro #define.CurrentVersion(1) and #define.Version1(1) are useful for scaling our class
 when we need to change/upgrade it preserving compatibility.

Now two methods: pack and unpack. These methods are used for store and retrieve user's 
preferences and perform the batch operation when none user are available (in batch mode).
 Here is the point where we use the macro defined previously:



public container pack()
{
    return [#CurrentVersion,#CurrentList];
}

public boolean unpack(container packedClass)
{
    Version version = RunBase::getVersion(packedClass);
;
    switch (version)
    {
        case #CurrentVersion:
            [version,#CurrentList] = packedClass;
            break;
        //case #otherVersion:
            //[version,#CurrentList, #otherList] = packedClass;
            //break;
        default:
            return false;
    }

    return true;
}


Bacause we need to ask the user about preferences for run this object (report,
 process, query, etc.), we must implement some methods:

dialog: for build the custom dialog for prompting user's preferences.
dialogPostRun: after dialog was created, you can perform other UI tasks here.
getFromDialog: used for retrive user's preferences (dialog values).
validate: here, you can perform a validation for any data entered by the user.

public Object dialog()
{
    DialogRunbase       dialog = super();
    ;
 
    // Creation of our custom dialog fields/controls to ask user for preferences
    dlgTransDate = dialog.addFieldValue(typeid(TransDate),transDate);
    dlgCustAccount = dialog.addFieldValue(typeid(CustAccount),custAccount);

    return dialog;
}

public void dialogPostRun(DialogRunbase dialog)
{
;
    super(dialog);
}

public boolean getFromDialog()
{
    ;

    // Retrieving user's preferences
    transDate   = dlgTransDate.value();
    custAccount = dlgCustAccount.value();

    return super();
}

public boolean validate()
{
    // We can perform some validations here
    if (false)
        return checkFailed("");

    return true;
}


Finally, the main method constructs the object from this batch class, and after
 prompting user and validation was successful, the run method is called to perform 
some task (in batch mode if it was selected):

server static Tutorial_RunbaseBatch construct()
{
    return new Tutorial_RunbaseBatch();
}

public void run()
{
    try
    {
        // Obviously, this code is silly, why show something to nobody?
        // remember, this code is running in batch without user interaction
        info(strfmt("Parameters: %1 for %2", transDate, custAccount));
    }
    catch(Exception::Deadlock)
    {
        retry;
    }
    catch(Exception::UpdateConflict)
    {
        throw Exception::UpdateConflict;
    }
    catch
    {
        throw Exception::Error;
    }
}

static void main(Args args)
{
    Tutorial_RunbaseBatch    tutorial_RunBase;
    ;

    // Instanciating this batch class
    tutorial_RunBase = Tutorial_RunbaseBatch::construct();

    // Prompting user and run if all is ok
    if (tutorial_RunBase.prompt())
        tutorial_RunBase.run();
}

2 comments:

  1. Why use runsImpersonated() method in batch classes??

    ReplyDelete
  2. runsImpersonated() method is used to Determines if the batch task is run on the server or on a client.

    True to run the task on the server; otherwise false.

    Your classes that extend RunBaseBatch must override the runsImpersonated method and return false, if you want those tasks to run on a client.

    ReplyDelete