mirror of
https://github.com/microsoft/BCQuality.git
synced 2026-10-05 14:46:55 +01:00
Address second round of Jesper Schulz-Wedde's review on PR #156
- dimension-management-wiring.md/.good.al: split into the two distinct
models the article was conflating - master data (Default Dimension
records via ValidateDimValueCode/SaveDefaultDim) vs. transactional/
document data (a single Dimension Set ID assembled via AddDimSource +
GetDefaultDimID, verified against BCApps' ExchRateAdjmtProcess.Codeunit.al).
Added a compiling document-table example alongside the existing master
table one.
- Deleted api-page-flowfields-must-be-calcfields (.md/.good.al/.bad.al):
Microsoft's own FlowFields documentation states a FlowField used as a
control's direct source expression is automatically calculated on any
page - no API-page exception is documented, and none could be
reproduced.
- prefer-email-module.bad.al/.md: Codeunit Mail has no Send/GetErrorDesc
members; fixed to the real current 7-argument CreateMessage signature,
and corrected the claim that the legacy path "still runs" - its base
implementation no longer sends anything, only raises integration events.
- check-post-line-batch-pattern.md/.good.al: reframed from a universal
invariant to the standard shape, naming the real Gen./Item/CA/Res./Job/
Insurance/Mfg. Item/FA Jnl.-Check Line/-Post Line/-Post Batch codeunits
it's based on. Added the missing Check Line companion codeunit so the
good fixture is internally complete.
- test-data-must-be-random-and-complete.good.al: removed leftover
"collision-free" wording contradicting the already-corrected article text.
- fixed-choice-set-must-use-enum-not-integer.md: removed the reintroduced
state-count heuristic ("the line is the state count"), aligned with
binary-choice-must-be-boolean.md's semantics-based distinction.
- namespace-must-be-verified-from-source.md: removed the false claim that
the compiler and AL Language Server use different namespace-resolution
rules.
- intrinsic-al-functions-must-use-modern-casing.md: removed the unverified
claim that PascalCase is the VS Code formatter's default output.
Worklist completeness: added cues for the 8 rules in data-modeling,
testing, performance, and web-services that had none (Jesper's explicit
ask), plus the same gap in all 7 style rules from this PR (not explicitly
named this round, but the identical systemic issue) - 15 cues total across
al-data-modeling-review.md, al-testing-review.md, al-performance-review.md,
al-web-services-review.md, and al-style-review.md.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
fa3d04c56c
commit
a28ba1a1d7
18 changed files with 98 additions and 94 deletions
|
|
@ -1,3 +1,13 @@
|
|||
codeunit 50100 "Meter Jnl.-Check Line"
|
||||
{
|
||||
procedure CheckLine(var MeterJnlLine: Record "Meter Journal Line")
|
||||
begin
|
||||
// Reads setup/dimension data only, shows no UI beyond errors.
|
||||
if MeterJnlLine.Quantity = 0 then
|
||||
Error('Quantity must not be zero.');
|
||||
end;
|
||||
}
|
||||
|
||||
codeunit 50101 "Meter Jnl.-Post Line"
|
||||
{
|
||||
procedure PostLine(var MeterJnlLine: Record "Meter Journal Line")
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ application-area: [all]
|
|||
|
||||
## Description
|
||||
|
||||
Every journal-based posting routine in Business Central is split across three companion codeunits with distinct, non-overlapping responsibilities: `Check Line` validates one line, `Post Line` writes exactly one line to the ledger, and `Post Batch` loops both across the journal. A document posting routine (posting one document at a time) calls `Post Line` directly and skips `Post Batch`. A new posting routine that blurs this split either misses functionality other code expects to call directly, or exposes an interaction surface it shouldn't.
|
||||
Business Central's own journal-based posting routines consistently follow a three-codeunit split with distinct, non-overlapping responsibilities — `Codeunit "Gen. Jnl.-Check Line"` / `"Gen. Jnl.-Post Line"` / `"Gen. Jnl.-Post Batch"` for the general journal, and the same `<Journal>-Check Line` / `<Journal>-Post Line` / `<Journal>-Post Batch` shape repeated for Item, Resource, Job, Fixed Asset, Insurance, and Cost Accounting journals: `Check Line` validates one line, `Post Line` writes exactly one line to the ledger, and `Post Batch` loops both across the journal. A document posting routine (posting one document at a time) calls `Post Line` directly and skips `Post Batch`. This is the standard shape to evaluate a new journal-based posting routine against, not a platform-enforced constraint — a routine with a genuinely different transaction/reuse shape may legitimately organize itself differently. But a new routine that blurs this split without a specific reason either misses functionality other code expects to call directly, or exposes an interaction surface it shouldn't.
|
||||
|
||||
## Best Practice
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
// Master data: Default Dimension records, no Dimension Set ID field.
|
||||
table 50100 "Course"
|
||||
{
|
||||
fields
|
||||
|
|
@ -26,3 +27,56 @@ table 50100 "Course"
|
|||
DimMgt.DeleteDefaultDim(Database::Course, "No.");
|
||||
end;
|
||||
}
|
||||
|
||||
// Transactional/document data: a single Dimension Set ID, inherited from the
|
||||
// related master record and overridable via shortcut dimension fields.
|
||||
table 50101 "Course Registration Header"
|
||||
{
|
||||
fields
|
||||
{
|
||||
field(1; "No."; Code[20]) { }
|
||||
field(2; "Customer No."; Code[20])
|
||||
{
|
||||
TableRelation = Customer;
|
||||
|
||||
trigger OnValidate()
|
||||
begin
|
||||
UpdateDimensionSetID();
|
||||
end;
|
||||
}
|
||||
field(10; "Shortcut Dimension 1 Code"; Code[20])
|
||||
{
|
||||
CaptionClass = '1,1,1';
|
||||
TableRelation = "Dimension Value".Code where(
|
||||
"Global Dimension No." = const(1), Blocked = const(false));
|
||||
|
||||
trigger OnValidate()
|
||||
var
|
||||
DimMgt: Codeunit DimensionManagement;
|
||||
begin
|
||||
DimMgt.ValidateShortcutDimValues(1, "Shortcut Dimension 1 Code", "Dimension Set ID");
|
||||
end;
|
||||
}
|
||||
field(480; "Dimension Set ID"; Integer)
|
||||
{
|
||||
Editable = false;
|
||||
TableRelation = "Dimension Set Entry"."Dimension Set ID";
|
||||
}
|
||||
}
|
||||
|
||||
local procedure UpdateDimensionSetID()
|
||||
var
|
||||
Customer: Record Customer;
|
||||
DimMgt: Codeunit DimensionManagement;
|
||||
DefaultDimSource: List of [Dictionary of [Integer, Code[20]]];
|
||||
GlobalDim2Code: Code[20];
|
||||
begin
|
||||
if not Customer.Get("Customer No.") then
|
||||
exit;
|
||||
|
||||
DimMgt.AddDimSource(DefaultDimSource, Database::Customer, "Customer No.");
|
||||
"Dimension Set ID" :=
|
||||
DimMgt.GetDefaultDimID(
|
||||
DefaultDimSource, '', "Shortcut Dimension 1 Code", GlobalDim2Code, "Dimension Set ID", 0);
|
||||
end;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,11 +13,18 @@ application-area: [all]
|
|||
|
||||
## Description
|
||||
|
||||
Adding dimension support to a custom master or document table is not just a matter of adding a `Code[20]` field. Business Central expects a specific set of hooks into `Codeunit "Dimension Management"` so a dimension value is validated, persisted as a Default Dimension record, and flows through to transactions the same way it does for every standard table. Skipping any one hook produces a field that looks correct in the designer but silently fails to save, validate, or carry through to postings.
|
||||
Adding dimension support to a custom table is not just a matter of adding a `Code[20]` field, and master tables and document/transactional tables wire into `Codeunit "Dimension Management"` through two different models — treating them as one mechanism is itself the mistake this article corrects:
|
||||
|
||||
- **Master data** (a custom master table, e.g. "Course") persists **Default Dimension** records: each shortcut dimension field validates through `ValidateDimValueCode`, then the result is saved via `SaveDefaultDim`, and `DeleteDefaultDim` removes them again in `OnDelete`. The master record itself carries no `Dimension Set ID` field.
|
||||
- **Transactional/document data** (a custom document or journal-line table) carries a single **`Dimension Set ID`** field — a pointer to a shared, deduplicated set of dimension values in `Dimension Set Entry`, assembled from whatever the document inherited plus whatever the user overrode. A document does not acquire that ID by calling `SaveDefaultDim`; it builds a source list with `AddDimSource` (naming the related master table and its key, e.g. `Database::Customer`), then calls `GetDefaultDimID` to compute a new `Dimension Set ID` that inherits the master's Default Dimension records. Editing a shortcut dimension field directly on the document validates through `ValidateShortcutDimValues`, which updates the same `Dimension Set ID` in place rather than writing a separate Default Dimension record.
|
||||
|
||||
Skipping the model that actually matches the table's kind produces a field that looks correct in the designer but silently fails to save, validate, or carry through to postings — or, for a document, one that never picks up the customer's/vendor's own dimensions at all.
|
||||
|
||||
## Best Practice
|
||||
|
||||
A master table should validate its dimension fields through `ValidateDimValueCode` (or `ValidateShortcutDimValues` when a `DimSetID` is also needed) and `SaveDefaultDim`, and create/delete the matching Default Dimension records in `OnInsert`/`OnDelete`. A document table should add Shortcut Dimension fields validated the same way, and call `GetDefaultDimID` to pull inherited dimension values from the related master record whenever the field that attaches the document to that master changes.
|
||||
For a master table, validate each shortcut dimension field through `ValidateDimValueCode`, save the result with `SaveDefaultDim`, and delete the matching Default Dimension records in `OnDelete`.
|
||||
|
||||
For a document table, when the field that attaches the document to a master record changes (e.g. `Customer No.`), call `AddDimSource` naming that master table and key, then `GetDefaultDimID` to compute the document's new `Dimension Set ID`, inheriting the master's Default Dimension records. Validate the document's own Shortcut Dimension fields through `ValidateShortcutDimValues`, which updates that same `Dimension Set ID` rather than persisting a separate Default Dimension record.
|
||||
|
||||
See sample: `dimension-management-wiring.good.al`.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue