mirror of
https://github.com/microsoft/BCQuality.git
synced 2026-08-06 17:36:53 +01:00
Add Finance posting domain-knowledge pilot
Adds three atomic domain-rule knowledge files under community/knowledge/finance/ as a pilot for Type-A (normative) Business Central domain knowledge: post through the posting engine, treat posted ledger entries as immutable, and treat the Dimension Set ID as the source of truth for dimensions. Includes a good/bad AL sample pair for the posting rule. These encode BC-specific invariants that LLMs reliably get wrong, fitting the existing remedial/atomic knowledge grain with no schema or contract changes. Passes the repo frontmatter validator and is discovered by the knowledge index. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
8904ce583b
commit
3cb00e4d2d
5 changed files with 116 additions and 0 deletions
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
bc-version: [all]
|
||||
domain: finance
|
||||
keywords: [ledger-entry, immutable, reversal, audit-trail, correction, reverse, custentry-edit]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [finance]
|
||||
---
|
||||
|
||||
# Correct posted ledger entries by reversing, not by editing or deleting them
|
||||
|
||||
## Description
|
||||
|
||||
A posted ledger entry is an immutable record in Business Central's audit trail. The financial content of `G/L Entry`, `Cust. Ledger Entry`, `Vendor Ledger Entry`, and similar tables — amounts, accounts, posting date, quantities — must not change after posting, because registers, applications, VAT statements, and statutory reports all assume entries are append-only. Corrections are themselves postings: BC provides reversal (the `Reverse` / `Reverse Register` routines) and correcting documents (credit memos, correcting journals) that post a new, offsetting entry and leave the original intact.
|
||||
|
||||
## Best Practice
|
||||
|
||||
To undo or correct a posting, post a reversing or correcting entry through the normal posting path so the offset is itself a balanced, dated, traceable transaction. The original entry stays in place and the two net to zero, preserving the audit trail. A narrow set of non-financial fields (for example an entry's `Open` / `On Hold` status or a due date) is editable through dedicated platform routines such as `CustEntry-Edit` and `VendEntry-Edit`; use those routines rather than a raw `Modify`.
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Calling `Modify` or `Delete` on a posted ledger entry to fix a mistake — changing an amount, repointing an account, or removing the row. Detection signal: `Modify`, `ModifyAll`, `Delete`, or `DeleteAll` on a `*Ledger Entry` or `G/L Entry` record outside a dedicated `*Entry-Edit` routine. This destroys the audit trail, desynchronizes the entry from its register and detailed entries, and corrupts any report or reconciliation that already consumed the original value.
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// Demonstration only. Self-contained illustration, not derived from the
|
||||
// Business Central base application source.
|
||||
//
|
||||
// BAD: insert straight into the ledger table and hand-compute Entry No.
|
||||
// The row is unbalanced (no balancing entry), has no register, no resolved
|
||||
// dimensions, and no VAT. FindLast + "+ 1" races under concurrency and will
|
||||
// collide on the primary key. Reconciliation treats the result as corrupt.
|
||||
codeunit 50100 "Post GL Adjustment"
|
||||
{
|
||||
procedure PostAdjustment(AccountNo: Code[20]; Amount: Decimal; PostingDate: Date)
|
||||
var
|
||||
GLEntry: Record "G/L Entry";
|
||||
LastGLEntry: Record "G/L Entry";
|
||||
begin
|
||||
if LastGLEntry.FindLast() then;
|
||||
|
||||
GLEntry.Init();
|
||||
GLEntry."Entry No." := LastGLEntry."Entry No." + 1;
|
||||
GLEntry."G/L Account No." := AccountNo;
|
||||
GLEntry.Amount := Amount;
|
||||
GLEntry."Posting Date" := PostingDate;
|
||||
GLEntry.Insert();
|
||||
end;
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
// Demonstration only. Self-contained illustration, not derived from the
|
||||
// Business Central base application source.
|
||||
//
|
||||
// GOOD: build a journal line and let the posting engine create the ledger
|
||||
// entry. The platform assigns Entry No., writes the balancing entry, creates
|
||||
// the register, and resolves dimensions and VAT.
|
||||
codeunit 50100 "Post GL Adjustment"
|
||||
{
|
||||
procedure PostAdjustment(AccountNo: Code[20]; BalAccountNo: Code[20]; Amount: Decimal; PostingDate: Date)
|
||||
var
|
||||
GenJnlLine: Record "Gen. Journal Line";
|
||||
GenJnlPostLine: Codeunit "Gen. Jnl.-Post Line";
|
||||
begin
|
||||
GenJnlLine.Init();
|
||||
GenJnlLine."Posting Date" := PostingDate;
|
||||
GenJnlLine."Document Type" := GenJnlLine."Document Type"::" ";
|
||||
GenJnlLine."Account Type" := GenJnlLine."Account Type"::"G/L Account";
|
||||
GenJnlLine.Validate("Account No.", AccountNo);
|
||||
GenJnlLine."Bal. Account Type" := GenJnlLine."Bal. Account Type"::"G/L Account";
|
||||
GenJnlLine.Validate("Bal. Account No.", BalAccountNo);
|
||||
GenJnlLine.Validate(Amount, Amount);
|
||||
GenJnlLine."Source Code" := 'ADJUST';
|
||||
|
||||
GenJnlPostLine.Run(GenJnlLine);
|
||||
end;
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
bc-version: [all]
|
||||
domain: finance
|
||||
keywords: [posting, g-l-entry, ledger-entry, gen-jnl-post-line, journal-line, balancing, entry-no]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [finance]
|
||||
---
|
||||
|
||||
# Post ledger entries through the posting engine, never by inserting them directly
|
||||
|
||||
## Description
|
||||
|
||||
Ledger entries — `G/L Entry`, `Cust. Ledger Entry`, `Vendor Ledger Entry`, `Item Ledger Entry`, and their detailed counterparts — are the output of Business Central's posting engine, not ordinary tables an extension writes to. The supported way to create them is to populate a journal line (for example `Gen. Journal Line`) and run the matching posting codeunit (`Gen. Jnl.-Post Line`, codeunit 12, for general-ledger postings). The posting routine enforces double-entry balancing, allocates `Entry No.` safely under concurrency, creates the register, resolves dimensions, applies VAT, and links source and application data. None of that happens when a row is inserted into the ledger table directly.
|
||||
|
||||
## Best Practice
|
||||
|
||||
Build the transaction as one or more journal lines and hand them to the posting codeunit. Let the engine assign `Entry No.`, create the `G/L Register`, and write the balancing entries. When you need a reusable entry point, wrap the journal-line setup in your own codeunit but still post through `Gen. Jnl.-Post Line` — or through the document-posting routines (sales, purchase, service) that ultimately call it. See sample: `post-ledger-entries-through-posting-codeunits.good.al`.
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Calling `Insert` on a ledger table — typically after a `FindLast` to guess the next `Entry No.` Detection signal: any `Insert` on a `*Ledger Entry` or `G/L Entry` record, or an `Entry No.` computed in AL rather than returned by the platform. Such code produces an unbalanced, registerless, dimensionless row that reconciliation and reporting will treat as corrupt, and the hand-computed `Entry No.` races under concurrency. See sample: `post-ledger-entries-through-posting-codeunits.bad.al`.
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
bc-version: [all]
|
||||
domain: finance
|
||||
keywords: [dimensions, dimension-set-id, dimension-set-entry, global-dimension, shortcut-dimension, dimensionmanagement]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [finance]
|
||||
---
|
||||
|
||||
# Treat the Dimension Set ID as the source of truth for dimensions
|
||||
|
||||
## Description
|
||||
|
||||
Modern Business Central stores the dimensions of a record as a single `Dimension Set ID` that points at an immutable combination of `Dimension Set Entry` rows. The `Global Dimension 1 Code` / `Global Dimension 2 Code` fields and the `Shortcut Dimension 3..8` fields are denormalized projections the platform keeps in sync — they are not the source of truth, and they cover only a handful of the up to eight dimensions a set can hold. New or merged dimension combinations are obtained from codeunit `DimensionManagement` (for example `GetDimensionSetID`), which returns the `Dimension Set ID` to store on the record.
|
||||
|
||||
## Best Practice
|
||||
|
||||
When code sets, copies, or merges dimensions, work in terms of `Dimension Set ID` values and resolve or create them through `DimensionManagement`; assign the resulting set ID to the record and let the platform derive the global and shortcut projections. On records that expose shortcut-dimension fields (journal and document lines), call `Validate` on those fields — the field's logic updates the `Dimension Set ID` for you. To combine dimensions from several sources (document plus customer, header plus line) use the dimension-set combination routines instead of copying individual codes.
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Direct assignment (`:=`) to `Global Dimension 1 Code` or `Global Dimension 2 Code`, or reading those fields to determine "the dimensions," as if they were the record's dimension state. Detection signal: an assignment of a global/shortcut dimension field with no corresponding `Dimension Set ID` update, or analysis logic that branches on the global-dimension fields rather than the set entries. The projection silently disagrees with the set once a third dimension is involved, so posting and analysis-by-dimension produce wrong results.
|
||||
Loading…
Add table
Add a link
Reference in a new issue