Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,10 @@ codeunit 30178 "Shpfy Product Export"
UpdateProductVariant(ShopifyVariant, Item, ItemVariant, TempCurrVariant);
end;
until ShopifyVariant.Next() = 0;
// Re-fetch the parent product's item, as the loop above may have overwritten
// the Item variable with a child item from "Add Item as Shopify Variant".
if not Item.GetBySystemId(ShopifyProduct."Item SystemId") then
exit;
ItemVariant.SetRange("Item No.", Item."No.");
ItemUnitofMeasure.SetRange("Item No.", Item."No.");
if ItemVariant.FindSet(false) then
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"data":{"productUpdate":{"product":{"onlineStoreUrl":"","onlineStorePreviewUrl":"","updatedAt":"2026-01-01T00:00:00Z"},"userErrors":[]}}}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"data":{"productVariantsBulkUpdate":{"productVariants":[],"userErrors":[]}}}
152 changes: 152 additions & 0 deletions src/Apps/W1/Shopify/Test/Products/ShpfyCreateProductTest.Codeunit.al
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,17 @@ codeunit 139601 "Shpfy Create Product Test"
Subtype = Test;
TestType = IntegrationTest;
TestPermissions = Disabled;
TestHttpRequestPolicy = BlockOutboundRequests;


var
ExportShop: Record "Shpfy Shop";
Any: Codeunit Any;
LibraryAssert: Codeunit "Library Assert";
OutboundHttpRequests: Codeunit "Library - Variable Storage";
LibraryRandom: Codeunit "Library - Random";
ShpfyInitializeTest: Codeunit "Shpfy Initialize Test";
ExportIsInitialized: Boolean;

[Test]
procedure UnitTestCreateTempProductFromItem()
Expand Down Expand Up @@ -2958,4 +2964,150 @@ codeunit 139601 "Shpfy Create Product Test"

until TempShopifyVariant.Next() = 0;
end;

[Test]
[HandlerFunctions('ProductExportChildItemVariantHttpHandler')]
procedure UnitTestProductExportDoesNotCreateVariantsForChildItemVariants()
var
ParentItem: Record Item;
ChildItem: Record Item;
ChildItemVariantMapped: Record "Item Variant";
ChildItemVariantUnmapped: Record "Item Variant";
ShopifyProduct: Record "Shpfy Product";
ShopifyVariant: Record "Shpfy Variant";
ProductExport: Codeunit "Shpfy Product Export";
ProductInitTest: Codeunit "Shpfy Product Init Test";
begin
// [SCENARIO] Product Export must not create additional Shopify variants
// [SCENARIO] for unmapped child-item variants when a child item was added as Shopify variant.
InitializeProductExport();

// [GIVEN] Register Expected Outbound API Requests.
RegExpectedOutboundHttpRequestsForProductExport();

// [GIVEN] A parent item (without BC variants) and a child item with two BC variants.
ParentItem := ProductInitTest.CreateItem(ExportShop."Item Templ. Code", Any.DecimalInRange(10, 100, 2), Any.DecimalInRange(100, 500, 2), false);
ChildItem := ProductInitTest.CreateItem(ExportShop."Item Templ. Code", Any.DecimalInRange(10, 100, 2), Any.DecimalInRange(100, 500, 2), false);
ChildItemVariantMapped := CreateItemVariantForExport(ChildItem, 'MAP');
ChildItemVariantUnmapped := CreateItemVariantForExport(ChildItem, 'UNMAPPED');

// [GIVEN] A Shopify product mapped to the parent item and one existing Shopify variant
// [GIVEN] mapped to only one child item variant.
ShopifyProduct := CreateShopifyProductForExport(ParentItem.SystemId);
ShopifyVariant := CreateMappedShopifyVariantForExport(ShopifyProduct.Id, ChildItem.SystemId, ChildItemVariantMapped.SystemId);

// [WHEN] Product export runs for the shop.
ProductExport.SetShop(ExportShop);
ExportShop.SetRange(Code, ExportShop.Code);
ProductExport.Run(ExportShop);
OutboundHttpRequests.AssertEmpty();

// [THEN] No new Shopify variant record is created for the unmapped child item variant.
ShopifyVariant.Reset();
ShopifyVariant.SetRange("Product Id", ShopifyProduct.Id);
ShopifyVariant.SetRange("Item SystemId", ChildItem.SystemId);
ShopifyVariant.SetRange("Item Variant SystemId", ChildItemVariantUnmapped.SystemId);
LibraryAssert.IsTrue(ShopifyVariant.IsEmpty(), 'Unexpected Shopify variant record created for unmapped child item variant.');
end;

