Shopify catalog, publication, price list relationships and updating them using GraphQL

Updating Shopify price lists and catalogs starts with understanding the relationships between catalogs, publications and price lists. In Shopify there is a 1 to 1 relationship between catalogs, publications and price lists. A catalog can be assigned to numerous customers, companies, or locations. A publication is the record that contains the variants listed on the catalog and is not visible through the Shopify UI. Price lists contain the prices added to the variants within the publication.

Using GraphQL you will need to update all three records to remove or add variants to a price list. A variant can’t be added to a price list if it is not listed on the publication. Now that we understand the relationships between each record, we can dive into manipulating them using GraphQL. For the purposes of this article, I will assume that a Shopify connection has been created and that you have a basic knowledge of creating GraphQL queries and mutations.

You can use the following documentation to set up the Shopify GraphQL connection and write queries.

Overall flow logic:

Export product and pricing data → lookup catalog by title or id → lookup variant ids by product id → lookup variants on price lists → import price lists [update] → import publication on catalog [update]

Export Product and Pricing Data:

This may need to be done in more than one flow step depending on how your ERP is setup and if a custom ‘Catalog’ record is used. The important step here is to format the data in the following fashion. Catalog and price level data at header level with products and pricing at an item level.

{
	'catalogTitle': '<shopify catalog title here>',
	'catalogId': '<shopify catalog id here if stored>',
	'priceListId': '<shopify price list id here if stored>',
	'items': [
		{
			'shopifyProductid': '<shopify product id here>',
			'basePrice': '<base price>',
			'compareAtPrice': '<compare at price or price list price>'
		},
		{
			'shopifyProductid': '<shopify product id here>',
			'basePrice': '<base price>',
			'compareAtPrice': '<compare at price or price list price>'
		}		
	]
}

Lookup Shopify Catalog by title:

This step is needed if you do not have the price list id, publication id or are not sure if the catalog is already created or is active. The catalog will need to have an active status or all downstream mutations will throw an error. You will also need the publication, price list, and catalog ids to perform mutations.

Query:

query GetCatalogs($catalogQuery: String) {
  catalogs(query: $catalogQuery, first: 1) {
    nodes {
      id
      status
      title
      priceList {
        id
      }
      publication {
        id
      }
    }
  }
}

Variables:

{"catalogQuery":"title:{{{record.catalogTitle}}}"}

Utilize the ‘Non Standard API response patterns’ section of the lookup step to narrow the data down to the node level.

Example of a response:

{
  "page_of_records": [
    {
      "record": {
        "id": "gid://shopify/CompanyLocationCatalog/1234567890123",
        "status": "ACTIVE",
        "title": "Retail-USD",
        "priceList": {
          "id": "gid://shopify/PriceList/345678901234"
        },
        "publication": {
          "id": "gid://shopify/Publication/456789012345"
        }
      }
    }
  ]
}

Lookup Shopify Variant ids by product id

This step is used to return the variant ids listed at the product level. All Shopify products, even with a one to one relationship, have a variant id. All pricelist and publication mutations must be performed with the variant id. This flow step can be skipped, if you already have your variant ids coming from your ERP/Product export.

NOTE: This flow step will need to be a ‘One to Many’ relationship looping through the items array.

Query:

query ProductVariants($productId: ID!) {
  product(id: $productId) {
    id
    title
    variants(first: 50) { # Use pagination for more than 50 variants
      nodes {
        id
        sku
        title
        price {
          amount
          currencyCode
        }
      }
    }
  }
}

Variables:

NOTE: productId needs to be the full GID value. Example → "gid://shopify/Product/1234567890”

{
  "productId": "{{{record.shopifyProductid}}}" 
}

Write back the variant ID(s) back to the item array index.

Lookup Shopify Variants on Price List

This flow step returns all of the variants listed on the price list. Pagination is required for this flow step. You can find documentation on pagination here. Notice, that a variables section is not required here and the Shopify id is passed in the second line of the query.

