Setting up Product Option Stock Amount and Pricing

Demonix
Shopify Partner
3 0 2

I thought I might ask here as I wasn't getting any help in the Technical Forums.

Basically I have an API that gives me product data which I format and then use in the graphql to create a product. I need to create the product, add its variants and then the quantity available for each and its price.

I've set this up so far but I'm stuck at doing the last part of setting the price for the options and their qty's

//Preparing the Data

const prepareAmrodProducts = (products) => {
  return products.map((product) => {
    // Prepare color options
    const colorOptions = product.variants.reduce((acc, variant) => {
      if (variant.codeColourName && !acc.includes(variant.codeColourName)) {
        acc.push(variant.codeColourName);
      }
      return acc;
    }, []);

    const sizeOptions = product.variants.reduce((acc, variant) => {
      if (variant.codeSizeName && !acc.includes(variant.codeSizeName)) {
        acc.push(variant.codeSizeName);
      }
      return acc;
    }, []);

    const productOptions = [];

    if (colorOptions.length > 0) {
      productOptions.push({
        name: "Color",
        values: colorOptions.map((color) => ({ name: color })),
      });
    }

    if (sizeOptions.length > 0) {
      productOptions.push({
        name: "Size",
        values: sizeOptions.map((size) => ({ name: size })),
      });
    }

    return {
      title: product.productName,
      bodyHtml: product.description,
      vendor: "Amrod",
      productType: product.categories[0]?.name || "Default Category",
      images: product.images.map((image) => ({
        src: image.urls[0]?.url || "Default Image URL",
        width: image.urls[0]?.width || 1024,
        height: image.urls[0]?.height || 1024,
      })),
      productOptions: productOptions,
      variants: product.variants.map((variant) => ({
        price: variant.price || 0,
        inventoryManagement: "SHOPIFY",
        inventoryPolicy: "DENY",
        sku: variant.fullCode,
        inventoryQuantity: variant.stock || 0,
        options: [
          variant.codeColourName || "",
          variant.codeSizeName || "",
        ].filter(Boolean),
      })),
    };
  });
};

 

And here is how I attempt to create the product:

const createProductBatch = async (productsData) => {
  // Product creation mutation
  const productCreateMutation = `
  mutation CreateProduct($input: ProductInput!, $media: [CreateMediaInput!]) {
    productCreate(input: $input, media: $media) {
        product {
          id
          options {
            id
            name
            position
            values
            optionValues {
              id
              name
              hasVariants
            }
          }
          media(first: 10) {
            nodes {
              alt
              mediaContentType
              preview {
                status
              }
            }
          }
          variants(first: 5) {
            nodes {
              id
              title
              selectedOptions {
                name
                value
              }
            }
          }          
          metafields(first: 10) {
            edges {
              node {
                type
                namespace
                key
                value
              }
            }
          }      
        }    
        userErrors {
          field
          message
        }
      }
    }
  `;

  const batchSize = 5;
  const delay = 500;
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
  let results = [];

  for (let i = 0; i < productsData.length; i += batchSize) {
    const batch = productsData.slice(i, i + batchSize);
    const batchResults = await Promise.all(
      batch.map(async (product) => {
        //Media
        const mediaInput = product.images.map((image) => ({
          originalSource: image.src,
          alt: image.altText || "",
          mediaContentType: "IMAGE",
        }));

        // Create the product
        const productResponse = await shopifyAPI.post(
          "",
          JSON.stringify({
            query: productCreateMutation,
            variables: {
              input: {
                title: product.title,
                bodyHtml: product.bodyHtml,
                vendor: product.vendor,
                productType: product.productType,
                metafields: [
                  {
                    namespace: "custom",
                    key: "sync_from_api",
                    type: "boolean",
                    value: "true",
                  },
                ],
                productOptions: product.productOptions,
              },
              media: mediaInput,
            },
          })
        );

        if (!productResponse.data || productResponse.data.errors) {
          console.error("GraphQL Error:", productResponse.data.errors);
          return null;
        }
        if (productResponse.data.data.productCreate.userErrors.length > 0) {
          console.error(
            "User Errors:",
            productResponse.data.data.productCreate.userErrors
          );
          return null;
        }

        // Product ID to use for creating variants
        const productId = productResponse.data.data.productCreate.product.id;

        return {
          productId: productId,
        };
      })
    );

    results = results.concat(batchResults);
    await sleep(delay);
  }
  return results.filter((result) => result !== null);
};

If anyone can give me some guidance on how to complete the last part or if I'm doing this correctly or wrong etc I'd really appreciate it. Even if you have some resource to teach me in an easy manor.



Reply 1 (1)

ShopifyDevSup
Shopify Staff
1357 224 473

Hi @Demonix,

 

Using the new Product API Model, once you create the Product with multiple Product Options, as you are doing here, you will then need to create the variants in a second API call. This is since the productCreate mutation when created with multiple Product Options, will create the Product Options, but doesn't actually create variants for them yet, however it does create a single default variant using the first listed product option values, and any additional variants will need to be created with a productVariantsBulkCreate call.

For example if you are running the productCreate mutation with the following input:

 

{
 "productInput": {
   "title": "My cool socks",
   "productOptions": [
     {
       "name": "Color",
       "values": [
         {
           "name": "Red"
         },
         {
           "name": "Green"
         },
         {
           "name": "Blue"
         }
       ]
     },
     {
       "name": "Size",
       "values": [
         {
           "name": "Small"
         },
         {
           "name": "Medium"
         },
         {
           "name": "Large"
         }
       ]
     }
   ]
 }
}

This will create a Product called My cool socks, this Product will have Product Options: Color and Size, with Option Values: Red, Green, Blue, and Small, Medium, Large. This product will also only have a single default variant created with Red/Small product option values.

Once you have created the product, you will need to create the Product Variant, with the productVariantsBulkCreate call. In the input of the productVariantsBulkCreate call you can set the prices and inventory values for each variant you are creating.

Additionally since you already have the Red/Small variant, instead of having to create new variants, then having to update the Red/Small variant with prices and inventory, you can set  strategy: REMOVE_STANDALONE_VARIANT in the initial productVariantsBulkCreate call and include the Red/Small variant in the productVariantsBulkCreate input. Here's an example for reference:


Call Body:

mutation productVariantsBulkCreate($productId: ID!, $strategy: ProductVariantsBulkCreateStrategy, $variants: [ProductVariantsBulkInput!]!) {
   productVariantsBulkCreate(productId: $productId, strategy: $strategy, variants: $variants) {

       productVariants{
           id
           title
           price
           inventoryPolicy
           inventoryQuantity
           .... etc ....
       }
   }
}

Call Variables:

{
   "productId": "gid://shopify/Product/1234567890",
   "strategy": "REMOVE_STANDALONE_VARIANT",
   "variants": [
       {
           "price": 4.99,
           "inventoryPolicy": "DENY",
           "inventoryQuantities": [
               {
                   "availableQuantity": 10,
                   "locationId": "gid://shopify/Location/1234567890"
               }
           ],
           "inventoryItem": {
               "cost":1.99,
               "tracked": true,
               "requiresShipping": true
           },        
           "optionValues": [
               {
                   "name": "Red",
                   "optionName": "Color"
               },
               {
                   "name": "Small",
                   "optionName": "Size"
               }
           ]
       },{
           "price": 4.99,
           "inventoryPolicy": "DENY",
           "inventoryQuantities": [
               {
                   "availableQuantity": 10,
                   "locationId": "gid://shopify/Location/1234567890"
               }
           ],
           "inventoryItem": {
               "cost":1.99,
               "tracked": true,
               "requiresShipping": true
           },        
           "optionValues": [
               {
                   "name": "Red",
                   "optionName": "Color"
               },
               {
                   "name": "Medium",
                   "optionName": "Size"
               }
           ]
       },{
           "price": 4.99,
           "inventoryPolicy": "DENY",
           "inventoryQuantities": [
               {
                   "availableQuantity": 10,
                   "locationId": "gid://shopify/Location/1234567890"
               }
           ],
           "inventoryItem": {
               "cost":1.99,
               "tracked": true,
               "requiresShipping": true
           },        
           "optionValues": [
               {
                   "name": "Red",
                   "optionName": "Color"
               },
               {
                   "name": "Large",
                   "optionName": "Size"
               }
           ]
       }
   ]
}

We do have some resources in our Shopify.dev documentation as well that does explain this in further detail, as well as providing guidance on using our new Product API Models. 
 

I hope this helps, and I hope you have a great day 🙂

Developer Support @ Shopify
- Was this reply helpful? Click Like to let us know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit Shopify.dev or the Shopify Web Design and Development Blog