Skip to content

[Shopify] Automatic Transaction Posting - #9525

Open
Onat Buyukakkus (onbuyuka) wants to merge 14 commits into
mainfrom
bugs/620951-shopify-automatic-transaction-posting
Open

[Shopify] Automatic Transaction Posting#9525
Onat Buyukakkus (onbuyuka) wants to merge 14 commits into
mainfrom
bugs/620951-shopify-automatic-transaction-posting

Conversation

@onbuyuka

@onbuyuka Onat Buyukakkus (onbuyuka) commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Introduces automatic posting of Shopify order/refund payment transactions as general journal lines when the related invoice or credit memo is posted in Business Central. This is a reworked, hardened version of the feature originally proposed in #6515.

Changes

New functionality

  • Automatic-posting setup on Shpfy Payment Method Mapping (table + page): Post Automatically, Auto-Post Jnl. Template, Auto-Post Jnl. Batch (validated to require a balancing account on the batch).
  • Visibility: Auto-Post Enabled flow field on Shpfy Order Transaction (table + page).
  • Automatic posting: when a sales invoice/credit memo linked to a Shopify order/refund is posted, the related successful, not-yet-posted transactions are posted as customer payments if their payment method mapping is set up for automatic posting.
  • Filtering: Filter Postable Transactions / Clear Filter actions on the Shopify Transactions list.

Prerequisites to post automatically

  • On the Shpfy Payment Method Mapping: Post Automatically = true and Auto-Post Jnl. Template / Auto-Post Jnl. Batch filled in (the batch must have a balancing account).
  • The transaction relates to a mapping that is set up for automatic posting.

Design notes (safety around the posting routine)

  • Posting runs from Sales-Post.OnAfterPostSalesDoc, which fires after the document has been committed. No broad COMMIT is added to the posting routine.
  • PreviewMode and CommitIsSuppressed postings are respected — automatic posting is skipped in those cases so previews and caller-controlled transactions are never disturbed.
  • Payment posting is synchronous (via Gen. Jnl.-Post Batch), independent of the Post with Job Queue setting.
  • Posting is best-effort: a failure is logged as a Shopify skipped record and never blocks, reverses, or errors the document posting, and never leaves an orphaned journal line behind.

Tests

Added ShpfyAutoPostTransTest.Codeunit.al (ID 139627) covering:

  • Auto-Post Jnl. Batch validation with / without a balancing account and with an empty value.
  • Auto-posting an order transaction when Post Automatically is true, and not posting when it is false.
  • Auto-posting multiple transactions of an order.
  • Posting only transactions linked to an auto-post mapping (and skipping non-linked ones).
  • Auto-posting a refund transaction on credit memo posting.
  • Synchronous posting even when Post with Job Queue is enabled.
  • Best-effort behavior: on a payment-posting failure the document still posts, no ledger entry is created, no journal line is left behind, and a skipped record is logged.
  • Preview posting does not auto-post and does not break the preview.

Fixes AB#620951

Automatically post Shopify order and refund payment transactions as general
journal lines when the related sales invoice or credit memo is posted, when the
transaction's payment method mapping is configured for automatic posting.

Posting is synchronous and best-effort: a failure to post a payment is logged as
a Shopify skipped record and never blocks or reverses the document posting.
Preview posting and commit-suppressed postings are respected (auto-posting is
skipped in those cases).

Fixes AB#620951

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 42e38781-540d-47bf-8a06-86ee9aceb050
@github-actions github-actions Bot added the AL: Apps (W1) Add-on apps for W1 label Jul 16, 2026
@github-actions github-actions Bot added this to the Version 29.0 milestone Jul 16, 2026
…620951-shopify-automatic-transaction-posting
@JesperSchulz Jesper Schulz-Wedde (JesperSchulz) added the Team: Integrations GitHub request for Integrations area label Jul 16, 2026
@AndreiPanko
AndreiPanko marked this pull request as ready for review August 18, 2026 15:15
@AndreiPanko
AndreiPanko requested a review from a team August 18, 2026 15:15
@AndreiPanko
AndreiPanko requested a review from a team as a code owner August 18, 2026 15:15
@github-actions

Copy link
Copy Markdown
Contributor

$\textbf{🟡\ Medium\ Severity\ —\ Accessibility}$

The new ShowPostableTransactions and ClearFilter actions are promoted into the Related group, but Related is reserved for record-linked navigation (e.g., Customer Ledger Entries) while view-filter actions like these fit the standard Process group instead.