NOTE: priceListId needs to be the full GID value. Example → "gid://shopify/PriceList/34628108535”. This value is either exported from your ERP or from the catalog lookup flow step.

Query:

query {
  priceList(id: {{record.shopifyPriceListGID}}) {
    catalog {
      id
      title
      publication {
        products(first: 100, after: {{#if export.http.paging.token}}\\"{{export.http.paging.token}}\\"{{else}}null{{/if}}) {
          nodes {
            id
          }
          pageInfo {
            endCursor
            hasNextPage
          }
        }
      }
    }
    currency
    parent {
      adjustment {
        type
        value
      }
    }
  }
}

A preSavePage hook is needed after this flow step to compare your exported variant ids to the variant ids listed on the current price list. The hook should return an array of variants to add and an array of variants to delete. You will use both arrays to to update price lists and publications. Within the hook script, you will also need to prepare the data in a way that chunks the arrays into batches of 100 records at a time. The publication update endpoint can only batch 100 records at a time.

Import Shopify Price Lists [Update]

This flow step will require a ‘One to Many’ relationship looping through the array of arrays created in the previous preSavePage hook. This allows for the chunking of data to be below 100 records at a time.

Query:

mutation priceListFixedPricesUpdate(
  $priceListID: ID!
  $pricesToAdd: [PriceListPriceInput!]!
  $variantIdsToDelete: [ID!]!
) {
  priceListFixedPricesUpdate(
    priceListId: $priceListID
    pricesToAdd: $pricesToAdd
    variantIdsToDelete: $variantIdsToDelete
  ) {
    deletedFixedPriceVariantIds
    priceList {
      id
    }
    pricesAdded {
      variant {
        id
      }
    }
    userErrors {
      field
      message
    }
  }
}

Variables:

NOTE: For the variables section, it is easier to pass the data through mapper2.0 mapping than it is using handlebar functions. However, this can be recreated using the variables section leveraging handlebars. If using the mapper2.0 mapping pass the following under the variables section.

{{{jsonSerialize record}}}

Mapping

The http body should look like the following:

NOTE: The ‘variantIdsToDelete’ array is a string array

{
  "priceListID": "gid://shopify/PriceList/34628108535",
  "pricesToAdd": [
    {
      "price": {
        "amount": "560.00",
        "currencyCode": "USD"
      },
      "compareAtPrice": {
        "amount": "999.99",
        "currencyCode": "USD"
      },
      "variantId": "gid://shopify/ProductVariant/49602457370871"
    }
  ],
  "variantIdsToDelete": [
    "gid://shopify/ProductVariant/49602457370333",
    "gid://shopify/ProductVariant/49602457311111",
    "gid://shopify/ProductVariant/49602452222222"
  ]
}

Import Shopify Publication [Update]

This flow step is similar to the previous one. Requires a ‘One to Many’ relationship looping through the array of chunked item data.

Query:

mutation publicationUpdate($id: ID!, $input: PublicationUpdateInput!) {
  publicationUpdate(id: $id, input: $input) {
    publication {
      id
    }
    userErrors {
      field
      message
    }
  }
}

Variables:

{{{jsonSerialize record}}}

Mapping:

The http body should look like the following:

NOTE: The ‘publishablesToAdd’ and ‘publishablesToRemove’ arrays are both string arrays

{
  "id": "gid://shopify/Publication/265919529207",
  "input": {
    "autoPublish": true,
    "publishablesToAdd": [
      "gid://shopify/Product/9645530667699",
      "gid://shopify/Product/9645530667341",
      "gid://shopify/Product/9645530667756"
    ],
    "publishablesToRemove": [
      "gid://shopify/Product/9645530611787",
      "gid://shopify/Product/9645530612333",
      "gid://shopify/Product/9645530086785",
      "gid://shopify/Product/9645567675556",
      "gid://shopify/Product/9645534534534",
      "gid://shopify/Product/9645530231231"
    ]
  }
}

2 Likes