diff --git a/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLNextOpenOrdToImport.Codeunit.al b/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLNextOpenOrdToImport.Codeunit.al index 8893537462..fe438dbc87 100644 --- a/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLNextOpenOrdToImport.Codeunit.al +++ b/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLNextOpenOrdToImport.Codeunit.al @@ -11,11 +11,11 @@ codeunit 30206 "Shpfy GQL NextOpenOrdToImport" implements "Shpfy IGraphQL" internal procedure GetGraphQL(): Text begin - exit('{"query": "{orders(first:150, after:\"{{After}}\", query: \"status:open updated_at:>''{{Time}}''\"){pageInfo{hasNextPage} edges{cursor node{legacyResourceId name createdAt updatedAt channel { name } test fullyPaid unpaid displayFinancialStatus displayFulfillmentStatus subtotalLineItemsQuantity totalPriceSet{shopMoney{amount currencyCode}} customAttributes{key value} tags risk { assessments { riskLevel }} displayAddress { countryCodeV2 } shippingAddress { countryCodeV2 } billingAddress { countryCodeV2 } totalTaxSet { presentmentMoney { amount } shopMoney { amount } } purchasingEntity { ... on PurchasingCompany { company { id }}}}}}}"}'); + exit('{"query": "{orders(first:150, after:\"{{After}}\", query: \"status:open updated_at:>''{{Time}}''\"){pageInfo{hasNextPage} edges{cursor node{legacyResourceId name createdAt updatedAt channel { name } test fullyPaid unpaid displayFinancialStatus displayFulfillmentStatus subtotalLineItemsQuantity totalPriceSet{shopMoney{amount currencyCode}} customAttributes{key value} tags risk { assessments { riskLevel }} displayAddress { countryCodeV2 } shippingAddress { countryCodeV2 } billingAddress { countryCodeV2 } totalTaxSet { presentmentMoney { amount } shopMoney { amount } } taxLines { channelLiable } purchasingEntity { ... on PurchasingCompany { company { id }}}}}}}"}'); end; internal procedure GetExpectedCost(): Integer begin - exit(602); + exit(612); end; } \ No newline at end of file diff --git a/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLNextOrdersToImport.Codeunit.al b/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLNextOrdersToImport.Codeunit.al index 45b8bcd602..cb317446e6 100644 --- a/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLNextOrdersToImport.Codeunit.al +++ b/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLNextOrdersToImport.Codeunit.al @@ -18,7 +18,7 @@ codeunit 30138 "Shpfy GQL NextOrdersToImport" implements "Shpfy IGraphQL" /// Return value of type Text. internal procedure GetGraphQL(): Text begin - exit('{"query": "{orders(first:25, after:\"{{After}}\", query: \"updated_at:>''{{Time}}''\"){ pageInfo { hasNextPage } edges { cursor node { legacyResourceId name createdAt updatedAt channel { name } test fullyPaid unpaid closed displayFinancialStatus displayFulfillmentStatus subtotalLineItemsQuantity totalPriceSet { shopMoney { amount currencyCode } } customAttributes { key value } tags risk { assessments { riskLevel }} displayAddress { countryCodeV2 } shippingAddress { countryCodeV2 } billingAddress { countryCodeV2 } totalTaxSet { presentmentMoney { amount } shopMoney { amount } } purchasingEntity { ... on PurchasingCompany { company { id }}}}}}}"}'); + exit('{"query": "{orders(first:25, after:\"{{After}}\", query: \"updated_at:>''{{Time}}''\"){ pageInfo { hasNextPage } edges { cursor node { legacyResourceId name createdAt updatedAt channel { name } test fullyPaid unpaid closed displayFinancialStatus displayFulfillmentStatus subtotalLineItemsQuantity totalPriceSet { shopMoney { amount currencyCode } } customAttributes { key value } tags risk { assessments { riskLevel }} displayAddress { countryCodeV2 } shippingAddress { countryCodeV2 } billingAddress { countryCodeV2 } totalTaxSet { presentmentMoney { amount } shopMoney { amount } } taxLines { channelLiable } purchasingEntity { ... on PurchasingCompany { company { id }}}}}}}"}'); end; /// @@ -27,6 +27,6 @@ codeunit 30138 "Shpfy GQL NextOrdersToImport" implements "Shpfy IGraphQL" /// Return value of type Integer. internal procedure GetExpectedCost(): Integer begin - exit(153); + exit(159); end; } diff --git a/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLNextShipmentLines.Codeunit.al b/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLNextShipmentLines.Codeunit.al index c211d05f3a..d087cce91c 100644 --- a/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLNextShipmentLines.Codeunit.al +++ b/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLNextShipmentLines.Codeunit.al @@ -9,7 +9,7 @@ codeunit 30222 "Shpfy GQL NextShipmentLines" implements "Shpfy IGraphQL" { internal procedure GetGraphQL(): Text begin - exit('{"query":"{order(id: \"gid://shopify/Order/{{OrderId}}\") {shippingLines(first: 10, after:\"{{After}}\") { pageInfo { endCursor hasNextPage } nodes { id title code source discountAllocations { allocatedAmountSet { presentmentMoney { amount } shopMoney { amount }}} originalPriceSet { presentmentMoney { amount } shopMoney { amount }} discountedPriceSet { presentmentMoney { amount } shopMoney { amount }} taxLines { title rate ratePercentage priceSet { presentmentMoney { amount } shopMoney {amount}}}}}}}"}'); + exit('{"query":"{order(id: \"gid://shopify/Order/{{OrderId}}\") {shippingLines(first: 10, after:\"{{After}}\") { pageInfo { endCursor hasNextPage } nodes { id title code source discountAllocations { allocatedAmountSet { presentmentMoney { amount } shopMoney { amount }}} originalPriceSet { presentmentMoney { amount } shopMoney { amount }} discountedPriceSet { presentmentMoney { amount } shopMoney { amount }} taxLines { channelLiable title rate ratePercentage priceSet { presentmentMoney { amount } shopMoney {amount}}}}}}}"}'); end; internal procedure GetExpectedCost(): Integer diff --git a/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLOpenOrdersToImport.Codeunit.al b/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLOpenOrdersToImport.Codeunit.al index ff416e7281..faaa23f657 100644 --- a/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLOpenOrdersToImport.Codeunit.al +++ b/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLOpenOrdersToImport.Codeunit.al @@ -11,11 +11,11 @@ codeunit 30205 "Shpfy GQL OpenOrdersToImport" implements "Shpfy IGraphQL" internal procedure GetGraphQL(): Text begin - exit('{"query": "{orders(first:25, query: \"status:open updated_at:>''{{Time}}''\"){ pageInfo { hasNextPage } edges { cursor node { legacyResourceId name createdAt updatedAt channel { name } test fullyPaid unpaid displayFinancialStatus displayFulfillmentStatus subtotalLineItemsQuantity totalPriceSet { shopMoney { amount currencyCode } } customAttributes { key value } tags risk { assessments { riskLevel }} displayAddress { countryCodeV2 } shippingAddress { countryCodeV2 } billingAddress { countryCodeV2 } totalTaxSet { presentmentMoney { amount } shopMoney { amount } } purchasingEntity { ... on PurchasingCompany { company { id }}}}}}}"}'); + exit('{"query": "{orders(first:25, query: \"status:open updated_at:>''{{Time}}''\"){ pageInfo { hasNextPage } edges { cursor node { legacyResourceId name createdAt updatedAt channel { name } test fullyPaid unpaid displayFinancialStatus displayFulfillmentStatus subtotalLineItemsQuantity totalPriceSet { shopMoney { amount currencyCode } } customAttributes { key value } tags risk { assessments { riskLevel }} displayAddress { countryCodeV2 } shippingAddress { countryCodeV2 } billingAddress { countryCodeV2 } totalTaxSet { presentmentMoney { amount } shopMoney { amount } } taxLines { channelLiable } purchasingEntity { ... on PurchasingCompany { company { id }}}}}}}"}'); end; internal procedure GetExpectedCost(): Integer begin - exit(152); + exit(158); end; } \ No newline at end of file diff --git a/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLOrdersToImport.Codeunit.al b/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLOrdersToImport.Codeunit.al index 3b5bc1952a..dc83890d86 100644 --- a/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLOrdersToImport.Codeunit.al +++ b/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLOrdersToImport.Codeunit.al @@ -18,7 +18,7 @@ codeunit 30145 "Shpfy GQL OrdersToImport" implements "Shpfy IGraphQL" /// Return value of type Text. internal procedure GetGraphQL(): Text begin - exit('{"query": "{orders(first:25, query: \"updated_at:>''{{Time}}''\"){pageInfo{hasNextPage} edges{cursor node{legacyResourceId name createdAt updatedAt channel { name } test fullyPaid unpaid closed displayFinancialStatus displayFulfillmentStatus subtotalLineItemsQuantity totalPriceSet{shopMoney{amount currencyCode}} customAttributes{key value} tags risk { assessments { riskLevel }} displayAddress { countryCodeV2 } shippingAddress { countryCodeV2 } billingAddress { countryCodeV2 } totalTaxSet { presentmentMoney { amount } shopMoney { amount } } purchasingEntity { ... on PurchasingCompany { company { id }}}}}}}"}'); + exit('{"query": "{orders(first:25, query: \"updated_at:>''{{Time}}''\"){pageInfo{hasNextPage} edges{cursor node{legacyResourceId name createdAt updatedAt channel { name } test fullyPaid unpaid closed displayFinancialStatus displayFulfillmentStatus subtotalLineItemsQuantity totalPriceSet{shopMoney{amount currencyCode}} customAttributes{key value} tags risk { assessments { riskLevel }} displayAddress { countryCodeV2 } shippingAddress { countryCodeV2 } billingAddress { countryCodeV2 } totalTaxSet { presentmentMoney { amount } shopMoney { amount } } taxLines { channelLiable } purchasingEntity { ... on PurchasingCompany { company { id }}}}}}}"}'); end; /// @@ -27,6 +27,6 @@ codeunit 30145 "Shpfy GQL OrdersToImport" implements "Shpfy IGraphQL" /// Return value of type Integer. internal procedure GetExpectedCost(): Integer begin - exit(153); + exit(159); end; } diff --git a/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLShipmentLines.Codeunit.al b/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLShipmentLines.Codeunit.al index 054ebd694c..33efb2370e 100644 --- a/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLShipmentLines.Codeunit.al +++ b/src/Apps/W1/Shopify/App/src/GraphQL/Codeunits/ShpfyGQLShipmentLines.Codeunit.al @@ -9,7 +9,7 @@ codeunit 30216 "Shpfy GQL ShipmentLines" implements "Shpfy IGraphQL" { internal procedure GetGraphQL(): Text begin - exit('{"query":"{order(id: \"gid://shopify/Order/{{OrderId}}\") {shippingLines(first: 10) { pageInfo { endCursor hasNextPage } nodes { id title code source discountAllocations { allocatedAmountSet { presentmentMoney { amount } shopMoney { amount }}} originalPriceSet { presentmentMoney { amount } shopMoney { amount }} discountedPriceSet { presentmentMoney { amount } shopMoney { amount }} taxLines { title rate ratePercentage priceSet { presentmentMoney { amount } shopMoney {amount}}}}}}}"}'); + exit('{"query":"{order(id: \"gid://shopify/Order/{{OrderId}}\") {shippingLines(first: 10) { pageInfo { endCursor hasNextPage } nodes { id title code source discountAllocations { allocatedAmountSet { presentmentMoney { amount } shopMoney { amount }}} originalPriceSet { presentmentMoney { amount } shopMoney { amount }} discountedPriceSet { presentmentMoney { amount } shopMoney { amount }} taxLines { channelLiable title rate ratePercentage priceSet { presentmentMoney { amount } shopMoney {amount}}}}}}}"}'); end; internal procedure GetExpectedCost(): Integer diff --git a/src/Apps/W1/Shopify/App/src/Order handling/Codeunits/ShpfyImportOrder.Codeunit.al b/src/Apps/W1/Shopify/App/src/Order handling/Codeunits/ShpfyImportOrder.Codeunit.al index 1b66c59204..ac4272638e 100644 --- a/src/Apps/W1/Shopify/App/src/Order handling/Codeunits/ShpfyImportOrder.Codeunit.al +++ b/src/Apps/W1/Shopify/App/src/Order handling/Codeunits/ShpfyImportOrder.Codeunit.al @@ -802,6 +802,7 @@ codeunit 30161 "Shpfy Import Order" JsonHelper.GetValueIntoField(JToken, 'ratePercentage', RecordRef, OrderTaxLine.FieldNo("Rate %")); JsonHelper.GetValueIntoField(JToken, 'priceSet.shopMoney.amount', RecordRef, OrderTaxLine.FieldNo(Amount)); JsonHelper.GetValueIntoField(JToken, 'priceSet.presentmentMoney.amount', RecordRef, OrderTaxLine.FieldNo("Presentment Amount")); + JsonHelper.GetValueIntoField(JToken, 'channelLiable', RecordRef, OrderTaxLine.FieldNo("Channel Liable")); RecordRef.Insert(true); RecordRef.Close(); end; diff --git a/src/Apps/W1/Shopify/App/src/Order handling/Codeunits/ShpfyOrdersAPI.Codeunit.al b/src/Apps/W1/Shopify/App/src/Order handling/Codeunits/ShpfyOrdersAPI.Codeunit.al index 2a6edad8da..d542ca4a43 100644 --- a/src/Apps/W1/Shopify/App/src/Order handling/Codeunits/ShpfyOrdersAPI.Codeunit.al +++ b/src/Apps/W1/Shopify/App/src/Order handling/Codeunits/ShpfyOrdersAPI.Codeunit.al @@ -244,6 +244,7 @@ codeunit 30165 "Shpfy Orders API" OrdersToImport.Tags := CopyStr(Tags.ToText(), 2, MaxStrLen(OrdersToImport.Tags)); end; OrdersToImport."High Risk" := IsHighRiskOrder(JNode); + OrdersToImport."Channel Liable Taxes" := ContainsChannelLiableTax(JNode); OrderHeader.SetRange("Shopify Order Id", Id); if OrderHeader.IsEmpty then OrdersToImport."Import Action" := OrdersToImport."Import Action"::New @@ -307,4 +308,22 @@ codeunit 30165 "Shpfy Orders API" exit(true); end; end; -} \ No newline at end of file + + local procedure ContainsChannelLiableTax(JOrder: JsonObject): Boolean + var + JTaxLines: JsonArray; + JTaxLine: JsonToken; + begin + if not JsonHelper.GetJsonArray(JOrder, JTaxLines, 'taxLines') then + exit(false); + + if JTaxLines.Count() = 0 then + exit(false); + + if not JTaxLines.Get(0, JTaxLine) then + exit(false); + + // Shopify keeps channelLiable consistent across tax lines, so checking the first entry is sufficient. + exit(JsonHelper.GetValueAsBoolean(JTaxLine, 'channelLiable')); + end; +} diff --git a/src/Apps/W1/Shopify/App/src/Order handling/Pages/ShpfyOrder.Page.al b/src/Apps/W1/Shopify/App/src/Order handling/Pages/ShpfyOrder.Page.al index f0979041e2..5d3c219494 100644 --- a/src/Apps/W1/Shopify/App/src/Order handling/Pages/ShpfyOrder.Page.al +++ b/src/Apps/W1/Shopify/App/src/Order handling/Pages/ShpfyOrder.Page.al @@ -353,6 +353,13 @@ page 30113 "Shpfy Order" ApplicationArea = All; ToolTip = 'Specifies if tax is included in the unit price.'; } + field("Channel Liable Taxes"; Rec."Channel Liable Taxes") + { + ApplicationArea = All; + Editable = false; + ToolTip = 'Specifies if any tax line on the order is liable to be charged by the sales channel.'; + } + field(CurrencyCode; Rec."Currency Code") { ApplicationArea = All; diff --git a/src/Apps/W1/Shopify/App/src/Order handling/Pages/ShpfyOrderTaxLines.Page.al b/src/Apps/W1/Shopify/App/src/Order handling/Pages/ShpfyOrderTaxLines.Page.al index 5232051acf..0edbf00389 100644 --- a/src/Apps/W1/Shopify/App/src/Order handling/Pages/ShpfyOrderTaxLines.Page.al +++ b/src/Apps/W1/Shopify/App/src/Order handling/Pages/ShpfyOrderTaxLines.Page.al @@ -46,6 +46,11 @@ page 30168 "Shpfy Order Tax Lines" ApplicationArea = All; ToolTip = 'Specifies the rate percentage of the tax line.'; } + field("Channel Liable"; Rec."Channel Liable") + { + ApplicationArea = All; + ToolTip = 'Specifies if the channel that submitted the tax line is liable for remitting.'; + } } } } diff --git a/src/Apps/W1/Shopify/App/src/Order handling/Pages/ShpfyOrders.Page.al b/src/Apps/W1/Shopify/App/src/Order handling/Pages/ShpfyOrders.Page.al index cef59da666..7cdf64caa1 100644 --- a/src/Apps/W1/Shopify/App/src/Order handling/Pages/ShpfyOrders.Page.al +++ b/src/Apps/W1/Shopify/App/src/Order handling/Pages/ShpfyOrders.Page.al @@ -158,6 +158,12 @@ page 30115 "Shpfy Orders" ToolTip = 'Specifies the name of the app used by the channel where you sell your products. A channel can be a platform or a marketplace such as an online store or POS.'; Visible = false; } + field("Channel Liable Taxes"; Rec."Channel Liable Taxes") + { + ApplicationArea = All; + ToolTip = 'Specifies if any tax line on the order is liable to be charged by the channel.'; + Visible = false; + } field(CancelReason; Rec."Cancel Reason") { ApplicationArea = All; diff --git a/src/Apps/W1/Shopify/App/src/Order handling/Pages/ShpfyOrdersToImport.Page.al b/src/Apps/W1/Shopify/App/src/Order handling/Pages/ShpfyOrdersToImport.Page.al index b09788c214..a90c006474 100644 --- a/src/Apps/W1/Shopify/App/src/Order handling/Pages/ShpfyOrdersToImport.Page.al +++ b/src/Apps/W1/Shopify/App/src/Order handling/Pages/ShpfyOrdersToImport.Page.al @@ -99,6 +99,11 @@ page 30121 "Shpfy Orders to Import" ApplicationArea = All; ToolTip = 'Specifies the order''s status in terms of fulfilled line items. Valid values are: Fulfilled, null, partial, restocked.'; } + field("Channel Liable Taxes"; Rec."Channel Liable Taxes") + { + ApplicationArea = All; + ToolTip = 'Specifies if any tax line on the order is liable to be collected by the sales channel.'; + } field(ChannelName; Rec."Channel Name") { ApplicationArea = All; diff --git a/src/Apps/W1/Shopify/App/src/Order handling/Reports/ShpfySyncOrdersfromShopify.Report.al b/src/Apps/W1/Shopify/App/src/Order handling/Reports/ShpfySyncOrdersfromShopify.Report.al index 8a2a3550f8..06a5f9ebb1 100644 --- a/src/Apps/W1/Shopify/App/src/Order handling/Reports/ShpfySyncOrdersfromShopify.Report.al +++ b/src/Apps/W1/Shopify/App/src/Order handling/Reports/ShpfySyncOrdersfromShopify.Report.al @@ -25,7 +25,7 @@ report 30104 "Shpfy Sync Orders from Shopify" { DataItemLink = "Shop Code" = field(Code); DataItemLinkReference = Shop; - RequestFilterFields = "Fully Paid", "Financial Status", "Fulfillment Status", Confirmed, "Import Action", "Attribute Key Filter", "Attribute Key Exists", "Channel Name", "Order No.", "High Risk", "Sell-to Country/Region Code", "Ship-to Country/Region Code", "Bill-to Country/Region Code", "VAT Amount"; + RequestFilterFields = "Fully Paid", "Financial Status", "Fulfillment Status", Confirmed, "Import Action", "Attribute Key Filter", "Attribute Key Exists", "Channel Name", "Channel Liable Taxes", "Order No.", "High Risk", "Sell-to Country/Region Code", "Ship-to Country/Region Code", "Bill-to Country/Region Code", "VAT Amount"; trigger OnPreDataItem() var diff --git a/src/Apps/W1/Shopify/App/src/Order handling/Tables/ShpfyOrderHeader.Table.al b/src/Apps/W1/Shopify/App/src/Order handling/Tables/ShpfyOrderHeader.Table.al index 0806ed5a78..9858f4275c 100644 --- a/src/Apps/W1/Shopify/App/src/Order handling/Tables/ShpfyOrderHeader.Table.al +++ b/src/Apps/W1/Shopify/App/src/Order handling/Tables/ShpfyOrderHeader.Table.al @@ -686,6 +686,13 @@ table 30118 "Shpfy Order Header" AutoFormatType = 1; AutoFormatExpression = "Currency Code"; } + field(134; "Channel Liable Taxes"; Boolean) + { + Caption = 'Channel Liable Taxes'; + Editable = false; + FieldClass = FlowField; + CalcFormula = exist("Shpfy Order Tax Line" where("Parent Id" = field("Shopify Order Id"), "Channel Liable" = const(true))); + } field(500; "Shop Code"; Code[20]) { Caption = 'Shop Code'; diff --git a/src/Apps/W1/Shopify/App/src/Order handling/Tables/ShpfyOrderTaxLine.Table.al b/src/Apps/W1/Shopify/App/src/Order handling/Tables/ShpfyOrderTaxLine.Table.al index 84585da0c2..516b6f46b6 100644 --- a/src/Apps/W1/Shopify/App/src/Order handling/Tables/ShpfyOrderTaxLine.Table.al +++ b/src/Apps/W1/Shopify/App/src/Order handling/Tables/ShpfyOrderTaxLine.Table.al @@ -75,6 +75,12 @@ table 30122 "Shpfy Order Tax Line" Editable = false; AutoFormatType = 0; } + field(9; "Channel Liable"; Boolean) + { + Caption = 'Channel Liable'; + DataClassification = SystemMetadata; + Editable = false; + } } keys { diff --git a/src/Apps/W1/Shopify/App/src/Order handling/Tables/ShpfyOrdersToImport.Table.al b/src/Apps/W1/Shopify/App/src/Order handling/Tables/ShpfyOrdersToImport.Table.al index baa5b53db9..48e9061898 100644 --- a/src/Apps/W1/Shopify/App/src/Order handling/Tables/ShpfyOrdersToImport.Table.al +++ b/src/Apps/W1/Shopify/App/src/Order handling/Tables/ShpfyOrdersToImport.Table.al @@ -217,6 +217,12 @@ table 30121 "Shpfy Orders to Import" AutoFormatType = 1; AutoFormatExpression = "Currency Code"; } + field(30; "Channel Liable Taxes"; Boolean) + { + Caption = 'Channel Liable Taxes'; + DataClassification = SystemMetadata; + Editable = false; + } field(100; "Import Action"; Enum "Shpfy Import Action") { Caption = 'Import Action'; diff --git a/src/Apps/W1/Shopify/Test/Order Handling/ShpfyOrdersAPITest.Codeunit.al b/src/Apps/W1/Shopify/Test/Order Handling/ShpfyOrdersAPITest.Codeunit.al index fac4d47deb..a27d2a03de 100644 --- a/src/Apps/W1/Shopify/Test/Order Handling/ShpfyOrdersAPITest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Order Handling/ShpfyOrdersAPITest.Codeunit.al @@ -25,6 +25,10 @@ codeunit 139608 "Shpfy Orders API Test" LibraryRandom: Codeunit "Library - Random"; OrdersAPISubscriber: Codeunit "Shpfy Orders API Subscriber"; Any: Codeunit Any; + OrdersToImportChannelLiableMismatchTxt: Label 'Orders to import Channel Liable Taxes mismatch when %1.', Locked = true; + OrderLevelTaxLineExpectedTxt: Label 'An order-level tax line should exist when %1.', Locked = true; + ChannelLiableFlagMismatchTxt: Label 'Channel Liable flag mismatch when %1.', Locked = true; + OrderHeaderChannelLiableMismatchTxt: Label 'Order header Channel Liable Taxes mismatch when %1.', Locked = true; [Test] procedure UnitTestExtractShopifyOrdersToImport() @@ -843,6 +847,329 @@ codeunit 139608 "Shpfy Orders API Test" LibraryAssert.AreEqual(OrderHeader."Sales Order No.", SalesHeader."No.", 'ShpfyOrderHeader."Sales Order No." = SalesHeader."No."'); end; + [Test] + procedure ChannelLiableFlagMissingDefaultsToFalseOnOrdersToImport() + var + Shop: Record "Shpfy Shop"; + OrdersToImport: Record "Shpfy Orders to Import"; + CommunicationMgt: Codeunit "Shpfy Communication Mgt."; + OrdersAPI: Codeunit "Shpfy Orders API"; + OrderHandlingHelper: Codeunit "Shpfy Order Handling Helper"; + Cursor: Text; + JOrdersToImport: JsonObject; + ExpectedChannelLiable: Boolean; + ScenarioName: Text; + ChannelLiableScenario: Option Missing,TrueValue,FalseValue,NullValue; + begin + // [GIVEN] Shopify shop context and an orders-to-import payload missing taxLines. + Initialize(); + Clear(OrdersToImport); + if not OrdersToImport.IsEmpty then + OrdersToImport.DeleteAll(); + + Shop := CommunicationMgt.GetShopRecord(); + JOrdersToImport := OrderHandlingHelper.GetOrdersToImport(false); + PrepareOrdersToImportChannelLiableScenario(ChannelLiableScenario::Missing, JOrdersToImport, ExpectedChannelLiable, ScenarioName); + + // [WHEN] Orders are extracted from Shopify. + OrdersAPI.ExtractShopifyOrdersToImport(Shop, JOrdersToImport, Cursor); + + // [THEN] Channel Liable flag on the staging record defaults to false. + OrdersToImport.Reset(); + LibraryAssert.IsTrue(OrdersToImport.FindLast(), 'Orders to import record is created'); + LibraryAssert.AreEqual(ExpectedChannelLiable, OrdersToImport."Channel Liable Taxes", StrSubstNo(OrdersToImportChannelLiableMismatchTxt, ScenarioName)); + end; + + [Test] + procedure ChannelLiableFlagTrueIsStoredOnOrdersToImport() + var + Shop: Record "Shpfy Shop"; + OrdersToImport: Record "Shpfy Orders to Import"; + CommunicationMgt: Codeunit "Shpfy Communication Mgt."; + OrdersAPI: Codeunit "Shpfy Orders API"; + OrderHandlingHelper: Codeunit "Shpfy Order Handling Helper"; + Cursor: Text; + JOrdersToImport: JsonObject; + ExpectedChannelLiable: Boolean; + ScenarioName: Text; + ChannelLiableScenario: Option Missing,TrueValue,FalseValue,NullValue; + begin + // [GIVEN] Shopify shop context and an orders-to-import payload with channelLiable set to true. + Initialize(); + Clear(OrdersToImport); + if not OrdersToImport.IsEmpty then + OrdersToImport.DeleteAll(); + + Shop := CommunicationMgt.GetShopRecord(); + JOrdersToImport := OrderHandlingHelper.GetOrdersToImport(false); + PrepareOrdersToImportChannelLiableScenario(ChannelLiableScenario::TrueValue, JOrdersToImport, ExpectedChannelLiable, ScenarioName); + + // [WHEN] Orders are extracted from Shopify. + OrdersAPI.ExtractShopifyOrdersToImport(Shop, JOrdersToImport, Cursor); + + // [THEN] Channel Liable flag on the staging record is true. + OrdersToImport.Reset(); + LibraryAssert.IsTrue(OrdersToImport.FindLast(), 'Orders to import record is created'); + LibraryAssert.AreEqual(ExpectedChannelLiable, OrdersToImport."Channel Liable Taxes", StrSubstNo(OrdersToImportChannelLiableMismatchTxt, ScenarioName)); + end; + + [Test] + procedure ChannelLiableFlagFalseIsStoredOnOrdersToImport() + var + Shop: Record "Shpfy Shop"; + OrdersToImport: Record "Shpfy Orders to Import"; + CommunicationMgt: Codeunit "Shpfy Communication Mgt."; + OrdersAPI: Codeunit "Shpfy Orders API"; + OrderHandlingHelper: Codeunit "Shpfy Order Handling Helper"; + Cursor: Text; + JOrdersToImport: JsonObject; + ExpectedChannelLiable: Boolean; + ScenarioName: Text; + ChannelLiableScenario: Option Missing,TrueValue,FalseValue,NullValue; + begin + // [GIVEN] Shopify shop context and an orders-to-import payload with channelLiable set to false. + Initialize(); + Clear(OrdersToImport); + if not OrdersToImport.IsEmpty then + OrdersToImport.DeleteAll(); + + Shop := CommunicationMgt.GetShopRecord(); + JOrdersToImport := OrderHandlingHelper.GetOrdersToImport(false); + PrepareOrdersToImportChannelLiableScenario(ChannelLiableScenario::FalseValue, JOrdersToImport, ExpectedChannelLiable, ScenarioName); + + // [WHEN] Orders are extracted from Shopify. + OrdersAPI.ExtractShopifyOrdersToImport(Shop, JOrdersToImport, Cursor); + + // [THEN] Channel Liable flag on the staging record is false. + OrdersToImport.Reset(); + LibraryAssert.IsTrue(OrdersToImport.FindLast(), 'Orders to import record is created'); + LibraryAssert.AreEqual(ExpectedChannelLiable, OrdersToImport."Channel Liable Taxes", StrSubstNo(OrdersToImportChannelLiableMismatchTxt, ScenarioName)); + end; + + [Test] + procedure ChannelLiableFlagNullDefaultsToFalseOnOrdersToImport() + var + Shop: Record "Shpfy Shop"; + OrdersToImport: Record "Shpfy Orders to Import"; + CommunicationMgt: Codeunit "Shpfy Communication Mgt."; + OrdersAPI: Codeunit "Shpfy Orders API"; + OrderHandlingHelper: Codeunit "Shpfy Order Handling Helper"; + Cursor: Text; + JOrdersToImport: JsonObject; + ExpectedChannelLiable: Boolean; + ScenarioName: Text; + ChannelLiableScenario: Option Missing,TrueValue,FalseValue,NullValue; + begin + // [GIVEN] Shopify shop context and an orders-to-import payload with channelLiable provided as null. + Initialize(); + Clear(OrdersToImport); + if not OrdersToImport.IsEmpty then + OrdersToImport.DeleteAll(); + + Shop := CommunicationMgt.GetShopRecord(); + JOrdersToImport := OrderHandlingHelper.GetOrdersToImport(false); + PrepareOrdersToImportChannelLiableScenario(ChannelLiableScenario::NullValue, JOrdersToImport, ExpectedChannelLiable, ScenarioName); + + // [WHEN] Orders are extracted from Shopify. + OrdersAPI.ExtractShopifyOrdersToImport(Shop, JOrdersToImport, Cursor); + + // [THEN] Channel Liable flag on the staging record defaults to false. + OrdersToImport.Reset(); + LibraryAssert.IsTrue(OrdersToImport.FindLast(), 'Orders to import record is created'); + LibraryAssert.AreEqual(ExpectedChannelLiable, OrdersToImport."Channel Liable Taxes", StrSubstNo(OrdersToImportChannelLiableMismatchTxt, ScenarioName)); + end; + + [Test] + procedure ChannelLiableFlagMissingDefaultsToFalse() + var + Shop: Record "Shpfy Shop"; + OrderHeader: Record "Shpfy Order Header"; + OrdersToImport: Record "Shpfy Orders to Import"; + OrderTaxLine: Record "Shpfy Order Tax Line"; + CommunicationMgt: Codeunit "Shpfy Communication Mgt."; + ImportOrder: Codeunit "Shpfy Import Order"; + OrderHandlingHelper: Codeunit "Shpfy Order Handling Helper"; + JOrder: JsonObject; + JLineItems: JsonArray; + ExpectedChannelLiable: Boolean; + ScenarioName: Text; + OrderTaxLineFound: Boolean; + begin + // [GIVEN] Shopify shop context and an order JSON with the taxLines array removed. + Initialize(); + + Shop := CommunicationMgt.GetShopRecord(); + ImportOrder.SetShop(Shop.Code); + + JOrder := OrderHandlingHelper.CreateShopifyOrderAsJson(Shop, OrdersToImport, JLineItems, false); + + JOrder.Remove('taxLines'); + + ExpectedChannelLiable := false; + ScenarioName := 'missing taxLines'; + + // [WHEN] Order is imported + + OrderHandlingHelper.ImportShopifyOrder(Shop, OrderHeader, OrdersToImport, ImportOrder, JOrder, JLineItems); + + // [THEN] Channel Liable in tax line and order header is set properly + OrderTaxLine.Reset(); + OrderTaxLine.SetRange("Parent Id", OrderHeader."Shopify Order Id"); + + OrderTaxLineFound := not OrderTaxLine.IsEmpty(); + LibraryAssert.IsFalse(OrderTaxLineFound, 'Order-level tax lines should not be created when taxLines array is missing.'); + + OrderHeader.CalcFields("Channel Liable Taxes"); + LibraryAssert.AreEqual(ExpectedChannelLiable, OrderHeader."Channel Liable Taxes", StrSubstNo(OrderHeaderChannelLiableMismatchTxt, ScenarioName)); + end; + + [Test] + procedure ChannelLiableFlagTrueIsImported() + var + Shop: Record "Shpfy Shop"; + OrderHeader: Record "Shpfy Order Header"; + OrdersToImport: Record "Shpfy Orders to Import"; + OrderTaxLine: Record "Shpfy Order Tax Line"; + CommunicationMgt: Codeunit "Shpfy Communication Mgt."; + ImportOrder: Codeunit "Shpfy Import Order"; + OrderHandlingHelper: Codeunit "Shpfy Order Handling Helper"; + JOrder: JsonObject; + JLineItems: JsonArray; + ExpectedHasRecord: Boolean; + ExpectedChannelLiable: Boolean; + ScenarioName: Text; + OrderTaxLineFound: Boolean; + ChannelLiableScenario: Option Missing,TrueValue,FalseValue,NullValue; + begin + // [GIVEN] Shopify shop context and an order JSON with channelLiable explicitly set to true. + Initialize(); + + Shop := CommunicationMgt.GetShopRecord(); + ImportOrder.SetShop(Shop.Code); + + JOrder := OrderHandlingHelper.CreateShopifyOrderAsJson(Shop, OrdersToImport, JLineItems, false); + + JOrder.Remove('taxLines'); + + PrepareChannelLiableWithTaxLines(ChannelLiableScenario::TrueValue, JOrder, ExpectedHasRecord, ExpectedChannelLiable, ScenarioName); + + // [WHEN] Order is imported + OrderHandlingHelper.ImportShopifyOrder(Shop, OrderHeader, OrdersToImport, ImportOrder, JOrder, JLineItems); + + // [THEN] Channel Liable in tax line and order header is set properly + OrderTaxLine.Reset(); + OrderTaxLine.SetRange("Parent Id", OrderHeader."Shopify Order Id"); + + OrderTaxLineFound := OrderTaxLine.FindFirst(); + if ExpectedHasRecord then begin + LibraryAssert.IsTrue(OrderTaxLineFound, StrSubstNo(OrderLevelTaxLineExpectedTxt, ScenarioName)); + if OrderTaxLineFound then + LibraryAssert.AreEqual(ExpectedChannelLiable, OrderTaxLine."Channel Liable", StrSubstNo(ChannelLiableFlagMismatchTxt, ScenarioName)); + end else + LibraryAssert.IsFalse(OrderTaxLineFound, 'Order-level tax lines should not be created when taxLines array is missing.'); + + OrderHeader.CalcFields("Channel Liable Taxes"); + LibraryAssert.AreEqual(ExpectedChannelLiable, OrderHeader."Channel Liable Taxes", StrSubstNo(OrderHeaderChannelLiableMismatchTxt, ScenarioName)); + end; + + [Test] + procedure ChannelLiableFlagFalseIsImported() + var + Shop: Record "Shpfy Shop"; + OrderHeader: Record "Shpfy Order Header"; + OrdersToImport: Record "Shpfy Orders to Import"; + OrderTaxLine: Record "Shpfy Order Tax Line"; + CommunicationMgt: Codeunit "Shpfy Communication Mgt."; + ImportOrder: Codeunit "Shpfy Import Order"; + OrderHandlingHelper: Codeunit "Shpfy Order Handling Helper"; + JOrder: JsonObject; + JLineItems: JsonArray; + ExpectedHasRecord: Boolean; + ExpectedChannelLiable: Boolean; + ScenarioName: Text; + OrderTaxLineFound: Boolean; + ChannelLiableScenario: Option Missing,TrueValue,FalseValue,NullValue; + begin + // [GIVEN] Shopify shop context and an order JSON with channelLiable explicitly set to false. + Initialize(); + + Shop := CommunicationMgt.GetShopRecord(); + ImportOrder.SetShop(Shop.Code); + + JOrder := OrderHandlingHelper.CreateShopifyOrderAsJson(Shop, OrdersToImport, JLineItems, false); + + JOrder.Remove('taxLines'); + + PrepareChannelLiableWithTaxLines(ChannelLiableScenario::FalseValue, JOrder, ExpectedHasRecord, ExpectedChannelLiable, ScenarioName); + + // [WHEN] Order is imported + OrderHandlingHelper.ImportShopifyOrder(Shop, OrderHeader, OrdersToImport, ImportOrder, JOrder, JLineItems); + + // [THEN] Channel Liable in tax line and order header is set properly + OrderTaxLine.Reset(); + OrderTaxLine.SetRange("Parent Id", OrderHeader."Shopify Order Id"); + + OrderTaxLineFound := OrderTaxLine.FindFirst(); + if ExpectedHasRecord then begin + LibraryAssert.IsTrue(OrderTaxLineFound, StrSubstNo(OrderLevelTaxLineExpectedTxt, ScenarioName)); + if OrderTaxLineFound then + LibraryAssert.AreEqual(ExpectedChannelLiable, OrderTaxLine."Channel Liable", StrSubstNo(ChannelLiableFlagMismatchTxt, ScenarioName)); + end else + LibraryAssert.IsFalse(OrderTaxLineFound, 'Order-level tax lines should not be created when taxLines array is missing.'); + + OrderHeader.CalcFields("Channel Liable Taxes"); + LibraryAssert.AreEqual(ExpectedChannelLiable, OrderHeader."Channel Liable Taxes", StrSubstNo(OrderHeaderChannelLiableMismatchTxt, ScenarioName)); + end; + + [Test] + procedure ChannelLiableFlagNullDefaultsToFalse() + var + Shop: Record "Shpfy Shop"; + OrderHeader: Record "Shpfy Order Header"; + OrdersToImport: Record "Shpfy Orders to Import"; + OrderTaxLine: Record "Shpfy Order Tax Line"; + CommunicationMgt: Codeunit "Shpfy Communication Mgt."; + ImportOrder: Codeunit "Shpfy Import Order"; + OrderHandlingHelper: Codeunit "Shpfy Order Handling Helper"; + JOrder: JsonObject; + JLineItems: JsonArray; + ExpectedHasRecord: Boolean; + ExpectedChannelLiable: Boolean; + ScenarioName: Text; + OrderTaxLineFound: Boolean; + ChannelLiableScenario: Option Missing,TrueValue,FalseValue,NullValue; + begin + // [GIVEN] Shopify shop context and an order JSON with channelLiable provided as null. + Initialize(); + + Shop := CommunicationMgt.GetShopRecord(); + ImportOrder.SetShop(Shop.Code); + + JOrder := OrderHandlingHelper.CreateShopifyOrderAsJson(Shop, OrdersToImport, JLineItems, false); + + JOrder.Remove('taxLines'); + + PrepareChannelLiableWithTaxLines(ChannelLiableScenario::NullValue, JOrder, ExpectedHasRecord, ExpectedChannelLiable, ScenarioName); + + // [WHEN] Order is imported + OrderHandlingHelper.ImportShopifyOrder(Shop, OrderHeader, OrdersToImport, ImportOrder, JOrder, JLineItems); + + // [THEN] Channel Liable in tax line and order header is set properly + OrderTaxLine.Reset(); + OrderTaxLine.SetRange("Parent Id", OrderHeader."Shopify Order Id"); + + OrderTaxLineFound := OrderTaxLine.FindFirst(); + if ExpectedHasRecord then begin + LibraryAssert.IsTrue(OrderTaxLineFound, StrSubstNo(OrderLevelTaxLineExpectedTxt, ScenarioName)); + if OrderTaxLineFound then + LibraryAssert.AreEqual(ExpectedChannelLiable, OrderTaxLine."Channel Liable", StrSubstNo(ChannelLiableFlagMismatchTxt, ScenarioName)); + end else + LibraryAssert.IsFalse(OrderTaxLineFound, 'Order-level tax lines should not be created when taxLines array is missing.'); + + OrderHeader.CalcFields("Channel Liable Taxes"); + LibraryAssert.AreEqual(ExpectedChannelLiable, OrderHeader."Channel Liable Taxes", StrSubstNo(OrderHeaderChannelLiableMismatchTxt, ScenarioName)); + end; + local procedure CreateTaxArea(var TaxArea: Record "Tax Area"; var ShopifyTaxArea: Record "Shpfy Tax Area"; Shop: Record "Shpfy Shop") var ShopifyCustomerTemplate: Record "Shpfy Customer Template"; @@ -889,4 +1216,116 @@ codeunit 139608 "Shpfy Orders API Test" Codeunit.Run(Codeunit::"Shpfy Initialize Test"); if BindSubscription(OrdersAPISubscriber) then; end; + + local procedure PrepareOrdersToImportChannelLiableScenario(ChannelLiableScenario: Option Missing,TrueValue,FalseValue,NullValue; var JOrdersToImport: JsonObject; var ExpectedChannelLiable: Boolean; var ScenarioName: Text) + var + JOrder: JsonToken; + JOrders: JsonArray; + JNode: JsonObject; + JTaxLines: JsonArray; + JTaxLine: JsonObject; + JNull: JsonValue; + begin + JOrdersToImport.GetObject('data').GetObject('orders').GetArray('edges').Get(0, JOrder); + JNode := JOrder.AsObject().GetObject('node'); + + if JNode.Contains('taxLines') then + JNode.Remove('taxLines'); + + Clear(JTaxLine); + Clear(JTaxLines); + + case ChannelLiableScenario of + ChannelLiableScenario::Missing: + begin + ExpectedChannelLiable := false; + ScenarioName := 'missing taxLines'; + end; + ChannelLiableScenario::TrueValue: + begin + JTaxLine.Add('channelLiable', true); + JTaxLines.Add(JTaxLine); + JNode.Add('taxLines', JTaxLines); + + ScenarioName := Format(ChannelLiableScenario); + ExpectedChannelLiable := true; + end; + ChannelLiableScenario::FalseValue: + begin + JTaxLine.Add('channelLiable', false); + JTaxLines.Add(JTaxLine); + JNode.Add('taxLines', JTaxLines); + + ScenarioName := Format(ChannelLiableScenario); + ExpectedChannelLiable := false; + end; + ChannelLiableScenario::NullValue: + begin + JNull.SetValueToNull(); + JTaxLine.Add('channelLiable', JNull); + JTaxLines.Add(JTaxLine); + JNode.Add('taxLines', JTaxLines); + + ScenarioName := Format(ChannelLiableScenario); + ExpectedChannelLiable := false; + end; + end; + + Clear(JOrders); + JOrders.Add(JOrder); + JOrdersToImport.GetObject('data').GetObject('orders').Replace('edges', JOrders); + end; + + local procedure PrepareChannelLiableWithTaxLines(ChannelLiableScenario: Option Missing,TrueValue,FalseValue,NullValue; var JOrder: JsonObject; var ExpectedHasRecord: Boolean; var ExpectedChannelLiable: Boolean; var ScenarioName: Text) + var + JTaxLines: JsonArray; + JTaxLine: JsonObject; + JPriceSet: JsonObject; + JShopMoney: JsonObject; + JPresentmentMoney: JsonObject; + JNull: JsonValue; + begin + ExpectedHasRecord := true; + ScenarioName := Format(ChannelLiableScenario); + + Clear(JTaxLine); + Clear(JTaxLines); + Clear(JPriceSet); + Clear(JShopMoney); + Clear(JPresentmentMoney); + + JTaxLine.Add('title', 'VAT'); + JTaxLine.Add('rate', 0.10); + JTaxLine.Add('ratePercentage', 10); + + JShopMoney.Add('amount', '10'); + JPriceSet.Add('shopMoney', JShopMoney); + + JPresentmentMoney.Add('amount', '10'); + JPriceSet.Add('presentmentMoney', JPresentmentMoney); + + JTaxLine.Add('priceSet', JPriceSet); + + case ChannelLiableScenario of + ChannelLiableScenario::TrueValue: + begin + JTaxLine.Add('channelLiable', true); + ExpectedChannelLiable := true; + end; + ChannelLiableScenario::FalseValue: + begin + JTaxLine.Add('channelLiable', false); + ExpectedChannelLiable := false; + end; + ChannelLiableScenario::NullValue: + begin + JNull.SetValueToNull(); + JTaxLine.Add('channelLiable', JNull); + ExpectedChannelLiable := false; + end; + end; + + JTaxLines.Add(JTaxLine); + JOrder.Add('taxLines', JTaxLines); + end; }