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 91682b1d2a..864386938b 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 @@ -151,6 +151,7 @@ codeunit 30161 "Shpfy Import Order" var OrderLine: Record "Shpfy Order Line"; DataCapture: Record "Shpfy Data Capture"; + OrderTaxLine: Record "Shpfy Order Tax Line"; Hash: Codeunit "Shpfy Hash"; JOrderLine: JsonToken; LineIds: Text; @@ -172,7 +173,7 @@ codeunit 30161 "Shpfy Import Order" OrderLine.Copy(TempOrderLine); UpdateLocationIdAndDeliveryMethodOnOrderLine(OrderLine); OrderLine.Insert(); - AddTaxLines(OrderLine."Line Id", JsonHelper.GetJsonArray(JOrderLine, 'taxLines')); + OrderTaxLine.ImportFromJson(OrderLine."Line Id", JsonHelper.GetJsonArray(JOrderLine, 'taxLines')); ImportCustomAttributtes(OrderLine."Shopify Order Id", OrderLine.SystemId, JsonHelper.GetJsonArray(JOrderLine, 'customAttributes')); end; DataCapture.Add(Database::"Shpfy Order Line", OrderLine.SystemId, Format(JOrderLine)); @@ -268,6 +269,7 @@ codeunit 30161 "Shpfy Import Order" local procedure SetAndCreateRelatedRecords(JOrder: JsonObject; var OrderHeader: Record "Shpfy Order Header") var + OrderTaxLine: Record "Shpfy Order Tax Line"; FulfillmentOrdersAPI: Codeunit "Shpfy Fulfillment Orders API"; ShippingCharges: Codeunit "Shpfy Shipping Charges"; Transactions: Codeunit "Shpfy Transactions"; @@ -275,7 +277,7 @@ codeunit 30161 "Shpfy Import Order" RefundsAPI: Codeunit "Shpfy Refunds API"; IReturnRefundProcess: Interface "Shpfy IReturnRefund Process"; begin - AddTaxLines(OrderHeader."Shopify Order Id", JsonHelper.GetJsonArray(JOrder, 'taxLines')); + OrderTaxLine.ImportFromJson(OrderHeader."Shopify Order Id", JsonHelper.GetJsonArray(JOrder, 'taxLines')); OrderHeader.SetWorkDescription(JsonHelper.GetValueAsText(JOrder, 'note')); ImportCustomAttributtes(OrderHeader."Shopify Order Id", JsonHelper.GetJsonArray(JOrder, 'customAttributes')); OrderHeader.UpdateTags(JsonHelper.GetArrayAsText(JOrder, 'tags')); @@ -794,35 +796,6 @@ codeunit 30161 "Shpfy Import Order" OrderHeaderRecordRef.Field(OrderHeader.FieldNo("Salesperson Code")).Value := StaffMember."Salesperson Code"; end; - /// - /// Add Tax Lines. - /// - /// Parameter of type BigInteger. - /// Parameter of type JsonArray. - local procedure AddTaxLines(ParentId: BigInteger; JTaxLines: JsonArray) - var - OrderTaxLine: Record "Shpfy Order Tax Line"; - RecordRef: RecordRef; - JToken: JsonToken; - begin - OrderTaxLine.SetRange("Parent Id", ParentId); - if not OrderTaxLine.IsEmpty() then - OrderTaxLine.DeleteAll(); - foreach JToken in JTaxLines do begin - RecordRef.Open(Database::"Shpfy Order Tax Line"); - RecordRef.Init(); - RecordRef.Field(OrderTaxLine.FieldNo("Parent Id")).Value := ParentId; - JsonHelper.GetValueIntoField(JToken, 'title', RecordRef, OrderTaxLine.FieldNo(Title)); - JsonHelper.GetValueIntoField(JToken, 'rate', RecordRef, OrderTaxLine.FieldNo(Rate)); - 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; - end; - /// /// Description for GetName. /// 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 a59953d374..6f91cb79de 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 @@ -74,12 +74,19 @@ page 30168 "Shpfy Order Tax Lines" var OrderHeader: Record "Shpfy Order Header"; OrderLine: Record "Shpfy Order Line"; + OrderShippingCharges: Record "Shpfy Order Shipping Charges"; begin OrderLine.SetRange("Line Id", Rec."Parent Id"); - if not OrderLine.FindFirst() then - exit; - if not OrderHeader.Get(OrderLine."Shopify Order Id") then - exit; + if OrderLine.FindFirst() then begin + if not OrderHeader.Get(OrderLine."Shopify Order Id") then + exit; + end else + if OrderShippingCharges.Get(Rec."Parent Id") then begin + if not OrderHeader.Get(OrderShippingCharges."Shopify Order Id") then + exit; + end else + if not OrderHeader.Get(Rec."Parent Id") then + exit; PresentmentCurrencyVisible := OrderHeader.IsPresentmentCurrencyOrder(); end; 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 516b6f46b6..901b5340c0 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 @@ -103,23 +103,64 @@ table 30122 "Shpfy Order Tax Line" end; end; + internal procedure ImportFromJson(ParentId: BigInteger; JTaxLines: JsonArray) + var + OrderTaxLine: Record "Shpfy Order Tax Line"; + JsonHelper: Codeunit "Shpfy Json Helper"; + RecordRef: RecordRef; + JToken: JsonToken; + begin + OrderTaxLine.SetRange("Parent Id", ParentId); + if not OrderTaxLine.IsEmpty() then + OrderTaxLine.DeleteAll(false); + foreach JToken in JTaxLines do begin + RecordRef.Open(Database::"Shpfy Order Tax Line"); + RecordRef.Init(); + RecordRef.Field(OrderTaxLine.FieldNo("Parent Id")).Value := ParentId; + JsonHelper.GetValueIntoField(JToken, 'title', RecordRef, OrderTaxLine.FieldNo(Title)); + JsonHelper.GetValueIntoField(JToken, 'rate', RecordRef, OrderTaxLine.FieldNo(Rate)); + 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; + end; + local procedure OrderCurrencyCode(): Code[10] var OrderHeader: Record "Shpfy Order Header"; OrderLine: Record "Shpfy Order Line"; + OrderShippingCharges: Record "Shpfy Order Shipping Charges"; begin - if OrderLine.Get("Parent Id") then + if OrderLine.Get("Parent Id") then begin if OrderHeader.Get(OrderLine."Shopify Order Id") then exit(OrderHeader."Currency Code"); + end else + if OrderShippingCharges.Get("Parent Id") then begin + if OrderHeader.Get(OrderShippingCharges."Shopify Order Id") then + exit(OrderHeader."Currency Code"); + end else + if OrderHeader.Get("Parent Id") then + exit(OrderHeader."Currency Code"); end; local procedure OrderPresentmentCurrencyCode(): Code[10] var OrderHeader: Record "Shpfy Order Header"; OrderLine: Record "Shpfy Order Line"; + OrderShippingCharges: Record "Shpfy Order Shipping Charges"; begin - if OrderLine.Get("Parent Id") then + if OrderLine.Get("Parent Id") then begin if OrderHeader.Get(OrderLine."Shopify Order Id") then exit(OrderHeader."Presentment Currency Code"); + end else + if OrderShippingCharges.Get("Parent Id") then begin + if OrderHeader.Get(OrderShippingCharges."Shopify Order Id") then + exit(OrderHeader."Presentment Currency Code"); + end else + if OrderHeader.Get("Parent Id") then + exit(OrderHeader."Presentment Currency Code"); end; } \ No newline at end of file diff --git a/src/Apps/W1/Shopify/App/src/Shipping/Codeunits/ShpfyShippingCharges.Codeunit.al b/src/Apps/W1/Shopify/App/src/Shipping/Codeunits/ShpfyShippingCharges.Codeunit.al index e4f7f64c61..73404ce556 100644 --- a/src/Apps/W1/Shopify/App/src/Shipping/Codeunits/ShpfyShippingCharges.Codeunit.al +++ b/src/Apps/W1/Shopify/App/src/Shipping/Codeunits/ShpfyShippingCharges.Codeunit.al @@ -53,6 +53,7 @@ codeunit 30191 "Shpfy Shipping Charges" OrderShippingCharges: Record "Shpfy Order Shipping Charges"; ShipmentMethodMapping: Record "Shpfy Shipment Method Mapping"; DataCapture: Record "Shpfy Data Capture"; + OrderTaxLine: Record "Shpfy Order Tax Line"; RecordRef: RecordRef; Id: BigInteger; JToken: JsonToken; @@ -83,6 +84,7 @@ codeunit 30191 "Shpfy Shipping Charges" else OrderShippingCharges.Modify(); RecordRef.Close(); + OrderTaxLine.ImportFromJson(OrderShippingCharges."Shopify Shipping Line Id", JsonHelper.GetJsonArray(JToken, 'taxLines')); DataCapture.Add(Database::"Shpfy Order Shipping Charges", OrderShippingCharges.SystemId, JToken); if not ShipmentMethodMapping.Get(OrderHeader."Shop Code", OrderShippingCharges.Title) then begin Clear(ShipmentMethodMapping); diff --git a/src/Apps/W1/Shopify/App/src/Shipping/Pages/ShpfyOrderShippingCharges.Page.al b/src/Apps/W1/Shopify/App/src/Shipping/Pages/ShpfyOrderShippingCharges.Page.al index 33c1b2b2cc..e51cb3e27c 100644 --- a/src/Apps/W1/Shopify/App/src/Shipping/Pages/ShpfyOrderShippingCharges.Page.al +++ b/src/Apps/W1/Shopify/App/src/Shipping/Pages/ShpfyOrderShippingCharges.Page.al @@ -16,7 +16,6 @@ page 30128 "Shpfy Order Shipping Charges" InsertAllowed = false; ModifyAllowed = false; PageType = List; - PromotedActionCategories = 'New,Process,Report,Inspect'; SourceTable = "Shpfy Order Shipping Charges"; UsageCategory = None; @@ -87,15 +86,26 @@ page 30128 "Shpfy Order Shipping Charges" { area(Processing) { + action(TaxLines) + { + ApplicationArea = All; + Caption = 'Tax Lines'; + Image = TaxDetail; + ToolTip = 'View the tax lines for the selected shipping charge.'; + + trigger OnAction(); + var + OrderTaxLine: Record "Shpfy Order Tax Line"; + begin + OrderTaxLine.SetRange("Parent Id", Rec."Shopify Shipping Line Id"); + Page.Run(Page::"Shpfy Order Tax Lines", OrderTaxLine); + end; + } action(RetrievedShopifyData) { ApplicationArea = All; Caption = 'Retrieved Shopify Data'; Image = Entry; - Promoted = true; - PromotedCategory = Category4; - PromotedIsBig = true; - PromotedOnly = true; ToolTip = 'View the data retrieved from Shopify.'; trigger OnAction(); @@ -109,6 +119,11 @@ page 30128 "Shpfy Order Shipping Charges" end; } } + area(Promoted) + { + actionref(TaxLines_Promoted; TaxLines) { } + actionref(RetrievedShopifyData_Promoted; RetrievedShopifyData) { } + } } var diff --git a/src/Apps/W1/Shopify/App/src/Shipping/Tables/ShpfyOrderShippingCharges.Table.al b/src/Apps/W1/Shopify/App/src/Shipping/Tables/ShpfyOrderShippingCharges.Table.al index 2c51fd79f0..12e9e0fd96 100644 --- a/src/Apps/W1/Shopify/App/src/Shipping/Tables/ShpfyOrderShippingCharges.Table.al +++ b/src/Apps/W1/Shopify/App/src/Shipping/Tables/ShpfyOrderShippingCharges.Table.al @@ -91,8 +91,13 @@ table 30130 "Shpfy Order Shipping Charges" trigger OnDelete() var + OrderTaxLine: Record "Shpfy Order Tax Line"; DataCapture: Record "Shpfy Data Capture"; begin + OrderTaxLine.SetRange("Parent Id", Rec."Shopify Shipping Line Id"); + if not OrderTaxLine.IsEmpty() then + OrderTaxLine.DeleteAll(false); + DataCapture.SetRange("Linked To Table", Database::"Shpfy Order Shipping Charges"); DataCapture.SetRange("Linked To Id", Rec.SystemId); if not DataCapture.IsEmpty then diff --git a/src/Apps/W1/Shopify/Test/Shipping/ShpfyShippingChargesTest.Codeunit.al b/src/Apps/W1/Shopify/Test/Shipping/ShpfyShippingChargesTest.Codeunit.al index f32abf44c8..0db27930bb 100644 --- a/src/Apps/W1/Shopify/Test/Shipping/ShpfyShippingChargesTest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Shipping/ShpfyShippingChargesTest.Codeunit.al @@ -33,6 +33,7 @@ codeunit 139546 "Shpfy Shipping Charges Test" CommunicationMgt: Codeunit "Shpfy Communication Mgt."; InitializeTest: Codeunit "Shpfy Initialize Test"; IsInitialized: Boolean; + ShippingLineGIdTok: Label 'gid://shopify/ShippingLine/%1', Locked = true, Comment = '%1 = Shipping line id'; trigger OnRun() begin @@ -353,6 +354,209 @@ codeunit 139546 "Shpfy Shipping Charges Test" ShpfyShipmentMethodMapping."Shipping Charges No." ); end; + + [Test] + [HandlerFunctions('ShippingChargesHttpHandler')] + procedure UnitTestImportShippingTaxLineWithChannelLiableTrue() + var + OrderHeader: Record "Shpfy Order Header"; + OrderTaxLine: Record "Shpfy Order Tax Line"; + ShippingCharges: Codeunit "Shpfy Shipping Charges"; + JShippingLines: JsonArray; + JTaxLines: JsonArray; + ShippingLineId: BigInteger; + ChannelLiableScenario: Option Missing,TrueValue,FalseValue,NullValue; + begin + // [SCENARIO] The tax line of a shipping line is persisted against the shipping charge with all values preserved and Channel Liable = true. + Initialize(); + + // [GIVEN] Shopify Shop and an imported Shopify order + Shop := CommunicationMgt.GetShopRecord(); + CreateImportedOrderHeader(OrderHeader); + + // [GIVEN] A shipping line with a single tax line where channelLiable is true + ShippingLineId := LibraryRandom.RandInt(100000); + Clear(JTaxLines); + AddTaxLineToArray(JTaxLines, 'TAX1', ChannelLiableScenario::TrueValue); + AddShippingLine(JShippingLines, ShippingLineId, JTaxLines); + + // [WHEN] Shipping cost infos are imported + ShippingCharges.UpdateShippingCostInfos(OrderHeader, JShippingLines); + + // [THEN] A single tax line is stored against the shipping charge with all values preserved + OrderTaxLine.SetRange("Parent Id", ShippingLineId); + LibraryAssert.AreEqual(1, OrderTaxLine.Count(), 'Exactly one shipping tax line must be stored.'); + OrderTaxLine.FindFirst(); + LibraryAssert.AreEqual('TAX1', OrderTaxLine.Title, 'Shipping tax line title must be preserved.'); + LibraryAssert.AreEqual(0.1, OrderTaxLine.Rate, 'Shipping tax line rate must be preserved.'); + LibraryAssert.AreEqual(10, OrderTaxLine."Rate %", 'Shipping tax line rate percentage must be preserved.'); + LibraryAssert.AreEqual(5, OrderTaxLine.Amount, 'Shipping tax line shop amount must be preserved.'); + LibraryAssert.AreEqual(6, OrderTaxLine."Presentment Amount", 'Shipping tax line presentment amount must be preserved.'); + LibraryAssert.IsTrue(OrderTaxLine."Channel Liable", 'Shipping tax line Channel Liable must be true.'); + end; + + [Test] + [HandlerFunctions('ShippingChargesHttpHandler')] + procedure UnitTestImportShippingTaxLineWithChannelLiableFalse() + var + ChannelLiableScenario: Option Missing,TrueValue,FalseValue,NullValue; + begin + // [SCENARIO] The Channel Liable flag of a shipping tax line is imported as false when explicitly set to false. + VerifyShippingTaxLineChannelLiable(ChannelLiableScenario::FalseValue, false); + end; + + [Test] + [HandlerFunctions('ShippingChargesHttpHandler')] + procedure UnitTestImportShippingTaxLineWithChannelLiableNull() + var + ChannelLiableScenario: Option Missing,TrueValue,FalseValue,NullValue; + begin + // [SCENARIO] A null channelLiable on a shipping tax line defaults to false. + VerifyShippingTaxLineChannelLiable(ChannelLiableScenario::NullValue, false); + end; + + [Test] + [HandlerFunctions('ShippingChargesHttpHandler')] + procedure UnitTestImportShippingTaxLineWithChannelLiableMissing() + var + ChannelLiableScenario: Option Missing,TrueValue,FalseValue,NullValue; + begin + // [SCENARIO] A missing channelLiable on a shipping tax line defaults to false. + VerifyShippingTaxLineChannelLiable(ChannelLiableScenario::Missing, false); + end; + + [Test] + [HandlerFunctions('ShippingChargesHttpHandler')] + procedure UnitTestImportShippingTaxLinesForMultipleShippingLinesAreSeparated() + var + OrderHeader: Record "Shpfy Order Header"; + OrderTaxLine: Record "Shpfy Order Tax Line"; + ShippingCharges: Codeunit "Shpfy Shipping Charges"; + JShippingLines: JsonArray; + JFirstTaxLines: JsonArray; + JSecondTaxLines: JsonArray; + FirstShippingLineId: BigInteger; + SecondShippingLineId: BigInteger; + ChannelLiableScenario: Option Missing,TrueValue,FalseValue,NullValue; + begin + // [SCENARIO] Tax lines of multiple shipping lines are kept separated per shipping charge. + Initialize(); + + // [GIVEN] Shopify Shop and an imported Shopify order + Shop := CommunicationMgt.GetShopRecord(); + CreateImportedOrderHeader(OrderHeader); + + // [GIVEN] Two shipping lines: the first with two tax lines, the second with one + FirstShippingLineId := LibraryRandom.RandInt(100000); + SecondShippingLineId := LibraryRandom.RandInt(100000); + + AddTaxLineToArray(JFirstTaxLines, 'TAX1', ChannelLiableScenario::TrueValue); + AddTaxLineToArray(JFirstTaxLines, 'TAX2', ChannelLiableScenario::FalseValue); + AddShippingLine(JShippingLines, FirstShippingLineId, JFirstTaxLines); + + AddTaxLineToArray(JSecondTaxLines, 'TAX3', ChannelLiableScenario::TrueValue); + AddShippingLine(JShippingLines, SecondShippingLineId, JSecondTaxLines); + + // [WHEN] Shipping cost infos are imported + ShippingCharges.UpdateShippingCostInfos(OrderHeader, JShippingLines); + + // [THEN] Each shipping charge keeps its own tax lines + OrderTaxLine.SetRange("Parent Id", FirstShippingLineId); + LibraryAssert.AreEqual(2, OrderTaxLine.Count(), 'First shipping line must keep both tax lines.'); + OrderTaxLine.SetRange("Parent Id", SecondShippingLineId); + LibraryAssert.AreEqual(1, OrderTaxLine.Count(), 'Second shipping line must keep its single tax line.'); + end; + + [Test] + [HandlerFunctions('ShippingChargesHttpHandler')] + procedure UnitTestReimportShippingTaxLinesReplacesWithoutDuplicates() + var + OrderHeader: Record "Shpfy Order Header"; + OrderTaxLine: Record "Shpfy Order Tax Line"; + ShippingCharges: Codeunit "Shpfy Shipping Charges"; + JShippingLines: JsonArray; + JTaxLines: JsonArray; + ShippingLineId: BigInteger; + ChannelLiableScenario: Option Missing,TrueValue,FalseValue,NullValue; + begin + // [SCENARIO] Reimporting the same shipping line replaces its tax lines without duplicates or stale rows. + Initialize(); + + // [GIVEN] Shopify Shop and an imported Shopify order + Shop := CommunicationMgt.GetShopRecord(); + CreateImportedOrderHeader(OrderHeader); + + // [GIVEN] A shipping line with two tax lines is imported + ShippingLineId := LibraryRandom.RandInt(100000); + Clear(JTaxLines); + AddTaxLineToArray(JTaxLines, 'TAX1', ChannelLiableScenario::TrueValue); + AddTaxLineToArray(JTaxLines, 'TAX2', ChannelLiableScenario::FalseValue); + AddShippingLine(JShippingLines, ShippingLineId, JTaxLines); + ShippingCharges.UpdateShippingCostInfos(OrderHeader, JShippingLines); + + OrderTaxLine.SetRange("Parent Id", ShippingLineId); + LibraryAssert.AreEqual(2, OrderTaxLine.Count(), 'Two tax lines must be stored after the first import.'); + + // [WHEN] The same shipping line is reimported with a single tax line + Clear(JShippingLines); + Clear(JTaxLines); + AddTaxLineToArray(JTaxLines, 'TAX1', ChannelLiableScenario::TrueValue); + AddShippingLine(JShippingLines, ShippingLineId, JTaxLines); + ShippingCharges.UpdateShippingCostInfos(OrderHeader, JShippingLines); + + // [THEN] The stale tax line is removed and no duplicates remain + OrderTaxLine.SetRange("Parent Id", ShippingLineId); + LibraryAssert.AreEqual(1, OrderTaxLine.Count(), 'Reimport must replace the tax lines without duplicates or stale rows.'); + OrderTaxLine.FindFirst(); + LibraryAssert.AreEqual('TAX1', OrderTaxLine.Title, 'The remaining tax line must be the reimported one.'); + end; + + [Test] + [HandlerFunctions('ShippingChargesHttpHandler')] + procedure UnitTestShippingTaxLinesRemovedWhenShippingLineRemoved() + var + OrderHeader: Record "Shpfy Order Header"; + OrderShippingCharges: Record "Shpfy Order Shipping Charges"; + OrderTaxLine: Record "Shpfy Order Tax Line"; + ShippingCharges: Codeunit "Shpfy Shipping Charges"; + JShippingLines: JsonArray; + JFirstTaxLines: JsonArray; + JSecondTaxLines: JsonArray; + JReimportShippingLines: JsonArray; + JReimportTaxLines: JsonArray; + FirstShippingLineId: BigInteger; + SecondShippingLineId: BigInteger; + ChannelLiableScenario: Option Missing,TrueValue,FalseValue,NullValue; + begin + // [SCENARIO] When a shipping line is no longer returned by Shopify, its shipping charge and tax lines are removed. + Initialize(); + + // [GIVEN] Shopify Shop and an imported Shopify order + Shop := CommunicationMgt.GetShopRecord(); + CreateImportedOrderHeader(OrderHeader); + + // [GIVEN] Two shipping lines, each with a tax line, are imported + FirstShippingLineId := LibraryRandom.RandInt(100000); + SecondShippingLineId := LibraryRandom.RandInt(100000); + + AddTaxLineToArray(JFirstTaxLines, 'TAX1', ChannelLiableScenario::TrueValue); + AddShippingLine(JShippingLines, FirstShippingLineId, JFirstTaxLines); + AddTaxLineToArray(JSecondTaxLines, 'TAX2', ChannelLiableScenario::FalseValue); + AddShippingLine(JShippingLines, SecondShippingLineId, JSecondTaxLines); + ShippingCharges.UpdateShippingCostInfos(OrderHeader, JShippingLines); + + // [WHEN] Only the first shipping line is returned on reimport + AddTaxLineToArray(JReimportTaxLines, 'TAX1', ChannelLiableScenario::TrueValue); + AddShippingLine(JReimportShippingLines, FirstShippingLineId, JReimportTaxLines); + ShippingCharges.UpdateShippingCostInfos(OrderHeader, JReimportShippingLines); + + // [THEN] The removed shipping charge and its tax lines are deleted + LibraryAssert.IsFalse(OrderShippingCharges.Get(SecondShippingLineId), 'The removed shipping charge must be deleted.'); + OrderTaxLine.SetRange("Parent Id", SecondShippingLineId); + LibraryAssert.AreEqual(0, OrderTaxLine.Count(), 'Tax lines of the removed shipping line must be deleted.'); + OrderTaxLine.SetRange("Parent Id", FirstShippingLineId); + LibraryAssert.AreEqual(1, OrderTaxLine.Count(), 'The remaining shipping line must keep its tax line.'); + end; #endregion [HttpClientHandler] @@ -467,6 +671,96 @@ codeunit 139546 "Shpfy Shipping Charges Test" OrderShippingCharges.Insert(true); end; + local procedure CreateImportedOrderHeader(var OrderHeader: Record "Shpfy Order Header") + var + ImportOrder: Codeunit "Shpfy Import Order"; + begin + ImportOrder.SetShop(Shop.Code); + ImportShopifyOrder(Shop, OrderHeader, ImportOrder, false); + end; + + local procedure VerifyShippingTaxLineChannelLiable(ChannelLiableScenario: Option Missing,TrueValue,FalseValue,NullValue; ExpectedChannelLiable: Boolean) + var + OrderHeader: Record "Shpfy Order Header"; + OrderTaxLine: Record "Shpfy Order Tax Line"; + ShippingCharges: Codeunit "Shpfy Shipping Charges"; + JShippingLines: JsonArray; + JTaxLines: JsonArray; + ShippingLineId: BigInteger; + begin + Initialize(); + + // [GIVEN] Shopify Shop and an imported Shopify order + Shop := CommunicationMgt.GetShopRecord(); + CreateImportedOrderHeader(OrderHeader); + + // [GIVEN] A shipping line with a single tax line for the given channelLiable scenario + ShippingLineId := LibraryRandom.RandInt(100000); + Clear(JTaxLines); + AddTaxLineToArray(JTaxLines, 'TAX1', ChannelLiableScenario); + AddShippingLine(JShippingLines, ShippingLineId, JTaxLines); + + // [WHEN] Shipping cost infos are imported + ShippingCharges.UpdateShippingCostInfos(OrderHeader, JShippingLines); + + // [THEN] A tax line is stored with the expected Channel Liable value + OrderTaxLine.SetRange("Parent Id", ShippingLineId); + LibraryAssert.AreEqual(1, OrderTaxLine.Count(), 'Exactly one shipping tax line must be stored.'); + OrderTaxLine.FindFirst(); + LibraryAssert.AreEqual(ExpectedChannelLiable, OrderTaxLine."Channel Liable", 'Shipping tax line Channel Liable is not as expected.'); + end; + + local procedure AddShippingLine(var JShippingLines: JsonArray; ShippingLineId: BigInteger; JTaxLines: JsonArray) + var + JShippingLine: JsonObject; + JPriceSet: JsonObject; + JShopMoney: JsonObject; + JPresentmentMoney: JsonObject; + begin + JShippingLine.Add('id', StrSubstNo(ShippingLineGIdTok, ShippingLineId)); + JShippingLine.Add('title', StrSubstNo('Shipping %1', ShippingLineId)); + JShippingLine.Add('code', 'STD'); + JShippingLine.Add('source', 'shopify'); + JShopMoney.Add('amount', '20'); + JPriceSet.Add('shopMoney', JShopMoney); + JPresentmentMoney.Add('amount', '24'); + JPriceSet.Add('presentmentMoney', JPresentmentMoney); + JShippingLine.Add('originalPriceSet', JPriceSet); + JShippingLine.Add('taxLines', JTaxLines); + JShippingLines.Add(JShippingLine); + end; + + local procedure AddTaxLineToArray(var JTaxLines: JsonArray; Title: Text; ChannelLiableScenario: Option Missing,TrueValue,FalseValue,NullValue) + var + JTaxLine: JsonObject; + JPriceSet: JsonObject; + JShopMoney: JsonObject; + JPresentmentMoney: JsonObject; + JNull: JsonValue; + begin + JTaxLine.Add('title', Title); + JTaxLine.Add('rate', 0.1); + JTaxLine.Add('ratePercentage', 10); + JShopMoney.Add('amount', '5'); + JPriceSet.Add('shopMoney', JShopMoney); + JPresentmentMoney.Add('amount', '6'); + JPriceSet.Add('presentmentMoney', JPresentmentMoney); + JTaxLine.Add('priceSet', JPriceSet); + case ChannelLiableScenario of + ChannelLiableScenario::TrueValue: + JTaxLine.Add('channelLiable', true); + ChannelLiableScenario::FalseValue: + JTaxLine.Add('channelLiable', false); + ChannelLiableScenario::NullValue: + begin + JNull.SetValueToNull(); + JTaxLine.Add('channelLiable', JNull); + end; + // Missing: channelLiable is intentionally not added + end; + JTaxLines.Add(JTaxLine); + end; + local procedure CreateGLAccount(var GLAccount: Record "G/L Account") var VATPostingSetup: Record "VAT Posting Setup";