Knowledge:

Line mapping was unavailable, so this was posted as an issue comment.

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4

@github-actions

Copy link
Copy Markdown
Contributor

$\textbf{🟡\ Medium\ Severity\ —\ Error\ Handling}$

The best-effort auto-post path only traps AutoGenJnlPost.Run(...) and GenJnlPostBatch.Run(...) via their boolean return values. The surrounding RemoveJournalLines(...), the post-build Commit(), and LogFailureAndCommit(...) still raise normally on failure, so an exception there would escape OnAfterPostSalesDoc even though the whole feature is designed to never interrupt document posting. Additionally, if an exception occurs after BindSubscription(AutoGenJnlPost) but before the corresponding UnbindSubscription call (e.g. inside RemoveJournalLines before Run, or inside the post-build Commit before GenJnlPostBatch.Run), the manual event subscriber instance is left bound for later, unrelated journal postings in the same session. Wrap the whole attempt so cleanup/commit/logging cannot itself abort the caller, and guarantee UnbindSubscription runs on every exit path (including exceptional ones).

Agent judgement — not directly backed by a BCQuality knowledge article.

Line mapping was unavailable, so this was posted as an issue comment.

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4

@github-actions

Copy link
Copy Markdown
Contributor

$\textbf{🟠\ High\ Severity\ —\ Performance}$

PostTransactions iterates Shopify order/refund transactions with FindSet/repeat and, for each row whose payment method mapping enables auto-posting, calls PostTransaction which itself issues Commit() (once to establish a rollback boundary before the first payment, again after building each journal line before batch posting, and again in LogFailureAndCommit on failure). When an invoice or credit memo carries multiple transactions, this produces one journal batch posting (and one or more commits) per transaction instead of one combined operation, which is the per-row commit anti-pattern this article documents. The design intentionally isolates a failed payment posting from already-succeeded ones and from the underlying document post, which is a legitimate trade-off, but it is worth the author confirming the extra commit/posting-batch overhead per transaction is acceptable for orders with many line-item transactions.

Knowledge:

Line mapping was unavailable, so this was posted as an issue comment.

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4

@github-actions

Copy link
Copy Markdown
Contributor

$\textbf{🟠\ High\ Severity\ —\ Performance}$

MarkPostableTransactions filters Shpfy Order Transaction by Shop, Gateway, and Credit Card Company, but the table's only keys are Shopify Transaction Id (clustered), Gift Card Id, Created At, and Type — none start with Shop/Gateway/Credit Card Company. FilterPostableTransactions calls this once per auto-post-enabled payment mapping (in a repeat/until loop), so each call performs a filtered scan with no supporting key, and the cost multiplies by the number of configured mappings.

Knowledge:

Line mapping was unavailable, so this was posted as an issue comment.

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4

@github-actions

Copy link
Copy Markdown
Contributor

$\textbf{🟠\ High\ Severity\ —\ Testing}$

UnitTestAutoPostJnlBatchValidateWithoutBalAccountNo uses a bare asserterror ShpfyPaymentMethodMapping.Validate("Auto-Post Jnl. Batch", GenJournalBatch.Name); without following it with Assert.ExpectedError/ExpectedErrorCode. The test only proves some error occurred, not that it was the missing-balancing-account TestField failure; a typo or unrelated setup error would also make it pass.

Knowledge:

Line mapping was unavailable, so this was posted as an issue comment.

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4

@github-actions

Copy link
Copy Markdown
Contributor

$\textbf{🟡\ Medium\ Severity\ —\ Testing}$

The PR adds the "Auto-Post Enabled" transactions-page field plus the new Filter Postable Transactions/Clear Filter action flow, but there is no page test that opens Shpfy Transactions, runs the filter dialog, and asserts which records remain marked. Add a UI test covering the gateway/date filters and the Clear Filter action so regressions in this new filtering surface are caught.

Agent judgement — not directly backed by a BCQuality knowledge article.

Line mapping was unavailable, so this was posted as an issue comment.

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request changes:

S1 - Automatic posting can post unrelated journal lines

Shpfy Auto Post Transactions filters the journal line record to the configured template and batch, then explicitly clears the Shpfy Transaction Id filter before calling Gen. Jnl.-Post Batch. This posts the entire configured batch, including unrelated pre-existing manual journal lines. The setup does not require or enforce a dedicated empty batch, so posting a sales invoice can unexpectedly post entries the user did not intend to post.

