Skip to main content

I’ve been having trouble getting a custom event handler to fire. I’ve followed this process...

...as closely as I could, and while the action that fires the event runs, the transition triggered by the event does not. 

I feel like I’m missing something obvious, but I just can't put my finger on it. 

I’m working with 24R1 Build 24.101.0059.

For context, this is more an exercise in personal edification than looking for a workaround. 

Any input would be appreciated. 

 // Acuminator disable once PX1016 ExtensionDoesNotDeclareIsActiveMethod extension should be constantly active
public class APInvoiceExtZ : PXCacheExtension<PX.Objects.AP.APInvoice>
{
public class Events : PXEntityEvent<APInvoice>.Container<Events>
{
public PXEntityEvent<APInvoice> DescChanged;
}
}



// Acuminator disable once PX1016 ExtensionDoesNotDeclareIsActiveMethod extension should be constantly active
public class APInvoiceEntry_Ext : PXGraphExtension<APInvoiceEntry>
{

public PXAction<APInvoice> ChangeDescription;
nPXUIField(DisplayName = "ChangeDescription")]
)PXButton]
protected virtual IEnumerable changeDescription(PXAdapter adapter)
{
Base.Document.Current.DocDesc = "The Action changed this Description";

APInvoiceExtZ.Events
.Select(e => e.DescChanged)
.FireOn(Base, Base.Document.Current);

return adapter.Get();

}


#region Entity Event Handlers
public PXWorkflowEventHandler<APInvoice> OnDescChanged;
#endregion
}



// Acuminator disable once PX1016 ExtensionDoesNotDeclareIsActiveMethod extension should be constantly active
public class APInvoiceEntry_WorklowExt : PXGraphExtension<APInvoiceEntry_Workflow, APInvoiceEntry_Ext, APInvoiceEntry>
{
public sealed override void Configure(PXScreenConfiguration config) { Configure(config.GetScreenConfigurationContext<APInvoiceEntry, APInvoice>()); }

protected static void Configure(WorkflowContext<APInvoiceEntry, APInvoice> context)
{
context.UpdateScreenConfigurationFor(screen =>
{
return
screen
.WithActions(actions =>
{
actions.Add<APInvoiceEntry_Ext>(g => g.ChangeDescription, a => a.WithCategory(PredefinedCategory.Actions));
})

.WithHandlers(handlers =>
{
handlers
.Add(handler =>
{
return
handler
.WithTargetOf<APInvoice>()
.OfEntityEvent<APInvoiceExtZ.Events>(e => e.DescChanged)
.Is<APInvoiceEntry_Ext>(g => g.OnDescChanged)
.UsesTargetAsPrimaryEntity()
.DisplayName("OnDescChanged");
});

})

.UpdateDefaultFlow(flow =>
flow
.WithFlowStates(fss =>
{
fss.Update<APDocStatus.hold>(flowState =>
{
return flowState
.WithActions(actions => actions.Add<APInvoiceEntry_Ext>(g => g.ChangeDescription))
.WithEventHandlers(handlers => { handlers.Add<APInvoiceEntry_Ext>(g => g.OnDescChanged); });
});

})

.WithTransitions(transitions =>
{
transitions.UpdateGroupFrom<APDocStatus.hold>(ts =>
ts.Add(t => t
.To<APDocStatus.balanced>()
.IsTriggeredOn<APInvoiceEntry_Ext>(g => g.OnDescChanged)
)
);
})
);
});
}
}

 

@Keith Richardson 


I notice there is no saving in the action, give that a try.

    APInvoiceExtZ.Events
        .Select(e => e.DescChanged)
        .FireOn(Base, Base.Document.Current);
    Base.Persist();


Thanks for taking my question, Keith.

I had tried Base.Save.Press(). Unfortunately neither that nor Base.Persist() are doing the trick. It’s performing the rest of the action fine. The Description updates and saves as expected. But still no transition. 

I’m still learning the workflow API. Would the fact that the hold state is a substate of the H > B state on the APInvoiceEntry default workflow impact how the transition fires? 


Looking through the 2023r2 code (which I am on) I can see one thing - and it may be due to the document type you picked to test on. This screen has a sequence, and the states are buried in there:

 

All of the transitions to Open come from HoldToBalance. 

Inside there, there is a sequence, and many of the actions go move to next.

For example, when adding the approval workflow, it states:

 that Pending Approval is added after hold, and is skipped when approved or rejected. So if approvals are not running, it goes to the next, and the next, etc.

So how are these triggered?
The transition shows going from hold to balance to itself when OnUpdateStatus is triggered:


This is defined as a handler tied to updating the hold flag:
 



So because of this screen, and how the workflow is configured, I would use existing logic and just remove from hold, when your handler is called, and give that a try:




Give this a try:
 

.WithHandlers(handlers =>
{
handlers
.Add(handler =>
{
return
handler
.WithTargetOf<APInvoice>()
.OfEntityEvent<APInvoiceExtZ.Events>(e => e.DescChanged)
.Is<APInvoiceEntry_Ext>(g => g.OnDescChanged)
.UsesTargetAsPrimaryEntity()
.WithFieldAssignments(fas => fas.Add<APInvoice.hold>(f => f.SetFromValue(false)))
.DisplayName("OnDescChanged");
});

})

 


Thank you again, Keith. 

Although that would have been a perfectly fine work around in a pinch, I knew I was supposed to be able to manipulate the sub-states with more nuance than being slave to the hold flag. 

 

You were exactly right, that it had to do with the sequence on the screen. I ended up digging into sequences, and here’s what I was able to come up with:

//Replace
.WithFlowStates(fss =>
{
fss.Update<APDocStatus.hold>(flowState =>
{
return flowState
.WithActions(actions => actions.Add<APInvoiceEntry_Ext>(g => g.ChangeDescription))
.WithEventHandlers(handlers => { handlers.Add<APInvoiceEntry_Ext>(g => g.OnDescChanged); });
});
})

//With

.WithFlowStates(fss =>
{
fss.UpdateSequence<APDocStatus.HoldToBalance>(seq =>
seq.WithStates(sss =>
{
sss.Update<APDocStatus.hold>(flowstate =>
{
return
flowstate
.WithActions(actions =>
actions.Add<APInvoiceEntry_Ext>(g => g.ChangeDescription)
)
.WithEventHandlers(handlers =>
{
handlers.Add<APInvoiceEntry_Ext>(g => g.OnDescChanged);
});
}
);
}
)
);
})

This got the actions and events working as intended, and gave me full control over the workflow.

It seems existing sequence states(sub states)/actions/events must be manipulated at the sequence method level, not directly in the flowstate method level. 


Reply