mirror of
https://github.com/microsoft/BCQuality.git
synced 2026-08-07 01:46:53 +01:00
Add CMFRT best practices and anti-patterns documentation for various coding standards
This commit is contained in:
parent
cfcf16a00e
commit
906ddcfd72
21 changed files with 427 additions and 5 deletions
34
custom/knowledge/events/cmfrt-onbefore-do-onafter.bad.al
Normal file
34
custom/knowledge/events/cmfrt-onbefore-do-onafter.bad.al
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
codeunit 55043 "CMFRT AQ FS ProForma Meth"
|
||||
{
|
||||
procedure CMFRTAQFSResolveItemToGLAcc(var SalesLine: Record "Sales Line")
|
||||
var
|
||||
Item: Record Item;
|
||||
GeneralPostingSetup: Record "General Posting Setup";
|
||||
IsHandled: Boolean;
|
||||
begin
|
||||
OnBeforeCMFRTAQFSResolveItemToGLAcc(SalesLine, IsHandled);
|
||||
if IsHandled then
|
||||
exit;
|
||||
|
||||
// Business logic inline in the shell: this early exit
|
||||
// silently skips the OnAfter event below.
|
||||
if SalesLine."CMFRT AQ FS Item No." = '' then
|
||||
exit;
|
||||
|
||||
if not Item.Get(SalesLine."CMFRT AQ FS Item No.") then
|
||||
exit;
|
||||
// ... more inline logic ...
|
||||
|
||||
OnAfterCMFRTAQFSResolveItemToGLAcc(SalesLine);
|
||||
end;
|
||||
|
||||
[IntegrationEvent(false, false)]
|
||||
local procedure OnBeforeCMFRTAQFSResolveItemToGLAcc(var SalesLine: Record "Sales Line"; var IsHandled: Boolean)
|
||||
begin
|
||||
end;
|
||||
|
||||
[IntegrationEvent(false, false)]
|
||||
local procedure OnAfterCMFRTAQFSResolveItemToGLAcc(var SalesLine: Record "Sales Line")
|
||||
begin
|
||||
end;
|
||||
}
|
||||
38
custom/knowledge/events/cmfrt-onbefore-do-onafter.good.al
Normal file
38
custom/knowledge/events/cmfrt-onbefore-do-onafter.good.al
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
codeunit 55043 "CMFRT AQ FS ProForma Meth"
|
||||
{
|
||||
procedure CMFRTAQFSResolveItemToGLAcc(var SalesLine: Record "Sales Line")
|
||||
var
|
||||
IsHandled: Boolean;
|
||||
begin
|
||||
IsHandled := false;
|
||||
OnBeforeCMFRTAQFSResolveItemToGLAcc(SalesLine, IsHandled);
|
||||
if IsHandled then
|
||||
exit;
|
||||
|
||||
DoCMFRTAQFSResolveItemToGLAcc(SalesLine);
|
||||
|
||||
OnAfterCMFRTAQFSResolveItemToGLAcc(SalesLine);
|
||||
end;
|
||||
|
||||
local procedure DoCMFRTAQFSResolveItemToGLAcc(var SalesLine: Record "Sales Line")
|
||||
var
|
||||
Item: Record Item;
|
||||
GeneralPostingSetup: Record "General Posting Setup";
|
||||
ItemNotFoundErr: Label 'Item %1 was not found.', Comment = '%1 = Item No.';
|
||||
begin
|
||||
Item.SetLoadFields("No.", "Gen. Prod. Posting Group", Description);
|
||||
if not Item.Get(SalesLine."CMFRT AQ FS Item No.") then
|
||||
Error(ItemNotFoundErr, SalesLine."CMFRT AQ FS Item No.");
|
||||
// ... business logic only; early exits here cannot skip OnAfter ...
|
||||
end;
|
||||
|
||||
[IntegrationEvent(false, false)]
|
||||
local procedure OnBeforeCMFRTAQFSResolveItemToGLAcc(var SalesLine: Record "Sales Line"; var IsHandled: Boolean)
|
||||
begin
|
||||
end;
|
||||
|
||||
[IntegrationEvent(false, false)]
|
||||
local procedure OnAfterCMFRTAQFSResolveItemToGLAcc(var SalesLine: Record "Sales Line")
|
||||
begin
|
||||
end;
|
||||
}
|
||||
26
custom/knowledge/events/cmfrt-onbefore-do-onafter.md
Normal file
26
custom/knowledge/events/cmfrt-onbefore-do-onafter.md
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
bc-version: [all]
|
||||
domain: events
|
||||
keywords: [on-before, on-after, do-procedure, thin-shell, meth, is-handled, extraction, entry-point]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [all]
|
||||
---
|
||||
|
||||
# CMFRT OnBefore → Do → OnAfter procedure shape
|
||||
|
||||
## Description
|
||||
|
||||
Global procedures in CMFRT Meth codeunits are thin shells: the body consists of firing `OnBefore<Name>` with `var IsHandled: Boolean`, exiting if handled, calling a local `Do<Name>` procedure that holds all business logic, and firing `OnAfter<Name>`. Local variables that only serve the business logic (record buffers, labels, working values) live in the `Do` procedure, not in the shell. This keeps the event bracket structurally impossible to bypass — the `OnAfter` event cannot be skipped by an early `exit` inside business logic, because business logic lives one level down.
|
||||
|
||||
## Best Practice
|
||||
|
||||
Write every global Meth procedure as: reset `IsHandled`, fire `OnBefore`, `if IsHandled then exit;`, call `Do<Name>(...)`, fire `OnAfter`. When review finds a global procedure whose body mixes event calls with business logic, extract the logic into `Do<Name>` and move its private variables and labels along with it.
|
||||
|
||||
See sample: `cmfrt-onbefore-do-onafter.good.al`.
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
A global procedure whose business logic sits inline between the `OnBefore` and `OnAfter` calls. Inline bodies grow early `exit` paths that silently skip the `OnAfter` event, and their local variables and labels accumulate at the shell level where every branch can touch them. Equally wrong: declaring the event pair but never calling the events from the procedure (dead events), which advertises an extension point that never fires.
|
||||
|
||||
See sample: `cmfrt-onbefore-do-onafter.bad.al`.
|
||||
|
|
@ -23,4 +23,6 @@ See sample: `cmfrt-onbefore-onafter-all-globals.good.al`.
|
|||
|
||||
Publishing a global procedure without both `OnBefore` and `OnAfter` integration events, or adding events only after a downstream extension explicitly requests an extension point. A global procedure with no event bracket is a black box: dependent extensions cannot inject logic around it without an AL override, which is a breaking pattern. Adding events later is itself a non-breaking change but causes unnecessary churn and review cycles.
|
||||
|
||||
The inverse is equally a violation: event declarations that nothing raises. An `[IntegrationEvent]` declared but never called from any procedure, or a wrapper procedure (for example a `[TryFunction]` insert wrapper) that no caller invokes, is dead code that advertises an extension point which never fires. Wire the event into the owning procedure or delete the declaration and its plumbing.
|
||||
|
||||
See sample: `cmfrt-onbefore-onafter-all-globals.bad.al`.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue