bcquality/custom/knowledge/integration/make-failed-integration-messages-manually-resolvable.good.al
Tharanga Chandrasekara 07140e2223 Add new action skills for AL testing and documentation
- Introduced `al-test-writer` to generate AL test codeunits for production objects based on TDD principles.
- Added `al-userguide-test-writer` to create test codeunits from user guide steps, mapping actions and assertions.
- Implemented `bc-extension-test-guide` to generate a comprehensive TEST_GUIDE.md for Business Central extensions, covering various categories.
- Created `bc-webclient-runner` to automate UI testing of the Business Central web client, capturing screenshots and asserting UI states.
- Developed `page-scripting-e2e` to produce a recording plan for Page Scripting, ensuring a structured approach to browser-level testing.
2026-06-07 12:26:47 +12:00

75 lines
3.2 KiB
AL

// Best practice: a resolution page over failed messages. Ops corrects the payload and flips
// Status to New; the processor re-runs the SAME Message ID under the SAME idempotency key, so
// the fix reprocesses without creating a duplicate. The shape mirrors what Microsoft ships for
// E-Document: editable staging, a resolution page, a status enum, retry actions, and an audit trail.
page 50220 "Integration Resolution"
{
PageType = List;
SourceTable = "Integration Message";
// Failed messages stay EDITABLE so ops can correct the payload without a developer or a deploy.
Editable = true;
SourceTableView = where(Status = const(Failed));
Caption = 'Integration Resolution';
layout
{
area(Content)
{
repeater(Group)
{
// The id is shown but NOT editable: re-running must reuse it so the idempotency
// key (derived from this id) stays the same and the retry cannot double-apply.
field("Message ID"; Rec."Message ID") { Editable = false; }
field("External Reference"; Rec."External Reference") { Editable = false; }
// The error context ops needs to diagnose the failure. Read-only: it is history.
field("Error Message"; Rec."Error Message") { Editable = false; }
field("Retry Count"; Rec."Retry Count") { Editable = false; }
// The payload ops actually edits to fix a malformed or mis-mapped message.
field(Request; Rec.GetRequest()) { }
field("Resolution Note"; Rec."Resolution Note") { } // audit of what was decided
}
}
}
actions
{
area(Processing)
{
action(Resolve)
{
Caption = 'Resolve';
trigger OnAction()
begin
// Re-run the SAME message: same Message ID, therefore same idempotency key.
// The processor reprocesses the corrected payload, and the receiver recognises
// the repeat, so a side effect that partly landed is not applied a second time.
Rec.Status := Rec.Status::New;
Rec.Modify(true);
end;
}
action(ConfirmByException)
{
Caption = 'Confirm by Exception';
trigger OnAction()
begin
// Accept as handled with NO retry (for example the work was completed manually
// out of band). The audit record is kept so the decision is traceable.
Rec.Status := Rec.Status::Resolved;
Rec."Resolution Note" := 'Confirmed by exception';
Rec.Modify(true);
end;
}
action(Reassign)
{
Caption = 'Reassign';
trigger OnAction()
begin
// Route to another handler/queue without losing the message or its history.
Rec."Assigned To" := PickHandler();
Rec.Modify(true);
end;
}
}
}
}