[HttpClientHandler]
internal procedure ProductExportChildItemVariantHttpHandler(Request: TestHttpRequestMessage; var Response: TestHttpResponseMessage): Boolean
var
ProductUpdateResponseTok: Label 'Products/ProductUpdateResponse.txt', Locked = true;
ProductVariantsBulkUpdateResponseTok: Label 'Products/ProductVariantsBulkUpdateResponse.txt', Locked = true;
UnexpectedAPICallsErr: Label 'More than expected API calls to Shopify detected.';
begin
if not ShpfyInitializeTest.VerifyRequestUrl(Request.Path, ExportShop."Shopify URL") then
exit(true);

case OutboundHttpRequests.Length() of
2:
LoadProductExportResourceIntoHttpResponse(ProductUpdateResponseTok, Response);
1:
LoadProductExportResourceIntoHttpResponse(ProductVariantsBulkUpdateResponseTok, Response);
0:
Error(UnexpectedAPICallsErr);
end;
exit(false);
end;

local procedure InitializeProductExport()
var
CommunicationMgt: Codeunit "Shpfy Communication Mgt.";
AccessToken: SecretText;
begin
Any.SetDefaultSeed();
OutboundHttpRequests.Clear();
// CreateShop() sets IsTestInProgress = true on the singleton CommunicationMgt,
// which redirects HTTP calls through event mocking instead of HttpClient.Send().
// Disable that so [HttpClientHandler] can intercept the requests instead.
CommunicationMgt.SetTestInProgress(false);
if ExportIsInitialized then
exit;

ExportShop := ShpfyInitializeTest.CreateShop();
ExportShop."Can Update Shopify Products" := true;
ExportShop."Product Metafields To Shopify" := false;
ExportShop.Modify();
Commit();

AccessToken := LibraryRandom.RandText(20);
ShpfyInitializeTest.RegisterAccessTokenForShop(ExportShop.GetStoreName(), AccessToken);

ExportIsInitialized := true;
end;

local procedure RegExpectedOutboundHttpRequestsForProductExport()
begin
OutboundHttpRequests.Enqueue('GQL Update Product');
OutboundHttpRequests.Enqueue('GQL Update Product Variants');
end;

local procedure LoadProductExportResourceIntoHttpResponse(ResourceText: Text; var Response: TestHttpResponseMessage)
begin
Response.Content.WriteFrom(NavApp.GetResourceAsText(ResourceText, TextEncoding::UTF8));
OutboundHttpRequests.DequeueText();
end;

local procedure CreateItemVariantForExport(Item: Record Item; VariantCodePrefix: Text): Record "Item Variant"
var
ItemVariant: Record "Item Variant";
begin
ItemVariant.Init();
ItemVariant.Validate("Item No.", Item."No.");
ItemVariant.Code := CopyStr(VariantCodePrefix + Any.AlphabeticText(5), 1, MaxStrLen(ItemVariant.Code));
ItemVariant.Description := CopyStr(Any.AlphabeticText(20), 1, MaxStrLen(ItemVariant.Description));
ItemVariant.Insert();
exit(ItemVariant);
end;

local procedure CreateShopifyProductForExport(ItemSystemId: Guid): Record "Shpfy Product"
var
ShopifyProduct: Record "Shpfy Product";
begin
ShopifyProduct.Init();
ShopifyProduct.Id := Any.IntegerInRange(10000, 99999);
ShopifyProduct."Shop Code" := ExportShop.Code;
ShopifyProduct."Item SystemId" := ItemSystemId;
ShopifyProduct.Title := CopyStr(Any.AlphabeticText(20), 1, MaxStrLen(ShopifyProduct.Title));
ShopifyProduct.Insert();
exit(ShopifyProduct);
end;

local procedure CreateMappedShopifyVariantForExport(ProductId: BigInteger; ItemSystemId: Guid; ItemVariantSystemId: Guid): Record "Shpfy Variant"
var
ShopifyVariant: Record "Shpfy Variant";
begin
ShopifyVariant.Init();
ShopifyVariant.Id := Any.IntegerInRange(100000, 999999);
ShopifyVariant."Shop Code" := ExportShop.Code;
ShopifyVariant."Product Id" := ProductId;
ShopifyVariant."Item SystemId" := ItemSystemId;
ShopifyVariant."Item Variant SystemId" := ItemVariantSystemId;
ShopifyVariant."Option 1 Name" := 'Variant';
ShopifyVariant."Option 1 Value" := CopyStr(Any.AlphabeticText(10), 1, MaxStrLen(ShopifyVariant."Option 1 Value"));
ShopifyVariant.Title := CopyStr(Any.AlphabeticText(20), 1, MaxStrLen(ShopifyVariant.Title));
ShopifyVariant.Insert();
exit(ShopifyVariant);
end;
}