-
Notifications
You must be signed in to change notification settings - Fork 444
[Shopify] Automatic Transaction Posting #9525
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 4 commits
6ade014
43ccc94
9fc5f4a
a1dc4d7
34eab3e
bd28a61
f4b58b5
2f9d707
b77c22f
addd2a6
97bba5e
b61a70b
1c5672b
7d9d4bb
3e7a814
73ba397
7e2fbbc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -103,6 +103,8 @@ permissionset 30104 "Shpfy - Objects" | |
| report "Shpfy Translator" = X, | ||
| codeunit "Company Details Checklist Item" = X, | ||
| codeunit "Shpfy Authentication Mgt." = X, | ||
| codeunit "Shpfy Auto Gen. Jnl.-Post" = X, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The new automatic-posting feature exposes general-journal setup on the Shopify payment-method mapping page and then creates/posts isolated general-journal batches during invoice/credit-memo posting, but none of the app's assignable Shopify permission sets (built on top of "Shpfy - Objects", which only grants execute permission on the new Shopify objects themselves) add the underlying Gen. Journal Line/Gen. Journal Batch table permissions the feature needs at setup and posting time. A user assigned only the app's Shopify roles cannot configure or exercise this feature without an additional, non-Shopify role, matching the AppSource anti-pattern of shipping a workflow with missing permission coverage. Knowledge: 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.35.4 |
||
| codeunit "Shpfy Auto Post Transactions" = X, | ||
| codeunit "Shpfy Background Syncs" = X, | ||
| codeunit "Shpfy Balance Today" = X, | ||
| codeunit "Shpfy Base64" = X, | ||
|
|
@@ -301,6 +303,7 @@ permissionset 30104 "Shpfy - Objects" | |
| page "Shpfy Customers" = X, | ||
| page "Shpfy Data Capture List" = X, | ||
| page "Shpfy Disputes" = X, | ||
| page "Shpfy Filter Transactions" = X, | ||
| page "Shpfy Fulfillment Order Card" = X, | ||
| page "Shpfy Fulfillment Order Lines" = X, | ||
| page "Shpfy Fulfillment Orders" = X, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| // ------------------------------------------------------------------------------------------------ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. See License.txt in the project root for license information. | ||
| // ------------------------------------------------------------------------------------------------ | ||
|
|
||
| namespace Microsoft.Integration.Shopify; | ||
|
|
||
| using Microsoft.Finance.GeneralLedger.Posting; | ||
|
|
||
| /// <summary> | ||
| /// Codeunit Shpfy Auto Gen. Jnl.-Post (ID 30422). | ||
| /// Builds the general journal line(s) for a single Shopify order/refund payment transaction. It is invoked | ||
| /// through Codeunit.Run so that a failure while building is trapped and rolled back without leaving a line | ||
| /// behind. While bound, it also pre-confirms the "posting after working date" prompt so the automatic | ||
| /// posting stays non-interactive. | ||
| /// </summary> | ||
| codeunit 30422 "Shpfy Auto Gen. Jnl.-Post" | ||
| { | ||
| Access = Internal; | ||
| EventSubscriberInstance = Manual; | ||
| TableNo = "Shpfy Order Transaction"; | ||
|
|
||
| trigger OnRun() | ||
| begin | ||
| BuildJournalLines(Rec); | ||
| end; | ||
|
|
||
| var | ||
| PaymentMethodMapping: Record "Shpfy Payment Method Mapping"; | ||
| PostingDate: Date; | ||
|
|
||
| internal procedure SetParameters(NewPaymentMethodMapping: Record "Shpfy Payment Method Mapping"; NewPostingDate: Date) | ||
| begin | ||
| PaymentMethodMapping := NewPaymentMethodMapping; | ||
| PostingDate := NewPostingDate; | ||
| end; | ||
|
|
||
| local procedure BuildJournalLines(var OrderTransaction: Record "Shpfy Order Transaction") | ||
| var | ||
| SuggestPayments: Report "Shpfy Suggest Payments"; | ||
| begin | ||
| SuggestPayments.SetJournalParameters(PaymentMethodMapping."Auto-Post Jnl. Template", PaymentMethodMapping."Auto-Post Jnl. Batch", PostingDate); | ||
| SuggestPayments.GetOrderTransactions(OrderTransaction); | ||
| SuggestPayments.CreateGeneralJournalLines(); | ||
| end; | ||
|
|
||
| [EventSubscriber(ObjectType::Codeunit, Codeunit::"Gen. Jnl.-Post Batch", 'OnBeforeCheckLine', '', false, false)] | ||
| local procedure PreconfirmWorkingDateOnBeforeCheckLine(var PostingAfterWorkingDateConfirmed: Boolean) | ||
| begin | ||
| PostingAfterWorkingDateConfirmed := true; | ||
| end; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,170 @@ | ||
| // ------------------------------------------------------------------------------------------------ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. See License.txt in the project root for license information. | ||
| // ------------------------------------------------------------------------------------------------ | ||
|
|
||
| namespace Microsoft.Integration.Shopify; | ||
|
|
||
| using Microsoft.Finance.GeneralLedger.Journal; | ||
| using Microsoft.Finance.GeneralLedger.Posting; | ||
| using Microsoft.Sales.History; | ||
|
|
||
| /// <summary> | ||
| /// Codeunit Shpfy Auto Post Transactions (ID 30236). | ||
| /// Automatically posts Shopify order and refund payment transactions as general journal lines when | ||
| /// the related sales invoice or credit memo is posted, provided 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 skipped record and never blocks or reverses the document posting. | ||
| /// </summary> | ||
| codeunit 30236 "Shpfy Auto Post Transactions" | ||
| { | ||
| Access = Internal; | ||
|
|
||
| internal procedure AutoPostTransactions(SalesInvoiceHeaderNo: Code[20]; SalesCrMemoHeaderNo: Code[20]) | ||
|
onbuyuka marked this conversation as resolved.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The new automatic-posting pipeline in Knowledge: 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.35.4 |
||
| begin | ||
| if SalesInvoiceHeaderNo <> '' then | ||
| PostOrderTransactions(SalesInvoiceHeaderNo); | ||
| if SalesCrMemoHeaderNo <> '' then | ||
| PostRefundTransactions(SalesCrMemoHeaderNo); | ||
| end; | ||
|
|
||
| local procedure PostOrderTransactions(SalesInvoiceHeaderNo: Code[20]) | ||
| var | ||
| SalesInvoiceHeader: Record "Sales Invoice Header"; | ||
| OrderTransaction: Record "Shpfy Order Transaction"; | ||
| begin | ||
| if not SalesInvoiceHeader.Get(SalesInvoiceHeaderNo) then | ||
| exit; | ||
| if SalesInvoiceHeader."Shpfy Order Id" = 0 then | ||
| exit; | ||
|
|
||
| OrderTransaction.SetRange("Shopify Order Id", SalesInvoiceHeader."Shpfy Order Id"); | ||
| OrderTransaction.SetFilter(Type, '%1|%2', OrderTransaction.Type::Capture, OrderTransaction.Type::Sale); | ||
|
onbuyuka marked this conversation as resolved.
|
||
| PostTransactions(OrderTransaction, SalesInvoiceHeader."Posting Date"); | ||
| end; | ||
|
|
||
| local procedure PostRefundTransactions(SalesCrMemoHeaderNo: Code[20]) | ||
| var | ||
| SalesCrMemoHeader: Record "Sales Cr.Memo Header"; | ||
| OrderTransaction: Record "Shpfy Order Transaction"; | ||
| begin | ||
| if not SalesCrMemoHeader.Get(SalesCrMemoHeaderNo) then | ||
| exit; | ||
| if SalesCrMemoHeader."Shpfy Refund Id" = 0 then | ||
| exit; | ||
|
|
||
| OrderTransaction.SetRange("Refund Id", SalesCrMemoHeader."Shpfy Refund Id"); | ||
| OrderTransaction.SetRange(Type, OrderTransaction.Type::Refund); | ||
| PostTransactions(OrderTransaction, SalesCrMemoHeader."Posting Date"); | ||
| end; | ||
|
|
||
| local procedure PostTransactions(var OrderTransaction: Record "Shpfy Order Transaction"; PostingDate: Date) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Knowledge: 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.35.4 |
||
| var | ||
| PaymentMethodMapping: Record "Shpfy Payment Method Mapping"; | ||
| BoundaryCommitted: Boolean; | ||
| begin | ||
| OrderTransaction.SetRange(Status, OrderTransaction.Status::Success); | ||
|
onbuyuka marked this conversation as resolved.
|
||
| OrderTransaction.SetRange(Used, false); | ||
| if not OrderTransaction.FindSet() then | ||
| exit; | ||
|
|
||
| repeat | ||
|
onbuyuka marked this conversation as resolved.
|
||
| if GetAutoPostMapping(OrderTransaction, PaymentMethodMapping) then begin | ||
| if not BoundaryCommitted then begin | ||
| // The posted document has already been committed by Sales-Post at this point. | ||
| // Commit again to establish a rollback boundary before best-effort payment posting, | ||
| // so a posting failure only rolls back to here and preserves the posted document and | ||
| // its Shopify document links. | ||
| Commit(); | ||
| BoundaryCommitted := true; | ||
| end; | ||
| PostTransaction(OrderTransaction, PaymentMethodMapping, PostingDate); | ||
| end; | ||
| until OrderTransaction.Next() = 0; | ||
| end; | ||
|
|
||
| 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 | ||
|
onbuyuka marked this conversation as resolved.
Outdated
|
||
| exit(false); | ||
| if not PaymentMethodMapping."Post Automatically" then | ||
| exit(false); | ||
| exit((PaymentMethodMapping."Auto-Post Jnl. Template" <> '') and (PaymentMethodMapping."Auto-Post Jnl. Batch" <> '')); | ||
| end; | ||
|
|
||
| local procedure PostTransaction(OrderTransaction: Record "Shpfy Order Transaction"; PaymentMethodMapping: Record "Shpfy Payment Method Mapping"; PostingDate: Date) | ||
| var | ||
| GenJournalLine: Record "Gen. Journal Line"; | ||
| GenJnlPostBatch: Codeunit "Gen. Jnl.-Post Batch"; | ||
| AutoGenJnlPost: Codeunit "Shpfy Auto Gen. Jnl.-Post"; | ||
| TemplateName: Code[10]; | ||
| BatchName: Code[10]; | ||
| ErrorText: Text; | ||
| begin | ||
| TemplateName := PaymentMethodMapping."Auto-Post Jnl. Template"; | ||
| BatchName := PaymentMethodMapping."Auto-Post Jnl. Batch"; | ||
|
|
||
| // Guard against lines left behind by a previously interrupted attempt for this transaction. | ||
| RemoveJournalLines(TemplateName, BatchName, OrderTransaction."Shopify Transaction Id"); | ||
|
|
||
| AutoGenJnlPost.SetParameters(PaymentMethodMapping, PostingDate); | ||
| BindSubscription(AutoGenJnlPost); | ||
|
|
||
| // Build the journal line(s) through Codeunit.Run so a failure while building is trapped and rolled | ||
| // back to the boundary commit instead of blocking the document posting. | ||
| if not AutoGenJnlPost.Run(OrderTransaction) then begin | ||
|
onbuyuka marked this conversation as resolved.
Outdated
|
||
| ErrorText := GetLastErrorText(); | ||
| UnbindSubscription(AutoGenJnlPost); | ||
| LogFailureAndCommit(OrderTransaction, ErrorText); | ||
| exit; | ||
| end; | ||
|
|
||
| GenJournalLine.SetRange("Journal Template Name", TemplateName); | ||
| GenJournalLine.SetRange("Journal Batch Name", BatchName); | ||
| GenJournalLine.SetRange("Shpfy Transaction Id", OrderTransaction."Shopify Transaction Id"); | ||
| if GenJournalLine.IsEmpty() then begin | ||
| UnbindSubscription(AutoGenJnlPost); | ||
| exit; | ||
| end; | ||
| GenJournalLine.SetRange("Shpfy Transaction Id"); | ||
| GenJournalLine.FindSet(); | ||
|
|
||
| // Commit the built line(s) so the batch posting - which commits internally - runs in a valid | ||
| // transaction context (matching the platform's own SendToPosting behavior). | ||
| Commit(); | ||
|
onbuyuka marked this conversation as resolved.
Outdated
|
||
|
|
||
| if GenJnlPostBatch.Run(GenJournalLine) then begin | ||
| UnbindSubscription(AutoGenJnlPost); | ||
| exit; | ||
| end; | ||
| ErrorText := GetLastErrorText(); | ||
| UnbindSubscription(AutoGenJnlPost); | ||
|
|
||
| // Posting failed after the line(s) were committed: remove them and log the failure without | ||
| // blocking or reversing the document posting. | ||
| RemoveJournalLines(TemplateName, BatchName, OrderTransaction."Shopify Transaction Id"); | ||
| LogFailureAndCommit(OrderTransaction, ErrorText); | ||
| end; | ||
|
|
||
| local procedure RemoveJournalLines(TemplateName: Code[10]; BatchName: Code[10]; ShopifyTransactionId: BigInteger) | ||
| var | ||
| GenJournalLine: Record "Gen. Journal Line"; | ||
| begin | ||
| GenJournalLine.SetRange("Journal Template Name", TemplateName); | ||
| GenJournalLine.SetRange("Journal Batch Name", BatchName); | ||
| GenJournalLine.SetRange("Shpfy Transaction Id", ShopifyTransactionId); | ||
| if not GenJournalLine.IsEmpty() then | ||
| GenJournalLine.DeleteAll(true); | ||
| end; | ||
|
|
||
| local procedure LogFailureAndCommit(OrderTransaction: Record "Shpfy Order Transaction"; ErrorText: Text) | ||
| var | ||
| Shop: Record "Shpfy Shop"; | ||
| SkippedRecord: Codeunit "Shpfy Skipped Record"; | ||
| begin | ||
| if Shop.Get(OrderTransaction.Shop) then | ||
| SkippedRecord.LogSkippedRecord(OrderTransaction."Shopify Transaction Id", OrderTransaction.RecordId, CopyStr(ErrorText, 1, 250), Shop); | ||
| // Commit the log (and any cleanup) so a subsequent transaction's rollback cannot discard it. | ||
| Commit(); | ||
| end; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| // ------------------------------------------------------------------------------------------------ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. See License.txt in the project root for license information. | ||
| // ------------------------------------------------------------------------------------------------ | ||
|
|
||
| namespace Microsoft.Integration.Shopify; | ||
|
|
||
| /// <summary> | ||
| /// Page Shpfy Filter Transactions (ID 30176). | ||
| /// Request dialog used to filter the Shopify transactions list down to the transactions that are | ||
| /// ready to be posted, optionally narrowed by payment gateway and creation date range. | ||
| /// </summary> | ||
| page 30176 "Shpfy Filter Transactions" | ||
| { | ||
| Caption = 'Filter Postable Transactions'; | ||
| PageType = StandardDialog; | ||
| ApplicationArea = All; | ||
|
onbuyuka marked this conversation as resolved.
|
||
|
|
||
| layout | ||
| { | ||
| area(Content) | ||
| { | ||
| field(Gateway; Gateway) | ||
| { | ||
| Caption = 'Gateway'; | ||
| ToolTip = 'Specifies the transaction gateway to filter transactions by. Leave blank to include all gateways set up for automatic posting.'; | ||
| Editable = false; | ||
|
|
||
| trigger OnAssistEdit() | ||
| var | ||
| PaymentMethodMapping: Record "Shpfy Payment Method Mapping"; | ||
| begin | ||
| PaymentMethodMapping.SetRange("Post Automatically", true); | ||
| if Page.RunModal(0, PaymentMethodMapping) = Action::LookupOK then begin | ||
|
onbuyuka marked this conversation as resolved.
Outdated
|
||
| ShopCode := PaymentMethodMapping."Shop Code"; | ||
| Gateway := PaymentMethodMapping.Gateway; | ||
| CreditCardCompany := PaymentMethodMapping."Credit Card Company"; | ||
| end; | ||
| end; | ||
| } | ||
| field(StartDate; StartDate) | ||
| { | ||
| Caption = 'Start Date'; | ||
| ToolTip = 'Specifies the earliest transaction creation date to include in the filter. Leave blank to include all transactions from the beginning.'; | ||
| } | ||
| field(EndDate; EndDate) | ||
| { | ||
| Caption = 'End Date'; | ||
| ToolTip = 'Specifies the latest transaction creation date to include in the filter. Leave blank to include all transactions.'; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| var | ||
| ShopCode: Code[20]; | ||
| Gateway: Text[30]; | ||
| CreditCardCompany: Text[50]; | ||
| StartDate: Date; | ||
| EndDate: Date; | ||
|
|
||
| internal procedure GetParameters(var NewShopCode: Code[20]; var NewGateway: Text[30]; var NewCreditCardCompany: Text[50]; var NewStartDate: DateTime; var NewEndDate: DateTime) | ||
| begin | ||
| NewShopCode := ShopCode; | ||
| NewGateway := Gateway; | ||
| NewCreditCardCompany := CreditCardCompany; | ||
| NewStartDate := CreateDateTime(StartDate, 0T); | ||
| NewEndDate := CreateDateTime(EndDate, 0T); | ||
| end; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.