From e50728d860957ebd3dbdcc574140128aa84cb785 Mon Sep 17 00:00:00 2001
From: Onat Buyukakkus <55088871+onbuyuka@users.noreply.github.com>
Date: Thu, 16 Jul 2026 14:46:07 +0200
Subject: [PATCH 1/6] [Shopify] Store tax lines linked to order shipping
charges
Shopify shipping GraphQL queries already retrieve shippingLines.taxLines
(channelLiable, title, rate, ratePercentage, priceSet), but the shipping
import did not persist them, so shipping tax details and Channel Liable
status were only available in raw Retrieved Shopify Data.
Shipping-line tax lines are now imported into the existing "Shpfy Order Tax
Line" table, linked to the shipping charge via Parent Id = shipping line Id,
and can be opened from the Shipping Costs page via a new Tax Lines action.
- ShpfyShippingCharges: parse and persist each shipping line's tax lines
(delete-then-recreate for clean reimport, no duplicates or stale rows).
- ShpfyOrderShippingCharges.Table: OnDelete removes linked tax lines.
- ShpfyOrderTaxLine.Table: currency-code AutoFormat resolves via shipping charges.
- ShpfyOrderShippingCharges.Page: new promoted Tax Lines action (modern area(Promoted)).
- ShpfyOrderTaxLines.Page: presentment-currency visibility resolves via shipping charges.
- Tests cover channelLiable true/false/null/missing, multiple shipping lines,
reimport dedupe, and removal cleanup.
Fixes AB#642379
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c81e7b74-3793-4602-82ae-e74c7ceab730
---
.../Pages/ShpfyOrderTaxLines.Page.al | 15 +-
.../Tables/ShpfyOrderTaxLine.Table.al | 14 +-
.../ShpfyShippingCharges.Codeunit.al | 25 ++
.../Pages/ShpfyOrderShippingCharges.Page.al | 25 +-
.../Tables/ShpfyOrderShippingCharges.Table.al | 5 +
.../ShpfyShippingChargesTest.Codeunit.al | 287 ++++++++++++++++++
6 files changed, 360 insertions(+), 11 deletions(-)
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..d653794533 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
@@ -107,19 +107,29 @@ table 30122 "Shpfy Order Tax Line"
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
+ if OrderHeader.Get(OrderShippingCharges."Shopify Order 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
+ if OrderHeader.Get(OrderShippingCharges."Shopify Order 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..10bbbb0291 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
@@ -83,6 +83,7 @@ codeunit 30191 "Shpfy Shipping Charges"
else
OrderShippingCharges.Modify();
RecordRef.Close();
+ AddTaxLines(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);
@@ -120,4 +121,28 @@ codeunit 30191 "Shpfy Shipping Charges"
foreach JAllocationAmountSet in JDiscountAllocations do
Result += JsonHelper.GetValueAsDecimal(JAllocationAmountSet, StrSubstNo(amountLbl, MoneyType));
end;
+
+ 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;
}
\ No newline at end of file
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..473531ee16 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,202 @@ codeunit 139546 "Shpfy Shipping Charges Test"
ShpfyShipmentMethodMapping."Shipping Charges No."
);
end;
+
+ [Test]
+ 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 a Shopify order header
+ Shop := CommunicationMgt.GetShopRecord();
+ CreateOrderHeaderForShop(OrderHeader);
+
+ // [GIVEN] A shipping line with a single tax line where channelLiable is true
+ ShippingLineId := LibraryRandom.RandIntInRange(100000, 199999);
+ 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]
+ 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]
+ 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]
+ 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]
+ 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 a Shopify order header
+ Shop := CommunicationMgt.GetShopRecord();
+ CreateOrderHeaderForShop(OrderHeader);
+
+ // [GIVEN] Two shipping lines: the first with two tax lines, the second with one
+ FirstShippingLineId := LibraryRandom.RandIntInRange(200000, 299999);
+ SecondShippingLineId := LibraryRandom.RandIntInRange(300000, 399999);
+
+ 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]
+ 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 a Shopify order header
+ Shop := CommunicationMgt.GetShopRecord();
+ CreateOrderHeaderForShop(OrderHeader);
+
+ // [GIVEN] A shipping line with two tax lines is imported
+ ShippingLineId := LibraryRandom.RandIntInRange(400000, 499999);
+ 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]
+ 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 a Shopify order header
+ Shop := CommunicationMgt.GetShopRecord();
+ CreateOrderHeaderForShop(OrderHeader);
+
+ // [GIVEN] Two shipping lines, each with a tax line, are imported
+ FirstShippingLineId := LibraryRandom.RandIntInRange(500000, 599999);
+ SecondShippingLineId := LibraryRandom.RandIntInRange(600000, 699999);
+
+ 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 +664,96 @@ codeunit 139546 "Shpfy Shipping Charges Test"
OrderShippingCharges.Insert(true);
end;
+ local procedure CreateOrderHeaderForShop(var OrderHeader: Record "Shpfy Order Header")
+ begin
+ Clear(OrderHeader);
+ OrderHeader."Shopify Order Id" := LibraryRandom.RandIntInRange(700000, 999999);
+ OrderHeader."Shop Code" := Shop.Code;
+ OrderHeader.Insert();
+ 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 a Shopify order header
+ Shop := CommunicationMgt.GetShopRecord();
+ CreateOrderHeaderForShop(OrderHeader);
+
+ // [GIVEN] A shipping line with a single tax line for the given channelLiable scenario
+ ShippingLineId := LibraryRandom.RandIntInRange(100000, 199999);
+ 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";
From c6fee210a8c5196a8b44c2063442afe8b107c8d6 Mon Sep 17 00:00:00 2001
From: Onat Buyukakkus <55088871+onbuyuka@users.noreply.github.com>
Date: Thu, 16 Jul 2026 15:23:57 +0200
Subject: [PATCH 2/6] Resolve currency code for order-level tax lines in
AutoFormat
OrderCurrencyCode/OrderPresentmentCurrencyCode now fall back to
OrderHeader.Get("Parent Id") for order-level tax lines (Parent Id =
Shopify Order Id), consistent with the Shpfy Order Tax Lines page
presentment-visibility trigger. Without this, order-level tax line
amounts rendered without currency formatting.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c81e7b74-3793-4602-82ae-e74c7ceab730
---
.../Order handling/Tables/ShpfyOrderTaxLine.Table.al | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
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 d653794533..ec0c1ee6a5 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
@@ -113,9 +113,12 @@ table 30122 "Shpfy Order Tax Line"
if OrderHeader.Get(OrderLine."Shopify Order Id") then
exit(OrderHeader."Currency Code");
end else
- if OrderShippingCharges.Get("Parent Id") then
+ 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]
@@ -128,8 +131,11 @@ table 30122 "Shpfy Order Tax Line"
if OrderHeader.Get(OrderLine."Shopify Order Id") then
exit(OrderHeader."Presentment Currency Code");
end else
- if OrderShippingCharges.Get("Parent Id") then
+ 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
From 2e4d34fd4e9477e1173e94332ab274b94dc3af9e Mon Sep 17 00:00:00 2001
From: Onat Buyukakkus <55088871+onbuyuka@users.noreply.github.com>
Date: Thu, 16 Jul 2026 22:31:00 +0200
Subject: [PATCH 3/6] Fix order-id collisions in shipping tax line tests
The new tests generated the Shopify Order Id via LibraryRandom.RandIntInRange,
which is deterministic, so every test produced the same id (774600). Under the
CI codeunit-level test isolation, records are not rolled back between methods
within a codeunit, so the second test onward failed inserting a duplicate
Order Header.
Route all order-id and shipping-line-id generation through a persistent
GetUniqueId() counter so each test uses globally unique ids within the run.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c81e7b74-3793-4602-82ae-e74c7ceab730
---
.../ShpfyShippingChargesTest.Codeunit.al | 25 +++++++++++++------
1 file changed, 17 insertions(+), 8 deletions(-)
diff --git a/src/Apps/W1/Shopify/Test/Shipping/ShpfyShippingChargesTest.Codeunit.al b/src/Apps/W1/Shopify/Test/Shipping/ShpfyShippingChargesTest.Codeunit.al
index 473531ee16..33f2fb78f9 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;
+ NextUniqueId: BigInteger;
ShippingLineGIdTok: Label 'gid://shopify/ShippingLine/%1', Locked = true, Comment = '%1 = Shipping line id';
trigger OnRun()
@@ -374,7 +375,7 @@ codeunit 139546 "Shpfy Shipping Charges Test"
CreateOrderHeaderForShop(OrderHeader);
// [GIVEN] A shipping line with a single tax line where channelLiable is true
- ShippingLineId := LibraryRandom.RandIntInRange(100000, 199999);
+ ShippingLineId := GetUniqueId();
Clear(JTaxLines);
AddTaxLineToArray(JTaxLines, 'TAX1', ChannelLiableScenario::TrueValue);
AddShippingLine(JShippingLines, ShippingLineId, JTaxLines);
@@ -442,8 +443,8 @@ codeunit 139546 "Shpfy Shipping Charges Test"
CreateOrderHeaderForShop(OrderHeader);
// [GIVEN] Two shipping lines: the first with two tax lines, the second with one
- FirstShippingLineId := LibraryRandom.RandIntInRange(200000, 299999);
- SecondShippingLineId := LibraryRandom.RandIntInRange(300000, 399999);
+ FirstShippingLineId := GetUniqueId();
+ SecondShippingLineId := GetUniqueId();
AddTaxLineToArray(JFirstTaxLines, 'TAX1', ChannelLiableScenario::TrueValue);
AddTaxLineToArray(JFirstTaxLines, 'TAX2', ChannelLiableScenario::FalseValue);
@@ -481,7 +482,7 @@ codeunit 139546 "Shpfy Shipping Charges Test"
CreateOrderHeaderForShop(OrderHeader);
// [GIVEN] A shipping line with two tax lines is imported
- ShippingLineId := LibraryRandom.RandIntInRange(400000, 499999);
+ ShippingLineId := GetUniqueId();
Clear(JTaxLines);
AddTaxLineToArray(JTaxLines, 'TAX1', ChannelLiableScenario::TrueValue);
AddTaxLineToArray(JTaxLines, 'TAX2', ChannelLiableScenario::FalseValue);
@@ -529,8 +530,8 @@ codeunit 139546 "Shpfy Shipping Charges Test"
CreateOrderHeaderForShop(OrderHeader);
// [GIVEN] Two shipping lines, each with a tax line, are imported
- FirstShippingLineId := LibraryRandom.RandIntInRange(500000, 599999);
- SecondShippingLineId := LibraryRandom.RandIntInRange(600000, 699999);
+ FirstShippingLineId := GetUniqueId();
+ SecondShippingLineId := GetUniqueId();
AddTaxLineToArray(JFirstTaxLines, 'TAX1', ChannelLiableScenario::TrueValue);
AddShippingLine(JShippingLines, FirstShippingLineId, JFirstTaxLines);
@@ -667,11 +668,19 @@ codeunit 139546 "Shpfy Shipping Charges Test"
local procedure CreateOrderHeaderForShop(var OrderHeader: Record "Shpfy Order Header")
begin
Clear(OrderHeader);
- OrderHeader."Shopify Order Id" := LibraryRandom.RandIntInRange(700000, 999999);
+ OrderHeader."Shopify Order Id" := GetUniqueId();
OrderHeader."Shop Code" := Shop.Code;
OrderHeader.Insert();
end;
+ local procedure GetUniqueId(): BigInteger
+ begin
+ if NextUniqueId = 0 then
+ NextUniqueId := 100000;
+ NextUniqueId += 1;
+ exit(NextUniqueId);
+ end;
+
local procedure VerifyShippingTaxLineChannelLiable(ChannelLiableScenario: Option Missing,TrueValue,FalseValue,NullValue; ExpectedChannelLiable: Boolean)
var
OrderHeader: Record "Shpfy Order Header";
@@ -688,7 +697,7 @@ codeunit 139546 "Shpfy Shipping Charges Test"
CreateOrderHeaderForShop(OrderHeader);
// [GIVEN] A shipping line with a single tax line for the given channelLiable scenario
- ShippingLineId := LibraryRandom.RandIntInRange(100000, 199999);
+ ShippingLineId := GetUniqueId();
Clear(JTaxLines);
AddTaxLineToArray(JTaxLines, 'TAX1', ChannelLiableScenario);
AddShippingLine(JShippingLines, ShippingLineId, JTaxLines);
From b93bc93f21cf734a71c112bf728e4ee4aafc8e08 Mon Sep 17 00:00:00 2001
From: Onat Buyukakkus <55088871+onbuyuka@users.noreply.github.com>
Date: Thu, 16 Jul 2026 23:29:33 +0200
Subject: [PATCH 4/6] Address PR review: use ImportShopifyOrder fixture;
DeleteAll(false)
Tests: replace the hand-rolled order-header fixture and unique-id counter
with the file's established ImportShopifyOrder helper (idempotent upsert
that respects Validate) and LibraryRandom-based shipping-line ids, matching
the CreateOrderShippingCharges convention. Added the ShippingChargesHttpHandler
to each new test since the import path is HTTP-backed.
App: AddTaxLines now uses DeleteAll(false); "Shpfy Order Tax Line" has no
OnDelete trigger, consistent with the sibling cleanup in
ShpfyOrderShippingCharges.Table.al added in this PR.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c81e7b74-3793-4602-82ae-e74c7ceab730
---
.../ShpfyShippingCharges.Codeunit.al | 2 +-
.../ShpfyShippingChargesTest.Codeunit.al | 59 +++++++++----------
2 files changed, 29 insertions(+), 32 deletions(-)
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 10bbbb0291..903bfadf9c 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
@@ -130,7 +130,7 @@ codeunit 30191 "Shpfy Shipping Charges"
begin
OrderTaxLine.SetRange("Parent Id", ParentId);
if not OrderTaxLine.IsEmpty() then
- OrderTaxLine.DeleteAll();
+ OrderTaxLine.DeleteAll(false);
foreach JToken in JTaxLines do begin
RecordRef.Open(Database::"Shpfy Order Tax Line");
RecordRef.Init();
diff --git a/src/Apps/W1/Shopify/Test/Shipping/ShpfyShippingChargesTest.Codeunit.al b/src/Apps/W1/Shopify/Test/Shipping/ShpfyShippingChargesTest.Codeunit.al
index 33f2fb78f9..55b7a47a36 100644
--- a/src/Apps/W1/Shopify/Test/Shipping/ShpfyShippingChargesTest.Codeunit.al
+++ b/src/Apps/W1/Shopify/Test/Shipping/ShpfyShippingChargesTest.Codeunit.al
@@ -33,7 +33,6 @@ codeunit 139546 "Shpfy Shipping Charges Test"
CommunicationMgt: Codeunit "Shpfy Communication Mgt.";
InitializeTest: Codeunit "Shpfy Initialize Test";
IsInitialized: Boolean;
- NextUniqueId: BigInteger;
ShippingLineGIdTok: Label 'gid://shopify/ShippingLine/%1', Locked = true, Comment = '%1 = Shipping line id';
trigger OnRun()
@@ -357,6 +356,7 @@ codeunit 139546 "Shpfy Shipping Charges Test"
end;
[Test]
+ [HandlerFunctions('ShippingChargesHttpHandler')]
procedure UnitTestImportShippingTaxLineWithChannelLiableTrue()
var
OrderHeader: Record "Shpfy Order Header";
@@ -370,12 +370,12 @@ codeunit 139546 "Shpfy Shipping Charges Test"
// [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 a Shopify order header
+ // [GIVEN] Shopify Shop and an imported Shopify order
Shop := CommunicationMgt.GetShopRecord();
- CreateOrderHeaderForShop(OrderHeader);
+ CreateImportedOrderHeader(OrderHeader);
// [GIVEN] A shipping line with a single tax line where channelLiable is true
- ShippingLineId := GetUniqueId();
+ ShippingLineId := LibraryRandom.RandInt(100000);
Clear(JTaxLines);
AddTaxLineToArray(JTaxLines, 'TAX1', ChannelLiableScenario::TrueValue);
AddShippingLine(JShippingLines, ShippingLineId, JTaxLines);
@@ -396,6 +396,7 @@ codeunit 139546 "Shpfy Shipping Charges Test"
end;
[Test]
+ [HandlerFunctions('ShippingChargesHttpHandler')]
procedure UnitTestImportShippingTaxLineWithChannelLiableFalse()
var
ChannelLiableScenario: Option Missing,TrueValue,FalseValue,NullValue;
@@ -405,6 +406,7 @@ codeunit 139546 "Shpfy Shipping Charges Test"
end;
[Test]
+ [HandlerFunctions('ShippingChargesHttpHandler')]
procedure UnitTestImportShippingTaxLineWithChannelLiableNull()
var
ChannelLiableScenario: Option Missing,TrueValue,FalseValue,NullValue;
@@ -414,6 +416,7 @@ codeunit 139546 "Shpfy Shipping Charges Test"
end;
[Test]
+ [HandlerFunctions('ShippingChargesHttpHandler')]
procedure UnitTestImportShippingTaxLineWithChannelLiableMissing()
var
ChannelLiableScenario: Option Missing,TrueValue,FalseValue,NullValue;
@@ -423,6 +426,7 @@ codeunit 139546 "Shpfy Shipping Charges Test"
end;
[Test]
+ [HandlerFunctions('ShippingChargesHttpHandler')]
procedure UnitTestImportShippingTaxLinesForMultipleShippingLinesAreSeparated()
var
OrderHeader: Record "Shpfy Order Header";
@@ -438,13 +442,13 @@ codeunit 139546 "Shpfy Shipping Charges Test"
// [SCENARIO] Tax lines of multiple shipping lines are kept separated per shipping charge.
Initialize();
- // [GIVEN] Shopify Shop and a Shopify order header
+ // [GIVEN] Shopify Shop and an imported Shopify order
Shop := CommunicationMgt.GetShopRecord();
- CreateOrderHeaderForShop(OrderHeader);
+ CreateImportedOrderHeader(OrderHeader);
// [GIVEN] Two shipping lines: the first with two tax lines, the second with one
- FirstShippingLineId := GetUniqueId();
- SecondShippingLineId := GetUniqueId();
+ FirstShippingLineId := LibraryRandom.RandInt(100000);
+ SecondShippingLineId := LibraryRandom.RandInt(100000);
AddTaxLineToArray(JFirstTaxLines, 'TAX1', ChannelLiableScenario::TrueValue);
AddTaxLineToArray(JFirstTaxLines, 'TAX2', ChannelLiableScenario::FalseValue);
@@ -464,6 +468,7 @@ codeunit 139546 "Shpfy Shipping Charges Test"
end;
[Test]
+ [HandlerFunctions('ShippingChargesHttpHandler')]
procedure UnitTestReimportShippingTaxLinesReplacesWithoutDuplicates()
var
OrderHeader: Record "Shpfy Order Header";
@@ -477,12 +482,12 @@ codeunit 139546 "Shpfy Shipping Charges Test"
// [SCENARIO] Reimporting the same shipping line replaces its tax lines without duplicates or stale rows.
Initialize();
- // [GIVEN] Shopify Shop and a Shopify order header
+ // [GIVEN] Shopify Shop and an imported Shopify order
Shop := CommunicationMgt.GetShopRecord();
- CreateOrderHeaderForShop(OrderHeader);
+ CreateImportedOrderHeader(OrderHeader);
// [GIVEN] A shipping line with two tax lines is imported
- ShippingLineId := GetUniqueId();
+ ShippingLineId := LibraryRandom.RandInt(100000);
Clear(JTaxLines);
AddTaxLineToArray(JTaxLines, 'TAX1', ChannelLiableScenario::TrueValue);
AddTaxLineToArray(JTaxLines, 'TAX2', ChannelLiableScenario::FalseValue);
@@ -507,6 +512,7 @@ codeunit 139546 "Shpfy Shipping Charges Test"
end;
[Test]
+ [HandlerFunctions('ShippingChargesHttpHandler')]
procedure UnitTestShippingTaxLinesRemovedWhenShippingLineRemoved()
var
OrderHeader: Record "Shpfy Order Header";
@@ -525,13 +531,13 @@ codeunit 139546 "Shpfy Shipping Charges Test"
// [SCENARIO] When a shipping line is no longer returned by Shopify, its shipping charge and tax lines are removed.
Initialize();
- // [GIVEN] Shopify Shop and a Shopify order header
+ // [GIVEN] Shopify Shop and an imported Shopify order
Shop := CommunicationMgt.GetShopRecord();
- CreateOrderHeaderForShop(OrderHeader);
+ CreateImportedOrderHeader(OrderHeader);
// [GIVEN] Two shipping lines, each with a tax line, are imported
- FirstShippingLineId := GetUniqueId();
- SecondShippingLineId := GetUniqueId();
+ FirstShippingLineId := LibraryRandom.RandInt(100000);
+ SecondShippingLineId := LibraryRandom.RandInt(100000);
AddTaxLineToArray(JFirstTaxLines, 'TAX1', ChannelLiableScenario::TrueValue);
AddShippingLine(JShippingLines, FirstShippingLineId, JFirstTaxLines);
@@ -665,20 +671,11 @@ codeunit 139546 "Shpfy Shipping Charges Test"
OrderShippingCharges.Insert(true);
end;
- local procedure CreateOrderHeaderForShop(var OrderHeader: Record "Shpfy Order Header")
- begin
- Clear(OrderHeader);
- OrderHeader."Shopify Order Id" := GetUniqueId();
- OrderHeader."Shop Code" := Shop.Code;
- OrderHeader.Insert();
- end;
-
- local procedure GetUniqueId(): BigInteger
+ local procedure CreateImportedOrderHeader(var OrderHeader: Record "Shpfy Order Header")
+ var
+ ImportOrder: Codeunit "Shpfy Import Order";
begin
- if NextUniqueId = 0 then
- NextUniqueId := 100000;
- NextUniqueId += 1;
- exit(NextUniqueId);
+ ImportShopifyOrder(Shop, OrderHeader, ImportOrder, false);
end;
local procedure VerifyShippingTaxLineChannelLiable(ChannelLiableScenario: Option Missing,TrueValue,FalseValue,NullValue; ExpectedChannelLiable: Boolean)
@@ -692,12 +689,12 @@ codeunit 139546 "Shpfy Shipping Charges Test"
begin
Initialize();
- // [GIVEN] Shopify Shop and a Shopify order header
+ // [GIVEN] Shopify Shop and an imported Shopify order
Shop := CommunicationMgt.GetShopRecord();
- CreateOrderHeaderForShop(OrderHeader);
+ CreateImportedOrderHeader(OrderHeader);
// [GIVEN] A shipping line with a single tax line for the given channelLiable scenario
- ShippingLineId := GetUniqueId();
+ ShippingLineId := LibraryRandom.RandInt(100000);
Clear(JTaxLines);
AddTaxLineToArray(JTaxLines, 'TAX1', ChannelLiableScenario);
AddShippingLine(JShippingLines, ShippingLineId, JTaxLines);
From dcc10163d0a1f154680770cde80f9606353bb9c7 Mon Sep 17 00:00:00 2001
From: Onat Buyukakkus <55088871+onbuyuka@users.noreply.github.com>
Date: Thu, 16 Jul 2026 23:38:51 +0200
Subject: [PATCH 5/6] Deduplicate tax-line import into Shpfy Order Tax Line
table
Extract the shared "parse tax-line JSON, replace existing rows" logic into a
single internal procedure ImportFromJson on the "Shpfy Order Tax Line" table.
Both ShpfyImportOrder (order-header and order-line tax lines) and
ShpfyShippingCharges (shipping-line tax lines) now call it, removing the two
duplicate AddTaxLines copies so the mapping can no longer drift.
The shared method uses DeleteAll(false) (the table has no OnDelete trigger).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c81e7b74-3793-4602-82ae-e74c7ceab730
---
.../Codeunits/ShpfyImportOrder.Codeunit.al | 35 +++----------------
.../Tables/ShpfyOrderTaxLine.Table.al | 25 +++++++++++++
.../ShpfyShippingCharges.Codeunit.al | 27 ++------------
3 files changed, 31 insertions(+), 56 deletions(-)
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/Tables/ShpfyOrderTaxLine.Table.al b/src/Apps/W1/Shopify/App/src/Order handling/Tables/ShpfyOrderTaxLine.Table.al
index ec0c1ee6a5..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,6 +103,31 @@ 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";
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 903bfadf9c..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,7 +84,7 @@ codeunit 30191 "Shpfy Shipping Charges"
else
OrderShippingCharges.Modify();
RecordRef.Close();
- AddTaxLines(OrderShippingCharges."Shopify Shipping Line Id", JsonHelper.GetJsonArray(JToken, 'taxLines'));
+ 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);
@@ -121,28 +122,4 @@ codeunit 30191 "Shpfy Shipping Charges"
foreach JAllocationAmountSet in JDiscountAllocations do
Result += JsonHelper.GetValueAsDecimal(JAllocationAmountSet, StrSubstNo(amountLbl, MoneyType));
end;
-
- 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(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;
}
\ No newline at end of file
From 9096ef96d3f7a662abf04d462aa10757e60e9c6f Mon Sep 17 00:00:00 2001
From: Onat Buyukakkus <55088871+onbuyuka@users.noreply.github.com>
Date: Fri, 17 Jul 2026 15:23:55 +0200
Subject: [PATCH 6/6] Set shop on import order in shipping tax line test
fixture
CreateImportedOrderHeader created a fresh "Shpfy Import Order" codeunit but
never called SetShop, so the order import's SetAndCreateRelatedRecords invoked
the fulfillment/shipping sub-imports with a blank internal Shop, failing with
"Shopify Admin URL must have a value in Shopify Shop: Code=". Call
ImportOrder.SetShop(Shop.Code) first, matching the other tests in this codeunit.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c81e7b74-3793-4602-82ae-e74c7ceab730
---
.../Shopify/Test/Shipping/ShpfyShippingChargesTest.Codeunit.al | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/Apps/W1/Shopify/Test/Shipping/ShpfyShippingChargesTest.Codeunit.al b/src/Apps/W1/Shopify/Test/Shipping/ShpfyShippingChargesTest.Codeunit.al
index 55b7a47a36..0db27930bb 100644
--- a/src/Apps/W1/Shopify/Test/Shipping/ShpfyShippingChargesTest.Codeunit.al
+++ b/src/Apps/W1/Shopify/Test/Shipping/ShpfyShippingChargesTest.Codeunit.al
@@ -675,6 +675,7 @@ codeunit 139546 "Shpfy Shipping Charges Test"
var
ImportOrder: Codeunit "Shpfy Import Order";
begin
+ ImportOrder.SetShop(Shop.Code);
ImportShopifyOrder(Shop, OrderHeader, ImportOrder, false);
end;