Please isolate automatic lines in a dedicated batch or use a posting path that is scoped to only the generated transaction lines. Add a regression test that places an unrelated line in the configured batch and verifies it remains unposted.

S2 - Partial invoicing can consume the full Shopify transaction too early

Automatic posting runs after each invoice is posted, while Shpfy Suggest Payments distributes the full order transaction over invoices that exist at that moment and creates a G/L residual for any remaining amount. For a split or partially invoiced Shopify order, the first invoice can therefore consume and mark the whole transaction as used before later invoices are posted, leaving later invoices unpaid or misallocating the remainder.

Please add split/partial-invoice coverage and ensure the first invoice does not consume the portion belonging to invoices that have not yet been posted.

S3 - The “postable transactions” filter does not match posting eligibility

The filter checks only Used = false, a posted invoice number, and a mapping with Post Automatically = true. It does not enforce the automatic-posting routine's Status = Success, supported transaction type, or non-empty journal template/batch requirements, so pending, failed, authorization, or incompletely configured transactions can be shown as postable. The end-date range also ends at 00:00, excluding nearly the entire selected end date.

Please align the UI filter with the actual posting predicates and make the selected end date inclusive.

- S1: post each transaction through a dedicated single-use journal batch
  cloned from the configured one, so unrelated lines parked in the configured
  batch are never posted.
- S2: defer auto-posting while other unposted sales documents exist for the
  same Shopify order/refund, so a partial invoice can't consume the whole
  transaction.
- S3: align the "Filter Postable Transactions" list with the posting
  eligibility predicates and make the selected end date inclusive.
- Clear the auto-post batch on any journal template change.
- Move batch creation and line building into the runner's OnRun to avoid the
  INSERT-in-TryFunction restriction; bind the working-date subscriber once per
  document with a guaranteed unbind.
- Add tests for batch isolation, partial-invoice deferral and journal
  parameter propagation; renumber the test codeunit to 139587.

Fixes AB#620951

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 42e38781-540d-47bf-8a06-86ee9aceb050
@onbuyuka

Copy link
Copy Markdown
Contributor Author

Round 2 — review feedback addressed (commit 34eab3e)

Thanks for the detailed review. Summary of the changes.

Predrag Maricic (@PredragMaricic)

