diff --git a/src/Layers/APAC/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al b/src/Layers/APAC/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al index 2b7a225aea..d72f90f326 100644 --- a/src/Layers/APAC/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al +++ b/src/Layers/APAC/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al @@ -236,7 +236,7 @@ codeunit 12 "Gen. Jnl.-Post Line" SequenceNoMgt.ClearSequenceNoCheck(); GenJnlLine.Copy(GenJnlLine2); - Code(GenJnlLine, true); + Post(GenJnlLine, true); OnAfterRunWithCheck(GenJnlLine); GenJnlLine2 := GenJnlLine; exit(GLEntryNo); @@ -259,13 +259,51 @@ codeunit 12 "Gen. Jnl.-Post Line" exit(GLEntryNo); GenJnlLine.Copy(GenJnlLine2); - Code(GenJnlLine, false); + Post(GenJnlLine, false); OnAfterRunWithoutCheck(GenJnlLine); GenJnlLine2 := GenJnlLine; exit(GLEntryNo); end; - local procedure "Code"(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + /// + /// A wrapper procedure to delegate to either a procedure that allows commit or a procedure that ignores commit. + /// By default, commits are suppressed during the critical posting window to prevent duplicate-key races + /// on G/L Entry (table 17). Subscribers can opt out by setting IgnoreCommit to false via OnSetCommitBehavior. + /// + /// The general journal line that is being posted. + /// Indicates whether to check the lines before posting. + local procedure Post(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + var + IgnoreCommit: Boolean; + begin + IgnoreCommit := true; + OnSetCommitBehavior(IgnoreCommit); + + if IgnoreCommit then + PostLineCommitBehaviorIgnore(GenJnlLine, CheckLine) + else + PostLine(GenJnlLine, CheckLine); + end; + + /// + /// A wrapper procedure to delegate to PostLine in order to ignore commits. + /// While this procedure is on the call stack, the platform turns every Commit() into a no-op, + /// preventing intermittent duplicate-key errors on G/L Entry (table 17). + /// + /// The general journal line that is being posted. + /// Indicates whether to check the line before posting. + [CommitBehavior(CommitBehavior::Ignore)] + local procedure PostLineCommitBehaviorIgnore(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + begin + PostLine(GenJnlLine, CheckLine); + end; + + /// + /// Posting procedure for general journal lines + /// + /// The general journal line that is being posted. + /// Indicates whether to check the line before posting. + local procedure PostLine(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) var xGLEntryNo: Integer; Balancing: Boolean; @@ -13545,4 +13583,9 @@ codeunit 12 "Gen. Jnl.-Post Line" local procedure OnPostUnapplyOnBeforeInsertTempVATEntry(var VATEntry: Record "VAT Entry"; var UnapplyVATEntries: Boolean) begin end; + + [IntegrationEvent(false, false)] + local procedure OnSetCommitBehavior(var IgnoreCommit: Boolean) + begin + end; } \ No newline at end of file diff --git a/src/Layers/BE/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al b/src/Layers/BE/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al index fc01e1d44d..d3d3a564c0 100644 --- a/src/Layers/BE/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al +++ b/src/Layers/BE/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al @@ -218,7 +218,7 @@ codeunit 12 "Gen. Jnl.-Post Line" SequenceNoMgt.ClearSequenceNoCheck(); GenJnlLine.Copy(GenJnlLine2); - Code(GenJnlLine, true); + Post(GenJnlLine, true); OnAfterRunWithCheck(GenJnlLine); GenJnlLine2 := GenJnlLine; exit(GLEntryNo); @@ -241,13 +241,51 @@ codeunit 12 "Gen. Jnl.-Post Line" exit(GLEntryNo); GenJnlLine.Copy(GenJnlLine2); - Code(GenJnlLine, false); + Post(GenJnlLine, false); OnAfterRunWithoutCheck(GenJnlLine); GenJnlLine2 := GenJnlLine; exit(GLEntryNo); end; - local procedure "Code"(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + /// + /// A wrapper procedure to delegate to either a procedure that allows commit or a procedure that ignores commit. + /// By default, commits are suppressed during the critical posting window to prevent duplicate-key races + /// on G/L Entry (table 17). Subscribers can opt out by setting IgnoreCommit to false via OnSetCommitBehavior. + /// + /// The general journal line that is being posted. + /// Indicates whether to check the lines before posting. + local procedure Post(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + var + IgnoreCommit: Boolean; + begin + IgnoreCommit := true; + OnSetCommitBehavior(IgnoreCommit); + + if IgnoreCommit then + PostLineCommitBehaviorIgnore(GenJnlLine, CheckLine) + else + PostLine(GenJnlLine, CheckLine); + end; + + /// + /// A wrapper procedure to delegate to PostLine in order to ignore commits. + /// While this procedure is on the call stack, the platform turns every Commit() into a no-op, + /// preventing intermittent duplicate-key errors on G/L Entry (table 17). + /// + /// The general journal line that is being posted. + /// Indicates whether to check the line before posting. + [CommitBehavior(CommitBehavior::Ignore)] + local procedure PostLineCommitBehaviorIgnore(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + begin + PostLine(GenJnlLine, CheckLine); + end; + + /// + /// Posting procedure for general journal lines + /// + /// The general journal line that is being posted. + /// Indicates whether to check the line before posting. + local procedure PostLine(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) var xGLEntryNo: Integer; Balancing: Boolean; @@ -11146,4 +11184,9 @@ codeunit 12 "Gen. Jnl.-Post Line" local procedure OnPostUnapplyOnBeforeInsertTempVATEntry(var VATEntry: Record "VAT Entry"; var UnapplyVATEntries: Boolean) begin end; + + [IntegrationEvent(false, false)] + local procedure OnSetCommitBehavior(var IgnoreCommit: Boolean) + begin + end; } \ No newline at end of file diff --git a/src/Layers/CH/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al b/src/Layers/CH/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al index 20826749d9..fd9fa6801b 100644 --- a/src/Layers/CH/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al +++ b/src/Layers/CH/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al @@ -219,7 +219,7 @@ codeunit 12 "Gen. Jnl.-Post Line" SequenceNoMgt.ClearSequenceNoCheck(); GenJnlLine.Copy(GenJnlLine2); - Code(GenJnlLine, true); + Post(GenJnlLine, true); OnAfterRunWithCheck(GenJnlLine); GenJnlLine2 := GenJnlLine; exit(GLEntryNo); @@ -242,13 +242,51 @@ codeunit 12 "Gen. Jnl.-Post Line" exit(GLEntryNo); GenJnlLine.Copy(GenJnlLine2); - Code(GenJnlLine, false); + Post(GenJnlLine, false); OnAfterRunWithoutCheck(GenJnlLine); GenJnlLine2 := GenJnlLine; exit(GLEntryNo); end; - local procedure "Code"(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + /// + /// A wrapper procedure to delegate to either a procedure that allows commit or a procedure that ignores commit. + /// By default, commits are suppressed during the critical posting window to prevent duplicate-key races + /// on G/L Entry (table 17). Subscribers can opt out by setting IgnoreCommit to false via OnSetCommitBehavior. + /// + /// The general journal line that is being posted. + /// Indicates whether to check the lines before posting. + local procedure Post(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + var + IgnoreCommit: Boolean; + begin + IgnoreCommit := true; + OnSetCommitBehavior(IgnoreCommit); + + if IgnoreCommit then + PostLineCommitBehaviorIgnore(GenJnlLine, CheckLine) + else + PostLine(GenJnlLine, CheckLine); + end; + + /// + /// A wrapper procedure to delegate to PostLine in order to ignore commits. + /// While this procedure is on the call stack, the platform turns every Commit() into a no-op, + /// preventing intermittent duplicate-key errors on G/L Entry (table 17). + /// + /// The general journal line that is being posted. + /// Indicates whether to check the line before posting. + [CommitBehavior(CommitBehavior::Ignore)] + local procedure PostLineCommitBehaviorIgnore(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + begin + PostLine(GenJnlLine, CheckLine); + end; + + /// + /// Posting procedure for general journal lines + /// + /// The general journal line that is being posted. + /// Indicates whether to check the line before posting. + local procedure PostLine(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) var xGLEntryNo: Integer; Balancing: Boolean; @@ -11168,4 +11206,9 @@ codeunit 12 "Gen. Jnl.-Post Line" local procedure OnPostUnapplyOnBeforeInsertTempVATEntry(var VATEntry: Record "VAT Entry"; var UnapplyVATEntries: Boolean) begin end; + + [IntegrationEvent(false, false)] + local procedure OnSetCommitBehavior(var IgnoreCommit: Boolean) + begin + end; } \ No newline at end of file diff --git a/src/Layers/ES/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al b/src/Layers/ES/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al index befcec4b41..92f6dc5da0 100644 --- a/src/Layers/ES/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al +++ b/src/Layers/ES/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al @@ -258,7 +258,7 @@ codeunit 12 "Gen. Jnl.-Post Line" exit(GLEntryNo); GenJnlLine.Copy(GenJnlLine2); - Code(GenJnlLine, true); + Post(GenJnlLine, true); OnAfterRunWithCheck(GenJnlLine); GenJnlLine2 := GenJnlLine; exit(GLEntryNo); @@ -281,13 +281,51 @@ codeunit 12 "Gen. Jnl.-Post Line" exit(GLEntryNo); GenJnlLine.Copy(GenJnlLine2); - Code(GenJnlLine, false); + Post(GenJnlLine, false); OnAfterRunWithoutCheck(GenJnlLine); GenJnlLine2 := GenJnlLine; exit(GLEntryNo); end; - local procedure "Code"(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + /// + /// A wrapper procedure to delegate to either a procedure that allows commit or a procedure that ignores commit. + /// By default, commits are suppressed during the critical posting window to prevent duplicate-key races + /// on G/L Entry (table 17). Subscribers can opt out by setting IgnoreCommit to false via OnSetCommitBehavior. + /// + /// The general journal line that is being posted. + /// Indicates whether to check the lines before posting. + local procedure Post(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + var + IgnoreCommit: Boolean; + begin + IgnoreCommit := true; + OnSetCommitBehavior(IgnoreCommit); + + if IgnoreCommit then + PostLineCommitBehaviorIgnore(GenJnlLine, CheckLine) + else + PostLine(GenJnlLine, CheckLine); + end; + + /// + /// A wrapper procedure to delegate to PostLine in order to ignore commits. + /// While this procedure is on the call stack, the platform turns every Commit() into a no-op, + /// preventing intermittent duplicate-key errors on G/L Entry (table 17). + /// + /// The general journal line that is being posted. + /// Indicates whether to check the line before posting. + [CommitBehavior(CommitBehavior::Ignore)] + local procedure PostLineCommitBehaviorIgnore(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + begin + PostLine(GenJnlLine, CheckLine); + end; + + /// + /// Posting procedure for general journal lines + /// + /// The general journal line that is being posted. + /// Indicates whether to check the line before posting. + local procedure PostLine(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) var Balancing: Boolean; IsTransactionConsistent: Boolean; @@ -12859,4 +12897,9 @@ codeunit 12 "Gen. Jnl.-Post Line" local procedure OnPostUnapplyOnBeforeInsertTempVATEntry(var VATEntry: Record "VAT Entry"; var UnapplyVATEntries: Boolean) begin end; + + [IntegrationEvent(false, false)] + local procedure OnSetCommitBehavior(var IgnoreCommit: Boolean) + begin + end; } \ No newline at end of file diff --git a/src/Layers/ES/Tests/General Journal/ERMJournalPosting.Codeunit.al b/src/Layers/ES/Tests/General Journal/ERMJournalPosting.Codeunit.al index c98a6ba6bf..cba05f1e0c 100644 --- a/src/Layers/ES/Tests/General Journal/ERMJournalPosting.Codeunit.al +++ b/src/Layers/ES/Tests/General Journal/ERMJournalPosting.Codeunit.al @@ -24,6 +24,10 @@ codeunit 134420 "ERM Journal Posting" MinRange: Decimal; MiddleRange: Decimal; MaxRange: Decimal; + SetIgnoreCommitToFalse: Boolean; + RaiseErrorAfterCommit: Boolean; + CommitTestMarkerID: Guid; + TestRollbackAfterCommitErr: Label 'Test error to trigger rollback after Commit() call.'; [Test] [Scope('OnPrem')] @@ -465,11 +469,88 @@ codeunit 134420 "ERM Journal Posting" LibraryAssert.AreEqual('', NoSeriesLine."Last No. Used", 'Last No. Used must be empty.'); end; + [Test] + [Scope('OnPrem')] + procedure PostGenJnlLineCommitBySubscriberIsSuppressed() + var + GenJnlBatch: Record "Gen. Journal Batch"; + GenJnlLine: Record "Gen. Journal Line"; + ActivityLog: Record "Activity Log"; + begin + // [FEATURE] [General Journal] [Posting] [CommitBehavior] + // [SCENARIO 637261] A Commit() called by a posting subscriber is suppressed during Gen. Jnl.-Post Line. + Initialize(); + + // [GIVEN] General journal line. + LibraryJournals.CreateGenJournalBatch(GenJnlBatch); + LibraryERM.CreateGeneralJnlLineWithBalAcc( + GenJnlLine, GenJnlBatch."Journal Template Name", GenJnlBatch.Name, GenJnlLine."Document Type"::" ", + GenJnlLine."Account Type"::"G/L Account", LibraryERM.CreateGLAccountNo(), + GenJnlLine."Bal. Account Type"::"G/L Account", LibraryERM.CreateGLAccountNo(), LibraryRandom.RandDec(1000, 2)); + Commit(); + + // [GIVEN] Subscriber inserts a marker record, calls Commit(), and raises an error inside the posting scope. + CommitTestMarkerID := CreateGuid(); + RaiseErrorAfterCommit := true; + BindSubscription(this); + + // [WHEN] Post the journal line (posting fails because subscriber raises an error). + asserterror LibraryERM.PostGeneralJnlLine(GenJnlLine); + UnbindSubscription(this); + + // [THEN] The marker record does not exist because Commit() was suppressed and the error rolled back all changes. + ActivityLog.SetRange("Activity Message", Format(CommitTestMarkerID)); + Assert.RecordIsEmpty(ActivityLog); + end; + + [Test] + [Scope('OnPrem')] + procedure PostGenJnlLineCommitBehaviorOptOutRestoresCommit() + var + GenJnlBatch: Record "Gen. Journal Batch"; + GenJnlLine: Record "Gen. Journal Line"; + ActivityLog: Record "Activity Log"; + begin + // [FEATURE] [General Journal] [Posting] [CommitBehavior] + // [SCENARIO 637261] Setting IgnoreCommit := false via OnSetCommitBehavior restores old commit behavior. + Initialize(); + + // [GIVEN] General journal line. + LibraryJournals.CreateGenJournalBatch(GenJnlBatch); + LibraryERM.CreateGeneralJnlLineWithBalAcc( + GenJnlLine, GenJnlBatch."Journal Template Name", GenJnlBatch.Name, GenJnlLine."Document Type"::" ", + GenJnlLine."Account Type"::"G/L Account", LibraryERM.CreateGLAccountNo(), + GenJnlLine."Bal. Account Type"::"G/L Account", LibraryERM.CreateGLAccountNo(), LibraryRandom.RandDec(1000, 2)); + Commit(); + + // [GIVEN] Subscriber sets IgnoreCommit := false to opt out of commit suppression. + SetIgnoreCommitToFalse := true; + + // [GIVEN] Subscriber inserts a marker record, calls Commit(), and raises an error inside the posting scope. + CommitTestMarkerID := CreateGuid(); + RaiseErrorAfterCommit := true; + BindSubscription(this); + + // [WHEN] Post the journal line (posting fails because subscriber raises an error). + asserterror LibraryERM.PostGeneralJnlLine(GenJnlLine); + UnbindSubscription(this); + + // [THEN] The marker record exists because Commit() was not suppressed and persisted before the error. + ActivityLog.SetRange("Activity Message", Format(CommitTestMarkerID)); + Assert.RecordIsNotEmpty(ActivityLog); + + // Cleanup: Remove test marker. + ActivityLog.DeleteAll(); + end; + local procedure Initialize() var LibraryERMCountryData: Codeunit "Library - ERM Country Data"; begin LibraryTestInitialize.OnTestInitialize(CODEUNIT::"ERM Journal Posting"); + SetIgnoreCommitToFalse := false; + RaiseErrorAfterCommit := false; + Clear(CommitTestMarkerID); if isInitialized then exit; LibraryTestInitialize.OnBeforeTestSuiteInitialize(CODEUNIT::"ERM Journal Posting"); @@ -598,4 +679,36 @@ codeunit 134420 "ERM Journal Posting" GenJournalBatch.Validate("Posting No. Series", ''); GenJournalBatch.Modify(true); end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Gen. Jnl.-Post Line", OnSetCommitBehavior, '', false, false)] + local procedure OnSetCommitBehaviorHandler(var IgnoreCommit: Boolean) + begin + if SetIgnoreCommitToFalse then + IgnoreCommit := false; + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Gen. Jnl.-Post Line", OnAfterGLFinishPosting, '', false, false)] + local procedure OnAfterGLFinishPostingHandler(GLEntry: Record "G/L Entry"; var GenJnlLine: Record "Gen. Journal Line") + var + ActivityLog: Record "Activity Log"; + begin + if not RaiseErrorAfterCommit then + exit; + if IsNullGuid(CommitTestMarkerID) then + exit; + + // Insert a marker record + ActivityLog.Init(); + ActivityLog."Activity Date" := CurrentDateTime(); + ActivityLog."User ID" := CopyStr(UserId(), 1, MaxStrLen(ActivityLog."User ID")); + ActivityLog."Activity Message" := Format(CommitTestMarkerID); + ActivityLog.Status := ActivityLog.Status::Success; + ActivityLog.Insert(true); + + // Call Commit() - this should be suppressed by CommitBehavior::Ignore + Commit(); + + // Raise an error to abort the transaction + Error(TestRollbackAfterCommitErr); + end; } diff --git a/src/Layers/FI/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al b/src/Layers/FI/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al index fbe8c6eda3..fc590b3645 100644 --- a/src/Layers/FI/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al +++ b/src/Layers/FI/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al @@ -220,7 +220,7 @@ codeunit 12 "Gen. Jnl.-Post Line" SequenceNoMgt.ClearSequenceNoCheck(); GenJnlLine.Copy(GenJnlLine2); - Code(GenJnlLine, true); + Post(GenJnlLine, true); OnAfterRunWithCheck(GenJnlLine); GenJnlLine2 := GenJnlLine; exit(GLEntryNo); @@ -243,13 +243,51 @@ codeunit 12 "Gen. Jnl.-Post Line" exit(GLEntryNo); GenJnlLine.Copy(GenJnlLine2); - Code(GenJnlLine, false); + Post(GenJnlLine, false); OnAfterRunWithoutCheck(GenJnlLine); GenJnlLine2 := GenJnlLine; exit(GLEntryNo); end; - local procedure "Code"(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + /// + /// A wrapper procedure to delegate to either a procedure that allows commit or a procedure that ignores commit. + /// By default, commits are suppressed during the critical posting window to prevent duplicate-key races + /// on G/L Entry (table 17). Subscribers can opt out by setting IgnoreCommit to false via OnSetCommitBehavior. + /// + /// The general journal line that is being posted. + /// Indicates whether to check the lines before posting. + local procedure Post(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + var + IgnoreCommit: Boolean; + begin + IgnoreCommit := true; + OnSetCommitBehavior(IgnoreCommit); + + if IgnoreCommit then + PostLineCommitBehaviorIgnore(GenJnlLine, CheckLine) + else + PostLine(GenJnlLine, CheckLine); + end; + + /// + /// A wrapper procedure to delegate to PostLine in order to ignore commits. + /// While this procedure is on the call stack, the platform turns every Commit() into a no-op, + /// preventing intermittent duplicate-key errors on G/L Entry (table 17). + /// + /// The general journal line that is being posted. + /// Indicates whether to check the line before posting. + [CommitBehavior(CommitBehavior::Ignore)] + local procedure PostLineCommitBehaviorIgnore(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + begin + PostLine(GenJnlLine, CheckLine); + end; + + /// + /// Posting procedure for general journal lines + /// + /// The general journal line that is being posted. + /// Indicates whether to check the line before posting. + local procedure PostLine(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) var xGLEntryNo: Integer; Balancing: Boolean; @@ -11169,4 +11207,9 @@ codeunit 12 "Gen. Jnl.-Post Line" local procedure OnPostUnapplyOnBeforeInsertTempVATEntry(var VATEntry: Record "VAT Entry"; var UnapplyVATEntries: Boolean) begin end; + + [IntegrationEvent(false, false)] + local procedure OnSetCommitBehavior(var IgnoreCommit: Boolean) + begin + end; } \ No newline at end of file diff --git a/src/Layers/FR/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al b/src/Layers/FR/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al index b666e639cb..d6ac0212b8 100644 --- a/src/Layers/FR/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al +++ b/src/Layers/FR/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al @@ -228,7 +228,7 @@ codeunit 12 "Gen. Jnl.-Post Line" SequenceNoMgt.ClearSequenceNoCheck(); GenJnlLine.Copy(GenJnlLine2); - Code(GenJnlLine, true); + Post(GenJnlLine, true); OnAfterRunWithCheck(GenJnlLine); GenJnlLine2 := GenJnlLine; exit(GLEntryNo); @@ -251,13 +251,51 @@ codeunit 12 "Gen. Jnl.-Post Line" exit(GLEntryNo); GenJnlLine.Copy(GenJnlLine2); - Code(GenJnlLine, false); + Post(GenJnlLine, false); OnAfterRunWithoutCheck(GenJnlLine); GenJnlLine2 := GenJnlLine; exit(GLEntryNo); end; - local procedure "Code"(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + /// + /// A wrapper procedure to delegate to either a procedure that allows commit or a procedure that ignores commit. + /// By default, commits are suppressed during the critical posting window to prevent duplicate-key races + /// on G/L Entry (table 17). Subscribers can opt out by setting IgnoreCommit to false via OnSetCommitBehavior. + /// + /// The general journal line that is being posted. + /// Indicates whether to check the lines before posting. + local procedure Post(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + var + IgnoreCommit: Boolean; + begin + IgnoreCommit := true; + OnSetCommitBehavior(IgnoreCommit); + + if IgnoreCommit then + PostLineCommitBehaviorIgnore(GenJnlLine, CheckLine) + else + PostLine(GenJnlLine, CheckLine); + end; + + /// + /// A wrapper procedure to delegate to PostLine in order to ignore commits. + /// While this procedure is on the call stack, the platform turns every Commit() into a no-op, + /// preventing intermittent duplicate-key errors on G/L Entry (table 17). + /// + /// The general journal line that is being posted. + /// Indicates whether to check the line before posting. + [CommitBehavior(CommitBehavior::Ignore)] + local procedure PostLineCommitBehaviorIgnore(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + begin + PostLine(GenJnlLine, CheckLine); + end; + + /// + /// Posting procedure for general journal lines + /// + /// The general journal line that is being posted. + /// Indicates whether to check the line before posting. + local procedure PostLine(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) var xGLEntryNo: Integer; Balancing: Boolean; @@ -11472,4 +11510,9 @@ codeunit 12 "Gen. Jnl.-Post Line" local procedure OnPostUnapplyOnBeforeInsertTempVATEntry(var VATEntry: Record "VAT Entry"; var UnapplyVATEntries: Boolean) begin end; + + [IntegrationEvent(false, false)] + local procedure OnSetCommitBehavior(var IgnoreCommit: Boolean) + begin + end; } \ No newline at end of file diff --git a/src/Layers/IT/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al b/src/Layers/IT/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al index 4ae77b19be..73955c5b76 100644 --- a/src/Layers/IT/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al +++ b/src/Layers/IT/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al @@ -260,7 +260,7 @@ codeunit 12 "Gen. Jnl.-Post Line" SequenceNoMgt.ClearSequenceNoCheck(); GenJnlLine.Copy(GenJnlLine2); - Code(GenJnlLine, true); + Post(GenJnlLine, true); OnAfterRunWithCheck(GenJnlLine); GenJnlLine2 := GenJnlLine; exit(GLEntryNo); @@ -283,13 +283,51 @@ codeunit 12 "Gen. Jnl.-Post Line" exit(GLEntryNo); GenJnlLine.Copy(GenJnlLine2); - Code(GenJnlLine, false); + Post(GenJnlLine, false); OnAfterRunWithoutCheck(GenJnlLine); GenJnlLine2 := GenJnlLine; exit(GLEntryNo); end; - local procedure "Code"(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + /// + /// A wrapper procedure to delegate to either a procedure that allows commit or a procedure that ignores commit. + /// By default, commits are suppressed during the critical posting window to prevent duplicate-key races + /// on G/L Entry (table 17). Subscribers can opt out by setting IgnoreCommit to false via OnSetCommitBehavior. + /// + /// The general journal line that is being posted. + /// Indicates whether to check the lines before posting. + local procedure Post(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + var + IgnoreCommit: Boolean; + begin + IgnoreCommit := true; + OnSetCommitBehavior(IgnoreCommit); + + if IgnoreCommit then + PostLineCommitBehaviorIgnore(GenJnlLine, CheckLine) + else + PostLine(GenJnlLine, CheckLine); + end; + + /// + /// A wrapper procedure to delegate to PostLine in order to ignore commits. + /// While this procedure is on the call stack, the platform turns every Commit() into a no-op, + /// preventing intermittent duplicate-key errors on G/L Entry (table 17). + /// + /// The general journal line that is being posted. + /// Indicates whether to check the line before posting. + [CommitBehavior(CommitBehavior::Ignore)] + local procedure PostLineCommitBehaviorIgnore(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + begin + PostLine(GenJnlLine, CheckLine); + end; + + /// + /// Posting procedure for general journal lines + /// + /// The general journal line that is being posted. + /// Indicates whether to check the line before posting. + local procedure PostLine(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) var xGLEntryNo: Integer; Balancing: Boolean; @@ -12224,4 +12262,9 @@ codeunit 12 "Gen. Jnl.-Post Line" local procedure OnPostUnapplyOnBeforeInsertTempVATEntry(var VATEntry: Record "VAT Entry"; var UnapplyVATEntries: Boolean) begin end; + + [IntegrationEvent(false, false)] + local procedure OnSetCommitBehavior(var IgnoreCommit: Boolean) + begin + end; } \ No newline at end of file diff --git a/src/Layers/NA/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al b/src/Layers/NA/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al index 4c5356aa7a..232d8833a6 100644 --- a/src/Layers/NA/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al +++ b/src/Layers/NA/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al @@ -221,7 +221,7 @@ codeunit 12 "Gen. Jnl.-Post Line" SequenceNoMgt.ClearSequenceNoCheck(); GenJnlLine.Copy(GenJnlLine2); - Code(GenJnlLine, true); + Post(GenJnlLine, true); OnAfterRunWithCheck(GenJnlLine); GenJnlLine2 := GenJnlLine; exit(GLEntryNo); @@ -244,13 +244,51 @@ codeunit 12 "Gen. Jnl.-Post Line" exit(GLEntryNo); GenJnlLine.Copy(GenJnlLine2); - Code(GenJnlLine, false); + Post(GenJnlLine, false); OnAfterRunWithoutCheck(GenJnlLine); GenJnlLine2 := GenJnlLine; exit(GLEntryNo); end; - local procedure "Code"(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + /// + /// A wrapper procedure to delegate to either a procedure that allows commit or a procedure that ignores commit. + /// By default, commits are suppressed during the critical posting window to prevent duplicate-key races + /// on G/L Entry (table 17). Subscribers can opt out by setting IgnoreCommit to false via OnSetCommitBehavior. + /// + /// The general journal line that is being posted. + /// Indicates whether to check the lines before posting. + local procedure Post(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + var + IgnoreCommit: Boolean; + begin + IgnoreCommit := true; + OnSetCommitBehavior(IgnoreCommit); + + if IgnoreCommit then + PostLineCommitBehaviorIgnore(GenJnlLine, CheckLine) + else + PostLine(GenJnlLine, CheckLine); + end; + + /// + /// A wrapper procedure to delegate to PostLine in order to ignore commits. + /// While this procedure is on the call stack, the platform turns every Commit() into a no-op, + /// preventing intermittent duplicate-key errors on G/L Entry (table 17). + /// + /// The general journal line that is being posted. + /// Indicates whether to check the line before posting. + [CommitBehavior(CommitBehavior::Ignore)] + local procedure PostLineCommitBehaviorIgnore(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + begin + PostLine(GenJnlLine, CheckLine); + end; + + /// + /// Posting procedure for general journal lines + /// + /// The general journal line that is being posted. + /// Indicates whether to check the line before posting. + local procedure PostLine(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) var xGLEntryNo: Integer; Balancing: Boolean; @@ -11595,4 +11633,9 @@ codeunit 12 "Gen. Jnl.-Post Line" local procedure OnPostUnapplyOnBeforeInsertTempVATEntry(var VATEntry: Record "VAT Entry"; var UnapplyVATEntries: Boolean) begin end; + + [IntegrationEvent(false, false)] + local procedure OnSetCommitBehavior(var IgnoreCommit: Boolean) + begin + end; } \ No newline at end of file diff --git a/src/Layers/NO/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al b/src/Layers/NO/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al index 67a1b6f901..fd2be544f3 100644 --- a/src/Layers/NO/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al +++ b/src/Layers/NO/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al @@ -222,7 +222,7 @@ codeunit 12 "Gen. Jnl.-Post Line" SequenceNoMgt.ClearSequenceNoCheck(); GenJnlLine.Copy(GenJnlLine2); - Code(GenJnlLine, true); + Post(GenJnlLine, true); OnAfterRunWithCheck(GenJnlLine); GenJnlLine2 := GenJnlLine; exit(GLEntryNo); @@ -245,13 +245,51 @@ codeunit 12 "Gen. Jnl.-Post Line" exit(GLEntryNo); GenJnlLine.Copy(GenJnlLine2); - Code(GenJnlLine, false); + Post(GenJnlLine, false); OnAfterRunWithoutCheck(GenJnlLine); GenJnlLine2 := GenJnlLine; exit(GLEntryNo); end; - local procedure "Code"(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + /// + /// A wrapper procedure to delegate to either a procedure that allows commit or a procedure that ignores commit. + /// By default, commits are suppressed during the critical posting window to prevent duplicate-key races + /// on G/L Entry (table 17). Subscribers can opt out by setting IgnoreCommit to false via OnSetCommitBehavior. + /// + /// The general journal line that is being posted. + /// Indicates whether to check the lines before posting. + local procedure Post(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + var + IgnoreCommit: Boolean; + begin + IgnoreCommit := true; + OnSetCommitBehavior(IgnoreCommit); + + if IgnoreCommit then + PostLineCommitBehaviorIgnore(GenJnlLine, CheckLine) + else + PostLine(GenJnlLine, CheckLine); + end; + + /// + /// A wrapper procedure to delegate to PostLine in order to ignore commits. + /// While this procedure is on the call stack, the platform turns every Commit() into a no-op, + /// preventing intermittent duplicate-key errors on G/L Entry (table 17). + /// + /// The general journal line that is being posted. + /// Indicates whether to check the line before posting. + [CommitBehavior(CommitBehavior::Ignore)] + local procedure PostLineCommitBehaviorIgnore(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + begin + PostLine(GenJnlLine, CheckLine); + end; + + /// + /// Posting procedure for general journal lines + /// + /// The general journal line that is being posted. + /// Indicates whether to check the line before posting. + local procedure PostLine(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) var xGLEntryNo: Integer; Balancing: Boolean; @@ -11206,4 +11244,9 @@ codeunit 12 "Gen. Jnl.-Post Line" local procedure OnPostUnapplyOnBeforeInsertTempVATEntry(var VATEntry: Record "VAT Entry"; var UnapplyVATEntries: Boolean) begin end; + + [IntegrationEvent(false, false)] + local procedure OnSetCommitBehavior(var IgnoreCommit: Boolean) + begin + end; } \ No newline at end of file diff --git a/src/Layers/RU/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al b/src/Layers/RU/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al index 16a6c109a1..8894ea1a66 100644 --- a/src/Layers/RU/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al +++ b/src/Layers/RU/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al @@ -255,7 +255,7 @@ codeunit 12 "Gen. Jnl.-Post Line" SequenceNoMgt.ClearSequenceNoCheck(); GenJnlLine.Copy(GenJnlLine2); - Code(GenJnlLine, true); + Post(GenJnlLine, true); OnAfterRunWithCheck(GenJnlLine); GenJnlLine2 := GenJnlLine; exit(GLEntryNo); @@ -278,13 +278,51 @@ codeunit 12 "Gen. Jnl.-Post Line" exit(GLEntryNo); GenJnlLine.Copy(GenJnlLine2); - Code(GenJnlLine, false); + Post(GenJnlLine, false); OnAfterRunWithoutCheck(GenJnlLine); GenJnlLine2 := GenJnlLine; exit(GLEntryNo); end; - local procedure "Code"(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + /// + /// A wrapper procedure to delegate to either a procedure that allows commit or a procedure that ignores commit. + /// By default, commits are suppressed during the critical posting window to prevent duplicate-key races + /// on G/L Entry (table 17). Subscribers can opt out by setting IgnoreCommit to false via OnSetCommitBehavior. + /// + /// The general journal line that is being posted. + /// Indicates whether to check the lines before posting. + local procedure Post(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + var + IgnoreCommit: Boolean; + begin + IgnoreCommit := true; + OnSetCommitBehavior(IgnoreCommit); + + if IgnoreCommit then + PostLineCommitBehaviorIgnore(GenJnlLine, CheckLine) + else + PostLine(GenJnlLine, CheckLine); + end; + + /// + /// A wrapper procedure to delegate to PostLine in order to ignore commits. + /// While this procedure is on the call stack, the platform turns every Commit() into a no-op, + /// preventing intermittent duplicate-key errors on G/L Entry (table 17). + /// + /// The general journal line that is being posted. + /// Indicates whether to check the line before posting. + [CommitBehavior(CommitBehavior::Ignore)] + local procedure PostLineCommitBehaviorIgnore(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + begin + PostLine(GenJnlLine, CheckLine); + end; + + /// + /// Posting procedure for general journal lines + /// + /// The general journal line that is being posted. + /// Indicates whether to check the line before posting. + local procedure PostLine(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) var xGLEntryNo: Integer; Balancing: Boolean; @@ -13637,4 +13675,9 @@ codeunit 12 "Gen. Jnl.-Post Line" local procedure OnPostUnapplyOnBeforeInsertTempVATEntry(var VATEntry: Record "VAT Entry"; var UnapplyVATEntries: Boolean) begin end; + + [IntegrationEvent(false, false)] + local procedure OnSetCommitBehavior(var IgnoreCommit: Boolean) + begin + end; } \ No newline at end of file diff --git a/src/Layers/W1/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al b/src/Layers/W1/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al index 85589d60ea..98e2d96d7f 100644 --- a/src/Layers/W1/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al +++ b/src/Layers/W1/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al @@ -218,7 +218,7 @@ codeunit 12 "Gen. Jnl.-Post Line" SequenceNoMgt.ClearSequenceNoCheck(); GenJnlLine.Copy(GenJnlLine2); - Code(GenJnlLine, true); + Post(GenJnlLine, true); OnAfterRunWithCheck(GenJnlLine); GenJnlLine2 := GenJnlLine; exit(GLEntryNo); @@ -241,13 +241,51 @@ codeunit 12 "Gen. Jnl.-Post Line" exit(GLEntryNo); GenJnlLine.Copy(GenJnlLine2); - Code(GenJnlLine, false); + Post(GenJnlLine, false); OnAfterRunWithoutCheck(GenJnlLine); GenJnlLine2 := GenJnlLine; exit(GLEntryNo); end; - local procedure "Code"(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + /// + /// A wrapper procedure to delegate to either a procedure that allows commit or a procedure that ignores commit. + /// By default, commits are suppressed during the critical posting window to prevent duplicate-key races + /// on G/L Entry (table 17). Subscribers can opt out by setting IgnoreCommit to false via OnSetCommitBehavior. + /// + /// The general journal line that is being posted. + /// Indicates whether to check the lines before posting. + local procedure Post(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + var + IgnoreCommit: Boolean; + begin + IgnoreCommit := true; + OnSetCommitBehavior(IgnoreCommit); + + if IgnoreCommit then + PostLineCommitBehaviorIgnore(GenJnlLine, CheckLine) + else + PostLine(GenJnlLine, CheckLine); + end; + + /// + /// A wrapper procedure to delegate to PostLine in order to ignore commits. + /// While this procedure is on the call stack, the platform turns every Commit() into a no-op, + /// preventing intermittent duplicate-key errors on G/L Entry (table 17). + /// + /// The general journal line that is being posted. + /// Indicates whether to check the line before posting. + [CommitBehavior(CommitBehavior::Ignore)] + local procedure PostLineCommitBehaviorIgnore(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) + begin + PostLine(GenJnlLine, CheckLine); + end; + + /// + /// Posting procedure for general journal lines + /// + /// The general journal line that is being posted. + /// Indicates whether to check the line before posting. + local procedure PostLine(var GenJnlLine: Record "Gen. Journal Line"; CheckLine: Boolean) var xGLEntryNo: Integer; Balancing: Boolean; @@ -11103,4 +11141,9 @@ codeunit 12 "Gen. Jnl.-Post Line" local procedure OnPostUnapplyOnBeforeInsertTempVATEntry(var VATEntry: Record "VAT Entry"; var UnapplyVATEntries: Boolean) begin end; + + [IntegrationEvent(false, false)] + local procedure OnSetCommitBehavior(var IgnoreCommit: Boolean) + begin + end; } diff --git a/src/Layers/W1/Tests/General Journal/ERMJournalPosting.Codeunit.al b/src/Layers/W1/Tests/General Journal/ERMJournalPosting.Codeunit.al index 633eb8931f..c0d798ed4c 100644 --- a/src/Layers/W1/Tests/General Journal/ERMJournalPosting.Codeunit.al +++ b/src/Layers/W1/Tests/General Journal/ERMJournalPosting.Codeunit.al @@ -24,6 +24,10 @@ codeunit 134420 "ERM Journal Posting" MinRange: Decimal; MiddleRange: Decimal; MaxRange: Decimal; + SetIgnoreCommitToFalse: Boolean; + RaiseErrorAfterCommit: Boolean; + CommitTestMarkerID: Guid; + TestRollbackAfterCommitErr: Label 'Test error to trigger rollback after Commit() call.'; [Test] [Scope('OnPrem')] @@ -464,11 +468,88 @@ codeunit 134420 "ERM Journal Posting" LibraryAssert.AreEqual('', NoSeriesLine."Last No. Used", 'Last No. Used must be empty.'); end; + [Test] + [Scope('OnPrem')] + procedure PostGenJnlLineCommitBySubscriberIsSuppressed() + var + GenJnlBatch: Record "Gen. Journal Batch"; + GenJnlLine: Record "Gen. Journal Line"; + ActivityLog: Record "Activity Log"; + begin + // [FEATURE] [General Journal] [Posting] [CommitBehavior] + // [SCENARIO 637261] A Commit() called by a posting subscriber is suppressed during Gen. Jnl.-Post Line. + Initialize(); + + // [GIVEN] General journal line. + LibraryJournals.CreateGenJournalBatch(GenJnlBatch); + LibraryERM.CreateGeneralJnlLineWithBalAcc( + GenJnlLine, GenJnlBatch."Journal Template Name", GenJnlBatch.Name, GenJnlLine."Document Type"::" ", + GenJnlLine."Account Type"::"G/L Account", LibraryERM.CreateGLAccountNo(), + GenJnlLine."Bal. Account Type"::"G/L Account", LibraryERM.CreateGLAccountNo(), LibraryRandom.RandDec(1000, 2)); + Commit(); + + // [GIVEN] Subscriber inserts a marker record, calls Commit(), and raises an error inside the posting scope. + CommitTestMarkerID := CreateGuid(); + RaiseErrorAfterCommit := true; + BindSubscription(this); + + // [WHEN] Post the journal line (posting fails because subscriber raises an error). + asserterror LibraryERM.PostGeneralJnlLine(GenJnlLine); + UnbindSubscription(this); + + // [THEN] The marker record does not exist because Commit() was suppressed and the error rolled back all changes. + ActivityLog.SetRange("Activity Message", Format(CommitTestMarkerID)); + Assert.RecordIsEmpty(ActivityLog); + end; + + [Test] + [Scope('OnPrem')] + procedure PostGenJnlLineCommitBehaviorOptOutRestoresCommit() + var + GenJnlBatch: Record "Gen. Journal Batch"; + GenJnlLine: Record "Gen. Journal Line"; + ActivityLog: Record "Activity Log"; + begin + // [FEATURE] [General Journal] [Posting] [CommitBehavior] + // [SCENARIO 637261] Setting IgnoreCommit := false via OnSetCommitBehavior restores old commit behavior. + Initialize(); + + // [GIVEN] General journal line. + LibraryJournals.CreateGenJournalBatch(GenJnlBatch); + LibraryERM.CreateGeneralJnlLineWithBalAcc( + GenJnlLine, GenJnlBatch."Journal Template Name", GenJnlBatch.Name, GenJnlLine."Document Type"::" ", + GenJnlLine."Account Type"::"G/L Account", LibraryERM.CreateGLAccountNo(), + GenJnlLine."Bal. Account Type"::"G/L Account", LibraryERM.CreateGLAccountNo(), LibraryRandom.RandDec(1000, 2)); + Commit(); + + // [GIVEN] Subscriber sets IgnoreCommit := false to opt out of commit suppression. + SetIgnoreCommitToFalse := true; + + // [GIVEN] Subscriber inserts a marker record, calls Commit(), and raises an error inside the posting scope. + CommitTestMarkerID := CreateGuid(); + RaiseErrorAfterCommit := true; + BindSubscription(this); + + // [WHEN] Post the journal line (posting fails because subscriber raises an error). + asserterror LibraryERM.PostGeneralJnlLine(GenJnlLine); + UnbindSubscription(this); + + // [THEN] The marker record exists because Commit() was not suppressed and persisted before the error. + ActivityLog.SetRange("Activity Message", Format(CommitTestMarkerID)); + Assert.RecordIsNotEmpty(ActivityLog); + + // Cleanup: Remove test marker. + ActivityLog.DeleteAll(); + end; + local procedure Initialize() var LibraryERMCountryData: Codeunit "Library - ERM Country Data"; begin LibraryTestInitialize.OnTestInitialize(CODEUNIT::"ERM Journal Posting"); + SetIgnoreCommitToFalse := false; + RaiseErrorAfterCommit := false; + Clear(CommitTestMarkerID); if isInitialized then exit; LibraryTestInitialize.OnBeforeTestSuiteInitialize(CODEUNIT::"ERM Journal Posting"); @@ -597,4 +678,36 @@ codeunit 134420 "ERM Journal Posting" GenJournalBatch.Validate("Posting No. Series", ''); GenJournalBatch.Modify(true); end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Gen. Jnl.-Post Line", OnSetCommitBehavior, '', false, false)] + local procedure OnSetCommitBehaviorHandler(var IgnoreCommit: Boolean) + begin + if SetIgnoreCommitToFalse then + IgnoreCommit := false; + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Gen. Jnl.-Post Line", OnAfterGLFinishPosting, '', false, false)] + local procedure OnAfterGLFinishPostingHandler(GLEntry: Record "G/L Entry"; var GenJnlLine: Record "Gen. Journal Line") + var + ActivityLog: Record "Activity Log"; + begin + if not RaiseErrorAfterCommit then + exit; + if IsNullGuid(CommitTestMarkerID) then + exit; + + // Insert a marker record + ActivityLog.Init(); + ActivityLog."Activity Date" := CurrentDateTime(); + ActivityLog."User ID" := CopyStr(UserId(), 1, MaxStrLen(ActivityLog."User ID")); + ActivityLog."Activity Message" := Format(CommitTestMarkerID); + ActivityLog.Status := ActivityLog.Status::Success; + ActivityLog.Insert(true); + + // Call Commit() - this should be suppressed by CommitBehavior::Ignore + Commit(); + + // Raise an error to abort the transaction + Error(TestRollbackAfterCommitErr); + end; }