Copying variant metafield data to order metafield using Shopify Flow

Topic summary

Problem: A user needed to automatically copy bin location data from variant metafields (variant.metafields.stock.bin) to order metafields (order.metafields.stock.bin) when orders are created, so the information appears on packing slips.

Initial Challenge: The original Shopify Flow workflow wasn’t successfully transferring the metafield data from product variants to orders.

Community Input:

  • One responder questioned how to handle orders with multiple line items containing bin metafields
  • Another suggested retrieving variants separately rather than directly from line items for better conditional checks and future-proofing

Solution Found: The original poster resolved the issue with a simple Flow workflow:

  • Trigger: Order created
  • Action: Set order metafield using Liquid code that loops through line items and variant metafields
  • The code creates a comma-separated string format: SKU:bin_location,SKU:bin_location
  • Example output: CM343001:bin #1,17-0761:bin #2

Implementation: The packing slip template then parses this string, splits by commas and colons, matches SKUs to current line items, and displays the corresponding bin location for each product.

Optimization Note: A later comment recommended avoiding metafield loops in Flow by selecting specific metafields directly to prevent API limit issues.

Summarized with AI on October 28. AI used: claude-sonnet-4-5-20250929.

Hello all.

Currently trying to add bin location data to packing slips for when I’m shipping products and I have the packing slip template code implemented correctly to where it shows on the packing slip the data from order.metafields.stock.bin, but the problem is auto-populating it when an order is created from variant.metafields.stock.bin . I have a workflow set up with value code being:

{% for lineItems_item in order.lineItems %}{% for metafields_item in lineItems_item.variant.metafields %}{% if metafields_item.key == ‘bin’ and metafields_item.key != ‘’ %},{{ lineItems_item.sku }}:Bin:{{ metafields_item.value }}{% endif %}{% endfor %}{% endfor %}

I did grab this code from a different forum post here: https://community.shopify.com/c/technical-q-a/adding-line-item-location-to-packing-slip/m-p/2023589

Here’s a picture of the work flow:

If anyone knows what I’m doing wrong please let me know, thank you!

If anyone needs more information please let me know!!

Can you have a situation where an order contains multiple line items whose variants have the “stock.bin” metafield populated? If so, from which line item would you take the metafield value to set on the order?

If an order always has only one line item with this metafield filled in, then the following workflow should work:

To obtain the value of the variant metafield “stock.bin”, use the field “lineItemsForeachitem.variant.metafield”:

Tbh I am not sure if you can get full metadata directly from lineitems. I suggest getting the variants separately anyway as it will give you the ability to apply conditional checks and future proof the solution.

Like this:

Query:

{%- for lineItems_item in order.lineItems -%}
  {{ ' ' }}{% if forloop.index > 1 %}OR{% endif %} id:{{lineItems_item.variant.id | replace: 'gid://shopify/ProductVariant/', ''}}
{%- endfor -%}

You can then iterate over each variant to check for your bin metafield and add another action that assigns it to the order.

Thank you for your reply!! Thankfully I did figure it out after some grinding and I’ll post what I did to achieve that:

The flow was pretty simple in theory but took quite a while figuring out the value code for a single line text for order.metafields.stock.bin :

{% assign output = ‘’ %}{% for item in order.lineItems %}{% for mf in item.variant.metafields %}{% if mf.namespace == ‘stock’ and mf.key == ‘bin’ and mf.value != blank %}{% assign output = output | append: item.sku | append: ‘:’ | append: mf.value | append: ‘,’ %}{% endif %}{% endfor %}{% endfor %}{{ output | remove_last: ‘,’ | strip_newlines | strip }}

That code make a single line text look like this in the order metafield: CM343001:bin #1,17-0761:bin #2,45-0761F:bin #3,44-SSE13:bin #4

That then gets read by the packing slip template code via this section:

{% assign all_pairs = order.metafields.stock.bin | split: “,” %}
{% assign matched_bin = “” %}
{% assign current_sku = line_item.sku | strip %}

{% for pair in all_pairs %}
{% assign pair = pair | strip %}
{% assign parts = pair | split: “:” %}
{% assign pair_sku = parts[0] | strip %}
{% assign pair_bin = parts[1] | strip %}
{% if pair_sku == current_sku %}
{% assign matched_bin = pair_bin %}
{% endif %}
{% endfor %}

{% if matched_bin != “” %}

Bin Location: {{ matched_bin }}

{% else %}

Bin Location: Not assigned

{% endif %}

Then it makes the packing slips look like this (I had put in some random things to test):

OEM Aprilia Passenger Seat Cowl Matte Black
CM343001
Bin Location: A64

Woodcraft Sidestand Elimination Plug
44-SSE13
Bin Location: something

Woodcraft Top Triple Clamp
17-0761
Bin Location: Space Space

Woodcraft Front Axle Sliders
45-0761F
Bin Location: 12345567965465165

1 Like

Thank you for your reply!! Thankfully I did figure it out after some grinding and I’ll post what I did to achieve that:

The flow was pretty simple in theory but took quite a while figuring out the value code for a single line text for order.metafields.stock.bin :

{% assign output = ‘’ %}{% for item in order.lineItems %}{% for mf in item.variant.metafields %}{% if mf.namespace == ‘stock’ and mf.key == ‘bin’ and mf.value != blank %}{% assign output = output | append: item.sku | append: ‘:’ | append: mf.value | append: ‘,’ %}{% endif %}{% endfor %}{% endfor %}{{ output | remove_last: ‘,’ | strip_newlines | strip }}

That code make a single line text look like this in the order metafield: CM343001:bin #1,17-0761:bin #2,45-0761F:bin #3,44-SSE13:bin #4

That then gets read by the packing slip template code via this section:

{% assign all_pairs = order.metafields.stock.bin | split: “,” %}
{% assign matched_bin = “” %}
{% assign current_sku = line_item.sku | strip %}

{% for pair in all_pairs %}
{% assign pair = pair | strip %}
{% assign parts = pair | split: “:” %}
{% assign pair_sku = parts[0] | strip %}
{% assign pair_bin = parts[1] | strip %}
{% if pair_sku == current_sku %}
{% assign matched_bin = pair_bin %}
{% endif %}
{% endfor %}

{% if matched_bin != “” %}

Bin Location: {{ matched_bin }}

{% else %}

Bin Location: Not assigned

{% endif %}

Then it makes the packing slips look like this (I had put in some random things to test):

OEM Aprilia Passenger Seat Cowl Matte Black
CM343001
Bin Location: A64

Woodcraft Sidestand Elimination Plug
44-SSE13
Bin Location: something

Woodcraft Top Triple Clamp
17-0761
Bin Location: Space Space

Woodcraft Front Axle Sliders
45-0761F
Bin Location: 12345567965465165

Tried copying my solution into here as a reply like I did for the others, couldn’t for some reason. Thank you for replying as well!

In Flow, you should avoid looping over metafields like this. If you choose order / metafield then you can choose a specific metafield that Flow will add to the environment. This prevents looping over a potentially very large set of metafield data that might actually cause you to hit API limits.

So your code would turn into (adding newlines to make it easier to see, also “bin” might not be the right name for your metafield):

{% assign output = '' %}
{% for item in order.lineItems %}
{% if item.variant.bin.value != blank %}
{% assign output = output | append: item.sku | append: ':' | append: item.variant.bin.value | append: ',' %}
{% endif %}
{% endfor %}
{{ output | remove_last: ',' | strip_newlines | strip }}