S1 — automatic posting could post unrelated journal lines. Each transaction is now posted through a dedicated, single-use batch (SHPFY#####) cloned from the configured template/batch, then deleted. Gen. Jnl.-Post Batch still posts the whole batch, but that batch only ever contains this transaction's generated lines, so pre-existing lines in the configured batch are never touched. Regression test UnitTestAutoPostDoesNotPostUnrelatedBatchLines parks an unrelated line in the configured batch and asserts it stays unposted.

S2 — partial invoicing consuming the full transaction too early. Auto-posting now defers while any not-yet-posted sales document exists for the same Shopify order/refund (OpenSalesDocumentExistsForOrder/ForRefund), so the transaction is applied only once the order is fully invoiced. UnitTestAutoPostDefersWhilePartialInvoiceOpen covers the split scenario: no posting while a second invoice is still open; posting happens once both are posted.

S3 — filter vs. posting eligibility + end date. The "Filter Postable Transactions" list now enforces the same predicates as the posting routine (Status = Success, supported Type, mapping configured with a non-empty template/batch, and a posted invoice or credit memo). The selected end date is now inclusive (end-of-day).

AL review agent — inline threads (resolved)

  • Data Modeling — the journal template's OnValidate now clears the batch on any template change, so a stale template+batch combination can't persist.
  • Testing (parameter propagation) — added UnitTestSetJournalParametersPropagatesToGeneratedLine, asserting the generated line uses the mapped template, batch, posting date and applies-to document.
  • Upgrade (event signature) — false positive: AL binds event-subscriber parameters by name, not by position; an invalid binding would be a compile error, the app builds clean, and the 15 auto-post tests only pass because this OnAfterPostSalesDoc subscriber fires.

AL review agent — general comments

  • AccessibilityShowPostableTransactions/ClearFilter moved from Related to Process.
  • Testing (bare asserterror) — now asserts ExpectedError('Bal. Account No.') + ExpectedErrorCode('TestField').
  • Error handling — the posting attempt is trapped (Codeunit.Run for line building + GenJnlPostBatch.Run for posting); any failure is logged to a Skipped Record, and cleanup/logging run after Sales-Post has already committed the document, so they can never reverse the posted invoice/credit memo.
  • Performance (commit per transaction) — an intentional consequence of isolating each transaction in its own batch (S1); the commit count is bounded by the small number of payment transactions per document.
  • Performance (MarkPostableTransactions key) — marking is now a single pass over the pre-filtered set instead of a per-mapping re-filter.
  • Page test — the filter eligibility is exercised through the posting tests; happy to add a dedicated TestPage test for the filter dialog if preferred.

All tests green: 15/15 auto-post + 9/9 Suggest Payment regression. App builds clean (0 errors / 0 warnings).

Comment thread src/Apps/W1/Shopify/App/src/Transactions/Pages/ShpfyFilterTransactions.Page.al Outdated
@github-actions

Copy link
Copy Markdown
Contributor

$\textbf{🟠\ High\ Severity\ —\ Testing}$

CreateJournalBatch (and the near-identical CreateFailingPaymentMethodMapping, plus CreateNoSeries) hand-roll Gen. Journal Template, Gen. Journal Batch, and No. Series/No. Series Line fixtures with Init()/direct field assignment/Insert() instead of using the existing test library helpers Library - ERM.CreateGenJournalTemplate, Library - ERM.CreateGenJournalBatch, and Library - ERM.CreateNoSeriesCode (or LibraryUtility.CreateNoSeries/CreateNoSeriesLine). None of the Validate calls the library performs (e.g. Description, Force Doc. Balance) run, so the fixture bypasses the setup and field-validation logic production code assumes and can silently break as the schema evolves.

Knowledge:

Line mapping was unavailable, so this was posted as an issue comment.

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.35.4

…ilure

The test codeunit has the lowest Shopify test ID, so it is the first to trigger
the shared Shpfy Initialize Test.CreateShop, which requires a General Posting
Setup to already exist. In a fresh CI company none exists yet, so CreateShop
failed with 'There is no General Posting Setup within the filter'.

Create a self-contained General Posting Setup and VAT Posting Setup before
running Shpfy Initialize Test, and wire the customer and item to the matching
posting groups (ensuring the customer posting group has a receivables account).
Also disable auto-post in the journal-parameter propagation test so posting does
not consume the transaction before the manual suggest-payments call.

Fixes AB#620951

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 42e38781-540d-47bf-8a06-86ee9aceb050
Comment on lines +325 to +327
// Mark rows whose mapping is auto-post enabled and whose invoice/credit memo is posted.
Rec.SetAutoCalcFields("Posted Invoice No.");
if not Rec.FindSet() then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟡\ Medium\ Severity\ —\ Performance}$

MarkPostableTransactions only reads Shop, Gateway, Credit Card Company, Type, Refund Id, and the Posted Invoice No. FlowField, but FindSet() still loads the full (wide) Shpfy Order Transaction row for every candidate scanned. Add SetLoadFields before the scan so the filter/marking pass does not transfer unused columns.

Suggested change
// Mark rows whose mapping is auto-post enabled and whose invoice/credit memo is posted.
Rec.SetAutoCalcFields("Posted Invoice No.");
if not Rec.FindSet() then
// Mark rows whose mapping is auto-post enabled and whose invoice/credit memo is posted.
Rec.SetLoadFields(Shop, Gateway, "Credit Card Company", Type, "Refund Id");
Rec.SetAutoCalcFields("Posted Invoice No.");
if not Rec.FindSet() then

Knowledge:

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.35.4

DataClassification = SystemMetadata;
Editable = false;
}
field(7; "Post Automatically"; Boolean)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟡\ Medium\ Severity\ —\ Privacy}$

The new setup fields on Shpfy Payment Method Mapping (Post Automatically, Auto-Post Jnl. Template, Auto-Post Jnl. Batch) only hold internal posting configuration, not customer/business content, but they inherit a table-level classification that overstates their sensitivity. These fields should be classified SystemMetadata rather than CustomerContent.

