Skip to main content

How To: Export Matrix Item cost to Shopify product variant

  • 14 February 2024
  • 9 replies
  • 223 views

In this topic, I want to share how to create a customization to export matrix item cost info to Shopify product variant. This customization can work well on 2023R2.

  1. Create a customization project
  2. Create a new code file, and extend the Graph from “PX.Commerce.Shopify.SPTemplateItemProcessor”.
     

     

  3. Select the “OVERRIDE METHOD” from the menu, and then select the Method “SaveBucketExport” from the list.
     

     

  4.  Modify the code as below.
    • Create the new instance of ProductRestDataProvider and InventoryItemRestDataProvider object
    • Use the ProductRestDataProvider instance to get the existing Product/Product variant info from Shopify, cost info doesn’t save in Product variant object, it’s in the InventoryItem object, so we need to get the InventoryItemId from the variant object.
    • We save the variant mapping info(ExternID is Product variant ID, LocalID is the Matrix item NoteID) in BCSyncDetails, so we can find the associated Matrix item by NoteID. All matrix items are saved in Bucket.Product.Local.Matrix object. 
    • Get the cost info from LastCost field or CurrentStdCost field from the Matrix item object, if you want to get the cost from other field, you can implement your own logic.
    • Create the new InventoryItem object and add to the list.
    • Call InventoryItemRestDataProvider to update the InventoryItem one by one
    	public class SPTemplateItemProcessor_Extension : PXGraphExtension<PX.Commerce.Shopify.SPTemplateItemProcessor>
    {
    public static bool IsActive() => CommerceFeaturesHelper.ShopifyConnector;
    #region Event Handlers
    public delegate Task SaveBucketExportDelegate(SPTemplateItemEntityBucket bucket, IMappedEntity existing, String operation, CancellationToken cancellationToken);

    tPXOverride]
    public Task SaveBucketExport(SPTemplateItemEntityBucket bucket, IMappedEntity existing, String operation, CancellationToken cancellationToken, SaveBucketExportDelegate baseMethod)
    {
    var baseResult = baseMethod(bucket, existing, operation, cancellationToken);

    var client = SPConnector.GetRestClient(Base.GetBindingExt<BCBindingShopify>());
    ProductRestDataProvider productRestDataProvider = new ProductRestDataProvider(client);
    InventoryItemRestDataProvider itemRestDataProvider = new InventoryItemRestDataProvider(client);

    List<InventoryItemData> inventoryItems = new List<InventoryItemData>();

    ProductData data = productRestDataProvider.GetByID(bucket.Product.ExternID, false, cancellationToken);
    if (data?.Variants?.Count > 0)
    {
    var syncDetails = bucket.Product.Details?.Where(x => x?.EntityType == BCEntitiesAttribute.Variant).ToList();
    foreach(var variant in data.Variants)
    {
    if (variant.InventoryItemId == null) continue;

    //Get the NoteID from the sync detail
    var localId = syncDetails.FirstOrDefault(x => string.Equals(x.ExternID, variant.Id.ToString()))?.LocalID;
    if (localId == null) continue;

    var matchMatrixItem = bucket.Product.Local?.Matrix?.Where(x => x.Id == localId).FirstOrDefault();
    if (matchMatrixItem == null) continue;

    var itemCost = matchMatrixItem.LastCost?.Value ?? matchMatrixItem.CurrentStdCost?.Value ?? 0;
    //Depends on the type of item, you need to provide the RequiresShipping and Tracked field value
    inventoryItems.Add(new InventoryItemData() { Id = variant.InventoryItemId, Cost = itemCost, Sku = variant.Sku, RequiresShipping = true, Tracked = true});
    }

    if(inventoryItems.Count > 0)
    {
    inventoryItems.ForEach(x => itemRestDataProvider.Update(x));
    }
    }

    return baseResult;
    }

    #endregion
    }
  5. Save the changes and publish the customization package, and then re-sync the TemplateItem from Sync history screen, you will see the cost info in Shopify product variant.
     

     

  6. If you wan to export the cost info from stock item or non stock item, you need to override SPStockItemProcessor or SPNonStockItemProcessor, and the stock item or non-stock item is saved in Bucket.Product.Local, you have to modify the code. 

I attached the customization package for your reference. 

Hope it helps! 

9 replies

Userlevel 7
Badge

Thank you for sharing this tip with the community @simonliang91!

Userlevel 5
Badge

@simonliang91 - Will this work in 23R1?

Userlevel 5
Badge +1

@StevenRatner, if you want to implement it into 2023r1, you have to modify the code to get the matrix item’s cost info from ERP. In 2023r1, there is no Cost data in Matrix item object.

Userlevel 5
Badge

@simonliang91 - Could I pull the cost from the stock item (INItemCost - AvgCost) or would you see that being an issue?

Userlevel 5
Badge

@simonliang91 - Do you have an updated package for 24R1?

Userlevel 5
Badge +1

@StevenRatner , I will provide an updated package for 24R1 as soon as possible.

Userlevel 5
Badge

@simonliang91 - Thanks for the update!

Userlevel 5
Badge

@simonliang91 - Any update on this for 24R1?

Userlevel 5
Badge +1

@StevenRatner , this is the customization package for 2024R1, because we have to use the dependency Injection to create the IShopifyRestClientFactory object, you need to create a solution project and attach the dll file to the customization package instead of coding in the UI page. I have attached the project code and package, you can modify the project code depends on your actual requirement.

 

using PX.Commerce.Shopify.API.REST;
using PX.Commerce.Core;
using PX.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using PX.Common;
using System.Threading.Tasks;
using System.Threading;
using PX.Objects;
using PX.Commerce.Shopify;

namespace PX.Commerce.Shopify
{
[PXProtectedAccess(typeof(PX.Commerce.Shopify.SPTemplateItemProcessor))]
public abstract class SPTemplateItemProcessorExt : PXGraphExtension<PX.Commerce.Shopify.SPTemplateItemProcessor>
{
[PXProtectedAccess]
public abstract IProductRestDataProvider<ProductData> productDataProvider { get; set; }
}

public class SPTemplateItemProcessor_Extension : PXGraphExtension<SPTemplateItemProcessorExt, PX.Commerce.Shopify.SPTemplateItemProcessor>
{
public static bool IsActive() => CommerceFeaturesHelper.ShopifyConnector;

[InjectDependency]
public IShopifyRestClientFactory shopifyRestClientFactoryExt { get; set; }
#region Event Handlers
public delegate Task SaveBucketExportDelegate(SPTemplateItemEntityBucket bucket, IMappedEntity existing, String operation, CancellationToken cancellationToken);

[PXOverride]
public async Task SaveBucketExport(SPTemplateItemEntityBucket bucket, IMappedEntity existing, String operation, CancellationToken cancellationToken, SaveBucketExportDelegate baseMethod)
{
baseMethod(bucket, existing, operation, cancellationToken);

var client = shopifyRestClientFactoryExt.GetRestClient(Base.GetBindingExt<BCBindingShopify>());
InventoryItemRestDataProvider itemRestDataProvider = new InventoryItemRestDataProvider(client);

List<InventoryItemData> inventoryItems = new List<InventoryItemData>();

ProductData data = await Base1.productDataProvider.GetByID(bucket.Product.ExternID, false, cancellationToken);
if (data?.Variants?.Count > 0)
{
var syncDetails = bucket.Product.Details?.Where(x => x?.EntityType == BCEntitiesAttribute.Variant).ToList();
foreach (var variant in data.Variants)
{
if (variant.InventoryItemId == null) continue;

//Get the NoteID from the sync detail
var localId = syncDetails.FirstOrDefault(x => string.Equals(x.ExternID, variant.Id.ToString()))?.LocalID;
if (localId == null) continue;

var matchMatrixItem = bucket.Product.Local?.Matrix?.Where(x => x.Id == localId).FirstOrDefault();
if (matchMatrixItem == null) continue;

var itemCost = matchMatrixItem.LastCost?.Value ?? matchMatrixItem.CurrentStdCost?.Value ?? 0;
//Depends on the type of item, you need to provide the RequiresShipping and Tracked field value
inventoryItems.Add(new InventoryItemData() { Id = variant.InventoryItemId, Cost = itemCost, Sku = variant.Sku, RequiresShipping = true, Tracked = true });
}

if (inventoryItems.Count > 0)
{
foreach (var oneInventoryItem in inventoryItems)
{
await itemRestDataProvider.Update(oneInventoryItem);
}
}
}
}

#endregion
}
}

 

Reply