diff --git a/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLPaymTransByIds.Codeunit.al b/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLPaymTransByIds.Codeunit.al new file mode 100644 index 0000000000..a2c503791f --- /dev/null +++ b/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLPaymTransByIds.Codeunit.al @@ -0,0 +1,21 @@ +// ------------------------------------------------------------------------------------------------ +// 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; + +codeunit 30421 "Shpfy GQL PaymTransByIds" implements "Shpfy IGraphQL" +{ + Access = Internal; + + procedure GetGraphQL(): Text + begin + exit('{"query":"{ shopifyPaymentsAccount { balanceTransactions(first: 200, query: \"id:{{IdFilter}}\") { nodes { id associatedPayout { id } } } } }"}'); + end; + + procedure GetExpectedCost(): Integer + begin + exit(23); + end; +} \ No newline at end of file diff --git a/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLPayoutsByIds.Codeunit.al b/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLPayoutsByIds.Codeunit.al new file mode 100644 index 0000000000..d829b71336 --- /dev/null +++ b/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLPayoutsByIds.Codeunit.al @@ -0,0 +1,21 @@ +// ------------------------------------------------------------------------------------------------ +// 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; + +codeunit 30420 "Shpfy GQL PayoutsByIds" implements "Shpfy IGraphQL" +{ + Access = Internal; + + procedure GetGraphQL(): Text + begin + exit('{"query":"{ shopifyPaymentsAccount { payouts(first: 200, query: \"id:{{IdFilter}}\") { nodes { id status } } } }"}'); + end; + + procedure GetExpectedCost(): Integer + begin + exit(13); + end; +} diff --git a/src/Apps/W1/Shopify/App/src/GraphQL/Enums/ShpfyGraphQLType.Enum.al b/src/Apps/W1/Shopify/App/src/GraphQL/Enums/ShpfyGraphQLType.Enum.al index 42c4bcffbe..f682cdf682 100644 --- a/src/Apps/W1/Shopify/App/src/GraphQL/Enums/ShpfyGraphQLType.Enum.al +++ b/src/Apps/W1/Shopify/App/src/GraphQL/Enums/ShpfyGraphQLType.Enum.al @@ -725,4 +725,15 @@ enum 30111 "Shpfy GraphQL Type" implements "Shpfy IGraphQL" Caption = 'Set Variant Image'; Implementation = "Shpfy IGraphQL" = "Shpfy GQL SetVariantImage"; } + + value(145; GetPaymTransByIds) + { + Caption = 'Get Payment Transactions By Ids'; + Implementation = "Shpfy IGraphQL" = "Shpfy GQL PaymTransByIds"; + } + value(146; GetPayoutsByIds) + { + Caption = 'Get Payouts By Ids'; + Implementation = "Shpfy IGraphQL" = "Shpfy GQL PayoutsByIds"; + } } diff --git a/src/Apps/W1/Shopify/App/src/Payments/Codeunits/ShpfyPayments.Codeunit.al b/src/Apps/W1/Shopify/App/src/Payments/Codeunits/ShpfyPayments.Codeunit.al index 6dac5a19ee..7f959dd88b 100644 --- a/src/Apps/W1/Shopify/App/src/Payments/Codeunits/ShpfyPayments.Codeunit.al +++ b/src/Apps/W1/Shopify/App/src/Payments/Codeunits/ShpfyPayments.Codeunit.al @@ -22,16 +22,86 @@ codeunit 30169 "Shpfy Payments" PaymentsAPI.SetShop(Shop); end; - internal procedure SyncPaymentTransactions() + #region Payouts + internal procedure SyncPayouts() + begin + UpdatePaymentTransactionPayoutIds(); + UpdatePendingPayouts(); + ImportNewPaymentTransactions(); + ImportNewPayouts(); + end; + + local procedure ImportNewPaymentTransactions() var + PaymentTransaction: Record "Shpfy Payment Transaction"; SinceId: BigInteger; begin - SinceId := GetLastTransactionPayoutId(Shop.Code); + PaymentTransaction.SetRange("Shop Code", Shop.Code); + if PaymentTransaction.FindLast() then + SinceId := PaymentTransaction.Id; PaymentsAPI.ImportPaymentTransactions(SinceId); - if SinceId > 0 then - ImportPayouts(SinceId - 1); end; + local procedure ImportNewPayouts() + var + Payout: Record "Shpfy Payout"; + SinceId: BigInteger; + begin + if Payout.FindLast() then + SinceId := Payout.Id; + PaymentsAPI.ImportPayouts(SinceId); + end; + + local procedure UpdatePaymentTransactionPayoutIds() + var + PaymentTransaction: Record "Shpfy Payment Transaction"; + PaymentTransactionIdFilter: Text; + PaymentTransactionCount: Integer; + begin + PaymentTransaction.SetRange("Payout Id", 0); + if PaymentTransaction.FindSet() then + repeat + if PaymentTransactionIdFilter <> '' then + PaymentTransactionIdFilter += ' OR '; + PaymentTransactionIdFilter += Format(PaymentTransaction.Id); + PaymentTransactionCount += 1; + if PaymentTransactionCount = 200 then begin + PaymentsAPI.UpdatePaymentTransactionPayoutIds(PaymentTransactionIdFilter); + PaymentTransactionIdFilter := ''; + PaymentTransactionCount := 0; + end; + until PaymentTransaction.Next() = 0; + + if PaymentTransactionIdFilter <> '' then + PaymentsAPI.UpdatePaymentTransactionPayoutIds(PaymentTransactionIdFilter); + end; + + local procedure UpdatePendingPayouts() + var + Payout: Record "Shpfy Payout"; + PayoutIdFilter: Text; + PayoutCount: Integer; + begin + Payout.SetFilter(Status, '<>%1&<>%2', "Shpfy Payout Status"::Paid, "Shpfy Payout Status"::Canceled); + if Payout.FindSet() then + repeat + if PayoutIdFilter <> '' then + PayoutIdFilter += ' OR '; + PayoutIdFilter += Format(Payout.Id); + PayoutCount += 1; + if PayoutCount = 200 then begin + PaymentsAPI.UpdatePayoutStatuses(PayoutIdFilter); + PayoutIdFilter := ''; + PayoutCount := 0; + end; + until Payout.Next() = 0; + + if PayoutIdFilter <> '' then + PaymentsAPI.UpdatePayoutStatuses(PayoutIdFilter); + end; + #endregion + + #region Disputes internal procedure SyncDisputes() begin UpdateUnfinishedDisputes(); @@ -58,26 +128,5 @@ codeunit 30169 "Shpfy Payments" SinceId := Dispute.Id; PaymentsAPI.ImportDisputes(SinceId); end; - - local procedure ImportPayouts(SinceId: BigInteger) - var - Payout: Record "Shpfy Payout"; - Math: Codeunit "Shpfy Math"; - begin - Payout.SetFilter(Status, '<>%1&<>%2', "Shpfy Payout Status"::Paid, "Shpfy Payout Status"::Canceled); - Payout.SetLoadFields(Id); - if Payout.FindFirst() then - SinceId := Math.Min(SinceId, Payout.Id); - - PaymentsAPI.ImportPayouts(SinceId); - end; - - local procedure GetLastTransactionPayoutId(ShopCode: Code[20]): BigInteger - var - PaymentTransaction: Record "Shpfy Payment Transaction"; - begin - PaymentTransaction.SetRange("Shop Code", ShopCode); - if PaymentTransaction.FindLast() then - exit(PaymentTransaction."Payout Id"); - end; + #endregion } \ No newline at end of file diff --git a/src/Apps/W1/Shopify/App/src/Payments/Codeunits/ShpfyPaymentsAPI.Codeunit.al b/src/Apps/W1/Shopify/App/src/Payments/Codeunits/ShpfyPaymentsAPI.Codeunit.al index 6fe45004b3..f62bd293e2 100644 --- a/src/Apps/W1/Shopify/App/src/Payments/Codeunits/ShpfyPaymentsAPI.Codeunit.al +++ b/src/Apps/W1/Shopify/App/src/Payments/Codeunits/ShpfyPaymentsAPI.Codeunit.al @@ -17,7 +17,8 @@ codeunit 30385 "Shpfy Payments API" CommunicationMgt: Codeunit "Shpfy Communication Mgt."; JsonHelper: Codeunit "Shpfy Json Helper"; - internal procedure ImportPaymentTransactions(var SinceId: BigInteger) + #region Payouts + internal procedure ImportPaymentTransactions(SinceId: BigInteger) var GraphQLType: Enum "Shpfy GraphQL Type"; JTransactions: JsonArray; @@ -30,7 +31,6 @@ codeunit 30385 "Shpfy Payments API" begin GraphQLType := GraphQLType::GetPaymentTransactions; Parameters.Add('SinceId', Format(SinceId)); - Clear(SinceId); repeat JResponse := CommunicationMgt.ExecuteGraphQL(GraphQLType, Parameters); if JsonHelper.GetJsonObject(JResponse, JPaymentsAccount, 'data.shopifyPaymentsAccount') then @@ -38,7 +38,7 @@ codeunit 30385 "Shpfy Payments API" foreach JItem in JTransactions do begin Cursor := JsonHelper.GetValueAsText(JItem.AsObject(), 'cursor'); if JsonHelper.GetJsonObject(JItem.AsObject(), JNode, 'node') then - ImportPaymentTransaction(JNode, SinceId); + ImportPaymentTransaction(JNode); end; if Parameters.ContainsKey('After') then Parameters.Set('After', Cursor) @@ -49,19 +49,18 @@ codeunit 30385 "Shpfy Payments API" until not JsonHelper.GetValueAsBoolean(JResponse, 'data.shopifyPaymentsAccount.balanceTransactions.pageInfo.hasNextPage'); end; - internal procedure ImportPaymentTransaction(JTransaction: JsonObject; var SinceId: BigInteger) + internal procedure ImportPaymentTransaction(JTransaction: JsonObject) var DataCapture: Record "Shpfy Data Capture"; PaymentTransaction: Record "Shpfy Payment Transaction"; - Math: Codeunit "Shpfy Math"; RecordRef: RecordRef; Id: BigInteger; - PayoutId: BigInteger; begin Id := CommunicationMgt.GetIdOfGId(JsonHelper.GetValueAsText(JTransaction, 'id')); - Clear(PaymentTransaction); - PaymentTransaction.SetRange(Id, Id); - if PaymentTransaction.IsEmpty then begin + if PaymentTransaction.Get(Id) then begin + PaymentTransaction."Payout Id" := CommunicationMgt.GetIdOfGId(JsonHelper.GetValueAsText(JTransaction, 'associatedPayout.id')); + PaymentTransaction.Modify(); + end else begin RecordRef.Open(Database::"Shpfy Payment Transaction"); RecordRef.Init(); JsonHelper.GetValueIntoField(JTransaction, 'test', RecordRef, PaymentTransaction.FieldNo(Test)); @@ -82,20 +81,6 @@ codeunit 30385 "Shpfy Payments API" PaymentTransaction."Payout Id" := CommunicationMgt.GetIdOfGId(JsonHelper.GetValueAsText(JTransaction, 'associatedPayout.id')); PaymentTransaction.Insert(); DataCapture.Add(Database::"Shpfy Payment Transaction", PaymentTransaction.SystemId, JTransaction); - if SinceId = 0 then - SinceId := PaymentTransaction."Payout Id" - else - if PaymentTransaction."Payout Id" > 0 then - SinceId := Math.Min(SinceId, PaymentTransaction."Payout Id"); - end else begin - PaymentTransaction.Get(Id); - if PaymentTransaction."Payout Id" = 0 then begin - PayoutId := CommunicationMgt.GetIdOfGId(JsonHelper.GetValueAsText(JTransaction, 'associatedPayout.id')); - if PayoutId <> 0 then begin - PaymentTransaction."Payout Id" := PayoutId; - PaymentTransaction.Modify(); - end; - end; end; end; @@ -166,6 +151,79 @@ codeunit 30385 "Shpfy Payments API" DataCapture.Add(Database::"Shpfy Payout", Payout.SystemId, JPayout); end; + internal procedure UpdatePaymentTransactionPayoutIds(IdFilter: Text) + var + PaymentTransaction: Record "Shpfy Payment Transaction"; + GraphQLType: Enum "Shpfy GraphQL Type"; + JTransactions: JsonArray; + JPaymentsAccount: JsonObject; + JNode: JsonToken; + JResponse: JsonToken; + Parameters: Dictionary of [Text, Text]; + Id: BigInteger; + PayoutId: BigInteger; + begin + GraphQLType := GraphQLType::GetPaymTransByIds; + Parameters.Add('IdFilter', IdFilter); + JResponse := CommunicationMgt.ExecuteGraphQL(GraphQLType, Parameters); + if JsonHelper.GetJsonObject(JResponse, JPaymentsAccount, 'data.shopifyPaymentsAccount') then + if JsonHelper.GetJsonArray(JResponse, JTransactions, 'data.shopifyPaymentsAccount.balanceTransactions.nodes') then + foreach JNode in JTransactions do begin + Id := CommunicationMgt.GetIdOfGId(JsonHelper.GetValueAsText(JNode.AsObject(), 'id')); + PayoutId := CommunicationMgt.GetIdOfGId(JsonHelper.GetValueAsText(JNode.AsObject(), 'associatedPayout.id')); + if PaymentTransaction.Get(Id) then + if PaymentTransaction."Payout Id" <> PayoutId then begin + PaymentTransaction."Payout Id" := PayoutId; + PaymentTransaction.Modify(); + end; + end; + end; + + internal procedure UpdatePayoutStatuses(IdFilter: Text) + var + Payout: Record "Shpfy Payout"; + GraphQLType: Enum "Shpfy GraphQL Type"; + JPayouts: JsonArray; + JPaymentsAccount: JsonObject; + JNode: JsonToken; + JResponse: JsonToken; + Parameters: Dictionary of [Text, Text]; + Id: BigInteger; + begin + GraphQLType := GraphQLType::GetPayoutsByIds; + Parameters.Add('IdFilter', IdFilter); + JResponse := CommunicationMgt.ExecuteGraphQL(GraphQLType, Parameters); + if JsonHelper.GetJsonObject(JResponse, JPaymentsAccount, 'data.shopifyPaymentsAccount') then + if JsonHelper.GetJsonArray(JResponse, JPayouts, 'data.shopifyPaymentsAccount.payouts.nodes') then + foreach JNode in JPayouts do begin + Id := CommunicationMgt.GetIdOfGId(JsonHelper.GetValueAsText(JNode.AsObject(), 'id')); + if Payout.Get(Id) then begin + Payout.Status := ConvertToPayoutStatus(JsonHelper.GetValueAsText(JNode.AsObject(), 'status')); + Payout.Modify(); + end; + end; + end; + + local procedure ConvertToPayoutStatus(Value: Text): Enum "Shpfy Payout Status" + begin + Value := CommunicationMgt.ConvertToCleanOptionValue(Value); + if Enum::"Shpfy Payout Status".Names().Contains(Value) then + exit(Enum::"Shpfy Payout Status".FromInteger(Enum::"Shpfy Payout Status".Ordinals().Get(Enum::"Shpfy Payout Status".Names().IndexOf(Value)))) + else + exit(Enum::"Shpfy Payout Status"::Unknown); + end; + + local procedure ConvertToPaymentTranscationType(Value: Text): Enum "Shpfy Payment Trans. Type" + begin + Value := CommunicationMgt.ConvertToCleanOptionValue(Value); + if Enum::"Shpfy Payment Trans. Type".Names().Contains(Value) then + exit(Enum::"Shpfy Payment Trans. Type".FromInteger(Enum::"Shpfy Payment Trans. Type".Ordinals().Get(Enum::"Shpfy Payment Trans. Type".Names().IndexOf(Value)))) + else + exit(Enum::"Shpfy Payment Trans. Type"::Unknown); + end; + #endregion + + #region Disputes internal procedure ImportDisputes(SinceId: BigInteger) var GraphQLType: Enum "Shpfy GraphQL Type"; @@ -251,30 +309,6 @@ codeunit 30385 "Shpfy Payments API" end; end; - internal procedure SetShop(ShopifyShop: Record "Shpfy Shop") - begin - Shop := ShopifyShop; - CommunicationMgt.SetShop(Shop); - end; - - local procedure ConvertToPayoutStatus(Value: Text): Enum "Shpfy Payout Status" - begin - Value := CommunicationMgt.ConvertToCleanOptionValue(Value); - if Enum::"Shpfy Payout Status".Names().Contains(Value) then - exit(Enum::"Shpfy Payout Status".FromInteger(Enum::"Shpfy Payout Status".Ordinals().Get(Enum::"Shpfy Payout Status".Names().IndexOf(Value)))) - else - exit(Enum::"Shpfy Payout Status"::Unknown); - end; - - local procedure ConvertToPaymentTranscationType(Value: Text): Enum "Shpfy Payment Trans. Type" - begin - Value := CommunicationMgt.ConvertToCleanOptionValue(Value); - if Enum::"Shpfy Payment Trans. Type".Names().Contains(Value) then - exit(Enum::"Shpfy Payment Trans. Type".FromInteger(Enum::"Shpfy Payment Trans. Type".Ordinals().Get(Enum::"Shpfy Payment Trans. Type".Names().IndexOf(Value)))) - else - exit(Enum::"Shpfy Payment Trans. Type"::Unknown); - end; - local procedure ConvertToDisputeStatus(Value: Text): Enum "Shpfy Dispute Status" begin Value := CommunicationMgt.ConvertToCleanOptionValue(Value); @@ -301,4 +335,11 @@ codeunit 30385 "Shpfy Payments API" else exit(Enum::"Shpfy Dispute Reason"::Unknown); end; + #endregion + + internal procedure SetShop(ShopifyShop: Record "Shpfy Shop") + begin + Shop := ShopifyShop; + CommunicationMgt.SetShop(Shop); + end; } \ No newline at end of file diff --git a/src/Apps/W1/Shopify/App/src/Payments/Reports/ShpfySyncPayments.Report.al b/src/Apps/W1/Shopify/App/src/Payments/Reports/ShpfySyncPayments.Report.al index 74e91b3052..dfe4f15fe5 100644 --- a/src/Apps/W1/Shopify/App/src/Payments/Reports/ShpfySyncPayments.Report.al +++ b/src/Apps/W1/Shopify/App/src/Payments/Reports/ShpfySyncPayments.Report.al @@ -26,7 +26,7 @@ report 30105 "Shpfy Sync Payments" Payments: Codeunit "Shpfy Payments"; begin Payments.SetShop(Shop); - Payments.SyncPaymentTransactions(); + Payments.SyncPayouts(); end; } } diff --git a/src/Apps/W1/Shopify/App/src/PermissionSets/ShpfyObjects.PermissionSet.al b/src/Apps/W1/Shopify/App/src/PermissionSets/ShpfyObjects.PermissionSet.al index a95327891b..92928afb81 100644 --- a/src/Apps/W1/Shopify/App/src/PermissionSets/ShpfyObjects.PermissionSet.al +++ b/src/Apps/W1/Shopify/App/src/PermissionSets/ShpfyObjects.PermissionSet.al @@ -267,6 +267,8 @@ permissionset 30104 "Shpfy - Objects" codeunit "Shpfy GQL Payment Terms" = X, codeunit "Shpfy GQL PaymentTransactions" = X, codeunit "Shpfy GQL Payouts" = X, + codeunit "Shpfy GQL PayoutsByIds" = X, + codeunit "Shpfy GQL PaymTransByIds" = X, codeunit "Shpfy GQL ProductById" = X, codeunit "Shpfy GQL ProductIds" = X, codeunit "Shpfy GQL ProductImages" = X, diff --git a/src/Apps/W1/Shopify/Test/Payments/ShpfyPaymentsTest.Codeunit.al b/src/Apps/W1/Shopify/Test/Payments/ShpfyPaymentsTest.Codeunit.al index 3bfc054a93..3e5271e343 100644 --- a/src/Apps/W1/Shopify/Test/Payments/ShpfyPaymentsTest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Payments/ShpfyPaymentsTest.Codeunit.al @@ -23,7 +23,6 @@ codeunit 139566 "Shpfy Payments Test" PaymentTransaction: Record "Shpfy Payment Transaction"; PaymentsAPI: Codeunit "Shpfy Payments API"; Id: BigInteger; - LastPayoutId: BigInteger; JPayment: JsonObject; begin // [SCENARIO] Extract the data out json token that contains a payment info into the "Shpfy Payment Transaction" record. @@ -31,8 +30,8 @@ codeunit 139566 "Shpfy Payments Test" Id := Any.IntegerInRange(10000, 99999); JPayment := GetRandomPayment(Id); - // [WHEN] Invoke the function ImportPaymentTransaction(JPayment, LastPayoutId) - PaymentsAPI.ImportPaymentTransaction(JPayment, LastPayoutId); + // [WHEN] Invoke the function ImportPaymentTransaction(JPayment) + PaymentsAPI.ImportPaymentTransaction(JPayment); // [THEN] We must find the "Shpfy Payment" record with the same id LibraryAssert.IsTrue(PaymentTransaction.Get(Id), 'Get "Shpfy Payment Transaction" record');