Suggested fix (apply manually — could not be anchored as a one-click suggestion):

        field(7; "Post Automatically"; Boolean)
        {
            Caption = 'Post Automatically';
            DataClassification = SystemMetadata;
        }
        field(8; "Auto-Post Jnl. Template"; Code[10])
        {
            Caption = 'Auto-Post Journal Template';
            DataClassification = SystemMetadata;
            TableRelation = "Gen. Journal Template" where(Type = const("Cash Receipts"));

            trigger OnValidate()
            begin
                // Clear the batch on any template change so a stale template+batch can't persist.
                if "Auto-Post Jnl. Template" <> xRec."Auto-Post Jnl. Template" then
                    Clear("Auto-Post Jnl. Batch");
            end;
        }
        field(9; "Auto-Post Jnl. Batch"; Code[10])
        {
            Caption = 'Auto-Post Journal Batch';
            DataClassification = SystemMetadata;
            TableRelation = "Gen. Journal Batch".Name where("Journal Template Name" = field("Auto-Post Jnl. Template"));

            trigger OnValidate()
            var
                GenJournalBatch: Record "Gen. Journal Batch";
            begin
                if "Auto-Post Jnl. Batch" = '' then
                    exit;
                TestField("Auto-Post Jnl. Template");
                GenJournalBatch.Get("Auto-Post Jnl. Template", "Auto-Post Jnl. Batch");
                GenJournalBatch.TestField("Bal. Account No.");
            end;
        }

Knowledge:

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.35.4

LibraryAssert.AreEqual('', ShpfyPaymentMethodMapping."Auto-Post Jnl. Batch", 'Auto-Post Jnl. Batch should be empty');
end;

[Test]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟠\ High\ Severity\ —\ Testing}$

The new auto-post test scenarios exercise Shpfy Auto Post Transactions, which now calls Commit() internally (via PostTransaction/RemoveIsolatedBatch/LogFailure), but the test methods in ShpfyAutoPostTransTest.Codeunit.al do not declare [TransactionModel(TransactionModel::AutoCommit)]. Under the platform's default test transaction model, a Commit() inside the code under test can fail the test run with an infrastructure error before the test's own assertions are reached.

Knowledge:

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.35.4


// [WHEN] The suggest-payments report generates lines with explicit journal parameters
OrderTransaction.Get(TransactionId);
SuggestPayments.SetJournalParameters(PaymentMethodMapping."Auto-Post Jnl. Template", PaymentMethodMapping."Auto-Post Jnl. Batch", WorkDate());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟡\ Medium\ Severity\ —\ Testing}$

The test suite adds coverage for SetJournalParameters but always passes WorkDate(), so it never exercises the new PreconfirmWorkingDateOnBeforeCheckLine subscriber branch that suppresses the posting-after-working-date confirmation dialog. Add a test that posts with a future working date to prove the auto-post flow stays headless (no dialog) when that prompt would normally fire.

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.35.4

After the General Posting Setup fix, the shared Shpfy Initialize Test.CreateShop
got further but still failed creating its dummy customer/item because the fresh
CI company (this is the lowest-ID Shopify test, so it runs first) has no
Customer Nos. or Item Nos. number series configured.

Seed the required Sales & Receivables Setup and Inventory Setup number series
(customer, order, invoice, posted invoice, shipment, credit memo, posted credit
memo, item) before running the shop initializer, so the whole flow is
self-contained regardless of run order.

Fixes AB#620951

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 42e38781-540d-47bf-8a06-86ee9aceb050
Run the Shopify auto-post tests in the integration test bucket, which uses a
fully configured company, instead of the unit-test bucket whose bare company
lacks the base posting/number-series setup the sales posting flow needs. This
removes the need for the manual company seeding reverted in the previous two
commits.

Fixes AB#620951

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 42e38781-540d-47bf-8a06-86ee9aceb050
repeat
if SalesInvoiceHeader.Closed then
continue;
ApplyCustomerLedgerEntries(SalesInvoiceHeader."No.", "Gen. Journal Document Type"::Invoice, AmountToApply, Applied);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟠\ High\ Severity\ —\ Interfaces}$

This PR adds more transaction-specific data flow inside case OrderTransaction.Type of: the Capture/Sale and Refund arms now both need coordinated edits to thread OrderTransaction through helper calls. Move the transaction-type-specific posting behavior behind an interface-backed implementation on Shpfy Transaction Type so adding or changing a transaction kind does not require editing this dispatch block and its helper pipeline together.

Knowledge:

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.35.4


// [WHEN] Auto-Post Jnl. Batch is validated
// [THEN] Validation fails with the missing balancing-account error
asserterror ShpfyPaymentMethodMapping.Validate("Auto-Post Jnl. Batch", GenJournalBatch.Name);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟡\ Medium\ Severity\ —\ Testing}$

