[Shopify] Automatic Transaction Posting - #9525
[Shopify] Automatic Transaction Posting#9525Onat Buyukakkus (onbuyuka) wants to merge 20 commits into
Conversation
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
…620951-shopify-automatic-transaction-posting
…620951-shopify-automatic-transaction-posting
…620951-shopify-automatic-transaction-posting
|
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 |
|
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 |
|
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 |
|
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 |
|
UnitTestAutoPostJnlBatchValidateWithoutBalAccountNo uses a bare 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 |
|
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 |
Predrag Maricic (PredragMaricic)
left a comment
There was a problem hiding this comment.
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
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 ( 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 ( S3 — filter vs. posting eligibility + end date. The "Filter Postable Transactions" list now enforces the same predicates as the posting routine ( AL review agent — inline threads (resolved)
AL review agent — general comments
All tests green: 15/15 auto-post + 9/9 Suggest Payment regression. App builds clean (0 errors / 0 warnings). |
Resolves the PR review findings on automatic transaction posting: - Remove the two unused using directives that broke every app build (AL0792). - Perf: calculate the Used FlowField via SetAutoCalcFields on the eligibility callers instead of a per-row CalcFields inside the loop. - Privacy/telemetry: stop emitting raw error text/call stack; the finalization failure event now carries only an error code as SystemMetadata. - Drop the journal permission pre-check and the finalize codeunit's elevated Permissions property in favour of best-effort posting. - Revert Credit Card Company to Text[30] to avoid a primary-key width change on the released Shpfy Payment Method Mapping table. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5155ee0a-3835-4415-9dc0-a79dfd96b734
7e2fbbc
Good Sense Reviewer - Round 3Recommendation: Request ChangesWhat this PR doesThis change adds automatic posting for Shopify sale, capture, and refund transactions when the related sales invoice or credit memo is posted. It also adds setup fields, a postable-transaction filter, isolated journal batches, failure logging, and regression tests. The approach is mostly aligned with the requested feature: it uses a shared eligibility codeunit, avoids posting unrelated journal lines, skips previews and suppressed-commit posting, and defers partial documents. Two issues still block merge: the current build fails on new analyzer warnings, and refund posting can still apply one refund transaction across other refunds on the same Shopify order. Problem-solution fitFit: Partial The feature goal is clear and the implementation covers the main sale, capture, and refund flows. The fit is not strong yet because the current head does not pass the build and one refund edge case can post the wrong application. Since last review
New observationsS3 (🔴 High): Filter refund posting to the current refund S4 (🔴 High): Rename the shadowing test variables Risk assessment and necessityRisk: This is a financial posting path. An incorrect refund match can apply customer ledger entries to the wrong credit memo, and a build failure blocks all validation of the Shopify app changes. Necessity: The feature is useful and the scope is appropriate: merchants need Shopify payments and refunds to follow posted documents without manual journal work. The blockers should be fixed before merge because they affect build health and refund posting correctness.
|
Scope refund payment application to the transaction's refund and add regression coverage for cross-refund application. Rename the shadowing test variables so analyzer builds no longer fail on AA0198 warnings. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 975e816a-005e-432e-979c-7b8664c73886
|
Addressed the two actionable Round 3 findings in ecef587:
The remaining automated inline recommendations were assessed, replied to, and resolved individually. |
|
The new refund path reads OrderTransaction."Refund Id" in GetOrderTransactions(), but OnPreDataItem still partial-loads "Shpfy Order Transaction" without that field. Each refund row will therefore fall back to a just-in-time field load, which undermines the report's partial-record optimization. Add "Refund Id" to the preloaded field set before iteration starts. Suggested fix (apply manually — could not be anchored as a one-click suggestion): SetLoadFields(Amount, "Rounding Amount", Type, "Shopify Order Id", "Shopify Transaction Id", Gateway, "Gift Card Id", "Payment Method", "Refund Id");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 |
Good Sense Reviewer - Round 4Recommendation: Accept with SuggestionsWhat this PR doesThis change adds automatic posting for Shopify sale, capture, and refund transactions when the related sales invoice or credit memo is posted. It also adds setup fields, a shared eligibility check, isolated journal batches, failure logging, and regression tests. The latest commit addresses the open refund correctness issue by limiting refund application to the current refund id and adds a regression test for two refunds on the same order. It also fixes the analyzer warning caused by shadowing test variables. The remaining concern is non-blocking: the new refund-id read should be included in the report's partial-record field list. Status of previous suggestions
New observations (commits since round 3)S5 (🟠 Moderate): Preload Refund Id for refund report filtering Risk assessment and necessityRisk: This remains a financial posting path. The prior wrong-refund risk is addressed, and the remaining issue is limited to report performance on refund transaction iteration. Necessity: The feature is justified because it removes manual journal work for Shopify payments and refunds while preserving setup and posting safeguards. The latest round is small and directly targets the previous blockers.
|
Preserve the legacy order-level refund fallback when imported transactions do not have a refund ID. Make the cross-refund test localization-safe and configure future posting dates before sales lines are created. Fixes AB#620951 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 975e816a-005e-432e-979c-7b8664c73886
|
IgnorePostedTransactionsLbl is used as the prompt for Confirm() on line 199, but the label-suffix guidance reserves Qst for confirmation questions. Keeping the Lbl suffix hides that this text is a question prompt and makes the call-site contract harder to scan. Rename it to IgnorePostedTransactionsQst and update the Confirm(...) call so the suffix matches the actual usage. 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 |
Good Sense Reviewer - Round 5Recommendation: Accept with SuggestionsWhat this PR doesThis change adds automatic posting for Shopify sale, capture, and refund transactions when the related sales invoice or credit memo is posted. It also adds setup fields, a shared eligibility check, isolated journal batches, failure logging, and regression tests. The latest commit preserves the manual refund suggestion fallback when a refund transaction has no refund id, while keeping exact refund-id filtering for transactions that do have one. It also adjusts the refund regression test to use the posted credit memo total and fixes the future-posting-date test setup. The remaining concern is still non-blocking: the report reads the refund id but does not include it in the dataitem load field list. Status of previous suggestions
New observations (commits since round 4)None - the latest commit only adjusts the refund fallback and tests. The remaining open item is tracked above. Risk assessment and necessityRisk: This remains a sensitive financial posting path because it creates and posts journal lines for Shopify payments and refunds. The latest refund-id change keeps exact matching when the transaction has a refund id, and only uses the broader order-level fallback for legacy rows without one. The remaining issue is limited to extra database reads during report iteration. Necessity: The feature is justified because it removes manual payment and refund journal work while keeping setup, permission, posting, and failure-handling safeguards. The latest commit is narrow and supports existing legacy refund data behavior without changing the automatic posting filter for exact refund ids.
|
Make the automatic-posting integration tests localization-safe: - Reset the posted shipment/invoice number series' Last Date Used before posting so an earlier test's future posting date does not break later posts under Date Order number series (IT). - Unlink the open sales document from the order instead of deleting it, avoiding the gap-fill posting number series message on deletion (AU). Fixes AB#620951 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5155ee0a-3835-4415-9dc0-a79dfd96b734
|
The Suggest Shopify Payments action on the Shpfy Transactions page still uses Suggested fix (apply manually — could not be anchored as a one-click suggestion): CurrPage.SetSelectionFilter(OrderTransaction);
if not OrderTransaction.MarkedOnly then
OrderTransaction.Copy(Rec);Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.36.6 |
Good Sense Reviewer - Round 6Recommendation: Accept with SuggestionsWhat this PR doesThis change adds automatic posting for Shopify sale, capture, and refund transactions when the related sales invoice or credit memo is posted. It also adds setup fields, a shared eligibility check, isolated journal batches, failure logging, and regression tests. The new commit only makes tests safer across localizations: it resets posted shipment and invoice number-series date usage before scenarios affected by a future posting date, and it unlinks an open sales document instead of deleting it. These changes do not alter the runtime posting flow. The remaining non-blocking concern is still that the payment suggestion report reads the refund id but does not include it in the dataitem load field list. Status of previous suggestions
New observations (commits since round 5)None - the latest commit only adjusts tests for localization and number-series behavior. The remaining open item is tracked above. Risk assessment and necessityRisk: This remains a sensitive financial posting path because it creates and posts journal lines for Shopify payments and refunds. The latest commit is test-only, so it does not add posting risk. The remaining issue is limited to extra database reads during refund transaction iteration in the suggestion report. Necessity: The feature is justified because it removes manual payment and refund journal work while keeping setup, permission, posting, and failure-handling safeguards. The latest commit is narrow and helps the automatic-posting test suite run consistently across localizations.
|
|
Pull request was closed
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
Automatic-posting setup
Post Automatically,Auto-Post Jnl. Template, andAuto-Post Jnl. Batchto payment-method mappings.Auto-Post Enabledtransaction FlowField aligned with the complete setup requirements.Posting and filtering
Posting safety
Codeunit.Runoperations.Tests
The automatic-posting test suite covers setup validation, Sale/Capture/Refund posting, multiple and mixed transactions, unrelated journal lines, partial invoices and credit memos, document-link transaction boundaries, future posting dates, suppressed commits, preview, job-queue setup, failure handling, parameter propagation, and shared postable eligibility.
The Shopify app builds successfully with the AL MCP server. The full local test-project build is currently blocked by the existing
MockAzureKeyVaultSecretProviderenvironment dependency; CI provides the full test matrix.Fixes AB#620951