Let’s just say I like construction. I’m never happier than when I am building something, be it something physical or software. With creation comes learning, from creation actually I think. Come to think of it, learning mostly comes from messing stuff up and then fixing it better.
It may seem awkward to confess here that I keep on messing stuff up, after all many people pay me for my expertise. But I would not be an expert if I had not messed up, and learned, so much. My mistakes are like the dirt under my fingernails and the grease stains on my jeans. I wear them with pride.
Another great thing that comes from mistakes are stories to tell. Fortunately we can learn from each other’s mistakes. Don’t listen to negative people, I see people do this. Again and again. Not just people either, we’re not that special. When I have to give one of our cats his medicine the other will run away. Learning from one another just happens.
Back to mistakes. I have such an almighty balls-up to share I hardly know where to begin. It all began about three years ago when my coworkers and myself started using events.
Let’s be clear that what I am about to tell is not exclusive to events, it is something that has been with us for many years. I am about to tell you about our old friend the Commit().
Just a quick recap of the problem. When, in Business Central, you are writing to the database Business Central will wait committing the changes to the database until you are done and all the code has been executed. If at any point an error is raised all changes to the database are rolled back. However when we are running complex routines like posting we sometimes need to manually commit changes to the database before doing some cleanup or some other posting. This is fine, Business Central will never raise an error after a commit. To ensure this a simple design pattern is used.
- Test near
- Test far
- Do it
- Clean up
By moving all testing to before the actual posting we ensure everything is in order before the first commit.
And then Microsoft added a ton of events to all the posting Codeunits. This is not a problem as such, they are useful and necessary to us. Unfortunately by looking at the events list in VS Code we cannot see where the commits are. Enter my mistake, I raised an error after a commit. The result was a ton of half posted documents in a production database.
The problem here was that it was not as simple as removing an error message. This was a very complex extension that ensured the simultaneous and correct posting of multiple documents. Beside using many event subscribers itself it also triggered a lot of custom code in other extensions. Finding this problem, and fixing it, has given me a deeper insight in how to deal with events.
I had four insights that I would like to share.
First, obviously, know what you are subscribing to. This is where events make things harder, because we don’t change the original object we often don’t know what it is we influence. Fortunately Microsoft made it easy for us to check the standard code. Even in Business Central 2019 wave 2 and newer it is easy to unpack your symbols file and check the source code.
Second, keep it simple. I found it is hardly ever necessary to subscribe to events that are deeper than the OnBeforeRun or OnAfterRun events in posting Codeunits. If I do need something deeper it is usually because of a design flaw in my code. This makes sense if you look at the design pattern mentioned earlier. You either set or test something before posting or you clean up something after posting.
Third, leave the actual posting alone. The posting routines have been designed and tested in order to build trust. Accountants trust the posting routines of Business Central. If we start messing about with posting routines we lose that trust. Besides, the actual posting is also used by other third party extensions. If you touch the posting itself you will break something else.
Fourth, if you can’t keep it simple then override. Most posting Codeunits have Codeunits that call them. In those Codeunits you can override the standard posting Codeunit. This is a last resort though, it will certainly mean compatibility issues with other third party extensions.
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Whse.-Post Shipment (Yes/No)", 'OnBeforeConfirmWhseShipmentPost', '', false, false)] local procedure OnBeforeConfirmWhseShipmentPost(var WhseShptLine: Record "Warehouse Shipment Line"; var HideDialog: Boolean; var Invoice: Boolean; var IsPosted: Boolean); var CustomPostingRED: Codeunit "Custom Posting RED"; begin if IsPosted then exit; if CustomPostingRED.PostWarehouseShipment(WhseShptLine, HideDialog, Invoice) then IsPosted := true; end;
In the end I had to opt for the fourth option. I created a new Codeunit that runs a number of posting routines with some smart error catching.
There we have it. One more mistake, one more fix that made everything better. I hope this helps you in your quest for clean code. Do you have any embarrassing mistakes to share?