In this video, I dive into one of the Release Management codeunits and explain how that works, then I show how to use the codeunit to perform extra validation on documents before release or posting.

One of the most commonly requested customizations in Business Central involves adding logic that runs when a user finishes editing a sales document, purchase document, or any other document. Since there’s no simple “OK” button that performs final actions, the way a user indicates their document is complete is by releasing it. This makes the release function an excellent place to add final validation, trigger additional business logic, or enforce rules that only make sense when evaluated across the entire document rather than at the field level.
In this video, Erik walks through the release management code in Business Central’s base application, explains its somewhat confusing structure, and demonstrates how to subscribe to the right event to add custom validation logic.
Understanding Release in Business Central
When a sales order is open, you can edit all fields on it. Once you release it, the document becomes locked — you can no longer change things that would affect the amount, what has been approved, or other critical details. You can still perform certain actions related to actually shipping the document, but the substantive content is frozen.
This makes the release function the ideal hook point for adding custom validation. If you need to ensure certain conditions are met before a document can be considered “done,” subscribing to the release process is the way to do it.
Navigating the Release Code in the Base App
Erik uses the AL Code Actions / AL Dev Tools extension by Andrej Šarinić (which has surpassed 100,000 installations) to explore the base application code. Starting from Page 42 (Sales Order), he locates the Release action, which contains this trigger:
The action creates a variable of type Codeunit "Release Sales Document" and calls PerformManualRelease. That seems straightforward enough. But following the code deeper reveals a more convoluted structure.
The Call Chain
PerformManualRelease does some prepayment checks and fires events (OnBeforeManualRelease and OnAfterManualRelease), but the real work happens in PerformManualCheckAndRelease. Inside that function:
- Another event fires:
OnBeforePerformManualCheckAndRelease - Prepayment logic is handled if applicable
- Pending approvals are checked (you can’t release something that hasn’t been approved if approval is required)
- Then something unusual happens:
Codeunit.Runis called on the same codeunit we’re already inside
When you call Codeunit.Run, it triggers the OnRun trigger of that codeunit. So Codeunit 414 (Release Sales Document) runs itself. Inside the OnRun trigger, the sales header record is copied into a global variable, and then a local procedure named "Code" is called.
The Infamous “Code” Procedure
This is where things get particularly quirky:
- The procedure is called without parentheses — a legacy AL quirk where parentheses were optional
- The name
Codeis actually a reserved word in AL, so Microsoft has to wrap it in quotes when declaring it - It shows up as a reserved-word color in most editor themes (like Dracula), which adds to the confusion
Inside the "Code" procedure is where the actual release logic lives: field validations, inventory checks, location code checks, and ultimately — setting the document status to Released. Erik notes that this codeunit “could really use some refactoring.”
Events Available Inside “Code”
The "Code" procedure contains several events you can subscribe to:
OnBeforeReleaseSalesDocOnBeforeCheckCustomerCreated/OnAfterCheckCustomerCreditOnBeforeSalesLineFindOnCodeAfterCheckOnReleaseSalesDoc
Because "Code" is the innermost function — the one that gets called regardless of how the release is triggered — subscribing to events here ensures your logic always executes.
Building the Custom Validation
Erik creates a simple example: enforcing a minimum lead time of 14 days on the requested delivery date due to a hypothetical global pandemic. Here’s the complete codeunit:
codeunit 50144 "Sale Management stuff"
{
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Release Sales Document", 'OnBeforeReleaseSalesDoc', '', true, true)]
local procedure ReleaseSalesDoc(var IsHandled: Boolean; var SalesHeader: Record "Sales Header"; PreviewMode: Boolean)
var
CutOffDate: Date;
begin
CutOffDate := CalcDate('14D', Today());
if SalesHeader."Requested Delivery Date" < CutOffDate then
error('There''s a global pandemic, we cannot deliver that soon!');
end;
}
How This Works
- The
[EventSubscriber]attribute subscribes to theOnBeforeReleaseSalesDocevent in Codeunit"Release Sales Document" - The two
trueparameters correspond toSkipOnMissingLicenseandSkipOnMissingPermission - A cutoff date is calculated as 14 days from today using
CalcDate('14D', Today()) - If the sales header's Requested Delivery Date falls before the cutoff date, an error is raised, preventing the release
Important Note on Event Parameters
The OnBeforeReleaseSalesDoc event actually exposes four parameters: SalesHeader, PreviewMode, IsHandled, and SkipCheckReleaseRestrictions. However, when creating your subscriber, you don't have to use all four parameters, and you don't have to specify them in the same order as the event definition. You only need to include the ones relevant to your logic.
Why OnBeforeReleaseSalesDoc and Not OnBeforeManualRelease?
This is a critical distinction. If you subscribe to OnBeforeManualRelease (which lives in the PerformManualRelease procedure), your validation will fire when a user clicks the Release button on the UI. That seems fine — until you consider what happens when a user presses F9 to post the document directly.
When Business Central posts a document, it performs an implicit release even though the user never clicked Release explicitly. If your validation is only on the manual release event, it won't fire during posting. By subscribing to OnBeforeReleaseSalesDoc inside the "Code" procedure, your logic runs regardless of whether the release was triggered manually or as part of the posting process. It also works correctly with background posting.
Choosing Where to Place Your Check
There are multiple events available within the release process, and where you subscribe matters:
- OnBeforeReleaseSalesDoc — fires at the very beginning of the code procedure, before most standard checks
- Events further down inside "Code" — fire after some standard checks have already passed, meaning certain fields are guaranteed to be populated by the time your subscriber runs
If your custom check depends on other validations having already passed (for example, you need the customer credit check to have completed first), you may want to subscribe to an event that fires later in the process. If you want clean, independent error messages, subscribing earlier (like OnBeforeReleaseSalesDoc) is a good choice.
Conclusion
The release management code in Business Central's base application has an unusual structure — a codeunit that runs itself, a procedure named after a reserved word, and calls without parentheses. But once you understand the call chain, it becomes clear that the "Code" procedure is the central hub where all release logic converges, regardless of how the release was initiated. Subscribing to events within this procedure (particularly OnBeforeReleaseSalesDoc) is the most reliable way to add custom validation that works across manual release, posting, and background posting scenarios. This pattern applies not just to sales documents but to purchase documents and other document types that follow the same release management architecture.