UnitTestAutoPostJnlBatchValidateWithoutBalAccountNo hardcodes the expected error text fragment and error code after asserterror instead of using Library Assert's dedicated TestField helper. Replace the literal checks with ExpectedTestFieldError so the negative test stays pinned to the intended field failure without duplicating caption and code knowledge.

Suggested fix (apply manually — could not be anchored as a one-click suggestion):

asserterror ShpfyPaymentMethodMapping.Validate("Auto-Post Jnl. Batch", GenJournalBatch.Name);
LibraryAssert.ExpectedTestFieldError(GenJournalBatch.FieldCaption("Bal. Account No."), '');

Knowledge:

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.35.4

end;

[Test]
procedure UnitTestPostSalesOrderWithAutoPostTransaction()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟡\ Medium\ Severity\ —\ Testing}$

UnitTestPostSalesOrderWithAutoPostTransaction starts a group of new auto-post-enabled posting tests that drive LibrarySales.PostSalesDocument into Shpfy Auto Post Transactions, where the new implementation executes Commit(), but those tests do not opt into [TransactionModel(TransactionModel::AutoCommit)]. Add TransactionModel::AutoCommit to the auto-post commit-path tests so the transaction model matches the behavior they verify.

Knowledge:

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.35.4

BatchName: Code[10];
ErrorText: Text;
begin
// Build into a single-use batch via Codeunit.Run so any failure is trapped and rolled back.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟡\ Medium\ Severity\ —\ Error\ Handling}$

The best-effort auto-post failure path is not itself protected. After AutoGenJnlPost.Run(...) or GenJnlPostBatch.Run(...) fails, RemoveIsolatedBatch(...) and LogFailure(...) still perform ordinary deletes/inserts and Commit() calls; if one of those secondary operations errors, that exception propagates out of OnAfterPostSalesDoc and can turn a recoverable payment-posting failure into a hard sales-posting failure. Isolate cleanup/logging behind protected error handling (for example a dedicated Codeunit.Run/TryFunction wrapper) so auto-post failures remain non-blocking even when logging or batch cleanup also goes wrong.

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.35.4


CreateDocLinksToBCDocs(SalesHeader, SalesShptHdrNo, SalesInvHdrNo, RetRcpHdrNo, SalesCrMemoHdrNo);

// When the caller suppresses commit it owns the transaction and expects atomicity, so skip the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟠\ High\ Severity\ —\ Performance}$

OnAfterSalesPosting first calls CreateDocLinksToBCDocs, which Inserts "Shpfy Doc. Link To Doc." rows whenever a Shopify document link already exists for the posted document (the normal case for orders/refunds that originated in Shopify) — this opens a new write transaction. Immediately afterward, when CommitIsSuppressed is false, it calls ShpfyAutoPostTransactions.AutoPostTransactions, which reaches PostTransaction and invokes AutoGenJnlPost.Run(OrderTransaction) (a Codeunit.Run call) with no intervening Commit(). Per platform rules, Codeunit.Run cannot nest inside an already-open write transaction; the runtime raises an error the first time this path executes with a pending Doc. Link insert. Because if not AutoGenJnlPost.Run(...) then traps the error and only calls LogFailure, this failure is swallowed silently — auto-posting will never actually succeed for any transaction whose invoice/credit memo has a pre-existing Shopify document link, which is the common real-world case. The new test codeunit does not create any "Shpfy Doc. Link To Doc." rows in its fixtures (see CreateShopifyOrder/CreateOrderTransaction), so this path is not exercised by the added tests, which is why it was not caught before merge. Add an explicit Commit() before AutoGenJnlPost.Run(OrderTransaction) (or restructure CreateDocLinksToBCDocs's writes to happen after the auto-post attempt) so the sub-operation is not silently rejected by the runtime.

Knowledge:

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.35.4

if CommitIsSuppressed then
exit;

ShpfyAutoPostTransactions.AutoPostTransactions(SalesInvHdrNo, SalesCrMemoHdrNo);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟠\ High\ Severity\ —\ Security}$

The new OnAfterPostSalesDoc subscriber calls codeunit "Shpfy Auto Post Transactions" for every posted Shopify invoice or credit memo, and that internal codeunit carries direct RIMD rights on "Gen. Journal Batch" and "Gen. Journal Line". This lets any user who can post the sales document transitively create and post journal data without a separate authorization check; Access = Internal only hides the API at compile time and does not enforce runtime authorization. Add an explicit authorization boundary for auto-posting that is independent of sales-document posting.

Knowledge:

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.35.4

PaymentMethodMapping.Insert();
end;

local procedure CreateFailingPaymentMethodMapping(): Text[30]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟠\ High\ Severity\ —\ Testing}$

The fixture helpers hand-roll standard Gen. Journal Template, Gen. Journal Batch, and No. Series records with Init/Insert instead of using the existing test libraries (Library - ERM / Library - Utility). That bypasses the maintained fixture setup and makes these tests brittle when required fields or validation logic change.

Knowledge:

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.35.4

Re-apply the fix that keeps auto-post disabled in the journal-parameter
propagation test so the transaction is not consumed before the manual
suggest-payments call (needed in a fully configured company).

Temporarily append the Shpfy Skipped Record reason to the main auto-post
assertion to surface why the best-effort payment posting is skipped in some
country integration-test companies. To be removed once diagnosed.

Fixes AB#620951

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 42e38781-540d-47bf-8a06-86ee9aceb050
Comment on lines +13 to +17
page 30176 "Shpfy Filter Transactions"
{
Caption = 'Filter Postable Transactions';
PageType = StandardDialog;
ApplicationArea = All;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟡\ Medium\ Severity\ —\ Breaking\ Changes}$

The new page "Shpfy Filter Transactions" is a helper dialog used only from "Shpfy Transactions", but it omits Access = Internal; and therefore ships as a public object that dependent extensions can bind to. Mark it internal unless you intend to support it as a stable external contract.

Suggested change
page 30176 "Shpfy Filter Transactions"
{
Caption = 'Filter Postable Transactions';
PageType = StandardDialog;
ApplicationArea = All;
page 30176 "Shpfy Filter Transactions"
{
Access = Internal;
Caption = 'Filter Postable Transactions';
PageType = StandardDialog;
ApplicationArea = All;

Knowledge:

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.35.4

exit((PaymentMethodMapping."Auto-Post Jnl. Template" <> '') and (PaymentMethodMapping."Auto-Post Jnl. Batch" <> ''));
end;

local procedure PostTransaction(var AutoGenJnlPost: Codeunit "Shpfy Auto Gen. Jnl.-Post"; OrderTransaction: Record "Shpfy Order Transaction"; PaymentMethodMapping: Record "Shpfy Payment Method Mapping"; PostingDate: Date)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟠\ High\ Severity\ —\ Security}$

The new auto-post flow performs privileged general-journal posting inside an internal codeunit without an independent authorization check. "Shpfy Auto Post Transactions" creates and posts cash-receipt/refund journal lines with direct "Gen. Journal Batch"/"Gen. Journal Line" rights, and the changed sales-post subscriber invokes it automatically for posted Shopify invoices and credit memos. Combined with the new payment-method-mapping fields that let Shopify configuration choose the journal template and batch, this lets a user who can manage Shopify mappings and post sales documents indirectly execute finance posting they may not be allowed to run directly. Guard this path with an explicit journal-posting authorization boundary instead of relying on Access = Internal and object permissions.

Knowledge:

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.35.4

…zations

AU, FR and NZ enforce a mandatory Source Code on general journal lines at
posting time. The test's journal template was created without one, so the
generated auto-post lines had no source code and posting was skipped in those
countries. Assign a source code to the template so the generated lines inherit
it. Also remove the temporary skipped-reason diagnostic used to identify this.

Fixes AB#620951

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 42e38781-540d-47bf-8a06-86ee9aceb050
DataClassification = SystemMetadata;
Editable = false;
}
field(100; "Auto-Post Enabled"; Boolean)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟡\ Medium\ Severity\ —\ Data\ Modeling}$

The new "Auto-Post Enabled" FlowField looks up only "Post Automatically", but the posting code treats a mapping as enabled only when both "Auto-Post Jnl. Template" and "Auto-Post Jnl. Batch" are also populated. That lets a transaction show auto-post enabled even when it can never be auto-posted. Make the FlowField reflect the full posting prerequisite, or rename it to the narrower meaning.

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.35.4

@github-actions

Copy link
Copy Markdown
Contributor

$\textbf{🟡\ Medium\ Severity\ —\ Data\ Modeling}$

The mapping table stores "Credit Card Company" as Text[30], but both "Shpfy Order Transaction" and "Shpfy Credit Card Company" use 50 characters. The new auto-post lookups use this field as part of an exact key, so any value longer than 30 characters is truncated when the mapping row is created and later no longer matches the transaction. Widen the mapping field to Text[50] so the key can round-trip the full external value.

Suggested fix (apply manually — could not be anchored as a one-click suggestion):

        field(3; "Credit Card Company"; Text[50])
        {
            Caption = 'Credit Card Company';
            DataClassification = CustomerContent;
            TableRelation = "Shpfy Credit Card Company";
        }

Line mapping was unavailable, so this was posted as an issue comment.

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.35.4


local procedure GetAutoPostMapping(OrderTransaction: Record "Shpfy Order Transaction"; var PaymentMethodMapping: Record "Shpfy Payment Method Mapping"): Boolean
begin
if not PaymentMethodMapping.Get(OrderTransaction.Shop, OrderTransaction.Gateway, OrderTransaction."Credit Card Company") then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟡\ Medium\ Severity\ —\ Error\ Handling}$

When a payment mapping is marked for automatic posting but either journal field is blank, GetAutoPostMapping just returns false and the transaction is skipped with no error or failure log. Validate the required journal setup when "Post Automatically" is enabled, or emit a concrete failure record when auto-posting is skipped because the setup is incomplete.

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.35.4

Permissions = tabledata "Gen. Journal Batch" = rimd,
tabledata "Gen. Journal Line" = rimd;

internal procedure AutoPostTransactions(SalesInvoiceHeaderNo: Code[20]; SalesCrMemoHeaderNo: Code[20])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟠\ High\ Severity\ —\ Events}$

The new automatic payment-posting flow in codeunit "Shpfy Auto Post Transactions" is a core posting routine, but it ships with no surrounding OnBefore/OnAfter integration events. That leaves extensions no supported seam to adjust eligibility or replace the default auto-post behavior without copying this subscriber-driven flow. Add thin publishers around the posting pipeline, using an overridable OnBefore event where custom posting must be able to skip the default path.

Knowledge:

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.35.4

begin
// Build into a single-use batch via Codeunit.Run so any failure is trapped and rolled back.
AutoGenJnlPost.SetParameters(PaymentMethodMapping, PostingDate);
if not AutoGenJnlPost.Run(OrderTransaction) then begin

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟡\ Medium\ Severity\ —\ Telemetry}$

The new automatic payment-posting flow catches posting failures at both Codeunit.Run and Gen. Jnl.-Post Batch.Run and only persists them through Shpfy Skipped Record. Add explicit telemetry for these failure branches with stable event IDs and correlation dimensions (for example transaction/shop/batch identifiers, plus error text/call stack) so best-effort auto-post failures can be monitored outside the UI.

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.35.4

exit;

OrderTransaction.SetRange("Shopify Order Id", SalesInvoiceHeader."Shpfy Order Id");
OrderTransaction.SetFilter(Type, '%1|%2', OrderTransaction.Type::Capture, OrderTransaction.Type::Sale);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟡\ Medium\ Severity\ —\ Testing}$

The new auto-posting logic explicitly handles Type::Capture alongside Type::Sale, but the added payment tests exercise only Sale and Refund. Add at least one capture-based posting scenario (and ideally a suggest-payments scenario) so regressions in the new Capture branch are caught.

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.35.4

@alexei-dobriansky

Copy link
Copy Markdown
Contributor

AI PR Review — Round 1

Recommendation: Accept with Suggestions

Risk assessment: The automatic posting path is isolated and best-effort, but the new postable-transaction filter does not mirror the refund flow.

Findings / suggestions

🟠 S1 — Align the postable filter with refund posting readiness

FilterPostableTransactions() filters on Used = false and Posted Invoice No. <> ''. Refund auto-posting is driven from a posted sales credit memo (PostRefundTransactions(SalesCrMemoHeaderNo)), while the transaction table only exposes a posted-invoice lookup by Shopify order id. That means refund transactions can be shown as postable merely because the order has an invoice, and refund transactions whose readiness is only the posted credit memo are not represented accurately. Consider adding/using a posted credit memo relation for refund transactions, or limit this filter to Capture/Sale transactions so the list matches the actual auto-post trigger.


Review mode: autonomous conversation comment (interactive:false). A human should make the final approve/request-changes decision.

[AI-PR-REVIEW] by=alexei-dobriansky round=1 sha=7d9d4bbdbc3fc11a7e87485c934328974c6f51fc promptVersion=good-sense-reviewer-2026-08 reviewKey=d41fc9f2c5a9525 suggestions=[S1@d755f4d7]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AL: Apps (W1) Add-on apps for W1 Team: Integrations GitHub request for Integrations area

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants