mirror of
https://github.com/microsoft/BCQuality.git
synced 2026-10-05 14:46:55 +01:00
Fix Finance review applicability and ownership boundaries
Remove application-area gating and later VAT-field dependencies, align dynamic shared conventions, separate SCM ownership, and keep journal examples focused on the intended invariant. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
fa63d5526c
commit
7c17aa3ab8
18 changed files with 77 additions and 52 deletions
|
|
@ -4,7 +4,7 @@ domain: finance
|
|||
keywords: [cust-ledger-entry, vendor-ledger-entry, detailed-ledger-entry, remaining-amount, application, unapplication, open, closed-by-entry-no]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [finance]
|
||||
application-area: [all]
|
||||
---
|
||||
|
||||
# Apply and unapply entries through the application workflow, not status flags
|
||||
|
|
@ -23,7 +23,7 @@ See sample: [`apply-ledger-entries-through-application-codeunits.good.al`](apply
|
|||
|
||||
## Anti Pattern
|
||||
|
||||
Implement payment matching, settlement, or reopening by directly persisting `Open`, `"Closed by Entry No."`, closure amounts/dates, or detailed-entry unapplication flags, or by deleting/rewriting detailed application amounts. Require confirmed writes to existing non-temporary records and settlement intent. Do not suggest assigning a `Remaining Amount` FlowField as a fix.
|
||||
Implement customer/vendor payment matching, settlement, or reopening by directly persisting `Open`, `"Closed by Entry No."`, closure amounts/dates, or detailed-entry unapplication flags, or by deleting/rewriting detailed application amounts. Require confirmed writes to existing non-temporary customer/vendor or detailed customer/vendor entries and settlement intent. Item/inventory application records belong to SCM, not this rule. Do not suggest assigning a `Remaining Amount` FlowField as a fix.
|
||||
|
||||
This article owns fabricated application state. Use the [posted-financial-content rule](do-not-modify-or-delete-posted-ledger-entries.md) for original accounting-value corrections, not a second finding prescribing the same application fix.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ domain: finance
|
|||
keywords: [due-date, initial-entry-due-date, cust-entry-edit, vend-entry-edit, detailed-ledger-entry, aging]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [finance]
|
||||
application-area: [all]
|
||||
---
|
||||
|
||||
# Change posted customer/vendor due dates through the entry-edit workflow
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ The same `Dimension Set ID` can be referenced by an unposted journal line and by
|
|||
|
||||
To change an unposted transaction's dimensions, load its set into a **temporary** `Dimension Set Entry` buffer with `DimensionManagement.GetDimensionSet`, change the buffer, and obtain a reusable ID with `GetDimensionSetID`. Validate dimension values in the buffer so `Dimension Value ID` matches the chosen value. Store the resulting ID on the transaction and synchronize its projections through that record's supported dimension validation.
|
||||
|
||||
For already-posted G/L dimensions, use the supported dimension-correction workflow rather than changing shared rows. Read-only access, temporary buffers, and standard maintenance of projection metadata such as `Global Dimension No.` are not membership changes. This rule protects shared posting history; it does not prescribe custom-table/default-dimension wiring.
|
||||
For already-posted G/L dimensions, use the supported dimension-correction workflow rather than changing shared rows. Read-only access, temporary buffers, and standard maintenance of projection metadata such as `Global Dimension No.` are not membership changes. This rule protects dimension sets reached from general-journal, financial-document, or Finance-ledger flows. It does not own Item, Value, Capacity, Warehouse, or inventory-application record writes, or prescribe custom-table/default-dimension wiring.
|
||||
|
||||
See sample: [`do-not-edit-shared-dimension-sets.good.al`](do-not-edit-shared-dimension-sets.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Follow a journal/document/ledger `Dimension Set ID` to a **persistent** `Dimension Set Entry` and modify, rename, or delete its dimension/value membership in order to change that one transaction. Inspect `IsTemporary` guards, aliases, and the fields written before reporting: the same operations on a temporary working copy are expected.
|
||||
Follow a general-journal, financial-document, or Finance-ledger `Dimension Set ID` to a **persistent** `Dimension Set Entry` and modify, rename, or delete its dimension/value membership in order to change that one transaction. Inspect `IsTemporary` guards, aliases, and the fields written before reporting: the same operations on a temporary working copy are expected.
|
||||
|
||||
See sample: [`do-not-edit-shared-dimension-sets.bad.al`](do-not-edit-shared-dimension-sets.bad.al).
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ domain: finance
|
|||
keywords: [g-l-entry, ledger-entry, reversal, audit-trail, correction, financial-content, entry-edit]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [finance]
|
||||
application-area: [all]
|
||||
---
|
||||
|
||||
# Correct posted financial content through posting workflows, not row surgery
|
||||
|
|
@ -19,11 +19,13 @@ Use a supported transaction/register reversal, credit memo, or correcting journa
|
|||
|
||||
Supported operational edits are deliberate exceptions: for example, `"G/L Entry-Edit"` supports description changes, and `"Cust. Entry-Edit"` / `"Vend. Entry-Edit"` handle their table-specific editable fields. [Due-date synchronization](change-ledger-due-dates-through-entry-edit.md), [application/unapplication](apply-ledger-entries-through-application-codeunits.md), G/L dimension correction, and supported date compression have their own workflows. Do not flag their standard implementations, temporary simulation buffers, or extension-only metadata updates as financial row surgery. A subscriber is not exempt merely because it runs inside a supported workflow: inspect the fields it actually changes.
|
||||
|
||||
This rule covers G/L, customer/vendor/detailed, VAT, and financial-posting/register records. Item, Value, Capacity, Warehouse, inventory-application, and other inventory-posting records are SCM concerns. The financial-row leg of one inventory-posting bypass is outside this rule when the same inventory correction resolves it; an independently actionable financial defect remains in scope regardless of the containing module's name.
|
||||
|
||||
See sample: [`do-not-modify-or-delete-posted-ledger-entries.good.al`](do-not-modify-or-delete-posted-ledger-entries.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Persist a change to original financial content, delete posted rows, or fabricate reversal flags/links to repair or undo a transaction outside the supported correction/maintenance workflow. Require an existing, non-temporary standard ledger record and evidence of the fields or rows affected; a `Modify` token alone is insufficient. Settlement-state writes belong to the application article rather than a duplicate finding here.
|
||||
Persist a change to original financial content, delete posted rows, or fabricate reversal flags/links to repair or undo a transaction outside the supported correction/maintenance workflow. Require an existing, non-temporary Finance-owned record and evidence of the fields or rows affected; a `Modify` token or `*Ledger Entry` name alone is insufficient. Settlement-state writes belong to the application article rather than a duplicate finding here.
|
||||
|
||||
See sample: [`do-not-modify-or-delete-posted-ledger-entries.bad.al`](do-not-modify-or-delete-posted-ledger-entries.bad.al).
|
||||
|
||||
|
|
|
|||
|
|
@ -35,13 +35,11 @@ codeunit 50108 "Import Purchase Journal Total"
|
|||
JournalLine.TestField("VAT Calculation Type", JournalLine."VAT Calculation Type"::"Normal VAT");
|
||||
JournalLine.TestField("VAT %", 25);
|
||||
JournalLine.TestField("VAT Difference", 0);
|
||||
JournalLine.TestField("Non-Deductible VAT %", 0);
|
||||
JournalLine.TestField("Bal. Gen. Posting Type", JournalLine."Bal. Gen. Posting Type"::" ");
|
||||
JournalLine.TestField("Bal. VAT %", 0);
|
||||
VATPostingSetup.Get(JournalLine."VAT Bus. Posting Group", JournalLine."VAT Prod. Posting Group");
|
||||
VATPostingSetup.TestField("VAT Calculation Type", VATPostingSetup."VAT Calculation Type"::"Normal VAT");
|
||||
VATPostingSetup.TestField("VAT %", 25);
|
||||
VATPostingSetup.TestField("Non-Deductible VAT %", 0);
|
||||
VATPostingSetup.TestField("Unrealized VAT Type", VATPostingSetup."Unrealized VAT Type"::" ");
|
||||
GeneralLedgerSetup.Get();
|
||||
GeneralLedgerSetup.TestField("Additional Reporting Currency", '');
|
||||
|
|
|
|||
|
|
@ -35,13 +35,11 @@ codeunit 50108 "Import Purchase Journal Total"
|
|||
JournalLine.TestField("VAT Calculation Type", JournalLine."VAT Calculation Type"::"Normal VAT");
|
||||
JournalLine.TestField("VAT %", 25);
|
||||
JournalLine.TestField("VAT Difference", 0);
|
||||
JournalLine.TestField("Non-Deductible VAT %", 0);
|
||||
JournalLine.TestField("Bal. Gen. Posting Type", JournalLine."Bal. Gen. Posting Type"::" ");
|
||||
JournalLine.TestField("Bal. VAT %", 0);
|
||||
VATPostingSetup.Get(JournalLine."VAT Bus. Posting Group", JournalLine."VAT Prod. Posting Group");
|
||||
VATPostingSetup.TestField("VAT Calculation Type", VATPostingSetup."VAT Calculation Type"::"Normal VAT");
|
||||
VATPostingSetup.TestField("VAT %", 25);
|
||||
VATPostingSetup.TestField("Non-Deductible VAT %", 0);
|
||||
VATPostingSetup.TestField("Unrealized VAT Type", VATPostingSetup."Unrealized VAT Type"::" ");
|
||||
GeneralLedgerSetup.Get();
|
||||
GeneralLedgerSetup.TestField("Additional Reporting Currency", '');
|
||||
|
|
|
|||
|
|
@ -4,20 +4,22 @@ domain: finance
|
|||
keywords: [normal-vat, automatic-vat-entry, gross-amount, net-amount, vat-posting-setup, gen-journal-line, purchase]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [finance]
|
||||
application-area: [all]
|
||||
---
|
||||
|
||||
# Supply a VAT-inclusive journal Amount for automatic Normal VAT
|
||||
|
||||
## Description
|
||||
|
||||
For a general-journal line using **Automatic VAT Entry** and **Normal VAT**, `Amount` includes VAT. The posting engine extracts tax from that total; it does not add tax to a VAT-exclusive expense imported into `Amount`. For an LCY purchase of net 100 plus 25 VAT, entering 100 produces an 80 expense and 20 VAT, rather than the intended 100 expense and 25 VAT from a total of 125.
|
||||
For a general-journal line using **Automatic VAT Entry** and **Normal VAT**, `Amount` includes VAT. The posting engine extracts tax from that total; it does not add tax to a VAT-exclusive expense imported into `Amount`. An LCY invoice with net 100 and VAT 25 therefore needs a journal total of 125, not 100.
|
||||
|
||||
## Best Practice
|
||||
|
||||
Map the source's VAT-inclusive total to journal `Amount` in this posting mode. Establish the intended account and posting-group combination before validating the final amount. Account-derived VAT defaults depend on `Copy VAT Setup to Jnl. Lines`; do not assume account selection always supplies the intended configuration.
|
||||
|
||||
Require the actual input contract and calculation mode, not just a variable named `NetAmount`. The examples encode source net, VAT, and gross values plus a 25% Normal-VAT setup check. They target an LCY G/L purchase with 0.01 amount rounding and without balancing-side VAT, additional reporting currency, VAT differences, unrealized VAT, or non-deductible VAT. Other calculation types, Manual VAT Entry, reverse charge, Full VAT, sales/use tax, non-deductible or unrealized tax, and other currency/rounding contexts need their own analysis; this is not a universal gross-up formula or country-specific tax advice.
|
||||
Require the actual input contract and calculation mode, not just a variable named `NetAmount`. The examples encode source net, VAT, and gross values plus a 25% Normal-VAT setup check. They target an LCY G/L purchase with 0.01 amount rounding and without balancing-side VAT, additional reporting currency, VAT differences, or unrealized VAT. Other calculation types, Manual VAT Entry, reverse charge, Full VAT, sales/use tax, unrealized tax, and other currency/rounding contexts need their own analysis; this is not a universal gross-up formula or country-specific tax advice.
|
||||
|
||||
The concern is the supplied transaction total, not its deductible/non-deductible allocation. Non-deductible VAT features can change the allocation of that total, not turn the source's net amount into its gross amount. Do not infer a particular expense or deductible-VAT split from this rule. The samples therefore do not depend on later-version non-deductible-VAT fields.
|
||||
|
||||
See sample: [`normal-vat-journal-amount-includes-vat.good.al`](normal-vat-journal-amount-includes-vat.good.al).
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ domain: finance
|
|||
keywords: [g-l-entry, ledger-entry, gen-jnl-post-line, gen-jnl-post-batch, journal-line, register, insert]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [finance]
|
||||
application-area: [all]
|
||||
---
|
||||
|
||||
# Create financial ledger entries through the owning posting engine
|
||||
|
|
@ -19,13 +19,15 @@ Use the owning document or journal posting workflow. For a normal persisted gene
|
|||
|
||||
`"Gen. Jnl.-Post Line".RunWithCheck` is appropriate for a complete journal line inside a correctly owned posting lifecycle, but it does not invent a balancing account or document number, allocate numbering merely from `Posting No. Series`, or replace [batch document-balancing policy](preserve-journal-batch-document-balance.md). The line codeunit is stateful; its checked wrapper owns its start/continue/finish work. Normal batch posting owns its numbering and commits by default; do not imply these entry points are transaction-neutral.
|
||||
|
||||
Exclude temporary buffers and the standard engine's own insertion points. A checked parent may legitimately use `RunWithoutCheck`; do not replace it without inspecting that parent. This rule concerns standard financial ledgers, not a custom table merely named `Ledger Entry` or a supported, specifically reviewed migration/repair workflow.
|
||||
Exclude temporary buffers and the standard engine's own insertion points. A checked parent may legitimately use `RunWithoutCheck`; do not replace it without inspecting that parent. This rule owns `G/L Entry`, `Cust. Ledger Entry`, `Vendor Ledger Entry`, their detailed customer/vendor entries, `VAT Entry`, and financial-posting/register records, not a custom table merely named `Ledger Entry` or a supported, specifically reviewed migration/repair workflow.
|
||||
|
||||
`Item Ledger Entry`, `Value Entry`, Capacity/Warehouse entries, `Item Application Entry`, and other inventory-posting records are SCM concerns, not this rule's financial-ledger scope. That exclusion includes the financial-row leg of a single inventory-posting bypass when restoring the inventory workflow corrects the whole operation. Distinct, independently actionable financial defects remain in scope.
|
||||
|
||||
See sample: [`post-ledger-entries-through-posting-codeunits.good.al`](post-ledger-entries-through-posting-codeunits.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Create posted financial effects by directly inserting persistent `G/L Entry`, customer/vendor ledger, or VAT ledger rows outside their owning posting workflow. Resolve the actual record type and lifecycle. Balanced debit/credit values, copied dimensions, `Insert(true)`, and a lock around entry-number allocation do not turn raw inserts into a complete posting.
|
||||
Create posted financial effects by directly inserting the Finance-owned records named above outside their owning posting workflow. Resolve the actual record type, operation, and lifecycle; do not match `*Ledger Entry` as a wildcard. Balanced debit/credit values, copied dimensions, `Insert(true)`, and a lock around entry-number allocation do not turn raw inserts into a complete posting.
|
||||
|
||||
See sample: [`post-ledger-entries-through-posting-codeunits.bad.al`](post-ledger-entries-through-posting-codeunits.bad.al).
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ codeunit 50107 "Post Journal Allocation"
|
|||
JournalTemplate: Record "Gen. Journal Template";
|
||||
JournalBatch: Record "Gen. Journal Batch";
|
||||
JournalLine: Record "Gen. Journal Line";
|
||||
LineToPost: Record "Gen. Journal Line";
|
||||
PostLine: Codeunit "Gen. Jnl.-Post Line";
|
||||
begin
|
||||
JournalTemplate.Get(TemplateName);
|
||||
|
|
@ -24,11 +25,12 @@ codeunit 50107 "Post Journal Allocation"
|
|||
AddAllocationLine(JournalTemplate, BatchName, 20000, CreditAccount, PostingDate, 'ALLOC-B', -90);
|
||||
JournalLine.FindSet();
|
||||
repeat
|
||||
PostLine.RunWithCheck(JournalLine);
|
||||
LineToPost := JournalLine;
|
||||
PostLine.RunWithCheck(LineToPost);
|
||||
until JournalLine.Next() = 0;
|
||||
end;
|
||||
|
||||
local procedure AddAllocationLine(JournalTemplate: Record "Gen. Journal Template"; BatchName: Code[10]; LineNo: Integer; AccountNo: Code[20]; PostingDate: Date; DocumentNo: Code[20]; Amount: Decimal)
|
||||
local procedure AddAllocationLine(JournalTemplate: Record "Gen. Journal Template"; BatchName: Code[10]; LineNo: Integer; AccountNo: Code[20]; PostingDate: Date; DocumentNo: Code[20]; LineAmount: Decimal)
|
||||
var
|
||||
JournalLine: Record "Gen. Journal Line";
|
||||
begin
|
||||
|
|
@ -42,7 +44,7 @@ codeunit 50107 "Post Journal Allocation"
|
|||
JournalLine.Validate("Account Type", JournalLine."Account Type"::"G/L Account");
|
||||
JournalLine.Validate("Account No.", AccountNo);
|
||||
JournalLine.Validate("Gen. Posting Type", JournalLine."Gen. Posting Type"::" ");
|
||||
JournalLine.Validate(Amount, Amount);
|
||||
JournalLine.Validate(Amount, LineAmount);
|
||||
JournalLine.Insert(true);
|
||||
end;
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ codeunit 50107 "Post Journal Allocation"
|
|||
PostBatch.Run(JournalLine);
|
||||
end;
|
||||
|
||||
local procedure AddAllocationLine(JournalTemplate: Record "Gen. Journal Template"; BatchName: Code[10]; LineNo: Integer; AccountNo: Code[20]; PostingDate: Date; DocumentNo: Code[20]; Amount: Decimal)
|
||||
local procedure AddAllocationLine(JournalTemplate: Record "Gen. Journal Template"; BatchName: Code[10]; LineNo: Integer; AccountNo: Code[20]; PostingDate: Date; DocumentNo: Code[20]; LineAmount: Decimal)
|
||||
var
|
||||
JournalLine: Record "Gen. Journal Line";
|
||||
begin
|
||||
|
|
@ -40,7 +40,7 @@ codeunit 50107 "Post Journal Allocation"
|
|||
JournalLine.Validate("Account Type", JournalLine."Account Type"::"G/L Account");
|
||||
JournalLine.Validate("Account No.", AccountNo);
|
||||
JournalLine.Validate("Gen. Posting Type", JournalLine."Gen. Posting Type"::" ");
|
||||
JournalLine.Validate(Amount, Amount);
|
||||
JournalLine.Validate(Amount, LineAmount);
|
||||
JournalLine.Insert(true);
|
||||
end;
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ domain: finance
|
|||
keywords: [force-doc-balance, gen-journal-template, gen-jnl-post-batch, runwithcheck, document-no, posting-date, balancing]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [finance]
|
||||
application-area: [all]
|
||||
---
|
||||
|
||||
# Preserve the journal batch's document-balancing policy
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ domain: finance
|
|||
keywords: [reversetransaction, reverseregister, transaction-no, entry-no, reversal-entry, g-l-register]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [finance]
|
||||
application-area: [all]
|
||||
---
|
||||
|
||||
# Pass the transaction number, not a ledger-entry number, to ReverseTransaction
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ When a journal line or posting document inherits dimensions, its `Dimension Set
|
|||
|
||||
For an intentional **complete** dimension transfer, copy the source set ID and synchronize the destination's projections through its supported validation or dimension-management routine. On `Gen. Journal Line`, `Validate("Dimension Set ID", SourceSetID)` updates the two shortcut fields. Do not assume another table has the same validation trigger.
|
||||
|
||||
When line-specific dimensions must survive a header change, use the appropriate set-combination or delta routine instead of blindly replacing the line's entire set. Reading or filtering a known global dimension is legitimate; it is not a claim to enumerate every dimension. This rule owns dimension transfer through existing journal/document posting records, not custom-table or master `Default Dimension` wiring, which belongs to data modeling.
|
||||
When line-specific dimensions must survive a header change, use the appropriate set-combination or delta routine instead of blindly replacing the line's entire set. Reading or filtering a known global dimension is legitimate; it is not a claim to enumerate every dimension. This rule owns transfers through general-journal and financial-document posting records, not writes to Item, Value, Capacity, Warehouse, or inventory-application records owned by SCM. Generic custom-table or master `Default Dimension` wiring belongs to data modeling.
|
||||
|
||||
See sample: [`write-dimensions-as-dimension-set-entries.good.al`](write-dimensions-as-dimension-set-entries.good.al).
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue