Part 2 of the series that I said that I was going to write has been a long time coming. If you don’t know what I’m talking about you might want to read the first post in the series here. Unfortunately it is possible that this series reflects the functionality that it is describing: full of early promise, but on closer inspection a little convoluted and disappointing. I’ll leave you to be the judge of that.
Unfortunately, I’ve just found the framework a little annoying to work with. Maybe I’m missing the correct way to use it but it seems like there are too many objects involved without providing the functions that I was expecting. Then again, if I am missing the best way to use it then that illustrates my point – it’s just not very friendly to work with. I’ll try to make some constructive suggestions as we go.
A quick reminder of what we’re trying to achieve here. I’ve got a journal page to record all the video calls for work and family that I’m having.* Before the journal is posted the lines are checked for lots of potential errors. Rather than presenting the user with one error at a time we are trying to batch them all together and present them in a list to be resolved all at once. I’m using the error message handling framework to do it.
*to be clear, I’m not using it. I’m sad…but not that sad**
Some basic principles to bear in mind when dealing with the Error Message Mgt. codeunit
- You need to trap all the errors
- The framework provides a way of collecting messages and displaying them to the user in a list page. It doesn’t fundamentally change how error handling in BC works. If you encounter an un-trapped error the code execution will stop, transaction be rolled back etc.
- Obviously that includes TestField() and FieldError() calls, not just Error()
- The Error Message framework must be activated before calling the code that you want to trap errors for
- Call PushContext to set the current context for which you are handling errors
- Call Finish to indicate that the previous context is complete
- You need to determine whether there are any errors to display and then, if so, display them
This is where the posting of my journal batch begins. We need to activate the error handling framework and if an error is trapped in the posting codeunit then show the errors that have been collected.
There is a ShowErrors method in the Error Message Management codeunit, but its only for on-prem. Don’t ask, I don’t know. You need to use if Codeunit.Run (or a TryFunction I suppose – although don’t) to determine whether to there are any errors to show. There is a HasErrors method in the Error Message Handler codeunit but that’s also only for on-prem. Still don’t ask.
procedure Post() var VideoCallBatchPost: Codeunit "Video Call Post Batch"; ErrorMessageMgt: Codeunit "Error Message Management"; ErrorMessageHandler: Codeunit "Error Message Handler"; begin ErrorMessageMgt.Activate(ErrorMessageHandler); if not VideoCallBatchPost.Run(Rec) then ErrorMessageHandler.ShowErrors(); end;
It would have been nice if there was a way to do with without declaring an extra two codeunits – but I don’t think there is.
Onto the next level on the callstack. Call PushContext with a record that gives the context within which the errors are being collected. Run the code that we want to collect errors from and then Finish. If any errors have been encountered the Finish method will throw an error with a blank error message to ensure that the transaction is rolled back to the last commit.
If Finish is called when GuiAllowed is false then SendTraceTag is called to “send a trace tag to the telemetry service”. Interesting.
local procedure PostBatch(VideoCallBatch: Record "Video Call Batch") var VideoJnlLine: Record "Video Journal Line"; ErrorMessageMgt: Codeunit "Error Message Management"; ErrorContextElement: Codeunit "Error Context Element"; begin ErrorMessageMgt.PushContext(ErrorContextElement, VideoCallBatch, 0, ''); VideoJnlLine.SetRange("Batch Name", VideoCallBatch.Name); VideoJnlLine.FindSet(); repeat VideoJnlLine.Post(); until VideoJnlLine.Next() = 0; ErrorMessageMgt.Finish(VideoCallBatch); end;
Now into the journal line posting and all the checks that are performed on each line. I won’t copy out the entire function – it’d be a bit tedious and you can check the source code afterwards if you’re interested.
local procedure Check(var VideoJnlLine: Record "Video Journal Line") var GLSetup: Record "General Ledger Setup"; ErrorMessageMgt: Codeunit "Error Message Management"; begin if VideoJnlLine."No. of Participants" = 0 then ErrorMessageMgt.LogErrorMessage(VideoJnlLine.FieldNo("No. of Participants"), StrSubstNo('%1 must not be 0', VideoJnlLine.FieldCaption("No. of Participants")), VideoJnlLine, VideoJnlLine.FieldNo("No. of Participants"), ''); if VideoJnlLine."Duration (mins)" = 0 then ErrorMessageMgt.LogErrorMessage(VideoJnlLine.FieldNo("Duration (mins)"), StrSubstNo('%1 must not be 0', VideoJnlLine.FieldCaption("Duration (mins)")), VideoJnlLine, VideoJnlLine.FieldNo("Duration (mins)"), ''); if VideoJnlLine."Posting Date" = 0D then ErrorMessageMgt.LogErrorMessage(VideoJnlLine.FieldNo("Posting Date"), StrSubstNo('%1 must be set', VideoJnlLine.FieldCaption("Posting Date")), VideoJnlLine, VideoJnlLine.FieldNo("Posting Date"), '');
This is where it really starts to get a bit messy. The TestFields are gone, replaced with calls to LogErrorMessage. LogError and LogSimpleErrorMessage are alternatives with slightly different signatures. Pass in the field no, error message, record and “help article code” related to the error and they will be collected by the framework.
If any errors have been logged then the Finish function (see above) will throw an (untrapped) error and prevent the journal from actually being posted.
I really tried to enjoy working with this. I’d like to have better error handling in our apps – but I don’t think we’re going to get round to introducing this sort of thing any time soon. The parameters on this method are too clunky.
- It requires a “context” field no. and a “source” field no. – I’m still not clear what the difference is
- I have to provide the error message text. That’s a problem. With TestField I can leave the system to generate the correct error text, in whatever language the client is set to. This way I have to create a label (I didn’t in my example because I’m lazy) and then translate it into different languages
- I don’t know what I’m supposed to provide for “help article code”
- I was hoping for an ErrorMessageMgt.TestField method. Couldn’t I just pass in my record and the field no. that I’m testing? I want to leave the framework to determine if an error needs to be logged and, if so, the correct text
You can view the changes that I’ve made since the first blog post here: https://github.com/jimmymcp/error-message-mgt/commit/6d768c5552c0fad433e75dea15a7d1d064cb040c
I’d love someone to tell me that I’ve missed how easy this framework is to work with and they’ve had a great time with it. It looked like it was going to be great but left me a bit flat. Like a roast potato that you’ve saved for your last mouthful at Sunday lunchtime only to discover it’s actually a parsnip.