Context.

Events allow us to change third part features. But I miss something when I subscribe an event: state. I can´t set any parameters from caller to publisher. Subscripted Code unit never has an instance. Is an object in the air as if we do this call: CODEUNIT::Subscriber Codeunit.

Example: combine shipment.

Some customizations are so common that I feel they are in standard NAV code. One of this is add a default shipping address to customer card. Other is set a new posting series number in shipping combination.
Now, I can´t modify the core object Report Combine Shipments to do this. Moreover, I don´t like to touch any core object, that was the beginning of a lot of problems. I may disagree with use AL only, but I fully agree with not touch the core no more.
So, without inappropriate core alterations there are only two options:
  • Object cloning. Gurus warn us about the evil of duplication, but with a very small object is venial sin only. With Combine Shipments report, I want to avoid this practice because it has a respectable amount of business logic to become a captivating cloning candidate.
  • Events subscription. Yes, you guessed it: that´s the right option. Combine shipments raises the right events to handle it. But a little point: How can I pass the posting No. series value to these events? We wrote it above, subscriptions are stateless, and I can´t store a value to use it latter.

Manual subscription.

A quick review:
  • Manual subscription is enabled when a Conduent property EventSubscriberInstance is settled to Manual.
  • That means that the Codeunit subscriptions will not be launched until we do the next actions: declare an instance variable of the Codeunit and apply the statement BindSubscription to this instance.
So, we got it, because the key expression is instance. When we create a Codeunit instance we can store variables and state in variable instance. When the event subscription is launched in the instance, this subscription can take global variable values.
That´s the way we get state in our event subscriptions.

Running example.

We run a new repot to ask for “Posting No. Series”:

 
This report calls standard report Combine Shipments execution:

 
Finally, invoices are created with selected posting number series, this is the difference from the standard feature Combine Shipments.

Listener Codeunit.

We need two components for this feature. First a new Codeunit with manual instance subscription with two key elements:
This Codeunit saves the series number in a global variable:
codeunit 50114 "Event Catching With State"
{
    EventSubscriberInstance = Manual;
    var
        PostingNoSeries: code[20];
    procedure SetPostingNoSeries(NewPostingNoSeries: Code[20])
    var
    begin
        PostingNoSeries := NewPostingNoSeries;
    end;
And subscribes (in manual mode) to OnFinalizeSalesInvHeader event in Combine Shipments to set the settled posting series:
    [EventSubscriber(ObjectType::Report, report::"Combine Shipments", 'OnFinalizeSalesInvHeader', '', true, true)]
    local procedure SetPostingHeader(var SalesHeader: Record "Sales Header")
    begin
        if PostingNoSeries = '' then
            exit;
        SalesHeader."Posting No. Series" := PostingNoSeries;
        SalesHeader.Modify();
    end;
 

Process wrapper.

Second object is a new report, with these elements:
A Request Page to ask for posting no. series:
                group(GroupName)
                {
                    field("Posting No. series"; PostingNoSeries)
On PreReport trigger we active events subscription in a new Codeunit instance with bindsubscription, setting the new no. series in the Codeunit instance:
    trigger OnPreReport()
    var
        CombineShipments: Report "Combine Shipments";
        EventCatchingWithState: Codeunit "Event Catching With State";       
    begin
        BindSubscription(EventCatchingWithState);
        EventCatchingWithState.SetPostingNoSeries(PostingNoSeries);
        Report.RunModal(Report::"Combine Shipments");
        UnbindSubscription(EventCatchingWithState);
    end;
And then, Combine Shipments report is called inside. Codeunit Event Catching With State, will trigger in every document finalization passing stored Series No. to new sales document.

Conclusion. The big downside.

This is a very good system to set information in an event subscription, but I see a weakness: is hard to explain. Someone told that if something not easy to explain, might not be a good idea. I know, I am shooting myself, but its important to transfer program decision in the easiest way, and this decision could be a bit hard to transfer.
In the debit column two pros:
  • There are few alternatives. Write a parameter table could be one.
  • This technique has been a good way to show all implications of manual event subscription: not only bind is manual, we have an instance with the able to save state.