Shared posts

12 Nov 11:44

X++ - Cancelling the event / method in its Pre event handler

by Abdul Qadir Khan
Eventing enables to extend applications as well to decouple the components and underlying implementations. It is a great way to write new logic without worrying a change in the underlying components.

Automatic Event Handlers

X++ provides custom delegates as well as Automatic event handlers
  • Pre - Occurs before
  • Post - Occurs after

Pre Event Handler

One of the responsibilities / usage of the Pre event handler is to validate if all conditions are correct for the commencement of the main method. Or to populate / fill any other component(s) which is required in the main method.

Cancel Event

This became a pain for me last night. I was trying to do something that we does in DotNet windows application :D 

e.Cancel = true;

Where is that ? Answer is, perhaps no where :P (educate me if I am wrong please)

But there are alternates to it. Thanks to my ex colleague and friend Asim Saeed's blog post: Yes it is quite a natural alternate, simply throw an exception.

Problem

But that will end the whole program execution, unless caught in any catch and taken care there. What I mean is, consider a complex logic that spans multiple classes or methods. What if one of the optional method's Pre event handler simply throw's exception whereas the process should have continued. In such case, that particular event should be cancelled rather than the whole program flow. I want to have granular control here, only cancel the specfic event producer method, not the whole process 

One way to do this is to have a flag parameter in the event producer method's parameters, set it to false in the Pre event handler on particular condition (i.e. if things are not found good to continue this event). Then at the start of event producer method, simply return out if the status flag is found to be false.

To demonstrate the same, we have 2 classes,

  • CMaqkProducer_Event
    • With a method and its event handler subscription
  • CMaqkConsumer_Event
    • With event handlers registered to handle event subscription defined on the producer

The code for the producer class is provided for our reference;

Producer class: Maq_EventProducer




class Maq_EventProducer
{
#PC
//Macros for Product Configuration, carries #true and #false for sting to bool conversions
}

///
/// This method is producing a Pre event handler
///

///
/// A sample parameter passed to the method for
///
///
/// Status parameter passed from XppPrePostArgs class containing the result from Pre event Handler
///
///
/// We will return in the begining of this method if the results form Pre event Handler are not ok
///

public void method1(str _str, str _status = "")
{

if (_status == #false)
return;
// continue; ;)
info("Maq_EventProducer.method1() called");

}

///
/// Another method besides method1()
///

///
/// This method exists and is called to demonstrate continuation of program flow besides event cancellation
///

public void method2()
{
info("second method, Maq_EventProducer.method2() called");
}


Notice the line 'return;' at the begining of method1() after evaluating the _status defaulted value (string) value with #true. Using this theme, we cancel the whole event from progress.

Consumer Class: Maq_EventConsumer


class Maq_EventConsumer
{
#PC
//Macros for Product Configuration

Maq_EventProducer objProducer; //Reference to producer
}


///
/// parm method to access producer
///

///
/// the reference variable to the event producer class object
///
///
///
///
///
///
///

public Maq_EventProducer parmObjProducer(Maq_EventProducer _objProducer = objProducer)
{
objProducer = _objProducer;

return objProducer;
}

///
/// The static main method
///

///
/// arguments passed
///
///
///
///

public static void main(Args args)
{
Maq_EventConsumer objConsumer = new Maq_EventConsumer();
Maq_EventProducer objProducer = new Maq_EventProducer();
objConsumer.parmObjProducer(objProducer);
objConsumer.run();
}



///
/// the default logic processing method of consumer class
///

///
///
///

public void run()
{
info("Consumer class run() method called");
this.parmObjProducer().method1("Maqk");
this.parmObjProducer().method2();

}


///
/// The pre Event Handler
///

///
/// payload
///
///
///
///

public static void preClass1_Method1(XppPrePostArgs _args)
{
info(strFmt("pre event handler called"));

// Cancelling the event by setting the function's parameter, "_status" = 0
_args.setArg("_status",#false);

// other code logic body may go here
// other code logic body may go here

// Checking if some logic has asked to cancel the event
if (_args.getArg("_status") == #false)
warning("Class1 method1() event cancelled due to condition");

// Later in the event producer method body, we will check the value and would work accordingly if found 0

}

Result


Calling the consumer class's run() method calls both the methods of producer class. The snapshot below shows the program flow result.

Results - Program flow continues only canceling the event

The Pre Event Handler cancels the main event, but the program flow continues. Had we thrown error / warning in the Pre event handler, producer's method2() would not have been called. But now using the event payload (XppPrePostArgs) and having a default value parameter theme, we can only cancel the event we want to and not the whole program flow.
26 Aug 21:52

AIF – How to get a better error message and stack trace for a failed document?

by cocochambo .

How many times when working with AIF you got an error and have no idea what caused the issue. The document fails for many reasons and what you get is a very generic error message. Debugging AIF is a painful process and time consuming. Below is the trick that we can find out more descriptive error message and even a stack trace.

image

image

Run the job below and replace the GUID with the GUID from the document that failed

static void TestOutboundMessage(Args _args)
{
    guid id = str2guid("{BCE9B13D-3F3C-497F-A566-55606C3F19B4}");
    
    AifQueueManager document;

    select firstOnly document where document.MessageId == id;

    // This static method is declared as private and this trick is to avoid changing the access specifier on the class
    new SysDictClass(classNum(AifOutboundProcessingService)).callStatic('processAsUser', [document.MessageId]);

}

Refresh and review the error again, you will see a more descriptive message and a stack trace that can save your life.

image


06 May 18:48

AX 2012 R2 – Where is the picking lines created ?

by Kurt Hatlevik

In R2 there are several steps and places that picking routes/Picking lines are created, depending if you have an order-based picking or consolidated picking.

The fundamental element for a picking route is output orders, that acts as the basis.

And Output orders are created through the class\WMSOrderCreate:

clip_image001

As soon as the WMSOrder is created, it can try to attach itself to a shipment (consolidated scenario)(Also WMSOrderCreate)

clip_image002

If you follow the trace in the wmsOrder.autoAddShipment() you will get to the code where the WMSOrderTrans is created.

WMSOrderTrans is created here

clip_image003

And here :

clip_image004

The final step is the creation of the WMSPickingRoute, that happens in the shipment reservation.  And inside this method you will also find the creation process for the picking route.

clip_image005

So the sequence and steps for creating a picking route is the following :

1. WMSOrder is created

2. WMSShipment is created

3. WMSOrderTrans is created

4. WMSPickingroute is created

Also remember that the structure of shipmentId, PickingrouteID’s is very loose entities, and is not strongly anchored to inventTrans. So it is possible to move wmsorderTrans transactions between pickingroute and shipments quite easily by using X++.

 

Happy DAX’ing